--[[ # ______ __ _ # # /_ __/___ _____ / /_ ____ ___ (_)____ ____ # # / / / _ \ / ___// __ \ / __ `__ \ / // __ \ / __ \ # # / / / __// /__ / / / // / / / / // // / / // /_/ / # # /_/ \___/ \___//_/ /_//_/ /_/ /_//_//_/ /_/ \____/ # Techmino is my first "huge project" optimization is welcomed if you also love tetromino stacking game Instructions: 1. I made a framework called Zframework, *most* code in Zframework are not directly relevant to game; 2. "xxx" are texts for reading by player, 'xxx' are string values just used in program; 3. Some goto statement are used for better performance. All goto-labes have detailed names so don't be afraid; 4. Except "gcinfo" function of lua itself, other "gc" are short for "graphics"; ]]-- --Var leak check -- setmetatable(_G,{__newindex=function(self,k,v)print('>>'..k)print(debug.traceback():match("\n.-\n\t(.-): "))rawset(self,k,v)end}) --System Global Vars Declaration local fs=love.filesystem VERSION=require"version" TIME=love.timer.getTime YIELD=coroutine.yield SYSTEM=love.system.getOS() FNSF=SYSTEM:find'\79\83'--What does FNSF stand for? IDK so don't ask me lol MOBILE=SYSTEM=='Android'or SYSTEM=='iOS' SAVEDIR=fs.getSaveDirectory() --Global Vars & Settings SFXPACKS={'chiptune'} VOCPACKS={'miya','mono','xiaoya','miku'} FIRSTLAUNCH=false DAILYLAUNCH=false --System setting math.randomseed(os.time()*626) love.setDeprecationOutput(false) love.keyboard.setKeyRepeat(true) love.keyboard.setTextInput(false) if MOBILE then local w,h,f=love.window.getMode() f.resizable=false love.window.setMode(w,h,f) end local _LOADTIMELIST_={} local _LOADTIME_=TIME() --Load modules Z=require'Zframework' FONT.load('parts/fonts/proportional.ttf') SCR.setSize(1280,720)--Initialize Screen size BGM.setMaxSources(5) BGM.setChange(function(name)MES.new('music',text.nowPlaying..name,5)end) VOC.setDiversion(1) table.insert(_LOADTIMELIST_,("Load Zframework: %.3fs"):format(TIME()-_LOADTIME_)) --Create shortcuts setFont=FONT.set getFont=FONT.get mStr=GC.mStr mText=GC.simpX mDraw=GC.draw Snd=SFX.playSample --Delete all naked files (from too old version) FILE.clear('') --Create directories for _,v in next,{'conf','record','replay','cache','lib'}do local info=fs.getInfo(v) if not info then fs.createDirectory(v) elseif info.type~='directory'then fs.remove(v) fs.createDirectory(v) end end CHAR=require'parts.char' require'parts.gameTables' require'parts.gameFuncs' --Load shader files from SOURCE ONLY SHADER={} for _,v in next,fs.getDirectoryItems('parts/shaders')do if isSafeFile('parts/shaders/'..v)then local name=v:sub(1,-6) SHADER[name]=love.graphics.newShader('parts/shaders/'..name..'.glsl') end end LINE= require'parts.line' DATA= require'parts.data' TEXTURE= require'parts.texture' SKIN= require'parts.skin' USERS= require'parts.users' NET= require'parts.net' VK= require'parts.virtualKey' BOT= require'parts.bot' RSlist= require'parts.RSlist'DSCP=RSlist.TRS.centerPos PLY= require'parts.player' NETPLY= require'parts.netPlayer' MODES= require'parts.modes' setmetatable(TEXTURE,{__index=function(self,k) MES.new('warn',"No texture called: "..k) self[k]=love.graphics.newCanvas(1,1) return self[k] end}) table.insert(_LOADTIMELIST_,("Load Parts: %.3fs"):format(TIME()-_LOADTIME_)) --Init Zframework Z.setIfPowerInfo(function() return SETTING.powerInfo and LOADED end) do--Z.setCursor local normImg=GC.DO{16,16, {'fCirc',8,8,4}, {'setCL',1,1,1,.7}, {'fCirc',8,8,6}, } local holdImg=GC.DO{16,16, {'setLW',2}, {'dCirc',8,8,7}, {'fCirc',8,8,3}, } local min,int,abs=math.min,math.floor,math.abs local gc_setColor,gc_draw=love.graphics.setColor,love.graphics.draw local ms=love.mouse Z.setCursor(function(time,x,y) if not SETTING.sysCursor then local R=int((time+1)/2)%7+1 _=BLOCK_COLORS[SETTING.skin[R]] gc_setColor(_[1],_[2],_[3],min(abs(1-time%2),.3)) _=DSCP[R][0] gc_draw(TEXTURE.miniBlock[R],x,y,time%3.14159265359*4,16,16,_[2]+.5,#BLOCKS[R][0]-_[1]-.5) gc_setColor(1,1,1) gc_draw(ms.isDown(1)and holdImg or normImg,x,y,nil,nil,nil,8,8) end end) end Z.setOnFnKeys({ function()MES.new('check',PROFILE.switch()and"profile start!"or"profile report copied!")end, function()MES.new('info',("System:%s[%s]\nluaVer:%s\njitVer:%s\njitVerNum:%s"):format(SYSTEM,jit.arch,_VERSION,jit.version,jit.version_num))end, function()MES.new('error',"挂了")end, function() if GAME.playing and not GAME.net then for _=1,8 do if #PLY_ALIVE>1 then local P=PLY_ALIVE[math.random(2,#PLY_ALIVE)] P.lastRecv=PLAYERS[1] P:lose() end end end end, function()print(WIDGET.getSelected()or"no widget selected")end, function()for k,v in next,_G do print(k,v)end end, function()if love['_openConsole']then love['_openConsole']()end end, }) do--Z.setOnFocus local function task_autoSoundOff() while true do coroutine.yield() local v=love.audio.getVolume() love.audio.setVolume(math.max(v-.05,0)) if v==0 then return end end end local function task_autoSoundOn() while true do coroutine.yield() local v=love.audio.getVolume() if v17 then v=17 end end if not RSlist[SETTING.RS]then SETTING.RS='TRS'end if SETTING.ghostType=='greyCell'then SETTING.ghostType='grayCell'end if type(SETTING.skinSet)=='number'then SETTING.skinSet='crystal_scf'end if not TABLE.find({8,10,13,17,22,29,37,47,62,80,100},SETTING.frameMul)then SETTING.frameMul=100 end if SETTING.cv then SETTING.vocPack,SETTING.cv=SETTING.cv end if RANKS.infinite then RANKS.infinite=0 end if RANKS.infinite_dig then RANKS.infinite_dig=0 end if not RANKS.sprint_10l then RANKS.sprint_10l=0 end if RANKS.master_l then RANKS.master_n,RANKS.master_l=RANKS.master_l needSave=true end if RANKS.master_u then RANKS.master_h,RANKS.master_u=RANKS.master_u needSave=true end for _,v in next,VK_ORG do v.color=nil end for name,rank in next,RANKS do if type(name)=='number'or type(rank)~='number'then RANKS[name]=nil needSave=true else local M=MODES[name] if M and M.unlock and rank>0 then for _,unlockName in next,M.unlock do if not RANKS[unlockName]then RANKS[unlockName]=0 needSave=true end end end if not(M and M.x)then RANKS[name]=nil needSave=true end end end if not MODES[STAT.lastPlay]then STAT.lastPlay='sprint_10l' needSave=true end if needSave then saveStats() saveProgress() saveSettings() love.event.quit('restart') end end --First start for phones if FIRSTLAUNCH and MOBILE then SETTING.VKSwitch=true SETTING.powerInfo=true SETTING.cleanCanvas=true end --Apply system setting applyAllSettings() --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) date, fileData=STRING.readLine(fileData)date=date:gsub("[a-zA-Z]","") mode, fileData=STRING.readLine(fileData)mode=MODE_UPDATE_MAP[mode]or mode version,fileData=STRING.readLine(fileData) player, fileData=STRING.readLine(fileData)if player=="Local Player"then player="Stacker"end local success success,fileData=pcall(love.data.decompress,'string','zlib',fileData) if not success then goto BREAK_cannotParse end seed, fileData=STRING.readLine(fileData) setting,fileData=STRING.readLine(fileData)setting=JSON.decode(setting) mod, fileData=STRING.readLine(fileData)mod=JSON.decode(mod) if not setting or not mod or not mode or #mode==0 then goto BREAK_cannotParse end fs.remove('replay/'..fileName) 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 ::BREAK_cannotParse:: local rep=DATA.parseReplay('replay/'..fileName) table.insert(REPLAY,rep) end table.sort(REPLAY,function(a,b)return a.fileName>b.fileName end) table.insert(_LOADTIMELIST_,("Initialize Data: %.3fs"):format(TIME()-_LOADTIME_)) for i=1,#_LOADTIMELIST_ do LOG(_LOADTIMELIST_[i])end --Launch testing task if launch param received if TABLE.find(arg,'--test')then TASK.new(function() while not LOADED do YIELD()end LOG("\27[92m\27[1mAutomatic Test Started\27[0m") BGM.setVol(0)SFX.setVol(0) love.keypressed('space') TEST.yieldUntilNextScene() for k,mode in next,MODES do if k~='netBattle'then LOG("Scanning mode: "..mode.name) loadGame(mode.name,true) TEST.yieldUntilNextScene() SCN.back() TEST.yieldUntilNextScene() end end LOG("\27[92m\27[1mAutomatic Test Passed :)\27[0m") TEST.yieldN(60) love.event.quit(0) end) TASK.new(function() while true do YIELD() if Z.errData[1]then break end end LOG("\27[91m\27[1mAutomatic Test Failed :(\27[0m\nThe error message is:\n"..table.concat(Z.errData[1].mes,"\n").."\27[91m\nAborting\27[0m") TEST.yieldN(60) love.event.quit(1) end) end