Advertisement
Guest User

NSMB Objects

a guest
Feb 1st, 2023
160
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.50 KB | None | 0 0
  1. -- Author: Suuper
  2. -- If you make any significant improvements, please share.
  3.  
  4. local addrPtrToFirstObjectByType = 0x208FB38
  5. local offsetNodeByType = 0x38
  6.  
  7. -- General stuff
  8. local typeOffset = 0x44
  9. local XOffset = 0x60
  10. local YOffset = 0x64
  11. local XVelOffset = 0xD0
  12. local YVelOffset = 0xD4
  13. local XVelTargetOffset = 0xE0
  14. local YVelTargetOffset = 0xE4
  15. local scaleXOffset = 0xF0
  16. local widthOffset = 0x13C
  17. local heightOffset = 0x140
  18. local someFlagsOffset = 0x146
  19.  
  20. -- Specific object types
  21. local MarioXVelOffset = 0xB4
  22. local MarioMaxSpeedOffset = 0xB8
  23.  
  24. function read_u32(addr)
  25.     return memory.read_u32_le(addr)
  26. end
  27. function write_u32(addr, value)
  28.     return memory.write_u32_le(addr, value, "ARM9 System Bus")
  29. end
  30. function read_s32(addr)
  31.     return memory.read_s32_le(addr)
  32. end
  33. function write_s32(addr, value)
  34.     return memory.write_s32_le(addr, value, "ARM9 System Bus")
  35. end
  36. function read_u16(addr)
  37.     return memory.read_u16_le(addr)
  38. end
  39. function write_u16(addr, value)
  40.     return memory.write_u16_le(addr, value, "ARM9 System Bus")
  41. end
  42. function read_u8(addr)
  43.     return memory.read_u8(addr)
  44. end
  45. function write_u8(addr, value)
  46.     memory.write_u8(addr, value, "ARM9 System Bus")
  47. end
  48. function read_s8(addr)
  49.     return memory.read_s8(addr)
  50. end
  51. function write_s8(addr, value)
  52.     return memory.write_s8(addr, value, "ARM9 System Bus")
  53. end
  54.  
  55. function drawText(x, y, str)
  56.     gui.drawText(x, y + 192, str)
  57. end
  58.  
  59. function isTangible(objAddr)
  60.     local flags = read_s8(objAddr + someFlagsOffset)
  61.     return bit.check(flags, 0)
  62. end
  63.  
  64. local objectTypes = {}
  65. objectTypes[0x1C] = "Mario"
  66. objectTypes[0x1F] = "Piranha Plant"
  67. objectTypes[0x28] = "Cheep Cheep"
  68. objectTypes[0x2B] = "Bumped Block"
  69. objectTypes[0x2C] = "Powerup" -- Fireflower and mini are same
  70. objectTypes[0x31] = "Flag Pole"
  71. objectTypes[0x32] = "Spring"
  72. objectTypes[0x35] = "Lakitu"
  73. objectTypes[0x36] = "Spiney Beetle"
  74. objectTypes[0x37] = "Boo"
  75. objectTypes[0x44] = "Buzzy Beetle"
  76. objectTypes[0x45] = "Dry Bones"
  77. objectTypes[0x47] = "Fireball Enemy"
  78. objectTypes[0x4C] = "Mega GP Drop"
  79. objectTypes[0x51] = "Hammer Bro"
  80. objectTypes[0x56] = "Shark"
  81. objectTypes[0x5A] = "Bowser's Fireball"
  82. objectTypes[0x5B] = "Spinny Spring"
  83. objectTypes[0x5F] = "Red Coin"
  84. objectTypes[0x60] = "P Switch"
  85. objectTypes[0x62] = "Blue P Switch"
  86. objectTypes[0x64] = "Red ! Switch"
  87. objectTypes[0x68] = "Expandable Block"
  88. objectTypes[0x7E] = "Swooper"
  89. objectTypes[0x84] = "Bowser Junior"
  90. objectTypes[0x85] = "Princess Peach"
  91. objectTypes[0x89] = "Roulette Block"
  92. objectTypes[0x8B] = "Basher"
  93. objectTypes[0x8F] = "Bounce Bubble"
  94. objectTypes[0x95] = "Bowser"
  95. objectTypes[0x9B] = "Boss Switch"
  96. objectTypes[0x9D] = "Respawnable Hidden Block"
  97. objectTypes[0x9E] = "Coin"
  98. objectTypes[0x9F] = "Star Coin"
  99. objectTypes[0xA0] = "Goomba"
  100. objectTypes[0xA3] = "Koopa" -- green or red
  101. objectTypes[0xA4] = "Paratroopa"
  102. objectTypes[0xAE] = "Giant Spike Fish"
  103. objectTypes[0xAF] = "Red Coin Ring"
  104. objectTypes[0xBB] = "Checkpoint"
  105. objectTypes[0xB4] = "Snowball Thrower"
  106. objectTypes[0xB5] = "Snowball"
  107. objectTypes[0xC3] = "Tightrope"
  108. objectTypes[0xC8] = "Moving Platform"
  109. objectTypes[0xCF] = "Moving Slate?"
  110. objectTypes[0xD2] = "See-saw"
  111. objectTypes[0xE3] = "Chomping Piranha Plant"
  112. objectTypes[0xE8] = "Pokey"
  113. objectTypes[0xE9] = "Manhole"
  114. objectTypes[0xEA] = "Bumpable Platform"
  115. objectTypes[0xEB] = "Tilting Mushroom"
  116. objectTypes[0xF7] = "Quicksnow"
  117. objectTypes[0x102] = "Falling Snow"
  118. objectTypes[0x103] = "One-Way Gate"
  119. objectTypes[0x105] = "Red ? Block"
  120. objectTypes[0x107] = "Boo Elevator Platform"
  121. objectTypes[0x115] = "Broken Brick"
  122. objectTypes[0x133] = "Temp Projectile Slot"
  123. objectTypes[0x135] = "Water"
  124. function getObjectTypeName(obj)
  125.     local objType = read_u16(obj + typeOffset)
  126.     if objectTypes[objType] ~= nil then
  127.         return objectTypes[objType]
  128.     else
  129.         return "Unknown"
  130.     end
  131. end
  132.  
  133. local allObjects = {}
  134. local objectsByDropDown = {}
  135. local objectCount = 0
  136.  
  137. local spawnActivity = false
  138.  
  139. local shouldExit = false
  140. local detailForms = {}
  141.  
  142. function GetFirstObject()
  143.     local nodePtr = read_u32(addrPtrToFirstObjectByType)
  144.     if nodePtr == 0 then
  145.         return 0
  146.     else
  147.         --return read_u32(nodePtr + 8) -- Works, but why bother reading it?
  148.         return nodePtr - offsetNodeByType
  149.     end
  150. end
  151. function GetNextObject(obj)
  152.     local nextNodePtr = read_u32(obj + offsetNodeByType + 4)
  153.     if nextNodePtr == 0 then
  154.         return 0
  155.     else
  156.         --return read_u32(nextNodePtr + 8)
  157.         return nextNodePtr - offsetNodeByType
  158.     end
  159. end
  160. function GetPreviousObject(obj)
  161.     local nextNodePtr = read_u32(obj + offsetNodeByType)
  162.     if nextNodePtr == 0 then
  163.         return 0
  164.     else
  165.         return read_u32(nextNodePtr + 8)
  166.     end
  167. end
  168.  
  169. function hex(v, padLength)
  170.     padLength = padLength or "1"
  171.     return string.format("%0" .. padLength .. "x", v)
  172. end
  173.  
  174. function formatPosition(v)
  175.     local sign = " "
  176.     if v < 0 then
  177.         sign = "-"
  178.         v = -v
  179.     end
  180.  
  181.     local subpixel = v % 0x1000
  182.     v = math.floor(v / 0x1000)
  183.     local pixel = v % 0x10
  184.     local block = math.floor(v / 0x10)
  185.    
  186.     return sign .. string.format("%04d", block) .. "|" .. string.format("%02d", pixel) .. "|" .. string.format("%04d", subpixel)
  187. end
  188. function formatSpeed(v)
  189.     local sign = " "
  190.     if v < 0 then
  191.         sign = "-"
  192.         v = -v
  193.     end
  194.  
  195.     local subpixel = v % 0x1000
  196.     local pixel = math.floor(v / 0x1000)
  197.    
  198.     return sign .. string.format("%02d", pixel) .. "|" .. string.format("%04d", subpixel)
  199. end
  200. function parseBarFormat(str)
  201.     local i, _ = string.find(str, "|")
  202.     if i == nil then
  203.         -- floor because we might have decimal, e.g. 1.51947
  204.         return math.floor(tonumber(str) * 0x1000)
  205.     else
  206.         local first = string.sub(str, 0, i - 1)
  207.         local second = string.sub(str, i + 1, nil)
  208.         i, _ = string.find(second, "|")
  209.         if i == nil then
  210.             -- pixel|subpixel
  211.             i, _ = string.find(first, "-")
  212.             if i == nil then
  213.                 return tonumber(first) * 0x1000 + tonumber(second)
  214.             else
  215.                 return tonumber(first) * 0x1000 - tonumber(second)
  216.             end
  217.         else
  218.             -- block|pixel|subpixel
  219.             local third = string.sub(second, i + 1, nil)
  220.             second = string.sub(second, 0, i - 1)
  221.             i, _ = string.find(first, "-")
  222.             if i == nil then
  223.                 return  tonumber(first) * 0x10000 + tonumber(second * 0x1000) + tonumber(third)
  224.             else
  225.                 return  tonumber(first) * 0x10000 - tonumber(second * 0x1000) - tonumber(third)
  226.             end
  227.         end
  228.     end
  229. end
  230. function formatNone(v)
  231.     return v
  232. end
  233.  
  234. -- Lua class for RAM Watch controls
  235. function GetAddress(pointerList)
  236.     local address = pointerList[1]
  237.     for i = 2, #pointerList do
  238.         address = read_u32(address) + pointerList[i]
  239.     end
  240.     return address
  241. end
  242.  
  243. WatchControls = {}
  244. WatchControls.x = 0
  245. WatchControls.y = 0
  246. WatchControls.value = 0
  247. WatchControls.editable = true
  248. function WatchControls:new(parentForm, x, y, width, ptr, name, readFunc, formatFunc, writeFunc, parseFunc)
  249.     -- Validate and set variables
  250.     if type(ptr) == "number" then
  251.         ptr = { ptr }
  252.     end
  253.     if (name ~= nil) then name = name .. ":" else name = "?:" end
  254.     if (readFunc == nil) then readFunc = read_u32 end
  255.     if (formatFunc == nil) then readFunc = formatNone end
  256.     if (parseFunc == nil) then parseFunc = tonumber end
  257.    
  258.     local newWatchControls = {}
  259.     newWatchControls.form = form
  260.     newWatchControls.ptr = ptr
  261.     newWatchControls.name = name
  262.     newWatchControls.readFunc = readFunc
  263.     newWatchControls.formatFunc = formatFunc
  264.     newWatchControls.writeFunc = writeFunc
  265.     newWatchControls.parseFunc = parseFunc
  266.    
  267.     -- Create controls
  268.     newWatchControls.label = forms.label(parentForm.handle, name, x, y, 0, 0, true)
  269.     forms.setproperty(newWatchControls.label, "AutoSize", true)
  270.     if writeFunc ~= nil then -- should be editable
  271.         x = x + forms.getproperty(newWatchControls.label, "Width")
  272.         newWatchControls.box = forms.textbox(parentForm.handle, "0", width, 18, nil, x, y, false, true)
  273.     end
  274.    
  275.     setmetatable(newWatchControls, self);
  276.     self.__index = self;
  277.     return newWatchControls
  278. end
  279. function WatchControls:updateDisplay()
  280.     local displayStr = self.formatFunc(self.readFunc(GetAddress(self.ptr)))
  281.     if self.writeFunc == nil then
  282.         forms.settext(self.label, self.name .. displayStr)
  283.     else
  284.         forms.settext(self.box, displayStr)
  285.     end
  286. end
  287. function WatchControls:applyEdits()
  288.     if self.writeFunc == nil then
  289.         return
  290.     end
  291.    
  292.     self.writeFunc(GetAddress(self.ptr), self.parseFunc(forms.gettext(self.box)))
  293. end
  294.  
  295. -- Set up form
  296. function createMainForm()
  297.     local form = {}
  298.     form.handle = forms.newform(210, 90, "NSMB Objects", function()
  299.         shouldExit = true
  300.         for k in pairs(detailForms) do
  301.             forms.destroy(detailForms[k].handle)
  302.         end
  303.     end)
  304.     forms.setDefaultTextBackground(form.handle, "Transparent")
  305.    
  306.     -- BizHawk is stupid and requires at least one element present.
  307.     -- It also requires that all elements already be strings.
  308.     form.objectDropDown = forms.dropdown(form.handle, {"placeholder"}, 10, 10, 180, 10)
  309.    
  310.     form.countLabel = forms.label(form.handle, "label", 10, 64)
  311.     forms.setproperty(form.countLabel, "AutoSize", true)
  312.  
  313.     form.getDetailsButton = forms.button(form.handle, "Get Details", function()
  314.         local index = tonumber(forms.getproperty(form.objectDropDown, "SelectedIndex"))
  315.         if index == -1 then
  316.             return
  317.         end
  318.        
  319.         local df = createObjectDetailsForm(objectsByDropDown[index + 1])
  320.         if objectsByDropDown[index + 1] == nil then
  321.             error("obj was nil, index: " .. index)
  322.         end
  323.         updateDetails(df)
  324.     end, 10, 35, 80, 24)
  325.    
  326.     return form
  327. end
  328. local mainForm = createMainForm()
  329.  
  330. function createObjectDetailsForm(objAddress)
  331.     local form = {}
  332.     form.obj = objAddress
  333.     local listIndex = #detailForms + 1
  334.     detailForms[#detailForms + 1] = form
  335.    
  336.     form.handle = forms.newform(300, 240, "Details for " .. getObjectTypeName(objAddress), function()
  337.         detailForms[listIndex] = nil
  338.     end)
  339.     forms.setDefaultTextBackground(form.handle, "Transparent")
  340.     form.objType = read_u16(objAddress + typeOffset)
  341.    
  342.     form.addressLabel = forms.label(form.handle, "Address: 0", 10, 10)
  343.     forms.setproperty(form.addressLabel, "AutoSize", true)
  344.  
  345.     -- Not all objects have the same properties
  346.     local _xVelOffset = XVelOffset
  347.     local _xVelTargetOffset = XVelTargetOffset
  348.     if form.objType == 0x1C then -- Mario
  349.         _xVelOffset = MarioXVelOffset
  350.         _xVelTargetOffset = MarioMaxSpeedOffset
  351.     end
  352.  
  353.     local verticalSeparation = 23
  354.     local y = 30
  355.     form.watches = {}
  356.     -- location
  357.     form.watches[#form.watches + 1] = WatchControls:new(
  358.         form, 10, y + verticalSeparation * 0, 100,
  359.         { form.obj + XOffset }, "X ",
  360.         read_s32, formatPosition, write_s32, parseBarFormat
  361.     )
  362.     form.watches[#form.watches + 1] = WatchControls:new(
  363.         form, 10, y + verticalSeparation * 1, 100,
  364.         { form.obj + YOffset }, "Y ",
  365.         read_s32, formatPosition, write_s32, parseBarFormat
  366.     )
  367.     -- velocity
  368.     form.watches[#form.watches + 1] = WatchControls:new(
  369.         form, 10, y + verticalSeparation * 2, 65,
  370.         { form.obj + _xVelOffset }, "X Speed",
  371.         read_s32, formatSpeed, write_s32, parseBarFormat
  372.     )
  373.     form.watches[#form.watches + 1] = WatchControls:new(
  374.         form, 10, y + verticalSeparation * 3, 65,
  375.         { form.obj + YVelOffset }, "Y Speed",
  376.         read_s32, formatSpeed, write_s32, parseBarFormat
  377.     )
  378.     -- size
  379.     form.watches[#form.watches + 1] = WatchControls:new(
  380.         form, 160, y + verticalSeparation * 0, 65,
  381.         { form.obj + widthOffset }, "Width ",
  382.         read_s32, formatSpeed, write_s32, parseBarFormat
  383.     )
  384.     form.watches[#form.watches + 1] = WatchControls:new(
  385.         form, 160, y + verticalSeparation * 1, 65,
  386.         { form.obj + heightOffset }, "Height",
  387.         read_s32, formatSpeed, write_s32, parseBarFormat
  388.     )
  389.     -- target velocity
  390.     form.watches[#form.watches + 1] = WatchControls:new(
  391.         form, 140, y + verticalSeparation * 2, 65,
  392.         { form.obj + _xVelTargetOffset }, "-> target",
  393.         read_s32, formatSpeed, write_s32, parseBarFormat
  394.     )
  395.     form.watches[#form.watches + 1] = WatchControls:new(
  396.         form, 140, y + verticalSeparation * 3, 65,
  397.         { form.obj + YVelTargetOffset }, "-> target",
  398.         read_s32, formatSpeed, write_s32, parseBarFormat
  399.     )
  400.  
  401.     form.tangibleCheckbox = forms.checkbox(form.handle, "Tangible", 10, y + verticalSeparation * 4)
  402.  
  403.     local applyButton = forms.button(form.handle, "Apply", function()
  404.         apply(form)
  405.     end, 200, y + verticalSeparation * 4)
  406.    
  407.     -- collision checking
  408.     local y = y + verticalSeparation * 5 + 10
  409.     form.collisionSelectLabel = forms.label(form.handle, "Check collision:", 10, y + 2)
  410.     forms.setproperty(form.collisionSelectLabel, "AutoSize", true)
  411.     form.objectDropDown = forms.dropdown(form.handle, { "placeholder" }, 96, y, 178, 10)
  412.     refreshObjectDropDown(form.objectDropDown, 0)
  413.  
  414.     forms.setproperty(form.objectDropDown, "SelectedIndex", -1)
  415.    
  416.     form.collisionDistanceLabel = forms.label(form.handle, "Distance to collide:", 10, y + 25)
  417.     form.collisionDistanceHLabel = forms.label(form.handle, "0", 30, y + 40, 0, 0, true)
  418.     form.collisionDistanceVLabel = forms.label(form.handle, "0", 30, y + 55, 0, 0, true)
  419.     forms.setproperty(form.collisionDistanceLabel, "AutoSize", true)
  420.     forms.setproperty(form.collisionDistanceHLabel, "AutoSize", true)
  421.     forms.setproperty(form.collisionDistanceVLabel, "AutoSize", true)
  422.  
  423.     return form
  424. end
  425.  
  426. function updateDetails(detailsForm)
  427.     local obj = detailsForm.obj
  428.     if read_u32(obj) == 0 or read_u16(obj + typeOffset) ~= detailsForm.objType then
  429.         forms.settext(detailsForm.addressLabel, "Address: Despawned")
  430.        
  431.         for i = 1, #detailsForm.watches do
  432.             if detailsForm.watches[i].box ~= nil then
  433.                 forms.settext(detailsForm.watches[i].box, "Despawned")
  434.             else
  435.                 forms.settext(detailsForm.watches[i].label, "Despawned")
  436.             end
  437.         end
  438.     else
  439.         forms.settext(detailsForm.addressLabel, "Address: 0x" .. hex(obj, 8))
  440.         forms.setproperty(detailsForm.tangibleCheckbox, "Checked", isTangible(obj))
  441.        
  442.         for i = 1, #detailsForm.watches do
  443.             detailsForm.watches[i]:updateDisplay()
  444.         end
  445.        
  446.         local index = tonumber(forms.getproperty(detailsForm.objectDropDown, "SelectedIndex"))
  447.         if spawnActivity then
  448.             refreshObjectDropDown(detailsForm.objectDropDown, detailsForm.selectedCollisionObj)
  449.             index = tonumber(forms.getproperty(detailsForm.objectDropDown, "SelectedIndex"))
  450.         end
  451.         if index ~= -1 then
  452.             detailsForm.selectedCollisionObj = objectsByDropDown[index + 1]
  453.             local this = {
  454.                 X = read_s32(obj + XOffset),
  455.                 Y = read_s32(obj + YOffset),
  456.                 Width = read_s32(obj + widthOffset),
  457.                 Height = read_s32(obj + heightOffset),
  458.             }
  459.             local otherObj = detailsForm.selectedCollisionObj
  460.             local other = {
  461.                 X = read_s32(otherObj + XOffset),
  462.                 Y = read_s32(otherObj + YOffset),
  463.                 Width = read_s32(otherObj + widthOffset),
  464.                 Height = read_s32(otherObj + heightOffset),
  465.             }
  466.             local distanceRight = math.max(0, (other.X - other.Width) - (this.X + this.Width))
  467.             local distanceLeft = math.max(0, (this.X - this.Width) - (other.X + other.Width))
  468.             local distanceH = math.max(distanceRight, distanceLeft)
  469.             forms.settext(detailsForm.collisionDistanceHLabel, formatPosition(distanceH))
  470.             local distanceUp = math.max(0, (other.Y) - (this.Y + this.Height))
  471.             local distanceDown = math.max(0, (this.Y) - (other.Y + other.Height))
  472.             local distanceV = math.max(distanceUp, distanceDown)
  473.             forms.settext(detailsForm.collisionDistanceVLabel, formatPosition(distanceV))
  474.         end
  475.     end
  476. end
  477.  
  478. function apply(detailsForm)
  479.     function inner()
  480.         local obj = detailsForm.obj
  481.         if read_u32(obj) == 0 then
  482.             return
  483.         end
  484.                
  485.         local flags = read_u8(obj + someFlagsOffset)
  486.         if forms.ischecked(detailsForm.tangibleCheckbox) then
  487.             flags = bit.set(flags, 0)
  488.         else
  489.             flags = bit.clear(flags, 0)
  490.         end
  491.         write_u8(obj + someFlagsOffset, flags)
  492.        
  493.         for i = 1, #detailsForm.watches do
  494.             detailsForm.watches[i]:applyEdits()
  495.         end
  496.     end
  497.    
  498.     local result = pcall(inner)
  499.     if not result then
  500.         console.log("Failed to apply changes. Ensure your inputs are in the correct format.")
  501.     end
  502. end
  503.  
  504. function getNewObjects(first, second)
  505.     local list = {}
  506.     for key in pairs(first) do
  507.         if second[key] == nil then
  508.             list[#list + 1] = key
  509.         end
  510.     end
  511.     return list
  512. end
  513.  
  514. function refreshObjectsList()
  515.     -- Update objects list
  516.     local currentObjects = {}
  517.     local addrCurrentNode = GetFirstObject()
  518.     objectCount = 0
  519.     while true do
  520.         addrCurrentNode = GetNextObject(addrCurrentNode)
  521.         if addrCurrentNode == 0 then
  522.             break
  523.         end
  524.        
  525.         if currentObjects[addrCurrentNode] ~= nil then
  526.             error("Encountered repeat object. This indicates memory is corrupt or the script thinks you're in a level when you're not.")
  527.         end
  528.        
  529.         currentObjects[addrCurrentNode] = true
  530.         objectCount = objectCount + 1
  531.         if objectCount > 200 then
  532.             error("Error: Found over 200 objects. This probably means there is a problem, because there shouldn't be that many objects.")
  533.         end
  534.     end
  535.    
  536.     -- Which objects are new, which despawned?
  537.     local despawned = getNewObjects(allObjects, currentObjects)
  538.     local newObjs = getNewObjects(currentObjects, allObjects)
  539.     spawnActivity = #newObjs > 0 or #despawned > 0
  540.     for i = 1, #newObjs do
  541.         local objType = read_u16(newObjs[i] + typeOffset)
  542.         local text = objectTypes[objType]
  543.         if text == nil then
  544.             text = "Unknown (Type 0x" .. hex(objType) .. ")"
  545.         end
  546.         console.log("Spawned " .. text)
  547.     end
  548.     for i = 1, #despawned do
  549.         local objType = read_u16(despawned[i] + typeOffset)
  550.         local text = objectTypes[objType]
  551.         if text == nil then
  552.             text = "Unknown (Type 0x" .. hex(objType) .. ")"
  553.         end
  554.         console.log("Despawned " .. text)
  555.     end
  556.    
  557.     allObjects = currentObjects
  558. end
  559.  
  560. local dropDownItems = nil
  561. function refreshObjectDropDownList()
  562.     dropDownItems = {}
  563.     objectsByDropDown = {}
  564.     for key in pairs(allObjects) do
  565.         local objType = read_u16(key + typeOffset)
  566.         local text = objectTypes[objType]
  567.         if text == nil then
  568.             text = "Unknown (Type 0x" .. hex(objType) .. ")"
  569.         end
  570.         text = text
  571.         dropDownItems[#dropDownItems + 1] = text
  572.         objectsByDropDown[#objectsByDropDown + 1] = key
  573.     end
  574. end
  575. function refreshObjectDropDown(dropDown, previousSelectedObj)
  576.     -- BizHawk is stupid and attempts to set SelectedIndex to 0 after updating the list
  577.     if #dropDownItems == 0 then
  578.         forms.setdropdownitems(dropDown, { "No objects" }, false)
  579.     else
  580.         forms.setdropdownitems(dropDown, dropDownItems, false)
  581.     end
  582.     forms.setproperty(dropDown, "SelectedIndex", -1)
  583.     for i = 1, #objectsByDropDown do
  584.         if objectsByDropDown[i] == previousSelectedObj then
  585.             forms.setproperty(dropDown, "SelectedIndex", i - 1)
  586.         end
  587.     end
  588. end
  589.  
  590. while not shouldExit do
  591.     refreshObjectsList()
  592.     if spawnActivity then
  593.         forms.settext(mainForm.countLabel, "Number of objects: " .. objectCount)
  594.         local selectedIndex = forms.getproperty(mainForm.objectDropDown, "SelectedIndex")
  595.         local selectedObject = objectsByDropDown[selectedIndex + 1]
  596.         refreshObjectDropDownList()
  597.         refreshObjectDropDown(mainForm.objectDropDown, selectedObject)
  598.     end
  599.    
  600.     for key in pairs(detailForms) do
  601.         updateDetails(detailForms[key])
  602.     end
  603.    
  604.     emu.frameadvance()
  605. end
  606.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement