-- Luascript for bizhawk 2.9.1 -- by mugg1991 -- Motocross Maniacs gb function text(x, y, text, color) gui.pixelText(x, y, text,color,0x00000000) end function drawButton(posx,posy,width,height,label,color,frequency,clickedfunction) if X>posx and Xposy and Y 0 do while dig > 0 do -- use digit instead so there is padding local mod = math.fmod(num, 16) s = string.sub(hexstr, mod+1, mod+1) .. s num = math.floor(num / 16) dig = dig - 1 end if s == '' then s = '0' end return s end local displayItems = function() hiddenjets=0 items=0 for i=0,192,8 do item_type = memory.read_u8(0xca00+i) item_status = memory.read_u8(0xca01+i) item_x = memory.read_u8(0xca03+i)+memory.read_u8(0xca04+i)*256 item_y = memory.read_u8(0xca02+i) item_xcam = item_x-Xcam/256 item_ycam = item_y-Ycam/256+28 exists=false if item_status==3 and item_y~=0 then exists=true end if item_type>0 and item_type<8 and exists then items=items+1 end if item_type==6 and exists then hiddenjets=hiddenjets+1 end if exists then for p=0,4096,4096 do --4096 is the length of the course. We want to display items for both laps. if item_ycam < 24 then -- don't obscure lua hud gui.drawBox(item_xcam+20+p,item_ycam,item_xcam+36+p,item_ycam+16,0x00FF0000,0x40FFFFFF) else gui.drawBox(item_xcam+20+p,item_ycam,item_xcam+36+p,item_ycam+16,0x60FF0000,0x20FF0000) text(item_xcam+21+p,item_ycam+1,itemTable[item_type],0xFF000000) end end end end text(110,30,"jets: "..hiddenjets,0x80000000) text(156,30,"items: "..items,0x80000000) end function file_exists(name) local f=io.open(name,"r") local exists=false if f~=nil then exists=true io.close(f) else exists=false end return exists end local writeGhostToFile = function() FileName=LevelName.."_Ghost_"..hex(Minutes).."_"..hex(Seconds).."_"..hex(Milliseconds)..".txt" if file_exists(FileName) then print("NOTE: The ghost file was not written because there already exists a ghost file with the same record time.") else io.output(FileName) table.sort(ghostTable) for key,value in pairs(ghostTable) do io.write(key.." "..ghostDataTable[value].."\n") end io.close() if file_exists(FileName)==true then print("NOTE: File "..FileName.." has been written.") elseif file_exists(FileName)==false then print("NOTE: File has not been written because of an unknown error.") end end end local writeGhostToTable = function() ghostFrames=ghostFrames+1 ghostTable[ghostFrames]=TotalTime 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 if TotalTime>50 then if TotalTimeUnchangedCount>20 or recordingGhost==false then --recording finished if Xpos~=0 then writeGhostToFile() else print("NOTE: The ghost file wasn't written because you haven't moved.") end recordingGhost=false ghostFrames=0 end end end function doLine(filename) local f = io.open(filename, "r") local iterator = function(f, prevLine) if f==nil then return nil else local success, line = pcall(function() return f:read("*l"):gsub("\n",""):gsub("\r","") end) if not success or line == nil then f:close() line = nil end return line end end return iterator, f, "" end local readGhostFromFile = function() local TimeIndex local XscreenIndex local YscreenIndex local XcamIndex local YcamIndex local SpeedIndex local AngleIndex local NitroIndex local highestTimeInGhost = 0 for line in doLine(PlayFilePath) do -- finding the settings and assigning them to variables TimeIndex = string.find(line,"Time:") XscreenIndex = string.find(line,"X:") YscreenIndex = string.find(line,"Y:") XcamIndex = string.find(line,"Xcam:") YcamIndex = string.find(line,"Ycam:") SpeedIndex = string.find(line,"Speed:") AngleIndex = string.find(line,"Angle:") NitroIndex = string.find(line,"Nitro:") Time = tonumber( string.sub(line, TimeIndex + 5, XscreenIndex - 1) ) Xscreen = tonumber( string.sub(line, XscreenIndex + 2, YscreenIndex - 1) ) Yscreen = tonumber( string.sub(line, YscreenIndex + 2, XcamIndex - 1) ) Xcam = tonumber( string.sub(line, XcamIndex + 5, YcamIndex - 1) ) Ycam = tonumber( string.sub(line, YcamIndex + 5, SpeedIndex - 1) ) Speed = tonumber( string.sub(line, SpeedIndex + 6, AngleIndex - 1) ) Angle = tonumber( string.sub(line, AngleIndex + 6, NitroIndex - 1) ) Nitros = tonumber( string.sub(line, NitroIndex + 6) ) playTable[Time] = { Xscreen = Xscreen, Yscreen = Yscreen, Xcam = Xcam, Ycam = Ycam, Speed = Speed, Angle = Angle, Nitros = Nitros } if Time > highestTimeInGhost then highestTimeInGhost = Time end end if TotalTime > highestTimeInGhost then playTable = {} playingGhost = false print("NOTE: Cannot play ghost, since its data is in the past.") end end local readGhostFromTable = function() if playTable[2]~=nil then -- ghost is loaded if playTable[TotalTime] == nil then -- ghost file reached its end playTable = {} playingGhost = false print("NOTE: End of ghost file reached.") else Xscreen = playTable[TotalTime].Xscreen Yscreen = playTable[TotalTime].Yscreen Xcam = playTable[TotalTime].Xcam / 256 Ycam = playTable[TotalTime].Ycam / 256 Speed = playTable[TotalTime].Speed Angle = playTable[TotalTime].Angle Nitros = playTable[TotalTime].Nitros -- draw ghost gui.drawBox(Xcam+20,Ycam,Xcam+36,Ycam+16,0xFFFF0000,0xFFFFFFFF) print(Xcam, Ycam, Xscreen, Yscreen) end else -- ghost not loaded yet readGhostFromFile() end end local displayGhosts = function() gui.drawBox(200,-1,300,172,0x00000000,0xFF000000) gui.drawLine(205,12,296,12,0xFF303030) --title lol text(205,4,"Ghost menu",0xFFFFFFFF) -- mode if playingGhost then text(205,16,"(playback)",0x60FFFFFF) elseif recordingGhost then text(205,16,"(recording)",0x60FFFFFF) else text(205,16,"(idle)",0x60FFFFFF) end --recording button if recordingGhost then drawButton(205,52,35,10,"Stop",0xFF484848,20,function() recordingGhost=false end) writeGhostToTable() gui.drawEllipse(245,54,6,6,0xFFFF0000,0xFFFF0000) else drawButton(205,52,35,10,"Record",0xFF484848,20,function() if playingGhost then print("NOTE: Stop playing back before recording.") ghostTable = {} ghostDataTable = {} recordingGhost=false else if TotalTime~=0 or Level==0 then print("NOTE: Not recording. Click Record before a course starts (time is 0).") ghostTable = {} ghostDataTable = {} recordingGhost=false else recordingGhost=true end end end) end -- selected playback file if PlayFilePath=="" then t="-" else t=PlayFilePath:sub(-21) end drawButton(205,82,95,10,t,0xFF181818,20,function() FormsSelectedPath = forms.openfile() end) if FormsSelectedPath ~= nil then PlayFilePath=FormsSelectedPath end -- playback buttons if playingGhost then drawButton(205,70,35,10,"Stop",0xFF484848,20,function() playingGhost=false end) readGhostFromTable() gui.drawLine(245,72,245,78,0xFF00FF00) gui.drawLine(246,73,246,77,0xFF00FF00) gui.drawLine(247,74,247,76,0xFF00FF00) gui.drawPixel(248,75,0xFF00FF00) else drawButton(205,70,35,10,"Play",0xFF484848,20,function() if recordingGhost then print("NOTE: Stop recording before playing back a ghost.") playingGhost=false playTable={} elseif FormsSelectedPath==nil then print("NOTE: Please select a file before playing back.") playingGhost=false playTable={} elseif tostring(PlayFilePath):sub(-4)~=".txt" then print("NOTE: The selected file is not a .txt file.") playingGhost=false playTable={} elseif not file_exists(PlayFilePath) then print("NOTE: The selected file doesn't exist.") playingGhost=false playTable={} else playingGhost=true end end) end end local getMouse = function() X = input.getmouse().X+20 Y = input.getmouse().Y+28 clicked = input.getmouse().Left if clicked then clickedFrames = clickedFrames + 1 else clickedFrames = 0 end end event.onexit(function() client.SetGameExtraPadding(0,0,0,0) end) event.onloadstate(function() if recordingGhost then recordingGhost=false print("NOTE: The recording has stopped because you have loaded a state.") end end) memory.usememorydomain("System Bus") itemTable = { [-1] = "???", [2] = "Speed", [3] = "Time", [4] = "Nitro", [5] = "Rig", [6] = "Jet", [7] = "Bike", } difficultyTable = { [-1]="?", [0]="A", [1]="B", [2]="C" } ghostTable = {} ghostDataTable = {} playTable = {} BOOL_items=false BOOL_ghosts=false X = 0 Y = 0 clicked=false clickedFrames=0 recordingGhost=false playingGhost=false TotalTimeUnchangedCount=0 TotalTimeBefore=0 TotalTime=0 ghostFrames=0 PlayFilePath="" -- the file path name of the ghost file that should be played back if BOOL_ghosts then client.SetGameExtraPadding(20,28,130,0) else client.SetGameExtraPadding(20,28,20,0) end console.clear() ----- while true do ---- Mouse getMouse() if emu.framecount()>400 then --declare values: Minutes = memory.read_u8(0xc804) Seconds = memory.read_u8(0xc803) Milliseconds = memory.read_u8(0xc802) MinutesLeft = memory.read_u8(0xc882) SecondsLeft = memory.read_u8(0xc881) MillisecondsLeft = memory.read_u8(0xc880) Speed1 = memory.read_s8(0xc810) -- pixels to advance screen Speed2 = memory.read_u16_le(0xc38a) Nitros1 = memory.read_u8(0xc792) Nitros2 = memory.read_u8(0xc812) Indicator = memory.read_u8(0xc780) if Indicator==1 then Nitros=Nitros1 else Nitros=Nitros2 end Xpos = memory.read_u16_le(0xe903) Xcam = memory.read_u24_le(0xc902) Ycam = memory.read_u16_le(0xc900) Xscreen = memory.read_u8(0xcc01)+8 Yscreen = memory.read_u8(0xcc00)+8 Level = memory.read_u8(0xc018) Difficulty = memory.read_u8(0xc01B) if Difficulty<0 or Difficulty>2 then Difficulty=-1 end LevelName=Level..difficultyTable[Difficulty] if Level~=0 then Angle = memory.read_u8(0xcc02) -- this is upper left corner, value is a pointer for what graphic to load I guess if Angle%2==0 then Angle=1+Angle/4 else Angle=16+(Angle+1)/4 end if Speed2==0 then Angle=0 end TotalTime = hex(Milliseconds) + hex(Seconds)*100 + hex(Minutes)*6000 else Angle = -1 TotalTime = -1 end Angle = math.floor(Angle) -- thanks newer Bizhawk lua if TotalTimeBefore==TotalTime then TotalTimeUnchangedCount=TotalTimeUnchangedCount+1 else TotalTimeUnchangedCount=0 end TotalTimeBefore=TotalTime --lines boxes gui.drawBox(-1,27,20,172,0x00000000,0xFFFFFFFF) gui.drawBox(179,27,201,172,0x00000000,0xFFFFFFFF) gui.drawBox(-1,-1,201,28,0x00000000,0xFF202020) gui.drawLine(34,0,34,27,0xFF404040) gui.drawLine(156,0,156,27,0xFF404040) --racetrack visualisation gui.drawBox(30,27,160,28,0xFFA0A0A0)--track gui.drawLine(30,27,30,28,0xFFa0FF00)--start gui.drawLine(95,27,95,28,0xFFa0FF00)--middle gui.drawLine(160,27,160,28,0xFFa0FF00)--finish Xtrack=30+(Xpos/8192)*130 gui.drawLine(Xtrack,27,Xtrack,28,0xFFFF0000)--you --display values text(45,2,"Time",0x80FFFFFF) text(49,10,"Gas",0x80FFFFFF) text(103,2,"Spd",0x80FFFFFF) text(103,10,"Ntr Angl",0x80FFFFFF) text(114,18,"pos",0x80FFFFFF) text(38,18,"cam",0x80FFFFFF) if Level ~=0 then text(1,30,"Lv: "..LevelName,0x80000000) --text(74,30,TotalTime,0x80000000) --text(46,30,Angle,0x80000000) text(65,2,hex(Minutes)..":"..hex(Seconds).."."..hex(Milliseconds),0xFFFFFFFF) text(65,10,hex(MinutesLeft)..":"..hex(SecondsLeft).."."..hex(MillisecondsLeft),0xFFFFFFFF) text(119,2,Speed2,0xFFFFFFFF) text(119,10,Nitros,0xFFFFFFFF) text(147,10,Angle,0xFFFFFFFF) text(134,18,Xpos,0xFFFFFFFF) text(53,18,Xcam,0xFFFFFFFF) text(88,18,Ycam,0xFFFFFFFF) end drawButton(160,2,35,10,"ITEMS",0xFF484848,15,function() BOOL_items= not BOOL_items end) drawButton(160,14,35,10,"GHOSTS",0xFF484848,15,function() BOOL_ghosts= not BOOL_ghosts if BOOL_ghosts then client.SetGameExtraPadding(20,28,130,0) else client.SetGameExtraPadding(20,28,20,0) end end) if BOOL_items==true then text(189,4,"o",0xFF00c000) displayItems() else text(189,4,"x",0xFFF05050) end if BOOL_ghosts==true then text(189,16,"o",0xFF00C000) displayGhosts() else text(189,16,"x",0xFFF05050) end end drawFrameCountAndInput(2,2) --emu.frameadvance() emu.yield() end