Find the exits by moving the characters and camera in a grid pattern
This commit is contained in:
parent
529f5dc67a
commit
52bdebfc81
10 changed files with 365 additions and 185 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
*.lsmv
|
||||
!pool/*.lsmv
|
||||
*.log
|
||||
catchem/
|
||||
state/
|
||||
|
|
|
@ -54,6 +54,7 @@ Located at [tools/bsnes-launcher.lua](tools/bsnes-launcher.lua), this script giv
|
|||
* [Serpent](https://github.com/pkulchenko/serpent)
|
||||
* [LibDeflate](https://github.com/SafeteeWoW/LibDeflate)
|
||||
* [watchexec](https://github.com/watchexec/watchexec/blob/main/LICENSE)
|
||||
* [Billiam's Promise library](https://github.com/Billiam/promise.lua)
|
||||
|
||||
## TODO
|
||||
|
||||
|
|
171
game.lua
171
game.lua
|
@ -1,12 +1,14 @@
|
|||
--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 Promise = nil
|
||||
|
||||
local util = nil
|
||||
local mathFunctions = dofile(base.."/mathFunctions.lua")
|
||||
local config = dofile(base.."/config.lua")
|
||||
local spritelist = dofile(base.."/spritelist.lua")
|
||||
local util = dofile(base.."/util.lua")()
|
||||
local mem = dofile(base.."/mem.lua")
|
||||
local _M = {
|
||||
leader = 0,
|
||||
|
@ -39,6 +41,27 @@ function _M.getPositions()
|
|||
_M.screenY = (_M.partyY-256-_M.cameraY)*2
|
||||
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()
|
||||
local bananas = memory.readword(0x7e08bc)
|
||||
return bananas
|
||||
|
@ -54,6 +77,126 @@ function _M.getKremCoins()
|
|||
return krem
|
||||
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()
|
||||
local sprites = _M.getSprites()
|
||||
for i=1,#sprites,1 do
|
||||
|
@ -454,7 +597,16 @@ local function registerHandler(space, regname, addr, callback)
|
|||
})
|
||||
end
|
||||
|
||||
local inputHandler = nil
|
||||
local setRewindHandler = nil
|
||||
local rewindHandler = nil
|
||||
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
|
||||
local handler = table.remove(handlers, i)
|
||||
handler.unregisterFn(handler.space, handler.addr, handler.fn)
|
||||
|
@ -462,6 +614,13 @@ function _M.unregisterHandlers()
|
|||
end
|
||||
|
||||
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.WRAM, 'registerread', 0x06b1, processMapLoad)
|
||||
for i=2,22,1 do
|
||||
|
@ -469,4 +628,10 @@ function _M.registerHandlers()
|
|||
end
|
||||
end
|
||||
|
||||
return _M
|
||||
return function(promise)
|
||||
Promise = promise
|
||||
if util == nil then
|
||||
util = dofile(base.."/util.lua")(Promise)
|
||||
end
|
||||
return _M
|
||||
end
|
8
mem.lua
8
mem.lua
|
@ -6,8 +6,16 @@ local _M = {
|
|||
verticalPointer = 0xc414,
|
||||
tiledataPointer = 0x7e0098,
|
||||
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,
|
||||
cameraY = 0x7e17c0,
|
||||
cameraX2 = 0x7e0ad7,
|
||||
cameraY2 = 0x7e0adb,
|
||||
leadChar = 0x7e08a4,
|
||||
partyX = 0x7e0a2a,
|
||||
partyY = 0x7e0a2c,
|
||||
|
|
|
@ -169,5 +169,6 @@ outFile:close()
|
|||
print(string.format('Wrote init to output at %d', ts))
|
||||
|
||||
waiter:next(waitLoop):catch(function(error)
|
||||
print('ERROR: '..error)
|
||||
print('Runner process error: '..error)
|
||||
io.stderr:write('Runner process error: '..error..'\n')
|
||||
end)
|
||||
|
|
|
@ -102,7 +102,9 @@ end
|
|||
return function(promise)
|
||||
-- FIXME Should this be a global???
|
||||
Promise = promise
|
||||
if util == nil then
|
||||
util = dofile(base.."/util.lua")(Promise)
|
||||
end
|
||||
-- FIXME Maybe don't do this in the "constructor"?
|
||||
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')
|
||||
|
|
145
runner.lua
145
runner.lua
|
@ -5,7 +5,7 @@ local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
|||
local Promise = nil
|
||||
|
||||
local config = dofile(base.."/config.lua")
|
||||
local game = dofile(base.."/game.lua")
|
||||
local game = nil
|
||||
local mathFunctions = dofile(base.."/mathFunctions.lua")
|
||||
local util = dofile(base.."/util.lua")()
|
||||
|
||||
|
@ -216,18 +216,6 @@ local function displayGenome(genome)
|
|||
gui.renderctx.setnull()
|
||||
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 buttonCtx = gui.renderctx.new(500, 70)
|
||||
local function displayButtons(_M)
|
||||
|
@ -287,9 +275,10 @@ local function displayForm(_M)
|
|||
gui.rectangle(0, 0, 500, guiHeight, 1, 0x00ffffff, 0xbb000000)
|
||||
--gui.circle(game.screenX-84, game.screenY-84, 192 / 2, 1, 0x50000000)
|
||||
|
||||
local rightmost = _M.rightmost[_M.currentArea]
|
||||
if rightmost == nil then
|
||||
rightmost = 0
|
||||
local areaInfo = _M.areaInfo[_M.currentArea]
|
||||
local distanceTraversed = 0
|
||||
if areaInfo ~= nil then
|
||||
distanceTraversed = areaInfo.startDistance - areaInfo.shortest
|
||||
end
|
||||
|
||||
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, 80, "PowerUp: " .. _M.powerUpCounter)
|
||||
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)
|
||||
formCtx:set()
|
||||
|
@ -417,24 +406,6 @@ local function fitnessAlreadyMeasured(_M)
|
|||
return genome.fitness ~= 0
|
||||
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 neuron = {}
|
||||
neuron.incoming = {}
|
||||
|
@ -481,6 +452,13 @@ local function generateNetwork(genome)
|
|||
genome.network = network
|
||||
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)
|
||||
settings.set_speed("turbo")
|
||||
|
@ -493,7 +471,7 @@ local function initializeRun(_M)
|
|||
exec('enable-sound '..enableSound)
|
||||
gui.subframe_update(false)
|
||||
|
||||
return rewind():next(function()
|
||||
return rewind():next(function(preferredExit)
|
||||
if config.StartPowerup ~= nil then
|
||||
game.writePowerup(config.StartPowerup)
|
||||
end
|
||||
|
@ -520,18 +498,49 @@ local function initializeRun(_M)
|
|||
_M.powerUpBefore = game.getBoth()
|
||||
_M.currentArea = game.getCurrentArea()
|
||||
_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]
|
||||
generateNetwork(genome)
|
||||
evaluateCurrent(_M)
|
||||
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)
|
||||
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
|
||||
message(_M, string.format("We missed %d frames", frame - lastFrame), 0x00990000)
|
||||
message(_M, string.format("We missed %d frames", frame - lastFrame), 0x00ff0000)
|
||||
end
|
||||
lastFrame = frame
|
||||
|
||||
|
@ -566,37 +575,16 @@ local function mainLoop(_M, genome)
|
|||
end
|
||||
end
|
||||
|
||||
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
|
||||
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
|
||||
local areaInfo = _M.areaInfo[_M.currentArea]
|
||||
if areaInfo ~= nil then
|
||||
local exitDist = math.floor(math.sqrt((areaInfo.preferredExit.y - game.partyY) ^ 2 + (areaInfo.preferredExit.x - game.partyX) ^ 2))
|
||||
if exitDist < areaInfo.shortest then
|
||||
areaInfo.shortest = exitDist
|
||||
if _M.timeout < timeoutConst then
|
||||
_M.timeout = timeoutConst
|
||||
end
|
||||
end
|
||||
end
|
||||
-- FIXME Measure distance to target / area exit
|
||||
-- We might not always be horizontal
|
||||
|
||||
local hitTimer = game.getHitTimer(_M.lastBoth)
|
||||
|
||||
|
@ -651,20 +639,9 @@ local function mainLoop(_M, genome)
|
|||
local bumpPenalty = _M.bumps * 100
|
||||
local powerUpBonus = _M.powerUpCounter * 100
|
||||
|
||||
local most = 0
|
||||
if not game.vertical then
|
||||
for k,v in pairs(_M.rightmost) do
|
||||
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 distanceTraversed = getDistanceTraversed(_M.areaInfo) - _M.currentFrame / 2
|
||||
|
||||
local fitness = bananaCoinsFitness - bumpPenalty - hitPenalty + powerUpBonus + most + game.getJumpHeight() / 100
|
||||
local fitness = bananaCoinsFitness - bumpPenalty - hitPenalty + powerUpBonus + distanceTraversed + game.getJumpHeight() / 100
|
||||
|
||||
local lives = game.getLives()
|
||||
|
||||
|
@ -855,13 +832,11 @@ local function run(_M, species, generationIdx, genomeCallback)
|
|||
register(_M, 'input', function()
|
||||
frame = frame + 1
|
||||
updateController()
|
||||
processFrameAdvanced(_M)
|
||||
saveLoadInput(_M)
|
||||
end)
|
||||
register(_M, 'keyhook', function(key, state)
|
||||
keyhook(_M, key, state)
|
||||
end)
|
||||
register(_M, 'post_rewind', rewound)
|
||||
|
||||
input.keyhook("1", true)
|
||||
input.keyhook("4", true)
|
||||
|
@ -885,6 +860,9 @@ end
|
|||
|
||||
return function(promise)
|
||||
Promise = promise
|
||||
if game == nil then
|
||||
game = dofile(base.."/game.lua")(Promise)
|
||||
end
|
||||
local _M = {
|
||||
currentGenerationIndex = 1,
|
||||
currentSpecies = nil,
|
||||
|
@ -912,16 +890,13 @@ return function(promise)
|
|||
powerUpBefore = 0,
|
||||
currentArea = 0,
|
||||
lastArea = 0,
|
||||
rightmost = {},
|
||||
upmost = {},
|
||||
areaInfo = {},
|
||||
lastBoth = 0,
|
||||
|
||||
onMessageHandler = {},
|
||||
onSaveHandler = {},
|
||||
onLoadHandler = {},
|
||||
onRenderFormHandler = {},
|
||||
onFrameAdvancedHandler = {},
|
||||
|
||||
}
|
||||
|
||||
_M.onRenderForm = function(handler)
|
||||
|
|
188
spritelist.lua
188
spritelist.lua
|
@ -4,100 +4,114 @@ _M.Sprites = {}
|
|||
|
||||
-- Make sure this list is sorted before initialization.
|
||||
_M.NeutralSprites = {
|
||||
0x0020, -- Krow egg fragments
|
||||
0x0060, -- Barrel fragments
|
||||
0x0064, -- Barrel fragments
|
||||
krowEggFragments = 0x0020,
|
||||
barrelFragments = 0x0060,
|
||||
barrelFragments2 = 0x0064,
|
||||
|
||||
-- Our heroes
|
||||
0x00e4, -- Diddy
|
||||
0x00e8, -- Dixie
|
||||
0x0100, -- Stars
|
||||
diddy = 0x00e4,
|
||||
dixie = 0x00e8,
|
||||
stars = 0x0100,
|
||||
|
||||
-- Items that require too much interaction
|
||||
0x01a4, -- Barrel
|
||||
0x01b0, -- Cannonball (immobile)
|
||||
0x01c0, -- Chest
|
||||
0x01bc, -- Small crate
|
||||
0x011c, -- Barrel
|
||||
0x013c, -- Cannon
|
||||
0x014c, -- Hook
|
||||
0x01b8, -- TNT
|
||||
barrel = 0x01a4, -- Barrel
|
||||
cannonball = 0x01b0,
|
||||
chest = 0x01c0,
|
||||
smallCrate = 0x01bc,
|
||||
barrel2 = 0x011c,
|
||||
cannon = 0x013c,
|
||||
hook = 0x014c,
|
||||
tnt = 0x01b8,
|
||||
|
||||
-- Inert
|
||||
0x0168, -- Goal pole
|
||||
0x016c, -- Goal roulette
|
||||
0x0160, -- Goal base
|
||||
0x0164, -- Goal barrel
|
||||
goalPole = 0x0168,
|
||||
goalroulette = 0x016c,
|
||||
goalBase = 0x0160,
|
||||
goalBarrel = 0x0164,
|
||||
|
||||
0x0238, -- Pow
|
||||
0x023c, -- Exploding crate
|
||||
0x0258, -- No Animals Sign
|
||||
pow = 0x0238,
|
||||
explodingCrate = 0x023c,
|
||||
noAnimalsSign = 0x0258,
|
||||
}
|
||||
|
||||
-- Make sure this list is sorted before initialization.
|
||||
_M.GoodSprites = {
|
||||
-- Destinations
|
||||
0x0094, -- Area exit
|
||||
0x00b0, -- Goal target
|
||||
areaExit = 0x0094,
|
||||
goalTarget = 0x00b0,
|
||||
|
||||
0x0120, -- Bonus barrel
|
||||
0x0128, -- Hot air balloon
|
||||
0x0140, -- Launch barrel
|
||||
0x0148, -- Animal crate
|
||||
0x0150, -- Invincibility barrel
|
||||
0x0154, -- Midpoint
|
||||
0x015c, -- Banana Coin/Kremkoin/DK Coin
|
||||
0x0170, -- Banana bunch
|
||||
0x0174, -- KONG letters
|
||||
0x0178, -- xUP balloon
|
||||
bonusBarrel = 0x0120,
|
||||
hotAirBalloon = 0x0128,
|
||||
launchBarrel = 0x0140,
|
||||
animalCrate = 0x0148,
|
||||
invincibilityBarrel = 0x0150,
|
||||
midpoint = 0x0154,
|
||||
allCoins = 0x015c, -- Banana Coin/Kremkoin/DK Coin
|
||||
bananaBunch = 0x0170,
|
||||
kongLetter = 0x0174,
|
||||
upBalloon = 0x0178, -- xUP balloon
|
||||
|
||||
-- Animals
|
||||
0x0190, -- Squitter
|
||||
0x0194, -- Rattly
|
||||
0x0198, -- Squawks
|
||||
0x019c, -- Rambi
|
||||
0x0304, -- Clapper
|
||||
squitter = 0x0190,
|
||||
rattly = 0x0194,
|
||||
squawks = 0x0198,
|
||||
rambi = 0x019c,
|
||||
clapper = 0x0304,
|
||||
|
||||
0x01a8, -- DK Barrel label
|
||||
dkBarrelLabel = 0x01a8,
|
||||
|
||||
0x01b4, -- Krow's eggs
|
||||
krowEgg = 0x01b4,
|
||||
|
||||
0x0220, -- Flitter (used as unavoidable platforms in some levels)
|
||||
0x02d4, -- Krochead (red and green)
|
||||
flitter = 0x0220,
|
||||
krocheadAllColors = 0x02d4,
|
||||
}
|
||||
|
||||
-- Currently not used.
|
||||
_M.BadSprites = {
|
||||
-- Baddies
|
||||
0x006c, -- Kannon
|
||||
0x01ac, -- Klobber (yellow and green)
|
||||
0x01d0, -- Kannon's fodder (Ball/barrel)
|
||||
0x01d8, -- Krusha
|
||||
0x01dc, -- Click-Clack
|
||||
0x01e4, -- Neek
|
||||
0x01ec, -- Klomp
|
||||
0x01e8, -- Klobber (awake)
|
||||
0x01f0, -- Klampon
|
||||
0x01f8, -- Flotsam
|
||||
0x0200, -- Klinger
|
||||
0x0208, -- Puftup
|
||||
0x0218, -- Zinger (red and yellow)
|
||||
0x0214, -- Mini-Necky
|
||||
0x020c, -- Lockjaw
|
||||
0x021c, -- Kaboing
|
||||
0x0224, -- Krow (Boss)
|
||||
0x025c, -- Krook (very large)
|
||||
kannon = 0x006c,
|
||||
klobberAllColors = 0x01ac,
|
||||
kannonFodder = 0x01d0,
|
||||
krusha = 0x01d8,
|
||||
clickClack = 0x01dc,
|
||||
neek = 0x01e4,
|
||||
klomp = 0x01ec,
|
||||
klobberAwake = 0x01e8,
|
||||
klampon = 0x01f0,
|
||||
flotsam = 0x01f8,
|
||||
klinger = 0x0200,
|
||||
puftup = 0x0208,
|
||||
zingerAllColors = 0x0218,
|
||||
miniNecky = 0x0214,
|
||||
lockjaw = 0x020c,
|
||||
kaboing = 0x021c,
|
||||
krow = 0x0224, -- Boss
|
||||
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()
|
||||
for i=1,#_M.GoodSprites,1 do
|
||||
_M.Sprites[_M.GoodSprites[i]] = 1
|
||||
for k,v in pairs(_M.GoodSprites) do
|
||||
_M.extSprites[v] = 1
|
||||
end
|
||||
for i=1,#_M.BadSprites,1 do
|
||||
_M.Sprites[_M.BadSprites[i]] = -1
|
||||
for k,v in pairs(_M.BadSprites) do
|
||||
_M.extSprites[v] = -1
|
||||
end
|
||||
for i=1,#_M.NeutralSprites,1 do
|
||||
_M.Sprites[_M.NeutralSprites[i]] = 0
|
||||
for k,v in pairs(_M.NeutralSprites) do
|
||||
_M.extSprites[v] = 0
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -108,21 +122,21 @@ _M.ExtNeutralSprites = {
|
|||
}
|
||||
|
||||
_M.ExtGoodSprites = {
|
||||
0xe0, -- banana
|
||||
0xe1, -- banana
|
||||
0xe2, -- banana
|
||||
0xe3, -- banana
|
||||
0xe4, -- banana
|
||||
0xe5, -- banana
|
||||
0xe6, -- banana
|
||||
0xe7, -- banana
|
||||
0xe8, -- banana
|
||||
0xe9, -- banana
|
||||
0xea, -- banana
|
||||
0xeb, -- banana
|
||||
0xec, -- banana
|
||||
0xed, -- banana
|
||||
0xee, -- banana
|
||||
banana01 = 0xe0, -- banana
|
||||
banana02 = 0xe1, -- banana
|
||||
banana03 = 0xe2, -- banana
|
||||
banana04 = 0xe3, -- banana
|
||||
banana05 = 0xe4, -- banana
|
||||
banana06 = 0xe5, -- banana
|
||||
banana07 = 0xe6, -- banana
|
||||
banana08 = 0xe7, -- banana
|
||||
banana09 = 0xe8, -- banana
|
||||
banana10 = 0xe9, -- banana
|
||||
banana11 = 0xea, -- banana
|
||||
banana12 = 0xeb, -- banana
|
||||
banana13 = 0xec, -- banana
|
||||
banana14 = 0xed, -- banana
|
||||
banana15 = 0xee, -- banana
|
||||
}
|
||||
|
||||
-- Currently not used.
|
||||
|
@ -130,14 +144,14 @@ _M.ExtBadSprites = {
|
|||
}
|
||||
|
||||
function _M.InitExtSpriteList()
|
||||
for i=1,#_M.ExtGoodSprites,1 do
|
||||
_M.extSprites[_M.ExtGoodSprites[i]] = 1
|
||||
for k,v in pairs(_M.ExtGoodSprites) do
|
||||
_M.extSprites[v] = 1
|
||||
end
|
||||
for i=1,#_M.ExtBadSprites,1 do
|
||||
_M.extSprites[_M.ExtBadSprites[i]] = -1
|
||||
for k,v in pairs(_M.ExtBadSprites) do
|
||||
_M.extSprites[v] = -1
|
||||
end
|
||||
for i=1,#_M.ExtNeutralSprites,1 do
|
||||
_M.extSprites[_M.ExtNeutralSprites[i]] = 0
|
||||
for k,v in pairs(_M.ExtNeutralSprites) do
|
||||
_M.extSprites[v] = 0
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
print(string.hex(bit.compose(0xef, 0xbe)))
|
||||
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
||||
local game = dofile(base.."/game.lua")
|
||||
local memory, movie, utime, callback, set_timer_timeout = memory, movie, utime, callback, set_timer_timeout
|
||||
|
||||
function on_input()
|
||||
end
|
||||
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
||||
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)
|
|
@ -9,7 +9,7 @@ print(warn)
|
|||
local util = dofile(base.."/util.lua")()
|
||||
local mem = dofile(base.."/mem.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")
|
||||
|
||||
spritelist.InitSpriteList()
|
||||
|
|
Loading…
Add table
Reference in a new issue