Find the exits by moving the characters and camera in a grid pattern

This commit is contained in:
Empathic Qubit 2021-05-03 03:23:04 -04:00
parent 529f5dc67a
commit 52bdebfc81
10 changed files with 365 additions and 185 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
*.lsmv
!pool/*.lsmv
*.log *.log
catchem/ catchem/
state/ state/

View file

@ -54,6 +54,7 @@ Located at [tools/bsnes-launcher.lua](tools/bsnes-launcher.lua), this script giv
* [Serpent](https://github.com/pkulchenko/serpent) * [Serpent](https://github.com/pkulchenko/serpent)
* [LibDeflate](https://github.com/SafeteeWoW/LibDeflate) * [LibDeflate](https://github.com/SafeteeWoW/LibDeflate)
* [watchexec](https://github.com/watchexec/watchexec/blob/main/LICENSE) * [watchexec](https://github.com/watchexec/watchexec/blob/main/LICENSE)
* [Billiam's Promise library](https://github.com/Billiam/promise.lua)
## TODO ## TODO

171
game.lua
View file

@ -1,12 +1,14 @@
--Notes here --Notes here
local memory, bit, memory2, input = memory, bit, memory2, input local memory, bit, memory2, input, callback, movie = memory, bit, memory2, input, callback, movie
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1") local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
local Promise = nil
local util = nil
local mathFunctions = dofile(base.."/mathFunctions.lua") local mathFunctions = dofile(base.."/mathFunctions.lua")
local config = dofile(base.."/config.lua") local config = dofile(base.."/config.lua")
local spritelist = dofile(base.."/spritelist.lua") local spritelist = dofile(base.."/spritelist.lua")
local util = dofile(base.."/util.lua")()
local mem = dofile(base.."/mem.lua") local mem = dofile(base.."/mem.lua")
local _M = { local _M = {
leader = 0, leader = 0,
@ -39,6 +41,27 @@ function _M.getPositions()
_M.screenY = (_M.partyY-256-_M.cameraY)*2 _M.screenY = (_M.partyY-256-_M.cameraY)*2
end end
function _M.setPartyPosition(x, y)
memory.writeword(mem.addr.partyX, x)
memory.writeword(mem.addr.partyY, y)
_M.setSpritePosition(0, x, y)
_M.setSpritePosition(1, x, y)
end
function _M.setCameraPosition(x, y)
memory.writeword(mem.addr.cameraX, x)
memory.writeword(mem.addr.cameraY, y)
memory.writeword(mem.addr.cameraX2, x)
memory.writeword(mem.addr.cameraY2, y)
end
function _M.setSpritePosition(index, x, y)
local offsets = mem.offset.sprite
local spriteBase = mem.addr.spriteBase + index * mem.size.sprite
memory.writeword(spriteBase + offsets.x, x)
memory.writeword(spriteBase + offsets.y, y)
end
function _M.getBananas() function _M.getBananas()
local bananas = memory.readword(0x7e08bc) local bananas = memory.readword(0x7e08bc)
return bananas return bananas
@ -54,6 +77,126 @@ function _M.getKremCoins()
return krem return krem
end end
function _M.getAreaWidth()
return memory.readword(mem.addr.areaWidth) + 256
end
function _M.getAreaHeight()
return memory.readword(mem.addr.areaHeight)
end
function _M.getAreaLength()
return memory.readword(mem.addr.areaLength)
end
local onFrameAdvancedQueue = {}
function _M.advanceFrame()
local promise = Promise.new()
table.insert(onFrameAdvancedQueue, promise)
return promise
end
local function processFrameAdvanced()
for i=#onFrameAdvancedQueue,1,-1 do
table.remove(onFrameAdvancedQueue, i):resolve()
end
end
local onSetRewindQueue = {}
function _M.setRewindPoint()
local promise = Promise.new()
table.insert(onSetRewindQueue, promise)
movie.unsafe_rewind()
return promise
end
local function processSetRewind(state)
for i=#onSetRewindQueue,1,-1 do
table.remove(onSetRewindQueue, i):resolve(state)
end
end
local onRewindQueue = {}
function _M.rewind(rew)
local promise = Promise.new()
movie.unsafe_rewind(rew)
table.insert(onRewindQueue, promise)
return promise
end
local function processRewind()
for i=#onRewindQueue,1,-1 do
local promise = table.remove(onRewindQueue, i)
promise:resolve()
end
end
local function findPreferredExitLoop(frame, searchX, searchY, found, uniqueExits)
return _M.advanceFrame():next(function()
frame = frame + 1
if frame % 2 ~=0 then
return
end
local areaWidth = _M.getAreaWidth()
memory.writebyte(0x7e19ce, 0x16)
memory.writebyte(0x7e0e12, 0x99)
memory.writebyte(0x7e0e70, 0x99)
local sprites = _M.getSprites()
for i=1,#sprites,1 do
local sprite = sprites[i]
local name = spritelist.SpriteNames[sprite.control]
if sprite.control == spritelist.GoodSprites.goalTarget or
sprite.control == spritelist.GoodSprites.areaExit then
found = true
uniqueExits[sprite.y * areaWidth + sprite.x] = sprite
end
end
_M.setPartyPosition(searchX, searchY)
_M.setCameraPosition(searchX, searchY)
searchX = searchX + 0x100
if searchX > areaWidth then
searchX = 0
searchY = searchY + 0xe0
if searchY > _M.getAreaHeight() then
table.sort(uniqueExits, function(a, b)
return a.control < b.control
end)
-- Return upper right corner if we can't find anything
if found then
for id,sprite in pairs(uniqueExits) do
return { x = sprite.x, y = sprite.y }
end
else
return { x = areaWidth, y = 0}
end
end
end
end):next(function(ret)
if ret == nil then
return findPreferredExitLoop(frame, searchX, searchY, found, uniqueExits)
else
return ret
end
end)
end
function _M.findPreferredExit()
local point = nil
local result = nil
return _M.setRewindPoint():next(function(p)
point = p
return findPreferredExitLoop(0, 0, 0, false, {})
end):next(function(r)
result = r
return _M.rewind(point)
end):next(function()
return result
end)
end
function _M.getGoalHit() function _M.getGoalHit()
local sprites = _M.getSprites() local sprites = _M.getSprites()
for i=1,#sprites,1 do for i=1,#sprites,1 do
@ -454,7 +597,16 @@ local function registerHandler(space, regname, addr, callback)
}) })
end end
local inputHandler = nil
local setRewindHandler = nil
local rewindHandler = nil
function _M.unregisterHandlers() function _M.unregisterHandlers()
callback.unregister('input', inputHandler)
callback.unregister('set_rewind', setRewindHandler)
callback.unregister('post_rewind', rewindHandler)
inputHandler = nil
setRewindHandler = nil
rewindHandler = nil
for i=#handlers,1,-1 do for i=#handlers,1,-1 do
local handler = table.remove(handlers, i) local handler = table.remove(handlers, i)
handler.unregisterFn(handler.space, handler.addr, handler.fn) handler.unregisterFn(handler.space, handler.addr, handler.fn)
@ -462,6 +614,13 @@ function _M.unregisterHandlers()
end end
function _M.registerHandlers() function _M.registerHandlers()
if inputHandler ~= nil then
error("Only call register handlers once")
end
inputHandler = callback.register('input', processFrameAdvanced)
setRewindHandler = callback.register('set_rewind', processSetRewind)
rewindHandler = callback.register('post_rewind', processRewind)
registerHandler(memory2.BUS, 'registerwrite', 0xb517b2, processAreaLoad) registerHandler(memory2.BUS, 'registerwrite', 0xb517b2, processAreaLoad)
registerHandler(memory2.WRAM, 'registerread', 0x06b1, processMapLoad) registerHandler(memory2.WRAM, 'registerread', 0x06b1, processMapLoad)
for i=2,22,1 do for i=2,22,1 do
@ -469,4 +628,10 @@ function _M.registerHandlers()
end end
end end
return _M return function(promise)
Promise = promise
if util == nil then
util = dofile(base.."/util.lua")(Promise)
end
return _M
end

View file

@ -6,8 +6,16 @@ local _M = {
verticalPointer = 0xc414, verticalPointer = 0xc414,
tiledataPointer = 0x7e0098, tiledataPointer = 0x7e0098,
haveBoth = 0x7e08c2, haveBoth = 0x7e08c2,
---Height in game units for vertical levels, width for horizontal
areaLength = 0x7e17b4,
---This is always the traditional width no matter the level type
areaWidth = 0x7e0afc,
---This is always the traditional height no matter the level type
areaHeight = 0x7e0afe,
cameraX = 0x7e17ba, cameraX = 0x7e17ba,
cameraY = 0x7e17c0, cameraY = 0x7e17c0,
cameraX2 = 0x7e0ad7,
cameraY2 = 0x7e0adb,
leadChar = 0x7e08a4, leadChar = 0x7e08a4,
partyX = 0x7e0a2a, partyX = 0x7e0a2a,
partyY = 0x7e0a2c, partyY = 0x7e0a2c,

View file

@ -169,5 +169,6 @@ outFile:close()
print(string.format('Wrote init to output at %d', ts)) print(string.format('Wrote init to output at %d', ts))
waiter:next(waitLoop):catch(function(error) waiter:next(waitLoop):catch(function(error)
print('ERROR: '..error) print('Runner process error: '..error)
io.stderr:write('Runner process error: '..error..'\n')
end) end)

View file

@ -102,7 +102,9 @@ end
return function(promise) return function(promise)
-- FIXME Should this be a global??? -- FIXME Should this be a global???
Promise = promise Promise = promise
util = dofile(base.."/util.lua")(Promise) if util == nil then
util = dofile(base.."/util.lua")(Promise)
end
-- FIXME Maybe don't do this in the "constructor"? -- FIXME Maybe don't do this in the "constructor"?
if util.isWin then if util.isWin then
util.downloadFile('https://github.com/watchexec/watchexec/releases/download/1.13.1/watchexec-1.13.1-x86_64-pc-windows-gnu.zip', base..'/watchexec.zip') util.downloadFile('https://github.com/watchexec/watchexec/releases/download/1.13.1/watchexec-1.13.1-x86_64-pc-windows-gnu.zip', base..'/watchexec.zip')

View file

@ -5,7 +5,7 @@ local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
local Promise = nil local Promise = nil
local config = dofile(base.."/config.lua") local config = dofile(base.."/config.lua")
local game = dofile(base.."/game.lua") local game = nil
local mathFunctions = dofile(base.."/mathFunctions.lua") local mathFunctions = dofile(base.."/mathFunctions.lua")
local util = dofile(base.."/util.lua")() local util = dofile(base.."/util.lua")()
@ -216,18 +216,6 @@ local function displayGenome(genome)
gui.renderctx.setnull() gui.renderctx.setnull()
end end
local function advanceFrame(_M)
local promise = Promise.new()
table.insert(_M.onFrameAdvancedHandler, promise)
return promise
end
local function processFrameAdvanced(_M)
for i=#_M.onFrameAdvancedHandler,1,-1 do
table.remove(_M.onFrameAdvancedHandler, i):resolve()
end
end
local buttons = nil local buttons = nil
local buttonCtx = gui.renderctx.new(500, 70) local buttonCtx = gui.renderctx.new(500, 70)
local function displayButtons(_M) local function displayButtons(_M)
@ -286,10 +274,11 @@ local function displayForm(_M)
formCtx:clear() formCtx:clear()
gui.rectangle(0, 0, 500, guiHeight, 1, 0x00ffffff, 0xbb000000) gui.rectangle(0, 0, 500, guiHeight, 1, 0x00ffffff, 0xbb000000)
--gui.circle(game.screenX-84, game.screenY-84, 192 / 2, 1, 0x50000000) --gui.circle(game.screenX-84, game.screenY-84, 192 / 2, 1, 0x50000000)
local rightmost = _M.rightmost[_M.currentArea] local areaInfo = _M.areaInfo[_M.currentArea]
if rightmost == nil then local distanceTraversed = 0
rightmost = 0 if areaInfo ~= nil then
distanceTraversed = areaInfo.startDistance - areaInfo.shortest
end end
gui.text(5, 30, "Timeout: " .. _M.timeout) gui.text(5, 30, "Timeout: " .. _M.timeout)
@ -307,7 +296,7 @@ local function displayForm(_M)
gui.text(230, 65, "Damage: " .. _M.partyHitCounter) gui.text(230, 65, "Damage: " .. _M.partyHitCounter)
gui.text(230, 80, "PowerUp: " .. _M.powerUpCounter) gui.text(230, 80, "PowerUp: " .. _M.powerUpCounter)
gui.text(320, 65, string.format("Current Area: %04x", _M.currentArea)) gui.text(320, 65, string.format("Current Area: %04x", _M.currentArea))
gui.text(320, 80, "Rightmost: "..rightmost) gui.text(320, 80, string.format("Traveled: %d", distanceTraversed))
displayButtons(_M) displayButtons(_M)
formCtx:set() formCtx:set()
@ -417,24 +406,6 @@ local function fitnessAlreadyMeasured(_M)
return genome.fitness ~= 0 return genome.fitness ~= 0
end end
local rewinds = {}
local rew = movie.to_rewind(config.NeatConfig.Filename)
local function rewind()
local promise = Promise.new()
movie.unsafe_rewind(rew)
table.insert(rewinds, promise)
return promise
end
local function rewound()
frame = 0
lastFrame = 0
for i=#rewinds,1,-1 do
local promise = table.remove(rewinds, i)
promise:resolve()
end
end
local function newNeuron() local function newNeuron()
local neuron = {} local neuron = {}
neuron.incoming = {} neuron.incoming = {}
@ -481,6 +452,13 @@ local function generateNetwork(genome)
genome.network = network genome.network = network
end end
local rew = movie.to_rewind(config.NeatConfig.Filename)
local function rewind()
return game.rewind(rew):next(function()
frame = 0
lastFrame = 0
end)
end
local function initializeRun(_M) local function initializeRun(_M)
settings.set_speed("turbo") settings.set_speed("turbo")
@ -493,7 +471,7 @@ local function initializeRun(_M)
exec('enable-sound '..enableSound) exec('enable-sound '..enableSound)
gui.subframe_update(false) gui.subframe_update(false)
return rewind():next(function() return rewind():next(function(preferredExit)
if config.StartPowerup ~= nil then if config.StartPowerup ~= nil then
game.writePowerup(config.StartPowerup) game.writePowerup(config.StartPowerup)
end end
@ -520,18 +498,49 @@ local function initializeRun(_M)
_M.powerUpBefore = game.getBoth() _M.powerUpBefore = game.getBoth()
_M.currentArea = game.getCurrentArea() _M.currentArea = game.getCurrentArea()
_M.lastArea = _M.currentArea _M.lastArea = _M.currentArea
_M.rightmost = { [_M.currentArea] = 0 }
_M.upmost = { [_M.currentArea] = 0 } for areaId,areaInfo in pairs(_M.areaInfo) do
areaInfo.shortest = areaInfo.startDistance
end
local genome = _M.currentSpecies.genomes[_M.currentGenomeIndex] local genome = _M.currentSpecies.genomes[_M.currentGenomeIndex]
generateNetwork(genome) generateNetwork(genome)
evaluateCurrent(_M) evaluateCurrent(_M)
end) end)
end end
local function getDistanceTraversed(areaInfo)
local distanceTraversed = 0
for areaId,areaInfo in pairs(areaInfo) do
distanceTraversed = areaInfo.startDistance - areaInfo.shortest
end
return distanceTraversed
end
local function mainLoop(_M, genome) local function mainLoop(_M, genome)
return advanceFrame(_M):next(function() return game.advanceFrame():next(function()
local nextArea = game.getCurrentArea()
if nextArea ~= _M.lastArea then
_M.lastArea = nextArea
game.onceAreaLoaded(function()
_M.timeout = _M.timeout + 60 * 5
_M.currentArea = nextArea
_M.lastArea = _M.currentArea
end)
elseif _M.currentArea == _M.lastArea and _M.areaInfo[_M.currentArea] == nil then
message(_M, 'Searching for the main exit in this area')
return game.findPreferredExit():next(function(preferredExit)
local startDistance = math.floor(math.sqrt((preferredExit.y - game.partyY) ^ 2 + (preferredExit.x - game.partyX) ^ 2))
_M.areaInfo[_M.currentArea] = {
startDistance = startDistance,
preferredExit = preferredExit,
shortest = startDistance,
}
end)
end
end):next(function()
if lastFrame + 1 ~= frame then if lastFrame + 1 ~= frame then
message(_M, string.format("We missed %d frames", frame - lastFrame), 0x00990000) message(_M, string.format("We missed %d frames", frame - lastFrame), 0x00ff0000)
end end
lastFrame = frame lastFrame = frame
@ -566,37 +575,16 @@ local function mainLoop(_M, genome)
end end
end end
local nextArea = game.getCurrentArea() local areaInfo = _M.areaInfo[_M.currentArea]
if nextArea ~= _M.lastArea then if areaInfo ~= nil then
_M.lastArea = nextArea local exitDist = math.floor(math.sqrt((areaInfo.preferredExit.y - game.partyY) ^ 2 + (areaInfo.preferredExit.x - game.partyX) ^ 2))
game.onceAreaLoaded(function() if exitDist < areaInfo.shortest then
_M.timeout = _M.timeout + 60 * 5 areaInfo.shortest = exitDist
_M.currentArea = nextArea
_M.lastArea = _M.currentArea
if _M.rightmost[_M.currentArea] == nil then
_M.rightmost[_M.currentArea] = 0
_M.upmost[_M.currentArea] = 0
end
end)
end
if not game.vertical then
if game.partyX > _M.rightmost[_M.currentArea] then
_M.rightmost[_M.currentArea] = game.partyX
if _M.timeout < timeoutConst then
_M.timeout = timeoutConst
end
end
else
if game.partyY > _M.upmost[_M.currentArea] then
_M.upmost[_M.currentArea] = game.partyY
if _M.timeout < timeoutConst then if _M.timeout < timeoutConst then
_M.timeout = timeoutConst _M.timeout = timeoutConst
end end
end end
end end
-- FIXME Measure distance to target / area exit
-- We might not always be horizontal
local hitTimer = game.getHitTimer(_M.lastBoth) local hitTimer = game.getHitTimer(_M.lastBoth)
@ -651,20 +639,9 @@ local function mainLoop(_M, genome)
local bumpPenalty = _M.bumps * 100 local bumpPenalty = _M.bumps * 100
local powerUpBonus = _M.powerUpCounter * 100 local powerUpBonus = _M.powerUpCounter * 100
local most = 0 local distanceTraversed = getDistanceTraversed(_M.areaInfo) - _M.currentFrame / 2
if not game.vertical then
for k,v in pairs(_M.rightmost) do local fitness = bananaCoinsFitness - bumpPenalty - hitPenalty + powerUpBonus + distanceTraversed + game.getJumpHeight() / 100
most = most + v
end
most = most - _M.currentFrame / 2
else
for k,v in pairs(_M.upmost) do
most = most + v
end
most = most - _M.currentFrame / 2
end
local fitness = bananaCoinsFitness - bumpPenalty - hitPenalty + powerUpBonus + most + game.getJumpHeight() / 100
local lives = game.getLives() local lives = game.getLives()
@ -855,13 +832,11 @@ local function run(_M, species, generationIdx, genomeCallback)
register(_M, 'input', function() register(_M, 'input', function()
frame = frame + 1 frame = frame + 1
updateController() updateController()
processFrameAdvanced(_M)
saveLoadInput(_M) saveLoadInput(_M)
end) end)
register(_M, 'keyhook', function(key, state) register(_M, 'keyhook', function(key, state)
keyhook(_M, key, state) keyhook(_M, key, state)
end) end)
register(_M, 'post_rewind', rewound)
input.keyhook("1", true) input.keyhook("1", true)
input.keyhook("4", true) input.keyhook("4", true)
@ -885,6 +860,9 @@ end
return function(promise) return function(promise)
Promise = promise Promise = promise
if game == nil then
game = dofile(base.."/game.lua")(Promise)
end
local _M = { local _M = {
currentGenerationIndex = 1, currentGenerationIndex = 1,
currentSpecies = nil, currentSpecies = nil,
@ -912,16 +890,13 @@ return function(promise)
powerUpBefore = 0, powerUpBefore = 0,
currentArea = 0, currentArea = 0,
lastArea = 0, lastArea = 0,
rightmost = {}, areaInfo = {},
upmost = {},
lastBoth = 0, lastBoth = 0,
onMessageHandler = {}, onMessageHandler = {},
onSaveHandler = {}, onSaveHandler = {},
onLoadHandler = {}, onLoadHandler = {},
onRenderFormHandler = {}, onRenderFormHandler = {},
onFrameAdvancedHandler = {},
} }
_M.onRenderForm = function(handler) _M.onRenderForm = function(handler)

View file

@ -4,100 +4,114 @@ _M.Sprites = {}
-- Make sure this list is sorted before initialization. -- Make sure this list is sorted before initialization.
_M.NeutralSprites = { _M.NeutralSprites = {
0x0020, -- Krow egg fragments krowEggFragments = 0x0020,
0x0060, -- Barrel fragments barrelFragments = 0x0060,
0x0064, -- Barrel fragments barrelFragments2 = 0x0064,
-- Our heroes -- Our heroes
0x00e4, -- Diddy diddy = 0x00e4,
0x00e8, -- Dixie dixie = 0x00e8,
0x0100, -- Stars stars = 0x0100,
-- Items that require too much interaction -- Items that require too much interaction
0x01a4, -- Barrel barrel = 0x01a4, -- Barrel
0x01b0, -- Cannonball (immobile) cannonball = 0x01b0,
0x01c0, -- Chest chest = 0x01c0,
0x01bc, -- Small crate smallCrate = 0x01bc,
0x011c, -- Barrel barrel2 = 0x011c,
0x013c, -- Cannon cannon = 0x013c,
0x014c, -- Hook hook = 0x014c,
0x01b8, -- TNT tnt = 0x01b8,
-- Inert -- Inert
0x0168, -- Goal pole goalPole = 0x0168,
0x016c, -- Goal roulette goalroulette = 0x016c,
0x0160, -- Goal base goalBase = 0x0160,
0x0164, -- Goal barrel goalBarrel = 0x0164,
0x0238, -- Pow pow = 0x0238,
0x023c, -- Exploding crate explodingCrate = 0x023c,
0x0258, -- No Animals Sign noAnimalsSign = 0x0258,
} }
-- Make sure this list is sorted before initialization. -- Make sure this list is sorted before initialization.
_M.GoodSprites = { _M.GoodSprites = {
-- Destinations -- Destinations
0x0094, -- Area exit areaExit = 0x0094,
0x00b0, -- Goal target goalTarget = 0x00b0,
0x0120, -- Bonus barrel bonusBarrel = 0x0120,
0x0128, -- Hot air balloon hotAirBalloon = 0x0128,
0x0140, -- Launch barrel launchBarrel = 0x0140,
0x0148, -- Animal crate animalCrate = 0x0148,
0x0150, -- Invincibility barrel invincibilityBarrel = 0x0150,
0x0154, -- Midpoint midpoint = 0x0154,
0x015c, -- Banana Coin/Kremkoin/DK Coin allCoins = 0x015c, -- Banana Coin/Kremkoin/DK Coin
0x0170, -- Banana bunch bananaBunch = 0x0170,
0x0174, -- KONG letters kongLetter = 0x0174,
0x0178, -- xUP balloon upBalloon = 0x0178, -- xUP balloon
-- Animals -- Animals
0x0190, -- Squitter squitter = 0x0190,
0x0194, -- Rattly rattly = 0x0194,
0x0198, -- Squawks squawks = 0x0198,
0x019c, -- Rambi rambi = 0x019c,
0x0304, -- Clapper clapper = 0x0304,
0x01a8, -- DK Barrel label dkBarrelLabel = 0x01a8,
0x01b4, -- Krow's eggs krowEgg = 0x01b4,
0x0220, -- Flitter (used as unavoidable platforms in some levels) flitter = 0x0220,
0x02d4, -- Krochead (red and green) krocheadAllColors = 0x02d4,
} }
-- Currently not used. -- Currently not used.
_M.BadSprites = { _M.BadSprites = {
-- Baddies -- Baddies
0x006c, -- Kannon kannon = 0x006c,
0x01ac, -- Klobber (yellow and green) klobberAllColors = 0x01ac,
0x01d0, -- Kannon's fodder (Ball/barrel) kannonFodder = 0x01d0,
0x01d8, -- Krusha krusha = 0x01d8,
0x01dc, -- Click-Clack clickClack = 0x01dc,
0x01e4, -- Neek neek = 0x01e4,
0x01ec, -- Klomp klomp = 0x01ec,
0x01e8, -- Klobber (awake) klobberAwake = 0x01e8,
0x01f0, -- Klampon klampon = 0x01f0,
0x01f8, -- Flotsam flotsam = 0x01f8,
0x0200, -- Klinger klinger = 0x0200,
0x0208, -- Puftup puftup = 0x0208,
0x0218, -- Zinger (red and yellow) zingerAllColors = 0x0218,
0x0214, -- Mini-Necky miniNecky = 0x0214,
0x020c, -- Lockjaw lockjaw = 0x020c,
0x021c, -- Kaboing kaboing = 0x021c,
0x0224, -- Krow (Boss) krow = 0x0224, -- Boss
0x025c, -- Krook (very large) krook = 0x025c,
} }
_M.SpriteNames = {}
function _M.InitSpriteNames()
for v,k in pairs(_M.GoodSprites) do
_M.SpriteNames[k] = v
end
for v,k in pairs(_M.BadSprites) do
_M.SpriteNames[k] = v
end
for v,k in pairs(_M.NeutralSprites) do
_M.SpriteNames[k] = v
end
end
function _M.InitSpriteList() function _M.InitSpriteList()
for i=1,#_M.GoodSprites,1 do for k,v in pairs(_M.GoodSprites) do
_M.Sprites[_M.GoodSprites[i]] = 1 _M.extSprites[v] = 1
end end
for i=1,#_M.BadSprites,1 do for k,v in pairs(_M.BadSprites) do
_M.Sprites[_M.BadSprites[i]] = -1 _M.extSprites[v] = -1
end end
for i=1,#_M.NeutralSprites,1 do for k,v in pairs(_M.NeutralSprites) do
_M.Sprites[_M.NeutralSprites[i]] = 0 _M.extSprites[v] = 0
end end
end end
@ -108,21 +122,21 @@ _M.ExtNeutralSprites = {
} }
_M.ExtGoodSprites = { _M.ExtGoodSprites = {
0xe0, -- banana banana01 = 0xe0, -- banana
0xe1, -- banana banana02 = 0xe1, -- banana
0xe2, -- banana banana03 = 0xe2, -- banana
0xe3, -- banana banana04 = 0xe3, -- banana
0xe4, -- banana banana05 = 0xe4, -- banana
0xe5, -- banana banana06 = 0xe5, -- banana
0xe6, -- banana banana07 = 0xe6, -- banana
0xe7, -- banana banana08 = 0xe7, -- banana
0xe8, -- banana banana09 = 0xe8, -- banana
0xe9, -- banana banana10 = 0xe9, -- banana
0xea, -- banana banana11 = 0xea, -- banana
0xeb, -- banana banana12 = 0xeb, -- banana
0xec, -- banana banana13 = 0xec, -- banana
0xed, -- banana banana14 = 0xed, -- banana
0xee, -- banana banana15 = 0xee, -- banana
} }
-- Currently not used. -- Currently not used.
@ -130,14 +144,14 @@ _M.ExtBadSprites = {
} }
function _M.InitExtSpriteList() function _M.InitExtSpriteList()
for i=1,#_M.ExtGoodSprites,1 do for k,v in pairs(_M.ExtGoodSprites) do
_M.extSprites[_M.ExtGoodSprites[i]] = 1 _M.extSprites[v] = 1
end end
for i=1,#_M.ExtBadSprites,1 do for k,v in pairs(_M.ExtBadSprites) do
_M.extSprites[_M.ExtBadSprites[i]] = -1 _M.extSprites[v] = -1
end end
for i=1,#_M.ExtNeutralSprites,1 do for k,v in pairs(_M.ExtNeutralSprites) do
_M.extSprites[_M.ExtNeutralSprites[i]] = 0 _M.extSprites[v] = 0
end end
end end

View file

@ -1,6 +1,18 @@
print(string.hex(bit.compose(0xef, 0xbe))) local memory, movie, utime, callback, set_timer_timeout = memory, movie, utime, callback, set_timer_timeout
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
local game = dofile(base.."/game.lua")
function on_input() local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
end local Promise = dofile(base.."/promise.lua")
callback.register('timer', function()
Promise.update()
set_timer_timeout(1)
end)
set_timer_timeout(1)
local game = dofile(base.."/game.lua")(Promise)
local util = dofile(base.."/util.lua")(Promise)
game.registerHandlers()
game.findPreferredExit():next(function(exit)
io.stderr:write(util.table_to_string(exit))
io.stderr:write('\n')
end)

View file

@ -9,7 +9,7 @@ print(warn)
local util = dofile(base.."/util.lua")() local util = dofile(base.."/util.lua")()
local mem = dofile(base.."/mem.lua") local mem = dofile(base.."/mem.lua")
local spritelist = dofile(base.."/spritelist.lua") local spritelist = dofile(base.."/spritelist.lua")
local game = dofile(base.."/game.lua") local game = dofile(base.."/game.lua")()
local config = dofile(base.."/config.lua") local config = dofile(base.."/config.lua")
spritelist.InitSpriteList() spritelist.InitSpriteList()