Added frame markers.
Largely the same as the previous version, otherwise.
--Renaming. I like the descriptive names, but I want something shorter.
--I'm also selecting functions I think I'll need.
--Turns out I needed to swap emulators. Yay, I just have one spot to change!
local R1u= memory.readbyte
--Don't like gui.text, so here's my number drawings.
--*****************************************************************************
Draw= {} --Draw[button]( x , y , c )
--*****************************************************************************
--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
--#############################################################################
--Handle RNG
--Data structure expected:
-- RNG_Table
-- [1] 00E4 R0
-- [2] 00E5 R1
-- [3] 00E6 R2
-- [4] 00E7 R3
-- TA 0092 Timer_Always
-- TL 009D Timer_Lagged
--These six values are what's required to know the output.
--If a rename is suggested, I'll probably listen.
--Because lag shifts the RNG, it's not precisely possible to know what the
--future RNG will hold, although knowing the underlying numbers may help to
--know what to expect as lag frames "shift" the RNG a little.
--In addition, whenever the RNG is called, R2 is modified using a different
--formula than the standard RNG shift, so if an enemy needs to "think", or an
--item is about to drop, the RNG can be "unpredictably" modified for the next
--16 frames. Since R2 is shifted out entirely after 16 frames, without any
--effect on the standard shifts, the change is only local to this call.
--It still happens more frequently than would be desired.
--*****************************************************************************
local function Roll(T)
--*****************************************************************************
--Gets the next RNG value. This is the "shift" calculation.
--In-place modification to the table. Make a copy first if you want the old.
--Will not modify timers, even though Timer_Always also updates with it.
local v= bit.band(0x02,bit.bxor(T[1],T[2])) / 2
for i= 1, 4 do
T[i]= T[i]+256*v
v= T[i]%2
T[i]= math.floor(T[i]/2)
end
end
--*****************************************************************************
local function RollAndTimer(T)
--*****************************************************************************
--Rolls the RNG. Will also update timers, too.
Roll(T)
T.TA= (T.TA+1)%256
T.TL= (T.TL+1)%256
end
--*****************************************************************************
local function LoadRNG(T)
--*****************************************************************************
--Reads the RNG values from memory.
--You can feed a table (it'll modify) or let it construct one itself.
--Returns the initialized table.
T= T or {}
T[1]= R1u(0x00E4) --R0
T[2]= R1u(0x00E5) --R1
T[3]= R1u(0x00E6) --R2
T[4]= R1u(0x00E7) --R3
T.TA= R1u(0x0092) --Timer_Always
T.TL= R1u(0x009D) --Timer_Lagged
return T
end
--*****************************************************************************
local function CalculateRNG(RNG)
--*****************************************************************************
--Gets the calculated RNG value. This is the "call" calculation.
--Returns three values: The RNG output, and the new R2.
-- Number: RNG output (value used for whatever RNG stuff is needed)
-- Number: What R2 is replaced with after the call is done
--This function will not do in-place modification. That's up to the caller.
--Take note that R2 and R3 both are modified. R3 takes the RNG output.
--[[
Carry is always set upon entry (add +1 to what you see)
0E:AE64 LDA $00E6 R2
0E:AE66 ADC $00E7 R3
0E:AE68 ADC $0000 #$59 (used for function call)
0E:AE6A SBC $009D Frame timer (affected by lag)
0E:AE6C STA $00E6 Modify R2 right away
0E:AE6E ADC $0001 #$AE (used for function call)
0E:AE70 SBC $00E5 R1
0E:AE72 ADC $0092 Frame timer (always runs)
0E:AE74 STA $00E7 R3
]]--
local RNG_Output= (( --Also New_R3
9
+ RNG[3] --R2
+ RNG[4] --R3
- RNG.TL --Timer_Lagged
- RNG[2] --R1
)%255 + 1
+ RNG.TA --Timer_Always
)%256
local New_R2= ((
0x59
+ RNG[3] --R2
+ RNG[4] --R3
)%255 + 1
+ (255 - RNG.TL)
)%256
return RNG_Output, New_R2
end
local BigNum= 0xFFFFFFFF + 1 --The 80red glitch handler
local BigW= 0x00FFFFFF --Big weapon recharge
local Life= 0xFFFFFFFF - BigNum --Extra life
local SmlE= 0xFF0000FF - BigNum --Small energy recharge
local BigE= 0xFF8000FF - BigNum --Big energy recharge
local SmlW= 0x0080FFFF --Small weapon recharge
local NoItem=0x808080FF - BigNum --nil
local ItemThresholds= {
[0]=BigW,BigW,
Life,
SmlE,SmlE,SmlE,SmlE,SmlE,
BigE,BigE,
SmlW,SmlW,SmlW,smlW
}
--*****************************************************************************
local function ItemColor(v)
--*****************************************************************************
return ItemThresholds[v%50] or NoItem
end
--#############################################################################
--GUI
local ST= {} --Scratch Table. Optimizing those table constructors.
local DropsTable= {}
local X_ShowDrops= 220
--*****************************************************************************
local function ShowDrops()
--*****************************************************************************
--The display half of the drop seeker.
--Dividing the two halves have their
local Y= 3
for i= 1, #DropsTable do
local Y= 6*i + 3
local entry= DropsTable[i]
local Bclr= "black"
-- if entry.frames <= 16 then Bclr= "grey" end
DrawNum(X_ShowDrops,Y,entry.frames,entry.clr,Bclr)
end
LoadRNG(ST)
DrawNum(X_ShowDrops,#DropsTable*6 + 14,(ST.TA - ST.TL)%256,"black","white")
end
local RNG= {{},{}} --For sake of calling the table constructor just once
--*****************************************************************************
local function RNG_HUD()
--*****************************************************************************
--To display some basic RNG information.
LoadRNG(RNG[1]) --If you never call the RNG, this is what you get
LoadRNG(RNG[2]) --If called just once at this moment, what you get
RNG[2][4],RNG[2][3]= CalculateRNG(RNG[2]) --To show effects of RNG call
-- DrawNum(15,9,(RNG[1].TA-RNG[1].TL)%256,"black","white")
local frame= emu.framecount()
for i= 1, 37 do
local Y= 6*i+3
local f= frame + i
if (f%5) == 0 then
local clr= "orange"
if (f%10) == 0 then clr= "green" end
if (f%100) == 0 then clr= "white" end
gui.line(255,Y,255,Y+3,clr)
end
for r= 1, 2 do
local v= CalculateRNG(RNG[r])
local clr= ItemColor(v)
DrawNum(252 - 14*(r-1),Y,v,clr,"black")
RollAndTimer(RNG[r])
end
end
end
--*****************************************************************************
local function BasicHUD()
--*****************************************************************************
RNG_HUD()
ShowDrops()
end
gui.register(BasicHUD)
--#############################################################################
--Base
local SeekRNG= {}
--*****************************************************************************
local function SeekDrops(count)
--*****************************************************************************
--The processing half of the drop seeker.
--It assumes zero lag and you don't trigger RNG calls.
--In practice, I'm not sure how helpful this would be. I'm getting the
--impression that a whole lot of things need the RNG, and even with the 16
--frame window, you're bound to crash into a few of these unexpected changes.
--Frames, lag, and RNG calls are the three factors in what a new number is.
--Very much less "clean" and more unpredictable than a single factor, which is
--what I'm more used to.
LoadRNG(SeekRNG)
local Wait= 0
for i= 1, count do
local Item= DropsTable[i] or {}
local v
repeat
v= CalculateRNG(SeekRNG)
v= ItemThresholds[v%50]
RollAndTimer(SeekRNG) --Advance RNG
Wait= Wait+1
until v
Item.frames= Wait
Item.clr= v
DropsTable[i]= Item
end
end
--*****************************************************************************
local function FrmAdv()
--*****************************************************************************
SeekDrops(20)
end
emu.registerafter(FrmAdv)
--*****************************************************************************
--while true do
--*****************************************************************************
-- SeekDrops(20)
-- emu.frameadvance()
--end