--------------------------------------------------------------------------------
-- Daffy Duck - the Marvin Missions --
-- --
-- Player & enemy data display --
-- By: Scepheo --
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- Global constants --
--------------------------------------------------------------------------------
local ScreenWidth = 160
local ScreenHeight = 144
HelpOn = true
EnemiesOn = false
MapOn = false
PlayerOn = true
--------------------------------------------------------------------------------
-- Objects --
--------------------------------------------------------------------------------
Weapons = {
"Original",
"Rapid Fire",
"Laser",
"Bouncing",
"Big Bullet"
}
function GetPlayer()
local _player = {}
memory.usememorydomain("HRAM")
_player.X = memory.read_u16_be(0x10)
_player.Y = memory.read_u16_be(0x12)
_player.ScreenX = memory.read_u8(0x14)
_player.ScreenY = memory.read_u8(0x15)
_player.Lives = memory.read_u8(0x18)
_player.HP = memory.read_u8(0x19)
_player.SpeedX = memory.read_s8(0x1B)
_player.SpeedY = memory.read_s8(0x1C)
_player.Jet = 19 - memory.read_u8(0x1E)
-- TODO: find a better place for this
_player.Tileset = memory.read_u8(0x3B)
memory.usememorydomain("WRAM")
local score = memory.read_u8(0x00CF)
_player.Score = bit.rshift(score, 4) * 10 + bit.band(score, 15)
-- US
--_player.Weapon = Weapons[memory.read_u8(0x1009) + 1]
-- EU
_player.Weapon = Weapons[memory.read_u8(0x1012) + 1]
return _player
end
function GetMap()
local _map = {}
memory.usememorydomain("HRAM")
_map.Length = memory.read_u8(0x0028)
_map.Horizontal = (memory.read_u8(0x0029) == 1)
return _map
end
function GetTile(map, x, y)
if (map.Horizontal) then
return memory.read_u8(0x300 + x * 8 + y)
else
return memory.read_u8(0x300 + y * 10 + x)
end
end
function GetEnemyAt(address)
local _enemy = {}
-- Uncollected ???
-- Killed
-- ???
-- Falling ???
-- Collectable
-- Platform
-- ???
-- Direction
-- 00 = 0000 0000 = Off screen (died)
-- A0 = 1010 0000 = Normal dog
-- C4 = 1100 0100 = Jumped dog
--
-- 89 = 1000 1001 = Normal heart
-- 09 = 0000 1001 = Collected heart
-- 81 = 1000 0001 = Normal turret (l&r)
-- C1 = 1100 0001 = Shot turret (r) / Shot dog
-- C0 = 1100 0000 = Shot turret (l) / Shot alarm
-- 80 = 1000 0000 = Normal alarm / Marvin / Egg
-- C5 = 1100 0101 = Jumped turret
-- 84 = 1000 0100 = Normal platform / Normal "float"
-- A4 = 1010 0100 = Used platform
-- C4 = 1100 0100 = Shot "float"
-- A1 = 1010 0001 = Normal raptor
-- 91 = 1001 0001 = Rock
-- 90 = 1001 0000 = Boss
-- 11 = 0001 0001 = Fallen rock
_enemy.State = memory.read_u8(address + 0x0)
_enemy.Active = bit.band(_enemy.State, 0x80) == 0x80
_enemy.Id = memory.read_u8(address + 0x1)
_enemy.X = memory.read_u16_be(address + 0x2)
_enemy.Y = memory.read_u16_be(address + 0x4)
_enemy.SpeedX = memory.read_s8(address + 0x8)
_enemy.SpeedY = memory.read_s8(address + 0x9)
_enemy.Invincibility = memory.read_u8(address + 0xA)
_enemy.HP = memory.read_u8(address + 0xB) + 1
_enemy.ScreenX = memory.read_u8(address + 0xD)
_enemy.ScreenY = memory.read_u8(address + 0xE)
_enemy.Number = (address - 0x1F00) / 16
return _enemy
end
function GetEnemyList()
local _list = {}
_list.Length = 0
memory.usememorydomain("WRAM")
for address = 0x1F00, 0x1FF0, 16 do
local enemy = GetEnemyAt(address)
_list[_list.Length] = enemy
_list.Length = _list.Length + 1
end
return _list
end
--------------------------------------------------------------------------------
-- Map drawing --
--------------------------------------------------------------------------------
function DrawMap()
local dx = Player.ScreenX - 8
local dy = Player.ScreenY - 15
local ScreenX = math.max(math.floor(Player.X / 16 - dx), 0)
local ScreenY = math.max(math.floor(Player.Y / 16 - dy), 0)
local offSetX = -(ScreenX % 16)
local offSetY = -(ScreenY % 16)
local xStart = math.floor(ScreenX / 16)
local yStart = math.floor(ScreenY / 16)
memory.usememorydomain("WRAM")
for x = 0, 10 do
for y = 0, 9 do
DrawTile(xStart + x, yStart + y, offSetX + x * 16, offSetY + y * 16)
end
end
end
function DrawTile(xMap, yMap, xDraw, yDraw)
if (yDraw >= 128) then
return
end
local tile = GetTile(Map, xMap, yMap)
local tileType = GetTileType(tile)
local color
if (tileType == FreeTile) then
color = 0x0000FF
elseif (tileType == WallTile) then
color = 0x00FF00
elseif (tileType == DoorTile) then
color = 0xFFFF00
elseif (tileType == KillTile) then
color = 0xFF0000
elseif (tileType == WarpTile) then
color = 0xFF00FF
end
local foreColor = bit.bor(color, 0x99000000)
local backColor = bit.bor(color, 0x66000000)
gui.drawRectangle(xDraw, yDraw, 16, 16, foreColor, backColor)
text(xDraw + 2, yDraw + 2, string.format("%X", tile))
end
FreeTile = 0
WallTile = 1
KillTile = 2
DoorTile = 3
WarpTile = 4
function GetTileType(tile)
if (Player.Tileset == 2) then
if (tile <= 60) then
return FreeTile
elseif (tile <= 76) then
return WallTile
elseif (tile <= 78) then
return DoorTile
else
return WarpTile
end
elseif (Player.Tileset == 3) then
if (tile <= 46) then
return FreeTile
elseif (tile <= 92) then
return WallTile
elseif (tile <= 95) then
return KillTile
elseif (tile <= 97) then
return DoorTile
else
return WarpTile
end
elseif (Player.Tileset == 4) then
if (tile <= 82) then
return FreeTile
elseif (tile <= 95) then
return WallTile
elseif (tile <= 97) then
return DoorTile
else
return WarpTile
end
elseif (Player.Tileset == 5) then
if (tile <= 60) then
return FreeTile
elseif (tile <= 79) then
return WallTile
elseif (tile <= 81) then
return KillTile
elseif (tile <= 97) then
return DoorTile
else
return WarpTile
end
end
return FreeTile
end
--------------------------------------------------------------------------------
-- GUI text drawing --
--------------------------------------------------------------------------------
local xScale, yScale, xOffset, yOffset
function Scale()
xOffset = client.borderwidth()
yOffset = client.borderheight()
console.log('Offset: '..xOffset..'x'..yOffset)
console.log('Buffer: '..client.bufferwidth()..'x'..client.bufferheight())
console.log('Client: '..client.screenwidth()..'x'..client.screenheight())
xScale = client.bufferwidth() / ScreenWidth
yScale = client.bufferheight() / ScreenHeight
console.log('Scale: '..xScale..'x'..yScale)
end
function text(x, y, text)
gui.text(xOffset + x * xScale, yOffset + y * yScale, text, 0xFF000000, 0xFFFFFFFF)
end
function head(x, y, text)
gui.text(xOffset + x * xScale, yOffset + y * yScale, text, 0xFF000000, 0xFF00FF00)
end
--------------------------------------------------------------------------------
-- Player data (over HUD) --
--------------------------------------------------------------------------------
function DrawHUD()
gui.drawBox(0, 128, 160, 144, 0xFF000000, 0xFF000000)
head(000, 129, "Player")
text(000, 134, "HP: " .. Player.HP)
text(000, 139, "Lives: " .. Player.Lives)
text(024, 134, "Jet: " .. Player.Jet)
text(024, 139, "Score: " .. Player.Score)
head(048, 129, "Pos")
text(048, 134, "X: " .. Player.X)
text(048, 139, "Y: " .. Player.Y)
head(072, 129, "Speed")
text(072, 134, "X: " .. Player.SpeedX)
text(072, 139, "Y: " .. Player.SpeedY)
head(096, 129, "Global")
text(096, 134, "Frame: " .. emu.framecount())
local weaponText = Player.Weapon or "---"
text(096, 139, "Weapon: " .. weaponText)
end
--------------------------------------------------------------------------------
-- Camhack --
--------------------------------------------------------------------------------
function CamHack()
-- Simple overwrite of cam limit
memory.usememorydomain("HRAM")
memory.writebyte(0x0028, 255)
-- Note that this does not allow for horizontal scrolling in vertical levels
-- or vice-versa. I still need to find a way to achieve that.
end
--------------------------------------------------------------------------------
-- Enemy data --
--------------------------------------------------------------------------------
function DrawEnemies()
for i = 0, EnemyList.Length - 1 do
if (EnemyList[i].Active) then
local x = EnemyList[i].ScreenX - 16
local y = EnemyList[i].ScreenY - 24
gui.drawRectangle(x, y, 16, 16, 0x80AA0000, 0x80AA0000)
text(x, y + 4, "HP:" .. EnemyList[i].HP)
text(x, y + 8, "X:" .. EnemyList[i].X)
text(x, y + 12, "Y:" .. EnemyList[i].Y)
text(x, y, EnemyList[i].Number)
--text(x, y , "HP:" .. EnemyList[i].HP)
--text(x, y + 4 , "Inv:" .. EnemyList[i].Invincibility)
--text(x, y + 8 , "X:" .. EnemyList[i].X)
--text(x, y + 12, "Y:" .. EnemyList[i].Y)
end
end
end
--------------------------------------------------------------------------------
-- Interface --
--------------------------------------------------------------------------------
function DrawHelp()
gui.text(8, 8, "Shortcut help")
local y = 12
for name in pairs(Input) do
if (Input[name].On) then
gui.text(8, y, Input[name].Key .. " - " .. name)
else
gui.text(8, y, Input[name].Key .. " - " .. name, 0xFF000000, 0xFFAAAAAA)
end
y = y + 16
end
end
--------------------------------------------------------------------------------
-- Input --
--------------------------------------------------------------------------------
Input = {}
Input["Help"] = {}
Input["Help"].Key = "H"
Input["Help"].Bind = DrawHelp
Input["Help"].On = false
Input["Enemies"] = {}
Input["Enemies"].Key = "E"
Input["Enemies"].Bind = DrawEnemies
Input["Enemies"].On = true
Input["Map"] = {}
Input["Map"].Key = "M"
Input["Map"].Bind = DrawMap
Input["Map"].On = true
Input["Player"] = {}
Input["Player"].Key = "S"
Input["Player"].Bind = DrawHUD
Input["Player"].On = true
lastInput = input.get()
function HandleInput()
local input = input.get()
for name, data in pairs(Input) do
if (input[data.Key] and not lastInput[data.Key]) then
Input[name].On = not Input[name].On
end
end
lastInput = input
end
--------------------------------------------------------------------------------
-- Main loop --
--------------------------------------------------------------------------------
while true do
Scale()
--CamHack()
Player = GetPlayer()
Map = GetMap()
EnemyList = GetEnemyList()
HandleInput()
for name in pairs(Input) do
if (Input[name].On) then
Input[name].Bind()
end
end
emu.frameadvance()
end