Compare commits

..

28 Commits

Author SHA1 Message Date
ParticleG
63da1b5585 - Add logs to upload-artifact 2021-11-08 04:37:22 +08:00
ParticleG
929be4faf0 - Test Windows with curl 2021-11-08 04:11:04 +08:00
MrZ626
59a5b52993 整理代码 2021-11-08 03:08:42 +08:00
ParticleG
c412e07153 - Add Upload Action 2021-11-08 02:48:38 +08:00
MrZ626
252f19c6df 进入newRoom菜单时不会试图修改背景和bgm 2021-11-07 22:09:22 +08:00
MrZ626
8264fdd4bf 修改更新历史和build号 2021-11-07 21:27:39 +08:00
MrZ626
dfd28f2f10 只在更新后触发自动转换以旧版本模式名存储的数据文件 2021-11-07 21:19:49 +08:00
MrZ626
5fc1257e58 颜色表改用hsv生成 2021-11-07 17:44:17 +08:00
MrZ626
5c39765f71 微调词典
微调两个小程序
整理代码
2021-11-07 17:44:13 +08:00
MrZ626
ca92622d5d 微调中文词典两个词条 2021-11-07 12:28:26 +08:00
MrZ626
e28902bc97 无尽马拉松的are每300行减小一次,line are每100行减小一次 2021-11-07 05:42:03 +08:00
MrZ626
d228809a53 无尽马拉松添加1700行的终点 2021-11-07 05:28:08 +08:00
MrZ626
60f8a22dd5 微调排行榜字体大小 2021-11-07 05:20:58 +08:00
MrZ626
db4ae56990 无尽马拉松添加排行榜 2021-11-07 05:19:12 +08:00
MrZ626
e95288b171 修改更新历史
整理代码
2021-11-07 05:00:41 +08:00
MrZ626
2511555eb0 调整无尽马拉松的难度曲线 2021-11-07 04:58:37 +08:00
Not-A-Normal-Robot
4c4f01cb95 Decrease lock delay when level up above lvl20 2021-11-07 04:55:02 +08:00
Not-A-Normal-Robot
7177118f34 Added Infinite Marathon 2021-11-07 04:55:02 +08:00
MrZ626
a6d5c4a1bf 修改更新历史 2021-11-07 04:02:40 +08:00
MrZ626
73a828d73a 修改mph模式的bgm 2021-11-07 04:02:22 +08:00
MrZ626
a7df4d6aa7 新模式:竞速-效率 2021-11-07 04:02:12 +08:00
MrZ626
7fe4802887 修复超级消除结算时分数计算变量写错导致报错 2021-11-07 01:49:19 +08:00
MrZ626
7eac341b9a move音效在方块因重力或旋转触地时也会播放,而不只是移动后
move音效名改为touch
2021-11-07 01:41:29 +08:00
MrZ626
1fa02a18b2 修改更新历史 2021-11-06 20:57:30 +08:00
MrZ626
b15cb64681 修正pr的一个符号错误 2021-11-06 20:53:45 +08:00
C₂₉H₂₅N₃O₅
bf345c8655 Changed the font and CN tips (#433)
* 补全英文词典翻译

* 大改字体

- 西文部分采用IBM Plex
- 全角标点样式采用西文样式
- 添加类Plex的IPA符号

* 微调中文tips

* 更新 Legals

* 修正一个语法错误
2021-11-06 19:10:44 +08:00
C₂₉H₂₅N₃O₅
cbdb15d658 补全英文词典翻译 (#431) 2021-11-06 04:21:44 +08:00
MrZ626
8b4504bfa0 新BGM:1989(用于几个经典模式)
重新安排一些模式的BGM
2021-11-06 04:05:08 +08:00
264 changed files with 2927 additions and 3454 deletions

View File

@@ -2,7 +2,8 @@
name: Bug report (bluescreen crash) Bug报告 (蓝屏报错)
about: Create a report of problems which made the crash with a bluescreen
---
Screenshot with crash information (*Image(s) Here*):
Screenshot with crash information: (delete this line before submitting)
*Image Here*
If you can reproduce it, write the steps here. If you can't, try to describe the exactly time the game crash, like pressing which key or which button (*Details Here*)
If you can reproduce it, write the steps here. If you can't, try to describe the exactly time the game crash, like pressing which key or which button: (delete this line before submitting)
*Details Here*

View File

@@ -2,7 +2,8 @@
name: Bug report (unintended behaviors) Bug报告 (奇怪的现象)
about: Create a report of unintended behaviors
---
Screenshot with unintended behaviors (*Image(s) Here*):
Screenshot with unintended behaviors: (delete this line before submitting)
*Image(s) Here*
If you can reproduce it, write the steps here. If you can't, try to describe the exactly time the game crash, like pressing which key or which button (*Details Here*):
If you can reproduce it, write the steps here. If you can't, try to describe the exactly time the game crash, like pressing which key or which button: (delete this line before submitting)
*Details Here*

View File

@@ -0,0 +1,51 @@
name: "upload artifact"
description: "upload file with webdav"
inputs:
WEBDAV_USERNAME:
required: true
description: "webdav username"
WEBDAV_PASSWORD:
required: true
description: "webdav password"
ARTIFACT_TYPE:
required: true
description: "file build type"
ARTIFACT_PLATFORM:
required: true
description: "file platform"
ARTIFACT_NAME:
required: true
description: "file name"
runs:
using: "composite"
steps:
- name: Install Webdav 4
shell: bash
run: |
pip install webdav4
- name: Update release
shell: python
run: |
import re
from webdav4.client import Client
client = Client(
"http://mc.yuhao7370.top:5212/dav",
auth=("${{ inputs.WEBDAV_USERNAME }}", "${{ inputs.WEBDAV_PASSWORD }}"),
timeout=None,
)
if '${{ inputs.ARTIFACT_TYPE }}' == 'release'
print('Removing previous ${{ inputs.ARTIFACT_PLATFORM }} release file...')
for file in client.ls('Techmino distribution'):
if re.search(r'(Techmino_a[0-9]+\.[0-9]+\.[0-9]_${{ inputs.ARTIFACT_PLATFORM }}.*)', file['name']):
client.remove(file['name'])
print('Uploading new ${{ inputs.ARTIFACT_PLATFORM }} release file...')
client.upload_file("${{ inputs.ARTIFACT_NAME }}", 'Techmino distribution/${{ inputs.ARTIFACT_NAME }}')
if '${{ inputs.ARTIFACT_TYPE }}' == 'test':
print('Removing previous ${{ inputs.ARTIFACT_PLATFORM }} test file...')
for file in client.ls('Techmino Snapshot'):
if re.search(r'(Techmino_pre[0-9]+\.[0-9]+\.[0-9]_[0-9a-z]{7}_${{ inputs.ARTIFACT_PLATFORM }}.*)', file['name']):
client.remove(file['name'])
print('Uploading new ${{ inputs.ARTIFACT_PLATFORM }} test file...')
client.upload_file("${{ inputs.ARTIFACT_NAME }}", 'Techmino Snapshot/${{ inputs.ARTIFACT_NAME }}')

Binary file not shown.

View File

@@ -56,6 +56,14 @@ jobs:
with:
name: ${{ needs.get-info.outputs.updateTitle }}
files: Techmino_a${{ needs.get-info.outputs.release }}_Win64.zip
- name: Upload artifact to server
uses: ./.github/actions/upload-artifact
with:
WEBDAV_USERNAME: ${{ secrets.WEBDAV_USERNAME }}
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
ARTIFACT_TYPE: release
ARTIFACT_PLATFORM: Win64
ARTIFACT_NAME: Techmino_a${{ needs.get-info.outputs.release }}_Win64.zip
build-windows-x86:
runs-on: windows-latest
@@ -77,6 +85,14 @@ jobs:
with:
name: ${{ needs.get-info.outputs.updateTitle }}
files: Techmino_a${{ needs.get-info.outputs.release }}_Win32.zip
- name: Upload artifact to server
uses: ./.github/actions/upload-artifact
with:
WEBDAV_USERNAME: ${{ secrets.WEBDAV_USERNAME }}
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
ARTIFACT_TYPE: release
ARTIFACT_PLATFORM: Win32
ARTIFACT_NAME: Techmino_a${{ needs.get-info.outputs.release }}_Win32.zip
build-linux:
runs-on: ubuntu-20.04
@@ -93,6 +109,14 @@ jobs:
with:
name: ${{ needs.get-info.outputs.updateTitle }}
files: Techmino_a${{ needs.get-info.outputs.release }}_Linux.AppImage
- name: Upload artifact to server
uses: ./.github/actions/upload-artifact
with:
WEBDAV_USERNAME: ${{ secrets.WEBDAV_USERNAME }}
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
ARTIFACT_TYPE: release
ARTIFACT_PLATFORM: Linux
ARTIFACT_NAME: Techmino_a${{ needs.get-info.outputs.release }}_Linux.AppImage
build-android:
runs-on: ubuntu-20.04
@@ -115,6 +139,14 @@ jobs:
with:
name: ${{ needs.get-info.outputs.updateTitle }}
files: Techmino_a${{ needs.get-info.outputs.release }}_Android.apk
- name: Upload artifact to server
uses: ./.github/actions/upload-artifact
with:
WEBDAV_USERNAME: ${{ secrets.WEBDAV_USERNAME }}
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
ARTIFACT_TYPE: release
ARTIFACT_PLATFORM: Android
ARTIFACT_NAME: Techmino_a${{ needs.get-info.outputs.release }}_Android.apk
build-macOS:
runs-on: macos-10.15
@@ -142,6 +174,14 @@ jobs:
with:
name: ${{ needs.get-info.outputs.updateTitle }}
files: Techmino_a${{ needs.get-info.outputs.release }}_MacOS.dmg
- name: Upload artifact to server
uses: ./.github/actions/upload-artifact
with:
WEBDAV_USERNAME: ${{ secrets.WEBDAV_USERNAME }}
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
ARTIFACT_TYPE: release
ARTIFACT_PLATFORM: MacOS
ARTIFACT_NAME: Techmino_a${{ needs.get-info.outputs.release }}_MacOS.dmg
build-iOS:
runs-on: macos-latest
@@ -176,6 +216,14 @@ jobs:
with:
name: ${{ needs.get-info.outputs.updateTitle }}
files: Techmino_a${{ needs.get-info.outputs.release }}_iOS.ipa
- name: Upload artifact to server
uses: ./.github/actions/upload-artifact
with:
WEBDAV_USERNAME: ${{ secrets.WEBDAV_USERNAME }}
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
ARTIFACT_TYPE: release
ARTIFACT_PLATFORM: iOS
ARTIFACT_NAME: Techmino_a${{ needs.get-info.outputs.release }}_iOS.ipa
Add-Release-note:
runs-on: ubuntu-20.04

View File

@@ -49,7 +49,20 @@ jobs:
with:
name: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Windows
path: love
- name: Pack Techmino
run: 7z a -tzip .\Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Windows.zip .\love
- name: Upload artifact to server
uses: ./.github/actions/upload-artifact
with:
WEBDAV_USERNAME: ${{ secrets.WEBDAV_USERNAME }}
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
ARTIFACT_TYPE: test
ARTIFACT_PLATFORM: Windows
ARTIFACT_NAME: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Windows.zip
# - name: Upload artifact to server
# run: |
# curl --user "${{ secrets.WEBDAV_USERNAME }}:${{ secrets.WEBDAV_PASSWORD }}" -T Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Windows.zip http://mc.yuhao7370.top:5212/dav/Techmino%20Snapshots/ -v
build-linux:
runs-on: ubuntu-20.04
needs: get-info
@@ -67,6 +80,16 @@ jobs:
with:
name: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Linux
path: Techmino.AppImage
- name: Pack Techmino
run: mv Techmino.AppImage Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Linux.AppImage
- name: Upload artifact to server
uses: ./.github/actions/upload-artifact
with:
WEBDAV_USERNAME: ${{ secrets.WEBDAV_USERNAME }}
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
ARTIFACT_TYPE: test
ARTIFACT_PLATFORM: Linux
ARTIFACT_NAME: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Linux.AppImage
build-android:
runs-on: ubuntu-20.04
@@ -92,6 +115,16 @@ jobs:
with:
name: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Android
path: Techmino_Snapshot.apk
- name: Pack Techmino
run: mv Techmino_Snapshot.apk Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Android.apk
- name: Upload artifact to server
uses: ./.github/actions/upload-artifact
with:
WEBDAV_USERNAME: ${{ secrets.WEBDAV_USERNAME }}
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
ARTIFACT_TYPE: test
ARTIFACT_PLATFORM: Android
ARTIFACT_NAME: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Android.apk
build-macOS:
runs-on: macos-10.15
@@ -119,6 +152,16 @@ jobs:
with:
name: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_MacOS
path: Techmino.dmg
- name: Pack Techmino
run: mv Techmino.dmg Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_MacOS.dmg
- name: Upload artifact to server
uses: ./.github/actions/upload-artifact
with:
WEBDAV_USERNAME: ${{ secrets.WEBDAV_USERNAME }}
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
ARTIFACT_TYPE: test
ARTIFACT_PLATFORM: MacOS
ARTIFACT_NAME: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_MacOS.dmg
build-iOS:
runs-on: macos-latest
@@ -152,3 +195,13 @@ jobs:
with:
name: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_iOS
path: Techmino.ipa
- name: Pack Techmino
run: mv Techmino.ipa Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_iOS.ipa
- name: Upload artifact to server
uses: ./.github/actions/upload-artifacts
with:
WEBDAV_USERNAME: ${{ secrets.WEBDAV_USERNAME }}
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
ARTIFACT_TYPE: test
ARTIFACT_PLATFORM: iOS
ARTIFACT_NAME: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_iOS.ipa

View File

@@ -1,6 +1,4 @@
local lastLoaded={}
local maxLoadedCount=3
local SourceObjList={}
local Sources={}
local volume=1
local BGM={
@@ -39,41 +37,15 @@ end
local function check_curFadeOut(task,code,src)
return task.code==code and task.args[1]==src
end
local function _tryReleaseSources()
local n=#lastLoaded
while #lastLoaded>maxLoadedCount do
local name=lastLoaded[n]
if SourceObjList[name].source:isPlaying()then
n=n-1
if n<=0 then return end
else
SourceObjList[name].source=SourceObjList[name].source:release()and nil
table.remove(lastLoaded,n)
return
end
end
end
function BGM.setDefault(bgm)
BGM.default=bgm
end
function BGM.setMaxSources(count)
maxLoadedCount=count
_tryReleaseSources()
end
function BGM.setChange(func)
BGM.onChange=func
end
function BGM.setVol(v)
assert(type(v)=='number'and v>=0 and v<=1,'Wrong volume')
assert(type(v)=='number'and v>=0 and v<=1)
volume=v
if BGM.playing then
if volume>0 then
BGM.playing:setVolume(volume)
BGM.playing:play()
elseif BGM.nowPlay then
BGM.playing:pause()
end
end
end
function BGM.init(list)
BGM.init=nil
@@ -81,7 +53,7 @@ function BGM.init(list)
local simpList={}
for _,v in next,list do
table.insert(simpList,v.name)
SourceObjList[v.name]={path=v.path,source=false}
Sources[v.name]=v.path
end
table.sort(simpList)
function BGM.getList()return simpList end
@@ -89,43 +61,58 @@ function BGM.init(list)
LOG(count.." BGM files added")
function BGM.getCount()return count end
local function _tryLoad(name)
if SourceObjList[name]then
if SourceObjList[name].source then
return true
elseif love.filesystem.getInfo(SourceObjList[name].path)then
SourceObjList[name].source=love.audio.newSource(SourceObjList[name].path,'stream')
SourceObjList[name].source:setLooping(true)
SourceObjList[name].source:setVolume(0)
table.insert(lastLoaded,1,name)
_tryReleaseSources()
local function _load(name)
if type(Sources[name])=='string'then
if love.filesystem.getInfo(Sources[name])then
Sources[name]=love.audio.newSource(Sources[name],'stream')
Sources[name]:setLooping(true)
Sources[name]:setVolume(0)
return true
else
LOG("No BGM: "..SourceObjList[name],5)
LOG("No BGM: "..Sources[name],5)
end
elseif Sources[name]then
return true
elseif name then
LOG("No BGM: "..name,5)
end
end
function BGM.setVol(v)
assert(type(v)=='number'and v>=0 and v<=1)
volume=v
if BGM.playing then
if volume>0 then
BGM.playing:setVolume(volume)
BGM.playing:play()
elseif BGM.nowPlay then
BGM.playing:pause()
end
end
end
function BGM.loadAll()--Not neccessary, unless you want avoid the lag when playing something for the first time
for name in next,Sources do
_load(name)
end
end
function BGM.play(name)
name=name or BGM.default
if not _tryLoad(name)then return end
if not _load(name)then return end
if volume==0 then
BGM.nowPlay=name
BGM.playing=SourceObjList[name].source
BGM.playing=Sources[name]
return true
end
if name and SourceObjList[name].source then
if name and Sources[name]then
if BGM.nowPlay~=name then
if BGM.nowPlay then
TASK.new(task_fadeOut,BGM.playing)
end
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,SourceObjList[name].source)
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,Sources[name])
TASK.removeTask_code(task_fadeIn)
TASK.new(task_fadeIn,SourceObjList[name].source)
TASK.new(task_fadeIn,Sources[name])
BGM.nowPlay=name
BGM.playing=SourceObjList[name].source
BGM.playing=Sources[name]
BGM.lastPlayed=BGM.nowPlay
BGM.playing:seek(0)
BGM.playing:play()
@@ -141,8 +128,8 @@ function BGM.init(list)
end
function BGM.continue()
if BGM.lastPlayed then
BGM.nowPlay,BGM.playing=BGM.lastPlayed,SourceObjList[BGM.lastPlayed].source
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,SourceObjList[BGM.nowPlay].source)
BGM.nowPlay,BGM.playing=BGM.lastPlayed,Sources[BGM.lastPlayed]
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,Sources[BGM.nowPlay])
TASK.removeTask_code(task_fadeIn)
TASK.new(task_fadeIn,BGM.playing)
BGM.playing:play()

View File

@@ -1,6 +1,6 @@
local abs=math.abs
local function hsv(h,s,v,a)
if s<=0 then return v,v,v,a end
if s<=0 then return v,v,v end
h=h*6
local c=v*s
local x=abs((h-1)%2-1)*c
@@ -16,62 +16,62 @@ end
local COLOR={
hsv=hsv,
red= {hsv(0.00, 0.89, 0.91)},
fire= {hsv(0.04, 0.93, 0.94)},
orange= {hsv(0.09, 0.99, 0.96)},
yellow= {hsv(0.16, 0.82, 0.90)},
lime= {hsv(0.18, 0.89, 0.88)},
jade= {hsv(0.23, 1.00, 0.82)},
green= {hsv(0.33, 1.00, 0.81)},
aqua= {hsv(0.48, 1.00, 0.74)},
cyan= {hsv(0.53, 1.00, 0.88)},
navy= {hsv(0.56, 1.00, 1.00)},
sea= {hsv(0.61, 1.00, 1.00)},
blue= {hsv(0.64, 1.00, 0.95)},
violet= {hsv(0.73, 1.00, 0.91)},
purple= {hsv(0.80, 1.00, 0.81)},
magenta= {hsv(0.86, 1.00, 0.78)},
wine= {hsv(0.94, 0.96, 0.91)},
red= {hsv(0, .85,.85)},
fire= {hsv(0.0625,.85,.85)},
orange= {hsv(0.125, .85,.85)},
yellow= {hsv(0.1875,.85,.85)},
lime= {hsv(0.25, .85,.85)},
jade= {hsv(0.3125,.85,.85)},
green= {hsv(0.375, .85,.85)},
aqua= {hsv(0.4375,.85,.85)},
cyan= {hsv(0.5, .85,.85)},
navy= {hsv(0.5625,.85,.85)},
sea= {hsv(0.625, .85,.85)},
blue= {hsv(0.6875,.85,.85)},
violet= {hsv(0.75, .85,.85)},
purple= {hsv(0.8125,.85,.85)},
magenta= {hsv(0.875, .85,.85)},
wine= {hsv(0.9375,.85,.85)},
lRed= {hsv(0.00, 0.38, 0.93)},
lFire= {hsv(0.04, 0.45, 0.91)},
lOrange= {hsv(0.10, 0.53, 0.92)},
lYellow= {hsv(0.15, 0.61, 0.95)},
lLime= {hsv(0.19, 0.66, 0.92)},
lJade= {hsv(0.24, 0.56, 0.90)},
lGreen= {hsv(0.34, 0.49, 0.89)},
lAqua= {hsv(0.49, 0.59, 0.85)},
lCyan= {hsv(0.51, 0.77, 0.88)},
lNavy= {hsv(0.54, 0.80, 0.95)},
lSea= {hsv(0.56, 0.72, 0.97)},
lBlue= {hsv(0.64, 0.44, 0.96)},
lViolet= {hsv(0.73, 0.47, 0.95)},
lPurple= {hsv(0.80, 0.62, 0.89)},
lMagenta= {hsv(0.86, 0.61, 0.89)},
lWine= {hsv(0.93, 0.57, 0.92)},
lRed= {hsv(0, .5,.95)},
lFire= {hsv(0.0625,.5,.95)},
lOrange= {hsv(0.125, .5,.95)},
lYellow= {hsv(0.1875,.5,.95)},
lLime= {hsv(0.25, .5,.95)},
lJade= {hsv(0.3125,.5,.95)},
lGreen= {hsv(0.375, .5,.95)},
lAqua= {hsv(0.4375,.5,.95)},
lCyan= {hsv(0.5, .5,.95)},
lNavy= {hsv(0.5625,.5,.95)},
lSea= {hsv(0.625, .5,.95)},
lBlue= {hsv(0.6875,.5,.95)},
lViolet= {hsv(0.75, .5,.95)},
lPurple= {hsv(0.8125,.5,.95)},
lMagenta={hsv(0.875, .5,.95)},
lWine= {hsv(0.9375,.5,.95)},
dRed= {hsv(0.00, 0.80, 0.48)},
dFire= {hsv(0.04, 0.80, 0.34)},
dOrange= {hsv(0.07, 0.80, 0.39)},
dYellow= {hsv(0.11, 0.80, 0.37)},
dLime= {hsv(0.17, 0.80, 0.26)},
dJade= {hsv(0.31, 0.80, 0.27)},
dGreen= {hsv(0.33, 0.80, 0.26)},
dAqua= {hsv(0.47, 0.80, 0.23)},
dCyan= {hsv(0.50, 0.80, 0.30)},
dNavy= {hsv(0.59, 0.80, 0.42)},
dSea= {hsv(0.64, 0.80, 0.40)},
dBlue= {hsv(0.67, 0.80, 0.34)},
dViolet= {hsv(0.71, 0.80, 0.35)},
dPurple= {hsv(0.76, 0.80, 0.32)},
dMagenta= {hsv(0.87, 0.80, 0.28)},
dWine= {hsv(0.92, 0.80, 0.28)},
dRed= {hsv(0, .9,.5)},
dFire= {hsv(0.0625,.9,.5)},
dOrange= {hsv(0.125, .9,.5)},
dYellow= {hsv(0.1875,.9,.5)},
dLime= {hsv(0.25, .9,.5)},
dJade= {hsv(0.3125,.9,.5)},
dGreen= {hsv(0.375, .9,.5)},
dAqua= {hsv(0.4375,.9,.5)},
dCyan= {hsv(0.5, .9,.5)},
dNavy= {hsv(0.5625,.9,.5)},
dSea= {hsv(0.625, .9,.5)},
dBlue= {hsv(0.6875,.9,.5)},
dViolet= {hsv(0.75, .9,.5)},
dPurple= {hsv(0.8125,.9,.5)},
dMagenta={hsv(0.875, .9,.5)},
dWine= {hsv(0.9375,.9,.5)},
black= {hsv(0.04, 0.04, 0.14)},
dGray= {hsv(0.02, 0.05, 0.44)},
gray= {hsv(0.02, 0.05, 0.65)},
lGray= {hsv(0.02, 0.06, 0.86)},
white= {hsv(0.01, 0.02, 0.99)},
black= {hsv(0,0,.05)},
dGray= {hsv(0,0,0.3)},
gray= {hsv(0,0,0.6)},
lGray= {hsv(0,0,0.8)},
white= {hsv(0,0,.97)},
}
for k,v in next,{
R='red', F='fire', O='orange', Y='yellow', L='lime', J='jade', G='green', A='aqua', C='cyan', N='navy', S='sea', B='blue', V='violet', P='purple', M='magenta', W='wine',

View File

@@ -1,60 +1,66 @@
local fs=love.filesystem
local FILE={}
function FILE.load(name,args)
if not args then args=''end
function FILE.load(name,mode)
if fs.getInfo(name)then
local F=fs.newFile(name)
assert(F:open'r','open error')
local s=F:read()F:close()
if args:sArg'-luaon'or args==''and s:sub(1,6)=='return{'then
local func=loadstring(s)
if func then
setfenv(func,{})
local res=func()
return assert(res,'decode error')
if F:open'r'then
local s=F:read()
F:close()
if mode=='luaon'or not mode and s:sub(1,6)=="return{"then
s=loadstring(s)
if s then
setfenv(s,{})
return s()
end
elseif mode=='json'or not mode and s:sub(1,1)=="["and s:sub(-1)=="]"or s:sub(1,1)=="{"and s:sub(-1)=="}"then
local res=JSON.decode(s)
if res then
return res
end
elseif mode=='string'or not mode then
return s
else
error('decode error')
MES.new("No file loading mode called "..tostring(mode))
end
elseif args:sArg'-json'or args==''and s:sub(1,1)=='['and s:sub(-1)==']'or s:sub(1,1)=='{'and s:sub(-1)=='}'then
local res=JSON.decode(s)
if res then
return res
end
error('decode error')
elseif args:sArg'-string'or args==''then
return s
else
error('unknown mode')
MES.new('error',name.." "..text.loadError)
end
else
error('no file')
end
end
function FILE.save(data,name,args)
if not args then args=''end
if args:sArg'-d'and fs.getInfo(name)then
error('duplicate')
end
function FILE.save(data,name,mode)
if not mode then mode=""end
if type(data)=='table'then
if args:sArg'-luaon'then
if mode:find'l'then
data=TABLE.dump(data)
if not data then
error('encode error')
MES.new('error',name.." "..text.saveError.."dump error")
return
end
else
data=JSON.encode(data)
if not data then
error('encode error')
MES.new('error',name.." "..text.saveError.."json error")
return
end
end
else
data=tostring(data)
end
if mode:find'd'and fs.getInfo(name)then
MES.new('error',text.saveError_duplicate)
return
end
local F=fs.newFile(name)
assert(F:open('w'),'open error')
F:write(data)F:flush()F:close()
F:open'w'
local success,mes=F:write(data)
F:flush()F:close()
if success then
return true
else
MES.new('error',text.saveError..(mes or"unknown error"))
MES.traceback()
end
end
function FILE.clear(path)
if fs.getRealDirectory(path)==SAVEDIR and fs.getInfo(path).type=='directory'then

View File

@@ -1,60 +1,67 @@
local gc=love.graphics
local set=gc.setFont
local fontFiles,fontCache={},{}
local defaultFont,defaultFallBack
local curFont=false--Current using font object
local fontCache={}
local currentFontSize
local FONT={}
function FONT.setDefault(name)defaultFont=name end
function FONT.setFallback(name)defaultFallBack=name end
function FONT.rawget(s)
function FONT.set(s)
if s~=currentFontSize then
if not fontCache[s]then
fontCache[s]=gc.setNewFont(s,'light',gc.getDPIScale()*SCR.k*2)
end
set(fontCache[s])
currentFontSize=s
end
end
function FONT.get(s)
if not fontCache[s]then
fontCache[s]=gc.setNewFont(s,'light',gc.getDPIScale()*SCR.k*2)
end
return fontCache[s]
end
function FONT.rawset(s)
set(fontCache[s]or FONT.rawget(s))
function FONT.reset()
for s in next,fontCache do
fontCache[s]=gc.setNewFont(s,'light',gc.getDPIScale()*SCR.k*2)
end
end
function FONT.load(fonts)
for name,path in next,fonts do
assert(love.filesystem.getInfo(path),("Font file $1($2) not exist!"):repD(name,path))
fontFiles[name]=love.filesystem.newFile(path)
fontCache[name]={}
function FONT.load(mainFont,secFont)
assert(love.filesystem.getInfo(mainFont),"Font file '"..mainFont.."' not exist!")
mainFont=love.filesystem.newFile(mainFont)
if secFont and love.filesystem.getInfo(secFont)then
secFont=love.filesystem.newFile(secFont)
else
secFont=false
end
function FONT.set(s)
if s~=currentFontSize then
if not fontCache[s]then
fontCache[s]=gc.setNewFont(mainFont,s,'light',gc.getDPIScale()*SCR.k*2)
if secFont then
fontCache[s]:setFallbacks(gc.setNewFont(secFont,s,'light',gc.getDPIScale()*SCR.k*2))
end
end
set(fontCache[s])
currentFontSize=s
end
end
function FONT.get(s)
if not fontCache[s]then
fontCache[s]=gc.setNewFont(mainFont,s,'light',gc.getDPIScale()*SCR.k*2)
if secFont then
fontCache[s]:setFallbacks(gc.setNewFont(secFont,s,'light',gc.getDPIScale()*SCR.k*2))
end
end
return fontCache[s]
end
function FONT.reset()
for s in next,fontCache do
fontCache[s]=gc.setNewFont(mainFont,s,'light',gc.getDPIScale()*SCR.k*2)
if secFont then
fontCache[s]:setFallbacks(gc.setNewFont(secFont,s,'light',gc.getDPIScale()*SCR.k*2))
end
end
end
FONT.reset()
end
function FONT.get(size,name)
if not name then name=defaultFont end
local f=fontCache[name][size]
if not f then
f=gc.setNewFont(fontFiles[name],size,'light',gc.getDPIScale()*SCR.k*2)
if defaultFallBack and name~=defaultFallBack then
f:setFallbacks(FONT.get(size,defaultFallBack))
end
fontCache[name][size]=f
end
return f
end
function FONT.set(size,name)
if not name then name=defaultFont end
local f=fontCache[name][size]
if f~=curFont then
curFont=f or FONT.get(size,name)
set(curFont)
end
end
function FONT.reset()
for name,cache in next,fontCache do
if type(cache)=='table'then
for size in next,cache do
cache[size]=FONT.get(size,name)
end
else
fontCache[name]=FONT.rawget(name)
end
end
end
return FONT

View File

@@ -95,7 +95,6 @@ do--function GC.DO(L)
setLJ="setLineJoin",
print="print",
rawFT=function(...)FONT.rawset(...)end,
setFT=function(...)FONT.set(...)end,
mText=GC.mStr,
mDraw=GC.draw,

View File

@@ -1,9 +1,9 @@
NONE={}function NULL()end
EDITING=""
LOADED=false
ERRDATA={}
--Pure lua modules (basic)
MATH= require'Zframework.mathExtend'
COLOR= require'Zframework.color'
TABLE= require'Zframework.tableExtend'
STRING= require'Zframework.stringExtend'
@@ -73,8 +73,7 @@ local xOy=SCR.xOy
local ITP=xOy.inverseTransformPoint
local mx,my,mouseShow=-20,-20,false
local jsState={}--map, joystickID->axisStates: {axisName->axisVal}
local errData={}--list, each error create {mes={errMes strings},scene=sceneNameStr}
joysticks={}
local devMode
@@ -288,18 +287,18 @@ function love.textinput(texts)
WIDGET.textinput(texts)
end
--analog sticks: -1, 0, 1 for neg, neutral, pos
--triggers: 0 for released, 1 for pressed
local jsAxisEventName={
leftx={'leftstick_left','leftstick_right'},
lefty={'leftstick_up','leftstick_down'},
rightx={'rightstick_left','rightstick_right'},
righty={'rightstick_up','rightstick_down'},
triggerleft='triggerleft',
triggerright='triggerright'
}
local gamePadKeys={'a','b','x','y','back','guide','start','leftstick','rightstick','leftshoulder','rightshoulder','dpup','dpdown','dpleft','dpright'}
local dPadToKey={
function love.joystickadded(JS)
table.insert(joysticks,JS)
MES.new('info',"Joystick added")
end
function love.joystickremoved(JS)
local i=TABLE.find(joysticks,JS)
if i then
table.remove(joysticks,i)
MES.new('info',"Joystick removed")
end
end
local keyMirror={
dpup='up',
dpdown='down',
dpleft='left',
@@ -307,92 +306,54 @@ local dPadToKey={
start='return',
back='escape',
}
function love.joystickadded(JS)
jsState[JS:getID()]={
_loveJSObj=JS,
leftx=0,lefty=0,
rightx=0,righty=0,
triggerleft=0,triggerright=0
}
MES.new('info',"Joystick added")
end
function love.joystickremoved(JS)
local js=jsState[JS:getID()]
if js then
for i=1,#gamePadKeys do
if JS:isGamepadDown(gamePadKeys[i])then
love.gamepadreleased(JS,gamePadKeys[i])
end
end
love.gamepadaxis(JS,'leftx',0)
love.gamepadaxis(JS,'lefty',0)
love.gamepadaxis(JS,'rightx',0)
love.gamepadaxis(JS,'righty',0)
love.gamepadaxis(JS,'triggerleft',-1)
love.gamepadaxis(JS,'triggerright',-1)
jsState[JS:getID()]=nil
MES.new('info',"Joystick removed")
end
end
function love.gamepadaxis(JS,axis,val)
local js=jsState[JS:getID()]
if js then
if axis=='leftx'or axis=='lefty'or axis=='rightx'or axis=='righty'then
local newVal=--range: [0,1]
val>.4 and 1 or
val<-.4 and -1 or
0
if newVal~=js[axis]then
if js[axis]==-1 then
love.gamepadreleased(JS,jsAxisEventName[axis][1])
elseif js[axis]~=0 then
love.gamepadreleased(JS,jsAxisEventName[axis][2])
end
if newVal==-1 then
love.gamepadpressed(JS,jsAxisEventName[axis][1])
elseif newVal==1 then
love.gamepadpressed(JS,jsAxisEventName[axis][2])
end
js[axis]=newVal
end
elseif axis=='triggerleft'or axis=='triggerright'then
local newVal=val>-.3 and 1 or 0--range: [-1,1]
if newVal~=js[axis]then
if newVal==1 then
love.gamepadpressed(JS,jsAxisEventName[axis])
else
love.gamepadreleased(JS,jsAxisEventName[axis])
end
js[axis]=newVal
end
end
end
end
function love.gamepadpressed(_,i)
mouseShow=false
if SCN.swapping then return end
if SCN.gamepadDown then SCN.gamepadDown(i)
elseif SCN.keyDown then SCN.keyDown(dPadToKey[i]or i)
elseif SCN.keyDown then SCN.keyDown(keyMirror[i]or i)
elseif i=="back"then SCN.back()
else WIDGET.gamepadPressed(dPadToKey[i]or i)
else WIDGET.gamepadPressed(keyMirror[i]or i)
end
end
function love.gamepadreleased(_,i)
if SCN.swapping then return end
if SCN.gamepadUp then SCN.gamepadUp(i)end
end
--[[
function love.joystickpressed(JS,k)
mouseShow=false
if SCN.swapping then return end
if SCN.gamepadDown then SCN.gamepadDown(i)
elseif SCN.keyDown then SCN.keyDown(keyMirror[i]or i)
elseif i=="back"then SCN.back()
else WIDGET.gamepadPressed(i)
end
end
function love.joystickreleased(JS,k)
if SCN.swapping then return end
if SCN.gamepadUp then SCN.gamepadUp(i)
end
end
function love.joystickaxis(JS,axis,val)
end
function love.joystickhat(JS,hat,dir)
end
function love.sendData(data)end
function love.receiveData(id,data)end
]]
function love.filedropped(file)
if SCN.fileDropped then SCN.fileDropped(file)end
end
function love.directorydropped(dir)
if SCN.directoryDropped then SCN.directoryDropped(dir)end
end
local autoGCcount=0
local lastGCtime=0
function love.lowmemory()
collectgarbage()
if autoGCcount<3 then
autoGCcount=autoGCcount+1
if love.timer.getTime()-lastGCtime>6.26 then
collectgarbage()
lastGCtime=love.timer.getTime()
MES.new('check',"[auto GC] low MEM 设备内存过低")
end
end
@@ -417,7 +378,7 @@ end
function love.errorhandler(msg)
if type(msg)~='string'then
msg="Unknown error"
elseif msg:find("Invalid UTF-8")and text then
elseif msg:find("Invaild UTF-8")and text then
msg=text.tryAnotherBuild
end
@@ -441,20 +402,20 @@ function love.errorhandler(msg)
love.audio.stop()
gc.reset()
if LOADED and #errData<3 then
if LOADED and #ERRDATA<3 then
BG.set('none')
local scn=SCN and SCN.cur or"NULL"
table.insert(errData,{mes=err,scene=scn})
table.insert(ERRDATA,{mes=err,scene=scn})
--Write messages to log file
love.filesystem.append('conf/error.log',
os.date("%Y/%m/%d %A %H:%M:%S\n")..
#errData.." crash(es) "..love.system.getOS().."-"..VERSION.string.." scene: "..scn.."\n"..
#ERRDATA.." crash(es) "..love.system.getOS().."-"..VERSION.string.." scene: "..scn.."\n"..
table.concat(err,"\n",1,c-2).."\n\n"
)
--Get screencapture
gc.captureScreenshot(function(_)errData[#errData].shot=gc.newImage(_)end)
gc.captureScreenshot(function(_)ERRDATA[#ERRDATA].shot=gc.newImage(_)end)
gc.present()
--Create a new mainLoop thread to keep game alive
@@ -527,17 +488,17 @@ local wsBottomImage do
wsBottomImage=GC.DO(L)
end
local ws_deadImg=GC.DO{20,20,
{'rawFT',20},
{'setFT',20},
{'setCL',1,.3,.3},
{'mText',"X",11,-1},
}
local ws_connectingImg=GC.DO{20,20,
{'rawFT',20},
{'setFT',20},
{'setLW',3},
{'mText',"C",11,-1},
}
local ws_runningImg=GC.DO{20,20,
{'rawFT',20},
{'setFT',20},
{'setCL',.5,1,0},
{'mText',"R",11,-1},
}
@@ -577,7 +538,7 @@ function love.run()
--Scene Launch
while #SCN.stack>0 do SCN.pop()end
SCN.push('quit','slowFade')
SCN.init(#errData==0 and'load'or'error')
SCN.init(#ERRDATA==0 and'load'or'error')
return function()
local _
@@ -661,8 +622,9 @@ function love.run()
--Left-down infos
gc_setColor(devColor[devMode])
gc_print("MEM "..gcinfo(),safeX+5,-40)
gc_print("Tasks "..TASK.getCount(),safeX+5,-60)
gc_print("Voices "..VOC.getQueueCount(),safeX+5,-80)
gc_print("Lines "..FREEROW.getCount(),safeX+5,-60)
gc_print("Tasks "..TASK.getCount(),safeX+5,-80)
gc_print("Voices "..VOC.getQueueCount(),safeX+5,-100)
--Update & draw frame time
table.insert(frameTimeList,1,dt)table.remove(frameTimeList,126)
@@ -741,9 +703,6 @@ end
local Z={}
Z.js=jsState
Z.errData=errData
function Z.setIfPowerInfo(func)showPowerInfo=func end
--[Warning] Color and line width is uncertain value, set it in the function.

View File

@@ -1,23 +0,0 @@
local MATH={}for k,v in next,math do MATH[k]=v end
local rnd=math.random
MATH.tau=2*math.pi
function MATH.sign(a)
return a>0 and 1 or a<0 and -1 or 0
end
function MATH.roll(chance)
return rnd()<(chance or .5)
end
function MATH.coin(a,b)
if rnd()<.5 then
return a
else
return b
end
end
return MATH

View File

@@ -1,5 +1,4 @@
local type,rem=type,table.remove
local int,rnd=math.floor,math.random
local sfxList={}
local packSetting={}
@@ -7,7 +6,7 @@ local Sources={}
local volume=1
local stereo=1
local noteVal={
local noteName={
C=1,c=1,
D=3,d=3,
E=5,e=5,
@@ -16,11 +15,10 @@ local noteVal={
A=10,a=10,
B=12,b=12,
}
local noteName={'C','C#','D','D#','E','F','F#','G','G#','A','A#','B'}
local function _getTuneHeight(tune)
local octave=tonumber(tune:sub(-1,-1))
if octave then
local tuneHeight=noteVal[tune:sub(1,1)]
local tuneHeight=noteName[tune:sub(1,1)]
if tuneHeight then
tuneHeight=tuneHeight+(octave-1)*12
local s=tune:sub(2,2)
@@ -57,14 +55,14 @@ function SFX.loadSample(pack)
assert(type(pack)=='table',"Usage: SFX.loadsample([table])")
assert(pack.name,"No field: name")
assert(pack.path,"No field: path")
packSetting[pack.name]={
base=(_getTuneHeight(pack.base)or 37)-1,
}
local num=1
while love.filesystem.getInfo(pack.path..'/'..num..'.ogg')do
Sources[pack.name..num]={love.audio.newSource(pack.path..'/'..num..'.ogg','static')}
num=num+1
end
local base=(_getTuneHeight(pack.base)or 37)-1
local top=base+num-1
packSetting[pack.name]={base=base,top=top}
LOG((num-1).." "..pack.name.." samples loaded")
end
@@ -72,47 +70,31 @@ function SFX.getCount()
return #sfxList
end
function SFX.setVol(v)
assert(type(v)=='number'and v>=0 and v<=1,'Wrong volume')
assert(type(v)=='number'and v>=0 and v<=1)
volume=v
end
function SFX.setStereo(v)
assert(type(v)=='number'and v>=0 and v<=1,'Wrong stereo')
assert(type(v)=='number'and v>=0 and v<=1)
stereo=v
end
function SFX.getNoteName(note)
if note<1 then
return'---'
else
note=note-1
local octave=int(note/12)+1
return noteName[note%12+1]..octave
end
end
function SFX.playSample(pack,...)--vol-1, sampSet1, vol-2, sampSet2
function SFX.playSample(pack,...)--vol-2, sampSet1, vol-3, sampSet2, vol-1
if ... then
local arg={...}
local vol
if type(arg[#arg])=='number'then vol=rem(arg)end
for i=1,#arg do
local a=arg[i]
if type(a)=='number'and a<=1 then
vol=a
if type(arg[i])=='number'then
vol=arg[i]
else
local base=packSetting[pack].base
local top=packSetting[pack].top
local tune=type(a)=='string'and _getTuneHeight(a)or a--Absolute tune in number
local playTune=tune+rnd(-2,2)
if playTune<=base then--Too low notes
playTune=base+1
elseif playTune>top then--Too high notes
playTune=top
end
SFX.play(pack..playTune-base,vol,nil,tune-playTune)
local tune=arg[i]
tune=_getTuneHeight(tune)-packSetting[pack].base
SFX.play(pack..tune,vol)
end
end
end
end
local function _play(name,vol,pos,pitch)
function SFX.play(name,vol,pos)
if volume==0 or vol==0 then return end
local S=Sources[name]--Source list
if not S then return end
@@ -134,13 +116,32 @@ local function _play(name,vol,pos,pitch)
S:setPosition(0,0,0)
end
end
S:setVolume(vol^1.626)
S:setPitch(pitch and 1.0594630943592953^pitch or 1)
S:setVolume(((vol or 1)*volume)^1.626)
S:play()
end
SFX.fplay=_play--Play sounds without apply module's volume setting
function SFX.play(name,vol,pos,pitch)
_play(name,(vol or 1)*volume,pos,pitch)
function SFX.fplay(name,vol,pos)
local S=Sources[name]--Source list
if not S then return end
local n=1
while S[n]:isPlaying()do
n=n+1
if not S[n]then
S[n]=S[1]:clone()
S[n]:seek(0)
break
end
end
S=S[n]--AU_SRC
if S:getChannelCount()==1 then
if pos then
pos=pos*stereo
S:setPosition(pos,1-pos^2,0)
else
S:setPosition(0,0,0)
end
end
S:setVolume(vol^1.626)
S:play()
end
function SFX.reset()
for _,L in next,Sources do

View File

@@ -2,25 +2,9 @@ local data=love.data
local STRING={}
local assert,tostring,tonumber=assert,tostring,tonumber
local int,format=math.floor,string.format
local find,sub,gsub,upper=string.find,string.sub,string.gsub,string.upper
local find,sub,upper=string.find,string.sub,string.upper
local char,byte=string.char,string.byte
--"Replace dollars", replace all $n with ...
function string.repD(str,...)
local l={...}
for i=#l,1,-1 do
str=gsub(str,'$'..i,l[i])
end
return str
end
--"Scan arg", scan if str has the arg (format of str is like "-json -q", arg is like "-q")
function string.sArg(str,switch)
if find(str.." ",switch.." ")then
return true
end
end
do--function STRING.shiftChar(c)
local shiftMap={
['1']='!',['2']='@',['3']='#',['4']='$',['5']='%',
@@ -79,9 +63,9 @@ function STRING.time(t)
if t<60 then
return format("%.3f\"",t)
elseif t<3600 then
return format("%d'%05.2f\"",int(t/60),int(t%60*100)/100)
return format("%d'%05.2f\"",int(t/60),t%60)
else
return format("%d:%.2d'%05.2f\"",int(t/3600),int(t/60%60),int(t%60*100)/100)
return format("%d:%.2d'%05.2f\"",int(t/3600),int(t/60%60),t%60)
end
end

View File

@@ -1,5 +1,4 @@
local find=string.find
local rem=table.remove
local next,type=next,type
local TABLE={}
@@ -47,18 +46,6 @@ function TABLE.cover(new,old)
end
end
--For all things in new, push to old
function TABLE.coverR(new,old)
for k,v in next,new do
if type(v)=='table'and type(old[k])=='table'then
TABLE.coverR(v,old[k])
else
old[k]=v
end
end
end
--For all things in new if same type in old, push to old
function TABLE.update(new,old)
for k,v in next,new do
@@ -84,7 +71,7 @@ function TABLE.complete(new,old)
end
end
--Remove [1~#] of table
--Remove positive integer index of table
function TABLE.cut(G)
for i=1,#G do
G[i]=nil
@@ -98,53 +85,11 @@ function TABLE.clear(G)
end
end
--Remove duplicated value of [1~#]
function TABLE.trimDuplicate(org)
local cache={}
for i=1,#org,-1 do
if cache[org[i]]then
rem(org,i)
else
cache[org[i]]=true
end
end
end
--Discard duplicated value
function TABLE.remDuplicate(org)
local cache={}
for k,v in next,org do
if cache[v]then
org[k]=nil
else
cache[v]=true
end
end
end
--Reverse [1~#]
function TABLE.reverse(org)
local l=#org
for i=1,math.floor(l/2)do
org[i],org[l+1-i]=org[l+1-i],org[i]
end
end
--------------------------
--Find value in [1~#]
function TABLE.find(t,val)
for i=1,#t do if t[i]==val then return i end end
end
--Return next value of [1~#] (by value)
function TABLE.next(t,val)
for i=1,#t do if t[i]==val then return t[i%#t+1]end end
end
--------------------------
--Find value in whole table
function TABLE.search(t,val)
for k,v in next,t do if v==val then return k end end
@@ -159,8 +104,6 @@ function TABLE.reIndex(org)
end
end
--------------------------
--Dump a simple lua table
do--function TABLE.dump(L,t)
local tabs={

View File

@@ -1,7 +1,5 @@
local rnd=math.random
local volume=1
local diversion=0
local VOC={
vol=1,
getCount=function()return 0 end,
getQueueCount=function()return 0 end,
load=function()error("Cannot load before init!")end,
@@ -9,16 +7,13 @@ local VOC={
play=NULL,
update=NULL,
}
function VOC.setDiversion(n)
assert(type(n)=='number'and n>0 and n<12,'Wrong div')
diversion=n
end
function VOC.setVol(v)
assert(type(v)=='number'and v>=0 and v<=1,'Wrong volume')
volume=v
assert(type(v)=='number'and v>=0 and v<=1)
VOC.vol=v
end
function VOC.init(list)
VOC.init=nil
local rnd=math.random
local rem=table.remove
local voiceQueue={free=0}
local bank={}--{vocName1={SRC1s},vocName2={SRC2s},...}
@@ -77,7 +72,7 @@ function VOC.init(list)
end
function VOC.play(s,chn)
if volume>0 then
if VOC.vol>0 then
local _=Source[s]
if not _ then return end
if chn then
@@ -100,15 +95,13 @@ function VOC.init(list)
end
elseif Q.s==1 then--Waiting load source
Q[1]=_getVoice(Q[1])
Q[1]:setVolume(volume)
Q[1]:setPitch(1.0594630943592953^(diversion*(rnd()*2-1)))
Q[1]:setVolume(VOC.vol)
Q[1]:play()
Q.s=Q[2]and 2 or 4
elseif Q.s==2 then--Playing 1,ready 2
if Q[1]:getDuration()-Q[1]:tell()<.08 then
Q[2]=_getVoice(Q[2])
Q[2]:setVolume(volume)
Q[1]:setPitch(1.0594630943592953^(diversion*(rnd()*2-1)))
Q[2]:setVolume(VOC.vol)
Q[2]:play()
Q.s=3
end

View File

@@ -29,7 +29,7 @@ local clearIcon=GC.DO{40,40,
{'fRect',11,14,18,21},
}
local sureIcon=GC.DO{40,40,
{'rawFT',35},
{'setFT',35},
{'mText',"?",20,0},
}
local smallerThen=GC.DO{20,20,
@@ -82,7 +82,7 @@ function text:draw()
end
end
end
function WIDGET.newText(D)--name,x,y[,fText][,color][,font=30][,fType][,align='M'][,hideF][,hide]
function WIDGET.newText(D)--name,x,y[,fText][,color][,font=30][,align='M'][,hideF][,hide]
local _={
name= D.name or"_",
x= D.x,
@@ -91,7 +91,6 @@ function WIDGET.newText(D)--name,x,y[,fText][,color][,font=30][,fType][,align='M
fText=D.fText,
color=D.color and(COLOR[D.color]or D.color)or COLOR.Z,
font= D.font or 30,
fType=D.fType,
align=D.align or'M',
hideF=D.hideF,
}
@@ -140,7 +139,7 @@ function button:reset()
end
function button:setObject(obj)
if type(obj)=='string'or type(obj)=='number'then
self.obj=gc.newText(FONT.get(self.font,self.fType),obj)
self.obj=gc.newText(FONT.get(self.font),obj)
elseif obj then
self.obj=obj
end
@@ -210,7 +209,7 @@ function button:draw()
end
end
function button:getInfo()
return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font,self.fType)
return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font)
end
function button:press(_,_,k)
self.code(k)
@@ -226,7 +225,7 @@ function button:press(_,_,k)
SFX.play('button')
end
end
function WIDGET.newButton(D)--name,x,y,w[,h][,fText][,color][,font=30][,fType][,sound=true][,align='M'][,edge=0],code[,hideF][,hide]
function WIDGET.newButton(D)--name,x,y,w[,h][,fText][,color][,font=30][,sound=true][,align='M'][,edge=0],code[,hideF][,hide]
if not D.h then D.h=D.w end
local _={
name= D.name or"_",
@@ -247,7 +246,6 @@ function WIDGET.newButton(D)--name,x,y,w[,h][,fText][,color][,font=30][,fType][,
fText=D.fText,
color=D.color and(COLOR[D.color]or D.color)or COLOR.Z,
font= D.font or 30,
fType=D.fType,
align=D.align or'M',
edge= D.edge or 0,
sound=D.sound~=false,
@@ -270,7 +268,7 @@ function key:reset()
end
function key:setObject(obj)
if type(obj)=='string'or type(obj)=='number'then
self.obj=gc.newText(FONT.get(self.font,self.fType),obj)
self.obj=gc.newText(FONT.get(self.font),obj)
elseif obj then
self.obj=obj
end
@@ -333,7 +331,7 @@ function key:draw()
end
end
function key:getInfo()
return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font,self.fType)
return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font)
end
function key:press(_,_,k)
self.code(k)
@@ -341,7 +339,7 @@ function key:press(_,_,k)
SFX.play('key')
end
end
function WIDGET.newKey(D)--name,x,y,w[,h][,fText][,fShade][,noFrame][,color][,font=30][,fType][,sound=true][,align='M'][,edge=0],code[,hideF][,hide]
function WIDGET.newKey(D)--name,x,y,w[,h][,fText][,fShade][,noFrame][,color][,font=30][,sound=true][,align='M'][,edge=0],code[,hideF][,hide]
if not D.h then D.h=D.w end
local _={
name= D.name or"_",
@@ -364,7 +362,6 @@ function WIDGET.newKey(D)--name,x,y,w[,h][,fText][,fShade][,noFrame][,color][,fo
noFrame=D.noFrame,
color= D.color and(COLOR[D.color]or D.color)or COLOR.Z,
font= D.font or 30,
fType=D.fType,
sound= D.sound~=false,
align= D.align or'M',
edge= D.edge or 0,
@@ -433,7 +430,7 @@ function switch:draw()
gc_draw(obj,x-12-ATV,y,nil,min(self.lim/obj:getWidth(),1),1,obj:getWidth(),obj:getHeight()*.5)
end
function switch:getInfo()
return("x=%d,y=%d,font=%d"):format(self.x,self.y,self.font,self.fType)
return("x=%d,y=%d,font=%d"):format(self.x,self.y,self.font)
end
function switch:press()
self.code()
@@ -441,7 +438,7 @@ function switch:press()
SFX.play('touch')
end
end
function WIDGET.newSwitch(D)--name,x,y[,lim][,fText][,color][,font=30][,fType][,sound=true][,disp],code[,hideF][,hide]
function WIDGET.newSwitch(D)--name,x,y[,lim][,fText][,color][,font=30][,sound=true][,disp],code[,hideF][,hide]
local _={
name= D.name or"_",
@@ -456,7 +453,6 @@ function WIDGET.newSwitch(D)--name,x,y[,lim][,fText][,color][,font=30][,fType][,
fText=D.fText,
color=D.color and(COLOR[D.color]or D.color)or COLOR.Z,
font= D.font or 30,
fType=D.fType,
sound=D.sound~=false,
disp= D.disp,
code= D.code,
@@ -599,7 +595,7 @@ end
function slider:arrowKey(k)
self:scroll((k=="left"or k=="up")and -1 or 1)
end
function WIDGET.newSlider(D)--name,x,y,w[,lim][,fText][,color][,unit][,smooth][,font=30][,fType][,change],disp[,show],code,hide
function WIDGET.newSlider(D)--name,x,y,w[,lim][,fText][,color][,unit][,smooth][,font=30][,change],disp[,show],code,hide
local _={
name= D.name or"_",
@@ -621,7 +617,6 @@ function WIDGET.newSlider(D)--name,x,y,w[,lim][,fText][,color][,unit][,smooth][,
unit= D.unit or 1,
smooth=false,
font= D.font or 30,
fType=D.fType,
change=D.change,
disp= D.disp,
code= D.code,
@@ -868,7 +863,7 @@ function inputBox:draw()
--Drawable
local f=self.font
FONT.set(f,self.fType)
FONT.set(f)
if self.obj then
mDraw_Y(self.obj,x-12-self.obj:getWidth(),y+h*.5)
end
@@ -911,7 +906,7 @@ function inputBox:keypress(k)
self.value=t
end
end
function WIDGET.newInputBox(D)--name,x,y,w[,h][,font=30][,fType][,secret][,regex][,limit],hide
function WIDGET.newInputBox(D)--name,x,y,w[,h][,font=30][,secret][,regex][,limit],hide
local _={
name= D.name or"_",
@@ -927,7 +922,6 @@ function WIDGET.newInputBox(D)--name,x,y,w[,h][,font=30][,fType][,secret][,regex
},
font= D.font or int(D.h/7-1)*5,
fType=D.fType,
secret=D.secret==true,
regex= D.regex,
limit= D.limit,
@@ -1028,7 +1022,7 @@ function textBox:draw()
gc_rectangle('line',x,y,w,h,3)
--Texts
FONT.set(self.font,self.fType)
FONT.set(self.font)
gc_push('transform')
gc_translate(x,y)
@@ -1060,7 +1054,7 @@ end
function textBox:getInfo()
return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h)
end
function WIDGET.newTextBox(D)--name,x,y,w,h[,font=30][,fType][,lineH][,fix],hide
function WIDGET.newTextBox(D)--name,x,y,w,h[,font=30][,lineH][,fix],hide
local _={
name= D.name or"_",
@@ -1082,7 +1076,6 @@ function WIDGET.newTextBox(D)--name,x,y,w,h[,font=30][,fType][,lineH][,fix],hide
h= D.h,
font= D.font or 30,
fType=D.fType,
fix= D.fix,
texts={},
hideF=D.hideF,

View File

@@ -1,23 +1,30 @@
TECHMINO © 2019-2021 26F Studio. Some rights reserved.
TECHMINO and "26F Studio" are trademarks of 26F Studio. The TECHMINO game and source code are under a GNU Lesser General Public License Version 3.
**TECHMINO © 2019-2021 26F Studio. Some rights reserved.**
TECHMINO and "26F Studio" are trademarks of 26F Studio.
The TECHMINO game and source code are under a GNU Lesser General Public License Version 3.
"Tetris" is the registered trademark of The Tetris Holding, LLC, licensed to The Tetris Company, Inc. TECHMINO is not a fan game of Tetris. TECHMINO and 26F Studio are not affiliated with Tetris Holding, LLC or The Tetris Company, Inc. in any way.
TECHMINO is not a fan game of Tetris. TECHMINO and 26F Studio are not affiliated with Tetris Holding, LLC or The Tetris Company, Inc. in any way.
"Tetris" is the registered trademark of The Tetris Holding, LLC, licensed to The Tetris Company, Inc.
Powered by LÖVE, © 2006-2021 LÖVE Development Team.
Lua is free software distributed under the terms of the MIT license. Copyright © 1994-2021 by Lua.org, PUC-Rio.
SIMPLE LOVE LIGHTS is under a MIT License. Created by Dylan Hunn.
json.lua is copyrighted by rxi. © 2021 rxi.
IBM Plex is copyrighted by the International Business Machines Corporation. IBM and IBM Plex are trademarks of IBM Corp, registered in many jurisdictions worldwide. IBM Plex is licensed under the SIL Open Font License, Version 1.1.
Lua is free software distributed under the terms of the MIT license. Copyright © 1994~2021 by Lua.org, PUC-Rio.
Source Han Sans is copyrighted by Adobe Inc. Source Han Sans and Adobe are registered trademarks of Adobe Inc. in United States and other countries or regions. Source Han Sans is licensed under the SIL Open Font License, Version 1.1.
The Apple logo, "Apple Inc.," iOS, iPadOS, macOS, iPhone, and Mac are registered trademarks of Apple Inc. in the United States of America and other countries or regions.
"Windows", the Windows logo, "Xbox", Xbox logo, and "Microsoft" are registered trademarks of Microsoft Corporation in the United States of America and other countries or regions.
Alibaba Sans is copyrighted by Alibaba Group Holding Limited. Alibaba is a trademark of Alibaba Group Holding Limited in the Peoples Republic of China and other countries or regions.
IBM Plex is copyrighted by the International Business Machines Corporation. IBM and IBM Plex are trademarks of IBM Corp, registered in many jurisdictions worldwide. IBM Plex is licensed under the SIL Open Font License.
JetBrains Mono is copyrighted by the JetBrains Mono Project authors. JetBrains Mono is a trademark of JetBrains s.r.o. JetBrains Mono is licensed under the SIL Open Font License, Version 1.1.
@@ -26,26 +33,22 @@ JetBrains Mono is copyrighted by the JetBrains Mono Project authors. JetBrains M
"PlayStation", "PS", "PlayStation Family Mark", "PS logo", "DualSense" and "Play Has No Limits" are registered trademarks or trademarks of Sony Interactive Entertainment Inc. "SONY" is a registered trademark of Sony Corporation. © 2021 Sony Interactive Entertainment LLC.
"Windows", the Windows logo, "Xbox", Xbox logo, and "Microsoft" are registered trademarks of Microsoft Corporation in the United States of America and other countries or regions.
The Apple logo, "Apple Inc.," iOS, iPadOS, macOS, iPhone, and Mac are registered trademarks of Apple Inc. in the United States of America and other countries or regions.
N3TWORK is a registered trademark of N3TWORK Inc. © 2021 N3TWORK Inc.
"EA" and "Electronic Arts" are registered trademarks of Electronic Arts Inc. © 2021 Electronic Arts Inc.
SEGA and the SEGA logo are registered trademarks of Sega Corporation. © 2021 Sega Corporation.
Oculus Quest is a registered trademark of Facebook Technologies, LLC. © Meta Platforms, Inc.
Oculus Quest is a registered trademark of Facebook Technologies, LLC. © Facebook, Inc.
"Nintendo" is a registered trademarks of Nintendo Co., Ltd. © 2021 Nintendo Co., Ltd.
N3TWORK is a registered trademark of N3TWORK Inc. © 2021 N3TWORK Inc.
GoldWave is a registered trademark of GoldWave, Inc.
Linux is a registered trademark of Linus Torvalds.
Linux is a registered trademark of Linus Torvalds.
Touhou Project © Team Shanghai Alice 2002-2021.
All other trademarks, logos, and copyrights are the properties of their respective owners.
All other trademarks are the properties of their respective owners.

View File

@@ -24,7 +24,6 @@ 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()
@@ -50,17 +49,9 @@ local _LOADTIME_=TIME()
--Load modules
Z=require'Zframework'
FONT.load{
norm='parts/fonts/proportional.ttf',
mono='parts/fonts/monospaced.ttf',
}
FONT.setDefault('norm')
FONT.setFallback('norm')
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_))
@@ -99,7 +90,7 @@ for _,v in next,fs.getDirectoryItems('parts/shaders')do
end
end
LINE= require'parts.line'
FREEROW= require'parts.freeRow'
DATA= require'parts.data'
TEXTURE= require'parts.texture'
@@ -113,12 +104,6 @@ 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
@@ -211,15 +196,15 @@ end
Z.setOnQuit(destroyPlayers)
--Load settings and statistics
TABLE.cover (loadFile('conf/user')or{},USER)
TABLE.cover (loadFile('conf/unlock')or{},RANKS)
TABLE.update(loadFile('conf/settings')or{},SETTING)
TABLE.coverR(loadFile('conf/data')or{},STAT)
TABLE.cover (loadFile('conf/key')or{},KEY_MAP)
TABLE.cover (loadFile('conf/virtualkey')or{},VK_ORG)
TABLE.cover (FILE.load('conf/user')or{},USER)
TABLE.cover (FILE.load('conf/unlock')or{},RANKS)
TABLE.update(FILE.load('conf/settings')or{},SETTING)
TABLE.update(FILE.load('conf/data')or{},STAT)
TABLE.cover (FILE.load('conf/key')or{},KEY_MAP)
TABLE.cover (FILE.load('conf/virtualkey')or{},VK_ORG)
--Initialize fields, sequence, missions, gameEnv for cutsom game
local fieldData=loadFile('conf/customBoards','-string')
local fieldData=FILE.load('conf/customBoards','string')
if fieldData then
fieldData=STRING.split(fieldData,"!")
for i=1,#fieldData do
@@ -228,15 +213,15 @@ if fieldData then
else
FIELD[1]=DATA.newBoard()
end
local sequenceData=loadFile('conf/customSequence','-string')
local sequenceData=FILE.load('conf/customSequence','string')
if sequenceData then
DATA.pasteSequence(sequenceData)
end
local missionData=loadFile('conf/customMissions','-string')
local missionData=FILE.load('conf/customMissions','string')
if missionData then
DATA.pasteMission(missionData)
end
local customData=loadFile('conf/customEnv')
local customData=FILE.load('conf/customEnv')
if customData and customData['version']==VERSION.code then
TABLE.complete(customData,CUSTOMENV)
end
@@ -459,7 +444,7 @@ do
fs.remove('record/round_l.rec')
fs.remove('record/round_u.rec')
end
if RANKS.stack_e then
if STAT.version<1604 then
RANKS.stack_e=nil
RANKS.stack_h=nil
RANKS.stack_u=nil
@@ -475,18 +460,6 @@ do
fs.remove('record/stack_40l.rec')
fs.remove('record/stack_100l.rec')
end
if RANKS.rhythm_e then
RANKS.rhythm_e=nil
RANKS.rhythm_h=nil
RANKS.rhythm_u=nil
fs.remove('record/rhythm_e.rec')
fs.remove('record/rhythm_h.rec')
fs.remove('record/rhythm_u.rec')
end
if RANKS.bigbang then
RANKS.clearRush,RANKS.bigbang=RANKS.bigbang
fs.remove('record/bigbang.rec')
end
if STAT.version~=VERSION.code then
for k,v in next,MODE_UPDATE_MAP do
if RANKS[k]then
@@ -639,9 +612,9 @@ if TABLE.find(arg,'--test')then
TASK.new(function()
while true do
YIELD()
if Z.errData[1]then break end
if 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")
LOG("\27[91m\27[1mAutomatic Test Failed :(\27[0m\nThe error message is:\n"..table.concat(ERRDATA[1].mes,"\n").."\27[91m\nAborting\27[0m")
TEST.yieldN(60)
love.event.quit(1)
end)

Binary file not shown.

BIN
media/music/1989.ogg Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -129,10 +129,10 @@ do
[10]={'+0+0','+1+0','+1-1','+0+2','+1-2','+1-2'},
[03]={'+0+0','+1+0','+1+1','+0-2','+1-1','+1-2'},
[30]={'+0+0','-1+0','-1-1','+0+2','-1+2','+0-1'},
[12]={'+0+0','+1+0','+1-1','+0+2','+1+2','+1+1'},
[21]={'+0+0','-1+0','-1+1','+0-2','-1-2','-1-1'},
[32]={'+0+0','-1+0','-1-1','+0+2','-1+2','+0-1'},
[23]={'+0+0','+1+0','+1+1','+0-2','+1-2','+0+1'},
[12]={'+0+0','+1+0','+1-1','+0+2','+1+2'},
[21]={'+0+0','-1+0','-1+1','+0-2','-1-2'},
[32]={'+0+0','-1+0','-1-1','+0+2','-1+2'},
[23]={'+0+0','+1+0','+1+1','+0-2','+1-2'},
[02]={'+0+0','+1+0','-1+0','+0-1','+0+1'},
[20]={'+0+0','-1+0','+1+0','+0+1','+0-1'},
[13]={'+0+0','+0-1','+0+1','+0-2'},
@@ -140,8 +140,8 @@ do
},--Z
false,--S
{
[01]={'+0+0','-1+0','-1+1','+0-2','+1+1','+0+1','+0-1'},
[10]={'+0+0','+1+0','+1-1','+0+2','-1-1','+0-1','+0+1'},
[01]={'+0+0','-1+0','-1+1','+0-2','+1+1','+0+1'},
[10]={'+0+0','+1+0','+1-1','+0+2','-1-1','+0-1'},
[03]={'+0+0','+1+0','+1+1','+0-2','+1-2','+1-1','+0+1'},
[30]={'+0+0','-1+0','-1-1','+0+2','-1+2','+0-1','-1+1'},
[12]={'+0+0','+1+0','+1-1','+1+1','-1+0','+0-1','+0+2','+1+2'},
@@ -353,8 +353,8 @@ do
[21]={'+0+0','-1+0','-1+1','+0+1','-1+2','+0+2','-1-1','+1+0','+0-2','-1-2'},
[32]={'+0+0','-1+0','-1+1','-1-1','+1+0','+0+2','-1+2','+0-2'},
[23]={'+0+0','+1+0','+1-1','+1+1','-1+0','+0-2','+1-2','+0+2'},
[02]={'+0+0','+0-1','-1-1','+1-1','-1+0','+2-1'},
[20]={'+0+0','+0+1','+1+1','-1+1','+1+0','-2+1'},
[02]={'+0+0','+0-1','+1-1','-1+0','+2-1'},
[20]={'+0+0','+0+1','-1+1','+1+0','-2+1'},
[13]={'+0+0','-1+0','-1-1','+0+1','-1-2'},
[31]={'+0+0','+1+0','+1+1','+0-1','+1+2'},
},--J5
@@ -686,7 +686,13 @@ do
sfx='prerotate'
elseif P:ifoverlap(icb,x,y+1)and P:ifoverlap(icb,x-1,y)and P:ifoverlap(icb,x+1,y)then
sfx='rotatekick'
P:_rotateField(d)
if P.gameEnv.shakeFX then
if d==1 or d==3 then
P.fieldOff.va=P.fieldOff.va+(2-d)*6e-3
else
P.fieldOff.va=P.fieldOff.va+P:getCenterX()*3e-3
end
end
else
sfx='rotate'
end

View File

@@ -1,4 +1,4 @@
--blockhole
--Blackhole
local gc=love.graphics
local gc_clear,gc_replaceTransform=gc.clear,gc.replaceTransform
local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth
@@ -51,7 +51,7 @@ function back.draw()
gc_draw(S.texture,S.d*cos(S.ang),S.d*sin(S.ang),S.rotate,S.size*.026,nil,15,15)
end
--blockhole
--Blackhole
gc_setColor(.07,.07,.07)
gc_circle('fill',0,0,157)
gc_setLineWidth(6)

View File

@@ -12,9 +12,10 @@ local ins,rem=table.insert,table.remove
local back={}
local t
local petal
local fan,petal
function back.init()
t=0
fan=SVG_TITLE_FAN
petal={}
end
function back.update()
@@ -61,7 +62,7 @@ function back.draw()
gc_setLineWidth(6)
gc_setColor(.8,.9,1,.3)
for i=1,#SVG_TITLE_FAN do gc_polygon('line',SVG_TITLE_FAN[i])end
for i=1,8 do gc_polygon('line',fan[i])end
gc_setLineWidth(2)
gc_setColor(1,.5,.7,.3)

View File

@@ -44,7 +44,8 @@ local function _ifoverlapAI(f,bk,x,y)
end
end end
end
local getRow,discardRow=LINE.new,LINE.discard
local discardRow=FREEROW.discard
local getRow=FREEROW.get
local function _resetField(f0,f,start)
for _=#f,start,-1 do
discardRow(f[_])

View File

@@ -12,7 +12,7 @@ local baseBot={
function baseBot.update(bot)
local P=bot.P
local keys=bot.keys
if P.control and P.waiting==0 then
if P.control and P.waiting==-1 then
bot.delay=bot.delay-1
if not keys[1]then
if bot.runningThread then

View File

@@ -6,8 +6,6 @@ return{
lock=1e99,
wait=0,
fall=0,
hang=5,
hurry=1e99,
--Control
nextCount=6,
@@ -21,7 +19,6 @@ return{
--Rule
sequence='bag',
lockout=false,
fieldH=20,
heightLimit=1e99,
bufferLimit=1e99,

View File

@@ -33,7 +33,6 @@ return{
P:_showText(text.maxspeed,0,-140,100,'appear',.6)
end
end
P:shakeField(9)
D.wave=D.wave+1
end
end

View File

@@ -42,7 +42,6 @@ return{
P:_showText(text.maxspeed,0,-140,100,'appear',.6)
end
end
P:shakeField(10)
D.wave=D.wave+1
end
end

View File

@@ -1,5 +1,5 @@
return{
hook_drop=function(P)
dropPiece=function(P)
if P.lastPiece.atk>0 then
P:receive(nil,P.lastPiece.atk,0,generateLine(P.holeRND:random(10)))
end

View File

@@ -1,5 +1,5 @@
return{
hook_drop=function(P)
dropPiece=function(P)
if P.lastPiece.atk>0 then
P:receive(nil,P.lastPiece.atk,120,generateLine(P.holeRND:random(10)))
end

View File

@@ -1,5 +1,5 @@
return{
hook_drop=function(P)
dropPiece=function(P)
if P.lastPiece.atk>0 then
P:receive(nil,P.lastPiece.atk,30,generateLine(P.holeRND:random(10)))
end

View File

@@ -1,5 +1,5 @@
return{
hook_drop=function(P)
dropPiece=function(P)
if P.lastPiece.atk>0 then
P:receive(nil,P.lastPiece.atk,60,generateLine(P.holeRND:random(10)))
end

View File

@@ -11,8 +11,8 @@ return{
task=function(P)
local F=P.field
for i=1,24 do
F[i]=LINE.new(20)
P.visTime[i]=LINE.new(20)
F[i]=FREEROW.get(20)
P.visTime[i]=FREEROW.get(20)
for x=4,7 do F[i][x]=0 end
end
if P.holeRND:random()<.6 then

View File

@@ -1,10 +1,10 @@
return{
hook_drop=function(P)
dropPiece=function(P)
if P.lastPiece.row>0 then
for _=1,#P.clearedRow do
local h=#P.field
P.field[h+1]=LINE.new(20)
P.visTime[h+1]=LINE.new(20)
P.field[h+1]=FREEROW.get(20)
P.visTime[h+1]=FREEROW.get(20)
for i=4,7 do P.field[h+1][i]=0 end
end
if P.combo>P.modeData.maxCombo then

View File

@@ -1,12 +1,12 @@
return{
hook_drop=function(P)
dropPiece=function(P)
if P.lastPiece.row==0 then
P:lose()
else
for _=1,P.lastPiece.row do
local h=#P.field
P.field[h+1]=LINE.new(20)
P.visTime[h+1]=LINE.new(20)
P.field[h+1]=FREEROW.get(20)
P.visTime[h+1]=FREEROW.get(20)
for i=4,7 do P.field[h+1][i]=0 end
end
if P.combo>P.modeData.maxCombo then

View File

@@ -6,7 +6,7 @@ return{
mText(TEXTOBJ.atk,63,243)
mText(TEXTOBJ.eff,63,363)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.atk>=100 then
P:win('finish')
end

View File

@@ -1,11 +1,13 @@
return{
hook_drop=function(P)
dropPiece=function(P)
if P.garbageBeneath==0 then
local D=P.modeData
D.finished=D.finished+1
if FIELD[D.finished+1]then
P.waiting=26
for i=#P.field,1,-1 do
FREEROW.discard(P.field[i])
FREEROW.discard(P.visTime[i])
P.field[i],P.visTime[i]=nil
end
setField(P,D.finished+1)

View File

@@ -6,7 +6,7 @@ return{
mStr(r,63,265)
PLY.draw.drawTargetLine(P,r)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=10 then
P:win('finish')
end

View File

@@ -6,7 +6,7 @@ return{
mStr(r,63,265)
PLY.draw.drawTargetLine(P,r)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=100 then
P:win('finish')
end

View File

@@ -6,7 +6,7 @@ return{
mStr(r,63,265)
PLY.draw.drawTargetLine(P,r)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=1000 then
P:win('finish')
end

View File

@@ -6,7 +6,7 @@ return{
mStr(r,63,265)
PLY.draw.drawTargetLine(P,r)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=20 then
P:win('finish')
end

View File

@@ -6,7 +6,7 @@ return{
mStr(r,63,265)
PLY.draw.drawTargetLine(P,r)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=200 then
P:win('finish')
end

View File

@@ -6,7 +6,7 @@ return{
mStr(r,63,265)
PLY.draw.drawTargetLine(P,r)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=40 then
P:win('finish')
end

View File

@@ -6,7 +6,7 @@ return{
mStr(r,63,265)
PLY.draw.drawTargetLine(P,r)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=400 then
P:win('finish')
end

View File

@@ -1,5 +1,5 @@
return{
hook_drop=function(P)
dropPiece=function(P)
if #PLY_ALIVE>1 then
P.control=false
local id1=P.sid

View File

@@ -1,5 +1,5 @@
return{
hook_drop=function(P)
dropPiece=function(P)
if P.stat.piece%7==0 and #PLY_ALIVE>1 then
P.control=false
local id1=P.sid

View File

@@ -1,4 +1,3 @@
local gc_setColor=love.graphics.setColor
return{
das=16,arr=6,
sddas=6,sdarr=6,
@@ -19,24 +18,12 @@ return{
mStr(r<10 and 9 or r<30 and r or("%02x"):format(r*10-300),63,210)
mText(TEXTOBJ.speedLV,63,290)
PLY.draw.drawProgress(P.stat.row,P.modeData.target)
if P.modeData.drought>7 then
if P.modeData.drought<=14 then
gc_setColor(1,1,1,P.modeData.drought/7-1)
else
local gb=P.modeData.drought<=21 and 2-P.modeData.drought/14 or .5
gc_setColor(1,gb,gb)
end
setFont(50)
mStr(P.modeData.drought,63,130)
mDraw(MODES.drought_l.icon,63,200,nil,.5)
end
end,
task=function(P)
P.modeData.target=10
end,
hook_drop=function(P)
dropPiece=function(P)
local D=P.modeData
D.drought=P.lastPiece.id==7 and 0 or D.drought+1
if P.stat.row>=D.target then
if D.target==110 then
P.gameEnv.drop,P.gameEnv.lock=5,5

View File

@@ -1,4 +1,3 @@
local gc_setColor=love.graphics.setColor
return{
das=16,arr=6,
sddas=3,sdarr=3,
@@ -19,22 +18,11 @@ return{
mStr(r<11 and 18 or r<22 and r+8 or("%02x"):format(r*10-220),63,210)
mText(TEXTOBJ.speedLV,63,290)
PLY.draw.drawProgress(P.stat.row,P.modeData.target)
if P.modeData.drought>7 then
if P.modeData.drought<=14 then
gc_setColor(1,1,1,P.modeData.drought/7-1)
else
local gb=P.modeData.drought<=21 and 2-P.modeData.drought/14 or .5
gc_setColor(1,gb,gb)
end
setFont(50)
mStr(P.modeData.drought,63,130)
mDraw(MODES.drought_l.icon,63,200,nil,.5)
end
end,
task=function(P)
P.modeData.target=10
end,
hook_drop=function(P)
dropPiece=function(P)
local D=P.modeData
if P.stat.row>=D.target then
if D.target==110 then

View File

@@ -1,4 +1,3 @@
local gc_setColor=love.graphics.setColor
return{
das=16,arr=6,
sddas=1,sdarr=1,
@@ -19,22 +18,11 @@ return{
mStr(r==1 and 29 or("%02x"):format(r*10-20),63,210)
mText(TEXTOBJ.speedLV,63,290)
PLY.draw.drawProgress(P.stat.row,P.modeData.target)
if P.modeData.drought>7 then
if P.modeData.drought<=14 then
gc_setColor(1,1,1,P.modeData.drought/7-1)
else
local gb=P.modeData.drought<=21 and 2-P.modeData.drought/14 or .5
gc_setColor(1,gb,gb)
end
setFont(50)
mStr(P.modeData.drought,63,130)
mDraw(MODES.drought_l.icon,63,200,nil,.5)
end
end,
task=function(P)
P.modeData.target=10
end,
hook_drop=function(P)
dropPiece=function(P)
local D=P.modeData
if P.stat.row>=D.target then
if D.target==100 then

View File

@@ -1,36 +0,0 @@
local function task_newBoard(P,init)
local targetLine
local F,L={},{1}
--TODO
P:pushNextList(L)
P.control=false
if not init then for _=1,26 do YIELD()end end
P.control=true
P.gameEnv.heightLimit=targetLine or #F
P:pushLineList(F)
end
local function _check(P)
P.gameEnv.heightLimit=P.gameEnv.heightLimit-P.lastPiece.row
if P.gameEnv.heightLimit==0 then
P.modeData.stage=P.modeData.stage+1
if P.modeData.stage>=100 then
P:win('finish')
else
P:newTask(task_newBoard)
end
end
end
return{
sequence='none',
RS="TRS",
pushSpeed=5,
mesDisp=function(P)
setFont(60)
mStr(P.modeData.stage,63,280)
mText(TEXTOBJ.wave,63,350)
end,
hook_drop=_check,
task=function(P)task_newBoard(P,true)P.fieldBeneath=0 end,--Just run one time at first to start first level
}

View File

@@ -40,7 +40,6 @@ return{
P:_showText(text.maxspeed,0,-140,100,'appear',.6)
P.dropDelay,P.gameEnv.drop=2,2
end
P:shakeField(3)
end
end
end

View File

@@ -40,7 +40,6 @@ return{
P.dropDelay,P.gameEnv.drop=5,5
P:_showText(text.maxspeed,0,-140,100,'appear',.6)
end
P:shakeField(3)
end
end
end

View File

@@ -3,7 +3,7 @@ return{
setFont(55)
mStr(100-P.stat.dig,63,265)
end,
hook_drop=function(P)
dropPiece=function(P)
for _=1,math.min(10,100-P.stat.dig)-P.garbageBeneath do
P:garbageRise(21,1,P:getHolePos())
end

View File

@@ -3,7 +3,7 @@ return{
setFont(55)
mStr(10-P.stat.dig,63,265)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.dig==10 then
P:win('finish')
end

View File

@@ -3,7 +3,7 @@ return{
setFont(55)
mStr(400-P.stat.dig,63,265)
end,
hook_drop=function(P)
dropPiece=function(P)
for _=1,math.min(10,400-P.stat.dig)-P.garbageBeneath do
P:garbageRise(21,1,P:getHolePos())
end

View File

@@ -3,7 +3,7 @@ return{
setFont(55)
mStr(40-P.stat.dig,63,265)
end,
hook_drop=function(P)
dropPiece=function(P)
for _=1,math.min(10,40-P.stat.dig)-P.garbageBeneath do
P:garbageRise(21,1,P:getHolePos())
end

View File

@@ -11,7 +11,7 @@ return{
task=function(P)
P.modeData.target=10
end,
hook_drop=function(P)
dropPiece=function(P)
local flag
local l=P.lastPiece
if P.combo>1 then flag=true;P:showText("2x",0,-220,40,'flicker',.3)end

View File

@@ -10,7 +10,7 @@ return
task=function(P)
P.modeData.target=50
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target==50 then
P.gameEnv.drop=.25

View File

@@ -30,7 +30,7 @@ return
task=function(P)
P.modeData.target=10
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target%300==0 then
P.gameEnv.wait=P.gameEnv.wait-1

View File

@@ -12,7 +12,7 @@ return
task=function(P)
P.modeData.target=10
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target==200 then
P:win('finish')

View File

@@ -30,7 +30,7 @@ return{
mStr(P.stat.row,63,230)
mStr(P.stat.clears[4],63,340)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.modeData.rankPoint<140-passPoint then--If Less then X
local R=#P.clearedRow
if R>0 then

View File

@@ -8,7 +8,7 @@ return{
mesDisp=function(P)
PLY.draw.drawProgress(P.modeData.pt,P.modeData.target)
end,
hook_drop=function(P)
dropPiece=function(P)
local D=P.modeData
local c=#P.clearedRow

View File

@@ -12,7 +12,7 @@ return{
mesDisp=function(P)
PLY.draw.drawProgress(P.modeData.pt,P.modeData.target)
end,
hook_drop=function(P)
dropPiece=function(P)
local D=P.modeData
local c=#P.clearedRow

View File

@@ -14,7 +14,7 @@ return
task=function(P)
P.modeData.target=10
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target==200 then
P:win('finish')

View File

@@ -12,7 +12,7 @@ return{
mesDisp=function(P)
PLY.draw.drawProgress(P.modeData.pt,P.modeData.target)
end,
hook_drop=function(P)
dropPiece=function(P)
local D=P.modeData
local c=#P.clearedRow

View File

@@ -13,7 +13,7 @@ return
mesDisp=function(P)
PLY.draw.drawProgress(P.modeData.pt,P.modeData.target)
end,
hook_drop=function(P)
dropPiece=function(P)
local p=P.modeData.pt+P.lastPiece.row
if p>=P.modeData.target then
local ENV=P.gameEnv
@@ -55,8 +55,8 @@ return
P.field[i][P.holeRND:random(10)]=0
end
else
P.field[i]=LINE.new(0)
P.visTime[i]=LINE.new(30)
P.field[i]=FREEROW.get(0)
P.visTime[i]=FREEROW.get(30)
for j=1,10 do
if P.holeRND:random()>.9 then
P.field[i][j]=P.holeRND:random(16)
@@ -89,7 +89,6 @@ return
ENV.bone=true
P.modeData.target=62
SFX.play('reach')
else
p=41
end
@@ -113,7 +112,6 @@ return
ENV.fall=4
P.modeData.target=162
SFX.play('reach')
elseif T==162 then--Stage 7: speed up+++
P:stageComplete(7)
P.life=P.life+1
@@ -148,7 +146,6 @@ return
P.modeData.target=260
p=260
SFX.play('blip_2')
SFX.play('reach')
else
p=260
end

View File

@@ -5,7 +5,7 @@ return{
mStr(P.stat.pc,63,340)
mText(TEXTOBJ.pc,63,410)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.lastPiece.pc and P.stat.row%4==0 then
P.gameEnv.heightLimit=4
if P.stat.pc%5==0 then

View File

@@ -1,54 +1,51 @@
local pc_drop={50,45,40,35,30,26,22,18,15,12}
local pc_lock={55,50,46,42,40,38,36,34,32,30}
local pc_lock={55,50,46,42,39,36,33,31,29,27}
local pc_fall={18,16,14,12,10,9,8,7,6,5}
local PCbase=require"parts.modes.PCbase"
local PClist=require"parts.modes.PClist"
local function task_PC(P)
local difficulty=P.stat.pc<10 and 4 or 5
local L=PClist[difficulty][P.holeRND:random(#PClist[difficulty])]
local symmetry=P.holeRND:random()>.5
P:pushNextList(L,symmetry)
P.control=false
if P.frameRun>180 then for _=1,26 do YIELD()end end
P.control=true
local base=PCbase[difficulty]
P:pushLineList(base[P.holeRND:random(#base)],symmetry)
if P.frameRun>180 then
P.control=false
for _=1,26 do YIELD()end
P.control=true
end
local base=PCbase[P.modeData.type]
P:pushLineList(base[P.holeRND:random(#base)],P.modeData.symmetry)
end
local function _check(P)
if #P.field>0 then
if #P.field+P.stat.row%4>4 then
local function check(P)
local f=P.field
if #f>0 then
if #f+P.stat.row%4>4 then
P:lose()
end
else
if P.stat.pc>=100 then
P:win('finish')
else
P:newTask(task_PC)
if P.frameRun<180 then P.fieldBeneath=0 end
local type=P.stat.pc<10 and 4 or 5
local L=PClist[type][P.holeRND:random(#PClist[type])]
local symmetry=P.holeRND:random()>.5
P.modeData.type=type
P.modeData.symmetry=symmetry
P:pushNextList(L,symmetry)
P.modeData.counter=P.stat.piece==0 and 20 or 0
P:newTask(task_PC)
if P.stat.pc%4==0 and P.stat.pc>0 and P.stat.pc<=40 then
local s=P.stat.pc/4
P.gameEnv.drop=pc_drop[s]or 10
P.gameEnv.lock=pc_lock[s]or 25
P.gameEnv.fall=pc_fall[s]or 4
if s==10 then
P:_showText(text.maxspeed,0,-140,100,'appear',.6)
end
local s=P.stat.pc*.25
if math.floor(s)==s and s>0 then
P.gameEnv.drop=pc_drop[s]or 10
P.gameEnv.lock=pc_lock[s]or 25
P.gameEnv.fall=pc_fall[s]or 4
if s==10 then
P:_showText(text.maxspeed,0,-140,100,'appear',.6)
end
end
end
end
return{
sequence='none',
RS="SRS",
mesDisp=function(P)
setFont(60)
mStr(P.stat.pc,63,260)
mText(TEXTOBJ.pc,63,330)
mStr(P.stat.pc,63,340)
mText(TEXTOBJ.pc,63,410)
end,
hook_drop=_check,
task=_check,--Just run one time at first to start first level
dropPiece=check,
task=check,
}

View File

@@ -8,40 +8,35 @@ local PCtype={
1,2,3,
}
local function task_PC(P)
local difficulty=PCtype[P.stat.pc+1]or 3
local L=PClist[difficulty][P.holeRND:random(#PClist[difficulty])]
local symmetry=P.holeRND:random()>.5
P:pushNextList(L,symmetry)
P.control=false
if P.frameRun>180 then for _=1,26 do YIELD()end end
for _=1,26 do YIELD()end
P.control=true
local base=PCbase[difficulty]
P:pushLineList(base[P.holeRND:random(#base)],symmetry)
local base=PCbase[P.modeData.type]
P:pushLineList(base[P.holeRND:random(#base)],P.modeData.symmetry)
end
local function _check(P)
if #P.field>0 then
if #P.field+P.stat.row%4>4 then
local function check(P)
local r=P.field
if #r>0 then
if #r+P.stat.row%4>4 then
P:lose()
end
else
if P.stat.pc>=60 then
P:win('finish')
else
P:newTask(task_PC)
if P.frameRun<180 then P.fieldBeneath=0 end
end
local type=PCtype[P.stat.pc+1]or 3
local L=PClist[type][P.holeRND:random(#PClist[type])]
local symmetry=P.holeRND:random()>.5
P.modeData.type=type
P.modeData.symmetry=symmetry
P:pushNextList(L,symmetry)
P.modeData.counter=P.stat.piece==0 and 20 or 0
P:newTask(task_PC)
end
end
return{
sequence='none',
RS="SRS",
mesDisp=function(P)
setFont(60)
mStr(P.stat.pc,63,260)
mText(TEXTOBJ.pc,63,330)
mStr(P.stat.pc,63,340)
mText(TEXTOBJ.pc,63,410)
end,
hook_drop=_check,
task=_check,--Just run one time at first to start first level
dropPiece=check,
task=check,
}

View File

@@ -0,0 +1,53 @@
local gc=love.graphics
local dropSpeed={[0]=40,33,27,20,16,12,11,10,9,8,7,6,5,4,3,3,2,2,1,1}
return{
drop=40,
lock=1e99,
wait=20,
fall=90,
mesDisp=function(P)
PLY.draw.drawProgress(P.stat.row,P.modeData.target)
setFont(30)
mStr(P.modeData.bpm,63,178)
gc.setLineWidth(4)
gc.circle('line',63,200,30)
local beat=P.modeData.counter/P.modeData.beatFrame
gc.setColor(1,1,1,1-beat)
gc.setLineWidth(3)
gc.circle('line',63,200,30+45*beat)
end,
dropPiece=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target==200 then
P:win('finish')
else
P.modeData.bpm=40+2*P.modeData.target/10
P.modeData.beatFrame=math.floor(3600/P.modeData.bpm)
P.gameEnv.fall=P.modeData.beatFrame
P.gameEnv.wait=math.max(P.gameEnv.wait-2,0)
P.gameEnv.drop=dropSpeed[P.modeData.target/10]
P.modeData.target=P.modeData.target+10
SFX.play('reach')
end
end
end,
task=function(P)
P.modeData.target=10
P.modeData.bpm=40
P.modeData.beatFrame=90
P.modeData.counter=90
while true do
YIELD()
P.modeData.counter=P.modeData.counter-1
if P.modeData.counter==0 then
P.modeData.counter=P.modeData.beatFrame
SFX.play('click',.3)
P:act_hardDrop()
end
end
end,
}

View File

@@ -0,0 +1,53 @@
local gc=love.graphics
local dropSpeed={[0]=30,26,23,20,17,14,12,10,8,6,5,4,3,2,1,1,.5,.5,.25,.25}
return{
drop=30,
lock=1e99,
wait=10,
fall=60,
mesDisp=function(P)
PLY.draw.drawProgress(P.stat.row,P.modeData.target)
setFont(30)
mStr(P.modeData.bpm,63,178)
gc.setLineWidth(4)
gc.circle('line',63,200,30)
local beat=P.modeData.counter/P.modeData.beatFrame
gc.setColor(1,1,1,1-beat)
gc.setLineWidth(3)
gc.circle('line',63,200,30+45*beat)
end,
dropPiece=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target==200 then
P:win('finish')
else
P.modeData.bpm=60+3*P.modeData.target/10
P.modeData.beatFrame=math.floor(3600/P.modeData.bpm)
P.gameEnv.fall=P.modeData.beatFrame
P.gameEnv.wait=math.max(P.gameEnv.wait-1,0)
P.gameEnv.drop=dropSpeed[P.modeData.target/10]
P.modeData.target=P.modeData.target+10
SFX.play('reach')
end
end
end,
task=function(P)
P.modeData.target=10
P.modeData.bpm=60
P.modeData.beatFrame=60
P.modeData.counter=60
while true do
YIELD()
P.modeData.counter=P.modeData.counter-1
if P.modeData.counter==0 then
P.modeData.counter=P.modeData.beatFrame
SFX.play('click',.3)
P:act_hardDrop()
end
end
end,
}

View File

@@ -0,0 +1,58 @@
local gc=love.graphics
return{
drop=.5,
lock=1e99,
wait=5,
fall=30,
mesDisp=function(P)
PLY.draw.drawProgress(P.stat.row,P.modeData.target)
setFont(30)
mStr(P.modeData.bpm,63,178)
gc.setLineWidth(4)
gc.circle('line',63,200,30)
local beat=P.modeData.counter/P.modeData.beatFrame
gc.setColor(1,1,1,1-beat)
gc.setLineWidth(3)
gc.circle('line',63,200,30+45*beat)
end,
dropPiece=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target==200 then
P:win('finish')
else
P.modeData.bpm=120+2*P.modeData.target/10
P.modeData.beatFrame=math.floor(3600/P.modeData.bpm)
P.gameEnv.fall=P.modeData.beatFrame
P.gameEnv.wait=math.max(P.gameEnv.wait-1,0)
if P.modeData.target==50 then
P.gameEnv.das=5
P.gameEnv.drop=.25
elseif P.modeData.target==100 then
P.gameEnv.das=4
P:set20G(true)
end
P.modeData.target=P.modeData.target+10
SFX.play('reach')
end
end
end,
task=function(P)
P.modeData.target=10
P.modeData.bpm=120
P.modeData.beatFrame=30
P.modeData.counter=30
while true do
YIELD()
P.modeData.counter=P.modeData.counter-1
if P.modeData.counter==0 then
P.modeData.counter=P.modeData.beatFrame
SFX.play('click',.3)
P:act_hardDrop()
end
end
end,
}

View File

@@ -1,24 +1,5 @@
local gc=love.graphics
local gc_draw,gc_print,gc_setColor=gc.draw,gc.print,gc.setColor
local setFont=setFont
local PLAYERS,PLY_ALIVE=PLAYERS,PLY_ALIVE
return{
mesDisp=function(P)
setFont(35)
mStr(#PLY_ALIVE.."/"..#PLAYERS,63,175)
mStr(P.modeData.ko,80,215)
gc_draw(TEXTOBJ.ko,60-TEXTOBJ.ko:getWidth(),222)
setFont(20)
gc_setColor(1,.5,0,.6)
gc_print(P.badge,103,227)
gc_setColor(.97,.97,.97)
setFont(25)
mStr(text.powerUp[P.strength],63,290)
gc_setColor(1,1,1)
for i=1,P.strength do
gc_draw(IMG.badgeIcon,16*i+6,260)
end
PLY.draw.drawRoyaleInfo(P)
end,
}

View File

@@ -1,20 +0,0 @@
return{
mesDisp=function(P)
setFont(45)
mStr(("%.1f"):format(P.stat.atk),63,270)
mText(TEXTOBJ.atk,63,323)
mStr(("%.2f"):format(P.stat.atk/P.stat.row),63,370)
mText(TEXTOBJ.eff,63,423)
setFont(55)
local r=40-P.stat.row
if r<0 then r=0 end
mStr(r,63,170)
PLY.draw.drawTargetLine(P,r)
end,
hook_drop=function(P)
if P.stat.row>=40 then
P:win('finish')
end
end
}

View File

@@ -36,7 +36,7 @@ return{
end
end
end,
hook_drop=function(P)
dropPiece=function(P)
if P.stat.row>=40 then
P:win('finish')
end

View File

@@ -14,7 +14,7 @@ return{
mStr(r,63,265)
PLY.draw.drawTargetLine(P,r)
end,
hook_drop=function(P)
dropPiece=function(P)
local F=P.field
for y=1,#F do
local l=F[y]

View File

@@ -7,9 +7,9 @@ return{
mText(TEXTOBJ.line,63,350)
PLY.draw.drawMarkLine(P,20,.3,1,1,TIME()%.42<.21 and .95 or .6)
end,
hook_die=function(P)
local cc=P:clearFilledLines(P.garbageBeneath+1,#P.field-P.garbageBeneath)
if cc>0 then
dropPiece=function(P)
if #P.field>20 then
local cc=P:clearFilledLines(P.garbageBeneath+1,#P.field-P.garbageBeneath)
local h=20-cc-P.garbageBeneath
if h>0 then
P:garbageRise(21,h,2e10-1)

View File

@@ -0,0 +1,22 @@
return{
fieldH=21,
fillClear=false,
mesDisp=function(P)
setFont(60)
mStr(P.stat.row,63,280)
mText(TEXTOBJ.line,63,350)
PLY.draw.drawMarkLine(P,18,.3,1,1,TIME()%.42<.21 and .95 or .6)
end,
dropPiece=function(P)
if #P.field>20 then
local cc=P:clearFilledLines(P.garbageBeneath+1,#P.field-P.garbageBeneath)
local h=20-cc-P.garbageBeneath-2
if h>0 then
P:garbageRise(21,h,2e10-1)
if P.garbageBeneath>=20 then
P:lose()
end
end
end
end,
}

View File

@@ -7,9 +7,9 @@ return{
mText(TEXTOBJ.line,63,350)
PLY.draw.drawMarkLine(P,17,.3,1,1,TIME()%.42<.21 and .95 or .6)
end,
hook_die=function(P)
local cc=P:clearFilledLines(P.garbageBeneath+1,#P.field-P.garbageBeneath)
if cc>0 then
dropPiece=function(P)
if #P.field>20 then
local cc=P:clearFilledLines(P.garbageBeneath+1,#P.field-P.garbageBeneath)
local h=20-cc-P.garbageBeneath-3
if h>0 then
P:garbageRise(21,h,2e10-1)

View File

@@ -1,30 +0,0 @@
local waitSpeed={60,59,58,57,56,55,54,52,50,48,46,44,42,40,38,36,34,32,30}
return
{
das=5,arr=1,
drop=0,lock=7,
wait=60,fall=0,
freshLimit=12,
mesDisp=function(P)
PLY.draw.drawProgress(P.stat.row,P.modeData.target)
PLY.draw.drawTargetLine(P,200-P.stat.row)
end,
task=function(P)
P.modeData.target=10
end,
hook_drop=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target==200 then
P:win('finish')
else
if P.modeData.target==100 then
P.modeData.lock=6
end
P.gameEnv.wait=waitSpeed[P.modeData.target/10]
P.modeData.target=P.modeData.target+10
SFX.play('reach')
end
end
end
}

View File

@@ -1,30 +0,0 @@
local waitSpeed={30,29,28,27,26,25,24,23,22,21,20,19,18,18,17,17,16,16,15}
return
{
das=4,arr=1,
drop=0,lock=6,
wait=30,fall=0,
freshLimit=12,
mesDisp=function(P)
PLY.draw.drawProgress(P.stat.row,P.modeData.target)
PLY.draw.drawTargetLine(P,200-P.stat.row)
end,
task=function(P)
P.modeData.target=10
end,
hook_drop=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target==200 then
P:win('finish')
else
if P.modeData.target==100 then
P.modeData.lock=5
end
P.gameEnv.wait=waitSpeed[P.modeData.target/10]
P.modeData.target=P.modeData.target+10
SFX.play('reach')
end
end
end
}

View File

@@ -1,30 +0,0 @@
local waitSpeed={15,15,14,14,13,13,12,12,11,11,10,10,9,9,8,8,7,7,7}
return
{
das=3,arr=1,
drop=0,lock=5,
wait=15,fall=0,
freshLimit=12,
mesDisp=function(P)
PLY.draw.drawProgress(P.stat.row,P.modeData.target)
PLY.draw.drawTargetLine(P,200-P.stat.row)
end,
task=function(P)
P.modeData.target=10
end,
hook_drop=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target==200 then
P:win('finish')
else
if P.modeData.target==100 then
P.modeData.lock=4
end
P.gameEnv.wait=waitSpeed[P.modeData.target/10]
P.modeData.target=P.modeData.target+10
SFX.play('reach')
end
end
end
}

View File

@@ -21,7 +21,6 @@ return{
if D.wave==60 then
P:_showText(text.maxspeed,0,-140,100,'appear',.6)
end
P:shakeField(3)
D.timer=0
D.wave=D.wave+1
end

View File

@@ -24,7 +24,6 @@ return{
if D.wave==30 then
P:_showText(text.maxspeed,0,-140,100,'appear',.6)
end
P:shakeField(9)
D.timer=0
D.wave=D.wave+1
end

View File

@@ -4,7 +4,7 @@ return{
mStr(P.stat.clear[7][4],63,250)
mText(TEXTOBJ.techrash,63,315)
end,
hook_drop=function(P)
dropPiece=function(P)
if P.lastPiece.row>0 and P.lastPiece.row<4 then
P:lose()
end

View File

@@ -8,12 +8,12 @@ return{
PLY.draw.applyField(P)
local L=P.modeData.history
for i=1,#L do
gc.setColor(1,.3,.3,.5-i*.04)
gc.setColor(1,.3,.3,.45-i*.04)
gc.rectangle('fill',30*L[i]-30,0,30,600)
end
PLY.draw.cancelField(P)
end,
hook_drop=function(P)
dropPiece=function(P)
local C=P.lastPiece
if C.row>0 then
if C.row==4 then

View File

@@ -4,7 +4,7 @@ return{
mStr(P.modeData.tsd,63,250)
mText(TEXTOBJ.tsd,63,315)
end,
hook_drop=function(P)
dropPiece=function(P)
local C=P.lastPiece
if C.row>0 then
if C.id==5 and C.row==2 and C.spin then

View File

@@ -13,7 +13,7 @@ return{
PLY.draw.cancelField(P)
end
end,
hook_drop=function(P)
dropPiece=function(P)
local C=P.lastPiece
if C.row>0 then
if C.id==5 and C.row==2 and C.spin then

View File

@@ -8,12 +8,12 @@ return{
PLY.draw.applyField(P)
local L=P.modeData.history
for i=1,#L do
gc.setColor(1,.3,.3,.4-i*.05)
gc.setColor(1,.3,.3,.3-i*.05)
gc.rectangle('fill',30*L[i]-30,0,30,600)
end
PLY.draw.cancelField(P)
end,
hook_drop=function(P)
dropPiece=function(P)
local C=P.lastPiece
if C.row>0 then
if C.id==5 and C.row==2 and C.spin then

View File

@@ -1,34 +1,24 @@
local gc=love.graphics
local warnTime={60,90,105,115,116,117,118,119,120}
for i=1,#warnTime do warnTime[i]=warnTime[i]*60 end
return{
mesDisp=function(P)
gc.setLineWidth(2)
gc.setColor(.98,.98,.98,.8)
gc.rectangle('line',0,260,126,80,4)
gc.setColor(.98,.98,.98,.4)
gc.rectangle('fill',0+2,260+2,126-4,80-4,2)
setFont(45)
local t=P.stat.frame/60
local T=("%.1f"):format(120-t)
gc.setColor(COLOR.dH)
mStr(T,65,270)
t=t/120
gc.setColor(1.7*t,2.3-2*t,.3)
mStr(T,63,268)
gc.rectangle('line',55,110,32,402)
local T=P.stat.frame/60/120
gc.setColor(2*T,2-2*T,.2)
gc.rectangle('fill',56,511,30,(T-1)*400)
end,
task=function(P)
BGM.seek(0)
P.modeData.section=1
P.modeData.stage=1
while true do
YIELD()
while P.stat.frame>=warnTime[P.modeData.section]do
if P.modeData.section<9 then
P.modeData.section=P.modeData.section+1
playReadySFX(3,.7+P.modeData.section*.03)
if P.stat.frame/60>=warnTime[P.modeData.stage]then
if P.modeData.stage<9 then
P.modeData.stage=P.modeData.stage+1
SFX.play('ready',.7+P.modeData.stage*.03)
else
playReadySFX(0,.7+P.modeData.section*.03)
SFX.play('start')
P:win('finish')
return
end

Binary file not shown.

Binary file not shown.

37
parts/freeRow.lua Normal file
View File

@@ -0,0 +1,37 @@
local FREEROW={}
local L={}--Storage
local len=0--Length
function FREEROW.reset(num)
if num<len then
for i=len,num+1,-1 do
L[i]=nil
end
elseif num>len then
for i=len+1,num do
L[i]={0,0,0,0,0,0,0,0,0,0,garbage=false}
end
end
len=num
end
function FREEROW.get(val,ifGarbage)
if len==0 then
for i=1,10 do
L[i]={0,0,0,0,0,0,0,0,0,0,garbage=false}
end
len=len+10
end
local t=L[len]
for i=1,10 do t[i]=val end
t.garbage=ifGarbage==true
L[len]=nil
len=len-1
return t
end
function FREEROW.discard(t)
len=len+1
L[len]=t
end
function FREEROW.getCount()
return len
end
return FREEROW

View File

@@ -15,48 +15,6 @@ local playSFX=SFX.play
--System
do--function loadFile(name,args), function saveFile(data,name,args)
local t=setmetatable({},{__index=function()return"'$1' loading failed: $2"end})
function loadFile(name,args)
local text=text or t
if not args then args=''end
local res,mes=pcall(FILE.load,name,args)
if res then
return mes
else
if mes:find'open error'then
MES.new('error',text.loadError_open:repD(name,""))
elseif mes:find'unknown mode'then
MES.new('error',text.loadError_errorMode:repD(name,args))
elseif mes:find'no file'then
if not args:sArg'-canSkip'then
MES.new('error',text.loadError_noFile:repD(name,""))
end
elseif mes then
MES.new('error',text.loadError_other:repD(name,mes))
else
MES.new('error',text.loadError_unknown:repD(name,""))
end
end
end
function saveFile(data,name,args)
local text=text or t
local res,mes=pcall(FILE.save,data,name,args)
if res then
return mes
else
MES.new('error',
mes:find'duplicate'and
text.saveError_duplicate:repD(name)or
mes:find'encode error'and
text.saveError_encode:repD(name)or
mes and
text.saveError_other:repD(name,mes)or
text.saveError_unknown:repD(name)
)
end
end
end
function isSafeFile(file,mes)
if love.filesystem.getRealDirectory(file)~=SAVEDIR then
return true
@@ -65,13 +23,13 @@ function isSafeFile(file,mes)
end
end
function saveStats()
return saveFile(STAT,'conf/data')
return FILE.save(STAT,'conf/data')
end
function saveProgress()
return saveFile(RANKS,'conf/unlock')
return FILE.save(RANKS,'conf/unlock')
end
function saveSettings()
return saveFile(SETTING,'conf/settings')
return FILE.save(SETTING,'conf/settings')
end
function applyLanguage()
text=LANG.get(SETTING.locale)
@@ -248,30 +206,34 @@ function playClearSFX(cc)
end
end
end
function playReadySFX(i,vol)
function playReadySFX(i)
if i==3 then
Snd('bass','A3',vol)
Snd('lead','A4',vol)
Snd('bass','A3')
Snd('lead','A4')
elseif i==2 then
Snd('bass','F3',vol)
Snd('lead','A4',vol)
Snd('lead','D5',vol)
Snd('bass','F3')
Snd('lead','A4')
Snd('lead','D5')
elseif i==1 then
Snd('bass','G3',vol)
Snd('lead','B4',vol)
Snd('lead','E5',vol)
Snd('bass','G3')
Snd('lead','B4')
Snd('lead','E5')
elseif i==0 then
Snd('bass','A3',vol)
Snd('lead','A4',vol)
Snd('lead','E5',vol)
Snd('lead','A5',vol)
Snd('bass','A3')
Snd('lead','A4')
Snd('lead','E5')
Snd('lead','A5')
end
end
--Game
function getItem(itemName,amount)
STAT.item[itemName]=STAT.item[itemName]+(amount or 1)
function coin(a,b)
if rnd()<.5 then
return a
else
return b
end
end
function generateLine(hole)
return 1023-2^(hole-1)
@@ -295,8 +257,8 @@ function setField(P,page)
local t=P.showTime*3
for y=1,height do
local notEmpty=notEmptyLine(F[y])
P.field[y]=LINE.new(0,notEmpty)
P.visTime[y]=LINE.new(t)
P.field[y]=FREEROW.get(0,notEmpty)
P.visTime[y]=FREEROW.get(t)
if notEmpty then
for x=1,10 do
P.field[y][x]=F[y][x]
@@ -305,19 +267,17 @@ function setField(P,page)
end
end
end
function freshDate(args)
if not args then
args=""
function freshDate(mode)
if not mode then
mode=""
end
local date=os.date("%Y/%m/%d")
if STAT.date~=date then
STAT.date=date
STAT.todayTime=0
getItem('zTicket',1)
if not args:find'q'then
if not mode:find'q'then
MES.new('info',text.newDay)
end
saveStats()
return true
end
end
@@ -339,15 +299,6 @@ function legalGameTime()--Check if today's playtime is legal
end
return true
end
do--function trySettingWarn()
local lastWarnTime=0
function trySettingWarn()
if TIME()-lastWarnTime>2.6 then
MES.new('warn',text.settingWarn,5)
end
lastWarnTime=TIME()
end
end
function mergeStat(stat,delta)--Merge delta stat. to global stat.
for k,v in next,delta do
@@ -380,8 +331,8 @@ function destroyPlayers()--Destroy all player objects, restore freerows and free
P.canvas:release()
end
while P.field[1]do
rem(P.field)
rem(P.visTime)
FREEROW.discard(rem(P.field))
FREEROW.discard(rem(P.visTime))
end
end
TABLE.cut(PLAYERS)
@@ -517,7 +468,7 @@ function gameOver()--Save record
D.date=os.date("%Y/%m/%d %H:%M")
ins(L,p+1,D)
if L[11]then L[11]=nil end
saveFile(L,('record/%s.rec'):format(M.name),'-luaon')
FILE.save(L,('record/%s.rec'):format(M.name),'l')
end
end
end
@@ -717,6 +668,7 @@ do--function resetGameData(args)
GAME.secDangerous=false
GAME.stage=1
end
FREEROW.reset(30*#PLAYERS)
TASK.removeTask_code(task_showMods)
if GAME.setting.allowMod then
TASK.new(task_showMods)
@@ -869,20 +821,13 @@ do--function pressKey(k)
end
do--CUS/SETXXX(k)
local CUSTOMENV=CUSTOMENV
local warnList={
'das','arr','dascut','dropcut','sddas','sdarr',
'ihs','irs','ims','RS',
'FTLock','frameMul','highCam',
'VKSwitch','VKIcon','VKTrack','VKDodge',
'simpMode',
}
function CUSval(k)return function()return CUSTOMENV[k]end end
function ROOMval(k)return function()return ROOMENV[k]end end
function SETval(k)return function()return SETTING[k]end end
function CUSrev(k)return function()CUSTOMENV[k]=not CUSTOMENV[k]end end
function ROOMrev(k)return function()ROOMENV[k]=not ROOMENV[k]end end
function SETrev(k)return function()if TABLE.find(warnList,k)then trySettingWarn()end SETTING[k]=not SETTING[k]end end
function SETrev(k)return function()SETTING[k]=not SETTING[k]end end
function CUSsto(k)return function(i)CUSTOMENV[k]=i end end
function ROOMsto(k)return function(i)ROOMENV[k]=i end end
function SETsto(k)return function(i)if TABLE.find(warnList,k)then trySettingWarn()end SETTING[k]=i end end
function SETsto(k)return function(i)SETTING[k]=i end end
end

View File

@@ -20,129 +20,140 @@ RANK_COLORS={
{1,.5,.4},
{.95,.5,.95},
}
do--SVG_TITLE_FILL, SVG_TITLE_LINE
SVG_TITLE_FILL={
do--SVG_TITLE
SVG_TITLE={
{
0,0,
0,34,
63,34,
63,227,
97,227,
97,34,
160,34,
160,0,
53, 60,
1035, 0,
964, 218,
660, 218,
391, 1300,
231, 1154,
415, 218,
0, 218,
},
{
126,60,
244,60,
244,94,
160,94,
160,127,
230,127,
230,161,
160,161,
160,194,
244,194,
244,227,
126,227,
716, 290,
1429, 290,
1312, 462,
875, 489,
821, 695,
1148, 712,
1017, 902,
761, 924,
707, 1127,
1106, 1101,
1198, 1300,
465, 1300,
},
{
262,82,
283,60,
385,60,
385,94,
296,94,
296,194,
385,194,
385,227,
283,227,
262,206,
1516, 287,
2102, 290,
2036, 464,
1598, 465,
1322, 905,
1395, 1102,
1819, 1064,
1743, 1280,
1286, 1310,
1106, 902,
},
{
404,60,
437,60,
437,127,
505,127,
505,60,
538,60,
538,227,
505,227,
505,161,
437,161,
437,227,
404,227,
2179, 290,
2411, 290,
2272, 688,
2674, 666,
2801, 290,
3041, 290,
2693, 1280,
2464, 1280,
2601, 879,
2199, 897,
2056, 1280,
1828, 1280,
},
{
558,60,
604,60,
640,153,
676,60,
722,60,
722,227,
688,227,
688,108,
655,194,
625,194,
591,108,
591,227,
558,227,
3123, 290,
3480, 290,
3496, 480,
3664, 290,
4017, 294,
3682, 1280,
3453, 1280,
3697, 578,
3458, 843,
3304, 842,
3251, 561,
3001, 1280,
2779, 1280,
},
{
743,60,
777,60,
777,227,
743,227,
4088, 290,
4677, 290,
4599, 501,
4426, 502,
4219, 1069,
4388, 1070,
4317, 1280,
3753, 1280,
3822, 1068,
3978, 1068,
4194, 504,
4016, 504,
},
{
798,60,
831,60,
899,173,
899,60,
933,60,
933,227,
899,227,
831,115,
831,227,
798,227,
4747, 290,
4978, 295,
4921, 464,
5186, 850,
5366, 290,
5599, 295,
5288, 1280,
5051, 1280,
5106, 1102,
4836, 709,
4641, 1280,
4406, 1280,
},
{
950,82,
971,60,
1064,60,
1085,82,
1085,206,
1064,227,
971,227,
950,206,
950,82,
984,94,
984,194,
1051,194,
1051,94,
984,94,
5814, 290,
6370, 295,
6471, 415,
6238, 1156,
6058, 1280,
5507, 1280,
5404, 1154,
5635, 416,
-- 5814, 290,
-- 5878, 463,
5770, 542,
5617, 1030,
5676, 1105,
5995, 1106,
6100, 1029,
6255, 541,
6199, 465,
5878, 463,
},
}
for _,C in next,SVG_TITLE_FILL do
for _,C in next,SVG_TITLE do
for i=1,#C do
C[i]=C[i]*.94
C[i]=C[i]*.1626
end
end
SVG_TITLE_LINE=TABLE.shift(SVG_TITLE_FILL)
SVG_TITLE_LINE[8],SVG_TITLE_LINE[9]={},{}
for j=1,16 do SVG_TITLE_LINE[8][j]=SVG_TITLE_FILL[8][j]end
for j=19,#SVG_TITLE_FILL[8]-2 do SVG_TITLE_LINE[9][j-18]=SVG_TITLE_FILL[8][j]end
end
do--SVG_TITLE_FAN
SVG_TITLE_FAN={}
local sin,cos=math.sin,math.cos
for i=1,9 do
local L=TABLE.copy(SVG_TITLE_LINE[i])
for i=1,8 do
local L={}
SVG_TITLE_FAN[i]=L
for j=1,#SVG_TITLE[i]do
L[j]=SVG_TITLE[i][j]
end
for j=1,#L,2 do
local x,y=L[j],L[j+1]--0<x<988, 290<y<1280
x,y=-(x+280)*.002,(y-580)*.9--X=ang, Y=dist
local x,y=L[j],L[j+1]--0<x<3041, 290<y<1280
x,y=-(x+240+y*.3)*.002,(y-580)*.9
x,y=y*cos(x),-y*sin(x)--Rec-Pol-Rec
L[j],L[j+1]=x,y+300
end
@@ -334,6 +345,7 @@ EVENTSETS={
'marathon_n','marathon_h',
'master_n','master_h','master_final','master_m','master_ex','master_ph',
'pctrain_n','pctrain_l','pc_inf',
'rhythm_e','rhythm_h','rhythm_u',
'survivor_e','survivor_n','survivor_h','survivor_l','survivor_u',
'tsd_e','tsd_h','tsd_u',
'ultra',
@@ -536,12 +548,13 @@ do--Game data tables
ROOMENV={
--Room config
capacity=10,
FTLock=true,
--Basic
drop=30,lock=60,
wait=0,fall=0,
hang=5,hurry=1e99,
drop=30,
lock=60,
wait=0,
fall=0,
FTLock=true,
--Control
nextCount=6,
@@ -714,10 +727,6 @@ do--Userdata tables
spin=(function()local L={}for i=1,29 do L[i]={0,0,0,0,0,0,0}end return L end)(),
pc=0,hpc=0,b2b=0,b3b=0,score=0,
lastPlay='sprint_10l',--Last played mode ID
item=setmetatable({},{__index=function(self,k)
self[k]=0
return 0
end}),
date=false,
todayTime=0,
}

View File

@@ -4,7 +4,7 @@ return{
{"Translator Note 1",
"",
"help",
"This translation of the TetroDictionary is provided by me, User670 (Discord: User670#9501).\n\nThe translation may not completely reflect the contents of the original Chinese text.\n\nCorrected by C₂₉H₂₅N₃O₅ (Discord: C29H25N3O5#1606).\n\nTo view the list of contributors or make contributions, feel free to visit the GitHub page.",
"This translation of the TetroDictionary is provided by me, User670 (Discord: User670#9501).\n\nThe translation may not completely reflect the contents of the original Chinese text.\n\nCorrected by C29H25N3O5.\n\nTo view the list of contributors or make contributions, feel free to visit the GitHub page.",
"https://github.com/26F-Studio/Techmino/blob/main/parts/language/dict_en.lua",
},
{"Official Website",
@@ -16,8 +16,8 @@ return{
{"To New Players",
"guide newbie noob",
"help",
"To new players that want to get better at the game:\n\tTwo principles:\n\t1. find a version with good controls (e.g. Techmino, Tetr.io, Tetris Online, Jstris, Tetr.js). Do not use those version used for programming practice.\n\t2. Build foundations in your skills (stable Techrashes using next queue to aid decisions), don't go for fancy T-Spins from the start.\n\n\tTwo main techniques:\n\t1. familiarize yourself with spawn locations of pieces, and the controls to move the piece into each location\n\t2. Plan ahead of where to put the pieces\nHere is a article written by a well-known player in Chinese Tetris community talking about advices to new players. Click the globe to read the translated article by User670.",
"https://github.com/user670/temp/blob/master/tips_to_those_new_to_top.md",
"To new players that want to get better at the game:\n\tTwo principles:\n\t1. find a version with good controls (e.g. Techmino, Tetr.io, Tetris Online, Jstris, Tetr.js). Do not use those version used for programming practice.\n\t2. Build foundations in your skills (stable Techrashes using next queue to aid decisions), don't go for fancy T-Spins from the start.\n\n\tTwo main techniques:\n\t1. familiarize yourself with spawn locations of pieces, and the controls to move the piece into each location\n\t2. Plan ahead of where to put the pieces\nWe recommend that you read this article titled \"Words to newbies from a Tetris Pro\" (Click \"Open URL\". In Simplified Chinese)",
"https://bilibili.com/read/cv2352939",
},
{"Learning T-spins",
"tspin learning study guide tips",
@@ -55,7 +55,7 @@ return{
"https://tetris.huijiwiki.com",
},
--Webpages / Organizations
--Organizations
{"Github Repository",
"githubrepository sourcecode",
"org",
@@ -65,7 +65,7 @@ return{
{"Communities",
"community communities",
"org",
"Join Tetris communities and talk with other players!\n\nChina: [QQ] Tetris Research: 112897780\nGlobal: [Discord] Hard Drop: discord.gg/harddrop (click the globe icon to open).",
"Join Tetris communities and talk with other players!\n\nChina: [QQ] Tetris Research: 112897780\nGlobal: [Discord] Hard Drop: discord.gg/harddrop (click \"Open URL\").",
"https://discord.gg/harddrop"
},
{"Mew",
@@ -77,15 +77,10 @@ return{
{"Tetris OL Servers",
"tetrisonline servers",
"org",
"Google \"Tetris Online Poland\" for the Poland server.\nClick on the globe icon for information about the Tetris Online Study server.",
"Google \"Tetris Online Poland\" for the Poland server.\nClick \"Open URL\" for information about the Tetris Online Study server.",
"http://teatube.ltd/tos",
},
{"P\97\116\114\101\111\110",
"p\97\116\114\101\111\110 support",
"org",
"Techmino's P\97\116\114\101\111\110 Page",
FNSF and"https://www.youtube.com/watch?v=DVl0IiUKX_g"or"https://www.p\97\116\114\101\111\110.com/techmino",
},
--Games
{"TTT",
"ttt tetris trainer tres bien",
@@ -167,7 +162,7 @@ return{
{"Tetris Gems",
"tetris online official gem",
"game",
"Another Tetris game from tetris.com. It has the gravity mechanism, and each game lasts for 1 minute. There are three kinds of gem blocks with different abilities.",
"Another Tetris game from tetris.com. It has the gravity mechanism, and each game lasts for 2 minutes. There are three kinds of gem blocks with different abilities.",
},
{"Tetris Mind Bender",
"tetris online official gem",
@@ -195,11 +190,6 @@ return{
"game",
"*Windows | Single-player*\nA game with all modes from TGM which you can use to practice. Has better controls than actual TGM. The world rule is slightly different, however (eg, instant-lock soft drops, and slightly different kick tables)",
},
{"Cambridge",
"cambridge",
"game",
"*Windows, macOS, Linux | Single-player*\nA Lua-based game engine dedicated to creating a robust, easily customizable platform for creating new, custom game modes. It was originally made by Joe Zeng, and starting with version 0.1.5 on October 8, 2020, Milla took over development of the game.\n--Tetris Wiki",
},
{"Tetris99",
"t99 tetris99",
@@ -259,17 +249,17 @@ return{
{"Tetris Blitz",
"blitz ea mobile phone",
"game",
"A mobile Tetris game by Electronic Arts (EA). It has the gravity mechanism, and each game lasts for 2 minutes. A bunch of minoes fall down to the field at the beginning of the game, and you can enter the \"Frenzy\" mode by performing line clears continuously. There are many different power-ups available. Also, this game has no top-out mechanism. When an incoming block overlaps with existing blocks in the field, the top lines will be cleared automatically. \n\nThis game is no longer available since April 2020.",
"A mobile Tetris game by Electronic Arts (EA). It has the gravity mechanism, and each game lasts for 2 minutes. A bunch of minoes fall down to the field at the beginning of the game, and you can enter the \"Frenzy\" mode by performing Tetris line clears continuously. There are many different power-ups available.\n\nThis game is no longer available since January 2020.",
},
{"Tetris (EA)",
"tetris ea galaxy universe cosmos mobile phone",
"game",
"Another mobile Tetris game by EA. It has two control modes Swipe and One-Touch. It also has a Galaxy Mode besides the Marathon Mode (with gravity mechanism), and the goal of this mode is to clear all Galaxy minoes before the sequence runs out.\n\nThis game is no longer available since April 2020."
},
{"Tetris (N3TWORK)",
{"TetrisN3TWORK",
"tetris n3twork mobile phone",
"game",
"The latest mobile Tetris from N3TWORK Inc. It has a 3-minute ultra mode, a marathon mode and a 100-player Royale mode. The UI is great but its controls are not so good.",
"The latest mobile Tetris from N3TWORK Inc. It has a 3-minute ultra mode, a marathon mode and a Royale mode. The UI is great but its controls are not so good.",
},
{"Tetris Beat",
"tetris beat n3twork rhythm",
@@ -312,7 +302,7 @@ return{
{"BPM",
"bpm blocksperminute piecesperminute speed",
"term",
"Blocks per minute\n\tReflects playing speed of a player.\nAlso called PPM (to avoid confusing with the musical term).",
"Blocks per minute\n\tReflects playing speed of a player.\nAlso MrZ forgot that it can be called PPM to avoid confusing with the music term.",
},
{"KPM",
"kpm keysperminute keypressesperminute",
@@ -378,7 +368,7 @@ return{
{"Tetris",
"tetris",
"term",
"The name of the game (and its trademark). Also the name for clearing 4 lines at one time in official games.\nCoined from Tetra (greek for \"four\") and Tennis (favorite sport of the creator of Tetris). Also, the Tetris games developed by Nintendo and SEGA was licensed by TTC and these two companies do not have the copyright of Tetris.",--Thanks to Alexey Pajitnov!
"The name of the game (and its trademark). Also the name for clearing 4 lines at one time in official games.\nCoined from Tetra (greek for \"four\") and Tennis (favorite sport of the creator of Tetris).",--Thanks to Alexey Pajitnov!
},
{"All Clear",
"pc perfectclear ac allclear",
@@ -545,7 +535,7 @@ return{
{"IMS",
"ims initialmovesystem",
"term",
"*Techmino-exclusive*\nInitial Movement System\nHolding a sideways movement key during spawn delay to spawn the piece one block off to the side. Sometimes prevents death.\nNote that DAS need to be full charged when new piece appear",
"*Techmino-exclusive*\nInitial Movement System\nHolding a sideways movement key during spawn delay to spawn the piece one block off to the side. Sometimes prevents death.",
},
{"Next",
"nextpreview",
@@ -592,7 +582,7 @@ return{
"term",
"A terminology used in the Chinese Tetris community. A \"debt\" refers to a situation where one must first finish constructing a specific setup before he or she can perform one or more T-spins with real attacks. When constructing a setup where one or multiple debts are created, it is important to observe the opponent carefully to ensure your safety; otherwise, there is a high probability of topping out before the construction is finished.\n\nThis term is frequently used to describe setups such as TST tower. No real attacks can be made before the setup is constructed completely.",
},
{"Attack & Defend",
{"Attacking & Defending",
"attacking defending",
"term",
"Attacking: send garbage lines to your opponent by clearing lines.\nDefending: after your opponent send you lines, you offset this garbage by clearing lines.\nCounter attack: Send attack back at your opponent after offsetting incoming garbage, or taking the hit then attack back.\nIn most games, garbage offsetting is 1:1, i.e. one attack offsets one incoming garbage.",
@@ -662,36 +652,20 @@ return{
"term",
"A way of stacking where you have a 6-block-wide stack on the left, and a 3-block-wide stack on the right.\nFor a skilled player, this method of stacking might reduce the keypresses needed for stacking, and is a popular Sprint stacking method. The reason why it works has to do with the fact that pieces spawn with a bias to the left.",
},
{"Freestyle",
"freestyle ziyou",
{"20G",
"20g",
"term",
"This term is usually used in 20TSDs. Freestyle means finishing 20 TSDs without using static stacking modes. Freestyle 20TSDs is more difficult than static tsacking modes such as LST, and the performance can represent the T-spin skills a player has in battles.",
"The fastest falling speed of modern Tetris. In 20G, pieces do not have a falling process and instantly appear on the bottom. This sometimes also limits a piece's sideways movements, as it is not always possible to make a piece climb over a bump or out of a well in 20G.",
},
{"Topping out",
"die death topout toppingout",
"term",
"Modern Tetris games have three different conditions in which the player tops out:\n1. Block out: when a piece spawned overlaps with the existing blocks in the field;\n2. Lock out: when a piece locks entirely above the skyline;\n3. Top out: when the stack exceeds 40 lines in height (often due to incoming garbage).\nTechmino does not check for locking out and topping out.",
},
{"Buffer zone",
"buffer zone above super invisible disappear",
"term",
"Refers to 21st-40th lines above the visible field. Because the blocks in the field could go over the visible field (this usually happens when multiple garbage lines come in) so the buffer zone was created so those blocks could go back to the field when garbage lines are cleared. Also, the buffer zone is usually located at 21st-40th lines because this is sufficient for most cases. Refer to \"Vanish Zone\" to learn more.",
},
{"Vanish zone",
"vanish zone disappear gone cut die",
"term",
"Refers to the area located above the 40th line. This is usually realised by combining c4w and multiple garbage lines. In many games, when any block reaches the vanish zone, the game is terminated immediately.\nHowever, this area can have different behaviours in different games. Some games are flawed because the game could crash when the blocks enter the vanish zone (e.g. Tetris Online). Wierd behaviours could also happen when the blocks enter the vanish zone (you can refer to this video, click on the globe icon to open the link).\n\nFurthermore, the vanish zone in Jstris is located above the 22nd line, and any blocks locked above the 21st line will disappear. ",
"https://youtu.be/z4WtWISkrdU",
"Modern Tetris games have three different conditions in which the player tops out:\n1. Block out: when a piece spawned overlaps with the existing blocks in the field;\n2. Lock out: when a piece locks entirely above the skyline;\n3. Garbage out: when the stack exceeds 40 lines in height (often due to incoming garbage).\nTechmino does not check for locking out and garbage out.",
},
{"Falling speed",
"fallingspeed gravity",
"fallingspeed",
"term",
"Falling speed is often described in terms of \"G\", i.e. how many lines the blocks fall in one frame (usually assuming 60 fps).\nG is a relatively large unit. The speed of Lv 1 in a regular Marathon (one second per line) is 1/60 G, and 1G is about Lv 13 speed. The highest speed of modern Tetris is 20G because the field height is 20 lines. In fact, the real meaning of 20G is \"Infinite falling speed\", and even when the field height is more than 20 lines, 20G modes force all the blocks to fall down to the bottom instantly. You can learn more about 20G at the \"20G\" entry.",
},
{"20G",
"20g gravity instant",
"term",
"The fastest falling speed of modern Tetris. In 20G modes, pieces appear instantly on the bottom of the field without the actual process of \"falling down\". This sometimes also limits a piece's sideways movements, as it is not always possible to make a piece climb over a bump or out of a well in 20G. You can learn more at the unit \"G\" at the \"falling speed\" entry. ",
"Falling speed is often described in terms of G, i.e. how many lines it falls in one frame (often assuming 60 frames per second).\nG is a large unit. The speed of Lv 1 in a regular Marathon (one second per line) is 1/60 G, and 1G is about Lv 13 speed. G usually caps at 20G, for there are only 20 (visible) blocks in the matrix's height.",
},
{"Lockdown Delay",
"lockdelay lockdowndelay lockdowntimer",
@@ -703,16 +677,11 @@ return{
"term",
"Sometimes called the Entry Delay. ARE refers to the delay between the lockdown of one piece and the spawn of another piece.",
},
{"Line ARE",
{"line ARE",
"line are appearance delay",
"term",
"The delay between the start of a line clear animation to the spawn of the next piece.",
},
{"Death ARE",
"death are die delay",
"term",
"(Techmino exclusive) When the spawn location of the next piece is blocked by an existing block in the field, a delay will be added in addition to the spawn ARE, and this delay is referred to as the death ARE. This mechanism can be used along with IHS and IRS to prevent death. \nOriginal idea by NOT_A_ROBOT",
},
{"Finesse",
"finesse",
"term",
@@ -833,16 +802,6 @@ return{
"term",
"The block skin used by the earliest version of Tetris.\nIn the early times, computers were all using Command Line Interface instead of Graphical User Interface, so at that time a single mino in the game of Tetris is represented using two enclosing square brackets [ ]. It looks kinds of like bones so it is sometimes called the bone blocks.\nIn Techmino, bone blocks are defined as \"A single, fancy block skin that all of the blocks use.\". Different block skins may have different types of bone block styles.",
},
{"Semi-invisible",
"half invisible semi",
"term",
"Refers to a rule where the tetrominoes will become invisible after a period of time.\nThis time interval is not definite and it is acceptable to describe it as \"disappear after a few seconds\".",
},
{"Invisible",
"invisible",
"term",
"Refers to a rule where blocks will disappear instantly when locked onto the field. \nN.B. It is also acceptable to refer to an invisible mode where a disappearing animation is shown. However, this makes the game a lot easier, so in this game, the invisible mode without such animations is referred to as \"Sudden Invisible\".",
},
{"MPH mode",
"mph",
"term",

Some files were not shown because too many files have changed in this diff Show More