Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- A Nightmare on Elm Street NES Door RNG FCEUX Lua Script
- -- by ZeroNoin
- -- The document explaining more about the manipulation is here:
- -- http://pastebin.com/diLdmYQP
- -- The RNG of the doors when a game starts in A Nightmare on Elm Street NES has a big effect on a speedrun.
- -- What really helps here is a RNG buffer/manipulation to get the ideal RNG every or about every time.
- -- The entrance to the first three stages are randomly shuffled when a game is started as the player number screen fades.
- -- The value for the resulting RNG is stored in $06F7, ranging from 0x00 to 0x05.
- -- A value of 0x00 is best because it goes in order of the doors left to right.
- -- A value of 0x01 is worst because it has the player do the first stage before finding the run is dead.
- -- Values ranging from 0x02 to 0x05 quickly signal the speedrunner to reset.
- -- The frame counter is stored in $0021.
- -- The controller inputs affecting RNG are stored in $0030, $0031, and $0032.
- -- The relevant RNG is stored in $06FD, and is updated each frame by adding the frame counter value and the controller inputs value.
- -- These addresses are uninitialized at Power On, meaning different consoles and emulators may need different manipulation inputs.
- -- The RNG updates most frames from when the game is Powered On.
- -- There is a black screen following the first text screen when the game is powered on.
- -- This black screen has 10 frames of loading and lagging, and the RNG value does not update during these frames.
- -- Start can be pressed and held during this black screen all the way through the title and player number screens to a game start.
- -- This black screen is very useful because an 11-frame window buffer can be created using this.
- -- A buffer can be created from Power On, and not from a Reset because the RNG does not clear on a Reset.
- -- Adding held buttons and/or directions from Power On can help set the RNG to a more advantageous place.
- -- This script is for finding all the input buffers utilitizing Power-On and the 11-frame window to create the ideal RNG.
- -- Note the test can be sped up by pressing the + key.
- -- It is also nice to see what inputs are happening using the option Config > Display > Input Display > 1 Player.
- -- Make sure the controller inputs are clear and that you do not press any inputs during the test.
- -- Five addresses affecting door RNG and five values to initialize them at Power On.
- -- On FCEUX and likely on most or all Toploader NES's, the Counter and Inputs begin at 0x00, and the RNG begins at 0xFF.
- -- On BizHawk all memory addresses begin at 0xFF.
- -- On Frontloader NES's it varies quite a bit often depending on the contents of the addresses before and the timing of the Power On.
- COUNTER_ADDRESS = 0x0021
- COUNTER_SET = 0x00
- INPUT1_ADDRESS = 0x0030
- INPUT1_SET = 0x00
- INPUT2_ADDRESS = 0x0031
- INPUT2_SET = 0x00
- INPUT3_ADDRESS = 0x0032
- INPUT3_SET = 0x00
- RNG_ADDRESS = 0x06FD
- RNG_SET = 0xFF
- -- The address where the result of the door RNG is stored, wanting 0x00.
- RESULT_ADDRESS = 0x06F7
- RESULT_SUCCESS = 0x00
- -- A number of frames after Power On during the black screen in which Start can be held through to start a game.
- BLACK_FRAME = 325
- -- A number of frames after Power On soon after the RNG is loaded into the doors address $06F7.
- RNG_FRAME = 440
- -- The current inputs for Power On and the black screen ranging 0 to 255 (0x00 to 0xFF) for all possible input values used by the NES.
- -- These values increment as the script tests all the possible sets of inputs.
- CURRENT_POWER = 0x00
- CURRENT_BLACK = 0x00
- -- List of successful input buffers in strings formatted "|ABSTUDLR||ABSTUDLR|".
- RESULTS = {}
- -- The FCEUX RAM address read function:
- -- memory.readbyte(int address)
- -- Compare the value at the result address with the value considered RNG success.
- function compare_result(address,value)
- if(memory.readbyte(address) == value) then
- return true
- else
- return false
- end
- end
- -- Transform an input integer into a bit table formatted {1,1,1,1,1,1,1,1}.
- -- The values correspond to {A,B,S,T,U,D,L,R}.
- -- Copy the global variable into a temporary variable so the global variable is not modified.
- -- Compare to the individual input values 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, and 0x01.
- -- Subtract if it contains that value and put 1 in the table, otherwise put 0 in the table.
- function to_bit_table(input)
- temp = input
- output = {}
- current = 0x80
- while(current >= 0x01) do
- if(temp >= current) then
- temp = temp - current
- table.insert(output,1)
- else
- table.insert(output,0)
- end
- current = current / 2
- end
- return output
- end
- -- The FCEUX functions for creating inputs:
- -- joypad.set(int player, table input)
- -- The FCEUX table input key values are:
- -- A B select start up down left right
- INPUT_TABLE = {"A", "B", "select", "start", "up", "down", "left", "right"}
- -- The commonly-used corresponding abbreviations for the inputs:
- INPUT_ABBR = {"A","B","S","T","U","D","L","R"}
- -- Create a new table with all the inputs set to false, then check each input and set it to true if it is present.
- -- Then set the input table to the new table.
- function input_buttons(input)
- buttons = {A=false,B=false,select=false,start=false,up=false,down=false,left=false,right=false}
- for i=1,8,1 do
- if(input[i] == 1) then
- buttons[INPUT_TABLE[i]] = true
- end
- end
- joypad.set(1,buttons)
- end
- -- Convert the input table values into their corresponding abbreviations in a readable string.
- -- If the value is present add its abbreviation, otherwise add a space.
- function bits_to_abbr(input)
- output = ""
- for i=1,8,1 do
- if(input[i] == 1) then
- output = output .. INPUT_ABBR[i]
- else
- output = output .. " "
- end
- end
- return output
- end
- -- Filter the Power On input for opposing directions or the Start button.
- -- The Start button should be off for the Power On and on for the black screen.
- function power_filter(input)
- output = true
- if(input[5] == 1 and input[6] == 1) then
- output = false
- end
- if(input[7] == 1 and input[8] == 1) then
- output = false
- end
- if(input[4] == 1) then
- output = false
- end
- return output
- end
- -- Filter the black screen input for opposing directions or no Start button.
- -- The Start button should be off for the Power On and on for the black screen.
- function black_filter(input)
- output = true
- if(input[5] == 1 and input[6] == 1) then
- output = false
- end
- if(input[7] == 1 and input[8] == 1) then
- output = false
- end
- if(input[4] == 0) then
- output = false
- end
- return output
- end
- -- The FCEUX console print command:
- -- emu.print(string str)
- -- Make successful pairs of inputs into a string formatted "|ABSTUDLR||ABSTUDLR|".
- -- Store these successful inputs and print them in the FCEUX console.
- function report(input1,input2)
- output = "|" .. bits_to_abbr(input1) .. "||" .. bits_to_abbr(input2) .. "|"
- table.insert(RESULTS,output)
- emu.print(output)
- end
- -- The FCEUX Power On command:
- -- emu.poweron()
- -- The FCEUX memory address write command:
- -- memory.writebyte(int address, int value)
- -- Power On and set (initialize) the relevant addresses to the given values.
- function power_on()
- emu.poweron()
- memory.writebyte(COUNTER_ADDRESS,COUNTER_SET)
- memory.writebyte(INPUT1_ADDRESS,INPUT1_SET)
- memory.writebyte(INPUT2_ADDRESS,INPUT2_SET)
- memory.writebyte(INPUT3_ADDRESS,INPUT3_SET)
- memory.writebyte(RNG_ADDRESS,RNG_SET)
- end
- -- The FCEUX frame advance (advance the emulator one frame) command:
- -- emu.frameadvance()
- -- Power On and run the first inputs until the black screen, then the second inputs until door RNG.
- -- Since inputs perhaps may be delayed in frame advancing in FCEUX, the Power On occurs a few frames in.
- -- If the RNG result is successful, store and print the successful inputs.
- function run_test(input1,input2)
- current = -2
- while(current <= RNG_FRAME) do
- if(current == 0) then
- power_on()
- end
- if(current <= BLACK_FRAME) then
- input_buttons(input1)
- else
- input_buttons(input2)
- end
- emu.frameadvance()
- current = current + 1
- end
- if(compare_result(RESULT_ADDRESS,RESULT_SUCCESS)) then
- report(input1,input2)
- end
- end
- -- MAIN (Run Tests)
- -- On loading the script these instructions are automatically run utilizing the above variables and functions.
- -- Print in the console the initialized RAM settings.
- -- Test all the 5184 possible combinations of inputs, making each into a bit table during its test.
- -- Increment through each possibility filtering out the impossible 60352 combinations.
- -- Store and print in the console successes and notify when finished.
- INPUTS_SET = tostring(INPUT1_SET) .. " " .. tostring(INPUT2_SET) .. " " .. tostring(INPUT3_SET)
- emu.print("Counter Set: " .. tostring(COUNTER_SET) .. " || Inputs Set: " .. INPUTS_SET .. " || RNG Set: " .. tostring(RNG_SET))
- while(CURRENT_POWER < 256) do
- temp1 = to_bit_table(CURRENT_POWER)
- if(power_filter(temp1)) then
- while(CURRENT_BLACK < 256) do
- temp2 = to_bit_table(CURRENT_BLACK)
- if(black_filter(temp2)) then
- run_test(temp1,temp2)
- end
- CURRENT_BLACK = CURRENT_BLACK + 1
- end
- CURRENT_BLACK = 0x00
- end
- CURRENT_POWER = CURRENT_POWER + 1
- end
- CURRENT_POWER = 0x00
- emu.print("DONE")
- -- ZeroNoin
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement