Stryder7x

PM64 Capchance JP

Oct 30th, 2017
241
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.59 KB | None | 0 0
  1. local ScriptInfo = {"Paper Mario 64 (J) Cap Calculator","2016-05-20 by Jdaster64"}
  2.  
  3. ---- Game Variables ----
  4.  
  5. local RngAddress = 0x743f0 --dword
  6. local CoinAnimationInfoAddress = 0x15bef0 --dword pointer
  7. local CoinFrameOffset = 0x4 --dword offset
  8. local CoinSubframeOffset = 0x47 --byte offset
  9. local CurrentBootsAddress = 0x10f450 --byte
  10. local CoinSlowAnimationStart = 0x108710 --dword pointer
  11. local CoinFastAnimationStart = 0x1087a4 --dword pointer
  12.  
  13. ---- Editable / Output Variables ----
  14.  
  15. local CapCount = 0
  16. local CapDecayMultiplier = 70
  17. local FramesToContact = 55
  18. local StartingDamage = 1
  19. local ExpectedRngValue = -1
  20. local LastExpectedRngValue = -1
  21. local UnexpectedRngValue = false
  22.  
  23. ---- Input Device Variables ----
  24.  
  25. local MousePos = { X=0, Y=0 }
  26. local ClickPos = { X=0, Y=0 }
  27. local Clicked = false
  28. local ClickedFrames = 0
  29.  
  30. ---- Color Constants ----
  31.  
  32. local ColorWhite = 0xffffffff
  33. local ColorLightGrey = 0xffccccd2
  34. local ColorMedGrey = 0xffaaaaaf
  35. local ColorDarkGrey = 0xff7f7f85
  36. local ColorDarkerGrey = 0xff2f2f35
  37. local ColorDarkestGrey = 0xff141424
  38. local ColorGreen = 0xff00e020
  39. local ColorDarkGreen = 0xff009810
  40. local ColorDarkerGreen = 0xff006409
  41. local ColorRed = 0xffe02000
  42. local ColorDarkRed = 0xff901200
  43. local ColorTranslucentWhite = 0x95ffffff
  44. local ColorTransparent = 0
  45. local ColorDebug = 0xff00ffff
  46.  
  47. ---- Structs ----
  48.  
  49. local ButtonStyle = {
  50. NormalBox = ColorDarkGrey,
  51. NormalFill = ColorMedGrey,
  52. HoverBox = ColorMedGrey,
  53. HoverFill = ColorLightGrey,
  54. ActiveBox = ColorDarkerGrey,
  55. ActiveFill = ColorDarkGrey
  56. }
  57.  
  58. local Position = {
  59. X = 0,
  60. Y = 0
  61. }
  62.  
  63. local Region = {
  64. L = 0,
  65. R = 0,
  66. T = 0,
  67. B = 0
  68. }
  69.  
  70. ---- Drawing Functions ----
  71.  
  72. function drawText(position,text,color)
  73. if color == nil then color = ColorWhite end
  74. gui.drawText(position.X,position.Y,text,color,ColorTransparent,9,"Arial")
  75. end
  76.  
  77. function drawRectangle(region,color,fill)
  78. gui.drawRectangle(region.L,region.T,region.R-region.L,region.B-region.T,color,fill)
  79. end
  80.  
  81. function drawArrow(position,size,dir,color) --dir = 1 for down, -1 for up
  82. local x=position.X
  83. local y=position.Y
  84. gui.drawPixel(x,y+(size-1)*dir,color)
  85. for i=0,size-1 do
  86. gui.drawLine(x-i, y+(size-1-i)*dir, x+i, y+(size-1-i)*dir, color)
  87. end
  88. end
  89.  
  90. function inRegion(position,region)
  91. return position.X >= region.L and position.X < region.R and position.Y >= region.T and position.Y < region.B
  92. end
  93.  
  94. function drawButton(region,style)
  95. if style == nil then
  96. style = ButtonStyle
  97. end
  98. if not inRegion(MousePos,region) then
  99. drawRectangle(region,style.NormalBox,style.NormalFill)
  100. elseif Clicked and inRegion(ClickPos,region) then
  101. drawRectangle(region,style.ActiveBox,style.ActiveFill)
  102. else
  103. drawRectangle(region,style.HoverBox,style.HoverFill)
  104. end
  105. end
  106.  
  107. function drawField(region,text)
  108. drawRectangle(region,ColorDarkestGrey,ColorDarkerGrey)
  109. drawText({X=region.L+2,Y=region.T+2},text,ColorWhite)
  110. end
  111.  
  112. function doButton(region,onclick,rate,style)
  113. if rate == nil then rate=100000000 end
  114. drawButton(region,style)
  115. if Clicked and ClickedFrames%rate == 1 and inRegion(ClickPos,region) then
  116. onclick()
  117. end
  118. end
  119.  
  120. ---- RNG / Utility Functions ----
  121.  
  122. function num2hex(n)
  123. local k = nil
  124. local digits = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"}
  125. local s = ""
  126. for i=1,8 do
  127. k=n%16+1
  128. n=math.floor(n/16)
  129. s=digits[k]..s
  130. end
  131. return s
  132. end
  133.  
  134. --32*32 bit multiplication with proper 32-bit overflow
  135. function intMul(a,b)
  136. local ahi = math.floor(a / 65536)
  137. local alo = a % 65536
  138. local bhi = math.floor(b / 65536)
  139. local blo = b % 65536
  140. local lolo = alo * blo
  141. local lolohi = math.floor(lolo / 65536)
  142. local lololo = lolo % 65536
  143. return (ahi * blo + bhi * alo + lolohi) % 65536 * 65536 + lololo
  144. end
  145.  
  146. function getCurrentRng()
  147. return mainmemory.read_u32_be(RngAddress)
  148. end
  149.  
  150. function rngAdvanceSeed(seed,range,frame)
  151. seed = (intMul(0x5d588b65,seed) + 1) % 4294967296
  152. local ar = 0
  153. local num = 0
  154. if range==1 then
  155. ar = math.floor(0xffffffff / 1001)
  156. num = math.floor(seed / ar)
  157. if num >= 1001 then
  158. return rngAdvanceSeed(seed,range)
  159. end
  160. elseif range==100 then
  161. ar = math.floor(0xffffffff / 1010)
  162. num = math.floor(seed / ar)
  163. if num >= 1010 then
  164. return rngAdvanceSeed(seed,range)
  165. end
  166. else
  167. ar = math.floor(0xffffffff / (range+1))
  168. num = math.floor(seed / ar)
  169. if num > range then
  170. return rngAdvanceSeed(seed,range)
  171. end
  172. end
  173. return seed
  174. end
  175.  
  176. function rngGetValue(seed,range)
  177. local ar = 0
  178. local num = 0
  179. if range==1 then
  180. ar = math.floor(0xffffffff / 1001)
  181. num = math.floor(seed / ar)
  182. if num < 501 then return 1 else return 0 end
  183. elseif range==100 then
  184. ar = math.floor(0xffffffff / 1010)
  185. num = math.floor(seed / ar)
  186. return math.floor(((0x66666667 * num) / 4294967296) / 4)
  187. else
  188. ar = math.floor(0xffffffff / range)
  189. num = math.floor(seed / ar)
  190. return num
  191. end
  192. end
  193.  
  194. function getStartingCoinFramecount()
  195. local coinaniminfo = mainmemory.read_u32_be(CoinAnimationInfoAddress) - 0x80000000
  196. local frameaddress = mainmemory.read_u32_be(coinaniminfo + CoinFrameOffset) - 0x80000000
  197. local subframe = mainmemory.readbyte(coinaniminfo + CoinSubframeOffset)
  198. if frameaddress >= CoinSlowAnimationStart and frameaddress <= CoinSlowAnimationStart + 0x70 then
  199. return (CoinSlowAnimationStart + 0x70 - frameaddress) * 3 / 0x10 + subframe
  200. elseif frameaddress == CoinFastAnimationStart then
  201. return 45 + subframe
  202. elseif frameaddress == CoinFastAnimationStart + 0x10 then
  203. return 43 + subframe
  204. elseif frameaddress == CoinFastAnimationStart + 0x20 then
  205. return 42 + 1
  206. elseif frameaddress == CoinFastAnimationStart + 0x30 then
  207. return 41 + 1
  208. elseif frameaddress == CoinFastAnimationStart + 0x40 then
  209. return 39 + subframe
  210. else
  211. return (CoinFastAnimationStart + 0x90 - frameaddress) * 3 / 0x10 + 24 + subframe
  212. end
  213. end
  214.  
  215. function randCallsForDamage(damage)
  216. if damage > 8 then
  217. return 11
  218. elseif damage > 4 then
  219. return 10
  220. elseif damage > 2 then
  221. return 8
  222. end
  223. return 6
  224. end
  225.  
  226. function bounceCap()
  227. --Get initial parameters for RNG prediction
  228. local capchance = 200
  229. local capdecay = math.floor(CapDecayMultiplier)
  230. local startframes = FramesToContact
  231. local seed = getCurrentRng()
  232. local coinframecount = getStartingCoinFramecount()
  233. --Debug frame count: drawText({X=20,Y=50},coinframecount,ColorDebug)
  234. local bouncelength = 26
  235. local damage = StartingDamage
  236. local currentboots = mainmemory.readbyte(CurrentBootsAddress)
  237. if currentboots == 1 then
  238. bouncelength = 39
  239. elseif currentboots == 2 then
  240. bouncelength = 27
  241. effectcount = 8
  242. end
  243. --Predict away!
  244. --Startup (before first bounce)
  245. for i=1,startframes-1 do
  246. coinframecount = coinframecount - 1
  247. if coinframecount == 0 then
  248. seed = rngAdvanceSeed(seed, 100)
  249. if rngGetValue(seed, 100) < 70 then
  250. coinframecount = 24
  251. else
  252. coinframecount = 48
  253. end
  254. end
  255. seed = rngAdvanceSeed(seed, 1)
  256. end
  257. --Repeated bounces; Loop 100 times or until capped
  258. for cap=2,101 do
  259. --Impact effects
  260. local effectcount=randCallsForDamage(damage)
  261. damage = damage-1
  262. if damage < 1 then damage = 1 end
  263. for eff=1,effectcount do
  264. seed = rngAdvanceSeed(seed, 360)
  265. effectcount=6
  266. end
  267. --Interim frames
  268. for fr=1,bouncelength-1 do
  269. coinframecount = coinframecount - 1
  270. if coinframecount == 0 then
  271. seed = rngAdvanceSeed(seed, 100)
  272. if rngGetValue(seed, 100) < 70 then
  273. coinframecount = 24
  274. else
  275. coinframecount = 48
  276. end
  277. end
  278. seed = rngAdvanceSeed(seed, 1)
  279. end
  280. --Cap frame
  281. capchance = math.floor(capchance * capdecay / 100)
  282. seed = rngAdvanceSeed(seed, 101)
  283. if rngGetValue(seed, 101) > capchance then
  284. return cap
  285. end
  286. coinframecount = coinframecount - 1
  287. if coinframecount == 0 then
  288. seed = rngAdvanceSeed(seed, 100)
  289. if rngGetValue(seed, 100) < 70 then
  290. coinframecount = 24
  291. else
  292. coinframecount = 48
  293. end
  294. end
  295. seed = rngAdvanceSeed(seed, 1)
  296. end
  297. return -1 --Never capped (distinct from cap on 101st bounce)
  298. end
  299.  
  300. function calcExpectedRngValue()
  301. local coinframecount = getStartingCoinFramecount()
  302. local seed = getCurrentRng()
  303. if coinframecount == 1 then
  304. seed = rngAdvanceSeed(seed, 100)
  305. end
  306. seed = rngAdvanceSeed(seed, 1)
  307. LastExpectedRngValue = ExpectedRngValue
  308. ExpectedRngValue = seed
  309. end
  310.  
  311. function isRngValueUnexpected()
  312. if LastExpectedRngValue < 0 or ExpectedRngValue < 0 then
  313. return false
  314. end
  315. return LastExpectedRngValue ~= getCurrentRng() and ExpectedRngValue ~= getCurrentRng()
  316. end
  317.  
  318. ---- GUI callbacks ----
  319.  
  320. function increaseCapDecayMultiplier()
  321. CapDecayMultiplier = CapDecayMultiplier + 2.5
  322. if CapDecayMultiplier > 100 then CapDecayMultiplier = 100 end
  323. end
  324.  
  325. function decreaseCapDecayMultiplier()
  326. CapDecayMultiplier = CapDecayMultiplier - 2.5
  327. if CapDecayMultiplier < 50 then CapDecayMultiplier = 50 end
  328. end
  329.  
  330. function increaseFramesToContact()
  331. FramesToContact = FramesToContact + 1
  332. if FramesToContact > 200 then FramesToContact = 200 end
  333. end
  334.  
  335. function decreaseFramesToContact()
  336. FramesToContact = FramesToContact - 1
  337. if FramesToContact < 0 then FramesToContact = 0 end
  338. end
  339.  
  340. function increaseStartingDamage()
  341. StartingDamage = StartingDamage + 1
  342. if StartingDamage > 30 then StartingDamage = 30 end
  343. end
  344.  
  345. function decreaseStartingDamage()
  346. StartingDamage = StartingDamage - 1
  347. if StartingDamage < 1 then StartingDamage = 1 end
  348. end
  349.  
  350. local MaxCapAmount = 0
  351. local MaxCapFramecount = 0
  352.  
  353. function resetExpectedRng()
  354. ExpectedRngValue = -1
  355. LastExpectedRngValue = -1
  356. UnexpectedRngValue = false
  357. MaxCapAmount = 0
  358. MaxCapFramecount = 0
  359. end
  360.  
  361. ---- Main execution ----
  362.  
  363. while true do
  364. local cx = input.getmouse().X
  365. local cy = input.getmouse().Y
  366.  
  367. MousePos = { X = cx, Y = cy }
  368. Clicked = input.getmouse().Left
  369.  
  370. if cx >= 320 or cx < 0 or cy >= 240 or cy < 0 then
  371. Clicked = false
  372. end
  373.  
  374. if Clicked then
  375. ClickedFrames = ClickedFrames + 1
  376. if ClickedFrames == 1 then
  377. ClickPos = { X = cx, Y = cy }
  378. end
  379. else
  380. ClickedFrames = 0
  381. end
  382.  
  383. if clicked then
  384. gui.drawText(128,2,cx.." "..cy,0xFFFFFFFF,nil,9,"Arial")
  385. console.Log(cx.." "..cy)
  386. end
  387.  
  388. -- Starting Damage field / buttons
  389. doButton({L=95,R=109,T=220,B=230},increaseStartingDamage,20)
  390. doButton({L=95,R=109,T=230,B=240},decreaseStartingDamage,20)
  391. drawArrow({X=102,Y=227},5,-1,0xffffffff)
  392. drawArrow({X=102,Y=233},5,1,0xffffffff)
  393. drawField({L=110,R=160,T=222,B=237},StartingDamage.." dmg.")
  394.  
  395. -- Cap Decay Multiplier field / buttons
  396. doButton({L=165,R=179,T=220,B=230},increaseCapDecayMultiplier,20)
  397. doButton({L=165,R=179,T=230,B=240},decreaseCapDecayMultiplier,20)
  398. drawArrow({X=172,Y=227},5,-1,0xffffffff)
  399. drawArrow({X=172,Y=233},5,1,0xffffffff)
  400. drawField({L=180,R=230,T=222,B=237},math.floor(CapDecayMultiplier).."%")
  401.  
  402. -- Frames to Contact field / buttons
  403. doButton({L=235,R=249,T=220,B=230},increaseFramesToContact,8)
  404. doButton({L=235,R=249,T=230,B=240},decreaseFramesToContact,8)
  405. drawArrow({X=242,Y=227},5,-1,0xffffffff)
  406. drawArrow({X=242,Y=233},5,1,0xffffffff)
  407. drawField({L=250,R=300,T=222,B=237},FramesToContact.." fr.")
  408.  
  409. -- Display Cap count
  410. local bouncecap = bounceCap()
  411. drawText({X=15,Y=224},"Caps: "..bouncecap)
  412.  
  413. -- See if RNG value is as expected last frame
  414. if isRngValueUnexpected() then
  415. UnexpectedRngValue = true
  416. end
  417. local rngexpectpos = {
  418. L = 75,
  419. R = 90,
  420. T = 222,
  421. B = 237
  422. }
  423. local rngexpectstyle = {
  424. NormalBox = ColorTransparent,
  425. NormalFill = ColorTransparent,
  426. HoverBox = ColorTransparent,
  427. HoverFill = ColorTranslucentWhite,
  428. ActiveBox = ColorDarkerGreen,
  429. ActiveFill = ColorDarkGreen
  430. }
  431. -- Draw green or red box based on UnexpectedRngValue
  432. if UnexpectedRngValue == true then
  433. drawRectangle(rngexpectpos,ColorDarkRed,ColorRed)
  434. else
  435. drawRectangle(rngexpectpos,ColorDarkGreen,ColorGreen)
  436. end
  437. -- Update ExpectedRngValue field
  438. doButton(rngexpectpos,resetExpectedRng,2,rngexpectstyle)
  439. -- Update ExpectedRngValue
  440. calcExpectedRngValue()
  441.  
  442. local framecount = emu.framecount()
  443. if bouncecap > MaxCapAmount then
  444. MaxCapAmount = bouncecap
  445. MaxCapFramecount = framecount
  446. end
  447. drawText({X=20,Y=50},framecount,ColorGreen)
  448. drawText({X=20,Y=60},"Max: "..MaxCapAmount.." Frame: "..MaxCapFramecount,ColorRed)
  449. emu.frameadvance()
  450. end
Advertisement
Add Comment
Please, Sign In to add comment