Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local ScriptInfo = {"Paper Mario 64 (J) Cap Calculator","2016-05-20 by Jdaster64"}
- ---- Game Variables ----
- local RngAddress = 0x743f0 --dword
- local CoinAnimationInfoAddress = 0x15bef0 --dword pointer
- local CoinFrameOffset = 0x4 --dword offset
- local CoinSubframeOffset = 0x47 --byte offset
- local CurrentBootsAddress = 0x10f450 --byte
- local CoinSlowAnimationStart = 0x108710 --dword pointer
- local CoinFastAnimationStart = 0x1087a4 --dword pointer
- ---- Editable / Output Variables ----
- local CapCount = 0
- local CapDecayMultiplier = 70
- local FramesToContact = 55
- local StartingDamage = 1
- local ExpectedRngValue = -1
- local LastExpectedRngValue = -1
- local UnexpectedRngValue = false
- ---- Input Device Variables ----
- local MousePos = { X=0, Y=0 }
- local ClickPos = { X=0, Y=0 }
- local Clicked = false
- local ClickedFrames = 0
- ---- Color Constants ----
- local ColorWhite = 0xffffffff
- local ColorLightGrey = 0xffccccd2
- local ColorMedGrey = 0xffaaaaaf
- local ColorDarkGrey = 0xff7f7f85
- local ColorDarkerGrey = 0xff2f2f35
- local ColorDarkestGrey = 0xff141424
- local ColorGreen = 0xff00e020
- local ColorDarkGreen = 0xff009810
- local ColorDarkerGreen = 0xff006409
- local ColorRed = 0xffe02000
- local ColorDarkRed = 0xff901200
- local ColorTranslucentWhite = 0x95ffffff
- local ColorTransparent = 0
- local ColorDebug = 0xff00ffff
- ---- Structs ----
- local ButtonStyle = {
- NormalBox = ColorDarkGrey,
- NormalFill = ColorMedGrey,
- HoverBox = ColorMedGrey,
- HoverFill = ColorLightGrey,
- ActiveBox = ColorDarkerGrey,
- ActiveFill = ColorDarkGrey
- }
- local Position = {
- X = 0,
- Y = 0
- }
- local Region = {
- L = 0,
- R = 0,
- T = 0,
- B = 0
- }
- ---- Drawing Functions ----
- function drawText(position,text,color)
- if color == nil then color = ColorWhite end
- gui.drawText(position.X,position.Y,text,color,ColorTransparent,9,"Arial")
- end
- function drawRectangle(region,color,fill)
- gui.drawRectangle(region.L,region.T,region.R-region.L,region.B-region.T,color,fill)
- end
- function drawArrow(position,size,dir,color) --dir = 1 for down, -1 for up
- local x=position.X
- local y=position.Y
- gui.drawPixel(x,y+(size-1)*dir,color)
- for i=0,size-1 do
- gui.drawLine(x-i, y+(size-1-i)*dir, x+i, y+(size-1-i)*dir, color)
- end
- end
- function inRegion(position,region)
- return position.X >= region.L and position.X < region.R and position.Y >= region.T and position.Y < region.B
- end
- function drawButton(region,style)
- if style == nil then
- style = ButtonStyle
- end
- if not inRegion(MousePos,region) then
- drawRectangle(region,style.NormalBox,style.NormalFill)
- elseif Clicked and inRegion(ClickPos,region) then
- drawRectangle(region,style.ActiveBox,style.ActiveFill)
- else
- drawRectangle(region,style.HoverBox,style.HoverFill)
- end
- end
- function drawField(region,text)
- drawRectangle(region,ColorDarkestGrey,ColorDarkerGrey)
- drawText({X=region.L+2,Y=region.T+2},text,ColorWhite)
- end
- function doButton(region,onclick,rate,style)
- if rate == nil then rate=100000000 end
- drawButton(region,style)
- if Clicked and ClickedFrames%rate == 1 and inRegion(ClickPos,region) then
- onclick()
- end
- end
- ---- RNG / Utility Functions ----
- function num2hex(n)
- local k = nil
- local digits = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"}
- local s = ""
- for i=1,8 do
- k=n%16+1
- n=math.floor(n/16)
- s=digits[k]..s
- end
- return s
- end
- --32*32 bit multiplication with proper 32-bit overflow
- function intMul(a,b)
- local ahi = math.floor(a / 65536)
- local alo = a % 65536
- local bhi = math.floor(b / 65536)
- local blo = b % 65536
- local lolo = alo * blo
- local lolohi = math.floor(lolo / 65536)
- local lololo = lolo % 65536
- return (ahi * blo + bhi * alo + lolohi) % 65536 * 65536 + lololo
- end
- function getCurrentRng()
- return mainmemory.read_u32_be(RngAddress)
- end
- function rngAdvanceSeed(seed,range,frame)
- seed = (intMul(0x5d588b65,seed) + 1) % 4294967296
- local ar = 0
- local num = 0
- if range==1 then
- ar = math.floor(0xffffffff / 1001)
- num = math.floor(seed / ar)
- if num >= 1001 then
- return rngAdvanceSeed(seed,range)
- end
- elseif range==100 then
- ar = math.floor(0xffffffff / 1010)
- num = math.floor(seed / ar)
- if num >= 1010 then
- return rngAdvanceSeed(seed,range)
- end
- else
- ar = math.floor(0xffffffff / (range+1))
- num = math.floor(seed / ar)
- if num > range then
- return rngAdvanceSeed(seed,range)
- end
- end
- return seed
- end
- function rngGetValue(seed,range)
- local ar = 0
- local num = 0
- if range==1 then
- ar = math.floor(0xffffffff / 1001)
- num = math.floor(seed / ar)
- if num < 501 then return 1 else return 0 end
- elseif range==100 then
- ar = math.floor(0xffffffff / 1010)
- num = math.floor(seed / ar)
- return math.floor(((0x66666667 * num) / 4294967296) / 4)
- else
- ar = math.floor(0xffffffff / range)
- num = math.floor(seed / ar)
- return num
- end
- end
- function getStartingCoinFramecount()
- local coinaniminfo = mainmemory.read_u32_be(CoinAnimationInfoAddress) - 0x80000000
- local frameaddress = mainmemory.read_u32_be(coinaniminfo + CoinFrameOffset) - 0x80000000
- local subframe = mainmemory.readbyte(coinaniminfo + CoinSubframeOffset)
- if frameaddress >= CoinSlowAnimationStart and frameaddress <= CoinSlowAnimationStart + 0x70 then
- return (CoinSlowAnimationStart + 0x70 - frameaddress) * 3 / 0x10 + subframe
- elseif frameaddress == CoinFastAnimationStart then
- return 45 + subframe
- elseif frameaddress == CoinFastAnimationStart + 0x10 then
- return 43 + subframe
- elseif frameaddress == CoinFastAnimationStart + 0x20 then
- return 42 + 1
- elseif frameaddress == CoinFastAnimationStart + 0x30 then
- return 41 + 1
- elseif frameaddress == CoinFastAnimationStart + 0x40 then
- return 39 + subframe
- else
- return (CoinFastAnimationStart + 0x90 - frameaddress) * 3 / 0x10 + 24 + subframe
- end
- end
- function randCallsForDamage(damage)
- if damage > 8 then
- return 11
- elseif damage > 4 then
- return 10
- elseif damage > 2 then
- return 8
- end
- return 6
- end
- function bounceCap()
- --Get initial parameters for RNG prediction
- local capchance = 200
- local capdecay = math.floor(CapDecayMultiplier)
- local startframes = FramesToContact
- local seed = getCurrentRng()
- local coinframecount = getStartingCoinFramecount()
- --Debug frame count: drawText({X=20,Y=50},coinframecount,ColorDebug)
- local bouncelength = 26
- local damage = StartingDamage
- local currentboots = mainmemory.readbyte(CurrentBootsAddress)
- if currentboots == 1 then
- bouncelength = 39
- elseif currentboots == 2 then
- bouncelength = 27
- effectcount = 8
- end
- --Predict away!
- --Startup (before first bounce)
- for i=1,startframes-1 do
- coinframecount = coinframecount - 1
- if coinframecount == 0 then
- seed = rngAdvanceSeed(seed, 100)
- if rngGetValue(seed, 100) < 70 then
- coinframecount = 24
- else
- coinframecount = 48
- end
- end
- seed = rngAdvanceSeed(seed, 1)
- end
- --Repeated bounces; Loop 100 times or until capped
- for cap=2,101 do
- --Impact effects
- local effectcount=randCallsForDamage(damage)
- damage = damage-1
- if damage < 1 then damage = 1 end
- for eff=1,effectcount do
- seed = rngAdvanceSeed(seed, 360)
- effectcount=6
- end
- --Interim frames
- for fr=1,bouncelength-1 do
- coinframecount = coinframecount - 1
- if coinframecount == 0 then
- seed = rngAdvanceSeed(seed, 100)
- if rngGetValue(seed, 100) < 70 then
- coinframecount = 24
- else
- coinframecount = 48
- end
- end
- seed = rngAdvanceSeed(seed, 1)
- end
- --Cap frame
- capchance = math.floor(capchance * capdecay / 100)
- seed = rngAdvanceSeed(seed, 101)
- if rngGetValue(seed, 101) > capchance then
- return cap
- end
- coinframecount = coinframecount - 1
- if coinframecount == 0 then
- seed = rngAdvanceSeed(seed, 100)
- if rngGetValue(seed, 100) < 70 then
- coinframecount = 24
- else
- coinframecount = 48
- end
- end
- seed = rngAdvanceSeed(seed, 1)
- end
- return -1 --Never capped (distinct from cap on 101st bounce)
- end
- function calcExpectedRngValue()
- local coinframecount = getStartingCoinFramecount()
- local seed = getCurrentRng()
- if coinframecount == 1 then
- seed = rngAdvanceSeed(seed, 100)
- end
- seed = rngAdvanceSeed(seed, 1)
- LastExpectedRngValue = ExpectedRngValue
- ExpectedRngValue = seed
- end
- function isRngValueUnexpected()
- if LastExpectedRngValue < 0 or ExpectedRngValue < 0 then
- return false
- end
- return LastExpectedRngValue ~= getCurrentRng() and ExpectedRngValue ~= getCurrentRng()
- end
- ---- GUI callbacks ----
- function increaseCapDecayMultiplier()
- CapDecayMultiplier = CapDecayMultiplier + 2.5
- if CapDecayMultiplier > 100 then CapDecayMultiplier = 100 end
- end
- function decreaseCapDecayMultiplier()
- CapDecayMultiplier = CapDecayMultiplier - 2.5
- if CapDecayMultiplier < 50 then CapDecayMultiplier = 50 end
- end
- function increaseFramesToContact()
- FramesToContact = FramesToContact + 1
- if FramesToContact > 200 then FramesToContact = 200 end
- end
- function decreaseFramesToContact()
- FramesToContact = FramesToContact - 1
- if FramesToContact < 0 then FramesToContact = 0 end
- end
- function increaseStartingDamage()
- StartingDamage = StartingDamage + 1
- if StartingDamage > 30 then StartingDamage = 30 end
- end
- function decreaseStartingDamage()
- StartingDamage = StartingDamage - 1
- if StartingDamage < 1 then StartingDamage = 1 end
- end
- local MaxCapAmount = 0
- local MaxCapFramecount = 0
- function resetExpectedRng()
- ExpectedRngValue = -1
- LastExpectedRngValue = -1
- UnexpectedRngValue = false
- MaxCapAmount = 0
- MaxCapFramecount = 0
- end
- ---- Main execution ----
- while true do
- local cx = input.getmouse().X
- local cy = input.getmouse().Y
- MousePos = { X = cx, Y = cy }
- Clicked = input.getmouse().Left
- if cx >= 320 or cx < 0 or cy >= 240 or cy < 0 then
- Clicked = false
- end
- if Clicked then
- ClickedFrames = ClickedFrames + 1
- if ClickedFrames == 1 then
- ClickPos = { X = cx, Y = cy }
- end
- else
- ClickedFrames = 0
- end
- if clicked then
- gui.drawText(128,2,cx.." "..cy,0xFFFFFFFF,nil,9,"Arial")
- console.Log(cx.." "..cy)
- end
- -- Starting Damage field / buttons
- doButton({L=95,R=109,T=220,B=230},increaseStartingDamage,20)
- doButton({L=95,R=109,T=230,B=240},decreaseStartingDamage,20)
- drawArrow({X=102,Y=227},5,-1,0xffffffff)
- drawArrow({X=102,Y=233},5,1,0xffffffff)
- drawField({L=110,R=160,T=222,B=237},StartingDamage.." dmg.")
- -- Cap Decay Multiplier field / buttons
- doButton({L=165,R=179,T=220,B=230},increaseCapDecayMultiplier,20)
- doButton({L=165,R=179,T=230,B=240},decreaseCapDecayMultiplier,20)
- drawArrow({X=172,Y=227},5,-1,0xffffffff)
- drawArrow({X=172,Y=233},5,1,0xffffffff)
- drawField({L=180,R=230,T=222,B=237},math.floor(CapDecayMultiplier).."%")
- -- Frames to Contact field / buttons
- doButton({L=235,R=249,T=220,B=230},increaseFramesToContact,8)
- doButton({L=235,R=249,T=230,B=240},decreaseFramesToContact,8)
- drawArrow({X=242,Y=227},5,-1,0xffffffff)
- drawArrow({X=242,Y=233},5,1,0xffffffff)
- drawField({L=250,R=300,T=222,B=237},FramesToContact.." fr.")
- -- Display Cap count
- local bouncecap = bounceCap()
- drawText({X=15,Y=224},"Caps: "..bouncecap)
- -- See if RNG value is as expected last frame
- if isRngValueUnexpected() then
- UnexpectedRngValue = true
- end
- local rngexpectpos = {
- L = 75,
- R = 90,
- T = 222,
- B = 237
- }
- local rngexpectstyle = {
- NormalBox = ColorTransparent,
- NormalFill = ColorTransparent,
- HoverBox = ColorTransparent,
- HoverFill = ColorTranslucentWhite,
- ActiveBox = ColorDarkerGreen,
- ActiveFill = ColorDarkGreen
- }
- -- Draw green or red box based on UnexpectedRngValue
- if UnexpectedRngValue == true then
- drawRectangle(rngexpectpos,ColorDarkRed,ColorRed)
- else
- drawRectangle(rngexpectpos,ColorDarkGreen,ColorGreen)
- end
- -- Update ExpectedRngValue field
- doButton(rngexpectpos,resetExpectedRng,2,rngexpectstyle)
- -- Update ExpectedRngValue
- calcExpectedRngValue()
- local framecount = emu.framecount()
- if bouncecap > MaxCapAmount then
- MaxCapAmount = bouncecap
- MaxCapFramecount = framecount
- end
- drawText({X=20,Y=50},framecount,ColorGreen)
- drawText({X=20,Y=60},"Max: "..MaxCapAmount.." Frame: "..MaxCapFramecount,ColorRed)
- emu.frameadvance()
- end
Advertisement
Add Comment
Please, Sign In to add comment