--[[ Leeland Kirwan (FatRatKnight), 2014
Allows user input to "mix" with TAS Editor using XOR logic.
If Recording is checked, this script will do its intended job.
Tap a P1 key to toggle input. If "All" is selected, will use other player keys
for related player. If an individual player is selected, uses P1 controls. You
can hold down a key to affect multiple frames, toggling the key on each frame.
For reference, Superimpose uses OR logic, and does not allow user input to
clear stored input. A great inconvience for those wishing to use the keyboard
to toggle individual input rather than mouse.
]]--
local btnstr= {A = "A", B = "B", select= "S", start= "T",
up= "U", down= "D", left = "L", right= "R"}
local btnval= {A = 0x01, B = 0x02, select= 0x04, start= 0x08,
up= 0x10, down= 0x20, left = 0x40, right= 0x80}
local plval= {["1P"]= 1, ["2P"]= 2, ["3P"]= 3, ["4P"]= 4}
local OldJoy= {{},{},{},{}}
--*****************************************************************************
local function InputToggle()
--*****************************************************************************
--Needs readonly mode to work, but won't interfere too much otherwise.
--Toggles inputs recorded in TAS Editor.
if not taseditor.engaged() then return end -- Escape
taseditor.clearinputchanges() -- Guessing something horrible w/o this.
local frame= emu.framecount()
local plSel= plval[taseditor.getrecordermode()]
local plLow = plSel or 1 -- Get the for loop range!
local plHigh= plSel or 4 -- Hardcoded 4 doesn't appear harmful...
local P1joy= nil -- If one player, get P1.
if plSel then P1joy= joypad.getimmediate(1) end -- Otherwise, get all.
local changed= false -- Flag, so I don't interfere too much
local History= "" -- Information for TASer in History list
for pl= plLow, plHigh do
local pad= taseditor.getinput(frame,pl)
local joy= P1joy or joypad.getimmediate(pl) -- Pick out the joypad
local mask= 0 -- Convert messy table to numbers
for btn,pressed in pairs(joy) do
if pressed and not OldJoy[pl][btn] then
mask= bit.bor(mask,btnval[btn])
History= History .. pl .. btnstr[btn]
end
end
taseditor.submitinputchange(frame,pl,bit.bxor(pad,mask))
OldJoy[pl]= joy -- To avoid repeated triggers
changed= changed or (mask ~= 0) -- If the bitmask did something, apply!
end
if changed then taseditor.applyinputchanges(History) end
end
taseditor.registerauto(InputToggle)
--#############################################################################
--#############################################################################
--My own functions for number display. FCEUX default text isn't liked by me.
local Keys, LastKeys= {}, {}
local function UpdateKeys() LastKeys= Keys; Keys= input.get() end
local function Press(k) return Keys[k] and not LastKeys[k] end
--*****************************************************************************
local Draw= {}
--*****************************************************************************
--Coordinates is the top-left pixel of the 3x5 digit.
--Used for drawing compact, colored numbers.
local Px,Li= gui.pixel, gui.line
Draw[0]= function(x,y,c) -- ###
Li(x ,y ,x ,y+4,c)-- # #
Li(x+2,y ,x+2,y+4,c)-- # #
Px(x+1,y ,c) -- # #
Px(x+1,y+4,c) -- ###
end
Draw[1]= function(x,y,c) -- #
Li(x ,y+4,x+2,y+4,c)-- ##
Li(x+1,y ,x+1,y+3,c)-- #
Px(x ,y+1,c) -- #
end -- ###
Draw[2]= function(x,y,c) -- ###
Li(x ,y ,x+2,y ,c)-- #
Li(x ,y+3,x+2,y+1,c)-- ###
Li(x ,y+4,x+2,y+4,c)-- #
Px(x ,y+2,c) -- ###
Px(x+2,y+2,c)
end
Draw[3]= function(x,y,c) -- ###
Li(x ,y ,x+1,y ,c)-- #
Li(x ,y+2,x+1,y+2,c)-- ###
Li(x ,y+4,x+1,y+4,c)-- #
Li(x+2,y ,x+2,y+4,c)-- ###
end
Draw[4]= function(x,y,c) -- # #
Li(x ,y ,x ,y+2,c)-- # #
Li(x+2,y ,x+2,y+4,c)-- ###
Px(x+1,y+2,c) -- #
end -- #
Draw[5]= function(x,y,c) -- ###
Li(x ,y ,x+2,y ,c)-- #
Li(x ,y+1,x+2,y+3,c)-- ###
Li(x ,y+4,x+2,y+4,c)-- #
Px(x ,y+2,c) -- ###
Px(x+2,y+2,c)
end
Draw[6]= function(x,y,c) -- ###
Li(x ,y ,x+2,y ,c)-- #
Li(x ,y+1,x ,y+4,c)-- ###
Li(x+2,y+2,x+2,y+4,c)-- # #
Px(x+1,y+2,c) -- ###
Px(x+1,y+4,c)
end
-- ###
Draw[7]= function(x,y,c) -- #
Li(x ,y ,x+1,y ,c)-- ##
Li(x+2,y ,x+1,y+4,c)-- #
end -- #
Draw[8]= function(x,y,c) -- ###
Li(x ,y ,x ,y+4,c)-- # #
Li(x+2,y ,x+2,y+4,c)-- ###
Px(x+1,y ,c) -- # #
Px(x+1,y+2,c) -- ###
Px(x+1,y+4,c)
end
Draw[9]= function(x,y,c) -- ###
Li(x ,y ,x ,y+2,c)-- # #
Li(x+2,y ,x+2,y+3,c)-- ###
Li(x ,y+4,x+2,y+4,c)-- #
Px(x+1,y ,c) -- ###
Px(x+1,y+2,c)
end
Draw[10]=function(x,y,c) -- #
Li(x ,y+1,x ,y+4,c)-- # #
Li(x+2,y+1,x+2,y+4,c)-- # #
Px(x+1,y ,c) -- ###
Px(x+1,y+3,c) -- # #
end
Draw[11]=function(x,y,c) -- ##
Li(x ,y ,x ,y+4,c)-- # #
Li(x+1,y ,x+2,y+1,c)-- ##
Li(x+1,y+4,x+2,y+3,c)-- # #
Px(x+1,y+2,c) -- ##
end
Draw[12]=function(x,y,c) -- #
Li(x ,y+1,x ,y+3,c)-- # #
Li(x+1,y ,x+2,y+1,c)-- #
Li(x+1,y+4,x+2,y+3,c)-- # #
end -- #
Draw[13]=function(x,y,c) -- ##
Li(x ,y ,x ,y+4,c)-- # #
Li(x+2,y+1,x+2,y+3,c)-- # #
Px(x+1,y ,c) -- # #
Px(x+1,y+4,c) -- ##
end
Draw[14]=function(x,y,c) -- ###
Li(x ,y ,x ,y+4,c)-- #
Li(x+1,y ,x+2,y ,c)-- ##
Li(x+1,y+4,x+2,y+4,c)-- #
Px(x+1,y+2,c) -- ###
end
Draw[15]=function(x,y,c) -- ###
Li(x ,y ,x ,y+4,c)-- #
Li(x+1,y ,x+2,y ,c)-- ##
Px(x+1,y+2,c) -- #
end -- #
--*****************************************************************************
local function __DN_AnyBase(right, y, Number, c, bkgnd, div)
--*****************************************************************************
-- Works with any base from 2 to 16. Paints the input number.
-- Returns the x position where it would paint another digit.
-- It only works with integers. Rounds fractions toward zero.
if div < 2 then return end -- Prevents the function from never returning.
local Digit= {}
local Negative= false
if Number < 0 then
Number= -Number
Negative= true
end
Number= math.floor(Number)
c= c or "white"
bkgnd= bkgnd or "clear"
local i= 0
if Number < 1 then
Digit[1]= 0
i= 1
end
while (Number >= 1) do
i= i+1
Digit[i]= Number % div
Number= math.floor(Number/div)
end
if Negative then i= i+1 end
local x= right - i*4
gui.box(x+1, y-1, right+1, y+5,bkgnd,bkgnd)
i= 1
while Draw[Digit[i]] do
Draw[Digit[i]](right-2,y,c)
right= right-4
i=i+1
end
if Negative then
gui.line(right, y+2,right-2,y+2,c)
right= right-4
end
return right
end
--*****************************************************************************
local function DrawNum(right, y, Number, c, bkgnd)
--*****************************************************************************
-- Paints the input number as right-aligned. Decimal version.
return __DN_AnyBase(right, y, Number, c, bkgnd, 10)
end
--*****************************************************************************
local function DrawNumx(right, y, Number, c, bkgnd)
--*****************************************************************************
-- Paints the input number as right-aligned. Hexadecimal version.
return __DN_AnyBase(right, y, Number, c, bkgnd, 16)
end
--#############################################################################
--#############################################################################
--Renaming things to my desires
local R1u= memory.readbyte
local R1s= memory.readbytesigned
local R2u= memory.readword
local R2s= memory.readwordsigned
local R1U= rom.readbyte
--#############################################################################
--#############################################################################
--Debug registry
--*****************************************************************************
local function DetectChange()
--*****************************************************************************
--For when the game shifts to another "world".
--Like going into a town or a palace, or a different part of the continent.
print("Changed at frame " .. emu.framecount())
end
--memory.registerwrite(0x6A00,DetectChange)
--*****************************************************************************
local function FnHit()
--*****************************************************************************
print(string.format("%02X %02X %02X:%02X - Zone",
R1u(0x0706),
R1u(0x0707),
R1u(0x070A),
R1u(0x056C)
))
end
memory.registerexec(0xCD40,FnHit)
--C2E6 is called every frame. Arr...
--CD40 is exclusive to "world" shifting. So, what calls it?
--#############################################################################
--#############################################################################
--Keyhook functions
--*****************************************************************************
local function PrintExits()
--*****************************************************************************
for i= 0, 0x3E do
print(string.format("%02X: %3d %3d . %3d %3d : %02X.%d %02X.%d %02X.%d %02X.%d",
i,
R1u(0x6A00+i),
R1u(0x6A3F+i),
R1u(0x6A7E+i),
R1u(0x6ABD+i),
math.floor(R1u(0x6AFC+i*4)/4),R1u(0x6AFC+i*4)%4,
math.floor(R1u(0x6AFD+i*4)/4),R1u(0x6AFD+i*4)%4,
math.floor(R1u(0x6AFE+i*4)/4),R1u(0x6AFE+i*4)%4,
math.floor(R1u(0x6AFF+i*4)/4),R1u(0x6AFF+i*4)%4
))
end
end
--#############################################################################
--#############################################################################
--Main
--*****************************************************************************
local function StatusHUD()
--*****************************************************************************
--Player info. Magic, health, position, velocity, you know.
local v, clr
--Coordinates
DrawNum( 16, 9,R2s(0x004D,0x003B),"white","black")
DrawNumx(24, 9,R1u(0x03D6),"grey","black")
DrawNumx(36, 9,R1s(0x0070),"cyan","black")
DrawNum( 16, 15,R2s(0x0029,0x0019),"white","black")
DrawNumx(24, 15,R1u(0x03E6),"grey","black")
DrawNumx(36, 15,R1s(0x057D),"cyan","black")
--Rooms
v= R1u(0x0561)
DrawNumx(180, 9,v,"cyan","black")
v= 0x6AFC + v*4
DrawNumx(190, 12,R1u(v) /4,"white","black")
DrawNumx(200, 15,R1u(v+1)/4,"white","black")
DrawNumx(200, 9,R1u(v+2)/4,"white","black")
DrawNumx(210, 12,R1u(v+3)/4,"white","black")
--Attack
v= R1u(0x0777)
v= R1U(0x01E66C + 0x10 + v)
DrawNum( 8, 23,v,"orange","black")
--Magic
DrawNum( 44,28,math.floor((R1u(0x0773)+1)/2),"white","black")
v= R1u(0x070C)
if v ~= 0 then DrawNum( 44,34,v,"cyan","black") end
v= R1u(0x0749)*8 + R1u(0x0778) --Spell Selection and Magic Level
v= R1U(0x000D7A + 0x10 + v)/2 --Spell cost table
clr= "orange"
if bit.band(R1u(0x074A),0x04) ~= 0 then clr= "grey" end
DrawNum( 46,18,v,clr,"black")
DrawNum( 54,9,R1u(0x0749),"white","black")
DrawNum( 44,9,R1u(0x074A),"white","black")
--Life
DrawNum(116,28,math.floor((R1u(0x0774)+1)/2),"white","black")
v= R1u(0x070D)
if v ~= 0 then DrawNum(116,34,v,"cyan","black") end
--EXP
end
--*****************************************************************************
local function BoxHUD()
--*****************************************************************************
local CamX= R2u(0x072C,0x072A)
--Link
local AtkPow= R1U(0x01E66C + 0x10 + R1u(0x0777))
local LifeLv= R1u(0x0779)
local Life = R1u(0x0774)
local X= R2u(0x004D,0x003B) - CamX
local Y= R2u(0x0029,0x0019) - 0x100
gui.box(X,Y,X+3,Y+3)
--Baddies
for i= 0, 5 do
if R1u(0x00B6+i) == 1 then
X= R2u(0x004E+i,0x003C+i) - CamX
Y= R2u(0x002A+i,0x001A+i) - 0x100
-- gui.box(X,Y,X+3,Y+3,nil,"cyan")
local hits= math.ceil(R1u(0x00C2+i)/AtkPow)
DrawNum(X,Y,hits,"cyan","black")
local dmg= R1u(0x6DF9 + R1u(0x00A1+i))%8
dmg= R1U(0x01E2AE + 0x10 + dmg*8 + LifeLv)
DrawNum(X,Y+6,dmg/2,"orange","black")
end
end
--Projectiles
for i= 0, 5 do
if R1u(0x0087+i) ~= 0 then
X= R2u(0x0054+i,0x0042+i) - CamX
Y= R2u(0x0030+i,0x0020+i) - 0x100
local dmg= R1u(0x6D17 + R1u(0x0087+i))%8
dmg= R1U(0x01E2AE + 0x10 + dmg*8 + LifeLv)
DrawNum(X,Y,dmg/2,"orange","black")
end
end
--Sword beam
end
--*****************************************************************************
local function WorldHUD()
--*****************************************************************************
--Coordinates
DrawNum( 13, 9,R1u(0x0073),"white","black")
DrawNum( 13, 15,R1u(0x0074),"white","black")
end
--*****************************************************************************
local function BasicHUD()
--*****************************************************************************
UpdateKeys()
if Press("space") then PrintExits() end
local mode= R1u(0x0768)
DrawNumx(9,202,mode,"white","black")
if mode == 6 then
StatusHUD()
BoxHUD()
gui.text(2,210,"Side")
elseif mode == 0 then
WorldHUD()
gui.text(2,210,"World")
else
gui.text(2,210,"What?")
end
DrawNumx(254, 9,R1u(0x0706),"white","black")
DrawNumx(254, 15,R1u(0x0707),"white","black")
DrawNumx(254, 21,R1u(0x070A),"white","black")
DrawNumx(254, 27,R1u(0x056C),"white","black")
DrawNumx(254, 33,R1u(0x0748),"orange","black")
DrawNumx(254, 39,R1u(0x0561),"cyan","black")
--[[
local LifeLv= R1u(0x0779)
--Link
DrawNumx( 24,43,R2u(0x0029,0x0019),"cyan","black") --Y pos
DrawNumx( 40,43,R2u(0x004D,0x003B),"cyan","black") --X pos
DrawNumx( 50,43,R1u(0x03D6),"cyan","black") --X sub
DrawNumx( 60,43,R1u(0x03E6),"cyan","black") --Y ??
DrawNumx( 70,43,R1u(0x005F),"cyan","black") --Facing
DrawNumx( 80,43,R1u(0x0070),"cyan","black") --?
DrawNumx( 90,43,R1u(0x0080),"cyan","black") --?
--Baddie
for y= 0, 5 do
local Y= y*7+50
local clr= "grey"
local state= R1u(0x00B6+y)
if state ~= 0 then clr= "white" end
DrawNumx( 8,Y,R1u(0x00A1+y),clr,"black")
DrawNumx( 24,Y,R2u(0x002A+y,0x001A+y),clr,"black") --Y pos
DrawNumx( 40,Y,R2u(0x004E+y,0x003C+y),clr,"black") --X pos
DrawNumx( 50,Y,R1u(0x03D7+y),clr,"black") --X sub
DrawNumx( 60,Y,R1u(0x03E7+y),clr,"black") --Y ??
DrawNumx( 70,Y,R1u(0x0060+y),clr,"black") --Facing
DrawNumx( 80,Y,R1u(0x0071+y),clr,"black") --?
DrawNumx( 90,Y,R1u(0x0081+y),clr,"black") --?
DrawNumx(100,Y,R1u(0x00C2+y),clr,"black") --
DrawNumx(110,Y,R1u(0x00BC+y),clr,"black") --
DrawNumx(120,Y,state,clr,"black") --
-- DmgCalc
local v= R1u(0x00A1+y)
v= R1u(0x6DF9 + v)%8
v= R1U(0x01E2AE + 0x10 + v*8 + LifeLv)
DrawNum(134,Y,v/2,clr,"black")
end
--Projectile
for y= 0, 5 do
local Y= y*7+92
local clr= "orange"
-- local state= R1u(0x00B6+y)
-- if state ~= 0 then clr= "white" end
DrawNumx( 8,Y,R1u(0x0087+y),clr,"black")
DrawNumx( 24,Y,R2u(0x0030+y,0x0020+y),clr,"black") --Y pos
DrawNumx( 40,Y,R2u(0x0054+y,0x0042+y),clr,"black") --X pos
DrawNumx( 50,Y,R1u(0x03DD+y),clr,"black") --X sub
DrawNumx( 60,Y,R1u(0x03ED+y),clr,"black") --Y ??
DrawNumx( 70,Y,R1u(0x0066+y),clr,"black") --Facing
DrawNumx( 80,Y,R1u(0x0077+y),clr,"black") --?
-- DmgCalc
local v= R1u(0x0087+y)
v= R1u(0x6D17 + v)%8
v= R1U(0x01E2AE + 0x10 + v*8 + LifeLv)
DrawNum(134,Y,v/2,clr,"black")
end
]]--
--[[
for y= 0, 0x0F do
local Y= y*7+50
local clr= "white"
-- if R1u(0x0019 + y) == 0 then clr= "grey" end
DrawNumx(16,Y,R2u(0x0029+y,0x0019+y),clr,"black") --Y pos
DrawNumx(32,Y,R2u(0x004D+y,0x003B+y),clr,"black") --X pos
DrawNumx(42,Y,R1u(0x005F+y),clr,"black") --Facing
DrawNumx(52,Y,R1u(0x0070+y),clr,"black") --?
DrawNumx(62,Y,R1u(0x0080+y),clr,"black") --?
DrawNumx(72,Y,R1u(0x03D6+y),clr,"black") --X sub
DrawNumx(82,Y,R1u(0x03E6+y),clr,"black") --Y ??
DrawNumx(92,Y,R1u(0x00C1+y),clr,"black") --
DrawNumx(102,Y,R1u(0x00A0+y),clr,"black") --
end
]]--
end
gui.register(BasicHUD)
--for spell= 0, 7 do
-- local s= ""
-- for lv= 1, 8 do
-- if s ~= "" then s= s .. " " end
-- s= string.format("%s%3d",
-- s,
-- R1U(0x000D7A + 0x10 + spell*8 + lv)/2
-- )
-- end
-- print(s)
--end
--#############################################################################
--#############################################################################
-- Ugh, need this loop on the very bottom...
--*****************************************************************************
while true do
--*****************************************************************************
-- Exists to detect frame advances, and thus clear the OldJoy array.
for i= 1, 4 do OldJoy[i]= {} end -- Retrigger keys
-- Any code that needs this loop can go here.
emu.frameadvance()
end
--[[
00DE - ((important for spells to have this zero))
0487 - Timer for selection repetition
0501 - An array of something?
0524 - ((important for spells to have this zero))
0564 - Magic (mirror)
0565 - Life (mirror)
057D - Vertical velocity
070D - Life restore
0743 - vertical related?
0744 - vertical related?
0749 - Spell selection
074A - Spell related
074C - (())
0768 - Mode?
0773 - Magic
0774 - Life
0775 - EXP (hi) ?
0776 - EXP (lo)
0777 - Attack level
0778 - Magic level
0779 - Life level
]]--