Advertisement
Guest User

S2 & S3&K Speedometer

a guest
May 6th, 2012
623
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 22.63 KB | None | 0 0
  1. --------------------------------------------------------------------------------
  2. --------------------------------------------------------------------------------
  3. ------ Sonic Speedometer v1.0 --------------------------------------------------
  4. ------ For multiple versions ---------------------------------------------------
  5. --------------------------------------------------------------------------------
  6. --------------------------------------------------------------------------------
  7.  
  8. -- This script shows several interesting values read directly out of the game
  9. -- engine.  All of these, except for the speedometer, will autohide themselves.
  10. -- This currently works for Sonic 2 and Sonic 3&K.
  11. -- Use Gens Rerecording http://code.google.com/p/gens-rerecording/
  12. -- Items marked with a + will appear when you press Up + C; tap C multiple times
  13. -- to get them to stay onscreen longer.  Or, use Up + B to toggle them.
  14. --  + Checkpoint: Shows most recent checkpoint.
  15. --  + Remaining: Shows how many rings are left to collect until you get a
  16. --    Perfect Bonus.
  17. --  + Circular thing with a number: This shows you the distance and bearing to
  18. --    the nearest ring.  In other words, the needle in the circle shows you in
  19. --    what direction the nearest ring lies, and the number next to it shows how
  20. --    far away the ring is.
  21. --    Yes, I know this won't always give a correct distance/bearing for
  22. --    Metropolis Zone due to the wrapping.
  23. --  + UP D/L/R: These change color to show which collision collision plane is
  24. --    active: yellow for the primary, cyan for the secondary.
  25. --  - LOCK: Shows up when a control lock engages.
  26. --  - Air: Shows up under water.  Shows how how many seconds of air are left.
  27. --  - Invincible: This timer shows up when you get an invincibility monitor.
  28. --  - Speed Shoe: When this timer show up, it shows what you'd think it shows.
  29. --  - Unlabeled red number: Shows how many FRAMES of temporary invunerability
  30. --    you have left.
  31. --  - Flying: Shows up when Tails is flying
  32. --  - Continues: Reminds you how many continues you have got.
  33. --  - Speed: This shows your speed.  The dial also features a digital readout
  34. --    of your speed below.  The gray needle bounces up and down to show your
  35. --    most recent maximum speed; the red number above shows what this speed is.
  36. --    You can change the range of the speedometer
  37. --  - Boost: Shows up only when you do a spin dash.  Freezes to show how
  38. --    powerful the dash was.
  39. --
  40. -- Lua is supposed to be easy to understand, even for non-coders.  You shouldn't
  41. -- have trouble changing what gets shown on screen.
  42. --
  43. -- To Do:
  44. --  - Fix ring finder for Metropolis Zone and other zones that loop
  45. --  - Implement non-sucky arc-drawing and fill algorithms.  I'll probably never
  46. --    actually do this, but feel free to contribute one for me.
  47. --
  48. -- Consider this script to be given out under the GPL. --- DrDnar, 1 May 2012
  49. --
  50. -- Change log:
  51.  
  52.  
  53.  
  54. --------------------------------------------------------------------------------
  55. ------ Game-specific Data ------------------------------------------------------
  56. --------------------------------------------------------------------------------
  57.  
  58. isPaused = {}
  59. gameActive = {}
  60. buttonsChanged = {}
  61. perfectCounter = {}
  62. xLocation = {}
  63. yLocation = {}
  64. xSpeed = {}
  65. ySpeed = {}
  66. continues = {}
  67. checkpoint = {}
  68. airRemaining = {}
  69. hLockTimer = {}
  70. invulnTimer = {}
  71. invincTimer = {}
  72. speedShoeTimer = {}
  73. spinDashFlag = {}
  74. spinDashCharge = {}
  75. collisionsUpFlag = {}
  76. collisionsDLRFlag = {}
  77. ringTable = {}
  78. checkpoint = {}
  79. spriteTable = {}
  80. currentCharacter = {}
  81.  
  82. function ReadROM(start, length)
  83.     array = {}
  84.     for n = 0, length-1 do
  85.         array[n+1] = memory.readbyte(start + n)
  86.     end
  87.     return array
  88. end
  89.  
  90. function ArrayEquals(one, two)
  91.     local n = 1
  92.     while one[n] ~= nil and two[n] ~= nil do
  93. --      print(string.format("%2X %2X | ", one[n], two[n]))
  94.         if one[n] ~= two[n] then return false end
  95.         n = n + 1
  96.     end
  97.     if one[n] ~= two[n] then return false end
  98.     return true
  99. end
  100.  
  101. romIDLocation = 0x180
  102. romIDLength = 14
  103. s1id = {0x47, 0x4D, 0x20, 0x30, 0x30, 0x30, 0x30, 0x34, 0x30, 0x34, 0x39, 0x2D, 0x30, 0x31}
  104. --        G     M           0     0     0     0     4     0     4     9     -     0     1
  105. sonic1 = 1
  106.  
  107. s2id = {0x47, 0x4D, 0x20, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x35, 0x31, 0x2D, 0x30, 0x31}
  108. --        G     M           0     0     0     0     1     0     5     1     -     0     1
  109. sonic2 = 2
  110.  
  111. s3kid = {0x47, 0x4D, 0x20, 0x4D, 0x4B, 0x2D, 0x31, 0x35, 0x36, 0x33, 0x20, 0x2D, 0x30, 0x30}
  112. --         G     M           M     K     -     1     5     6     3           -     0     0
  113. sonic3k = 3
  114.  
  115. --if ArrayEquals(s1id, ReadROM(romIDLocation, romIDLength)) then
  116. --  isS1 = true
  117. --  currentGame = sonic1
  118. --end
  119.  
  120. if ArrayEquals(s2id, ReadROM(romIDLocation, romIDLength)) then
  121.     print("ROM type: S2")
  122.     isS2 = true
  123.     currentGame = sonic2
  124.     isPaused.location = 0xFFF63B
  125.     gameActive.location = 0xFFF711
  126.     buttonsChanged.location = 0xFFF605
  127.     perfectCounter.location = 0xFFFF40
  128.     xLocation.location = 0xFFB008
  129.     yLocation.location = 0xFFB00C
  130.     xSpeed.location = 0xFFB010
  131.     ySpeed.location = 0xFFB012
  132.     continues.location = 0xFFFE18
  133.     checkpoint.location = 0xFFFE30
  134.     airRemaining.location = 0xFFB028
  135.     hLockTimer.location = 0xFFB02E
  136.     function invulnTimer.get() return memory.readwordunsigned(0xFFB030) end
  137.     function invincTimer.get() return memory.readwordunsigned(0xFFB032) end
  138.     function speedShoeTimer.get() return memory.readwordunsigned(0xFFB034) end
  139.     spinDashFlag.location = 0xFFB039
  140.     spinDashFlag.exists = true
  141.     spinDashCharge.location = 0xFFB03A
  142.     collisionsUpFlag.location = 0xFFB03E
  143.     collisionsUpFlag.exists = true
  144.     collisionsDLRFlag.location = 0xFFB03F
  145.     ringTable.location = 0xFFE806
  146.     ringTable.length = 0x600
  147.     ringTable.type = sonic2
  148.     checkpoint.location = 0xFFFE30
  149.     spriteTable.itemSize = 0x40
  150.     spriteTable.location = 0xFFB000
  151. end
  152.  
  153. if ArrayEquals(s3kid, ReadROM(romIDLocation, romIDLength)) then
  154.     print("ROM type: S3&K")
  155.     isS3K = true
  156.     currentGame = sonic3k
  157.     isPaused.location = 0xFFF63B
  158.     gameActive.location = 0xFFF711
  159.     buttonsChanged.location = 0xFFF605
  160.     perfectCounter.location = 0xFFFF04
  161.     xLocation.location = 0xFFB010
  162.     yLocation.location = 0xFFB014
  163.     xSpeed.location = 0xFFB018
  164.     ySpeed.location = 0xFFB01A
  165.     continues.location = 0xFFFE18
  166.     checkpoint.location = 0xFFFE2A
  167.     airRemaining.location = 0xFFB02C
  168.     hLockTimer.location = 0xFFB032
  169.     function invulnTimer.get() return memory.readbyteunsigned(0xFFB034) end
  170.     function invincTimer.get() return memory.readbyteunsigned(0xFFB035)*8 end
  171.     function speedShoeTimer.get() return memory.readbyteunsigned(0xFFB036)*8 end
  172.     spinDashFlag.location = 0xFFB03D
  173.     spinDashFlag.exists = true
  174.     spinDashCharge.location = 0xFFB03E
  175.     collisionsUpFlag.location = 0xFFB046
  176.     collisionsDLRFlag.location = 0xFFB047
  177.     ringTable.location = 0xFFE806
  178.     ringTable.length = 0x600
  179.     ringTable.type = sonic3k
  180.     checkpoint.location = 0xFFFE30
  181.     spriteTable.itemSize = 0x4A
  182.     spriteTable.location = 0xFFB000
  183.     currentCharacter.location = 0xFFFF08
  184. end
  185.  
  186. if currentGame == nil then
  187.     error("Possibily unsupported game. Script editing is required.")
  188. end
  189.  
  190.  
  191.  
  192. --------------------------------------------------------------------------------
  193. ------ Meter Object ------------------------------------------------------------
  194. --------------------------------------------------------------------------------
  195. -- This object lets you draw a generic meter or dial, with multiple needles.
  196. -- To declare a meter, do
  197. --  - Meter:Create(baseX, baseY, size, thetaMin, thetaMax, scaleInc, fgColor, bgColor, fill)
  198. --    - baseX, baseY: X&Y location for the meter
  199. --    - size: Radius of the meter
  200. --    - thetaMin, thetaMax: These variables control the arc that the meter's
  201. --      range will sweep.  You could, for example, have the meter sweep a range
  202. --      less than the semicircle used in this example.
  203. --    - scaleInc: If not nil, this controls how often tic marks appear on the
  204. --      meter's scale.  Specified as an angular incrememt.
  205. --    - fgColor, bgColor: Controls the color of the main outer arc and the
  206. --      shadowing of that arc.
  207. --    - fill: Controls the fill color of the arc.
  208. --    * The color values tend to look funny if you specify an opacity that isn't
  209. --      fully opaque or transparent.
  210. -- This function will return a new Meter object.  Don't forget to assign it to a
  211. -- variable:
  212. --  - aMeter = Meter:Create(...)
  213. -- To draw the meter on the screen, first do
  214. --  - aMeter:DrawMeter()
  215. -- to draw the meter's basic graphic.  Then, for each needle value you want to
  216. -- show, do
  217. --  - aMeter:DrawNeedle(percentage, color, saturate, size)
  218. --    - percentage: A number between 0 and 1 that represents the fraction of the
  219. --      arc between thetaMin and thetaMax that the needle should point to.
  220. --    - color: The color of the needle line.  At the moment, there is no support
  221. --      for shadowing the needle color.
  222. --    - saturate: If true, then any percentage value less than 0 will be treated
  223. --      as zero, and any value greater than 1 will be treated as one.
  224. --    - size: Controls the length (radius) of the needle.
  225. -- The parameters after color may be omitted.  They will default to
  226. --    - color: fgColor passed to Create() method
  227. --    - saturate: false
  228. --    - size: size passed to Create() method.
  229.  
  230. Meter = {}
  231. Meter.__index = Meter
  232.  
  233. function Meter:Create(baseX, baseY, size, thetaMin, thetaMax, scaleInc, fgColor, bgColor, fill)
  234.     local newItem = {}
  235.     setmetatable(newItem, Meter)  -- Bind object methods to new object
  236.     newItem.baseX = baseX -- x location
  237.     newItem.baseY = baseY -- y location
  238.     newItem.thetaMin = thetaMin
  239.     newItem.thetaMax = thetaMax
  240.     newItem.thetaDelta = thetaMax - thetaMin
  241.     newItem.size = size -- size
  242.     newItem.scaleInc = scaleInc
  243.     newItem.bg = bgColor
  244.     newItem.fg = fgColor
  245.     newItem.fill = fill
  246.     return newItem
  247. end
  248.  
  249. function Meter:DrawMeter()
  250.     local inc = math.asin(1/self.size)/(self.size/2)
  251.     local start, stop = self.thetaMin, self.thetaMax
  252.     if start > stop then
  253.         start, stop = stop, start
  254.     end
  255.     local theta = start
  256.     local size, basex, basey = self.size, self.baseX, self.baseY
  257.     local cos, sin
  258.    
  259.     while theta < stop do
  260.         cos = math.cos(theta)
  261.         sin = math.sin(theta)
  262.         gui.pixel(sin*size + basex, cos*size + basey, self.fg)
  263.         gui.pixel(sin*(size+0.5) + basex, cos*(size+0.5) + basey, self.fg)
  264.         --gui.pixel(sin*(size-1) + basex, cos*(size-1) + basey, self.fg)
  265.         if self.bg ~= nil then
  266.             gui.pixel(sin*(size-1) + basex, cos*(size-1) + basey, self.bg)
  267.             gui.pixel(sin*(size-1.5) + basex, cos*(size-1.5) + basey, self.bg)
  268.         end
  269.         if self.bg ~= nil then
  270.             gui.line(basex, basey, sin*(size-2) + basex, cos*(size-2) + basey, self.fill)
  271.         end
  272.         theta = theta + inc
  273.     end
  274.     theta = start
  275.     while theta < stop do
  276.         cos = math.cos(theta)
  277.         sin = math.sin(theta)
  278.         if self.scaleInc ~= nil and ((theta-start)/self.scaleInc)%1 * self.scaleInc > (self.scaleInc-(2*inc)) then
  279.             gui.pixel(sin*(size-1) + basex, cos*(size-1) + basey, self.fg)
  280.             gui.pixel(sin*(size-1.5) + basex, cos*(size-1.5) + basey, self.fg)
  281.             gui.pixel(sin*(size-2) + basex, cos*(size-2) + basey, self.fg)
  282.             gui.pixel(sin*(size-2.5) + basex, cos*(size-2.5) + basey, self.fg)
  283. --          gui.line(sin*(size-1) + basex, cos*(size-1) + basey, sin*(size-3) + basex, cos*(size-3) + basey, self.fg)
  284.         end
  285.         theta = theta + inc
  286.     end
  287.  
  288. end
  289.  
  290. function Meter:DrawNeedle(percent, fgColor, saturate, size)
  291.     if saturate and percent > 1 then
  292.         percent = 1
  293.     end
  294.     if saturate and percent < 0 then
  295.         percent = 0
  296.     end
  297.     if fgColor == nil then
  298.         fgColor = self.fgColor
  299.     end
  300.     if size == nil then
  301.         size = self.size - 3
  302.     end
  303.     local theta = percent * self.thetaDelta + self.thetaMin
  304.     local sin, cos = math.cos(theta), math.sin(theta)
  305.    
  306.     gui.line(self.baseX, self.baseY, size*cos + self.baseX, size*sin + self.baseY, fgColor)
  307. end
  308.  
  309.  
  310.  
  311. --------------------------------------------------------------------------------
  312. ------ Globals -----------------------------------------------------------------
  313. --------------------------------------------------------------------------------
  314.  
  315. lastSpindashValue = 0
  316. boostMeter = Meter:Create(231, 214, 20, 3*math.pi/2, math.pi/2, math.pi/8, 0xFFFF00FF, 0x000000FF, 0x202020FF)
  317. boostTimer = 0
  318. speedometer = Meter:Create(287, 214, 20, 3*math.pi/2, math.pi/2, math.pi/8, 0xFFFF00FF, 0x000000FF, 0x202020FF)
  319. maxSpeed = 0
  320. maxMaxSpeed = 0
  321. maximumSpeed = 4096
  322. ringFinder = Meter:Create(27, 72, 10, math.pi*2+math.pi/2, math.pi/2, math.pi/4, 0xFFFF00FF, 0x000000FF, 0x202020FF)
  323. lastRingBearing = 0
  324. showThingsTimer = 0
  325. showThingsFlag = false
  326. fps = 60 -- frames per second, change if needed
  327.  
  328. nearestDistance = 0 -- needs init, not really global though
  329.  
  330.  
  331. --------------------------------------------------------------------------------
  332. ------ Show Stuff on-Screen ----------------------------------------------------
  333. --------------------------------------------------------------------------------
  334.  
  335. gui.register( function ()
  336. -- Hide the additional HUD elements if we're not in the normal game loop.
  337. if memory.readbyte(gameActive.location) ~= 0 then
  338.  
  339.  
  340.     perfectCounter.value = memory.readwordunsigned(perfectCounter.location)
  341.     -- Cache Sonic's location and other things
  342.     xLocation.value = memory.readwordunsigned(xLocation.location)
  343.     yLocation.value = memory.readwordunsigned(yLocation.location)
  344.     xSpeed.value = memory.readwordsigned(xSpeed.location)
  345.     ySpeed.value = memory.readwordsigned(ySpeed.location)
  346.     perfectCounter.value = memory.readwordunsigned(perfectCounter.location)
  347.     if currentGame == sonic3k then
  348.         currentCharacter.value = memory.readwordunsigned(currentCharacter.location)
  349.     end
  350.    
  351.  
  352. -- Autohide various things
  353.  
  354.     isPaused.value = memory.readbyte(isPaused.location) ~= 0
  355.     currentButtons = joypad.get(1)
  356.     if currentButtons.up and AND(memory.readbyte(buttonsChanged.location), 0x20) == 0x20 then
  357.         showThingsTimer = showThingsTimer + 120
  358.         showThingsFlag = false
  359.     else
  360.         if not isPaused.value and showThingsTimer ~= 0 then
  361.             showThingsTimer = showThingsTimer - 1
  362.         end
  363.     end
  364.     if currentButtons.up and AND(memory.readbyte(buttonsChanged.location), 0x10) == 0x10 then
  365.         if showThingsFlag then
  366.             showThingsFlag = false
  367.         else
  368.             showThingsFlag = true
  369.             showThingsTimer = 1
  370.         end
  371.     end
  372.     if showThingsFlag then
  373.         showThingsTimer = 1
  374.     end
  375.     if showThingsTimer == 0 then
  376.         showThingsFlag = false
  377.     end
  378.  
  379.  
  380. -- Ring finder
  381.  
  382.     haveBearing = false
  383.     -- Sonic 2
  384.     if perfectCounter.value ~= 0 and not isPaused.value and currentGame == sonic2 then
  385.         -- Initalize scanning loop
  386.         keepGoing = true
  387.         address = 0xFFE806
  388.         nearestDistance = 65535
  389.         nearestBearing = 0
  390.        
  391.         -- Scan the ring table in RAM, keeping track of the smallest distance found.
  392.         -- The ring table has 6-byte entries.  The first word is the
  393.         -- destruction animation counter.  The next two are simply the x and y
  394.         -- location.  0xFFFFFFFF marks the end of the table.
  395.         while keepGoing and address < 0xFFEDFF do
  396.             value = memory.readwordunsigned(address)
  397.             if value == 0 then
  398.                 deltax = memory.readwordunsigned(address+2) - xLocation.value
  399.                 deltay = memory.readwordunsigned(address+4) - yLocation.value
  400.                 value = math.sqrt(deltax*deltax + deltay*deltay)
  401.                 if value < nearestDistance then
  402.                     nearestDistance = value
  403.                     nearestBearing = math.atan2(deltay, deltax)
  404.                 end
  405.                 haveBearing = true
  406.             else
  407.                 if value == 0xFFFF and memory.readwordunsigned(address+2) == 0xFFFF then
  408.                     keepGoing = false
  409.                 end
  410.             end
  411.             address = address + 6
  412.         end
  413.     end
  414.     -- S3&K
  415.     if perfectCounter.value ~= 0 and (currentGame == sonic3k) then
  416.         haveBearing = true
  417.         -- Scan backwards
  418.         keepGoing = true
  419.         address = memory.readwordunsigned(0xFFEE42)
  420.         address = address * 65536 + memory.readwordunsigned(0xFFEE44)
  421.         status = 0xFF0000 + memory.readwordunsigned(0xFFEE4A)
  422.         nearestDistance = 65535
  423.         nearestBearing = 0
  424.         while keepGoing do
  425.             value = memory.readwordunsigned(address)
  426.             if value ~= 0 then
  427.                 value = memory.readwordunsigned(status)
  428.                 if memory.readwordunsigned(status) == 0 then
  429.                     deltax = memory.readwordunsigned(address) - xLocation.value
  430.                     deltay = memory.readwordunsigned(address+2) - yLocation.value
  431.                     value = math.sqrt(deltax*deltax + deltay*deltay)
  432.                     if value < nearestDistance then
  433.                         nearestDistance = value
  434.                         nearestBearing = math.atan2(deltay, deltax)
  435.                         haveBearing = true
  436.                     end
  437.                 end
  438.             else
  439.                 keepGoing = false
  440.             end
  441.             address = address - 4
  442.             status = status - 2
  443.         end
  444.         -- Scan forwards
  445.         keepGoing = true
  446.         address = memory.readwordunsigned(0xFFEE42)
  447.         address = address * 65536 + memory.readwordunsigned(0xFFEE44)
  448.         status = 0xFF0000 + memory.readwordunsigned(0xFFEE4A)
  449.         while keepGoing do
  450.             value = memory.readwordunsigned(address)
  451.             if value ~= 0xFFFF then
  452.                 if memory.readwordunsigned(status) == 0 then
  453.                     deltax = memory.readwordunsigned(address) - xLocation.value
  454.                     deltay = memory.readwordunsigned(address+2) - yLocation.value
  455.                     value = math.sqrt(deltax*deltax + deltay*deltay)
  456.                     if value < nearestDistance then
  457.                         nearestDistance = value
  458.                         nearestBearing = math.atan2(deltay, deltax)
  459.                         haveBearing = true
  460.                     end
  461.                 end
  462.             else
  463.                 keepGoing = false
  464.             end
  465.             address = address + 4
  466.             status = status + 2
  467.         end
  468.     end
  469.     if haveBearing then
  470.         if not isPaused.value then
  471.             -- Make 0 < nearestBearing < 2*pi
  472.             if nearestBearing < 0 then nearestBearing = nearestBearing + 2*math.pi end
  473.             -- Now remap that to be between 0 and 1 (input for the DrawNeedle() method)
  474.             nearestBearing = nearestBearing/(2*math.pi)
  475.             -- Here's where we get to the fancy animation.  This finds the
  476.             -- direction the needle needs to swing in to make the shortest arc to
  477.             -- the current bearing.
  478.             smallestDelta = 1
  479.             for n = -1, 1 do
  480.                 delta = math.abs(nearestBearing - lastRingBearing - n)
  481.                 if smallestDelta > delta then
  482.                     smallestDelta = delta
  483.                     value = nearestBearing - lastRingBearing - n
  484.                 end
  485.             end
  486.             -- Then, by dividing by a number, we make it swing to the location,
  487.             -- instead of jumping there instantly.
  488.             if math.abs(value) > 0.006 then -- Implement minimum speed
  489.                 value = (value/math.abs(value)) * (value*value / 3 + 0.002)
  490.                 if math.abs(value) > 0.03 then -- Implement maximum speed
  491.                     value = 0.03 * value/math.abs(value)
  492.                 end
  493.             end
  494.             lastRingBearing = value + lastRingBearing
  495.             lastRingBearing = lastRingBearing - math.floor(lastRingBearing)
  496.             nearestDistance = nearestDistance - 14
  497.             if nearestDistance < 0 then nearestDistance = 0 end
  498.         end
  499.  
  500.         -- Now show the results, if wanted.  Note that the needle still
  501.         -- swings when it isn't visible.
  502.         if showThingsTimer ~= 0 then
  503.             ringFinder:DrawMeter()
  504.             ringFinder:DrawNeedle(lastRingBearing, 0xFFFF00FF)
  505.             gui.text(40, 64, string.format("%d", nearestDistance), 0xFFFF00FF, 0x000000FF)
  506.         end
  507.     end
  508.  
  509.  
  510. -- Speed
  511.  
  512.     value = math.sqrt(xSpeed.value*xSpeed.value + ySpeed.value*ySpeed.value)
  513.    
  514.     speedometer:DrawMeter()
  515.     if value > maxSpeed then
  516.         maxSpeed = value
  517.         maxMaxSpeed = value
  518.     else
  519.         if not isPaused.value then
  520.             maxSpeed = maxSpeed - 16
  521.             if maxSpeed < 0 then
  522.                 maxSpeed = 0
  523.                 maxMaxSpeed = 0
  524.             end
  525.         end
  526.     end
  527.     if maxMaxSpeed ~= 0 then
  528.         gui.text(290, 184, string.format("%5d", maxMaxSpeed), 0xFF0000FF, "black")
  529.     end
  530.     gui.text(266, 216, string.format("Speed:%5d", value), 0xFFFF00FF, "black")
  531.     speedometer:DrawNeedle(maxSpeed/maximumSpeed, 0xFF0000FF, false, speedometer.size-4)
  532.     speedometer:DrawNeedle(value/maximumSpeed, 0xFFFF00FF)
  533.  
  534.  
  535. -- Perfect counter
  536.    
  537.     if perfectCounter then
  538.     if showThingsTimer ~= 0 then
  539.     color = 0xFFFF00FF
  540.     bgColor = 0x000000FF
  541.     value = perfectCounter.value
  542.     if value == 0 then
  543.         color = 0xFFFFFF60
  544.         bgColor = 0x00000060
  545.     end
  546.     gui.text(17, 54, string.format("Remaining: %3d", value), color, bgColor)
  547.     end
  548.     end
  549.  
  550.  
  551. -- Spin-dash "Boost meter"
  552.  
  553.     if spinDashFlag then
  554.     color = 0xFFFF00FF--0xFFFF00A0
  555.     bgColor = 0x000000FF--0x000000A0
  556.     boostColor = 0x808080FF
  557.     if memory.readbyte(spinDashFlag.location) ~= 0 then
  558.         lastSpindashValue = memory.readword(spinDashCharge.location)
  559.         boostColor = 0xFF2020FF
  560.         boostTimer = 90
  561.     else
  562.         if not isPaused.value then
  563.             if boostTimer > 0 then boostTimer = boostTimer - 1 end
  564.         end
  565.     end
  566.     if boostTimer > 0 then
  567.         boostMeter:DrawMeter()
  568.         boostMeter:DrawNeedle(lastSpindashValue/0x800, boostColor)
  569.         message = string.format("Boost: %4d", lastSpindashValue)
  570.         gui.text(210, 216, message, color, bgColor)
  571.     end
  572.     end
  573.  
  574.  
  575. -- Collisions selection
  576.  
  577.     if showThingsTimer ~= 0 then
  578.     color = 0xFFFF00FF
  579.     bgColor = 0x000000FF
  580.     if memory.readbyte(collisionsUpFlag.location) ~= 0x0C then
  581.         color = 0x00FFFFFF
  582.     end
  583.     gui.text(276, 0, "UP", color, bgColor)
  584.     color = 0xFFFF00FF
  585.     bgColor = 0x000000FF
  586.     if memory.readbyte(collisionsDLRFlag.location) ~= 0x0D then
  587.         color = 0x00FFFFFF
  588.     end
  589.     gui.text(290, 0, "D/L/R", color, bgColor)
  590.     end
  591.  
  592.  
  593. -- Horizontal control lock
  594.    
  595.     value = memory.readwordunsigned(hLockTimer.location)
  596.     if value ~= 0 then
  597.         gui.text(300, 18, "LOCK", 0xFF0000FF, 0x000000FF)
  598.     end
  599.  
  600.    
  601. -- Continues
  602.  
  603.     if showThingsTimer ~= 0 then
  604.     color = 0xFFFF0080
  605.     bgColor = 0x00000080
  606.     gui.text(17, 216, string.format("Continues: %d", memory.readbyte(continues.location)), color, bgColor)
  607.     end
  608.  
  609.  
  610. -- Checkpoint
  611.  
  612.     if showThingsTimer ~= 0 then
  613.     color = 0xFFFFFF80
  614.     bgColor = 0x00000080
  615.     value = memory.readbyteunsigned(checkpoint.location)
  616.     if value ~= 0 then
  617.         color = 0xFFFF00FF
  618.         bgColor = 0x000000FF
  619.     end
  620.     gui.text(17, 0, string.format("Checkpoint: %d", value), color, bgColor)
  621. --  memory.writebyte(0xFFFE30,0)--Disables checkpoints
  622.     end
  623.  
  624.  
  625. -- Timers
  626.  
  627.     -- Air
  628.     value = memory.readbyte(airRemaining.location)
  629.     if value ~= 0x1E then
  630.         gui.text(80, 0, string.format("Air: %2d", value), 0xFFFFFFFF, 0x000000FF)
  631.     end
  632.  
  633.     -- Temporary invunerability
  634.     value = invulnTimer.get()
  635.     if value ~= 0 then
  636.         gui.text(246, 9, string.format("%4d", value), 0xFF0000FF, 0x000000FF)
  637.     end
  638.  
  639.     -- Invincibility
  640.     value = invincTimer.get()
  641.     if value ~= 0 then
  642.         gui.text(120, 0, string.format("Invincible: %2d", value/fps), 0xFFFFFFFF, 0x000000FF)
  643.     end
  644.  
  645.     -- Speed shoe
  646.     value = speedShoeTimer.get()
  647.     if value ~= 0 then
  648.         gui.text(192, 0, string.format("Speed Shoe: %2d", value/fps), 0xFFFFFFFF, 0x000000FF)
  649.     end
  650.  
  651.     -- Tails flying time
  652.     if currentGame == sonic3k then
  653.         if currentCharacter.value == 0 then
  654.             value = memory.readbyteunsigned(0xFFB06F)*2
  655.             if memory.readbyteunsigned(0xFFB079) ~= 0 then
  656.                 gui.text(192, 9, string.format("Flying: %2d", value/fps), 0xFFFFFFFF, 0x000000FF)
  657.             end
  658.         end
  659.         if currentCharacter.value == 02 then
  660.             value = memory.readbyteunsigned(0xFFB025)*2
  661.             if memory.readbyteunsigned(0xFFB02F) ~= 0 then
  662.                 gui.text(192, 9, string.format("Flying: %2d", value/fps), 0xFFFFFFFF, 0x000000FF)
  663.             end
  664.         end
  665.     end
  666.  
  667. else
  668.     showThingsFlag = false
  669. end
  670. end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement