Guest User

Motocross Maniacs Luascript Bizhawk

a guest
Jul 22nd, 2025
35
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.35 KB | Gaming | 0 0
  1. -- Luascript for bizhawk 2.9.1
  2. -- by mugg1991
  3. -- Motocross Maniacs gb
  4.  
  5. function text(x, y, text, color)
  6.     gui.pixelText(x, y, text,color,0x00000000)
  7. end
  8.  
  9. function drawButton(posx,posy,width,height,label,color,frequency,clickedfunction)
  10.  
  11.     if X>posx and X<posx+width and Y>posy and Y<posy+height then
  12.  
  13.         if clicked and clickedFrames%frequency==1 then
  14.             clickedfunction()
  15.         end
  16.         gui.drawBox(posx,posy,posx+width,posy+height,0x00606060,color+0xFF484848)
  17.     else
  18.         gui.drawBox(posx,posy,posx+width,posy+height,0x00606060,color)
  19.     end
  20.    
  21.     text(posx+2,posy+2,label,0xFFFFFFFF)
  22.    
  23. end
  24.  
  25. local drawFrameCountAndInput = function(x,y)
  26.  
  27.     if movie.mode()=="PLAY" then
  28.         text(x, y,emu.framecount().."/"..movie.length(),0xFFFFFFFF)
  29.     else
  30.         text(x, y,emu.framecount(),0xFFFFFFFF)
  31.     end
  32.        
  33.     if emu.islagged() then
  34.         text(x, y+8,emu.lagcount() .. " *",0xFFF05050)
  35.     else
  36.         text(x, y+8,emu.lagcount(),0xFFF05050)
  37.     end
  38.    
  39.     local inputtable = {}
  40.    
  41.     if movie.mode()=="INACTIVE" then
  42.         inputtable = joypad.getimmediate()
  43.     elseif movie.mode()=="PLAY" or movie.mode()=="RECORD" then
  44.         inputtable = movie.getinput(emu.framecount()-1)
  45.     end
  46.    
  47.     local buttons = {["Up"]="^", ["Down"]="v", ["Left"]="<", ["Right"]=">", ["Select"]="s", ["Start"]="S", ["A"]="A", ["B"]="B", ["L"]="L", ["R"]="R"}
  48.     local s = ""
  49.     for k,v in pairs(inputtable) do
  50.         if v==true then
  51.             s=s..buttons[k]
  52.         end
  53.     end
  54.     text(x,y+16,s,0xFFffffff)
  55.  
  56. end
  57.  
  58. function hex(num)
  59.    local dig = 2
  60.    local hexstr = '0123456789ABCDEF'
  61.    local s = ''
  62.    --while num > 0 do
  63.    while dig > 0 do -- use digit instead so there is padding
  64.        local mod = math.fmod(num, 16)
  65.        s = string.sub(hexstr, mod+1, mod+1) .. s
  66.        num = math.floor(num / 16)
  67.        dig = dig - 1
  68.    end
  69.    if s == '' then s = '0' end
  70.    return s
  71. end
  72.  
  73. local displayItems = function()
  74.     hiddenjets=0
  75.     items=0
  76.     for i=0,192,8 do
  77.    
  78.             item_type = memory.read_u8(0xca00+i)
  79.             item_status = memory.read_u8(0xca01+i)
  80.             item_x = memory.read_u8(0xca03+i)+memory.read_u8(0xca04+i)*256
  81.             item_y = memory.read_u8(0xca02+i)
  82.             item_xcam = item_x-Xcam/256
  83.             item_ycam = item_y-Ycam/256+28
  84.             exists=false
  85.             if item_status==3 and item_y~=0 then exists=true end
  86.            
  87.             if item_type>0 and item_type<8 and exists then
  88.                 items=items+1
  89.             end
  90.            
  91.             if item_type==6 and exists then
  92.                 hiddenjets=hiddenjets+1
  93.             end
  94.            
  95.             if exists then
  96.                 for p=0,4096,4096 do --4096 is the length of the course. We want to display items for both laps.
  97.                     if item_ycam < 24 then -- don't obscure lua hud
  98.                         gui.drawBox(item_xcam+20+p,item_ycam,item_xcam+36+p,item_ycam+16,0x00FF0000,0x40FFFFFF)            
  99.                     else
  100.                         gui.drawBox(item_xcam+20+p,item_ycam,item_xcam+36+p,item_ycam+16,0x60FF0000,0x20FF0000)
  101.                         text(item_xcam+21+p,item_ycam+1,itemTable[item_type],0xFF000000)
  102.                     end
  103.                 end
  104.             end
  105.     end
  106.     text(110,30,"jets: "..hiddenjets,0x80000000)
  107.     text(156,30,"items: "..items,0x80000000)
  108. end
  109.  
  110. function file_exists(name)
  111.    local f=io.open(name,"r")
  112.    local exists=false
  113.    if f~=nil then
  114.         exists=true
  115.         io.close(f)
  116.    else
  117.         exists=false
  118.    end
  119.     return exists
  120. end
  121.  
  122. local writeGhostToFile = function()
  123.  
  124.     FileName=LevelName.."_Ghost_"..hex(Minutes).."_"..hex(Seconds).."_"..hex(Milliseconds)..".txt"
  125.    
  126.     if file_exists(FileName) then
  127.            
  128.         print("NOTE: The ghost file was not written because there already exists a ghost file with the same record time.")
  129.        
  130.     else
  131.    
  132.         io.output(FileName)
  133.        
  134.         table.sort(ghostTable)
  135.  
  136.        
  137.         for key,value in pairs(ghostTable) do
  138.             io.write(key.." "..ghostDataTable[value].."\n")
  139.         end
  140.            
  141.         io.close()
  142.        
  143.         if file_exists(FileName)==true then
  144.             print("NOTE: File "..FileName.." has been written.")
  145.         elseif file_exists(FileName)==false then
  146.             print("NOTE: File has not been written because of an unknown error.")      
  147.         end
  148.    
  149.     end
  150.    
  151. end
  152.  
  153. local writeGhostToTable = function()
  154.  
  155.     ghostFrames=ghostFrames+1
  156.     ghostTable[ghostFrames]=TotalTime
  157.     ghostDataTable[TotalTime]="Time:"..TotalTime.." X:"..Xscreen.." Y:"..Yscreen.." Xcam:"..Xcam.." Ycam:"..Ycam.." Speed:"..Speed2.." Angle:"..Angle.." Nitro:"..Nitros -- extra table because table.sort didn't work in my favor
  158.    
  159.     if TotalTime>50 then
  160.         if TotalTimeUnchangedCount>20 or recordingGhost==false then --recording finished
  161.             if Xpos~=0 then
  162.                 writeGhostToFile()
  163.             else
  164.                 print("NOTE: The ghost file wasn't written because you haven't moved.")
  165.             end
  166.             recordingGhost=false
  167.             ghostFrames=0
  168.         end
  169.     end
  170.    
  171. end
  172.  
  173. function doLine(filename)
  174.     local f = io.open(filename, "r")
  175.  
  176.     local iterator = function(f, prevLine)
  177.         if f==nil then
  178.             return nil
  179.         else
  180.             local success, line = pcall(function()
  181.                 return f:read("*l"):gsub("\n",""):gsub("\r","")
  182.             end)
  183.             if not success or line == nil then
  184.                 f:close()
  185.                 line = nil
  186.             end
  187.  
  188.             return line
  189.         end
  190.     end
  191.  
  192.     return iterator, f, ""
  193. end
  194.  
  195. local readGhostFromFile = function()
  196.  
  197.         local TimeIndex
  198.         local XscreenIndex
  199.         local YscreenIndex
  200.         local XcamIndex
  201.         local YcamIndex
  202.         local SpeedIndex
  203.         local AngleIndex
  204.         local NitroIndex
  205.        
  206.         local highestTimeInGhost = 0
  207.        
  208.         for line in doLine(PlayFilePath) do  -- finding the settings and assigning them to variables
  209.  
  210.             TimeIndex = string.find(line,"Time:")
  211.             XscreenIndex = string.find(line,"X:")
  212.             YscreenIndex = string.find(line,"Y:")
  213.             XcamIndex = string.find(line,"Xcam:")
  214.             YcamIndex = string.find(line,"Ycam:")
  215.             SpeedIndex = string.find(line,"Speed:")
  216.             AngleIndex = string.find(line,"Angle:")
  217.             NitroIndex = string.find(line,"Nitro:")
  218.            
  219.             Time = tonumber( string.sub(line, TimeIndex + 5, XscreenIndex - 1) )
  220.             Xscreen = tonumber( string.sub(line, XscreenIndex + 2, YscreenIndex - 1) )
  221.             Yscreen = tonumber( string.sub(line, YscreenIndex + 2, XcamIndex - 1) )
  222.             Xcam = tonumber( string.sub(line, XcamIndex + 5, YcamIndex - 1) )
  223.             Ycam = tonumber( string.sub(line, YcamIndex + 5, SpeedIndex - 1) )
  224.             Speed = tonumber( string.sub(line, SpeedIndex + 6, AngleIndex - 1) )
  225.             Angle = tonumber( string.sub(line, AngleIndex + 6, NitroIndex - 1) )
  226.             Nitros = tonumber( string.sub(line, NitroIndex + 6) )
  227.                        
  228.             playTable[Time] = {
  229.                 Xscreen = Xscreen,
  230.                 Yscreen = Yscreen,
  231.                 Xcam = Xcam,
  232.                 Ycam = Ycam,
  233.                 Speed = Speed,
  234.                 Angle = Angle,
  235.                 Nitros = Nitros
  236.             }
  237.            
  238.             if Time > highestTimeInGhost then highestTimeInGhost = Time end
  239.            
  240.         end
  241.        
  242.         if TotalTime > highestTimeInGhost then
  243.             playTable = {}
  244.             playingGhost = false
  245.             print("NOTE: Cannot play ghost, since its data is in the past.")
  246.         end
  247. end
  248.  
  249. local readGhostFromTable = function()
  250.     if playTable[2]~=nil then -- ghost is loaded       
  251.         if playTable[TotalTime] == nil then
  252.             -- ghost file reached its end
  253.             playTable = {}
  254.             playingGhost = false
  255.             print("NOTE: End of ghost file reached.")
  256.            
  257.         else
  258.             Xscreen = playTable[TotalTime].Xscreen
  259.             Yscreen = playTable[TotalTime].Yscreen
  260.             Xcam = playTable[TotalTime].Xcam / 256
  261.             Ycam = playTable[TotalTime].Ycam / 256
  262.             Speed = playTable[TotalTime].Speed
  263.             Angle = playTable[TotalTime].Angle
  264.             Nitros = playTable[TotalTime].Nitros
  265.  
  266.             -- draw ghost
  267.             gui.drawBox(Xcam+20,Ycam,Xcam+36,Ycam+16,0xFFFF0000,0xFFFFFFFF)
  268.             print(Xcam, Ycam, Xscreen, Yscreen)
  269.         end
  270.     else -- ghost not loaded yet
  271.         readGhostFromFile()
  272.     end
  273. end
  274.  
  275. local displayGhosts = function()
  276.    
  277.     gui.drawBox(200,-1,300,172,0x00000000,0xFF000000)
  278.     gui.drawLine(205,12,296,12,0xFF303030)
  279.    
  280.     --title lol
  281.     text(205,4,"Ghost menu",0xFFFFFFFF)
  282.     -- mode
  283.     if playingGhost then
  284.         text(205,16,"(playback)",0x60FFFFFF)
  285.     elseif recordingGhost then
  286.         text(205,16,"(recording)",0x60FFFFFF)  
  287.     else
  288.         text(205,16,"(idle)",0x60FFFFFF)
  289.     end
  290.    
  291.     --recording button
  292.     if recordingGhost then
  293.    
  294.         drawButton(205,52,35,10,"Stop",0xFF484848,20,function()
  295.             recordingGhost=false
  296.         end)
  297.        
  298.         writeGhostToTable()
  299.        
  300.         gui.drawEllipse(245,54,6,6,0xFFFF0000,0xFFFF0000)
  301.        
  302.     else
  303.    
  304.         drawButton(205,52,35,10,"Record",0xFF484848,20,function()
  305.             if playingGhost then
  306.                 print("NOTE: Stop playing back before recording.")
  307.                 ghostTable = {}
  308.                 ghostDataTable = {}
  309.                 recordingGhost=false
  310.             else
  311.                 if TotalTime~=0 or Level==0 then
  312.                     print("NOTE: Not recording. Click Record before a course starts (time is 0).")
  313.                     ghostTable = {}
  314.                     ghostDataTable = {}
  315.                     recordingGhost=false
  316.                 else
  317.                     recordingGhost=true
  318.                 end
  319.             end
  320.         end)
  321.        
  322.     end
  323.    
  324.     -- selected playback file
  325.     if PlayFilePath=="" then
  326.         t="-"
  327.     else
  328.         t=PlayFilePath:sub(-21)
  329.     end
  330.    
  331.     drawButton(205,82,95,10,t,0xFF181818,20,function()
  332.         FormsSelectedPath = forms.openfile()
  333.     end)
  334.    
  335.     if FormsSelectedPath ~= nil then PlayFilePath=FormsSelectedPath end
  336.    
  337.     -- playback buttons
  338.     if playingGhost then
  339.    
  340.         drawButton(205,70,35,10,"Stop",0xFF484848,20,function()
  341.             playingGhost=false
  342.         end)
  343.        
  344.         readGhostFromTable()
  345.                
  346.         gui.drawLine(245,72,245,78,0xFF00FF00)
  347.         gui.drawLine(246,73,246,77,0xFF00FF00)
  348.         gui.drawLine(247,74,247,76,0xFF00FF00)
  349.         gui.drawPixel(248,75,0xFF00FF00)
  350.    
  351.     else
  352.    
  353.         drawButton(205,70,35,10,"Play",0xFF484848,20,function()
  354.             if recordingGhost then
  355.            
  356.                 print("NOTE: Stop recording before playing back a ghost.")
  357.                 playingGhost=false
  358.                 playTable={}
  359.                
  360.             elseif FormsSelectedPath==nil then
  361.            
  362.                 print("NOTE: Please select a file before playing back.")
  363.                 playingGhost=false
  364.                 playTable={}
  365.                
  366.             elseif tostring(PlayFilePath):sub(-4)~=".txt" then
  367.            
  368.                 print("NOTE: The selected file is not a .txt file.")
  369.                 playingGhost=false
  370.                 playTable={}
  371.                
  372.             elseif not file_exists(PlayFilePath) then
  373.            
  374.                 print("NOTE: The selected file doesn't exist.")
  375.                 playingGhost=false
  376.                 playTable={}
  377.                
  378.             else
  379.                 playingGhost=true
  380.             end
  381.    
  382.  
  383.         end)
  384.    
  385.     end
  386.    
  387. end
  388.  
  389. local getMouse = function()
  390.     X = input.getmouse().X+20
  391.     Y = input.getmouse().Y+28
  392.     clicked = input.getmouse().Left
  393.    
  394.     if clicked then clickedFrames = clickedFrames + 1
  395.     else clickedFrames = 0 end
  396. end
  397.  
  398. event.onexit(function()
  399.     client.SetGameExtraPadding(0,0,0,0)
  400. end)
  401.  
  402. event.onloadstate(function()
  403.  
  404.     if recordingGhost then
  405.         recordingGhost=false
  406.         print("NOTE: The recording has stopped because you have loaded a state.")
  407.     end
  408.    
  409. end)
  410.  
  411. memory.usememorydomain("System Bus")
  412.  
  413. itemTable = {
  414. [-1] = "???",
  415. [2] = "Speed",
  416. [3] = "Time",
  417. [4] = "Nitro",
  418. [5] = "Rig",
  419. [6] = "Jet",
  420. [7] = "Bike",
  421. }
  422.  
  423. difficultyTable = {
  424. [-1]="?",
  425. [0]="A",
  426. [1]="B",
  427. [2]="C"
  428. }
  429.  
  430. ghostTable = {}
  431. ghostDataTable = {}
  432. playTable = {}
  433.  
  434. BOOL_items=false
  435. BOOL_ghosts=false
  436. X = 0
  437. Y = 0
  438. clicked=false
  439. clickedFrames=0
  440. recordingGhost=false
  441. playingGhost=false
  442. TotalTimeUnchangedCount=0
  443. TotalTimeBefore=0
  444. TotalTime=0
  445. ghostFrames=0
  446. PlayFilePath="" -- the file path name of the ghost file that should be played back
  447.  
  448. if BOOL_ghosts then client.SetGameExtraPadding(20,28,130,0)
  449. else client.SetGameExtraPadding(20,28,20,0) end
  450. console.clear()
  451.  
  452. -----
  453.  
  454. while true do
  455.    
  456.     ---- Mouse
  457.     getMouse()
  458.    
  459.     if emu.framecount()>400 then       
  460.         --declare values:
  461.         Minutes = memory.read_u8(0xc804)
  462.         Seconds = memory.read_u8(0xc803)
  463.         Milliseconds = memory.read_u8(0xc802)
  464.         MinutesLeft = memory.read_u8(0xc882)
  465.         SecondsLeft = memory.read_u8(0xc881)
  466.         MillisecondsLeft = memory.read_u8(0xc880)
  467.         Speed1 = memory.read_s8(0xc810) -- pixels to advance screen
  468.         Speed2 = memory.read_u16_le(0xc38a)
  469.         Nitros1 = memory.read_u8(0xc792)
  470.         Nitros2 = memory.read_u8(0xc812)
  471.         Indicator = memory.read_u8(0xc780)
  472.         if Indicator==1 then Nitros=Nitros1 else Nitros=Nitros2 end
  473.         Xpos = memory.read_u16_le(0xe903)
  474.         Xcam = memory.read_u24_le(0xc902)
  475.         Ycam = memory.read_u16_le(0xc900)
  476.        
  477.         Xscreen = memory.read_u8(0xcc01)+8
  478.         Yscreen = memory.read_u8(0xcc00)+8
  479.        
  480.         Level = memory.read_u8(0xc018)
  481.         Difficulty = memory.read_u8(0xc01B)
  482.         if Difficulty<0 or Difficulty>2 then Difficulty=-1 end
  483.         LevelName=Level..difficultyTable[Difficulty]
  484.        
  485.         if Level~=0 then
  486.             Angle = memory.read_u8(0xcc02) -- this is upper left corner, value is a pointer for what graphic to load I guess
  487.             if Angle%2==0 then Angle=1+Angle/4
  488.             else Angle=16+(Angle+1)/4 end
  489.             if Speed2==0 then Angle=0 end
  490.             TotalTime = hex(Milliseconds) + hex(Seconds)*100 + hex(Minutes)*6000
  491.         else
  492.             Angle = -1
  493.             TotalTime = -1
  494.         end
  495.        
  496.         Angle = math.floor(Angle) -- thanks newer Bizhawk lua
  497.        
  498.         if TotalTimeBefore==TotalTime then
  499.             TotalTimeUnchangedCount=TotalTimeUnchangedCount+1
  500.         else
  501.             TotalTimeUnchangedCount=0
  502.         end
  503.        
  504.         TotalTimeBefore=TotalTime
  505.        
  506.         --lines boxes
  507.         gui.drawBox(-1,27,20,172,0x00000000,0xFFFFFFFF)
  508.         gui.drawBox(179,27,201,172,0x00000000,0xFFFFFFFF)
  509.         gui.drawBox(-1,-1,201,28,0x00000000,0xFF202020)
  510.         gui.drawLine(34,0,34,27,0xFF404040)
  511.         gui.drawLine(156,0,156,27,0xFF404040)
  512.        
  513.         --racetrack visualisation
  514.         gui.drawBox(30,27,160,28,0xFFA0A0A0)--track
  515.         gui.drawLine(30,27,30,28,0xFFa0FF00)--start
  516.         gui.drawLine(95,27,95,28,0xFFa0FF00)--middle
  517.         gui.drawLine(160,27,160,28,0xFFa0FF00)--finish
  518.         Xtrack=30+(Xpos/8192)*130
  519.         gui.drawLine(Xtrack,27,Xtrack,28,0xFFFF0000)--you
  520.        
  521.         --display values
  522.         text(45,2,"Time",0x80FFFFFF)
  523.         text(49,10,"Gas",0x80FFFFFF)
  524.         text(103,2,"Spd",0x80FFFFFF)
  525.         text(103,10,"Ntr   Angl",0x80FFFFFF)   
  526.         text(114,18,"pos",0x80FFFFFF)  
  527.         text(38,18,"cam",0x80FFFFFF)
  528.        
  529.         if Level ~=0 then
  530.             text(1,30,"Lv: "..LevelName,0x80000000)
  531.             --text(74,30,TotalTime,0x80000000)
  532.             --text(46,30,Angle,0x80000000)
  533.             text(65,2,hex(Minutes)..":"..hex(Seconds).."."..hex(Milliseconds),0xFFFFFFFF)
  534.             text(65,10,hex(MinutesLeft)..":"..hex(SecondsLeft).."."..hex(MillisecondsLeft),0xFFFFFFFF)
  535.             text(119,2,Speed2,0xFFFFFFFF)  
  536.             text(119,10,Nitros,0xFFFFFFFF) 
  537.             text(147,10,Angle,0xFFFFFFFF)          
  538.             text(134,18,Xpos,0xFFFFFFFF)   
  539.             text(53,18,Xcam,0xFFFFFFFF)
  540.             text(88,18,Ycam,0xFFFFFFFF)
  541.         end    
  542.        
  543.         drawButton(160,2,35,10,"ITEMS",0xFF484848,15,function()
  544.             BOOL_items= not BOOL_items
  545.         end)
  546.        
  547.         drawButton(160,14,35,10,"GHOSTS",0xFF484848,15,function()
  548.             BOOL_ghosts= not BOOL_ghosts
  549.             if BOOL_ghosts then
  550.                 client.SetGameExtraPadding(20,28,130,0)
  551.             else
  552.                 client.SetGameExtraPadding(20,28,20,0)
  553.             end
  554.         end)
  555.        
  556.         if BOOL_items==true then
  557.             text(189,4,"o",0xFF00c000)
  558.             displayItems()
  559.         else   
  560.             text(189,4,"x",0xFFF05050)
  561.         end
  562.        
  563.         if BOOL_ghosts==true then
  564.             text(189,16,"o",0xFF00C000)
  565.             displayGhosts()
  566.         else   
  567.             text(189,16,"x",0xFFF05050)
  568.         end
  569.     end
  570.    
  571.     drawFrameCountAndInput(2,2)
  572.    
  573.     --emu.frameadvance()
  574.     emu.yield()
  575. end
Advertisement
Add Comment
Please, Sign In to add comment