Stage rulers. Try to hijack algorithm used to determine tile location

of characters.
This commit is contained in:
Empathic Qubit 2021-03-03 15:40:19 -05:00
parent c63f6a9325
commit ee2198b93f
9 changed files with 1687 additions and 1542 deletions

5
.editorconfig Normal file
View file

@ -0,0 +1,5 @@
[*]
indent_size = 4
indent_style = space
charset = utf-8
end_of_line = lf

4
.gitignore vendored
View file

@ -1,3 +1,5 @@
*.lsmv
*.log
catchem/
state/
crashsave*
*.backup

View file

@ -1,22 +1,19 @@
local _M = {}
--[[
Change BizhawkDir to your BizHawk directory.
Change script dir to your script directory
--]]
--_M.BizhawkDir = "C:/Users/mmill/Downloads/BizHawk-2.2/"
_M.BizhawkDir = "X:/B2_BizHawkLab/BizHawk-2.2.2/"
_M.ScriptDir = "/media/removable/Main/user1000/neat-donk"
_M.StateDir = _M.BizhawkDir .. "Lua/SNES/neat-mario/state/"
_M.PoolDir = _M.BizhawkDir .. "Lua/SNES/neat-mario/pool/"
_M.StateDir = _M.ScriptDir .. "/state/"
_M.PoolDir = _M.ScriptDir .. "/pool/"
--[[
At the moment the first in list will get loaded.
Rearrange for other savestates. (will be redone soon)
--]]
_M.State = {
"DP1.state", -- Donut Plains 1
"YI1.state", -- Yoshi's Island 1
"YI2.state", -- Yoshi's Island 2
"PiratePanic.lsmv",
}
--[[
@ -51,19 +48,23 @@ MaxNodes = 1000000,
}
_M.ButtonNames = {
"A",
"B",
"X",
"Y",
"Up",
"Down",
"Left",
"Right",
}
"B",
"Y",
"Select",
"Start",
"Up",
"Down",
"Left",
"Right",
"A",
"X",
"L",
"R",
}
_M.BoxRadius = 6
_M.InputSize = (_M.BoxRadius*2+1)*(_M.BoxRadius*2+1)
_M.Running = false
_M.Running = true
return _M

View file

@ -1,63 +1,65 @@
util = require "util"
FG_COLOR = 0x00ffffff
BG_COLOR = 0x99000000
TILEDATA_POINTER = 0x7e0098
TILE_SIZE = 32
TILE_RADIUS = 4
SPRITE_BASE = 0x7e0de2
SOLID_LESS_THAN = 0x7e00a0
DIDDY_X_VELOCITY = 0x7e0e02
DIDDY_Y_VELOCITY = 0x7e0e06
DIXIE_X_VELOCITY = 0x7e0e60
DIXIE_Y_VELOCITY = 0x7e0e64
CAMERA_X = 0x7e17ba
CAMERA_Y = 0x7e17c0
CAMERA_MODE = 0x7e054f
TILE_COLLISION_MATH_POINTER = 0x7e17b2
VERTICAL_POINTER = 0xc414
PARTY_X = 0x7e0a2a
PARTY_Y = 0x7e0a2c
count = 0
detailsidx = -1
jumping = false
helddown = false
floatmode = false
rulers = true
pokemon = false
pokecount = 0
showhelp = false
locked = false
lockdata = nil
incsprite = 0
fgcolor = 0x00ffffff
bgcolor = 0x99000000
function table_to_string(tbl)
local result = "{"
local keys = {}
for k in pairs(tbl) do
table.insert(keys, k)
end
table.sort(keys)
for _, k in ipairs(keys) do
local v = tbl[k]
if type(v) == "number" and v == 0 then
goto continue
end
party_tile_offset = 0
party_y_ground = 0
-- Check the key type (ignore any numerical keys - assume its an array)
if type(k) == "string" then
result = result.."[\""..k.."\"]".."="
end
last_called = 0
function set_party_tile_offset (val)
if party_tile_offset_debounce == val then
return
end
local sec, usec = utime()
last_called = sec * 1000000 + usec
party_tile_offset_debounce = val
end
-- Check the value type
if type(v) == "table" then
result = result..table_to_string(v)
elseif type(v) == "boolean" then
result = result..tostring(v)
else
result = result.."\""..v.."\""
end
result = result..",\n"
::continue::
end
-- Remove leading commas from the result
if result ~= "" then
result = result:sub(1, result:len()-1)
end
return result.."}"
function text(x, y, msg)
gui.text(x, y, msg, FG_COLOR, BG_COLOR)
end
function on_keyhook (key, state)
if not helddown and state["value"] == 1 then
if not helddown and state.value == 1 then
if key == "1" and not locked then
helddown = true
detailsidx = detailsidx - 1
if detailsidx < -1 then
detailsidx = 20
detailsidx = 22
end
elseif key == "2" and not locked then
helddown = true
detailsidx = detailsidx + 1
if detailsidx > 20 then
if detailsidx > 22 then
detailsidx = -1
end
elseif key == "3" then
@ -80,89 +82,90 @@ function on_keyhook (key, state)
elseif key == "7" then
helddown = true
floatmode = not floatmode
elseif key == "8" then
helddown = true
rulers = not rulers
elseif key == "0" then
showhelp = true
end
elseif state["value"] == 0 then
elseif state.value == 0 then
helddown = false
showhelp = false
end
end
function on_input (subframe)
jumping = input.get(0,0) ~= 0
if floatmode then
memory.writebyte(0x7e19ce, 0x16)
memory.writebyte(0x7e0e12, 0x99)
memory.writebyte(0x7e0e70, 0x99)
if input.get(0, 6) == 1 then
memory.writeword(0x7e0e02, -0x5ff)
memory.writeword(0x7e0e60, -0x5ff)
memory.writeword(DIDDY_X_VELOCITY, -0x5ff)
memory.writeword(DIXIE_X_VELOCITY, -0x5ff)
memory.writeword(0x7e0e06, 0)
memory.writeword(0x7e0e64, 0)
memory.writeword(DIDDY_Y_VELOCITY, 0)
memory.writeword(DIXIE_Y_VELOCITY, 0)
elseif input.get(0, 7) == 1 then
memory.writeword(0x7e0e02, 0x5ff)
memory.writeword(0x7e0e60, 0x5ff)
memory.writeword(DIDDY_X_VELOCITY, 0x5ff)
memory.writeword(DIXIE_X_VELOCITY, 0x5ff)
memory.writeword(0x7e0e06, 0)
memory.writeword(0x7e0e64, 0)
memory.writeword(DIDDY_Y_VELOCITY, 0)
memory.writeword(DIXIE_Y_VELOCITY, 0)
end
if input.get(0, 4) == 1 then
memory.writeword(0x7e0e06, -0x05ff)
memory.writeword(0x7e0e64, -0x05ff)
memory.writeword(DIDDY_Y_VELOCITY, -0x05ff)
memory.writeword(DIXIE_Y_VELOCITY, -0x05ff)
elseif input.get(0, 5) == 1 then
memory.writeword(0x7e0e06, 0x5ff)
memory.writeword(0x7e0e64, 0x5ff)
memory.writeword(DIDDY_Y_VELOCITY, 0x5ff)
memory.writeword(DIXIE_Y_VELOCITY, 0x5ff)
end
end
end
function file_exists(name)
local f=io.open(name,"r")
if f~=nil then io.close(f) return true else return false end
end
function get_sprite(base_addr)
return {
["control"] = memory.readword(base_addr),
["draworder"] = memory.readword(base_addr + 0x02),
["x"] = memory.readword(base_addr + 0x06),
["y"] = memory.readword(base_addr + 0x0a),
["jumpheight"] = memory.readword(base_addr + 0x0e),
["style"] = memory.readword(base_addr + 0x12),
["currentframe"] = memory.readword(base_addr + 0x18),
["nextframe"] = memory.readword(base_addr + 0x1a),
["state"] = memory.readword(base_addr + 0x1e),
["velox"] = memory.readsword(base_addr + 0x20),
["veloy"] = memory.readsword(base_addr + 0x24),
["velomaxx"] = memory.readsword(base_addr + 0x26),
["velomaxy"] = memory.readsword(base_addr + 0x2a),
["motion"] = memory.readword(base_addr + 0x2e),
["attr"] = memory.readword(base_addr + 0x30),
["animnum"] = memory.readword(base_addr + 0x36),
["remainingframe"] = memory.readword(base_addr + 0x38),
["animcontrol"] = memory.readword(base_addr + 0x3a),
["animreadpos"] = memory.readword(base_addr + 0x3c),
["animcontrol2"] = memory.readword(base_addr + 0x3e),
["animformat"] = memory.readword(base_addr + 0x40),
["damage1"] = memory.readword(base_addr + 0x44),
["damage2"] = memory.readword(base_addr + 0x46),
["damage3"] = memory.readword(base_addr + 0x48),
["damage4"] = memory.readword(base_addr + 0x4a),
["damage5"] = memory.readword(base_addr + 0x4c),
["damage6"] = memory.readword(base_addr + 0x4e),
["spriteparam"] = memory.readword(base_addr + 0x58),
base_addr = string.format("%04x", base_addr),
control = memory.readword(base_addr),
draworder = memory.readword(base_addr + 0x02),
x = memory.readword(base_addr + 0x06),
y = memory.readword(base_addr + 0x0a),
jumpheight = memory.readword(base_addr + 0x0e),
style = memory.readword(base_addr + 0x12),
currentframe = memory.readword(base_addr + 0x18),
nextframe = memory.readword(base_addr + 0x1a),
state = memory.readword(base_addr + 0x1e),
velox = memory.readsword(base_addr + 0x20),
veloy = memory.readsword(base_addr + 0x24),
velomaxx = memory.readsword(base_addr + 0x26),
velomaxy = memory.readsword(base_addr + 0x2a),
motion = memory.readword(base_addr + 0x2e),
attr = memory.readword(base_addr + 0x30),
animnum = memory.readword(base_addr + 0x36),
remainingframe = memory.readword(base_addr + 0x38),
animcontrol = memory.readword(base_addr + 0x3a),
animreadpos = memory.readword(base_addr + 0x3c),
animcontrol2 = memory.readword(base_addr + 0x3e),
animformat = memory.readword(base_addr + 0x40),
damage1 = memory.readword(base_addr + 0x44),
damage2 = memory.readword(base_addr + 0x46),
damage3 = memory.readword(base_addr + 0x48),
damage4 = memory.readword(base_addr + 0x4a),
damage5 = memory.readword(base_addr + 0x4c),
damage6 = memory.readword(base_addr + 0x4e),
spriteparam = memory.readword(base_addr + 0x58),
}
end
function sprite_details(idx)
local base_addr = idx * 94 + 0x7e0e9e
local base_addr = idx * 94 + SPRITE_BASE
local sprite = get_sprite(base_addr)
if sprite["control"] == 0 then
gui.text(0, 0, "Sprite "..idx.." (Empty)", fgcolor, bgcolor)
if sprite.control == 0 then
text(0, 0, "Sprite "..idx.." (Empty)")
incsprite = 0
locked = false
lockdata = nil
@ -170,7 +173,7 @@ function sprite_details(idx)
end
if incsprite ~= 0 then
memory.writeword(base_addr + 0x36, sprite["animnum"] + incsprite)
memory.writeword(base_addr + 0x36, sprite.animnum + incsprite)
lockdata = nil
incsprite = 0
@ -184,7 +187,7 @@ function sprite_details(idx)
memory.writeregion(base_addr, 94, lockdata)
end
gui.text(0, 0, "Sprite "..idx..(locked and " (Locked)" or "")..":\n\n"..table_to_string(sprite), fgcolor, bgcolor)
text(0, 0, "Sprite "..idx..(locked and " (Locked)" or "")..":\n\n"..util.table_to_string(sprite))
end
function on_paint (not_synth)
@ -193,7 +196,7 @@ function on_paint (not_synth)
local guiWidth, guiHeight = gui.resolution()
if showhelp then
gui.text(0, 0, [[
text(0, 0, [[
Keyboard Help
===============
@ -207,70 +210,156 @@ Sprite Details:
[6] Enable / Disable Pokemon mode (take screenshots of enemies)
[7] Enable / Disable float mode (fly with up/down)
]], fgcolor, bgcolor)
[8] Enable / Disable stage tile rulers
]])
return
end
gui.text(guiWidth - 75, 0, "Help [0]", fgcolor, bgcolor)
local stats = ""
local toggles = ""
if pokemon then
stats = stats.."Pokemon: "..pokecount.."\n"
toggles = toggles..string.format("Pokemon: %d\n", pokecount)
end
if floatmode then
stats = stats.."Float on\n"
toggles = toggles.."Float on\n"
end
gui.text(0, guiHeight - 40, stats, fgcolor, bgcolor)
text(0, guiHeight - 40, toggles)
stats = stats.."\nPokemon: "..pokecount
local directions = {
"Standard",
"Blur",
"Up"
}
local cameraX = memory.readword(0x7e17ba) - 256
local cameraY = memory.readword(0x7e17c0) - 256
local cameraX = memory.readword(CAMERA_X) - 256
local cameraY = memory.readword(CAMERA_Y) - 256
local cameraDir = memory.readbyte(CAMERA_MODE)
local partyScreenX = (memory.readword(0x7e0a2a) - 256 - cameraX) * 2
local partyScreenY = (memory.readword(0x7e0a2c) - 256 - cameraY) * 2
local direction = directions[cameraDir+1]
if detailsidx ~= -1 then
sprite_details(detailsidx)
else
gui.text(0, 0, "[1] <- Sprite Details Off -> [2]", fgcolor, bgcolor)
end
local vertical = memory.readword(TILE_COLLISION_MATH_POINTER) == VERTICAL_POINTER
gui.text(guiWidth - 200, guiHeight - 20, "Camera: "..tostring(cameraX)..","..tostring(cameraY), fgcolor, bgcolor)
local stats = string.format([[
%s camera %d,%d
Vertical: %s
Tile offset: %04x
]], direction, cameraX, cameraY, vertical, party_tile_offset)
gui.text(partyScreenX, partyScreenY, "Party", fgcolor, bgcolor)
text(guiWidth - 200, guiHeight - 60, stats)
local partyX = memory.readword(PARTY_X) - 256
local partyY = memory.readword(PARTY_Y) - 256
text((partyX - cameraX) * 2, (partyY - cameraY) * 2 + 20, "Party")
local sprites = {}
for idx = 0,20,1 do
local base_addr = idx * 94 + 0x7e0e9e
for idx = 0,22,1 do
local base_addr = idx * 94 + SPRITE_BASE
local sprite = get_sprite(base_addr)
sprites[idx] = sprite
if sprite["control"] == 0 then
if sprite.control == 0 then
goto continue
end
local spriteScreenX = (sprite["x"] - 256 - cameraX) * 2
local spriteScreenY = (sprite["y"] - 256 - cameraY) * 2
local spriteScreenX = (sprite.x - 256 - cameraX) * 2
local spriteScreenY = (sprite.y - 256 - cameraY) * 2
local sprcolor = bgcolor
local sprcolor = BG_COLOR
if detailsidx == idx then
sprcolor = 0x00ff0000
end
gui.text(spriteScreenX, spriteScreenY, sprite["animnum"]..","..sprite["attr"], fgcolor, sprcolor)
gui.text(spriteScreenX, spriteScreenY, sprite.control..","..sprite.animnum..","..sprite.attr, FG_COLOR, sprcolor)
local filename = os.getenv("HOME").."/neat-donk/catchem/"..sprite["animnum"]..","..sprite["attr"]..".png"
if pokemon and spriteScreenX > (guiWidth / 4) and spriteScreenX < (guiWidth / 4) * 3 and spriteScreenY > (guiHeight / 3) and spriteScreenY < guiHeight and not file_exists(filename) then
local filename = os.getenv("HOME").."/neat-donk/catchem/"..sprite.animnum..","..sprite.attr..".png"
if pokemon and spriteScreenX > (guiWidth / 4) and spriteScreenX < (guiWidth / 4) * 3 and spriteScreenY > (guiHeight / 3) and spriteScreenY < guiHeight and not util.file_exists(filename) then
gui.screenshot(filename)
pokecount = pokecount + 1
end
::continue::
end
if rulers and cameraX >= 0 then
local halfWidth = math.floor(guiWidth / 2)
local halfHeight = math.floor(guiHeight / 2)
local cameraTileX = math.floor(cameraX / TILE_SIZE)
gui.line(0, halfHeight, guiWidth, halfHeight, BG_COLOR)
for i = cameraTileX, cameraTileX + guiWidth / TILE_SIZE / 2,1 do
gui.text((i * TILE_SIZE - cameraX) * 2, halfHeight, tostring(i), FG_COLOR, BG_COLOR)
end
local cameraTileY = math.floor(cameraY / TILE_SIZE)
gui.line(halfWidth, 0, halfWidth, guiHeight, BG_COLOR)
for i = cameraTileY, cameraTileY + guiHeight / TILE_SIZE / 2,1 do
gui.text(halfWidth, (i * TILE_SIZE - cameraY) * 2, tostring(i), FG_COLOR, BG_COLOR)
end
end
local tilePtr = memory.readhword(TILEDATA_POINTER)
local solidLessThan = memory.readword(SOLID_LESS_THAN)
for x = -TILE_RADIUS, TILE_RADIUS, 1 do
for y = -TILE_RADIUS, TILE_RADIUS, 1 do
local offset = 0
if vertical then
offset = party_tile_offset + (y * 24 + x) * 2
else
offset = party_tile_offset + (x * 16 + y) * 2
end
local tile = memory.readword(tilePtr + offset)
if tile == 0 or tile >= solidLessThan then
goto continue
end
local tileX = (math.floor(partyX / TILE_SIZE + x) * TILE_SIZE - cameraX)
local tileY = (math.floor(party_y_ground / TILE_SIZE + y) * TILE_SIZE - cameraY)
gui.text(tileX * 2, tileY * 2, string.format("%04x,%02x", offset & 0xffff, tile), FG_COLOR, 0x66888800)
::continue::
end
end
if detailsidx ~= -1 then
sprite_details(detailsidx)
else
text(0, 20, "[1] <- Sprite Details Off -> [2]")
end
text(guiWidth - 125, 20, "Help [Hold 0]")
end
function tile_retrieval()
local tile = math.floor(memory.getregister("y") / 2) * 2
local newX = memory.readword(0x7e00a6)
local partyX = memory.readword(PARTY_X)
local oldX = partyX & 0x1f
local partyY = memory.readword(PARTY_Y)
if oldX - 5 < newX and newX < oldX + 5 and
not jumping and
memory.readword(0x7e0034) == partyY then
set_party_tile_offset(tile)
party_y_ground = partyY - 256
end
end
function on_timer()
local sec, usec = utime()
local now = sec * 1000000 + usec
if last_called + 100 * 1000 < now then
party_tile_offset = party_tile_offset_debounce
end
set_timer_timeout(100 * 1000)
end
input.keyhook("1", true)
@ -280,4 +369,9 @@ input.keyhook("4", true)
input.keyhook("5", true)
input.keyhook("6", true)
input.keyhook("7", true)
input.keyhook("8", true)
input.keyhook("0", true)
memory2.BUS:registerexec(TILE_RETRIEVAL, tile_retrieval)
set_timer_timeout(100 * 1000)

View file

@ -7,11 +7,11 @@ function _M.getPositions()
partyX = memory.readword(0x7e0a2a) - 256
partyY = memory.readword(0x7e0a2c) - 256
local cameraX = memory.readword(0x7e17ba) - 256
local cameraY = memory.readword(0x7e17c0) - 256
local cameraX = memory.readword(0x7e17ba) - 256
local cameraY = memory.readword(0x7e17c0) - 256
_M.screenX = partyX-layer1x
_M.screenY = partyY-layer1y
_M.screenX = (partyX-cameraX)*2
_M.screenY = (partyY-cameraY)*2
end
function _M.getBananas()
@ -99,10 +99,9 @@ function _M.getInputs()
local inputs = {}
local inputDeltaDistance = {}
local layer1x = memory.read_s16_le(0x1A);
local layer1x = memory.readword(0x7f0000);
local layer1y = memory.read_s16_le(0x1C);
for dy=-config.BoxRadius*16,config.BoxRadius*16,16 do
for dx=-config.BoxRadius*16,config.BoxRadius*16,16 do
inputs[#inputs+1] = 0
@ -154,11 +153,9 @@ function _M.getInputs()
end
function _M.clearJoypad()
controller = {}
for b = 1,#config.ButtonNames do
controller["P1 " .. config.ButtonNames[b]] = false
input.set(0, b - 1, 0)
end
joypad.set(controller)
end
return _M

View file

@ -4,6 +4,7 @@ config = require "config"
spritelist = require "spritelist"
game = require "game"
mathFunctions = require "mathFunctions"
util = require "util"
Inputs = config.InputSize+1
Outputs = #config.ButtonNames
@ -640,7 +641,10 @@ function initializePool()
end
function initializeRun()
savestate.load(config.NeatConfig.Filename);
print("Hello")
print(config.NeatConfig.Filename)
local rew = movie.to_rewind(config.NeatConfig.Filename)
movie.unsafe_rewind(rew)
if config.StartPowerup ~= NIL then
game.writePowerup(config.StartPowerup)
end
@ -686,7 +690,6 @@ if pool == nil then
initializePool()
end
function nextGenome()
pool.currentGenome = pool.currentGenome + 1
if pool.currentGenome > #pool.species[pool.currentSpecies].genomes then
@ -709,7 +712,6 @@ end
form = forms.newform(500, 500, "Mario-Neat")
netPicture = forms.pictureBox(form, 5, 250,470, 200)
--int forms.pictureBox(int formhandle, [int? x = null], [int? y = null], [int? width = null], [int? height = null])
function displayGenome(genome)

BIN
pool/PiratePanic.lsmv Normal file

Binary file not shown.

44
util.lua Normal file
View file

@ -0,0 +1,44 @@
local _M = {}
function _M.table_to_string(tbl)
local result = "{"
local keys = {}
for k in pairs(tbl) do
table.insert(keys, k)
end
table.sort(keys)
for _, k in ipairs(keys) do
local v = tbl[k]
if type(v) == "number" and v == 0 then
goto continue
end
-- Check the key type (ignore any numerical keys - assume its an array)
if type(k) == "string" then
result = result.."[\""..k.."\"]".."="
end
-- Check the value type
if type(v) == "table" then
result = result..table_to_string(v)
elseif type(v) == "boolean" then
result = result..tostring(v)
else
result = result.."\""..v.."\""
end
result = result..",\n"
::continue::
end
-- Remove leading commas from the result
if result ~= "" then
result = result:sub(1, result:len()-1)
end
return result.."}"
end
function _M.file_exists(name)
local f=io.open(name,"r")
if f~=nil then io.close(f) return true else return false end
end
return _M