diff --git a/Zframework/tableExtend.lua b/Zframework/tableExtend.lua index 9417157c6..7c73e0da1 100644 --- a/Zframework/tableExtend.lua +++ b/Zframework/tableExtend.lua @@ -15,6 +15,29 @@ function TABLE.new(val,count) return L end +-- Get a new empty table with __lock() and __unlock() to protect changes +function TABLE.newWithLockMetamethod() + local t + do + local lockedKey={} + local realTable={} + t=setmetatable( -- This is the fake table, act like a wrapper of realTable + { + __lock =function(k) if k then lockedKey[k]=true end end, + __unlock=function(k) + if k then lockedKey[k]=false else TABLE.cut(lockedKey) end + end, + },{ + __index =realTable, + __newindex=function(_,k,v) if not lockedKey[k] then realTable[k]=v end end, + __len =function(_,_,_) return #realTable end, + __tostring=function(_,_,_) return tostring(realTable) end, + } + ) + end + return t +end + -- Get a copy of [1~#] elements function TABLE.shift(org,depth) if not depth then depth=1e99 end diff --git a/parts/data.lua b/parts/data.lua index 444d57000..987051a48 100644 --- a/parts/data.lua +++ b/parts/data.lua @@ -314,6 +314,7 @@ do-- function DATA.saveReplay() seed=GAME.seed, setting=GAME.setting, mod=_getModList(), + modApplyAt=GAME.modApplyAt, tasUsed=GAME.tasUsed, } if GAME.curMode.savePrivate then @@ -373,6 +374,7 @@ function DATA.parseReplayData(fileName,fileData,ifFull) seed=metaData.seed, setting=metaData.setting, mod=metaData.mod, + modApplyAt=metaData.modApplyAt, tasUsed=metaData.tasUsed, } if ifFull then rep.data=fileData end diff --git a/parts/gameFuncs.lua b/parts/gameFuncs.lua index ad2ebada6..1cd9373ef 100644 --- a/parts/gameFuncs.lua +++ b/parts/gameFuncs.lua @@ -510,10 +510,8 @@ function mergeStat(stat,delta)-- Merge delta stat. to global stat. end end function scoreValid()-- Check if any unranked mods are activated - for number,sel in next,GAME.mod do - if sel>0 and MODOPT[number].unranked then - return false - end + for _,sel in next,GAME.mod do + if sel>0 then return false end end if GAME.playing and GAME.tasUsed then return false @@ -1000,23 +998,28 @@ do-- function dumpBasicConfig() end end do-- function resetGameData(args) - local function task_showMods() -- TODO + local function task_showMods() coroutine.yield() local counter=0 - for number,sel in next,GAME.mod do - if sel>0 then - if counter==0 then - coroutine.yield() - else + + if usingMod() then + SFX.play('collect',.2) + TEXT.show(GAME.modApplyAt,640,26,45,'spin') + for _=1,90 do coroutine.yield() end + + for number,sel in next,GAME.mod do + if sel>0 then for _=1,20 do coroutine.yield() end + local M=MODOPT[number] + SFX.play('collect',.2) + TEXT.show(M.id,640+(counter%5-2)*80,26,45,'spin') + counter=counter+1 end - local M=MODOPT[number] - SFX.play('collect',.2) - TEXT.show(M.id,640+(counter%5-2)*80,26,45,'spin') - counter=counter+1 end + for _=1,(counter%5)*20+90 do coroutine.yield() end + if GAME.playing then PLAYERS[1].showUsername=true end end end local gameSetting={ diff --git a/parts/gameTables.lua b/parts/gameTables.lua index 99ea3b9f4..a28dbbd1a 100644 --- a/parts/gameTables.lua +++ b/parts/gameTables.lua @@ -354,144 +354,172 @@ do-- Mod data local function _disableKey(P,key) table.insert(P.gameEnv.keyCancel,key) end + local _invisibleTime={ + ['easy'] =300, + ['slow'] =100, + ['medium']=60, + ['fast'] =20, + ['none'] =0, + } + local function _lockVfunc(k) do + local K=k + return function(P,O) + P.gameEnv[K]=O or true + P.gameEnv.__lock(K) + end + end end MODOPT={-- Mod options {no=0,id="NX",name="next", key="q",x=80,y=230,color='lO', list={0,1,2,3,4,5,6}, - func=function(P,O) P.gameEnv.nextCount=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.nextCount=O end, + funcOnce =_lockVfunc('nextCount'), }, {no=1,id="HL",name="hold", key="w",x=200,y=230,color='lO', list={0,1,2,3,4,5,6}, - func=function(P,O) P.gameEnv.holdCount=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.holdCount=O end, + funcOnce =_lockVfunc('holdCount'), }, {no=2,id="FL",name="hideNext", key="e",x=320,y=230,color='lA', list={1,2,3,4,5}, - func=function(P,O) P.gameEnv.nextStartPos=O+1 end, - unranked=true, + funcInit =function(P,O) P.gameEnv.nextStartPos=O+1 end, + funcOnce =_lockVfunc('nextStartPos'), }, {no=3,id="IH",name="infHold", key="r",x=440,y=230,color='lA', - func=function(P) P.gameEnv.infHold=true end, - unranked=true, + funcInit =function(P) P.gameEnv.infHold=true end, + funcOnce =_lockVfunc('infHold'), }, {no=4,id="HB",name="hideBlock", key="y",x=680,y=230,color='lV', - func=function(P) P.gameEnv.block=false end, - unranked=true, + funcInit =function(P) P.gameEnv.block=false end, + funcOnce =_lockVfunc('block'), }, {no=5,id="HG",name="hideGhost", key="u",x=800,y=230,color='lV', - func=function(P) P.gameEnv.ghost=false end, - unranked=true, + funcInit =function(P) P.gameEnv.ghost=false end, + funcOnce =function(P) P.gameEnv.ghost=false; P.gameEnv.__lock('ghost') end, }, {no=6,id="HD",name="hidden", key="i",x=920,y=230,color='lP', list={'easy','slow','medium','fast','none'}, - func=function(P,O) P.gameEnv.visible=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.visible=O end, + funcRepeat=function(P,O) + if P.showTime~=_invisibleTime[O] then + P:setInvisible(_invisibleTime[O]) + end + end, }, {no=7,id="HB",name="hideBoard", key="o",x=1040,y=230,color='lP', list={'down','up','all'}, - func=function(P,O) P.gameEnv.hideBoard=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.hideBoard=O end, }, {no=8,id="FB",name="flipBoard", key="p",x=1160,y=230,color='lJ', list={'U-D','L-R','180'}, - func=function(P,O) P.gameEnv.flipBoard=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.flipBoard=O end, + funcOnce =_lockVfunc('flipBoard'), }, {no=9,id="DT",name="dropDelay", key="a",x=140,y=350,color='lR', list={0,.125,.25,.5,1,2,3,4,5,6,7,8,9,10,12,14,16,18,20,25,30,40,60,180,1e99}, - func=function(P,O) P.gameEnv.drop=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.drop=O end, + funcRepeat=function(P,O) + if P.dropDelay~=O then + P.gameEnv.drop=O + P:set20G(O==0) + end + end, }, {no=10,id="LT",name="lockDelay", key="s",x=260,y=350,color='lR', list={0,1,2,3,4,5,6,7,8,9,10,12,14,16,18,20,25,30,40,60,180,1e99}, - func=function(P,O) P.gameEnv.lock=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.lock=O end, + funcRepeat=function(P,O) P.dropDelay=O end, }, {no=11,id="ST",name="waitDelay", key="d",x=380,y=350,color='lR', list={0,1,2,3,4,5,6,7,8,10,15,20,30,60}, - func=function(P,O) P.gameEnv.wait=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.wait=O end, + funcOnce =_lockVfunc('waitDelay'), }, {no=12,id="CT",name="fallDelay", key="f",x=500,y=350,color='lR', list={0,1,2,3,4,5,6,7,8,10,15,20,30,60}, - func=function(P,O) P.gameEnv.fall=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.fall=O end, + funcOnce =_lockVfunc('fallDelay'), }, {no=13,id="LF",name="life", key="j",x=860,y=350,color='lY', list={0,1,2,3,5,10,15,26,42,87,500}, - func=function(P,O) P.gameEnv.life=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.life=O end, + funcOnce =_lockVfunc('life'), }, {no=14,id="FB",name="forceB2B", key="k",x=980,y=350,color='lY', - func=function(P) P.gameEnv.b2bKill=true end, - unranked=true, + funcInit =function(P) P.gameEnv.b2bKill=true end, + funcOnce =_lockVfunc('b2bKill'), }, {no=15,id="PF",name="forceFinesse", key="l",x=1100,y=350,color='lY', - func=function(P) P.gameEnv.fineKill=true end, - unranked=true, + funcInit =function(P) P.gameEnv.fineKill=true end, + funcOnce =_lockVfunc('fineKill'), }, {no=16,id="TL",name="tele", key="z",x=200,y=470,color='lH', - func=function(P) + funcInit =function(P) P.gameEnv.das,P.gameEnv.arr=0,0 P.gameEnv.sddas,P.gameEnv.sdarr=0,0 end, - unranked=true, + funcOnce =function(P) + for _,k in pairs{'das','arr','sddas','sdarr'} do + P.gameEnv[k]=0 + P.gameEnv.__lock(k) + end + end }, {no=17,id="FX",name="noRotation", key="x",x=320,y=470,color='lH', - func=function(P) + funcInit =function(P) _disableKey(P,3) _disableKey(P,4) _disableKey(P,5) end, - unranked=true, }, {no=18,id="GL",name="noMove", key="c",x=440,y=470,color='lH', - func=function(P) - _disableKey(P,1)_disableKey(P,2) + funcInit =function(P) + _disableKey(P,1) _disableKey(P,2) _disableKey(P,11)_disableKey(P,12) _disableKey(P,17)_disableKey(P,18) _disableKey(P,19)_disableKey(P,20) end, - unranked=true, }, {no=19,id="CS",name="customSeq", key="b",x=680,y=470,color='lB', list={'bag','bagES','his','hisPool','c2','bagP1inf','rnd','mess','reverb'}, - func=function(P,O) P.gameEnv.sequence=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.sequence=O end, + funcOnce =_lockVfunc('sequence'), }, {no=20,id="PS",name="pushSpeed", key="n",x=800,y=470,color='lB', list={.5,1,2,3,5,15,1e99}, - func=function(P,O) P.gameEnv.pushSpeed=O end, - unranked=true, + funcInit =function(P,O) P.gameEnv.pushSpeed=O end, + funcOnce =_lockVfunc('pushSpeed'), }, {no=21,id="BN",name="boneBlock", key="m",x=920,y=470,color='lB', list={'on','off'}, - func=function(P,O) P.gameEnv.bone=O=='on' end, - unranked=true, + funcInit =function(P,O) P.gameEnv.bone=O=='on' end, + funcOnce =function(P,O) + P.gameEnv.bone=O=='on' + P.gameEnv.__lock('bone') + end }, } for i=1,#MODOPT do @@ -521,7 +549,9 @@ do-- Game data tables seed=1046101471, -- Game seed curMode=false, -- Current gamemode object initPlayerCount=0, -- Player count when init game + modUsed=false, mod=TABLE.new(0,#MODOPT),-- List of loaded mods + modApplyAt='postInit', -- Apply mod when? (preInit, postInit, always) modeEnv=false, -- Current gamemode environment setting={}, -- Game settings rep={}, -- Recording list, key,time,key,time... diff --git a/parts/language/lang_en.lua b/parts/language/lang_en.lua index f0bb5a5b4..a5ebadffa 100644 --- a/parts/language/lang_en.lua +++ b/parts/language/lang_en.lua @@ -209,6 +209,7 @@ return { tryAnotherBuild="UTF-8 decoding error. If you are using Microsoft Windows, please try to download the build with the different architecture. If you are using the x86 version of Techmino, try downloading the x64 version and vice versa.", modInstruction="Choose your modifiers here!\nMods allow you to change the rules in various ways, but they can also crash the game. Feel free to play the game your way!\nNote that scores are not saved when using mods. You can also toggle mods using your keyboard keys (hold Shift to reverse).", + modApplyAtInstruction="Choose when you want the modifier to be applied.\npreInit: Before loading the modes. The default option in V0.17.15 and earlier, but may cause problems with certain mods.\npostInit: After loading the game. This can fix some bugs in preInit, but some mods can still be disabled by modes in some situations.\nalways: Throughout the entire game. Keeps the effects of the mods in all situations.", modInfo={ next="NEXT\nOverrides the number of Next pieces displayed.", hold="HOLD\nOverrides the number of Hold pieces displayed.", @@ -449,6 +450,7 @@ C. Gamepad }, mod={ title="Mods", + applyAt="Apply during", reset="Reset (tab)", unranked="Unranked", }, diff --git a/parts/language/lang_es.lua b/parts/language/lang_es.lua index 8dcbc7658..3b4899559 100644 --- a/parts/language/lang_es.lua +++ b/parts/language/lang_es.lua @@ -208,6 +208,7 @@ return { tryAnotherBuild="[UTF-8 Inválido] Si estás usando Windows, intenta descargar Techmino-win32 o Techmino-win64 (el que no estés usando ahora).", modInstruction="¡Elige tus mods!\nLos mods permiten modificar el juego,\npero también es posible que lo crasheen.\nLos scores no se guardan durante el uso de mods.", + -- modApplyAtInstruction="Choose when you want the modifier to be applied.\npreInit: Before loading the modes. The default option in V0.17.15 and earlier, but may cause problems with certain mods.\npostInit: After loading the game. This can fix some bugs in preInit, but some mods can still be disabled by modes in some situations.\nalways: Throughout the entire game. Keeps the effects of the mods in all situations. ", modInfo={ next="NEXT\nSobreescribe el nro. de piezas siguientes a mostrar.", hold="HOLD\nSobreescribe el nro. de piezas en reserva disponibles.", @@ -411,6 +412,7 @@ return { mod={ title="Mods", reset="Reinic. (tab)", + -- applyAt="Apply during", unranked="Sin rango", }, pause={ diff --git a/parts/language/lang_fr.lua b/parts/language/lang_fr.lua index 099cc907c..c07129b5f 100644 --- a/parts/language/lang_fr.lua +++ b/parts/language/lang_fr.lua @@ -210,6 +210,7 @@ return { -- tryAnotherBuild="[Invalid UTF-8] If you are on Windows, try downloading Techmino-win32 or Techmino-win64 (different from what you are using now).", modInstruction="", + -- modApplyAtInstruction="Choose when you want the modifier to be applied.\npreInit: Before loading the modes. The default option in V0.17.15 and earlier, but may cause problems with certain mods.\npostInit: After loading the game. This can fix some bugs in preInit, but some mods can still be disabled by modes in some situations.\nalways: Throughout the entire game. Keeps the effects of the mods in all situations. ", modInfo={},-- See lang_en.lua pauseStat={ "Temps :", @@ -387,6 +388,7 @@ return { mod={ title="Mods", reset="Réinitialiser (tab)", + -- applyAt="Apply during", unranked="Unranked", }, pause={ diff --git a/parts/language/lang_id.lua b/parts/language/lang_id.lua index 310ffb146..f4019f7fb 100644 --- a/parts/language/lang_id.lua +++ b/parts/language/lang_id.lua @@ -209,6 +209,7 @@ return { errorMsg="Techmino mengalami eror dan harus memuat ulang.\nAnda bisa mengirim log eror ke developer.", modInstruction="Pilih mod Anda!\nMod memungkinkan Anda untuk mengubah permainan,\ntetapi juga bisa menghancur permainan.\nNilai-nilai tidak akan disimpan saat menggunakan mod.", + -- modApplyAtInstruction="Choose when you want the modifier to be applied.\npreInit: Before loading the modes. The default option in V0.17.15 and earlier, but may cause problems with certain mods.\npostInit: After loading the game. This can fix some bugs in preInit, but some mods can still be disabled by modes in some situations.\nalways: Throughout the entire game. Keeps the effects of the mods in all situations. ", modInfo={ next="BLOK LANJUT\nMengubah jumlah pratinjau blok lanjut yang ditampilkan.", hold="SIMPAN\nMengubah jumlah blok yang bisa disimpan.", @@ -412,6 +413,7 @@ return { mod={ title="Mod", reset="Reset (tab)", + -- applyAt="Apply during", unranked="Tidak Berperingkat", }, pause={ diff --git a/parts/language/lang_ja.lua b/parts/language/lang_ja.lua index 46e7b7259..cade7f939 100644 --- a/parts/language/lang_ja.lua +++ b/parts/language/lang_ja.lua @@ -210,6 +210,7 @@ return { tryAnotherBuild="[Invalid UTF-8]使用しているOSがMicrosoft WindowsであればTechmino-win32かTechmino-win64をダウンロードしてください! (現在使用しているソフトは違うバージョンです)", modInstruction="Modを選択してください!\nModはゲームルールを変えられますが正常にプレイできなくなる可能性があります\nModを使用した場合、スコアは保存されません", + -- modApplyAtInstruction="Choose when you want the modifier to be applied.\npreInit: Before loading the modes. The default option in V0.17.15 and earlier, but may cause problems with certain mods.\npostInit: After loading the game. This can fix some bugs in preInit, but some mods can still be disabled by modes in some situations.\nalways: Throughout the entire game. Keeps the effects of the mods in all situations. ", modInfo={ next="NEXT\nNEXTの個数を変更します", hold="HOLD\nHOLDの個数を変更します", @@ -456,6 +457,7 @@ C. ゲームパッド mod={ title="Mods", reset="リセット (tab)", + -- applyAt="Apply during", unranked="記録不可", }, pause={ diff --git a/parts/language/lang_pt.lua b/parts/language/lang_pt.lua index 7768e2d67..853347361 100644 --- a/parts/language/lang_pt.lua +++ b/parts/language/lang_pt.lua @@ -197,6 +197,7 @@ return { -- tryAnotherBuild="[Invalid UTF-8] If you are on Windows, try downloading Techmino-win32 or Techmino-win64 (different from what you are using now).", modInstruction="Selecione mods (modificadores) para usar!\nMods irão modificar o jogo em formas diferentes\n(e possivelmente quebrar o jogo de formas estranhas).\nAlguns mods irão fazer seu jogo unranked.", + -- modApplyAtInstruction="Choose when you want the modifier to be applied.\npreInit: Before loading the modes. The default option in V0.17.15 and earlier, but may cause problems with certain mods.\npostInit: After loading the game. This can fix some bugs in preInit, but some mods can still be disabled by modes in some situations.\nalways: Throughout the entire game. Keeps the effects of the mods in all situations. ", modInfo={ next="Próximo\nSobrepõe a largura da fila de próximos", hold="Segure\nSobrepõe a largura da fila de segurar", @@ -400,6 +401,7 @@ return { mod={ title="Mods", reset="Reset (tab)", + -- applyAt="Apply during", unranked="Unranked", }, pause={ diff --git a/parts/language/lang_symbol.lua b/parts/language/lang_symbol.lua index 188e3e32d..dddf09bca 100644 --- a/parts/language/lang_symbol.lua +++ b/parts/language/lang_symbol.lua @@ -159,6 +159,7 @@ return { mod={ title="?!?!?!", reset="R (tab)", + applyAt="--:-- >[?!?!?!]", unranked="X!!!", }, pause={ diff --git a/parts/language/lang_vi.lua b/parts/language/lang_vi.lua index 0a681e523..756af5f2b 100644 --- a/parts/language/lang_vi.lua +++ b/parts/language/lang_vi.lua @@ -204,6 +204,7 @@ return { tryAnotherBuild="LOI UTF-8! Neu dang choi Techmino tren Windows: hay doi tu x86 sang x64 va nguoc lai.", modInstruction="Hãy chọn modifier bạn muốn.\nMod cho phép bạn có thể tùy biến game, nhưng cũng có thể làm game sập.\nKể cả thế, hãy thoải mái và chơi theo cách của bạn!\nBạn có thể dùng bàn phím để chọn mod (giữ Shift để chọn lùi)\nĐiểm sẽ không được lưu lại khi dùng mod.", + modApplyAtInstruction="Chọn thời điểm bạn muốn mod được áp dụng.\npreInit: Trước khi nạp game. Đây là tùy chọn mặc định từ phiên bản 0.17.15 trở về trước. Một số mod có thể gặp vấn đề nếu dùng tùy chọn này.\npostInit: Sau khi nạp chế độ chơi, có thể sửa được một số lỗi ở preInit. Tuy nhiên, mod có thể bị mất tác dụng ở một số trường hợp nhất định.\nalways: Luôn áp dụng trong cả game. Tùy chọn này sẽ giúp bạn giữ lại tác dụng của mod dù ở bất kì trường hợp nào.", modInfo={ next="NEXT\nGhi đè số gạch hiển thị ở hàng NEXT", hold="HOLD\nGhi đè số lượng gạch được giữ ở cột HOLD", @@ -445,6 +446,7 @@ C. Tay cầm chơi game (Gamepad): mod={ title="Mods", reset="Đặt lại (tab)", + applyAt="Áp dụng mod khi", unranked="Không tính điểm", }, pause={ diff --git a/parts/language/lang_zh.lua b/parts/language/lang_zh.lua index 9c5ed3405..7cad4a744 100644 --- a/parts/language/lang_zh.lua +++ b/parts/language/lang_zh.lua @@ -209,6 +209,7 @@ return { tryAnotherBuild="[解码UTF-8错误] 如果你现在用的是Windows系统,请重新下载 Techmino-32/64位 (和现在运行的不一样的那个)。", modInstruction="选择你要使用的Mod!\n不同Mod会用不同的方式改变初始游戏规则(可能导致不能正常游玩)\n来开发新玩法或者挑战自我吧!\n提醒:开启一些Mod会让成绩无效,你也可以用键盘开关Mod,按住shift反向", + -- modApplyAtInstruction="Choose when you want the modifier to be applied.\npreInit: Before loading the modes. The default option in V0.17.15 and earlier, but may cause problems with certain mods.\npostInit: After loading the game. This can fix some bugs in preInit, but some mods can still be disabled by modes in some situations.\nalways: Throughout the entire game. Keeps the effects of the mods in all situations. ", modInfo={ next="Next数量:\n强制使用Next的个数", hold="Hold数量:\n强制使用Hold的个数", diff --git a/parts/language/lang_zh_code.lua b/parts/language/lang_zh_code.lua index d825bbcdc..998f410a5 100644 --- a/parts/language/lang_zh_code.lua +++ b/parts/language/lang_zh_code.lua @@ -157,6 +157,8 @@ return { tryAnotherBuild="Error.DecodeUTF8(); //如果你现在用的是Windows系统,请重新下载 Techmino-32/64位 (和现在运行的不一样的那个)。", modInstruction="Mod.Instruction();\n/*选择你要使用的Mod\n不同Mod会用不同的方式改变初始游戏规则(可能导致不能正常游玩)\n提醒:开启一些Mod会让成绩无效,你也可以用键盘开关Mod,按住shift反向*/", + -- [[[NEED TRANSLATING!]]] + modApplyAtInstruction="Mod.ApplyDuring=PLAYERS[1].username=='shoucandanghehe' ? 'preInit' : 'postInit' --[[\npreInit: Before loading the modes. The default option in V0.17.15 and earlier, but may cause problems with certain mods.\npostInit: After loading the game. This can fix some bugs in preInit, but some mods can still be disabled by modes in some situations.\nalways: Throughout the entire game. Keeps the effects of the mods in all situations.]]", modInfo={ next="Mod.Next();\n//强制使用Next的个数", hold="Mod.Hold\n//强制使用Hold的个数", @@ -359,6 +361,7 @@ return { }, mod={ title="GameMod.UI", + applyAt="Mod.ApplyDuring=", reset="Mod.Reset();", unranked="unranked=true", }, diff --git a/parts/language/lang_zh_trad.lua b/parts/language/lang_zh_trad.lua index d23302f68..e56a6175e 100644 --- a/parts/language/lang_zh_trad.lua +++ b/parts/language/lang_zh_trad.lua @@ -209,6 +209,7 @@ return { tryAnotherBuild="[無效的UTF-8] 如果你使用的是Windows作業系統,請嘗試下載Techmino-win32或Techmino-win64(與你現在使用的不同的版本)。", modInstruction="選擇你想使用的Mod!\n不同的Mod會以不同的方式改變遊戲規則(可能導致遊戲異常)\n快來開發新玩法或挑戰自我吧!\n提醒:開啟Mod會使成績無效,你可以使用鍵盤開關Mod,按下shift反向", + -- modApplyAtInstruction="Choose when you want the modifier to be applied.\npreInit: Before loading the modes. The default option in V0.17.15 and earlier, but may cause problems with certain mods.\npostInit: After loading the game. This can fix some bugs in preInit, but some mods can still be disabled by modes in some situations.\nalways: Throughout the entire game. Keeps the effects of the mods in all situations. ", modInfo={ next="Next數量:\n強制使用Next的個數", hold="Hold數量:\n強制使用Hold的個數", @@ -412,6 +413,7 @@ return { mod={ title="Mods", reset="重設(tab)", + -- applyAt="Apply during", unranked="成績無效", }, pause={ diff --git a/parts/language/manual_vi.txt b/parts/language/manual_vi.txt index 954ac59a8..2c36f8a97 100644 --- a/parts/language/manual_vi.txt +++ b/parts/language/manual_vi.txt @@ -1,6 +1,6 @@ Cách chơi: Hệ thống sẽ cấp cho người chơi 7 loại tetromino (gạch 4 ô) bao gồm: Z, S, J, L, I, O, T; - và người chơi cần điều khiển chúng (di chuyển sang trái và phải; xoay 90 / 180 / 270 độ). + và người chơi cần điều khiển chúng (di chuyển sang trái và phải; xoay 90 / 180 / 270 độ). Cứ mỗi hàng được lấp đầy trong bảng bởi các viên gạch, chúng sẽ bị xóa ra khỏi bảng. Nếu có đối thủ, đối thủ sẽ bị tấn công mạnh hoặc nhẹ tùy theo số hàng bạn gửi. Chơi hết hoặc đạt mục tiêu của cấp độ để thắng. @@ -25,19 +25,19 @@ Cách phát hiện spin: bằng cách sử dụng hệ thống điểm. Hệ thống tấn công: - Kiểu xóa nâng cao (Special clear): - Kiểu xóa nâng cao chỉ trường hợp bạn thực hiện Spin, Techrash, Techrash+ — xóa 5 hàng hoặc hơn, PC, HPC. - Kiểu xóa nâng cao có thể sạc đầy thanh B2B. + Kiểu xóa Đặc biệt (Special clear): + Kiểu xóa Đặc biệt chỉ trường hợp bạn thực hiện Spin, Techrash, Techrash+ — xóa 5 hàng hoặc hơn, PC, HPC. + Kiểu xóa Đặc biệt có thể sạc đầy thanh B2B. Kiểu xóa Siêu cấp (Super clear): - Là Kiểu xóa nâng cao nhưng được thực hiện với B2B hoặc B3B + Là Kiểu xóa Đặc biệt nhưng được thực hiện với B2B hoặc B3B - Kiểu xóa nâng cao (spin): Gửi gấp đôi số hàng vừa xóa. + Kiểu xóa Đặc biệt (spin): Gửi gấp đôi số hàng vừa xóa. — B2B gửi thêm 1/1/2/4/8 cho Spin Đơn/Spin Đôi/Spin Tam/Techrash/Techrash+ — B2B2B gửi thêm (số hàng xóa × 0.5) trên B2B và +1 hàng để đánh chặn — Mini sẽ bị cắt chỉ còn ¼ so với giá trị ban đầu - Kiểu xóa nâng cao (Techrash/Techrash+ nhưng không spin): + Kiểu xóa Đặc biệt (Techrash/Techrash+ nhưng không spin): — B2B gửi thêm 1 hàng — B3B boost 50% tấn công và +1 hàng để đánh chặn @@ -55,7 +55,7 @@ Hệ thống tấn công: Perfect Clear (All Clear): Gửi 8 → 16 hàng rác PC thứ nhất chỉ gửi 8 hàng rác, các PC sau đó sẽ thêm 2 hàng rác để tấn công (tổng hàng rác có thể gửi tối đa là 16 hàng) - Sau khi tính toán xong, lượng sát thương sẽ bị làm tròn XUỐNG trước khi gửi. + Sau khi tính tổng xong, lượng sát thương sẽ bị làm tròn XUỐNG trước khi gửi. Thanh Back to Back (B2B): @@ -64,7 +64,7 @@ Thanh Back to Back (B2B): Một người chơi đang ở trang thái B3B khi thanh có hơn 800 điểm. Nếu có hàng được xóa: - Kiểu xóa nâng cao: + Kiểu xóa Đặc biệt: — Spin Đơn/Đôi/Tam/Techrash/Techrash+ lần lượt cộng thêm 50/100/180/800/1000 (×50% nếu là Mini) — Techrash +150 điểm, nếu xóa hơn 4 hàng cùng lúc, mỗi hàng từ hàng thứ 5 trở đi cộng thêm 50 điểm. — PC từ thứ 4 trở đi có giá trị 800 điểm @@ -82,12 +82,13 @@ Hệ thống tính điểm: Việc tính điểm cũng độc lập tùy vào từng chế độ. -Khoảng thời gian chờ trước khi tấn công: - Một đòn sát thương sẽ không có hiệu lực ngay lập tức để người chơi có chút thời gian phản ứng trước khi tràn vào bảng. - Thời gian chờ của các đòn tấn công như sau, sắp xếp từ nhanh nhất tới lâu nhất: +Khoảng thời gian chờ trước khi gây sát thương: + Một đòn sát thương sẽ không có hiệu lực ngay lập tức + để người chơi có chút thời gian phản ứng trước khi chúng có hiệu lực (rác tràn vào bảng). + Thời gian chờ của các đòn tấn công như sau, xếp từ ngắn nhất tới dài nhất: — Double và Triple (Đôi và Tam) là nhanh nhất — Theo sau là Techrash, Techrash+, spin; Mini, B2B và B3B có thêm thêm chút thời gian chờ nữa. - — Sát thương từ combo có thời gian chờ lâu nhất.. + — Sát thương từ combo có thời gian chờ lâu nhất. Phản công: diff --git a/parts/player/draw.lua b/parts/player/draw.lua index 0c118ea9d..fee32bfae 100644 --- a/parts/player/draw.lua +++ b/parts/player/draw.lua @@ -787,9 +787,11 @@ function draw.norm(P,repMode) gc_scale(P.size) -- Draw username - setFont(30) - gc_setColor(GROUP_COLORS[P.group]) - GC.mStr(P.username or USERS.getUsername(P.uid),300,-60) + if P.showUsername then + setFont(30) + gc_setColor(GROUP_COLORS[P.group]) + GC.mStr(P.username or USERS.getUsername(P.uid),300,-60) + end -- Draw HUD if ENV.nextCount>0 then _drawNext(P,repMode) end diff --git a/parts/player/init.lua b/parts/player/init.lua index cafd04ec2..55c58313a 100644 --- a/parts/player/init.lua +++ b/parts/player/init.lua @@ -175,8 +175,49 @@ local function _newEmptyPlayer(id,mini) } return P end +local function _executeMod(P) + local applyStatus=GAME.modApplyAt + if applyStatus=='always' then + if not GAME.modCodeList then GAME.modCodeList={} end + if not GAME.modCodeList[P.id] then GAME.modCodeList[P.id]={} end + + if not GAME.applyModsTask then + function GAME.applyModsTask() + while GAME.playing do + for _,p in pairs(GAME.modCodeList) do + for _,c in pairs(p) do pcall(c) end + end + coroutine.yield() + end + -- Kill mod patching function when game stopped + TASK.removeTask_code(GAME.applyModsTask) + TABLE.clear(GAME.modCodeList) + GAME.modCodeList=nil + GAME.applyModsTask=nil + end + TASK.new(GAME.applyModsTask) + end + end + + for i=1,#GAME.mod do + if GAME.mod[i]>0 then + local M=MODOPT[i] + if applyStatus=='always' then + if M.funcOnce then + M.funcOnce(P,M.list and M.list[GAME.mod[i]]) + elseif M.funcRepeat then + table.insert(GAME.modCodeList[P.id],function() M.funcInit(P,M.list and M.list[GAME.mod[i]]) end) + else + M.funcInit(P,M.list and M.list[GAME.mod[i]]) + end + else -- Already checked pre or post before calling _executeMod() + M.funcInit(P,M.list and M.list[GAME.mod[i]]) + end + end + end +end local function _loadGameEnv(P)-- Load gameEnv - P.gameEnv={}-- Current game setting environment + P.gameEnv=TABLE.newWithLockMetamethod()-- Current game setting environment local ENV=P.gameEnv local GAME,SETTING=GAME,SETTING -- Load game settings @@ -199,13 +240,9 @@ local function _loadGameEnv(P)-- Load gameEnv ENV[k]=TABLE.copy(v) end end - if ENV.allowMod then - for i=1,#GAME.mod do - if GAME.mod[i]>0 then - local M=MODOPT[i] - M.func(P,M.list and M.list[GAME.mod[i]]) - end - end + + if ENV.allowMod and GAME.modApplyAt=='preInit' then + _executeMod(P) end end local function _loadRemoteEnv(P,confStr)-- Load gameEnv @@ -309,6 +346,10 @@ local function _applyGameEnv(P)-- Finish gameEnv processing break end + if ENV.allowMod and GAME.modApplyAt~='preInit' then + _executeMod(P) + end + P._20G=ENV.drop==0 P.dropDelay=ENV.drop P.lockDelay=ENV.lock @@ -389,9 +430,7 @@ local function _applyGameEnv(P)-- Finish gameEnv processing P:newNext() end end - for _=1,ENV.trueNextCount do - P:newNext() - end + for _=1,ENV.trueNextCount do P:newNext() end if P.miniMode then ENV.lockFX=false @@ -477,6 +516,7 @@ function PLY.newAIPlayer(id,AIdata,mini,p) group=0, } if p then TABLE.coverR(p,pData) end P.username="BOT"..pData.uid + P.showUsername=true P.sid=NET.uid_sid[pData.uid] or pData.uid P.group=pData.group if not (P.group%1==0 and P.group>=1 and P.group<=6) then P.group=0 end @@ -510,6 +550,8 @@ function PLY.newPlayer(id,mini,p) _loadGameEnv(P) _applyGameEnv(P) + + P.showUsername=not (P.gameEnv.allowMod and usingMod()) end ---------------------------------------------------- -return PLY +return PLY \ No newline at end of file diff --git a/parts/player/player.lua b/parts/player/player.lua index aa58531af..f3dc4340d 100644 --- a/parts/player/player.lua +++ b/parts/player/player.lua @@ -2915,6 +2915,9 @@ function Player:_die() end end end + if GAME.applyModsTask then + TABLE.cut(GAME.modCodeList[self.id]) + end end function Player:update(dt) self.trigFrame=self.trigFrame+dt*60 diff --git a/parts/scenes/dict.lua b/parts/scenes/dict.lua index 3fe36891e..4d8ebabcc 100644 --- a/parts/scenes/dict.lua +++ b/parts/scenes/dict.lua @@ -325,5 +325,5 @@ scene.widgetList={ WIDGET.newButton{name='back',x=1185,y=60,w=170,h=80,sound='back',font=60,fText=CHAR.icon.back,code=backScene}, WIDGET.newKey{name='help',x=1170,y=140,w=200,h=60,font=40,fText=CHAR.controller.xboxY.."/[F1]: "..CHAR.icon.help,code=pressKey'f1'}, } --- NOTE: The gap between Link-Copy, Zoom is 60*1.5-10=80; the gap between 2 buttons in one group is 60+10=70 -return scene + +return scene \ No newline at end of file diff --git a/parts/scenes/mod.lua b/parts/scenes/mod.lua index 49d1d5143..afb1fde60 100644 --- a/parts/scenes/mod.lua +++ b/parts/scenes/mod.lua @@ -98,11 +98,11 @@ function scene.draw() local t=M.time*.01-- t range:0~0.1 GC.scale(1+3*t) GC.rotate(t) - local rad,side - if M.unranked then - rad,side=45,5 - else - rad=40 + local rad,side=45,5 + if GAME.modApplyAt=='always' then + if M.funcRepeat then side=nil + elseif M.funcOnce then side=8 + else side=5 end end local color=M.color GC.setColor(color[1],color[2],color[3],5*t) @@ -130,6 +130,9 @@ function scene.draw() if selected then setFont(30) GC.printf(text.modInfo[selected.name],70,540,950) + elseif WIDGET.isFocus(scene.widgetList.modApplyAt) then + setFont(20) + GC.printf(text.modApplyAtInstruction,70,540,950) else setFont(25) GC.printf(text.modInstruction,70,540,950) @@ -137,10 +140,11 @@ function scene.draw() end scene.widgetList={ - WIDGET.newText{name='title', x=80,y=50,font=70,align='L'}, - WIDGET.newText{name='unranked',x=1200,y=60,color='Y',font=50,align='R'}, - WIDGET.newButton{name='reset', x=1140,y=540,w=170,h=80,font=25,code=pressKey'tab'}, - WIDGET.newButton{name='back', x=1140,y=640,w=170,h=80,sound='back',font=60,fText=CHAR.icon.back,code=backScene}, + WIDGET.newText{name='title', x= 80,y=50,font=70,align='L'}, + WIDGET.newText{name='unranked', x= 970,y=70,color='Y',font=50,align='R'}, + WIDGET.newSelector{name='applyAt',x=1100,y=100,w=230,color='Y',font=20,list={'preInit','postInit','always'},disp=function() return GAME.modApplyAt end,code=function(v) GAME.modApplyAt=v end}, + WIDGET.newButton{name='reset', x=1140,y=540,w=170,h=80,font=25,code=pressKey'tab'}, + WIDGET.newButton{name='back', x=1140,y=640,w=170,h=80,sound='back',font=60,fText=CHAR.icon.back,code=backScene}, } return scene diff --git a/parts/scenes/replays.lua b/parts/scenes/replays.lua index 98067b41a..e119a0ced 100644 --- a/parts/scenes/replays.lua +++ b/parts/scenes/replays.lua @@ -48,6 +48,7 @@ local function _playRep(fileName) for _,m in next,rep.mod do GAME.mod[m[1]+1]=m[2] end + GAME.modApplyAt=rep.modApplyAt or 'preInit' -- For V0.17.15 and older GAME.rep={} DATA.pumpRecording(rep.data,GAME.rep)