diff --git a/Zframework/stringExtend.lua b/Zframework/stringExtend.lua index 920b899b..2e5d834a 100644 --- a/Zframework/stringExtend.lua +++ b/Zframework/stringExtend.lua @@ -81,6 +81,11 @@ do--function STRING.urlEncode(s) end end +function STRING.readLine(str) + local p=str:find("\n") + return str:sub(1,p-1),str:sub(p+1) +end + function STRING.packBin(s) return data.encode('string','base64',data.compress('string','zlib',s)) end diff --git a/main.lua b/main.lua index f601716f..33cfa50f 100644 --- a/main.lua +++ b/main.lua @@ -229,9 +229,7 @@ for _,v in next,fs.getDirectoryItems("parts/scenes")do LANG.addScene(sceneName) end end -LANG.set(SETTING.lang) -VK.setShape(SETTING.VKSkin) - +local modeTable={attacker_h="attacker_hard",attacker_u="attacker_ultimate",blind_e="blind_easy",blind_h="blind_hard",blind_l="blind_lunatic",blind_n="blind_normal",blind_u="blind_ultimate",c4wtrain_l="c4wtrain_lunatic",c4wtrain_n="c4wtrain_normal",defender_l="defender_lunatic",defender_n="defender_normal",dig_100l="dig_100",dig_10l="dig_10",dig_400l="dig_400",dig_40l="dig_40",dig_h="dig_hard",dig_u="dig_ultimate",drought_l="drought_lunatic",drought_n="drought_normal",marathon_h="marathon_hard",marathon_n="marathon_normal",pc_h="pcchallenge_hard",pc_l="pcchallenge_lunatic",pc_n="pcchallenge_normal",pctrain_l="pctrain_lunatic",pctrain_n="pctrain_normal",round_e="round_1",round_h="round_2",round_l="round_3",round_n="round_4",round_u="round_5",solo_e="solo_1",solo_h="solo_2",solo_l="solo_3",solo_n="solo_4",solo_u="solo_5",sprint_10l="sprint_10",sprint_20l="sprint_20",sprint_40l="sprint_40",sprint_400l="sprint_400",sprint_100l="sprint_100",sprint_1000l="sprint_1000",survivor_e="survivor_easy",survivor_h="survivor_hard",survivor_l="survivor_lunatic",survivor_n="survivor_normal",survivor_u="survivor_ultimate",tech_finesse_f="tech_finesse2",tech_h_plus="tech_hard2",tech_h="tech_hard",tech_l_plus="tech_lunatic2",tech_l="tech_lunatic",tech_n_plus="tech_normal2",tech_n="tech_normal",techmino49_e="techmino49_easy",techmino49_h="techmino49_hard",techmino49_u="techmino49_ultimate",techmino99_e="techmino99_easy",techmino99_h="techmino99_hard",techmino99_u="techmino99_ultimate",tsd_e="tsd_easy",tsd_h="tsd_hard",tsd_u="tsd_ultimate",master_extra="GM"} --Update data do local needSave,autoRestart @@ -249,6 +247,7 @@ do end if STAT.version<1505 then fs.remove('record/bigbang.rec') + fs.remove('conf/replay') end if STAT.version~=VERSION.code then STAT.version=VERSION.code @@ -271,7 +270,6 @@ do needSave=true end end - local modeTable={attacker_h="attacker_hard",attacker_u="attacker_ultimate",blind_e="blind_easy",blind_h="blind_hard",blind_l="blind_lunatic",blind_n="blind_normal",blind_u="blind_ultimate",c4wtrain_l="c4wtrain_lunatic",c4wtrain_n="c4wtrain_normal",defender_l="defender_lunatic",defender_n="defender_normal",dig_100l="dig_100",dig_10l="dig_10",dig_400l="dig_400",dig_40l="dig_40",dig_h="dig_hard",dig_u="dig_ultimate",drought_l="drought_lunatic",drought_n="drought_normal",marathon_h="marathon_hard",marathon_n="marathon_normal",pc_h="pcchallenge_hard",pc_l="pcchallenge_lunatic",pc_n="pcchallenge_normal",pctrain_l="pctrain_lunatic",pctrain_n="pctrain_normal",round_e="round_1",round_h="round_2",round_l="round_3",round_n="round_4",round_u="round_5",solo_e="solo_1",solo_h="solo_2",solo_l="solo_3",solo_n="solo_4",solo_u="solo_5",sprint_10l="sprint_10",sprint_20l="sprint_20",sprint_40l="sprint_40",sprint_400l="sprint_400",sprint_100l="sprint_100",sprint_1000l="sprint_1000",survivor_e="survivor_easy",survivor_h="survivor_hard",survivor_l="survivor_lunatic",survivor_n="survivor_normal",survivor_u="survivor_ultimate",tech_finesse_f="tech_finesse2",tech_h_plus="tech_hard2",tech_h="tech_hard",tech_l_plus="tech_lunatic2",tech_l="tech_lunatic",tech_n_plus="tech_normal2",tech_n="tech_normal",techmino49_e="techmino49_easy",techmino49_h="techmino49_hard",techmino49_u="techmino49_ultimate",techmino99_e="techmino99_easy",techmino99_h="techmino99_hard",techmino99_u="techmino99_ultimate",tsd_e="tsd_easy",tsd_h="tsd_hard",tsd_u="tsd_ultimate",master_extra="GM"} for k,v in next,modeTable do if RANKS[v]then RANKS[k]=RANKS[v] @@ -300,4 +298,42 @@ do if autoRestart then love.event.quit('restart') end -end \ No newline at end of file +end +LANG.set(SETTING.lang) +VK.setShape(SETTING.VKSkin) +--Load replays +for _,fileName in next,fs.getDirectoryItems("replay")do + if fileName:sub(12,12):match("[a-zA-Z]")then + local date,mode,version,player,seed,setting,mod + local fileData=fs.read('replay/'..fileName) + fs.remove('replay/'..fileName) + date, fileData=STRING.readLine(fileData)date=date:gsub("[a-zA-Z]","") + mode, fileData=STRING.readLine(fileData)mode=modeTable[mode]or mode + version,fileData=STRING.readLine(fileData) + player, fileData=STRING.readLine(fileData)if player=="Local Player"then player="Stacker"end + fileData=love.data.decompress('string','zlib',fileData) + seed, fileData=STRING.readLine(fileData) + setting,fileData=STRING.readLine(fileData)setting=JSON.decode(setting) + mod, fileData=STRING.readLine(fileData)mod=JSON.decode(mod) + + local newName=fileName:sub(1,10)..fileName:sub(15) + fs.write('replay/'..newName, + love.data.compress('string','zlib', + JSON.encode{ + date=date, + mode=mode, + version=version, + player=player, + seed=seed, + setting=setting, + mod=mod, + }.."\n".. + fileData + ) + ) + fileName=newName + end + local rep=DATA.parseReplay('replay/'..fileName) + table.insert(REPLAY,rep) +end +table.sort(REPLAY,function(a,b)return a.fileName>b.fileName end) \ No newline at end of file diff --git a/parts/data.lua b/parts/data.lua index eb4d014f..12607fe4 100644 --- a/parts/data.lua +++ b/parts/data.lua @@ -1,4 +1,5 @@ local loveCompress=love.data.compress +local loveDecompress=love.data.decompress local int=math.floor local char,byte=string.char,string.byte @@ -336,7 +337,7 @@ function DATA.pumpRecording(str,L) p=p+1 end end -do--function DATA.saveRecording() +do--function DATA.saveReplay() local noRecList={"custom","solo","round","techmino"} local function getModList() local res={} @@ -347,7 +348,7 @@ do--function DATA.saveRecording() end return res end - function DATA.saveRecording() + function DATA.saveReplay() --Filtering modes that cannot be saved for _,v in next,noRecList do if GAME.curModeName:find(v)then @@ -357,27 +358,59 @@ do--function DATA.saveRecording() end --Write file - local fileName=os.date("replay/%Y_%m_%d_%a_%H%M%S.rep") + local fileName=os.date("replay/%Y_%m_%d_%H%M%S.rep") if not love.filesystem.getInfo(fileName)then - local fileHead= - os.date("%Y/%m/%d %A %H:%M:%S\n").. - GAME.curModeName.."\n".. - VERSION.string.."\n".. - USERS.getUsername(USER.uid).."\n" - local fileBody= - GAME.seed.."\n".. - JSON.encode(GAME.setting).."\n".. - JSON.encode(getModList()).."\n".. - DATA.dumpRecording(GAME.rep) - - love.filesystem.write(fileName,fileHead..loveCompress('string','zlib',fileBody)) - ins(REPLAY,fileName) - FILE.save(REPLAY,'conf/replay') + love.filesystem.write(fileName, + loveCompress('string','zlib', + JSON.encode{ + date=os.date("%Y/%m/%d %H:%M:%S"), + mode=GAME.curModeName, + version=VERSION.string, + player=USERS.getUsername(USER.uid), + seed=GAME.seed, + setting=GAME.setting, + mod=getModList(), + }.."\n".. + DATA.dumpRecording(GAME.rep) + ) + ) + ins(REPLAY,1,DATA.parseReplay(fileName)) return true else MES.new('error',"Save failed: File already exists") end end end +function DATA.parseReplay(fileName,ifFull) + local fileData=love.filesystem.read(fileName) + if fileData and #fileData>0 then + fileData=loveDecompress('string','zlib',fileData) + local metaData + metaData,fileData=STRING.readLine(fileData) + metaData=JSON.decode(metaData) + local rep={ + fileName=fileName, + available=true, + + date=metaData.date, + mode=metaData.mode, + version=metaData.version, + player=metaData.player, + + seed=metaData.seed, + setting=metaData.setting, + mod=metaData.mod, + + modeName=("%s %s"):format(text.modes[metaData.mode][1],text.modes[metaData.mode][2]), + } + if ifFull then rep.data=fileData end + return rep + else + return{ + fileName=fileName, + available=false, + } + end +end return DATA \ No newline at end of file diff --git a/parts/gametoolfunc.lua b/parts/gametoolfunc.lua index 3d8c6d48..e6644107 100644 --- a/parts/gametoolfunc.lua +++ b/parts/gametoolfunc.lua @@ -228,10 +228,11 @@ function loadGame(mode,ifQuickPlay,ifNet)--Load a mode and go to game scene freshDate() if legalGameTime()then if MODES[mode].score then STAT.lastPlay=mode end + GAME.init=true + GAME.fromRepMenu=false GAME.curModeName=mode GAME.curMode=MODES[mode] GAME.modeEnv=GAME.curMode.env - GAME.init=true GAME.net=ifNet if ifNet then SCN.go('net_game','swipeD') diff --git a/parts/globalTables.lua b/parts/globalTables.lua index 5472a899..03f02ace 100644 --- a/parts/globalTables.lua +++ b/parts/globalTables.lua @@ -417,4 +417,4 @@ VK_org=FILE.load('conf/virtualkey')or{--Virtualkey layout, refresh all VKs' posi {ava=false, x=900, y=50, r=80},--addToLeft {ava=false, x=1000, y=50, r=80},--addToRight } -REPLAY=FILE.load('conf/replay')or{} \ No newline at end of file +REPLAY={}--Replay objects (not include stream data) \ No newline at end of file diff --git a/parts/scenes/pause.lua b/parts/scenes/pause.lua index d0d55e75..627995dd 100644 --- a/parts/scenes/pause.lua +++ b/parts/scenes/pause.lua @@ -130,11 +130,15 @@ function scene.keyDown(key,isRep) elseif key=="escape"then SCN.swapTo(GAME.result and'game'or'depause','none') elseif key=="s"then - GAME.prevBG=BG.cur - SCN.go('setting_sound') + if not GAME.fromRepMenu then + GAME.prevBG=BG.cur + SCN.go('setting_sound') + end elseif key=="r"then - resetGameData() - SCN.swapTo('game','none') + if not GAME.fromRepMenu then + resetGameData() + SCN.swapTo('game','none') + end elseif key=="p"then if(GAME.result or GAME.replaying)and #PLAYERS==1 then resetGameData('r') @@ -142,8 +146,9 @@ function scene.keyDown(key,isRep) end elseif key=="o"then if(GAME.result or GAME.replaying)and #PLAYERS==1 and not GAME.saved then - if DATA.saveRecording()then + if DATA.saveReplay()then GAME.saved=true + SFX.play('connected') end end else @@ -308,11 +313,11 @@ function scene.draw() end scene.widgetList={ - WIDGET.newButton{name="setting", x=1120,y=70,w=240,h=90, color='lB',code=pressKey"s"}, + WIDGET.newButton{name="setting", x=1120,y=70,w=240,h=90, color='lB',code=pressKey"s",hideF=function()return GAME.fromRepMenu end}, WIDGET.newButton{name="replay", x=535,y=250,w=200,h=100,color='lY',code=pressKey"p",hideF=function()return not(GAME.result or GAME.replaying)or #PLAYERS>1 end}, WIDGET.newButton{name="save", x=745,y=250,w=200,h=100,color='lP',code=pressKey"o",hideF=function()return not(GAME.result or GAME.replaying)or #PLAYERS>1 or GAME.saved end}, WIDGET.newButton{name="resume", x=640,y=367,w=240,h=100,color='lG',code=pressKey"escape"}, - WIDGET.newButton{name="restart", x=640,y=483,w=240,h=100,color='lR',code=pressKey"r"}, + WIDGET.newButton{name="restart", x=640,y=483,w=240,h=100,color='lR',code=pressKey"r",hideF=function()return GAME.fromRepMenu end}, WIDGET.newButton{name="quit", x=640,y=600,w=240,h=100,font=35,code=backScene}, } diff --git a/parts/scenes/replays.lua b/parts/scenes/replays.lua index 54b1fcf2..3ea63507 100644 --- a/parts/scenes/replays.lua +++ b/parts/scenes/replays.lua @@ -17,7 +17,7 @@ local listBox=WIDGET.newListBox{name="list",x=50,y=50,w=1200,h=520,lineH=40,draw if rep.available then gc_setColor(.9,.9,1) - gc_print(rep.modeName,405,-2) + gc_print(rep.modeName,310,-2) setFont(20) gc_setColor(1,1,.8) gc_print(rep.date,80,6) @@ -35,33 +35,22 @@ local scene={} local sure -local function readLine(str) - local p=str:find("\n") - return str:sub(1,p-1),str:sub(p+1) -end -local function replay(rep) +local function replay(fileName) + local rep=DATA.parseReplay(fileName,true) if not rep.available then MES.new('error',text.replayBroken) elseif MODES[rep.mode]then - local data=love.data.decompress('string','zlib',rep.data) - local seed,setting,mod - - seed,data=readLine(data) - GAME.seed=tonumber(seed) - - setting,data=readLine(data) - GAME.setting=JSON.decode(setting) - - mod,data=readLine(data) - GAME.mod=JSON.decode(mod) - + GAME.seed=rep.seed + GAME.setting=rep.setting + GAME.mod=rep.mod GAME.rep={} - DATA.pumpRecording(data,GAME.rep) + DATA.pumpRecording(rep.data,GAME.rep) loadGame(rep.mode,true) resetGameData('r') GAME.init=false GAME.saved=true + GAME.fromRepMenu=true else MES.new('error',("No mode id: [%s]"):format(rep.mode)) end @@ -69,73 +58,34 @@ end function scene.sceneInit() sure=0 - local repList={} - for i=#REPLAY,1,-1 do - local file=love.filesystem.newFile(REPLAY[i]) - if file:open('r')then - local metadata="" - local enter=0 - while true do - local b,len=file:read(1) - if len==0 then - repList[i]={ - fileName=REPLAY[i], - available=false, - } - break - end - metadata=metadata..b - if b=="\n"then - enter=enter+1 - if enter==4 then - metadata=STRING.split(metadata,'\n') - local mode=text.modes[metadata[2]]or{"["..metadata[2].."]",""} - repList[i]={ - fileName=REPLAY[i], - available=true, - date=metadata[1], - mode=metadata[2], - modeName=("%s %s"):format(mode[1],mode[2]), - version=metadata[3], - player=metadata[4], - data=file:read(), - } - break - end - end - end - file:close() - else - repList[i]={ - fileName=REPLAY[i], - available=false, - } - end - end - listBox:setList(repList) + listBox:setList(REPLAY) end function scene.keyDown(key) if key=="return"then - replay(listBox:getSel()) + local rep=listBox:getSel() + if rep then + replay(rep.fileName) + end elseif key=="escape"then SCN.back() elseif key=="delete"then - if sure>20 then - local rep=listBox:getSel() - if rep then + local rep=listBox:getSel() + if rep then + if sure>20 then sure=0 listBox:remove() love.filesystem.remove(rep.fileName) - - local i=TABLE.find(REPLAY,rep.fileName) - if i then table.remove(REPLAY,i)end - FILE.save(REPLAY,'conf/replay') - + for i=1,#REPLAY do + if REPLAY[i].fileName==rep.fileName then + table.remove(REPLAY,i) + break + end + end SFX.play('finesseError',.7) + else + sure=50 end - else - sure=50 end else WIDGET.keyPressed(key) @@ -159,6 +109,7 @@ scene.widgetList={ WIDGET.newButton{name="play",x=700,y=640,w=170,h=80,color='lY',code=pressKey"return",hideF=function()return listBox:getLen()==0 end,fText=DOGC{50,50,{'fPoly',10,0,49,24,10,49}}}, WIDGET.newButton{name="delete",x=850,y=640,w=80,h=80,color='lR',code=pressKey"delete",hideF=function()return listBox:getLen()==0 end,fText=DOGC{50,50,{'setLW',8},{'line',5,5,45,45},{'line',5,45,45,5}}}, WIDGET.newButton{name="back",x=1140,y=640,w=170,h=80,fText=TEXTURE.back,code=backScene}, + WIDGET.newButton{name="back",x=1140,y=640,w=170,h=80,fText=TEXTURE.back,code=backScene}, } return scene \ No newline at end of file