Compare commits

...

138 Commits

Author SHA1 Message Date
C29H25N3O5
f9ee43b1f0 Symbol update and more
- Added a few more icon symbols
- Added symbols for playing cards
- Added a note for translators in the English Zictionary
2024-01-21 00:04:35 -06:00
C29H25N3O5
bb2ede229c Merge branch 'main' of https://github.com/C29H25N3O5/Techmino 2024-01-20 22:45:26 -06:00
C6H12O6 + NaCl + H2O
2d12ab3a19 Log viewer (#1076)
* Add scene

* Modify app_console.lua

* Add fix ability to viewlog in case log got messed a bit
2024-01-16 15:19:19 +08:00
C₂₉H₂₅N₃O₅
efeb24b0f0 Updated monospaced font (#1084)
Redesigned the monospaced font based on the main UI font of the game (Exo 2).
2024-01-16 15:09:45 +08:00
C29H25N3O5
0e8ddee6c5 Update Monospaced font
Redesigned the monospaced font based on the main UI font of the game (Exo 2).
2024-01-15 17:40:22 -06:00
C₂₉H₂₅N₃O₅
bf0bb1d47b Merge branch '26F-Studio:main' into main 2024-01-15 16:20:37 -06:00
MrZ_26
bce60ee6c7 修卡块旋转音效播放条件错误 2024-01-14 22:36:55 +08:00
MrZ_26
b0a47f5222 更新赞助名单 2024-01-14 22:36:55 +08:00
sakurw
58bac2e290 Update lang_ja.lua (#1075) 2024-01-09 08:55:07 +08:00
MrZ_26
84361c3c31 新的mod开关音效 2024-01-04 21:28:26 +08:00
MrZ_26
d0a7a28349 补充新功能需要的文本 2024-01-01 00:32:56 +08:00
MrZ_26
10b8891cf0 修改更新历史 2023-12-31 23:37:20 +08:00
MrZ_26
3a4fcf6d7b 越南语变音字母简化函数改为全unicode版本
限制自定义菜单背景选择范围
微调自定义背景设置ui
整理代码
2023-12-31 23:32:19 +08:00
C6H12O6 + NaCl + H2O
19ea76dc4c No theme and default BG and patch BG bug when hitting F11 (#1070)
* Add setting slot for no theme & default BG

* Whitelist 4 seasons theme + blocking console's theme command if theme disabled

* Small update theme.lua

* Expose the WIDGET.setOnChange() to use in setting_video.lua

* Shorten "Use custom B.G." to "Custom B.G."

* Shorten theme code, and edit the UI

* Add lock BG

* Add VI translation

* Fullscreen patch

* Modify UI

* Small change

* Undo a mistake
2023-12-31 21:35:48 +08:00
C6H12O6 + NaCl + H2O
42942d1ac4 Multiple changes around Zictionary (mostly for Vietnamese) (#1069)
* Multiple changes
- Edit Vietnamese Zictionary
- Add STRING.viRemoveDiacritics()
- Edit the search behavior with string written in Vietnamese without diacritics

* Small changes

* Update lang_vi.lua

* Small change
2023-12-31 20:58:59 +08:00
MrZ_26
43215dfc4b 补充联网房间设置可选值 2023-12-21 00:51:18 +08:00
MrZ_26
5728194085 添加新的skip暂存模式,可在自定义使用 2023-12-21 00:51:09 +08:00
MrZ_26
1501ebd92a 修上个commit没删干净 2023-12-20 23:58:22 +08:00
MrZ_26
0299fde47b 删除ios/macos和appstore相关的支持 2023-12-20 23:46:40 +08:00
Imple Lee
11680aedbb set ghost Y when trying to hang with the current piece (#1066) 2023-12-20 23:30:40 +08:00
Imple Lee
ad0b73ff62 remove CI on iOS and Mac App Store (#1062) 2023-12-20 23:30:21 +08:00
MrZ_26
f38746ff96 直接放入Zframework文件 2023-12-04 20:02:12 +08:00
MrZ_26
8ee7a90eca 删除Zframework子模块 2023-12-04 19:58:56 +08:00
MrZ_26
d06d8ebfff 限制按键的极简模式从地图改为隐藏模式,目标改为消除40行 close #1060 2023-12-04 19:50:47 +08:00
Imple Lee
d3e4d7e62c add hold i_s from next (#1061) 2023-12-04 19:23:01 +08:00
MrZ_26
80ff30401a 修合并冲突错误 2023-11-26 21:45:41 +08:00
MrZ_26
4657515b61 Merge commit '2604d034fe1b220fb952fa6ba13c11c09b36cdb7' 2023-11-26 21:44:20 +08:00
MrZ_26
90dcb9ee1a 优化掉玩家的_get_new_block方法 2023-11-26 21:40:15 +08:00
Imple Lee
4e606f4e91 split spawn and popNext 2023-11-22 18:00:00 +08:00
sakurw
9c0dc60746 Update lang_ja.lua (#1054) 2023-11-21 17:46:09 +08:00
C6H12O6 + NaCl + H2O
58a5749d67 Update Vietnamese translation (#1051) 2023-11-21 17:45:05 +08:00
User670
7d0d89f1a6 Changed a wording in new string (#1053) 2023-11-21 17:43:19 +08:00
C6H12O6 + NaCl + H2O
f92121f093 Fix (#1052) 2023-11-21 17:42:49 +08:00
Imple Lee
2604d034fe add early return in freshBlockGhost 2023-11-21 15:10:37 +08:00
Imple Lee
3219da77e9 refactor freshBlockDelay 2023-11-21 15:07:28 +08:00
Imple Lee
ea3fb8d09e split freshBlock into 4 methods 2023-11-20 23:36:38 +08:00
Imple Lee
074cefed5d Make unable to read update log a hard error (#1050) 2023-11-17 20:59:41 +08:00
MrZ_26
7070e620c8 调整上个commit的相关内容 2023-11-17 15:11:13 +08:00
User670
1cd62bb163 Add checks and error messages for some invalid custom mode data (#1049) 2023-11-17 14:06:30 +08:00
Imple Lee
529b8d453d update field after loading AI (#1048)
make the bot know the current situation
2023-11-17 01:00:55 +08:00
C6H12O6 + NaCl + H2O
486eaeae52 Do optimization in Piano applet (#1047) 2023-11-17 00:10:21 +08:00
MrZ_26
a2931ea290 更新workflow中涉及读取updateLog的部分 2023-11-14 21:47:50 +08:00
C6H12O6 + NaCl + H2O
edb79e8156 Update translation (#1046) 2023-11-14 14:54:05 +08:00
MrZ_26
ed5abe0350 自定义场地高度上限改为126 close #1045 2023-11-14 13:56:28 +08:00
MrZ_26
3806f02fa5 优化textReader场景和相关方法
框架跟进
2023-11-13 18:23:27 +08:00
Imple Lee
7d23fe4acb fix many reference-vs-value error (#1044)
fixes #1039
completes #1007
2023-11-13 12:20:24 +08:00
C6H12O6 + NaCl + H2O
c650f268f6 Make a scene for showing full text (like manual) but for convenience use (#1043) 2023-11-13 12:15:58 +08:00
MrZ_26
55800e307f 场地编辑界面显示隐形块 2023-11-08 22:23:01 +08:00
MrZ_26
2671306688 half clear文本改为half pc 2023-11-07 18:47:36 +08:00
MrZ_26
20e9d085c0 更新赞助名单 2023-11-07 18:38:36 +08:00
MrZ_26
1c1a006723 Hemi PC改成Half PC 2023-11-07 18:38:29 +08:00
MrZ_26
a0fd8ff8b6 修复调整语音音量时测试音频太乱
框架跟进 close #823
2023-11-01 16:38:21 +08:00
C6H12O6 + NaCl + H2O
720325fcb9 Add touch gesture for radar (#1030) 2023-10-29 15:56:58 +08:00
C6H12O6 + NaCl + H2O
fcc1cba0cf Fix Piano app (#1029)
* Fix Piano app

* FIx mouse and keyboard interaction
2023-10-29 15:56:03 +08:00
sakurw
8ca17ba3ff Update lang_ja.lua (#1028) 2023-10-29 15:55:39 +08:00
MrZ_26
942a789b9e fix 2023-10-23 01:51:41 +08:00
MrZ_26
293e687077 Merge remote-tracking branch 'tech/cc-field-height' 2023-10-23 01:50:51 +08:00
MrZ_26
9929b888c5 继续补BiRS 2023-10-23 01:43:39 +08:00
MrZ_26
e66e30c14a 框架跟进 2023-10-23 01:39:47 +08:00
MrZ_26
adf225e192 修复BiRS一些180时对称方块踢墙表不对称 2023-10-23 01:34:46 +08:00
MrZ_26
4c2dc5e8df 框架跟进 close #1023 2023-10-23 01:09:50 +08:00
C6H12O6 + NaCl + H2O
e1a9222b1f Minor changes in VIetnamese translation (#1020)
* Update

* Minor changes
2023-10-21 23:57:42 +08:00
MrZ_26
86c673ee51 自定义游戏不能带ai开拼图模式
补充文本
2023-10-21 04:42:01 +08:00
Imple Lee
6aefa078da add comment 2023-10-21 00:44:10 +08:00
Imple Lee
dd743035f5 add a warning when field is too high
temporarily fixes #686
2023-10-21 00:42:04 +08:00
Imple Lee
045deb6954 make an error with filled lines when using cc (#1015)
fixes #1013
2023-10-20 22:47:04 +08:00
MrZ_26
8e169dbde8 制作名单修一个拼错 2023-10-20 21:56:12 +08:00
Imple Lee
90c428cf44 Save custom mode (#1007)
* first step to save custom game

* localize various settings in custom game

* rename fields to lower cases

* fix many bugs

* fixes #1014
2023-10-20 01:42:04 +08:00
C6H12O6 + NaCl + H2O
7ac2c282f6 Vietnamese translation update (again) (#1012)
* fix #983 (#995)

* Pull from Shard Nguyen

* Change a mode's description

* Fix a bug when Help button don't restore it's color in a condition

* Update dict_vi.lua

* Update Zictionary (mostly fix broken sentences)

* Update game's translation

* Update modes

* Update dictionary

* Minor change

* Update stat translation

* Minor change

* Minor change

* Update lang_vi.lua

* Minor changes

* Fix misunderstanding help text

* Update scene

* Rename _utf8lower to needLowerUTF8

* Add titleLowered and keywordsLowered

* Fix stuck color again

---------

Co-authored-by: scdhh <51957264+shoucandanghehe@users.noreply.github.com>
2023-10-19 22:48:24 +08:00
MrZ_26
ad58d38ab1 puzzle模式ai也会知道自己有没有hold 2023-10-19 21:48:27 +08:00
MrZ_26
5ea940d8c4 自定义游戏可以正确传递hold开关的参数了close #779 2023-10-19 20:26:10 +08:00
MrZ_26
c0dca683b5 初始化模式后记录玩家数量写入GAME表 2023-10-19 19:57:55 +08:00
MrZ_26
35635e10ea 修staff界面初始位置不对(测试代码忘删 2023-10-19 15:35:11 +08:00
MrZ_26
dfda067c08 Revert "暴力修复对战模式会出现回放按钮"
This reverts commit 09117c4704.
2023-10-19 13:15:26 +08:00
MrZ_26
09117c4704 暴力修复对战模式会出现回放按钮 2023-10-19 13:02:09 +08:00
MrZ_26
6083fd5097 回放时模式名称下边不再显示当前模式本地pb 2023-10-18 19:39:04 +08:00
MrZ_26
e104297b13 调整几个繁体中文语言的用词 2023-10-17 00:52:48 +08:00
MrZ_26
ed3927a8c3 框架跟进 2023-10-16 14:09:28 +08:00
MrZ_26
b62b907dc8 框架跟进 2023-10-15 20:40:38 +08:00
MrZ_26
769125a894 科研-极简模式优化 2023-10-10 15:04:40 +08:00
MrZ_26
6ccebcfe17 修一个文本错误 close #984 2023-10-10 14:49:02 +08:00
MrZ_26
8894701806 自定义模式的一些参数补充更多可选值
调整一些文本细节
2023-10-10 14:41:53 +08:00
MrZ_26
012e0f76a9 调整暂停和统计界面关于接收攻击一行的文本 close #1002 2023-10-10 14:32:28 +08:00
MrZ_26
d58f41b999 框架跟进 2023-10-10 14:22:17 +08:00
MrZ_26
95916a8736 删除词典中关于hold相同块不利于finesse判定的规则 close #1004 2023-10-10 14:22:17 +08:00
Imple Lee
69a288db8f simplify two code pieces in record manipulation (#1009) 2023-10-10 14:09:21 +08:00
Imple Lee
bb8a436604 use normal spin table for x-spin (#1008) 2023-10-09 19:18:47 +08:00
NOT_A_ROBOT
b58f266561 Change name of Tech modes (#1010)
* Update language files for Tech VS/B2B

* Add note about vectorized modeicons

* Vectorize Tech modeicons
2023-10-09 19:18:04 +08:00
Imple Lee
4e41024ba8 refactor GAME.mod (#1006)
Co-authored-by: MrZ_26 <1046101471@qq.com>
2023-10-08 18:56:44 +08:00
MrZ_26
323f8a72aa 再调整新拼花模式难度 2023-10-06 05:19:31 +08:00
MrZ_26
bb4d3023c6 再调整新拼花模式难度 2023-10-06 04:36:31 +08:00
MrZ_26
2208a0fca0 把几个pi常量换回数字(性能原因) 2023-10-06 03:40:28 +08:00
MrZ_26
3cd6f3d726 两个新的拼花模式评分要求调整 2023-10-06 03:28:48 +08:00
MrZ_26
8b1d72fb54 修新模式的一些相关问题 2023-10-06 03:12:33 +08:00
MrZ_26
73480d4a53 Merge remote-tracking branch 'bot/construct-modes' 2023-10-06 02:49:42 +08:00
NOT_A_ROBOT
120dc453d7 Add Autoskip feature to TAS (#992) 2023-10-06 02:37:12 +08:00
scdhh
eae441b18b fix #983 (#995) 2023-10-06 02:37:07 +08:00
MrZ_26
4f411ef78e 补充master_g模式缺失文本 2023-10-06 02:34:24 +08:00
MrZ_26
35ec325990 Merge commit '54758fd705c2ba485e8aed1c4006c112ad95f82d' 2023-10-06 02:23:26 +08:00
MrZ_26
54758fd705 update Zframework 2023-10-06 02:22:36 +08:00
MrZ_26
e7183025cf 修复dropper小程序玩不了 2023-10-04 19:16:34 +08:00
NOT_A_ROBOT
03a2e9356c Fix rank display bug in Construct modes 2023-10-03 08:49:22 +07:00
NOT_A_ROBOT
0eb4212e40 Adjust some mode shapes 2023-10-03 08:25:29 +07:00
NOT_A_ROBOT
22ca0a7c96 Add Construct modes to map 2023-10-03 08:18:10 +07:00
NOT_A_ROBOT
33fc59bd5c Add secret grade to old mode list 2023-10-03 07:43:42 +07:00
NOT_A_ROBOT
2f6a3392e6 De-nest Construct modes' display code 2023-10-03 07:32:56 +07:00
MrZ_26
75f3efc6a0 fix lang_vi 2023-09-25 22:01:25 +08:00
C6H12O6 + NaCl + H2O
19e4185668 Fix Piano app (#991)
* Trying to reduce memory leaks

* Formalize the usage of pianoVK.ctrl and pianoVK.shift

* Replace all loop, make a text object list

* Add auto-garbage cleaner and remove 2 unnecessary variable

* Update app_piano.lua
2023-09-25 21:27:31 +08:00
MrZ_26
bbb5fd7832 框架跟进 2023-09-25 13:22:16 +08:00
C6H12O6 + NaCl + H2O
8806ef602e Attemping to fix some bugs in Piano applet (#988)
* Trying to reduce memory leaks

* Formalize the usage of pianoVK.ctrl and pianoVK.shift

* Replace all loop, make a text object list

* Add auto-garbage cleaner and remove 2 unnecessary variable
2023-09-25 04:46:59 +08:00
C6H12O6 + NaCl + H2O
6f5ebb1a4d Update Vietnamese translation (#990)
* Update lang_vi.lua

* Update manual_vi.txt

* Update lang_vi.lua
2023-09-25 04:46:16 +08:00
sakurw
7cd03d0ec0 Update lang_ja.lua (#989) 2023-09-24 16:12:40 +08:00
Not-A-Normal-Robot
00c5233f3b Merge branch 'torikan-animation' of https://github.com/Not-A-Normal-Robot/Techmino into torikan-animation 2023-09-23 16:56:17 +07:00
Not-A-Normal-Robot
a08328ae02 Add Vietnamese translation for torikan 2023-09-23 16:50:32 +07:00
Not-A-Normal-Robot
a6a20b4332 Add torikan language entries 2023-09-23 16:21:31 +07:00
MrZ_26
732e2aee79 删除两个没用了的模式图标图片
修改消四挖掘模式图标
2023-09-23 04:36:17 +08:00
MrZ_26
74c3d3a313 修改挖掘10行评级条件和统计数据 2023-09-23 04:20:13 +08:00
MrZ_26
3ebc93e65c 整理代码 2023-09-23 04:01:55 +08:00
MrZ_26
7e8e67694d 消四挖掘模式放入地图,调整布局 2023-09-23 03:57:16 +08:00
MrZ_26
00f63a2ec5 修改c4w成绩显示格式 2023-09-23 03:56:54 +08:00
MrZ_26
5a30e4aec9 矢量化五连块模式图标
删除五连块和三连块模式图标图片
2023-09-23 03:56:41 +08:00
MrZ_26
43aecd2467 修改大方块模式说明 2023-09-23 03:56:15 +08:00
MrZ_26
622ae07b32 降低100行竞速最低评级标准 2023-09-23 03:54:53 +08:00
Imple Lee
63f443d4e4 put dig_quad_10l into the map (#987) 2023-09-23 02:56:39 +08:00
MrZ_26
688328e6b0 优化小程序piano 2023-09-23 02:51:52 +08:00
Not-A-Normal-Robot
0fc4957b21 Fade field on torikan and show torikan time 2023-09-22 21:30:49 +07:00
Not-A-Normal-Robot
5e88b23980 Add color to torikan diff text 2023-09-22 21:03:46 +07:00
MrZ_26
986f0dcdc8 框架跟进 2023-09-22 14:02:46 +08:00
MrZ_26
48f2ee9252 微调制作人员名单动画参数 2023-09-22 14:00:40 +08:00
MrZ_26
73d6eaaffc 制作名单里添加flore 2023-09-22 14:00:40 +08:00
Not-A-Normal-Robot
b1dbc4fa87 Use pi constants instead of numbers 2023-09-21 19:44:55 +07:00
NOT_A_ROBOT
2e9ea4a783 Fix Tech Finesse Lock DAS bug... again (#982)
wow one-liner commit
Man I sure hope there isn't another bug I haven't seen before
2023-09-19 22:37:31 +08:00
NOT_A_ROBOT
a475232432 Add Construct modes 2023-09-18 12:49:17 +07:00
Not-A-Normal-Robot
bc99af5b1b Add torikan animation 2023-09-16 19:12:52 +07:00
C29H25N3O5
06dc22544f Symbol and font fixes
- Added some more symbols
- Fixed some missing characters
2023-08-29 11:35:49 +08:00
C₂₉H₂₅N₃O₅
70c1b04bf6 Merge branch '26F-Studio:main' into main 2023-08-28 22:27:54 -05:00
C29H25N3O5
41c957cd8f Updated in-game font
- Adjusted the alignment of font
- Added missing glyphs in some languages
- Redesigned grade letters to increase legibility
2023-08-18 01:24:49 +08:00
C29H25N3O5
e3a0eacf1d Updated two block skins 2023-08-17 02:10:46 +08:00
243 changed files with 17090 additions and 3216 deletions

View File

@@ -39,7 +39,9 @@ jobs:
os.execute('echo "version-string=' .. version.string:gsub("%a", "") .. '" >> $GITHUB_OUTPUT')
os.execute('echo "version-code=' .. tostring(version.code) .. '" >> $GITHUB_OUTPUT')
local note = require 'parts.updateLog'
local f = io.open("updateLog.txt", 'r')
local note = f:read("*a")
f:close()
local p1 = note:find("\n%d") + 1
local p2 = note:find("\n", p1) - 1
os.execute('echo "update-title=' .. note:sub(p1, p2) .. '" >> $GITHUB_OUTPUT')
@@ -90,7 +92,7 @@ jobs:
- name: Build core love package
uses: love-actions/love-actions-core@v1
with:
build-list: ./media/ ./parts/ ./Zframework/ ./conf.lua ./main.lua ./version.lua
build-list: ./media/ ./parts/ ./Zframework/ ./conf.lua ./main.lua ./version.lua ./legals.md ./license.txt
package-path: ${{ env.CORE_LOVE_PACKAGE_PATH }}
- name: Upload core love package
uses: actions/upload-artifact@v3
@@ -213,87 +215,6 @@ jobs:
name: ${{ needs.get-info.outputs.update-title }}
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
build-ios:
runs-on: macos-latest
needs: [get-info, build-core, auto-test]
if: github.event_name != 'pull_request'
env:
OUTPUT_FOLDER: ./build
RELEASE_FOLDER: ./release
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Process app name
id: process-app-name
shell: python3 {0}
run: |
import os
import re
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
f.write('bundle-id=org.26f-studio.techmino\n')
f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\n')
- name: Download core love package
uses: actions/download-artifact@v3
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Download ColdClear
uses: ./.github/actions/get-cc
with:
platform: iOS
dir: ./ColdClear
- name: Build iOS packages
id: build-packages
uses: love-actions/love-actions-ios@v1
with:
app-name: ${{ needs.get-info.outputs.app-name }}
bundle-id: ${{ steps.process-app-name.outputs.bundle-id }}
copyright: "Copyright © 2019-2023 26F-Studio. Some Rights Reserved."
icon-path: ./.github/build/iOS/${{ env.BUILD_TYPE }}/icon
love-patch: ./.github/build/iOS/love.patch
love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }}
libs-path: ./ColdClear/arm64/
product-name: ${{ steps.process-app-name.outputs.product-name }}
version-string: ${{ needs.get-info.outputs.version-string }}
output-folder: ${{ env.OUTPUT_FOLDER }}
apple-development-base64: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_BASE64 }}
apple-development-password: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_PWD }}
api-key: ${{ secrets.APPLE_API_KEY }}
api-key-id: ${{ secrets.APPLE_API_KEY_ID }}
api-issuer-id: ${{ secrets.APPLE_API_ISSUER_ID }}
team-id: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }}
apple-id: ${{ secrets.APPLE_APPLE_ID }}
external-test: ${{ startsWith(github.ref, 'refs/tags/pre') }}
store-release: ${{ startsWith(github.ref, 'refs/tags/v') }}
- name: Upload logs artifact
uses: actions/upload-artifact@v3
with:
name: ${{ needs.get-info.outputs.base-name }}_iOS_logs
path: |
${{ env.OUTPUT_FOLDER }}/DistributionSummary.plist
${{ env.OUTPUT_FOLDER }}/ExportOptions.plist
${{ env.OUTPUT_FOLDER }}/Packaging.log
- name: Upload ipa artifact
uses: actions/upload-artifact@v3
with:
name: ${{ needs.get-info.outputs.base-name }}_iOS_ipa
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.ipa
- name: Prepare for release
if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }}
shell: bash
run: |
mkdir -p ${{ env.RELEASE_FOLDER }}
cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.ipa ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_iOS.ipa
- name: Upload release
if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }}
uses: ncipollo/release-action@v1
with:
allowUpdates: true
artifacts: ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_iOS.ipa
body: ${{ needs.get-info.outputs.update-note }}
name: ${{ needs.get-info.outputs.update-title }}
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
build-linux:
runs-on: ubuntu-latest
needs: [get-info, build-core, auto-test]
@@ -377,90 +298,6 @@ jobs:
name: ${{ needs.get-info.outputs.update-title }}
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
build-macos-appstore:
runs-on: macos-latest
needs: [get-info, build-core, auto-test]
if: github.event_name != 'pull_request'
env:
OUTPUT_FOLDER: ./build
RELEASE_FOLDER: ./release
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Process app name
id: process-app-name
shell: python3 {0}
run: |
import os
import re
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
f.write('bundle-id=org.26f-studio.techmino\n')
f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\n')
- name: Download core love package
uses: actions/download-artifact@v3
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Download ColdClear
uses: ./.github/actions/get-cc
with:
platform: macOS
dir: ./ColdClear
- name: Process ColdClear
shell: bash
run: |
rm ./ColdClear/universal/libcold_clear.a
- name: Build macOS packages
id: build-packages
uses: love-actions/love-actions-macos-appstore@v1
with:
app-name: ${{ needs.get-info.outputs.app-name }}
bundle-id: ${{ steps.process-app-name.outputs.bundle-id }}
copyright: "Copyright © 2019-2023 26F-Studio. Some Rights Reserved."
icon-path: ./.github/build/macOS/${{ env.BUILD_TYPE }}/icon.icns
love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }}
libs-path: ./ColdClear/universal/
product-name: ${{ steps.process-app-name.outputs.product-name }}
version-string: ${{ needs.get-info.outputs.version-string }}
output-folder: ${{ env.OUTPUT_FOLDER }}
apple-development-base64: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_BASE64 }}
apple-development-password: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_PWD }}
api-key: ${{ secrets.APPLE_API_KEY }}
api-key-id: ${{ secrets.APPLE_API_KEY_ID }}
api-issuer-id: ${{ secrets.APPLE_API_ISSUER_ID }}
team-id: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }}
apple-id: ${{ secrets.APPLE_APPLE_ID }}
external-test: ${{ startsWith(github.ref, 'refs/tags/pre') }}
store-release: ${{ startsWith(github.ref, 'refs/tags/v') }}
- name: Upload logs artifact
uses: actions/upload-artifact@v3
with:
name: ${{ needs.get-info.outputs.base-name }}_macOS_appstore_logs
path: |
${{ env.OUTPUT_FOLDER }}/DistributionSummary.plist
${{ env.OUTPUT_FOLDER }}/ExportOptions.plist
${{ env.OUTPUT_FOLDER }}/Packaging.log
- name: Upload pkg artifact
uses: actions/upload-artifact@v3
with:
name: ${{ needs.get-info.outputs.base-name }}_macOS_appstore_pkg
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.pkg
- name: Prepare for release
if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }}
shell: bash
run: |
mkdir -p ${{ env.RELEASE_FOLDER }}
cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.pkg ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_macOS_appstore.pkg
- name: Upload release
if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }}
uses: ncipollo/release-action@v1
with:
allowUpdates: true
artifacts: ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_macOS_appstore.pkg
body: ${{ needs.get-info.outputs.update-note }}
name: ${{ needs.get-info.outputs.update-title }}
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
build-macos-portable:
runs-on: macos-latest
needs: [get-info, build-core, auto-test]
@@ -684,9 +521,7 @@ jobs:
auto-test,
build-core,
build-android,
build-ios,
build-linux,
build-macos-appstore,
build-macos-portable,
build-web,
build-windows,
@@ -719,6 +554,6 @@ jobs:
"fields":[
{"name":"Version","value":"${{ needs.get-info.outputs.version-string }}","inline": true},
{"name":"Package Name","value":"${{ needs.get-info.outputs.base-name }}","inline": true},
{"name":"Status","value":"**Automatic Test:** ${{ needs.auto-test.result }}\n**Core:** ${{ needs.build-core.result }}\n**Android:** ${{ needs.build-android.result }}\n**iOS:** ${{ needs.build-ios.result }}\n**Linux:** ${{ needs.build-linux.result }}\n**macOS App Store:** ${{ needs.build-macos-appstore.result }}\n**macOS portable:** ${{ needs.build-macos-portable.result }}\n**Windows:** ${{ needs.build-windows.result }}"}
{"name":"Status","value":"**Automatic Test:** ${{ needs.auto-test.result }}\n**Core:** ${{ needs.build-core.result }}\n**Android:** ${{ needs.build-android.result }}\n**Linux:** ${{ needs.build-linux.result }}\n**macOS portable:** ${{ needs.build-macos-portable.result }}\n**Windows:** ${{ needs.build-windows.result }}"}
]
}'

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "Zframework"]
path = Zframework
url = https://github.com/26F-Studio/Zframework.git

Submodule Zframework deleted from c498582a8c

57
Zframework/background.lua Normal file
View File

@@ -0,0 +1,57 @@
local gc_clear=love.graphics.clear
local BGs={
none={draw=function() gc_clear(.08,.08,.084) end}
}
local BGlist={'none'}
local BG={
default='none',
locked=false,
cur='none',
init=false,
resize=false,
update=NULL,
draw=BGs.none.draw,
event=false,
discard=NULL,
}
function BG.lock() BG.locked=true end
function BG.unlock() BG.locked=false end
function BG.add(name,bg)
BGs[name]=bg
BGlist[#BGlist+1]=name
end
function BG.getList()
return BGlist
end
function BG.remList(name)
table.remove(BGlist,TABLE.find(BGlist,name))
end
function BG.send(...)
if BG.event then
BG.event(...)
end
end
function BG.setDefault(bg)
BG.default=bg
end
function BG.set(name,...)
name=name or BG.default
if not BGs[name] or BG.locked then return end
if name~=BG.cur then
BG.discard()
BG.cur=name
local bg=BGs[name]
BG.init= bg.init or NULL
BG.resize= bg.resize or NULL
BG.update= bg.update or NULL
BG.draw= bg.draw or NULL
BG.event= bg.event or NULL
BG.discard=bg.discard or NULL
BG.init()
if ... then BG.send(...) end
end
return true
end
return BG

350
Zframework/bgm.lua Normal file
View File

@@ -0,0 +1,350 @@
local audio=love.audio
local effectsSupported=audio.isEffectsSupported()
local nameList={}
local srcLib={}-- Stored bgm objects: {name='foo', source=bar, ...}, more info at function _addFile()
local lastLoadNames={}
local nowPlay={}
local lastPlay=NONE-- Directly stored last played bgm name(s)
local defaultBGM=false
local maxLoadedCount=3
local volume=1
local function task_setVolume(obj,ve,time,stop)
local vs=obj.vol
local t=0
while true do
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
local v=MATH.mix(vs,ve,t)
obj.vol=v
obj.source:setVolume(v*volume)
if t==1 then
obj.volChanging=false
break
end
end
if stop then
obj.source:stop()
end
obj.volChanging=false
return true
end
local function task_setPitch(obj,pe,time)
local ps=obj.pitch
local t=0
while true do
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
local p=MATH.mix(ps,pe,t)
obj.pitch=p
obj.source:setPitch(p)
if t==1 then
obj.pitchChanging=false
return true
end
end
end
local function task_setLowgain(obj,pe,time)
local ps=obj.lowgain
local t=0
while true do
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
local p=MATH.mix(ps,pe,t)
obj.lowgain=p
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain^9.42,highgain=obj.highgain^9.42,volume=1}
if t==1 then
obj.lowgainChanging=false
return true
end
end
end
local function task_setHighgain(obj,pe,time)
local ps=obj.highgain
local t=0
while true do
t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1
local p=MATH.mix(ps,pe,t)
obj.highgain=p
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain^9.42,highgain=obj.highgain^9.42,volume=1}
if t==1 then
obj.highgainChanging=false
return true
end
end
end
local function _clearTask(obj,mode)
local taskFunc=
mode=='volume' and task_setVolume or
mode=='pitch' and task_setPitch or
mode=='lowgain' and task_setLowgain or
mode=='highgain' and task_setHighgain or
'any'
TASK.removeTask_iterate(function(task)
return task.args[1]==obj and (taskFunc=='any' or task.code==taskFunc)
end,obj)
end
local function _updateSources()
local n=#lastLoadNames
while #lastLoadNames>maxLoadedCount and n>0 do
local name=lastLoadNames[n]
if srcLib[name].source and not srcLib[name].source:isPlaying() then
srcLib[name].source=srcLib[name].source:release() and nil
_clearTask(srcLib[name],'any')
end
n=n-1
end
end
local function _addFile(name,path)
if not srcLib[name] then
table.insert(nameList,name)
srcLib[name]={
name=name,path=path,source=false,
vol=0,volChanging=false,
pitch=1,pitchChanging=false,
lowgain=1,lowgainChanging=false,
highgain=1,highgainChanging=false,
}
end
end
local function _tryLoad(name)
if srcLib[name] then
local obj=srcLib[name]
if obj.source then
return true
elseif love.filesystem.getInfo(obj.path) then
obj.source=audio.newSource(obj.path,'stream')
obj.source:setLooping(true)
table.insert(lastLoadNames,1,name)
return true
else
LOG(STRING.repD("Wrong path for BGM '$1': $2",obj.name,obj.path),5)
end
elseif name then
LOG("No BGM: "..name,5)
end
end
local BGM={}
function BGM.getList() return nameList end
function BGM.getCount() return #nameList end
function BGM.setDefault(bgms)
if type(bgms)=='string' then
bgms={bgms}
elseif type(bgms)=='table' then
for i=1,#bgms do assert(type(bgms[i])=='string',"BGM list must be list of strings") end
else
error("BGM.setDefault(bgms): bgms must be string or table")
end
defaultBGM=bgms
end
function BGM.setMaxSources(count)
assert(type(count)=='number' and count>0 and count%1==0,"BGM.setMaxSources(count): count must be positive integer")
maxLoadedCount=count
_updateSources()
end
function BGM.setVol(vol)
assert(type(vol)=='number' and vol>=0 and vol<=1,"BGM.setVol(vol): count must be in range 0~1")
volume=vol
for i=1,#nowPlay do
local bgm=nowPlay[i]
if not bgm.volChanging then
bgm.source:setVolume(bgm.vol*vol)
end
end
end
function BGM.init(name,path)
if type(name)=='table' then
for k,v in next,name do
_addFile(k,v)
end
else
_addFile(name,path)
end
table.sort(nameList)
LOG(BGM.getCount().." BGM files added")
end
function BGM.play(bgms,args)
if not args then args='' end
if not bgms then bgms=defaultBGM end
if not bgms then return end
if type(bgms)=='string' then bgms={bgms} end
assert(type(bgms)=='table',"BGM.play(name,args): name must be string or table")
if
TABLE.compare(lastPlay,bgms) and
srcLib[lastPlay[1]] and srcLib[lastPlay[1]].source and
srcLib[lastPlay[1]].source:isPlaying()
then
return
end
BGM.stop()
if not STRING.sArg(args,'-preLoad') then
lastPlay=bgms
end
for i=1,#bgms do
local bgm=bgms[i]
assert(type(bgm)=='string',"BGM list can only be list of string")
if _tryLoad(bgm) and not STRING.sArg(args,'-preLoad') then
local obj=srcLib[bgms[i]]
obj.vol=0
obj.pitch=1
obj.lowgain=1
obj.highgain=1
obj.volChanging=false
obj.pitchChanging=false
obj.lowgainChanging=false
obj.highgainChanging=false
_clearTask(obj)
local source=obj.source
source:setLooping(not STRING.sArg(args,'-noloop'))
source:setPitch(1)
source:seek(0)
source:setFilter()
if STRING.sArg(args,'-sdin') then
obj.vol=1
source:setVolume(volume)
BGM.set(bgm,'volume',1,0)
else
source:setVolume(0)
BGM.set(bgm,'volume',1,.626)
end
source:play()
table.insert(nowPlay,obj)
return true
end
end
_updateSources()
end
function BGM.stop(time)
if #nowPlay>0 then
for i=1,#nowPlay do
local obj=nowPlay[i]
_clearTask(obj,'volume')
if time==0 then
obj.source:stop()
obj.volChanging=false
else
TASK.new(task_setVolume,obj,0,time or .626,true)
obj.volChanging=true
end
end
TABLE.cut(nowPlay)
lastPlay=NONE
end
end
---@param mode
---| 'volume'
---| 'lowgain'
---| 'highgain'
---| 'volume'
---| 'pitch'
---| 'seek'
function BGM.set(bgms,mode,...)
if type(bgms)=='string' then
if bgms=='all' then
bgms=nowPlay
else
bgms={srcLib[bgms]}
end
elseif type(bgms)=='table' then
bgms=TABLE.shift(bgms)
for i=1,#bgms do
assert(type(bgms[i])=='string',"BGM list must be list of strings")
bgms[i]=srcLib[bgms[i]]
end
else
error("BGM.play(name,args): name must be string or table")
end
for i=1,#bgms do
local obj=bgms[i]
if obj.source then
if mode=='volume' then
_clearTask(obj,'volume')
local vol,time=...
if not time then time=1 end
assert(type(vol)=='number' and vol>=0 and vol<=1,"BGM.set(...,volume): volume must be in range 0~1")
assert(type(time)=='number' and time>=0,"BGM.set(...,time): time must be positive number")
TASK.new(task_setVolume,obj,vol,time)
elseif mode=='pitch' then
_clearTask(obj,'pitch')
local pitch,changeTime=...
if not pitch then pitch=1 end
if not changeTime then changeTime=1 end
assert(type(pitch)=='number' and pitch>0 and pitch<=32,"BGM.set(...,pitch): pitch must be in range 0~32")
assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number")
TASK.new(task_setPitch,obj,pitch,changeTime)
elseif mode=='seek' then
local time=...
assert(type(time)=='number',"BGM.set(...,time): time must be number")
obj.source:seek(MATH.clamp(time,0,obj.source:getDuration()))
elseif mode=='lowgain' then
if effectsSupported then
_clearTask(obj,'lowgain')
local lowgain,changeTime=...
if not lowgain then lowgain=1 end
if not changeTime then changeTime=1 end
assert(type(lowgain)=='number' and lowgain>=0 and lowgain<=1,"BGM.set(...,lowgain,highgain): lowgain must be in range 0~1")
assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number")
TASK.new(task_setLowgain,obj,lowgain,changeTime)
obj.lowgain=lowgain
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain,highgain=obj.highgain,volume=1}
end
elseif mode=='highgain' then
if effectsSupported then
_clearTask(obj,'highgain')
local highgain,changeTime=...
if not highgain then highgain=1 end
if not changeTime then changeTime=1 end
assert(type(highgain)=='number' and highgain>=0 and highgain<=1,"BGM.set(...,lowgain,highgain): highgain must be in range 0~1")
assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number")
TASK.new(task_setHighgain,obj,highgain,changeTime)
obj.highgain=highgain
obj.source:setFilter{type='bandpass',lowgain=obj.lowgain,highgain=obj.highgain,volume=1}
end
else
error("BGM.set(...,mode): mode must be 'volume', 'pitch', or 'seek'")
end
end
end
end
function BGM.getPlaying()
return TABLE.shift(lastPlay)
end
function BGM.isPlaying()
return #nowPlay>0 and nowPlay[1].source:isPlaying()
end
function BGM.tell()
if nowPlay[1] then
return nowPlay[1].source:tell()
end
end
function BGM.getDuration()
if nowPlay[1] then
return nowPlay[1].source:getDuration()
end
end
return BGM

366
Zframework/bitop.lua Normal file
View File

@@ -0,0 +1,366 @@
local M = {_TYPE='module', _NAME='bitop.funcs', _VERSION='1.0-0'}
local floor = math.floor
local MOD = 2 ^ 32
local MODM = MOD - 1
local function memoize(f)
local mt = {}
local t = setmetatable({}, mt)
function mt:__index(k)
local v = f(k)
t[k] = v
return v
end
return t
end
local function make_bitop_uncached(t, m)
local function bitop(a, b)
local res, p = 0, 1
while a ~= 0 and b ~= 0 do
local am, bm = a % m, b % m
res = res + t[am][bm] * p
a = (a - am) / m
b = (b - bm) / m
p = p * m
end
res = res + (a + b) * p
return res
end
return bitop
end
local function make_bitop(t)
local op1 = make_bitop_uncached(t, 2 ^ 1)
local op2 = memoize(function(a)
return memoize(function(b)
return op1(a, b)
end)
end)
return make_bitop_uncached(op2, 2 ^ (t.n or 1))
end
-- ok? probably not if running on a 32-bit int Lua number type platform
function M.tobit(x)
return x % 2 ^ 32
end
M.bxor = make_bitop{[0]={[0]=0, [1]=1}, [1]={[0]=1, [1]=0}, n=4}
local bxor = M.bxor
function M.bnot(a)
return MODM - a
end
local bnot = M.bnot
function M.band(a, b)
return ((a + b) - bxor(a, b)) / 2
end
local band = M.band
function M.bor(a, b)
return MODM - band(MODM - a, MODM - b)
end
local bor = M.bor
local lshift, rshift -- forward declare
function M.rshift(a, disp) -- Lua5.2 insipred
if disp < 0 then
return lshift(a, -disp)
end
return floor(a % 2 ^ 32 / 2 ^ disp)
end
rshift = M.rshift
function M.lshift(a, disp) -- Lua5.2 inspired
if disp < 0 then
return rshift(a, -disp)
end
return (a * 2 ^ disp) % 2 ^ 32
end
lshift = M.lshift
function M.tohex(x, n) -- BitOp style
n = n or 8
local up
if n <= 0 then
if n == 0 then
return ''
end
up = true
n = -n
end
x = band(x, 16 ^ n - 1)
return ('%0' .. n .. (up and 'X' or 'x')):format(x)
end
local tohex = M.tohex
function M.extract(n, field, width) -- Lua5.2 inspired
width = width or 1
return band(rshift(n, field), 2 ^ width - 1)
end
local extract = M.extract
function M.replace(n, v, field, width) -- Lua5.2 inspired
width = width or 1
local mask1 = 2 ^ width - 1
v = band(v, mask1) -- required by spec?
local mask = bnot(lshift(mask1, field))
return band(n, mask) + lshift(v, field)
end
local replace = M.replace
function M.bswap(x) -- BitOp style
local a = band(x, 0xff);
x = rshift(x, 8)
local b = band(x, 0xff);
x = rshift(x, 8)
local c = band(x, 0xff);
x = rshift(x, 8)
local d = band(x, 0xff)
return lshift(lshift(lshift(a, 8) + b, 8) + c, 8) + d
end
local bswap = M.bswap
function M.rrotate(x, disp) -- Lua5.2 inspired
disp = disp % 32
local low = band(x, 2 ^ disp - 1)
return rshift(x, disp) + lshift(low, 32 - disp)
end
local rrotate = M.rrotate
function M.lrotate(x, disp) -- Lua5.2 inspired
return rrotate(x, -disp)
end
local lrotate = M.lrotate
M.rol = M.lrotate -- LuaOp inspired
M.ror = M.rrotate -- LuaOp insipred
function M.arshift(x, disp) -- Lua5.2 inspired
local z = rshift(x, disp)
if x >= 0x80000000 then
z = z + lshift(2 ^ disp - 1, 32 - disp)
end
return z
end
local arshift = M.arshift
function M.btest(x, y) -- Lua5.2 inspired
return band(x, y) ~= 0
end
--
-- Start Lua 5.2 "bit32" compat section.
--
M.bit32 = {} -- Lua 5.2 'bit32' compatibility
local function bit32_bnot(x)
return (-1 - x) % MOD
end
M.bit32.bnot = bit32_bnot
local function bit32_bxor(a, b, c, ...)
local z
if b then
a = a % MOD
b = b % MOD
z = bxor(a, b)
if c then
z = bit32_bxor(z, c, ...)
end
return z
elseif a then
return a % MOD
else
return 0
end
end
M.bit32.bxor = bit32_bxor
local function bit32_band(a, b, c, ...)
local z
if b then
a = a % MOD
b = b % MOD
z = ((a + b) - bxor(a, b)) / 2
if c then
z = bit32_band(z, c, ...)
end
return z
elseif a then
return a % MOD
else
return MODM
end
end
M.bit32.band = bit32_band
local function bit32_bor(a, b, c, ...)
local z
if b then
a = a % MOD
b = b % MOD
z = MODM - band(MODM - a, MODM - b)
if c then
z = bit32_bor(z, c, ...)
end
return z
elseif a then
return a % MOD
else
return 0
end
end
M.bit32.bor = bit32_bor
function M.bit32.btest(...)
return bit32_band(...) ~= 0
end
function M.bit32.lrotate(x, disp)
return lrotate(x % MOD, disp)
end
function M.bit32.rrotate(x, disp)
return rrotate(x % MOD, disp)
end
function M.bit32.lshift(x, disp)
if disp > 31 or disp < -31 then
return 0
end
return lshift(x % MOD, disp)
end
function M.bit32.rshift(x, disp)
if disp > 31 or disp < -31 then
return 0
end
return rshift(x % MOD, disp)
end
function M.bit32.arshift(x, disp)
x = x % MOD
if disp >= 0 then
if disp > 31 then
return (x >= 0x80000000) and MODM or 0
else
local z = rshift(x, disp)
if x >= 0x80000000 then
z = z + lshift(2 ^ disp - 1, 32 - disp)
end
return z
end
else
return lshift(x, -disp)
end
end
function M.bit32.extract(x, field, ...)
local width = ... or 1
if field < 0 or field > 31 or width < 0 or field + width > 32 then
error'out of range'
end
x = x % MOD
return extract(x, field, ...)
end
function M.bit32.replace(x, v, field, ...)
local width = ... or 1
if field < 0 or field > 31 or width < 0 or field + width > 32 then
error'out of range'
end
x = x % MOD
v = v % MOD
return replace(x, v, field, ...)
end
--
-- Start LuaBitOp "bit" compat section.
--
M.bit = {} -- LuaBitOp "bit" compatibility
function M.bit.tobit(x)
x = x % MOD
if x >= 0x80000000 then
x = x - MOD
end
return x
end
local bit_tobit = M.bit.tobit
function M.bit.tohex(x, ...)
return tohex(x % MOD, ...)
end
function M.bit.bnot(x)
return bit_tobit(bnot(x % MOD))
end
local function bit_bor(a, b, c, ...)
if c then
return bit_bor(bit_bor(a, b), c, ...)
elseif b then
return bit_tobit(bor(a % MOD, b % MOD))
else
return bit_tobit(a)
end
end
M.bit.bor = bit_bor
local function bit_band(a, b, c, ...)
if c then
return bit_band(bit_band(a, b), c, ...)
elseif b then
return bit_tobit(band(a % MOD, b % MOD))
else
return bit_tobit(a)
end
end
M.bit.band = bit_band
local function bit_bxor(a, b, c, ...)
if c then
return bit_bxor(bit_bxor(a, b), c, ...)
elseif b then
return bit_tobit(bxor(a % MOD, b % MOD))
else
return bit_tobit(a)
end
end
M.bit.bxor = bit_bxor
function M.bit.lshift(x, n)
return bit_tobit(lshift(x % MOD, n % 32))
end
function M.bit.rshift(x, n)
return bit_tobit(rshift(x % MOD, n % 32))
end
function M.bit.arshift(x, n)
return bit_tobit(arshift(x % MOD, n % 32))
end
function M.bit.rol(x, n)
return bit_tobit(lrotate(x % MOD, n % 32))
end
function M.bit.ror(x, n)
return bit_tobit(rrotate(x % MOD, n % 32))
end
function M.bit.bswap(x)
return bit_tobit(bswap(x % MOD))
end
return M

235
Zframework/color.lua Normal file
View File

@@ -0,0 +1,235 @@
local abs=math.abs
-- Converts from HSV to RGB color. All arguments should be between 0 and 1, inclusively.
local function HSVToRGB(h,s,v,a)
if s<=0 then return v,v,v,a end
h=h%1*6
local c=v*s
local x=abs((h-1)%2-1)*c
if h<1 then return v,x+v-c,v-c,a
elseif h<2 then return x+v-c,v,v-c,a
elseif h<3 then return v-c,v,x+v-c,a
elseif h<4 then return v-c,x+v-c,v,a
elseif h<5 then return x+v-c,v-c,v,a
else return v,v-c,x+v-c,a
end
end
local function RGBToHSV(r,g,b,a)
local max=math.max(r,g,b) -- = value
local min=math.min(r,g,b)
local chroma=max-min
local hue=(
max==min and 0 or
max==r and (g-b)/chroma%6/6 or
max==g and (2+(b-r)/chroma)/6 or
(4+(r-g)/chroma)/6
)
local saturation=max==0 and 0 or chroma/max
return hue,saturation,max,a
end
local COLOR={
hsv=HSVToRGB, -- backwards compatibility
HSVToRGB=HSVToRGB,
RGBToHSV=RGBToHSV,
red= {HSVToRGB(0.00, 0.89, 0.91)},
fire= {HSVToRGB(0.04, 0.93, 0.94)},
orange= {HSVToRGB(0.09, 0.99, 0.96)},
yellow= {HSVToRGB(0.15, 0.82, 0.90)},
lime= {HSVToRGB(0.20, 0.89, 0.88)},
jade= {HSVToRGB(0.25, 1.00, 0.82)},
green= {HSVToRGB(0.33, 1.00, 0.81)},
aqua= {HSVToRGB(0.47, 1.00, 0.76)},
cyan= {HSVToRGB(0.53, 1.00, 0.88)},
navy= {HSVToRGB(0.56, 1.00, 1.00)},
sea= {HSVToRGB(0.61, 1.00, 1.00)},
blue= {HSVToRGB(0.64, 1.00, 0.95)},
violet= {HSVToRGB(0.74, 1.00, 0.91)},
purple= {HSVToRGB(0.80, 1.00, 0.81)},
magenta= {HSVToRGB(0.86, 1.00, 0.78)},
wine= {HSVToRGB(0.92, 0.98, 0.91)},
lRed= {HSVToRGB(0.00, 0.38, 0.93)},
lFire= {HSVToRGB(0.04, 0.45, 0.91)},
lOrange= {HSVToRGB(0.10, 0.53, 0.92)},
lYellow= {HSVToRGB(0.14, 0.61, 0.95)},
lLime= {HSVToRGB(0.20, 0.66, 0.92)},
lJade= {HSVToRGB(0.26, 0.56, 0.90)},
lGreen= {HSVToRGB(0.34, 0.49, 0.89)},
lAqua= {HSVToRGB(0.47, 0.59, 0.86)},
lCyan= {HSVToRGB(0.51, 0.77, 0.88)},
lNavy= {HSVToRGB(0.54, 0.80, 0.95)},
lSea= {HSVToRGB(0.57, 0.72, 0.97)},
lBlue= {HSVToRGB(0.64, 0.44, 0.96)},
lViolet= {HSVToRGB(0.72, 0.47, 0.95)},
lPurple= {HSVToRGB(0.80, 0.62, 0.89)},
lMagenta= {HSVToRGB(0.86, 0.61, 0.89)},
lWine= {HSVToRGB(0.93, 0.57, 0.92)},
dRed= {HSVToRGB(0.00, 0.80, 0.48)},
dFire= {HSVToRGB(0.04, 0.80, 0.34)},
dOrange= {HSVToRGB(0.07, 0.80, 0.39)},
dYellow= {HSVToRGB(0.12, 0.80, 0.37)},
dLime= {HSVToRGB(0.20, 0.80, 0.26)},
dJade= {HSVToRGB(0.29, 0.80, 0.27)},
dGreen= {HSVToRGB(0.33, 0.80, 0.26)},
dAqua= {HSVToRGB(0.46, 0.80, 0.24)},
dCyan= {HSVToRGB(0.50, 0.80, 0.30)},
dNavy= {HSVToRGB(0.58, 0.80, 0.42)},
dSea= {HSVToRGB(0.64, 0.80, 0.40)},
dBlue= {HSVToRGB(0.67, 0.80, 0.34)},
dViolet= {HSVToRGB(0.71, 0.80, 0.35)},
dPurple= {HSVToRGB(0.76, 0.80, 0.32)},
dMagenta= {HSVToRGB(0.87, 0.80, 0.28)},
dWine= {HSVToRGB(0.92, 0.80, 0.28)},
black= {HSVToRGB(0.04, 0.04, 0.14)},
dGray= {HSVToRGB(0.02, 0.05, 0.44)},
gray= {HSVToRGB(0.02, 0.05, 0.65)},
lGray= {HSVToRGB(0.02, 0.06, 0.86)},
white= {HSVToRGB(0.01, 0.02, 0.99)},
xGray= {HSVToRGB(0.00, 0.00, 0.35,.8)},
lxGray= {HSVToRGB(0.00, 0.00, 0.62,.8)},
dxGray= {HSVToRGB(0.00, 0.00, 0.16,.8)},
}
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',
lR='lRed',lF='lFire',lO='lOrange',lY='lYellow',lL='lLime',lJ='lJade',lG='lGreen',lA='lAqua',lC='lCyan',lN='lNavy',lS='lSea',lB='lBlue',lV='lViolet',lP='lPurple',lM='lMagenta',lW='lWine',
dR='dRed',dF='dFire',dO='dOrange',dY='dYellow',dL='dLime',dJ='dJade',dG='dGreen',dA='dAqua',dC='dCyan',dN='dNavy',dS='dSea',dB='dBlue',dV='dViolet',dP='dPurple',dM='dMagenta',dW='dWine',
D='black',dH='dGray',H='gray',lH='lGray',Z='white',
X='xGray',lX='lxGray',dX='dxGray',
-- Remain letter: EIKQTU
} do
COLOR[k]=COLOR[v]
end
setmetatable(COLOR,{__index=function(_,k)
error("No color: "..tostring(k))
end})
do-- Random generators
local rnd=math.random
local list_norm={'red','fire','orange','yellow','lime','jade','green','aqua','cyan','navy','sea','blue','violet','purple','magenta','wine'}
local len_list_norm=#list_norm
function COLOR.random_norm()
return COLOR[list_norm[rnd(len_list_norm)]]
end
local list_bright={'lRed','lFire','lOrange','lYellow','lLime','lJade','lGreen','lAqua','lCyan','lNavy','lSea','lBlue','lViolet','lPurple','lMagenta','lWine'}
local len_list_bright=#list_bright
function COLOR.random_bright()
return COLOR[list_bright[rnd(len_list_bright)]]
end
local list_dark={'dRed','dFire','dOrange','dYellow','dLime','dJade','dGreen','dAqua','dCyan','dNavy','dSea','dBlue','dViolet','dPurple','dMagenta','dWine'}
local len_list_dark=#list_dark
function COLOR.random_dark()
return COLOR[list_dark[rnd(len_list_dark)]]
end
end
do-- Rainbow generators
local twoThirdsOfPi=2*math.pi/3
local sin=math.sin
function COLOR.rainbow(phase,alpha)
return
sin(phase)*.4+.6,
sin(phase+twoThirdsOfPi)*.4+.6,
sin(phase-twoThirdsOfPi)*.4+.6,
alpha
end
function COLOR.rainbow_light(phase,alpha)
return
sin(phase)*.2+.7,
sin(phase+twoThirdsOfPi)*.2+.7,
sin(phase-twoThirdsOfPi)*.2+.7,
alpha
end
function COLOR.rainbow_dark(phase,alpha)
return
sin(phase)*.2+.4,
sin(phase+twoThirdsOfPi)*.2+.4,
sin(phase-twoThirdsOfPi)*.2+.4,
alpha
end
function COLOR.rainbow_gray(phase,alpha)
return
sin(phase)*.16+.5,
sin(phase+twoThirdsOfPi)*.16+.5,
sin(phase-twoThirdsOfPi)*.16+.5,
alpha
end
end
do -- Color interpolation https://www.alanzucconi.com/2016/01/06/colour-interpolation/
local lerp=MATH.mix
function COLOR.interpolateRGB(rgba1,rgba2,t)
return
lerp(rgba1[1],rgba2[1],t),
lerp(rgba1[2],rgba2[2],t),
lerp(rgba1[3],rgba2[3],t),
((rgba1[4] and rgba2[4]) and lerp(rgba1[4],rgba1[4],t) or nil)
end
function COLOR.interpolateHSV(hsv1,hsv2,t)
local hue1,hue2=hsv1[1],hsv2[1]
if hue1>hue2 then
hue1,hue2=hue2,hue1
t=1-t
end
local hueDiff=hue2-hue1
local finalHue=.0
if hueDiff>.5 then
hue1=hue1+1
finalHue=(hue1+t*(hue2-hue1))%1;
else
finalHue=hue1+t*hueDiff
end
return finalHue,
lerp(hsv1[2],hsv2[2],t),
lerp(hsv1[3],hsv2[3],t),
((hsv1[4] and hsv2[4]) and lerp(hsv1[4],hsv1[4],t) or nil)
end
end
--[[
Returns either color1 or color2 based on time.
Args:
- color1, color2: colors to switch between
- period: the length of time in a cycle in seconds. ("wavelength")
- percentage [optional]: percentage of time in the color1 phase (default: 50%)
]]
function COLOR.flicker(color1,color2,period,percentage)
percentage=percentage or .5
return TIME()%period>percentage*period and color1 or color2
end
local lerpRGB,lerpHSV=COLOR.interpolateRGB,COLOR.interpolateHSV
local tau=MATH.tau
--[[
Oscillates between color1 and color2 over time in a sinusoidal fashion.
Args:
- color1, color2: colors to switch between.
[FORMAT IS BASED ON TYPE, WHICH DEFAULTS TO HSV]
Use COLOR.HSVtoRGB() and/or COLOR.RGBtoHSV to convert between color formats.
- period: the length of time in a cycle in seconds. (wavelength)
- type [optional]: the type of interpolation, defaulting to 'hsv'.
Supported values: nil, 'hsv', 'rgb'
]]
function COLOR.sine(color1,color2,period,type)
type=type or 'hsv'
local t=math.sin(tau*TIME()/period)
if type=='hsv' then
return lerpHSV(color1, color2, t)
elseif type=='rgb' then
return lerpRGB(color1, color2, t)
end
end
return COLOR

48
Zframework/diacritics.txt Normal file
View File

@@ -0,0 +1,48 @@
Á=A,Ă=A,Ắ=A,Ặ=A,Ằ=A,Ẳ=A,Ẵ=A,Ǎ=A,Â=A,Ấ=A,Ậ=A,Ầ=A,Ẩ=A,Ẫ=A,Ä=A,Ạ=A,À=A,Ả=A,Ā=A,Ą=A,Å=A,Ǻ=A,Ã=A,Æ=AE,Ǽ=AE
Ḅ=B,Ɓ=B,ʚ=B,ɞ=B
Ć=C,Č=C,Ç=C,Ĉ=C,Ċ=C,Ɔ=C,ʗ=C
Ď=D,Ḓ=D,Ḍ=D,Ɗ=D,Ḏ=D,Dz=DZ,Dž=DZ,Đ=D,Ð=D,DZ=DZ,DŽ=DZ
É=E,Ĕ=E,Ě=E,Ê=E,Ế=E,Ệ=E,Ề=E,Ể=E,Ễ=E,Ë=E,Ė=E,Ẹ=E,È=E,Ẻ=E,Ē=E,Ę=E,Ẽ=E,Ɛ=E,Ə=E
Ƒ=F
Ǵ=G,Ğ=G,Ǧ=G,Ģ=G,Ĝ=G,Ġ=G,Ḡ=G,ʛ=G
Ḫ=H,Ĥ=H,Ḥ=H,Ħ=H
Í=I,Ĭ=I,Ǐ=I,Î=I,Ï=I,İ=I,Ị=I,Ì=I,Ỉ=I,Ī=I,Į=I,Ĩ=I,IJ=IJ
Ĵ=J
Ķ=K,Ḳ=K,Ƙ=K,Ḵ=K
Ĺ=L,Ƚ=L,Ľ=L,Ļ=L,Ḽ=L,Ḷ=L,Ḹ=L,Ḻ=L,Ŀ=L,Lj=Lj,Ł=L,LJ=LJ
Ḿ=M,Ṁ=M,Ṃ=M
Ń=N,Ň=N,Ņ=N,Ṋ=N,Ṅ=N,Ṇ=N,Ǹ=N,Ɲ=N,Ṉ=N,Nj=Nj,Ñ=N,NJ=NJ
Ó=O,Ŏ=O,Ǒ=O,Ô=O,Ố=O,Ộ=O,Ồ=O,Ổ=O,Ỗ=O,Ö=O,Ọ=O,Ő=O,Ò=O,Ỏ=O,Ơ=O,Ớ=O,Ợ=O,Ờ=O,Ở=O,Ỡ=O,Ō=O,Ɵ=O,Ǫ=O,Ø=O,Ǿ=O,Õ=O,Œ=OE,ɶ=Oe
Þ=P
Ŕ=R,Ř=R,Ŗ=R,Ṙ=R,Ṛ=R,Ṝ=R,Ṟ=R,ʁ=R
Ś=S,Š=S,Ş=S,Ŝ=S,Ș=S,Ṡ=S,Ṣ=S,ẞ=S
Ť=T,Ţ=T,Ṱ=T,Ț=T,Ṭ=T,Ṯ=T,Ŧ=T,Þ=T,Ð=T
Ú=U,Ŭ=U,Ǔ=U,Û=U,Ü=U,Ǘ=U,Ǚ=U,Ǜ=U,Ǖ=U,Ụ=U,Ű=U,Ù=U,Ủ=U,Ư=U,Ứ=U,Ự=U,Ừ=U,Ử=U,Ữ=U,Ū=U,Ų=U,Ů=U,Ũ=U
Ẃ=W,Ŵ=W,Ẅ=W,Ẁ=W,ʬ=W
Ý=Y,Ŷ=Y,Ÿ=Y,Ẏ=Y,Ỵ=Y,Ỳ=Y,Ƴ=Y,Ỷ=Y,Ȳ=Y,Ỹ=Y
Ź=Z,Ž=Z,Ż=Z,Ẓ=Z,Ẕ=Z,Ƶ=Z
á=a,ă=a,ắ=a,ặ=a,ằ=a,ẳ=a,ẵ=a,ǎ=a,â=a,ấ=a,ậ=a,ầ=a,ẩ=a,ẫ=a,ä=a,ạ=a,à=a,ả=a,ā=a,ą=a,å=a,ǻ=a,ã=a,æ=a,ǽ=a,ɑ=a,ɐ=a,ɒ=a
ḅ=b,ɓ=b,ß=b
ć=c,č=c,ç=c,ĉ=c,ɕ=c,ċ=c
ď=d,ḓ=d,ḍ=d,ɗ=d,ḏ=d,đ=d,ɖ=d,ʤ=d,dz=d,ʣ=d,ʥ=d,dž=d,ð=d
é=e,ĕ=e,ě=e,ê=e,ế=e,ệ=e,ề=e,ể=e,ễ=e,ë=e,ė=e,ẹ=e,è=e,ẻ=e,ē=e,ę=e,ẽ=e,ʒ=e,ǯ=e,ʓ=e,ɘ=e,ɜ=e,ɝ=e,ə=e,ɚ=e,ʚ=e,ɞ=e
ƒ=f,ſ=f,ʩ=f,fi=f,fl=f,ʃ=f,ʆ=f,ʅ=f,ɟ=f,ʄ=f
ǵ=g,ğ=g,ǧ=g,ģ=g,ĝ=g,ġ=g,ɠ=g,ḡ=g,ɡ=g,ɣ=g
ḫ=h,ĥ=h,ḥ=h,ɦ=h,ẖ=h,ħ=h,ɧ=h,ɥ=h,ʮ=h,ʯ=h,ų=h
í=i,ĭ=i,ǐ=i,î=i,ï=i,ị=i,ì=i,ỉ=i,ī=i,į=i,ɨ=i,ĩ=i,ɩ=i,ı=i,ij=ij,ɟ=i
ǰ=j,ĵ=j,ʝ=j,ȷ=j,ɟ=j,ʄ=j
ķ=k,ḳ=k,ƙ=k,ḵ=k,ĸ=k,ʞ=k
ĺ=l,ƚ=l,ɬ=l,ľ=l,ļ=l,ḽ=l,ḷ=l,ḹ=l,ḻ=l,ŀ=l,ɫ=l,ɭ=l,ł=l,ƛ=l,ɮ=lz,lj=lj,ʪ=ls,ʫ=lz
ḿ=m,ṁ=m,ṃ=m,ɱ=m,ɯ=m,ɰ=m
ʼn=n,ń=n,ň=n,ņ=n,ṋ=n,ṅ=n,ṇ=n,ǹ=n,ɲ=n,ṉ=n,ɳ=n,ñ=n,nj=nj,ŋ=n,Ŋ=n
ó=o,ŏ=o,ǒ=o,ô=o,ố=o,ộ=o,ồ=o,ổ=o,ỗ=o,ö=o,ọ=o,ő=o,ò=o,ỏ=o,ơ=o,ớ=o,ợ=o,ờ=o,ở=o,ỡ=o,ō=o,ǫ=o,ø=o,ǿ=o,õ=o,ɛ=o,ɔ=o,ɵ=o,ʘ=o,œ=oe
ɸ=p,þ=p
ʠ=q
ŕ=r,ř=r,ŗ=r,ṙ=r,ṛ=r,ṝ=r,ɾ=r,ṟ=r,ɼ=r,ɽ=r,ɿ=r,ɹ=r,ɻ=r,ɺ=r
ś=s,š=s,ş=s,ŝ=s,ș=s,ṡ=s,ṣ=s,ʂ=s,ſ=s,ʃ=s,ʆ=s,ß=s,ʅ=s
ť=t,ţ=t,ṱ=t,ț=t,ẗ=t,ṭ=t,ṯ=t,ʈ=t,ŧ=t,ʨ=t,ʧ=t,þ=t,ð=t,ʦ=t,ʇ=t
ʉ=u,ú=u,ŭ=u,ǔ=u,û=u,ü=u,ǘ=u,ǚ=u,ǜ=u,ǖ=u,ụ=u,ű=u,ù=u,ủ=u,ư=u,ứ=u,ự=u,ừ=u,ử=u,ữ=u,ū=u,ų=u,ů=u,ũ=u,ʊ=u
ʋ=v,ʌ=v
ẃ=w,ŵ=w,ẅ=w,ẁ=w,ʍ=w
ý=y,ŷ=y,ÿ=y,ẏ=y,ỵ=y,ỳ=y,ƴ=y,ỷ=y,ȳ=y,ỹ=y,ʎ=y
ź=z,ž=z,ʑ=z,ż=z,ẓ=z,ẕ=z,ʐ=z,ƶ=z

112
Zframework/file.lua Normal file
View File

@@ -0,0 +1,112 @@
local fs=love.filesystem
local FILE={}
function FILE.isSafe(file)
return SYSTEM=='Web' or fs.getRealDirectory(file)~=fs.getSaveDirectory()
end
function FILE.load(name,args)
if not args then args='' end
if fs.getInfo(name) then
local F=fs.newFile(name)
assert(F:open'r','open error')
local s=F:read() F:close()
local mode=
STRING.sArg(args,'-luaon') and 'luaon' or
STRING.sArg(args,'-lua') and 'lua' or
STRING.sArg(args,'-json') and 'json' or
STRING.sArg(args,'-string') and 'string' or
s:sub(1,9):find('return%s*%{') and 'luaon' or
(s:sub(1,1)=='[' and s:sub(-1)==']' or s:sub(1,1)=='{' and s:sub(-1)=='}') and 'json' or
'string'
if mode=='luaon' then
local func,err_mes=loadstring(s)
if func then
setfenv(func,{})
local res=func()
return assert(res,'decode error')
else
error('decode error: '..err_mes)
end
elseif mode=='lua' then
local func,err_mes=loadstring(s)
if func then
local res=func()
return assert(res,'run error')
else
error('compile error: '..err_mes)
end
elseif mode=='json' then
local res=JSON.decode(s)
if res then
return res
end
error('decode error')
elseif mode=='string' then
return s
else
error('unknown mode')
end
elseif not STRING.sArg(args,'-canskip') then
error('no file')
end
end
function FILE.save(data,name,args)
if not args then args='' end
if STRING.sArg(args,'-d') and fs.getInfo(name) then
error('duplicate')
end
if type(data)=='table' then
if STRING.sArg(args,'-luaon') then
if STRING.sArg(args,'-expand') then
data=TABLE.dump(data)
else
data='return'..TABLE.dumpDeflate(data)
end
if not data then
error('encode error')
end
else
data=JSON.encode(data)
if not data then
error('encode error')
end
end
else
data=tostring(data)
end
local F=fs.newFile(name)
assert(F:open('w'),'open error')
F:write(data) F:flush() F:close()
end
function FILE.clear(path)
if not FILE.isSafe(path) and fs.getInfo(path).type=='directory' then
for _,name in next,fs.getDirectoryItems(path) do
name=path..'/'..name
if not FILE.isSafe(name) then
local t=fs.getInfo(name).type
if t=='file' then
fs.remove(name)
end
end
end
end
end
function FILE.clear_s(path)
if path=='' or (not FILE.isSafe(path) and fs.getInfo(path).type=='directory') then
for _,name in next,fs.getDirectoryItems(path) do
name=path..'/'..name
if not FILE.isSafe(name) then
local t=fs.getInfo(name).type
if t=='file' then
fs.remove(name)
elseif t=='directory' then
FILE.clear_s(name)
fs.remove(name)
end
end
end
fs.remove(path)
end
end
return FILE

59
Zframework/font.lua Normal file
View File

@@ -0,0 +1,59 @@
local set=GC.setFont
local fontFiles,fontCache={},{}
local defaultFont,defaultFallBack
local curFont=false-- Current using font object
local FONT={}
function FONT.setDefault(name) defaultFont=name end
function FONT.setFallback(name) defaultFallBack=name end
function FONT.rawget(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))
end
function FONT.load(fonts)
for name,path in next,fonts do
assert(love.filesystem.getInfo(path),STRING.repD("Font file $1($2) not exist!",name,path))
fontFiles[name]=love.filesystem.newFile(path)
fontCache[name]={}
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

184
Zframework/gcExtend.lua Normal file
View File

@@ -0,0 +1,184 @@
local gc=love.graphics
local setColor,printf,draw=gc.setColor,gc.printf,gc.draw
local sin,cos=math.sin,math.cos
local GC=setmetatable({},{
__index=gc,
__metatable=true
})
function GC.mStr(obj,x,y) printf(obj,x-626,y,1252,'center') end-- Printf a string with 'center'
function GC.simpX(obj,x,y) draw(obj,x-obj:getWidth()*.5,y) end-- Simply draw an obj with x=obj:getWidth()/2
function GC.simpY(obj,x,y) draw(obj,x,y-obj:getHeight()*.5) end-- Simply draw an obj with y=obj:getWidth()/2
function GC.X(obj,x,y,a,k) draw(obj,x,y,a,k,nil,obj:getWidth()*.5,0) end-- Draw an obj with x=obj:getWidth()/2
function GC.Y(obj,x,y,a,k) draw(obj,x,y,a,k,nil,0,obj:getHeight()*.5) end-- Draw an obj with y=obj:getWidth()/2
function GC.mDraw(obj,x,y,a,k) draw(obj,x,y,a,k,nil,obj:getWidth()*.5,obj:getHeight()*.5) end-- Draw an obj with both middle X & Y
function GC.outDraw(obj,div,x,y,a,k)
local w,h=obj:getWidth()*.5,obj:getHeight()*.5
draw(obj,x-div,y-div,a,k,nil,w,h)
draw(obj,x-div,y+div,a,k,nil,w,h)
draw(obj,x+div,y-div,a,k,nil,w,h)
draw(obj,x+div,y+div,a,k,nil,w,h)
end
function GC.shadedPrint(str,x,y,mode,d,clr1,clr2)
local w=1280
if mode=='center' then
x=x-w*.5
elseif mode=='right' then
x=x-w
end
if not d then d=1 end
setColor(clr1 or COLOR.D)
printf(str,x-d,y-d,w,mode)
printf(str,x-d,y+d,w,mode)
printf(str,x+d,y-d,w,mode)
printf(str,x+d,y+d,w,mode)
setColor(clr2 or COLOR.Z)
printf(str,x,y,w,mode)
end
function GC.regPolygon(mode,x,y,R,segments,phase)
local l={}
local ang=phase or 0
local angStep=6.283185307179586/segments
for i=1,segments do
l[2*i-1]=x+R*cos(ang)
l[2*i]=y+R*sin(ang)
ang=ang+angStep
end
gc.polygon(mode,l)
end
function GC.regRoundPolygon(mode,x,y,R,segments,r,phase)
local X,Y={},{}
local ang=phase or 0
local angStep=6.283185307179586/segments
for i=1,segments do
X[i]=x+R*cos(ang)
Y[i]=y+R*sin(ang)
ang=ang+angStep
end
X[segments+1]=x+R*cos(ang)
Y[segments+1]=y+R*sin(ang)
local halfAng=6.283185307179586/segments/2
local erasedLen=r*math.tan(halfAng)
if mode=='line' then
erasedLen=erasedLen+1-- Fix 1px cover
for i=1,segments do
-- Line
local x1,y1,x2,y2=X[i],Y[i],X[i+1],Y[i+1]
local dir=math.atan2(y2-y1,x2-x1)
gc.line(x1+erasedLen*cos(dir),y1+erasedLen*sin(dir),x2-erasedLen*cos(dir),y2-erasedLen*sin(dir))
-- Arc
ang=ang+angStep
local R2=R-r/cos(halfAng)
local arcCX,arcCY=x+R2*cos(ang),y+R2*sin(ang)
gc.arc('line','open',arcCX,arcCY,r,ang-halfAng,ang+halfAng)
end
elseif mode=='fill' then
local L={}
for i=1,segments do
-- Line
local x1,y1,x2,y2=X[i],Y[i],X[i+1],Y[i+1]
local dir=math.atan2(y2-y1,x2-x1)
L[4*i-3]=x1+erasedLen*cos(dir)
L[4*i-2]=y1+erasedLen*sin(dir)
L[4*i-1]=x2-erasedLen*cos(dir)
L[4*i]=y2-erasedLen*sin(dir)
-- Arc
ang=ang+angStep
local R2=R-r/cos(halfAng)
local arcCX,arcCY=x+R2*cos(ang),y+R2*sin(ang)
gc.arc('fill','open',arcCX,arcCY,r,ang-halfAng,ang+halfAng)
end
gc.polygon('fill',L)
else
error("Draw mode should be 'line' or 'fill'")
end
end
do-- function GC.DO(L)
local cmds={
origin="origin",
move="translate",
scale="scale",
rotate="rotate",
shear="shear",
clear="clear",
setCL="setColor",
setCM="setColorMask",
setLW="setLineWidth",
setLS="setLineStyle",
setLJ="setLineJoin",
setBM="setBlendMode",
print="print",
rawFT=function(...)FONT.rawset(...) end,
setFT=function(...)FONT.set(...) end,
mText=GC.mStr,
mDraw=GC.mDraw,
mDrawX=GC.X,
mDrawY=GC.Y,
mOutDraw=GC.outDraw,
draw="draw",
line="line",
fRect=function(...)gc.rectangle('fill',...) end,
dRect=function(...)gc.rectangle('line',...) end,
fCirc=function(...)gc.circle('fill',...) end,
dCirc=function(...)gc.circle('line',...) end,
fElps=function(...)gc.ellipse('fill',...) end,
dElps=function(...)gc.ellipse('line',...) end,
fPoly=function(...)gc.polygon('fill',...) end,
dPoly=function(...)gc.polygon('line',...) end,
dPie=function(...)gc.arc('line',...) end,
dArc=function(...)gc.arc('line','open',...) end,
dBow=function(...)gc.arc('line','closed',...) end,
fPie=function(...)gc.arc('fill',...) end,
fArc=function(...)gc.arc('fill','open',...) end,
fBow=function(...)gc.arc('fill','closed',...) end,
fRPol=function(...)GC.regPolygon('fill',...) end,
dRPol=function(...)GC.regPolygon('line',...) end,
fRRPol=function(...)GC.regRoundPolygon('fill',...) end,
dRRPol=function(...)GC.regRoundPolygon('line',...) end,
}
local sizeLimit=gc.getSystemLimits().texturesize
function GC.DO(L)
gc.push()
local success,canvas
repeat
success,canvas=pcall(gc.newCanvas,math.min(L[1],sizeLimit),math.min(L[2],sizeLimit))
if not success then
sizeLimit=math.floor(sizeLimit*.8)
end
until success
gc.setCanvas(canvas)
gc.origin()
gc.clear(1,1,1,0)
gc.setColor(1,1,1)
gc.setLineWidth(1)
for i=3,#L do
local cmd=L[i][1]
if type(cmd)=='boolean' and cmd then
table.remove(L[i],1)
cmd=L[i][1]
end
if type(cmd)=='string' then
local func=cmds[cmd] or gc[cmd]
if type(func)=='string' then
func=gc[func]
end
if func then
func(unpack(L[i],2))
else
error("No gc command: "..cmd)
end
end
end
gc.setCanvas()
gc.pop()
return canvas
end
end
return GC

191
Zframework/http.lua Normal file
View File

@@ -0,0 +1,191 @@
local sendCHN=love.thread.getChannel('inputChannel')
local recvCHN=love.thread.getChannel('outputChannel')
local threads={}
local threadCount=0
local threadCode=[[
local id=...
local http=require'socket.http'
local ltn12=require'ltn12'
local sendCHN=love.thread.getChannel('inputChannel')
local recvCHN=love.thread.getChannel('outputChannel')
while true do
local arg=sendCHN:demand()
if arg._destroy then
recvCHN:push{
destroy=true,
id=id,
}
break
end
-- print("\n------SEND------") for k,v in next,arg do print(k,v) end
local data={}
local _,code,detail=http.request{
method=arg.method,
url=arg.url,
headers=arg.headers,
source=ltn12.source.string(arg.body),
sink=ltn12.sink.table(data),
}
local result={
pool=arg.pool,
poolPtr=arg.poolPtr,
code=code,
body=table.concat(data),
detail=detail,
}
-- print("\n------RECV------") for k,v in next,result do print(k,v) end
recvCHN:push(result)
end
]]
local msgPool=setmetatable({},{
__index=function(self,k)
self[k]={}
return self[k]
end
})
local HTTP={
_msgCount=0,
_trigTime=0,
_trigInterval=.26,
_host=false,
}
local function addThread(num)
for i=1,26 do
if num<=0 then break end
if not threads[i] then
threads[i]=love.thread.newThread(threadCode)
threads[i]:start(i)
threadCount=threadCount+1
num=num-1
end
end
end
function HTTP.request(arg)
arg.method=arg.method or arg.body and 'POST' or 'GET'
if arg.url then
assert(type(arg.url)=='string',"Field 'url' need string, get "..type(arg.url))
if arg.url:sub(1,7)~='http://' then arg.url='http://'..arg.url end
else
arg.url=HTTP._host or error("Need url=<string> or set default host with HTTP.setHost")
end
if arg.path then
assert(type(arg.path)=='string',"Field 'path' need string, get "..type(arg.path))
arg.url=arg.url..arg.path
end
assert(arg.headers==nil or type(arg.headers)=='table',"Field 'headers' need table, get "..type(arg.headers))
if arg.body~=nil then
assert(type(arg.body)=='table',"Field 'body' need table, get "..type(arg.body))
arg.body=JSON.encode(arg.body)
if not arg.headers then arg.headers={} end
TABLE.cover({
['Content-Type']="application/json",
['Content-Length']=#arg.body,
},arg.headers)
end
if arg.pool==nil then arg.pool='_default' end
arg.poolPtr=tostring(msgPool[arg.pool])
sendCHN:push(arg)
end
function HTTP.reset()
for i=1,#threads do
threads[i]:release()
threads[i]=false
end
TABLE.clear(msgPool)
sendCHN:clear()
recvCHN:clear()
addThread(threadCount)
end
function HTTP.setThreadCount(n)
assert(type(n)=='number' and n>=1 and n<=26 and n%1==0,"function HTTP.setThreadCount(n): n must be integer from 1 to 26")
if n>threadCount then
addThread(n-threadCount)
else
for _=n+1,threadCount do
sendCHN:push{_destroy=true}
end
end
end
function HTTP.getThreadCount()
return threadCount
end
function HTTP.setInterval(interval)
if interval<=0 then interval=1e99 end
assert(type(interval)=='number',"Interval must be number")
HTTP._trigInterval=interval
end
function HTTP.clearPool(pool)
if pool==nil then pool='_default' end
assert(type(pool)=='string',"Pool must be nil or string")
HTTP._msgCount=HTTP._msgCount-#msgPool[pool]
msgPool[pool]={}
end
function HTTP.deletePool(pool)
assert(type(pool)=='string',"Pool must be nil or string")
assert(pool~='_default',"Cannot delete _default pool. What are you doing?")
HTTP._msgCount=HTTP._msgCount-#msgPool[pool]
msgPool[pool]=nil
end
function HTTP.pollMsg(pool)
if not (type(pool)=='nil' or type(pool)=='string') then error("Pool must be nil or string") end
HTTP.update()
local p=msgPool[pool or '_default']
if #p>0 then
HTTP._msgCount=HTTP._msgCount-1
return table.remove(p)
end
end
function HTTP.setHost(host)
assert(type(host)=='string',"Host must be string")
if host:sub(1,7)~='http://' then host='http://'..host end
HTTP._host=host
end
function HTTP.update(dt)
if dt then
HTTP._trigTime=HTTP._trigTime+dt
if HTTP._trigTime>HTTP._trigInterval then
HTTP._trigTime=HTTP._trigTime%HTTP._trigInterval
else
return
end
end
while recvCHN:getCount()>0 do
local m=recvCHN:pop()
if m.destroy then
threads[m.id]:release()
threads[m.id]=false
elseif tostring(msgPool[m.pool])==m.poolPtr then -- If pool were cleared, discard this datapack
table.insert(msgPool[m.pool],{
code=m.code,
body=m.body,
detail=m.detail,
})
HTTP._msgCount=HTTP._msgCount+1
end
end
end
setmetatable(HTTP,{__call=function(self,arg)
return self.request(arg)
end,__metatable=true})
HTTP.reset()
return HTTP

25
Zframework/image.lua Normal file
View File

@@ -0,0 +1,25 @@
local IMG={}
function IMG.init(list)
IMG.init=nil
setmetatable(IMG,{__index=function(self,name)
if type(list[name])=='table' then
self[name]={}
for k,v in next,list[name] do
self[name][k]=love.graphics.newImage(v)
end
elseif type(list[name])=='string' then
self[name]=love.graphics.newImage(list[name])
else
LOG("No IMG: "..name)
self[name]=PAPER
end
return self[name]
end})
function IMG.loadAll()
for k in next,list do local _=IMG[k] end
IMG.loadAll=nil
end
end
return IMG

932
Zframework/init.lua Normal file
View File

@@ -0,0 +1,932 @@
NONE={}function NULL() end PAPER=love.graphics.newCanvas(1,1)
EDITING=""
LOADED=false
SYSTEM=love.system.getOS()
if SYSTEM=='OS X' then SYSTEM='macOS' end
-- Bit module
local success
success,bit=pcall(require,"bit")
if not success then
bit=require"Zframework.bitop".bit
end
-- Pure lua modules (basic)
MATH= require'Zframework.mathExtend'
COLOR= require'Zframework.color'
TABLE= require'Zframework.tableExtend'
STRING= require'Zframework.stringExtend'
PROFILE= require'Zframework.profile'
JSON= require'Zframework.json'
TEST= require'Zframework.test'
do-- Add pcall & MES for JSON lib
local encode,decode=JSON.encode,JSON.decode
JSON.encode=function(val)
local a,b=pcall(encode,val)
if a then
return b
elseif MES then
MES.traceback()
end
end
JSON.decode=function(str)
local a,b=pcall(decode,str)
if a then
return b
elseif MES then
MES.traceback()
end
end
end
-- Pure lua modules (complex)
LOG= require'Zframework.log'
REQUIRE= require'Zframework.require'
TASK= require'Zframework.task'
LANG= require'Zframework.languages'
HASH= require'Zframework.sha2'
do
local bxor=bit.bxor
local char=string.char
local function sxor(s1, s2)
local b3=""
for i=1,#s1 do
b3=b3..char(bxor(s1:byte(i),s2:byte(i)))
end
return b3
end
function HASH.pbkdf2(hashFunc, pw, salt, n)
local u=HASH.hex2bin(HASH.hmac(hashFunc, pw, salt.."\0\0\0\1"))
local t=u
for _=2,n do
u=HASH.hex2bin(HASH.hmac(hashFunc, pw, u))
t=sxor(t, u)
end
return HASH.bin2hex(t):upper()
end
end
-- Love-based modules (basic)
HTTP= require'Zframework.http'
WS= require'Zframework.websocket'
FILE= require'Zframework.file'
WHEELMOV= require'Zframework.wheelScroll'
SCR= require'Zframework.screen'
SCN= require'Zframework.scene'
-- Love-based modules (complex)
GC= require'Zframework.gcExtend'
FONT= require'Zframework.font'
TEXT= require'Zframework.text'
SYSFX= require'Zframework.sysFX'
WAIT= require'Zframework.wait'
MES= require'Zframework.message'
BG= require'Zframework.background'
WIDGET= require'Zframework.widget'
VIB= require'Zframework.vibrate'
SFX= require'Zframework.sfx'
IMG= require'Zframework.image'
BGM= require'Zframework.bgm'
VOC= require'Zframework.voice'
local ms,kb=love.mouse,love.keyboard
local KBisDown=kb.isDown
local gc=love.graphics
local gc_push,gc_pop,gc_clear,gc_discard=gc.push,gc.pop,gc.clear,gc.discard
local gc_replaceTransform,gc_present=gc.replaceTransform,gc.present
local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth
local gc_draw,gc_line,gc_circle,gc_print=gc.draw,gc.line,gc.circle,gc.print
local BG,WIDGET,SCR,SCN,WAIT=BG,WIDGET,SCR,SCN,WAIT
local xOy=SCR.xOy
local ITP=xOy.inverseTransformPoint
local max,min=math.max,math.min
local debugMode
local mx,my,mouseShow,cursorSpd=640,360,false,0
local jsState={}-- map, joystickID->axisStates: {axisName->axisVal}
local errData={}-- list, each error create {mes={errMes strings},scene=sceneNameStr}
local function drawCursor(_,x,y)
gc_setColor(1,1,1)
gc_setLineWidth(2)
gc_circle(ms.isDown(1) and 'fill' or 'line',x,y,6)
end
local showPowerInfo=true
local showClickFX=true
local discardCanvas=false
local frameMul=100
local sleepInterval=1/60
local onQuit=NULL
local onBeforeQuit=false
local versionText=""
local batteryImg=GC.DO{31,20,
{'fRect',1,0,26,2},
{'fRect',1,18,26,2},
{'fRect',0,1,2,18},
{'fRect',26,1,2,18},
{'fRect',29,3,2,14},
}
local infoCanvas=gc.newCanvas(108,27)
local function updatePowerInfo()
local state,pow=love.system.getPowerInfo()
gc.setCanvas(infoCanvas)
gc_push('transform')
gc.origin()
gc_clear(0,0,0,.25)
if state~='unknown' then
gc_setLineWidth(4)
if state=='nobattery' then
gc_setColor(1,1,1)
gc_setLineWidth(2)
gc_line(74,5,100,22)
elseif pow then
if state=='charging' then gc_setColor(0,1,0)
elseif pow>50 then gc_setColor(1,1,1)
elseif pow>26 then gc_setColor(1,1,0)
elseif pow==26 then gc_setColor(.5,0,1)
else gc_setColor(1,0,0)
end
gc.rectangle('fill',76,6,pow*.22,14)
if pow<100 then
FONT.set(15)
gc.setColor(COLOR.D)
gc_print(pow,77,1)
gc_print(pow,77,3)
gc_print(pow,79,1)
gc_print(pow,79,3)
gc_setColor(COLOR.Z)
gc_print(pow,78,2)
end
end
gc_draw(batteryImg,73,3)
end
FONT.set(25)
gc_print(os.date("%H:%M"),3,-5)
gc_pop()
gc.setCanvas()
end
-------------------------------------------------------------
local lastX,lastY=0,0-- Last click pos
local function _updateMousePos(x,y,dx,dy)
if SCN.swapping or WAIT.state then return end
dx,dy=dx/SCR.k,dy/SCR.k
if SCN.mouseMove then SCN.mouseMove(x,y,dx,dy) end
if ms.isDown(1) then
WIDGET.drag(x,y,dx,dy)
else
WIDGET.cursorMove(x,y)
end
end
local function mouse_update(dt)
if not KBisDown('lctrl','rctrl') and KBisDown('up','down','left','right') then
local dx,dy=0,0
if KBisDown('up') then dy=dy-cursorSpd end
if KBisDown('down') then dy=dy+cursorSpd end
if KBisDown('left') then dx=dx-cursorSpd end
if KBisDown('right') then dx=dx+cursorSpd end
mx=max(min(mx+dx,1280),0)
my=max(min(my+dy,720),0)
if my==0 or my==720 then
WIDGET.sel=false
WIDGET.drag(0,0,0,-dy)
end
_updateMousePos(mx,my,dx,dy)
cursorSpd=min(cursorSpd+dt*26,12.6)
else
cursorSpd=6
end
end
local function gp_update(js,dt)
local sx,sy=js._jsObj:getGamepadAxis('leftx'),js._jsObj:getGamepadAxis('lefty')
if math.abs(sx)>.1 or math.abs(sy)>.1 then
local dx,dy=0,0
if sy<-.1 then dy=dy+2*sy*cursorSpd end
if sy>.1 then dy=dy+2*sy*cursorSpd end
if sx<-.1 then dx=dx+2*sx*cursorSpd end
if sx>.1 then dx=dx+2*sx*cursorSpd end
mx=max(min(mx+dx,1280),0)
my=max(min(my+dy,720),0)
if my==0 or my==720 then
WIDGET.sel=false
WIDGET.drag(0,0,0,-dy)
end
_updateMousePos(mx,my,dx,dy)
cursorSpd=min(cursorSpd+dt*26,12.6)
else
cursorSpd=6
end
end
function love.mousepressed(x,y,k,touch)
if touch or WAIT.state then return end
mouseShow=true
mx,my=ITP(xOy,x,y)
if debugMode==1 then
print(("(%d,%d)<-%d,%d ~~(%d,%d)<-%d,%d"):format(
mx,my,
mx-lastX,my-lastY,
math.floor(mx/10)*10,math.floor(my/10)*10,
math.floor((mx-lastX)/10)*10,math.floor((my-lastY)/10)*10
))
end
if SCN.swapping then return end
if SCN.mouseDown then SCN.mouseDown(mx,my,k) end
WIDGET.press(mx,my,k)
lastX,lastY=mx,my
if showClickFX then SYSFX.newTap(3,mx,my) end
end
function love.mousemoved(x,y,dx,dy,touch)
if touch then return end
mouseShow=true
mx,my=ITP(xOy,x,y)
_updateMousePos(mx,my,dx,dy)
end
function love.mousereleased(x,y,k,touch)
if touch or WAIT.state or SCN.swapping then return end
mx,my=ITP(xOy,x,y)
if SCN.mouseUp then SCN.mouseUp(mx,my,k) end
if WIDGET.sel then
WIDGET.release(mx,my,k)
else
if lastX and SCN.mouseClick and (mx-lastX)^2+(my-lastY)^2<62 then
SCN.mouseClick(mx,my,k)
end
end
end
function love.wheelmoved(x,y)
if math.abs(x)>=100 then x=x/100 end
if math.abs(y)>=100 then y=y/100 end
if WAIT.state or SCN.swapping then return end
if SCN.wheelMoved then
SCN.wheelMoved(x,y)
else
WIDGET.unFocus()
WIDGET.drag(0,0,0,100*y)
end
end
function love.touchpressed(id,x,y)
mouseShow=false
if WAIT.state or SCN.swapping then return end
if not SCN.mainTouchID then
SCN.mainTouchID=id
WIDGET.unFocus(true)
love.touchmoved(id,x,y,0,0)
end
x,y=ITP(xOy,x,y)
lastX,lastY=x,y
if SCN.touchDown then SCN.touchDown(x,y,id) end
if kb.hasTextInput() then kb.setTextInput(false) end
WIDGET.cursorMove(x,y)
WIDGET.press(x,y,1)
end
function love.touchmoved(id,x,y,dx,dy)
if WAIT.state or SCN.swapping then return end
x,y=ITP(xOy,x,y)
if SCN.touchMove then SCN.touchMove(x,y,dx/SCR.k,dy/SCR.k,id) end
WIDGET.drag(x,y,dx/SCR.k,dy/SCR.k)
end
function love.touchreleased(id,x,y)
if WAIT.state or SCN.swapping then return end
x,y=ITP(xOy,x,y)
if id==SCN.mainTouchID then
WIDGET.release(x,y,1)
WIDGET.cursorMove(x,y)
WIDGET.unFocus()
SCN.mainTouchID=false
end
if SCN.touchUp then SCN.touchUp(x,y,id) end
if (x-lastX)^2+(y-lastY)^2<62 then
if SCN.touchClick then SCN.touchClick(x,y) end
if showClickFX then SYSFX.newTap(3,x,y) end
end
end
-- function love.mousepressed(x,y,k) love.touchpressed(1,x,y) end
-- function love.mousemoved(x,y,dx,dy,touch) love.touchmoved(1,x,y,dx,dy) end
-- function love.mousereleased(x,y,k) love.touchreleased(1,x,y) end
local globalKey={
f8=function()
debugMode=1
MES.new('info',"DEBUG ON",.2)
end
}
local fnKey={NULL,NULL,NULL,NULL,NULL,NULL,NULL}
local function debugKeyPressed(key)
if key=='f1' then fnKey[1]()
elseif key=='f2' then fnKey[2]()
elseif key=='f3' then fnKey[3]()
elseif key=='f4' then fnKey[4]()
elseif key=='f5' then fnKey[5]()
elseif key=='f6' then fnKey[6]()
elseif key=='f7' then fnKey[7]()
elseif key=='f8' then debugMode=nil MES.new('info',"DEBUG OFF",.2)
elseif key=='f9' then debugMode=1 MES.new('info',"DEBUG 1")
elseif key=='f10' then debugMode=2 MES.new('info',"DEBUG 2")
elseif key=='f11' then debugMode=3 MES.new('info',"DEBUG 3")
elseif key=='f12' then debugMode=4 MES.new('info',"DEBUG 4")
elseif debugMode==2 then
local W=WIDGET.sel
if W then
if key=='left' then W.x=W.x-10
elseif key=='right' then W.x=W.x+10
elseif key=='up' then W.y=W.y-10
elseif key=='down' then W.y=W.y+10
elseif key==',' then W.w=W.w-10
elseif key=='.' then W.w=W.w+10
elseif key=='/' then W.h=W.h-10
elseif key=='\'' then W.h=W.h+10
elseif key=='[' then W.font=W.font-5
elseif key==']' then W.font=W.font+5
else return
end
else
return
end
else
return
end
return true
end
function love.keypressed(key,_,isRep)
mouseShow=false
if debugMode and debugKeyPressed(key) then
-- Do nothing
elseif globalKey[key] then
globalKey[key]()
else
if SCN.swapping then return end
if WAIT.state then
if key=='escape' and WAIT.arg.escapable then WAIT.interrupt() end
return
end
if EDITING=="" and (not SCN.keyDown or SCN.keyDown(key,isRep)) then
local W=WIDGET.sel
if key=='escape' and not isRep then
SCN.back()
elseif key=='up' or key=='down' or key=='left' or key=='right' then
mouseShow=true
if KBisDown('lctrl','rctrl') then
if W and W.arrowKey then W:arrowKey(key) end
end
elseif key=='space' or key=='return' then
mouseShow=true
if not isRep then
if showClickFX then SYSFX.newTap(3,mx,my) end
love.mousepressed(mx,my,1)
love.mousereleased(mx,my,1)
end
else
if W and W.keypress then
W:keypress(key)
end
end
end
end
end
function love.keyreleased(i)
if WAIT.state or SCN.swapping then return end
if SCN.keyUp then SCN.keyUp(i) end
end
function love.textedited(texts)
EDITING=texts
end
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={
dpup='up',
dpdown='down',
dpleft='left',
dpright='right',
start='return',
back='escape',
}
function love.joystickadded(JS)
table.insert(jsState,{
_id=JS:getID(),
_jsObj=JS,
leftx=0,lefty=0,
rightx=0,righty=0,
triggerleft=0,triggerright=0
})
MES.new('info',"Joystick added")
end
function love.joystickremoved(JS)
for i=1,#jsState do
if jsState[i]._jsObj==JS then
for j=1,#gamePadKeys do
if JS:isGamepadDown(gamePadKeys[j]) then
love.gamepadreleased(JS,gamePadKeys[j])
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)
MES.new('info',"Joystick removed")
table.remove(jsState,i)
break
end
end
end
function love.gamepadaxis(JS,axis,val)
if jsState[1] and JS==jsState[1]._jsObj then
local js=jsState[1]
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: [0,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(_,key)
mouseShow=false
if not SCN.swapping then
local cursorCtrl
if SCN.gamepadDown then
cursorCtrl=SCN.gamepadDown(key)
elseif SCN.keyDown then
cursorCtrl=SCN.keyDown(dPadToKey[key] or key)
else
cursorCtrl=true
end
if cursorCtrl then
key=dPadToKey[key] or key
mouseShow=true
local W=WIDGET.sel
if key=='back' then
SCN.back()
elseif key=='up' or key=='down' or key=='left' or key=='right' then
mouseShow=true
if W and W.arrowKey then W:arrowKey(key) end
elseif key=='return' then
mouseShow=true
if showClickFX then SYSFX.newTap(3,mx,my) end
love.mousepressed(mx,my,1)
love.mousereleased(mx,my,1)
else
if W and W.keypress then
W:keypress(key)
end
end
end
end
end
function love.gamepadreleased(_,i)
if WAIT.state or SCN.swapping then return end
if SCN.gamepadUp then SCN.gamepadUp(i) end
end
function love.filedropped(file)
if WAIT.state or SCN.swapping then return end
if SCN.fileDropped then SCN.fileDropped(file) end
end
function love.directorydropped(dir)
if WAIT.state or SCN.swapping then return end
if SCN.directoryDropped then SCN.directoryDropped(dir) end
end
local autoGCcount=0
function love.lowmemory()
collectgarbage()
if autoGCcount<3 then
autoGCcount=autoGCcount+1
MES.new('check',"[auto GC] low MEM 设备内存过低")
end
end
local onResize=NULL
function love.resize(w,h)
if SCR.w==w and SCR.h==h then return end
SCR.resize(w,h)
if BG.resize then BG.resize(w,h) end
if SCN.resize then SCN.resize(w,h) end
WIDGET.resize(w,h)
FONT.reset()
onResize(w,h)
end
local onFocus=NULL
function love.focus(f) onFocus(f) end
local yield=coroutine.yield
local function secondLoopThread()
local mainLoop=love.run()
repeat yield() until mainLoop()
end
function love.errorhandler(msg)
if type(msg)~='string' then
msg="Unknown error"
elseif msg:find("Invalid UTF-8") and text then
msg=text.tryAnotherBuild
end
-- Generate error message
local err={"Error:"..msg}
local c=2
for l in debug.traceback("",2):gmatch("(.-)\n") do
if c>2 then
if not l:find("boot") then
err[c]=l:gsub("^\t*","")
c=c+1
end
else
err[2]="Traceback"
c=3
end
end
print(table.concat(err,"\n",1,c-2))
-- Reset something
love.audio.stop()
gc.reset()
local sceneStack=SCN and table.concat(SCN.stack,"/") or "NULL"
if LOADED and #errData<3 then
BG.set('none')
table.insert(errData,{mes=err,scene=sceneStack})
-- Write messages to log file
love.filesystem.append('conf/error.log',
os.date("%Y/%m/%d %A %H:%M:%S\n")..
#errData.." crash(es) "..SYSTEM.."-"..VERSION.string.." scene: "..sceneStack.."\n"..
table.concat(err,"\n",1,c-2).."\n\n"
)
-- Get screencapture
gc.captureScreenshot(function(_) errData[#errData].shot=gc.newImage(_) end)
gc.present()
-- Create a new mainLoop thread to keep game alive
local status,resume=coroutine.status,coroutine.resume
local loopThread=coroutine.create(secondLoopThread)
local res,threadErr
repeat
res,threadErr=resume(loopThread)
until status(loopThread)=='dead'
if not res then
love.errorhandler(threadErr)
return
end
else
ms.setVisible(true)
local errorMsg
errorMsg=LOADED and
"Too many errors or fatal error occured.\nPlease restart the game." or
"An error has occurred during loading.\nError info has been created, and you can send it to the author."
while true do
love.event.pump()
for E,a,b in love.event.poll() do
if E=='quit' or a=='escape' then
return true
elseif E=='resize' then
SCR.resize(a,b)
end
end
gc_clear(.3,.5,.9)
gc_push('transform')
gc_replaceTransform(SCR.xOy)
FONT.set(100)gc_print(":(",100,0,0,1.2)
FONT.set(40)gc.printf(errorMsg,100,160,SCR.w0-100)
FONT.set(20)
gc_print(SYSTEM.."-"..VERSION.string.." scene:"..sceneStack,100,660)
gc.printf(err[1],100,360,1260-100)
gc_print("TRACEBACK",100,450)
for i=4,#err-2 do
gc_print(err[i],100,400+20*i)
end
gc_pop()
gc_present()
love.timer.sleep(.26)
end
end
end
love.threaderror=nil
love.draw,love.update=nil-- remove default draw/update
local debugColor={
COLOR.Z,
COLOR.lM,
COLOR.lG,
COLOR.lB,
}
local debugInfos={
{"Cache",gcinfo},
}
function love.run()
local love=love
local TEXT_update,TEXT_draw=TEXT.update,TEXT.draw
local MES_update,MES_draw=MES.update,MES.draw
local HTTP_update,WS_update=HTTP.update,WS.update
local TASK_update=TASK.update
local SYSFX_update,SYSFX_draw=SYSFX.update,SYSFX.draw
local WIDGET_update,WIDGET_draw=WIDGET.update,WIDGET.draw
local STEP,SLEEP=love.timer.step,love.timer.sleep
local FPS,MINI=love.timer.getFPS,love.window.isMinimized
local PUMP,POLL=love.event.pump,love.event.poll
local timer=love.timer.getTime
local frameTimeList={}
local lastFrame=timer()
local lastFreshPow=lastFrame
local FCT=0-- Framedraw counter, from 0~99
love.resize(gc.getWidth(),gc.getHeight())
-- Scene Launch
while #SCN.stack>0 do SCN.pop() end
if #errData>0 then
SCN.cur='error'
SCN.init('error')
else
SCN.init('load')
end
return function()
local _
local time=timer()
local dt=time-lastFrame
lastFrame=time
-- EVENT
PUMP()
for N,a,b,c,d,e in POLL() do
if love[N] then
love[N](a,b,c,d,e)
elseif N=='quit' then
if onBeforeQuit then
onBeforeQuit()
onBeforeQuit=false
else
onQuit()
return a or true
end
end
end
-- UPDATE
STEP()
if mouseShow then mouse_update(dt) end
if next(jsState) then gp_update(jsState[1],dt) end
VOC.update()
BG.update(dt)
TEXT_update(dt)
WAIT.update(dt)
MES_update(dt)
HTTP_update(dt)
WS_update(dt)
TASK_update(dt)
SYSFX_update(dt)
if SCN.update then SCN.update(dt) end
if SCN.swapping then SCN.swapUpdate(dt) end
WIDGET_update(dt)
-- DRAW
if not MINI() then
FCT=FCT+frameMul
if FCT>=100 then
FCT=FCT-100
gc_replaceTransform(SCR.origin)
gc_setColor(1,1,1)
BG.draw()
gc_replaceTransform(SCR.xOy)
if SCN.draw then SCN.draw() end
WIDGET_draw()
SYSFX_draw()
TEXT_draw()
-- Draw cursor
if mouseShow then drawCursor(time,mx,my) end
gc_replaceTransform(SCR.xOy_ul)
if showPowerInfo then
gc.translate(0,27)
end
MES_draw()
gc_replaceTransform(SCR.origin)
-- Draw power info.
if showPowerInfo then
gc_setColor(1,1,1)
gc_draw(infoCanvas,SCR.safeX,0,0,SCR.k)
end
-- Draw scene swapping animation
if SCN.swapping then
gc_setColor(1,1,1)
_=SCN.state
_.draw(_.time)
end
gc_replaceTransform(SCR.xOy_d)
-- Draw Version string
gc_setColor(.9,.9,.9,.42)
FONT.set(20)
GC.mStr(versionText,0,-30)
gc_replaceTransform(SCR.xOy_dl)
local safeX=SCR.safeX/SCR.k
-- Draw FPS
FONT.set(15)
gc_setColor(1,1,1)
gc_print(FPS(),safeX+5,-20)
-- Debug info.
if debugMode then
-- Debug infos at left-down
gc_setColor(debugColor[debugMode])
-- Text infos
for i=1,#debugInfos do
gc_print(debugInfos[i][1],safeX+5,-20-20*i)
gc_print(debugInfos[i][2](),safeX+62.6,-20-20*i)
end
-- Update & draw frame time
table.insert(frameTimeList,1,dt)table.remove(frameTimeList,126)
gc_setColor(1,1,1,.3)
for i=1,#frameTimeList do
gc.rectangle('fill',150+2*i,-20,2,-frameTimeList[i]*4000)
end
-- Cursor pos disp
gc_replaceTransform(SCR.origin)
local x,y=SCR.xOy:transformPoint(mx,my)
gc_setLineWidth(1)
gc_line(x,0,x,SCR.h)
gc_line(0,y,SCR.w,y)
local t=math.floor(mx+.5)..","..math.floor(my+.5)
gc.setColor(COLOR.D)
gc_print(t,x+1,y)
gc_print(t,x+1,y-1)
gc_print(t,x+2,y-1)
gc_setColor(COLOR.Z)
gc_print(t,x+2,y)
gc_replaceTransform(SCR.xOy_dr)
-- Websocket status
local status=WS.status('game')
if status=='dead' then
gc_setColor(COLOR.R)
elseif status=='connecting' then
gc_setColor(1,1,1,.5+.3*math.sin(time*6.26))
elseif status=='running' then
gc_setColor(COLOR.lG)
end
gc.rectangle('fill',-16,-16,12,12)
local t1,t2,t3=WS.getTimers('game')
if t1>0 then gc_setColor(.9,.9,.9,t1)gc.rectangle('fill',-60,-2,-16,-16) end
if t2>0 then gc_setColor(.3,1,.3,t2)gc.rectangle('fill',-42,-2,-16,-16) end
if t3>0 then gc_setColor(1,.2,.2,t3)gc.rectangle('fill',-24,-2,-16,-16) end
end
gc_replaceTransform(SCR.origin)
WAIT.draw()
gc_present()
-- SPEED UPUPUP!
if discardCanvas then gc_discard() end
end
end
-- Fresh power info.
if time-lastFreshPow>2.6 then
if showPowerInfo then
updatePowerInfo()
lastFreshPow=time
end
if gc.getWidth()~=SCR.w or gc.getHeight()~=SCR.h then
love.resize(gc.getWidth(),gc.getHeight())
end
end
-- Slow debugmode
if debugMode then
if debugMode==3 then
SLEEP(.1)
elseif debugMode==4 then
SLEEP(.5)
end
end
_=timer()-lastFrame
if _<sleepInterval*.9626 then SLEEP(sleepInterval*.9626-_) end
while timer()-lastFrame<sleepInterval do end
end
end
local Z={}
function Z.getJsState() return jsState end
function Z.getErr(i)
if i=='#' then
return errData[#errData]
elseif i then
return errData[i]
else
return errData
end
end
function Z.setPowerInfo(bool) showPowerInfo=bool end
function Z.setCleanCanvas(bool) discardCanvas=bool end
function Z.setFrameMul(n) frameMul=n end
function Z.setMaxFPS(fps) sleepInterval=1/fps end
function Z.setClickFX(bool) showClickFX=bool end
--[Warning] Color and line width is uncertain value, set it in the function.
function Z.setCursor(func) drawCursor=func end
function Z.setVersionText(str) versionText=str end
function Z.setDebugInfo(list)
assert(type(list)=='table',"Z.setDebugInfo(list): list must be table")
for i=1,#list do
assert(type(list[i][1])=='string',"Z.setDebugInfo(list): list[i][1] must be string")
assert(type(list[i][2])=='function',"Z.setDebugInfo(list): list[i][2] must be function")
end
debugInfos=list
end
-- Change F1~F7 events of debugmode (F8 mode)
function Z.setOnFnKeys(list)
assert(type(list)=='table',"Z.setOnFnKeys(list): list must be table")
for i=1,7 do fnKey[i]=assert(type(list[i])=='function' and list[i]) end
end
function Z.setOnGlobalKey(key,func)
assert(type(key)=='string',"Z.setOnFnKeys(key,func): key must be string")
if not func then
globalKey[key]=nil
else
assert(type(func)=='function',"Z.setOnFnKeys(key,func): func must be function")
globalKey[key]=func
end
end
function Z.setOnFocus(func)
onFocus=assert(type(func)=='function' and func,"Z.setOnFocus(func): func must be function")
end
function Z.setOnResize(func)
onResize=assert(type(func)=='function' and func,"Z.setOnResize(func): func must be function")
end
function Z.setOnQuit(func)
onQuit=assert(type(func)=='function' and func,"Z.setOnQuit(func): func must be function")
end
function Z.setOnBeforeQuit(func)
onBeforeQuit=assert(type(func)=='function' and func,"Z.setOnBeforeQuit(func): func must be function")
end
return Z

340
Zframework/json.lua Normal file
View File

@@ -0,0 +1,340 @@
-- json.lua
-- Copyright (c) 2020 rxi
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions:
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
-- Editted by MrZ
local ins,char=table.insert,string.char
local json = {}
-------------------------------------------------------------------------------
-- Encode
-------------------------------------------------------------------------------
local _encode
local escape_char_map = {
["\\"] = "\\",
["\""] = "\"",
["\b"] = "b",
["\f"] = "f",
["\n"] = "n",
["\r"] = "r",
["\t"] = "t"
}
local escape_char_map_inv = {["/"] = "/"}
for k, v in pairs(escape_char_map) do escape_char_map_inv[v] = k end
local function escape_char(c)
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
end
local function encode_nil() return "null" end
local function encode_table(val, stack)
local res = {}
stack = stack or {}
-- Circular reference?
if stack[val] then error("circular reference") end
stack[val] = true
if rawget(val, 1) ~= nil or next(val) == nil then
-- Treat as array -- check keys are valid and it is not sparse
local n = 0
for k in pairs(val) do
if type(k) ~= 'number' then
error("invalid table: mixed or invalid key types")
end
n = n + 1
end
if n ~= #val then error("invalid table: sparse array") end
-- Encode
for _, v in ipairs(val) do ins(res, _encode(v, stack)) end
stack[val] = nil
return "[" .. table.concat(res, ",") .. "]"
else
-- Treat as an object
for k, v in pairs(val) do
if type(k) ~= 'string' then
error("invalid table: mixed or invalid key types")
end
ins(res, _encode(k, stack) .. ":" .. _encode(v, stack))
end
stack[val] = nil
return "{" .. table.concat(res, ",") .. "}"
end
end
local function encode_string(val)
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
end
local function encode_number(val)
-- Check for NaN, -inf and inf
if val ~= val or val <= -math.huge or val >= math.huge then
error("unexpected number value '" .. tostring(val) .. "'")
end
return string.format("%.14g", val)
end
local type_func_map = {
['nil'] = encode_nil,
['table'] = encode_table,
['string'] = encode_string,
['number'] = encode_number,
['boolean'] = tostring
}
_encode = function(val, stack)
local t = type(val)
local f = type_func_map[t]
if f then return f(val, stack) end
error("unexpected type '" .. t .. "'")
end
json.encode=_encode
-------------------------------------------------------------------------------
-- Decode
-------------------------------------------------------------------------------
local parse
local function create_set(...)
local res = {}
for i = 1, select("#", ...) do res[select(i, ...)] = true end
return res
end
local space_chars = create_set(" ", "\t", "\r", "\n")
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
local literals = create_set("true", "false", "null")
local literal_map = {["true"] = true, ["false"] = false, ["null"] = nil}
local function next_char(str, idx, set, negate)
for i = idx, #str do if set[str:sub(i, i)] ~= negate then return i end end
return #str + 1
end
local function decode_error(str, idx, msg)
local line_count = 1
local col_count = 1
for i = 1, idx - 1 do
col_count = col_count + 1
if str:sub(i, i) == "\n" then
line_count = line_count + 1
col_count = 1
end
end
error(string.format("%s at line %d col %d", msg, line_count, col_count))
end
local function codepoint_to_utf8(n)
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
local f = bit.rshift
if n <= 0x7f then
return char(n)
elseif n <= 0x7ff then
return char(f(n, 6) + 192, n % 64 + 128)
elseif n <= 0xffff then
return char(f(n, 12) + 224, f(n % 4096, 6) + 128, n % 64 + 128)
elseif n <= 0x10ffff then
return char(f(n, 18) + 240, f(n % 262144, 12) + 128, f(n % 4096, 6) + 128, n % 64 + 128)
end
error(string.format("invalid unicode codepoint '%x'", n))
end
local function parse_unicode_escape(s)
local n1 = tonumber(s:sub(1, 4), 16)
local n2 = tonumber(s:sub(7, 10), 16)
-- Surrogate pair?
if n2 then
return
codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
else
return codepoint_to_utf8(n1)
end
end
local function parse_string(str, i)
local res = ""
local j = i + 1
local k = j
while j <= #str do
local x = str:byte(j)
if x < 32 then
decode_error(str, j, "control character in string")
elseif x == 92 then -- `\`: Escape
res = res .. str:sub(k, j - 1)
j = j + 1
local c = str:sub(j, j)
if c == "u" then
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) or
str:match("^%x%x%x%x", j + 1) or
decode_error(str, j - 1,
"invalid unicode escape in string")
res = res .. parse_unicode_escape(hex)
j = j + #hex
else
if not escape_chars[c] then
decode_error(str, j - 1,
"invalid escape char '" .. c .. "' in string")
end
res = res .. escape_char_map_inv[c]
end
k = j + 1
elseif x == 34 then -- `"`: End of string
res = res .. str:sub(k, j - 1)
return res, j + 1
end
j = j + 1
end
decode_error(str, i, "expected closing quote for string")
end
local function parse_number(str, i)
local x = next_char(str, i, delim_chars)
local s = str:sub(i, x - 1)
local n = tonumber(s)
if not n then decode_error(str, i, "invalid number '" .. s .. "'") end
return n, x
end
local function parse_literal(str, i)
local x = next_char(str, i, delim_chars)
local word = str:sub(i, x - 1)
if not literals[word] then
decode_error(str, i, "invalid literal '" .. word .. "'")
end
return literal_map[word], x
end
local function parse_array(str, i)
local res = {}
local n = 1
i = i + 1
while 1 do
local x
i = next_char(str, i, space_chars, true)
-- Empty / end of array?
if str:sub(i, i) == "]" then
i = i + 1
break
end
-- Read token
x, i = parse(str, i)
res[n] = x
n = n + 1
-- Next token
i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i)
i = i + 1
if chr == "]" then break end
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
end
return res, i
end
local function parse_object(str, i)
local res = {}
i = i + 1
while 1 do
local key, val
i = next_char(str, i, space_chars, true)
-- Empty / end of object?
if str:sub(i, i) == "}" then
i = i + 1
break
end
-- Read key
if str:sub(i, i) ~= '"' then
decode_error(str, i, "expected string for key")
end
key, i = parse(str, i)
-- Read ':' delimiter
i = next_char(str, i, space_chars, true)
if str:sub(i, i) ~= ":" then
decode_error(str, i, "expected ':' after key")
end
i = next_char(str, i + 1, space_chars, true)
-- Read value
val, i = parse(str, i)
-- Set
res[key] = val
-- Next token
i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i)
i = i + 1
if chr == "}" then break end
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
end
return res, i
end
local char_func_map = {
['"'] = parse_string,
["0"] = parse_number,
["1"] = parse_number,
["2"] = parse_number,
["3"] = parse_number,
["4"] = parse_number,
["5"] = parse_number,
["6"] = parse_number,
["7"] = parse_number,
["8"] = parse_number,
["9"] = parse_number,
["-"] = parse_number,
["t"] = parse_literal,
["f"] = parse_literal,
["n"] = parse_literal,
["["] = parse_array,
["{"] = parse_object
}
function parse(str, idx)
local chr = str:sub(idx, idx)
local f = char_func_map[chr]
if f then return f(str, idx) end
decode_error(str, idx, "unexpected character '" .. chr .. "'")
end
function json.decode(str)
if type(str) ~= 'string' then
error("expected argument of type string, got " .. type(str))
end
local res, idx = parse(str, next_char(str, 1, space_chars, true))
idx = next_char(str, idx, space_chars, true)
if idx <= #str then decode_error(str, idx, "trailing garbage") end
return res
end
return json

57
Zframework/languages.lua Normal file
View File

@@ -0,0 +1,57 @@
local LANG={}
-- ONLY FIRST CALL MAKE SENSE
-- Create LANG.get() and LANG.addScene()
function LANG.init(defaultLang,langList,publicText,pretreatFunc)
local function _langFallback(T0,T)
for k,v in next,T0 do
if type(v)=='table' and not v.refuseCopy then-- refuseCopy: just copy pointer, not contents
if not T[k] then T[k]={} end
if type(T[k])=='table' then
_langFallback(v,T[k])
end
elseif not T[k] then
T[k]=v
end
end
end
-- Set public text
if publicText then
for _,L in next,langList do
for key,list in next,publicText do L[key]=list end
end
end
-- Fallback to default language
for name,L in next,langList do
if name~=defaultLang then
_langFallback(langList[L.fallback or defaultLang],L)
end
end
-- Custom pretreatment for each language
if pretreatFunc then
for _,L in next,langList do
pretreatFunc(L)
end
end
function LANG.get(l)
if not langList[l] then
LOG("Wrong language: "..tostring(l))
l=defaultLang
end
return langList[l]
end
function LANG.addScene(name)
for _,L in next,langList do
if L.WidgetText and not L.WidgetText[name] then
L.WidgetText[name]={}
end
end
end
function LANG.init() end
end
return LANG

20
Zframework/log.lua Normal file
View File

@@ -0,0 +1,20 @@
local ins=table.insert
local logs={os.date("Techmino logs %Y/%m/%d %A")}
local function log(message)
ins(logs,os.date("[%H:%M:%S] ")..message)
end
local LOG=setmetatable({logs=logs},{
__call=function(_,message)
print(message)
log(message)
end
})
function LOG.read()
return table.concat(logs,"\n")
end
return LOG

26
Zframework/lowcaser.txt Normal file
View File

@@ -0,0 +1,26 @@
A=a,B=b,C=c,D=d,E=e,F=f,G=g,H=h,I=i,J=j,K=k,L=l,M=m,N=n,O=o,P=p,Q=q,R=r,S=s,T=t,U=u,V=v,W=w,X=x,Y=y,Z=z
Μ=µ,ẞ=ß,À=à,Á=á,Â=â,Ã=ã,Ä=ä,Å=å,Æ=æ,Ç=ç,È=è,É=é,Ê=ê,Ë=ë,Ì=ì,Í=í,Î=î,Ï=ï,Ð=ð,Ñ=ñ,Ò=ò,Ó=ó,Ô=ô,Õ=õ,Ö=ö,Ø=ø,Ù=ù,Ú=ú,Û=û,Ü=ü,Ý=ý,Þ=þ,Ÿ=ÿ,Ā=ā,Ă=ă,Ą=ą,Ć=ć,Ĉ=ĉ,Ċ=ċ,Č=č,Ď=ď,Đ=đ,Ē=ē,Ĕ=ĕ,Ė=ė,Ę=ę,Ě=ě,Ĝ=ĝ,Ğ=ğ,Ġ=ġ,Ģ=ģ,Ĥ=ĥ,Ħ=ħ,Ĩ=ĩ,Ī=ī,Ĭ=ĭ,Į=į,IJ=ij,Ĵ=ĵ,Ķ=ķ,Ĺ=ĺ,Ļ=ļ,Ľ=ľ,Ŀ=ŀ,Ł=ł,Ń=ń,Ņ=ņ,Ň=ň,Ŋ=ŋ,Ō=ō,Ŏ=ŏ,Ő=ő,Œ=œ,Ŕ=ŕ,Ŗ=ŗ,Ř=ř,Ś=ś,Ŝ=ŝ,Ş=ş,Š=š,Ţ=ţ,Ť=ť,Ŧ=ŧ,Ũ=ũ,Ū=ū,Ŭ=ŭ,Ů=ů,Ű=ű,Ų=ų,Ŵ=ŵ,Ŷ=ŷ,Ź=ź,Ż=ż,Ž=ž
Ƀ=ƀ,Ƃ=ƃ,Ƅ=ƅ,Ƈ=ƈ,Ƌ=ƌ,Ƒ=ƒ,Ƕ=ƕ,Ƙ=ƙ,Ƚ=ƚ,Ƞ=ƞ,Ơ=ơ,Ƣ=ƣ,Ƥ=ƥ,Ƨ=ƨ,Ƭ=ƭ,Ư=ư,Ƴ=ƴ,Ƶ=ƶ,Ƹ=ƹ,Ƽ=ƽ,Ƿ=ƿ,DŽ=dž,LJ=lj,NJ=nj,Ǎ=ǎ,Ǐ=ǐ,Ǒ=ǒ,Ǔ=ǔ,Ǖ=ǖ,Ǘ=ǘ,Ǚ=ǚ,Ǜ=ǜ,Ǝ=ǝ,Ǟ=ǟ,Ǡ=ǡ,Ǣ=ǣ,Ǥ=ǥ,Ǧ=ǧ,Ǩ=ǩ,Ǫ=ǫ,Ǭ=ǭ,Ǯ=ǯ,DZ=dz,Ǵ=ǵ,Ǹ=ǹ,Ǻ=ǻ,Ǽ=ǽ,Ǿ=ǿ,Ȁ=ȁ,Ȃ=ȃ,Ȅ=ȅ,Ȇ=ȇ,Ȉ=ȉ,Ȋ=ȋ,Ȍ=ȍ,Ȏ=ȏ,Ȑ=ȑ,Ȓ=ȓ,Ȕ=ȕ,Ȗ=ȗ,Ș=ș,Ț=ț,Ȝ=ȝ,Ȟ=ȟ,Ȣ=ȣ,Ȥ=ȥ,Ȧ=ȧ,Ȩ=ȩ,Ȫ=ȫ,Ȭ=ȭ,Ȯ=ȯ,Ȱ=ȱ,Ȳ=ȳ,Ȼ=ȼ,Ȿ=ȿ,Ɀ=ɀ,Ɂ=ɂ,Ɇ=ɇ,Ɉ=ɉ,Ɋ=ɋ,Ɍ=ɍ,Ɏ=ɏ
Ɐ=ɐ,Ɑ=ɑ,Ɒ=ɒ,Ɓ=ɓ,Ɔ=ɔ,Ɖ=ɖ,Ɗ=ɗ,Ə=ə,Ɛ=ɛ,=ɜ,Ɠ=ɠ,Ɡ=ɡ,Ɣ=ɣ,Ɥ=ɥ,Ɦ=ɦ,Ɨ=ɨ,Ɩ=ɩ,Ɪ=ɪ,Ɫ=ɫ,Ɬ=ɬ,Ɯ=ɯ,Ɱ=ɱ,Ɲ=ɲ,Ɵ=ɵ,Ɽ=ɽ,Ʀ=ʀ,Ʂ=ʂ,Ʃ=ʃ,Ʇ=ʇ,Ʈ=ʈ,Ʉ=ʉ,Ʊ=ʊ,Ʋ=ʋ,Ʌ=ʌ,Ʒ=ʒ,=ʝ,Ʞ=ʞ
Ͱ=ͱ,Ͳ=ͳ,Ͷ=ͷ,Ͻ=ͻ,Ͼ=ͼ,Ͽ=ͽ,Ά=ά,Έ=έ,Ή=ή,Ί=ί,Α=α,Β=β,Γ=γ,Δ=δ,Ε=ε,Ζ=ζ,Η=η,Θ=θ,Κ=κ,Λ=λ,Ν=ν,Ξ=ξ,Ο=ο,Π=π,Ρ=ρ,Σ=σ,Τ=τ,Υ=υ,Φ=φ,Χ=χ,Ψ=ψ,Ω=ω,Ϊ=ϊ,Ϋ=ϋ,Ό=ό,Ύ=ύ,Ώ=ώ,Ϗ=ϗ,Ϙ=ϙ,Ϛ=ϛ,Ϝ=ϝ,Ϟ=ϟ,Ϡ=ϡ,Ϣ=ϣ,Ϥ=ϥ,Ϧ=ϧ,Ϩ=ϩ,Ϫ=ϫ,Ϭ=ϭ,Ϯ=ϯ,Ϲ=ϲ,Ϳ=ϳ,Ϸ=ϸ,Ϻ
А=а,Б=б,В=в,Г=г,Д=д,Е=е,Ж=ж,З=з,И=и,Й=й,К=к,Л=л,М=м,Н=н,О=о,П=п,Р=р,С=с,Т=т,У=у,Ф=ф,Х=х,Ц=ц,Ч=ч,Ш=ш,Щ=щ,Ъ=ъ,Ы=ы,Ь=ь,Э=э,Ю=ю,Я=я,Ѐ=ѐ,Ё=ё,Ђ=ђ,Ѓ=ѓ,Є=є,Ѕ=ѕ,І=і,Ї=ї,Ј=ј,Љ=љ,Њ=њ,Ћ=ћ,Ќ=ќ,Ѝ=ѝ,Ў=ў,Џ=џ,Ѡ=ѡ,Ѣ=ѣ,Ѥ=ѥ,Ѧ=ѧ,Ѩ=ѩ,Ѫ=ѫ,Ѭ=ѭ,Ѯ=ѯ,Ѱ=ѱ,Ѳ=ѳ,Ѵ=ѵ,Ѷ=ѷ,Ѹ=ѹ,Ѻ=ѻ,Ѽ=ѽ,Ѿ=ѿ,Ҁ=ҁ,Ҋ=ҋ,Ҍ=ҍ,Ҏ=ҏ,Ґ=ґ,Ғ=ғ,Ҕ=ҕ,Җ=җ,Ҙ=ҙ,Қ=қ,Ҝ=ҝ,Ҟ=ҟ,Ҡ=ҡ,Ң=ң,Ҥ=ҥ,Ҧ=ҧ,Ҩ=ҩ,Ҫ=ҫ,Ҭ=ҭ,Ү=ү,Ұ=ұ,Ҳ=ҳ,Ҵ=ҵ,Ҷ=ҷ,Ҹ=ҹ,Һ=һ,Ҽ=ҽ,Ҿ=ҿ,Ӂ=ӂ,Ӄ=ӄ,Ӆ=ӆ,Ӈ=ӈ,Ӊ=ӊ,Ӌ=ӌ,Ӎ=ӎ,Ӏ=ӏ,Ӑ=ӑ,Ӓ=ӓ,Ӕ=ӕ,Ӗ=ӗ,Ә=ә,Ӛ=ӛ,Ӝ=ӝ,Ӟ=ӟ,Ӡ=ӡ,Ӣ=ӣ,Ӥ=ӥ,Ӧ=ӧ,Ө=ө,Ӫ=ӫ,Ӭ=ӭ,Ӯ=ӯ,Ӱ=ӱ,Ӳ=ӳ,Ӵ=ӵ,Ӷ=ӷ,Ӹ=ӹ,Ӻ=ӻ,Ӽ=ӽ,Ӿ=ӿ,Ԁ=ԁ,Ԃ=ԃ,Ԅ=ԅ,Ԇ=ԇ,Ԉ=ԉ,Ԋ=ԋ,Ԍ=ԍ,Ԏ=ԏ,Ԑ=ԑ,Ԓ=ԓ,Ԕ=ԕ,Ԗ=ԗ,Ԙ=ԙ,Ԛ=ԛ,Ԝ=ԝ,Ԟ=ԟ,Ԡ=ԡ,Ԣ=ԣ,Ԥ=ԥ,Ԧ=ԧ,Ԩ=ԩ,Ԫ=ԫ,Ԭ=ԭ,Ԯ=ԯ
Ա=ա,Բ=բ,Գ=գ,Դ=դ,Ե=ե,Զ=զ,Է=է,Ը=ը,Թ=թ,Ժ=ժ,Ի=ի,Լ=լ,Խ=խ,Ծ=ծ,Կ=կ,Հ=հ,Ձ=ձ,Ղ=ղ,Ճ=ճ,Մ=մ,Յ=յ,Ն=ն,Շ=շ,Ո=ո,Չ=չ,Պ=պ,Ջ=ջ,Ռ=ռ,Ս=ս,Վ=վ,Տ=տ,Ր=ր,Ց=ց,Ւ=ւ,Փ=փ,Ք=ք,Օ=օ,Ֆ=ֆ
Ა=ა,Ბ=ბ,Გ=გ,Დ=დ,Ე=ე,Ვ=ვ,Ზ=ზ,Თ=თ,Ი=ი,Კ=კ,Ლ=ლ,Მ=მ,Ნ=ნ,Ო=ო,Პ=პ,Ჟ=ჟ,Რ=რ,Ს=ს,Ტ=ტ,Უ=უ,Ფ=ფ,Ქ=ქ,Ღ=ღ,Ყ=,Შ=შ,Ჩ=ჩ,Ც=ც,Ძ=ძ,Წ=წ,Ჭ=ჭ,Ხ=ხ,Ჯ=ჯ,Ჰ=ჰ,Ჱ=ჱ,Ჲ=ჲ,Ჳ=ჳ,Ჴ=ჴ,Ჵ=ჵ,Ჶ=ჶ,Ჷ=ჷ,Ჸ=ჸ,Ჹ=ჹ,Ჺ=ჺ,Ჽ=ჽ,Ჾ=ჾ,Ჿ=
Ᏸ=ᏸ,Ᏹ=ᏹ,Ᏺ=ᏺ,=ᏻ,=ᏼ,Ᏽ=ᏽ
Ꙋ=ᲈ,Ᵹ=ᵹ,Ᵽ=ᵽ,Ᶎ=ᶎ,Ḁ=ḁ,Ḃ=ḃ,Ḅ=ḅ,Ḇ=ḇ,Ḉ=ḉ,Ḋ=ḋ,Ḍ=ḍ,Ḏ=ḏ,Ḑ=ḑ,Ḓ=ḓ,Ḕ=ḕ,Ḗ=ḗ,Ḙ=ḙ,Ḛ=ḛ,Ḝ=ḝ,Ḟ=ḟ,Ḡ=ḡ,Ḣ=ḣ,Ḥ=ḥ,Ḧ=ḧ,Ḩ=ḩ,Ḫ=ḫ,Ḭ=ḭ,Ḯ=ḯ,Ḱ=ḱ,Ḳ=ḳ,Ḵ=ḵ,Ḷ=ḷ,Ḹ=ḹ,Ḻ=ḻ,Ḽ=ḽ,Ḿ=ḿ,Ṁ=ṁ,Ṃ=ṃ,Ṅ=ṅ,Ṇ=ṇ,Ṉ=ṉ,Ṋ=ṋ,Ṍ=ṍ,Ṏ=ṏ,Ṑ=ṑ,Ṓ=ṓ,Ṕ=ṕ,Ṗ=ṗ,Ṙ=ṙ,Ṛ=ṛ,Ṝ=ṝ,Ṟ=ṟ,Ṡ=ṡ,Ṣ=ṣ,Ṥ=ṥ,Ṧ=ṧ,Ṩ=ṩ,Ṫ=ṫ,Ṭ=ṭ,Ṯ=ṯ,Ṱ=ṱ,Ṳ=ṳ,Ṵ=ṵ,Ṷ=ṷ,Ṹ=ṹ,Ṻ=ṻ,Ṽ=ṽ,Ṿ=ṿ,Ẁ=ẁ,Ẃ=ẃ,Ẅ=ẅ,Ẇ=ẇ,Ẉ=ẉ,Ẋ=ẋ,Ẍ=ẍ,Ẏ=ẏ,Ẑ=ẑ,Ẓ=ẓ,Ẕ=ẕ,Ạ=ạ,Ả=ả,Ấ=ấ,Ầ=ầ,Ẩ=ẩ,Ẫ=ẫ,Ậ=ậ,Ắ=ắ,Ằ=ằ,Ẳ=ẳ,Ẵ=ẵ,Ặ=ặ,Ẹ=ẹ,Ẻ=ẻ,Ẽ=ẽ,Ế=ế,Ề=ề,Ể=ể,Ễ=ễ,Ệ=ệ,Ỉ=ỉ,Ị=ị,Ọ=ọ,Ỏ=ỏ,Ố=ố,Ồ=ồ,Ổ=ổ,Ỗ=ỗ,Ộ=ộ,Ớ=ớ,Ờ=ờ,Ở=ở,Ỡ=ỡ,Ợ=ợ,Ụ=ụ,Ủ=ủ,Ứ=ứ,Ừ=ừ,Ử=ử,Ữ=ữ,Ự=ự,Ỳ=ỳ,Ỵ=ỵ,Ỷ=ỷ,Ỹ=ỹ,Ỻ=ỻ,Ỽ=ỽ,Ỿ=ỿ,Ἀ=ἀ,Ἁ=ἁ,Ἂ=ἂ,Ἃ=ἃ,Ἄ=ἄ,Ἅ=ἅ,Ἆ=ἆ,Ἇ=ἇ,Ἐ=ἐ,Ἑ=ἑ,Ἒ=ἒ,Ἓ=ἓ,Ἔ=ἔ,Ἕ=ἕ,Ἠ=ἠ,Ἡ=ἡ,Ἢ=ἢ,Ἣ=ἣ,Ἤ=ἤ,Ἥ=ἥ,Ἦ=ἦ,Ἧ=ἧ,Ἰ=ἰ,Ἱ=ἱ,Ἲ=ἲ,Ἳ=ἳ,Ἴ=ἴ,Ἵ=ἵ,Ἶ=ἶ,Ἷ=ἷ,Ὀ=ὀ,Ὁ=ὁ,Ὂ=ὂ,Ὃ=ὃ,Ὄ=ὄ,Ὅ=ὅ,Ὑ=ὑ,Ὓ=ὓ,Ὕ=ὕ,Ὗ=ὗ,Ὠ=ὠ,Ὡ=ὡ,Ὢ=ὢ,Ὣ=ὣ,Ὤ=ὤ,Ὥ=ὥ,Ὦ=ὦ,Ὧ=ὧ,Ὰ=ὰ,Ά=ά,Ὲ=ὲ,Έ=έ,Ὴ=ὴ,Ή=ή,Ὶ=ὶ,Ί=ί,Ὸ=ὸ,Ό=ό,Ὺ=ὺ,Ύ=ύ,Ὼ=ὼ,Ώ=ώ,Ᾰ=ᾰ,Ᾱ=ᾱ,Ῐ=ῐ,Ῑ=ῑ,Ῠ=ῠ,Ῡ=ῡ,Ῥ=ῥ,Ⅎ=ⅎ
=,Ⅱ=ⅱ,Ⅲ=ⅲ,Ⅳ=ⅳ,=,Ⅵ=ⅵ,Ⅶ=ⅶ,Ⅷ=ⅷ,Ⅸ=ⅸ,=,Ⅺ=ⅺ,Ⅻ=ⅻ,=,=,=,=ⅿ,Ↄ=ↄ
Ⓐ=ⓐ,Ⓑ=ⓑ,Ⓒ=ⓒ,Ⓓ=ⓓ,Ⓔ=ⓔ,Ⓕ=ⓕ,Ⓖ=ⓖ,Ⓗ=ⓗ,Ⓘ=ⓘ,Ⓙ=ⓙ,Ⓚ=ⓚ,Ⓛ=ⓛ,Ⓜ=ⓜ,Ⓝ=ⓝ,Ⓞ=ⓞ,Ⓟ=ⓟ,Ⓠ=ⓠ,Ⓡ=ⓡ,Ⓢ=ⓢ,Ⓣ=ⓣ,Ⓤ=ⓤ,Ⓥ=ⓥ,Ⓦ=ⓦ,Ⓧ=ⓧ,Ⓨ=ⓨ,Ⓩ=ⓩ
Ⰰ=ⰰ,Ⰱ=ⰱ,Ⰲ=ⰲ,Ⰳ=ⰳ,Ⰴ=ⰴ,Ⰵ=ⰵ,Ⰶ=ⰶ,Ⰷ=ⰷ,Ⰸ=ⰸ,Ⰹ=ⰹ,Ⰺ=ⰺ,Ⰻ=ⰻ,Ⰼ=ⰼ,Ⰽ=ⰽ,Ⰾ=ⰾ,Ⰿ=ⰿ,Ⱀ=ⱀ,Ⱁ=ⱁ,Ⱂ=ⱂ,Ⱃ=ⱃ,Ⱄ=ⱄ,Ⱅ=ⱅ,Ⱆ=ⱆ,Ⱇ=ⱇ,Ⱈ=ⱈ,Ⱉ=ⱉ,Ⱊ=ⱊ,Ⱋ=ⱋ,Ⱌ=ⱌ,Ⱍ=ⱍ,Ⱎ=ⱎ,Ⱏ=ⱏ,Ⱐ=ⱐ,Ⱑ=ⱑ,Ⱒ=ⱒ,Ⱓ=ⱓ,Ⱔ=ⱔ,Ⱕ=ⱕ,Ⱖ=ⱖ,Ⱗ=ⱗ,Ⱘ=ⱘ,Ⱙ=ⱙ,Ⱚ=ⱚ,Ⱛ=ⱛ,Ⱜ=ⱜ,Ⱝ=ⱝ,Ⱞ=ⱞ,Ⱟ=ⱟ
Ⱡ=ⱡ,Ⱥ=ⱥ,Ⱦ=ⱦ,Ⱨ=ⱨ,Ⱪ=ⱪ,Ⱬ=ⱬ,Ⱳ=ⱳ,Ⱶ=ⱶ
Ⲁ=ⲁ,Ⲃ=ⲃ,Ⲅ=,Ⲇ=ⲇ,Ⲉ=ⲉ,Ⲋ=ⲋ,Ⲍ=ⲍ,=ⲏ,Ⲑ=ⲑ,=ⲓ,=ⲕ,Ⲗ=ⲗ,=ⲙ,=ⲛ,Ⲝ=ⲝ,=,Ⲡ=ⲡ,=,=,=ⲧ,=ⲩ,Ⲫ=ⲫ,=ⲭ,Ⲯ=ⲯ,Ⲱ=ⲱ,Ⲳ=ⲳ,Ⲵ=ⲵ,Ⲷ=ⲷ,Ⲹ=ⲹ,=ⲻ,Ⲽ=ⲽ,Ⲿ=ⲿ,Ⳁ=ⳁ,Ⳃ=ⳃ,Ⳅ=ⳅ,=ⳇ,Ⳉ=ⳉ,=ⳋ,=ⳍ,Ⳏ=ⳏ,=ⳑ,=ⳓ,Ⳕ=ⳕ,Ⳗ=ⳗ,Ⳙ=ⳙ,Ⳛ=ⳛ,Ⳝ=ⳝ,Ⳟ=ⳟ,Ⳡ=ⳡ,Ⳣ=ⳣ,Ⳬ=ⳬ,Ⳮ=ⳮ,Ⳳ=ⳳ
Ⴀ=ⴀ,Ⴁ=ⴁ,Ⴂ=ⴂ,Ⴃ=ⴃ,Ⴄ=ⴄ,Ⴅ=ⴅ,Ⴆ=ⴆ,Ⴇ=ⴇ,Ⴈ=ⴈ,Ⴉ=ⴉ,Ⴊ=ⴊ,Ⴋ=ⴋ,Ⴌ=ⴌ,Ⴍ=ⴍ,Ⴎ=ⴎ,Ⴏ=ⴏ,Ⴐ=ⴐ,Ⴑ=ⴑ,Ⴒ=ⴒ,Ⴓ=ⴓ,Ⴔ=ⴔ,Ⴕ=ⴕ,Ⴖ=ⴖ,Ⴗ=ⴗ,Ⴘ=ⴘ,Ⴙ=ⴙ,Ⴚ=ⴚ,Ⴛ=ⴛ,Ⴜ=ⴜ,Ⴝ=ⴝ,Ⴞ=ⴞ,Ⴟ=ⴟ,Ⴠ=ⴠ,Ⴡ=ⴡ,Ⴢ=ⴢ,Ⴣ=ⴣ,Ⴤ=ⴤ,Ⴥ=ⴥ,Ⴧ=ⴧ
Ⴭ=ⴭ,Ꙁ=ꙁ,Ꙃ=ꙃ,=ꙅ,Ꙇ=,Ꙉ=ꙉ,Ꙍ=ꙍ,Ꙏ=ꙏ,Ꙑ=ꙑ,Ꙓ=ꙓ,Ꙕ=ꙕ,Ꙗ=ꙗ,Ꙙ=ꙙ,Ꙛ=ꙛ,Ꙝ=ꙝ,Ꙟ=ꙟ,Ꙡ=ꙡ,Ꙣ=ꙣ,Ꙥ=ꙥ,Ꙧ=ꙧ,Ꙩ=ꙩ,Ꙫ=ꙫ,Ꙭ=ꙭ,Ꚁ=ꚁ,Ꚃ=ꚃ,Ꚅ=ꚅ,Ꚇ=ꚇ,Ꚉ=ꚉ,Ꚋ=ꚋ,Ꚍ=ꚍ,Ꚏ=ꚏ,Ꚑ=ꚑ,Ꚓ=ꚓ,Ꚕ=ꚕ,Ꚗ=ꚗ,Ꚙ=ꚙ,Ꚛ=ꚛ,Ꜣ=ꜣ,Ꜥ=ꜥ,Ꜧ=ꜧ,Ꜩ=ꜩ,Ꜫ=ꜫ,Ꜭ=ꜭ,Ꜯ=ꜯ,Ꜳ=ꜳ,Ꜵ=ꜵ,Ꜷ=ꜷ,Ꜹ=ꜹ,Ꜻ=ꜻ,Ꜽ=ꜽ,Ꜿ=ꜿ,Ꝁ=ꝁ,Ꝃ=ꝃ,Ꝅ=ꝅ,Ꝇ=ꝇ,Ꝉ=ꝉ,Ꝋ=ꝋ,Ꝍ=ꝍ,Ꝏ=ꝏ,Ꝑ=ꝑ,Ꝓ=ꝓ,Ꝕ=ꝕ,Ꝗ=ꝗ,Ꝙ=ꝙ,=ꝛ,Ꝝ=ꝝ,Ꝟ=ꝟ,Ꝡ=ꝡ,Ꝣ=ꝣ,Ꝥ=ꝥ,Ꝧ=ꝧ,Ꝩ=ꝩ,=ꝫ,Ꝭ=ꝭ,=ꝯ,Ꝺ=ꝺ,Ꝼ=ꝼ,Ꝿ=ꝿ,Ꞁ=ꞁ,Ꞃ=ꞃ,Ꞅ=ꞅ,Ꞇ=ꞇ,Ꞌ=,Ꞑ=ꞑ,Ꞓ=ꞓ,Ꞔ=ꞔ,Ꞗ=ꞗ,=,Ꞛ=ꞛ,Ꞝ=ꞝ,Ꞟ=,Ꞡ=ꞡ,Ꞣ=ꞣ,Ꞥ=ꞥ,Ꞧ=ꞧ,Ꞩ=ꞩ,=ꞵ,Ꞷ=ꞷ,Ꞹ=ꞹ,Ꞻ=ꞻ,Ꞽ=ꞽ,Ꞿ=ꞿ,Ꟁ=ꟁ,Ꟃ=ꟃ,Ꟈ=ꟈ,Ꟊ=ꟊ,Ꟑ=ꟑ,Ꟗ=ꟗ,Ꟙ=ꟙ,Ꟶ=ꟶ,=ꭓ
=ꭰ,=ꭱ,=ꭲ,Ꭳ=ꭳ,Ꭴ=ꭴ,=,Ꭶ=ꭶ,Ꭷ=ꭷ,Ꭸ=ꭸ,=ꭹ,=ꭺ,=ꭻ,=ꭼ,Ꭽ=ꭽ,=ꭾ,Ꭿ=ꭿ,Ꮀ=ꮀ,Ꮁ=,Ꮂ=ꮂ,=,Ꮄ=ꮄ,Ꮅ=ꮅ,Ꮆ=ꮆ,=ꮇ,Ꮈ=ꮈ,Ꮉ=ꮉ,Ꮊ=ꮊ,=ꮋ,Ꮌ=ꮌ,=ꮍ,Ꮎ=ꮎ,Ꮏ=ꮏ,=ꮐ,Ꮑ=ꮑ,=ꮒ,=,Ꮔ=ꮔ,Ꮕ=ꮕ,Ꮖ=ꮖ,Ꮗ=ꮗ,Ꮘ=ꮘ,Ꮙ=ꮙ,Ꮚ=ꮚ,Ꮛ=ꮛ,Ꮜ=ꮜ,Ꮝ=ꮝ,=ꮞ,=ꮟ,Ꮠ=ꮠ,Ꮡ=ꮡ,=ꮢ,Ꮣ=ꮣ,=ꮤ,=ꮥ,Ꮦ=ꮦ,Ꮧ=ꮧ,Ꮨ=ꮨ,=,=,Ꮫ=ꮫ,Ꮬ=ꮬ,Ꮭ=ꮭ,=ꮮ,=,Ꮰ=ꮰ,Ꮱ=ꮱ,=ꮲ,Ꮳ=ꮳ,Ꮴ=ꮴ,Ꮵ=ꮵ,=ꮶ,=ꮷ,Ꮸ=ꮸ,Ꮹ=ꮹ,Ꮺ=ꮺ,Ꮻ=ꮻ,Ꮼ=ꮼ,Ꮽ=ꮽ,=ꮾ,Ꮿ=ꮿ
=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=
𐐀=𐐨,𐐁=𐐩,𐐂=𐐪,𐐃=𐐫,𐐄=𐐬,𐐅=𐐭,𐐆=𐐮,𐐇=𐐯,𐐈=𐐰,𐐉=𐐱,𐐊=𐐲,𐐋=𐐳,𐐌=𐐴,𐐍=𐐵,𐐎=𐐶,𐐏=𐐷,𐐐=𐐸,𐐑=𐐹,𐐒=𐐺,𐐓=𐐻,𐐔=𐐼,𐐕=𐐽,𐐖=𐐾,𐐗=𐐿,𐐘=𐑀,𐐙=𐑁,𐐚=𐑂,𐐛=𐑃,𐐜=𐑄,𐐝=𐑅,𐐞=𐑆,𐐟=𐑇,𐐠=𐑈,𐐡=𐑉,𐐢=𐑊,𐐣=𐑋,𐐤=𐑌,𐐥=𐑍,𐐦=𐑎,𐐧=𐑏
𐒰=𐓘,𐒱=𐓙,𐒲=𐓚,𐒳=𐓛,𐒴=𐓜,𐒵=𐓝,𐒶=𐓞,𐒷=𐓟,𐒸=𐓠,𐒹=𐓡,𐒺=𐓢,𐒻=𐓣,𐒼=𐓤,𐒽=𐓥,𐒾=𐓦,𐒿=𐓧,𐓀=𐓨,𐓁=𐓩,𐓂=𐓪,𐓃=𐓫,𐓄=𐓬,𐓅=𐓭,𐓆=𐓮,𐓇=𐓯,𐓈=𐓰,𐓉=𐓱,𐓊=𐓲,𐓋=𐓳,𐓌=𐓴,𐓍=𐓵,𐓎=𐓶,𐓏=𐓷,𐓐=𐓸,𐓑=𐓹,𐓒=𐓺,𐓓=𐓻
𐲀=𐳀,𐲁=𐳁,𐲂=𐳂,𐲃=𐳃,𐲄=𐳄,𐲅=𐳅,𐲆=𐳆,𐲇=𐳇,𐲈=𐳈,𐲉=𐳉,𐲊=𐳊,𐲋=𐳋,𐲌=𐳌,𐲍=𐳍,𐲎=𐳎,𐲏=𐳏,𐲐=𐳐,𐲑=𐳑,𐲒=𐳒,𐲓=𐳓,𐲔=𐳔,𐲕=𐳕,𐲖=𐳖,𐲗=𐳗,𐲘=𐳘,𐲙=𐳙,𐲚=𐳚,𐲛=𐳛,𐲜=𐳜,𐲝=𐳝,𐲞=𐳞,𐲟=𐳟,𐲠=𐳠,𐲡=𐳡,𐲢=𐳢,𐲣=𐳣,𐲤=𐳤,𐲥=𐳥,𐲦=𐳦,𐲧=𐳧,𐲨=𐳨,𐲩=𐳩,𐲪=𐳪,𐲫=𐳫,𐲬=𐳬,𐲭=𐳭,𐲮=𐳮,𐲯=𐳯,𐲰=𐳰,𐲱=𐳱,𐲲=𐳲
𑢠=𑣀,𑢡=𑣁,𑢢=𑣂,𑢣=𑣃,𑢤=𑣄,𑢥=𑣅,𑢦=𑣆,𑢧=𑣇,𑢨=𑣈,𑢩=𑣉,𑢪=𑣊,𑢫=𑣋,𑢬=𑣌,𑢭=𑣍,𑢮=𑣎,𑢯=𑣏,𑢰=𑣐,𑢱=𑣑,𑢲=𑣒,𑢳=𑣓,𑢴=𑣔,𑢵=𑣕,𑢶=𑣖,𑢷=𑣗,𑢸=𑣘,𑢹=𑣙,𑢺=𑣚,𑢻=𑣛,𑢼=𑣜,𑢽=𑣝,𑢾=𑣞,𑢿=𑣟
𖹀=𖹠,𖹁=𖹡,𖹂=𖹢,𖹃=𖹣,𖹄=𖹤,𖹅=𖹥,𖹆=𖹦,𖹇=𖹧,𖹈=𖹨,𖹉=𖹩,𖹊=𖹪,𖹋=𖹫,𖹌=𖹬,𖹍=𖹭,𖹎=𖹮,𖹏=𖹯,𖹐=𖹰,𖹑=𖹱,𖹒=𖹲,𖹓=𖹳,𖹔=𖹴,𖹕=𖹵,𖹖=𖹶,𖹗=𖹷,𖹘=𖹸,𖹙=𖹹,𖹚=𖹺,𖹛=𖹻,𖹜=𖹼,𖹝=𖹽,𖹞=𖹾,𖹟=𖹿
𞤀=𞤢,𞤁=𞤣,𞤂=𞤤,𞤃=𞤥,𞤄=𞤦,𞤅=𞤧,𞤆=𞤨,𞤇=𞤩,𞤈=𞤪,𞤉=𞤫,𞤊=𞤬,𞤋=𞤭,𞤌=𞤮,𞤍=𞤯,𞤎=𞤰,𞤏=𞤱,𞤐=𞤲,𞤑=𞤳,𞤒=𞤴,𞤓=𞤵,𞤔=𞤶,𞤕=𞤷,𞤖=𞤸,𞤗=𞤹,𞤘=𞤺,𞤙=𞤻,𞤚=𞤼,𞤛=𞤽,𞤜=𞤾,𞤝=𞤿,𞤞=𞥀,𞤟=𞥁,𞤠=𞥂,𞤡=𞥃
İ=i̇,ʼN=ʼn,J̌=ǰ,Ϊ́=ΐ,Ϋ́=ΰ,ԵՒ=և,H̱=ẖ,T̈=ẗ,W̊=ẘ,Y̊=ẙ,Aʾ=ẚ,Υ̓=ὐ,Υ̓̀=ὒ,Υ̓́=ὔ,Υ̓͂=ὖ,ἈΙ=ᾀ,ἉΙ=ᾁ,ἊΙ=ᾂ,ἋΙ=ᾃ,ἌΙ=ᾄ,ἍΙ=ᾅ,ἎΙ=ᾆ,ἏΙ=ᾇ,ἨΙ=ᾐ,ἩΙ=ᾑ,ἪΙ=ᾒ,ἫΙ=ᾓ,ἬΙ=ᾔ,ἭΙ=ᾕ,ἮΙ=ᾖ,ἯΙ=ᾗ,ὨΙ=ᾠ,ὩΙ=ᾡ,ὪΙ=ᾢ,ὫΙ=ᾣ,ὬΙ=ᾤ,ὭΙ=ᾥ,ὮΙ=ᾦ,ὯΙ=ᾧ,ᾺΙ=ᾲ,ΑΙ=ᾳ,ΆΙ=ᾴ,Α͂=ᾶ,Α͂Ι=ᾷ,ῊΙ=ῂ,ΗΙ=ῃ,ΉΙ=ῄ,Η͂=ῆ,Η͂Ι=ῇ,Ϊ̀=ῒ,Ι͂=ῖ,Ϊ͂=ῗ,Ϋ̀=ῢ,Ρ̓=ῤ,Υ͂=ῦ,Ϋ͂=ῧ,ῺΙ=ῲ,ΩΙ=ῳ,ΏΙ=ῴ,Ω͂=ῶ,Ω͂Ι=ῷ,ՄՆ=ﬓ,ՄԵ=ﬔ,ՄԻ=ﬕ,ՎՆ=ﬖ,ՄԽ=ﬗ

88
Zframework/mathExtend.lua Normal file
View File

@@ -0,0 +1,88 @@
local MATH={} for k,v in next,math do MATH[k]=v end
local floor,ceil=math.floor,math.ceil
local rnd=math.random
local exp=math.exp
MATH.tau=2*math.pi
MATH.phi=(1+math.sqrt(5))/2
MATH.inf=1/0
MATH.nan=0/0
function MATH.isnan(n)
return n~=n
end
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
function MATH.clamp(v,low,high)
if v<=low then
return low
elseif v>=high then
return high
else
return v
end
end
function MATH.mix(s,e,t)
return s+(e-s)*t
end
do-- function MATH.listMix(list,t)
local clamp,mix=MATH.clamp,MATH.mix
function MATH.listMix(list,t)
local t2=(#list-1)*clamp(t,0,1)+1
return mix(list[floor(t2)],list[ceil(t2)],t2%1)
end
end
function MATH.expApproach(a,b,k)
return b+(a-b)*exp(-k)
end
function MATH.distance(x1,y1,x2,y2)
return ((x1-x2)^2+(y1-y2)^2)^.5
end
-- By Pedro Gimeno,donated to the public domain
function MATH.pointInPolygon(x,y,poly,evenOddRule)
local x1,y1,x2,y2
local len=#poly
x2,y2=poly[len-1],poly[len]
local wn=0
for idx=1,len,2 do
x1,y1=x2,y2
x2,y2=poly[idx],poly[idx+1]
if y1>y then
if y2<=y and (x1-x)*(y2-y)<(x2-x)*(y1-y) then
wn=wn+1
end
else
if y2>y and (x1-x)*(y2-y)>(x2-x)*(y1-y) then
wn=wn-1
end
end
end
if evenOddRule then
return wn%2~=0
else-- non-zero winding rule
return wn~=0
end
end
return MATH

153
Zframework/message.lua Normal file
View File

@@ -0,0 +1,153 @@
local ins,rem=table.insert,table.remove
local max=math.max
local mesList={}
local mesIcon={
check=GC.DO{40,40,
{'setLW',10},
{'setCL',0,0,0},
{'line',4,19,15,30,36,9},
{'setLW',6},
{'setCL',.7,1,.6},
{'line',5,20,15,30,35,10},
},
info=GC.DO{40,40,
{'setCL',.2,.25,.85},
{'fCirc',20,20,15},
{'setCL',1,1,1},
{'setLW',2},
{'dCirc',20,20,15},
{'fRect',18,11,4,4},
{'fRect',18,17,4,12},
},
broadcast=GC.DO{40,40,
{'setCL',1,1,1},
{'fRect',2,4,36,26,3},
{'fPoly',2,27,2,37,14,25},
{'setCL',.5,.5,.5},
{'fRect',6,11,4,4,1},{'fRect',14,11,19,4,1},
{'fRect',6,19,4,4,1},{'fRect',14,19,19,4,1},
},
warn=GC.DO{40,40,
{'setCL',.95,.83,.4},
{'fPoly',20.5,1,0,38,40,38},
{'setCL',0,0,0},
{'dPoly',20.5,1,0,38,40,38},
{'fRect',17,10,7,18,2},
{'fRect',17,29,7,7,2},
{'setCL',1,1,1},
{'fRect',18,11,5,16,2},
{'fRect',18,30,5,5,2},
},
error=GC.DO{40,40,
{'setCL',.95,.3,.3},
{'fCirc',20,20,19},
{'setCL',0,0,0},
{'dCirc',20,20,19},
{'setLW',6},
{'line',10.2,10.2,29.8,29.8},
{'line',10.2,29.8,29.8,10.2},
{'setLW',4},
{'setCL',1,1,1},
{'line',11,11,29,29},
{'line',11,29,29,11},
},
music=GC.DO{40,40,
{'setLW',2},
{'dRect',1,3,38,34,3},
{'setLW',4},
{'line',21,26,21,10,28,10},
{'fElps',17,26,6,5},
},
}
local MES={}
local backColors={
check={.3,.6,.3,.7},
broadcast={.3,.3,.6,.8},
warn={.4,.4,.2,.9},
error={.4,.2,.2,.9},
music={.2,.4,.4,.9},
other={.5,.5,.5,.7},
}
function MES.new(icon,str,time)
local color=backColors.other
if type(icon)=='string' then
color=TABLE.shift(backColors[icon] or color)
icon=mesIcon[icon]
end
local text=GC.newText(FONT.get(30),str)
local w=math.max(text:getWidth()+(icon and 45 or 5),200)+15
local h=math.max(text:getHeight(),46)+2
local k=h>400 and 1/math.min(h/400,2.6) or 1
ins(mesList,1,{
startTime=.26,
endTime=.26,
time=time or 3,
color=color,
text=text,icon=icon,
w=w,h=h,k=k,
y=-h,
})
end
function MES.update(dt)
for i=#mesList,1,-1 do
local m=mesList[i]
if m.startTime>0 then
m.startTime=max(m.startTime-dt,0)
elseif m.time>0 then
m.time=max(m.time-dt,0)
elseif m.endTime>0 then
m.endTime=m.endTime-dt
else
rem(mesList,i)
end
if i>1 then
local _m=mesList[i-1]
m.y=MATH.expApproach(m.y,_m.y+_m.h*_m.k+3,dt*26)
else
m.y=MATH.expApproach(m.y,3,dt*26)
end
end
end
function MES.draw()
if #mesList>0 then
GC.setLineWidth(2)
for i=1,#mesList do
local m=mesList[i]
local a=3.846*(m.endTime-m.startTime)
GC.push('transform')
GC.translate(3+SCR.safeX,m.y)
GC.scale(m.k)
GC.setColor(m.color[1],m.color[2],m.color[3],m.color[4]*a)
GC.rectangle('fill',0,0,m.w,m.h,8)
GC.setColor(.62,.62,.62,a*.626)
GC.rectangle('line',1,1,m.w-2,m.h-2,4)
GC.setColor(1,1,1,a)
if m.icon then
GC.draw(m.icon,4,4,nil,40/m.icon:getWidth(),40/m.icon:getHeight())
end
GC.simpY(m.text,m.icon and 50 or 10,m.h/2)
GC.pop()
end
end
end
function MES.traceback()
local mes=
debug.traceback('',1)
:gsub(': in function',', in')
:gsub(':',' ')
:gsub('\t','')
MES.new('error',mes:sub(
mes:find("\n",2)+1,
mes:find("\n%[C%], in 'xpcall'")
),5)
end
return MES

157
Zframework/profile.lua Normal file
View File

@@ -0,0 +1,157 @@
local clock=os.clock
local profile={}
local _labeled={} -- function labels
local _defined={} -- function definitions
local _tcalled={} -- time of last call
local _telapsed={}-- total execution time
local _ncalls={} -- number of calls
local _internal={}-- list of internal profiler functions
local getInfo=debug.getinfo
function profile.hooker(event,line,info)
info=info or getInfo(2,'fnS')
local f=info.func
if _internal[f] then return end-- ignore the profiler itself
if info.name then _labeled[f]=info.name end-- get the function name if available
-- find the line definition
if not _defined[f] then
_defined[f]=info.short_src..":"..info.linedefined
_ncalls[f]=0
_telapsed[f]=0
end
if _tcalled[f] then
local dt=clock()-_tcalled[f]
_telapsed[f]=_telapsed[f]+dt
_tcalled[f]=nil
end
if event=='tail call' then
local prev=getInfo(3,'fnS')
profile.hooker('return',line,prev)
profile.hooker('call',line,info)
elseif event=='call' then
_tcalled[f]=clock()
else
_ncalls[f]=_ncalls[f]+1
end
end
--- Starts collecting data.
function profile.start()
if jit then
jit.off()
jit.flush()
end
debug.sethook(profile.hooker,'cr')
end
--- Stops collecting data.
function profile.stop()
debug.sethook()
for f in next,_tcalled do
local dt=clock()-_tcalled[f]
_telapsed[f]=_telapsed[f]+dt
_tcalled[f]=nil
end
-- merge closures
local lookup={}
for f,d in next,_defined do
local id=(_labeled[f] or "?")..d
local f2=lookup[id]
if f2 then
_ncalls[f2]=_ncalls[f2]+(_ncalls[f] or 0)
_telapsed[f2]=_telapsed[f2]+(_telapsed[f] or 0)
_defined[f],_labeled[f]=nil,nil
_ncalls[f],_telapsed[f]=nil,nil
else
lookup[id]=f
end
end
collectgarbage()
end
--- Resets all collected data.
function profile.reset()
for f in next,_ncalls do
_ncalls[f]=0
_telapsed[f]=0
_tcalled[f]=nil
end
collectgarbage()
end
local function _comp(a,b)
local dt=_telapsed[b]-_telapsed[a]
return dt==0 and _ncalls[b]<_ncalls[a] or dt<0
end
--- Iterates all functions that have been called since the profile was started.
function profile.query(limit)
local t={}
for f,n in next,_ncalls do
if n>0 then
t[#t+1]=f
end
end
table.sort(t,_comp)
if limit then while #t>limit do table.remove(t) end end
for i,f in ipairs(t) do
local dt=0
if _tcalled[f] then
dt=clock()-_tcalled[f]
end
t[i]={i,_labeled[f] or "?",math.floor((_telapsed[f]+dt)*1e6)/1e6,_ncalls[f],_defined[f]}
end
return t
end
local cols={3,20,8,6,32}
function profile.report(n)
local out={}
local report=profile.query(n)
for i,row in ipairs(report) do
for j=1,5 do
local s=tostring(row[j])
local l1,l2=#s,cols[j]
if l1<l2 then
s=s..(" "):rep(l2-l1)
elseif l1>l2 then
s=s:sub(l1-l2+1,l1)
end
row[j]=s
end
out[i]=table.concat(row," | ")
end
local row=" +-----+----------------------+----------+--------+----------------------------------+ \n"
local col=" | # | Function | Time | Calls | Code | \n"
local sz=row..col..row
if #out>0 then
sz=sz.." | "..table.concat(out," | \n | ").." | \n"
end
return "\n"..sz..row
end
local switch=false
function profile.switch()
switch=not switch
if not switch then
profile.stop()
love.system.setClipboardText(profile.report())
profile.reset()
return false
else
profile.start()
return true
end
end
-- store all internal profiler functions
for _,v in next,profile do
_internal[v]=type(v)=='function'
end
return profile

47
Zframework/require.lua Normal file
View File

@@ -0,0 +1,47 @@
package.cpath=package.cpath..';'..love.filesystem.getSaveDirectory()..'/lib/?.so;'..'?.dylib'
local loaded={}
local errorCount={}
return function(libName)
local require=require
local arch='unknown'
local success,res
if SYSTEM=='Web' then
return
end
if SYSTEM=='macOS' then
require=package.loadlib(libName..'.dylib','luaopen_'..libName)
success,res=pcall(require)
else
if SYSTEM=='Android' and not loaded[libName] then
local platform=(function()
local p=io.popen('uname -m')
arch=p:read('*a'):lower()
p:close()
if arch:find('v8') and not arch:find('v8l') or arch:find('64') then
return 'arm64-v8a'
else
return 'armeabi-v7a'
end
end)()
local data=love.filesystem.read('data','libAndroid/'..platform..'/'..libName..'.so')
if data then
love.filesystem.write('lib/'..libName..'.so',data)
end
loaded[libName]=true
end
success,res=pcall(require,libName)
end
if success and res then
return res
else
if not next(errorCount) then
MES.new('info',"Architecture: "..arch)
end
errorCount[libName]=(errorCount[libName] or 0)+1
if errorCount[libName]==1 then
MES.new('error',"Cannot load "..libName..": "..tostring(res):gsub('[\128-\255]+','??'))
else
MES.new('error',("Cannot load %s (x%d)"):format(libName,errorCount[libName]))
end
end
end

223
Zframework/scene.lua Normal file
View File

@@ -0,0 +1,223 @@
local scenes={}
local SCN={
mainTouchID=nil, -- First touching ID(userdata)
swapping=false, -- If Swapping
state={
tar=false, -- Swapping target
style=false, -- Swapping style
changeTime=false,-- Loading point
time=false, -- Full swap time
draw=false, -- Swap draw func
},
stack={},-- Scene stack
prev=false,
cur=false,
args={},-- Arguments from previous scene
scenes=scenes,
-- Events
update=false,
draw=false,
mouseClick=false,
touchClick=false,
mouseDown=false,
mouseMove=false,
mouseUp=false,
wheelMoved=false,
touchDown=false,
touchUp=false,
touchMove=false,
keyDown=false,
keyUp=false,
gamepadDown=false,
gamepadUp=false,
fileDropped=false,
directoryDropped=false,
resize=false,
}-- Scene datas, returned
function SCN.add(name,scene)
scenes[name]=scene
if scene.widgetList then
setmetatable(scene.widgetList,WIDGET.indexMeta)
end
end
function SCN.swapUpdate(dt)
local S=SCN.state
S.time=S.time-dt
if S.time<S.changeTime and S.time+dt>=S.changeTime then
-- Scene swapped this frame
SCN.stack[#SCN.stack]=S.tar
SCN.cur=S.tar
SCN.init(S.tar)
SCN.mainTouchID=nil
end
if S.time<0 then
SCN.swapping=false
end
end
function SCN.init(s)
love.keyboard.setTextInput(false)
local S=scenes[s]
WIDGET.setScrollHeight(S.widgetScrollHeight)
WIDGET.setWidgetList(S.widgetList)
SCN.enter=S.enter
SCN.leave=S.leave
SCN.mouseDown=S.mouseDown
SCN.mouseMove=S.mouseMove
SCN.mouseUp=S.mouseUp
SCN.mouseClick=S.mouseClick
SCN.wheelMoved=S.wheelMoved
SCN.touchDown=S.touchDown
SCN.touchUp=S.touchUp
SCN.touchMove=S.touchMove
SCN.touchClick=S.touchClick
SCN.keyDown=S.keyDown
SCN.keyUp=S.keyUp
SCN.gamepadDown=S.gamepadDown
SCN.gamepadUp=S.gamepadUp
SCN.fileDropped=S.fileDropped
SCN.directoryDropped=S.directoryDropped
SCN.resize=S.resize
SCN.update=S.update
SCN.draw=S.draw
if S.enter then
S.enter()
end
end
function SCN.push(tar)
table.insert(SCN.stack,tar or SCN.stack[#SCN.stack])
end
function SCN.pop()
table.remove(SCN.stack)
end
local swap={
none={
duration=0,changeTime=0,
draw=function() end
},
flash={
duration=.16,changeTime=.08,
draw=function() GC.clear(1,1,1) end
},
fade={
duration=.5,changeTime=.25,
draw=function(t)
t=t>.25 and 2-t*4 or t*4
GC.setColor(0,0,0,t)
GC.rectangle('fill',0,0,SCR.w,SCR.h)
end
},
fade_togame={
duration=2,changeTime=.5,
draw=function(t)
t=t>.5 and (2-t)/1.5 or t*.5
GC.setColor(0,0,0,t)
GC.rectangle('fill',0,0,SCR.w,SCR.h)
end
},
slowFade={
duration=3,changeTime=1.5,
draw=function(t)
t=t>1.5 and (3-t)/1.5 or t/1.5
GC.setColor(0,0,0,t)
GC.rectangle('fill',0,0,SCR.w,SCR.h)
end
},
swipeL={
duration=.5,changeTime=.25,
draw=function(t)
t=t*2
GC.setColor(.1,.1,.1,1-math.abs(t-.5))
t=t*t*(3-2*t)*2-1
GC.rectangle('fill',t*SCR.w,0,SCR.w,SCR.h)
end
},
swipeR={
duration=.5,changeTime=.25,
draw=function(t)
t=t*2
GC.setColor(.1,.1,.1,1-math.abs(t-.5))
t=t*t*(2*t-3)*2+1
GC.rectangle('fill',t*SCR.w,0,SCR.w,SCR.h)
end
},
swipeD={
duration=.5,changeTime=.25,
draw=function(t)
t=t*2
GC.setColor(.1,.1,.1,1-math.abs(t-.5))
t=t*t*(2*t-3)*2+1
GC.rectangle('fill',0,t*SCR.h,SCR.w,SCR.h)
end
},
}-- Scene swapping animations
function SCN.swapTo(tar,style,...)-- Parallel scene swapping, cannot back
if scenes[tar] then
if not SCN.swapping then
SCN.prev=SCN.stack[#SCN.stack]
style=style or 'fade'
SCN.swapping=true
SCN.args={...}
local S=SCN.state
S.tar,S.style=tar,style
S.time=swap[style].duration
S.changeTime=swap[style].changeTime
S.draw=swap[style].draw
end
else
MES.new('warn',"No Scene: "..tostring(tar))
end
end
function SCN.go(tar,style,...)-- Normal scene swapping, can back
if scenes[tar] then
if not SCN.swapping then
SCN.push(SCN.stack[#SCN.stack] or '_')
SCN.swapTo(tar,style,...)
end
else
MES.new('warn',"No Scene: "..tar)
end
end
function SCN.back(style,...)
if SCN.swapping then return end
-- Leave scene
if SCN.leave then
SCN.leave()
end
-- Poll&Back to previous Scene
if #SCN.stack>1 then
SCN.pop()
SCN.swapTo(SCN.stack[#SCN.stack],style,...)
else
SCN.swapTo('quit','slowFade')
end
end
function SCN.backTo(tar,style,...)
if SCN.swapping then return end
-- Leave scene
if SCN.leave then
SCN.leave()
end
-- Poll&Back to previous Scene
while SCN.stack[#SCN.stack]~=tar and #SCN.stack>1 do
SCN.pop()
end
SCN.swapTo(SCN.stack[#SCN.stack],style,...)
end
function SCN.printStack()
for i=0,#SCN.stack+1 do print(SCN.stack[i] or "-------") end
end
return SCN

73
Zframework/screen.lua Normal file
View File

@@ -0,0 +1,73 @@
local SCR={
w0=1280,h0=720, -- Default Screen Size
x=0,y=0, -- Up-left Coord on screen
cx=0,cy=0, -- Center Coord on screen (Center X/Y)
ex=0,ey=0, -- Down-right Coord on screen (End X/Y)
w=0,h=0, -- Fullscreen w/h for graphic functions
W=0,H=0, -- Fullscreen w/h for shader
safeX=0,safeY=0,-- Safe area
safeW=0,safeH=0,-- Safe area
rad=0, -- Radius
k=1, -- Scale size
dpi=1, -- DPI from gc.getDPIScale()
-- Screen transformation objects
origin=love.math.newTransform(),
xOy=love.math.newTransform(),
xOy_m=love.math.newTransform(),
xOy_ul=love.math.newTransform(),
xOy_u=love.math.newTransform(),
xOy_ur=love.math.newTransform(),
xOy_l=love.math.newTransform(),
xOy_r=love.math.newTransform(),
xOy_dl=love.math.newTransform(),
xOy_d=love.math.newTransform(),
xOy_dr=love.math.newTransform(),
}
function SCR.setSize(w,h)
SCR.w0,SCR.h0=w,h
end
function SCR.resize(w,h)
SCR.w,SCR.h,SCR.dpi=w,h,love.graphics.getDPIScale()
SCR.W,SCR.H=SCR.w*SCR.dpi,SCR.h*SCR.dpi
SCR.r=h/w
SCR.rad=(w^2+h^2)^.5
SCR.x,SCR.y=0,0
if SCR.r>=SCR.h0/SCR.w0 then
SCR.k=w/SCR.w0
SCR.y=(h-SCR.h0*SCR.k)/2
else
SCR.k=h/SCR.h0
SCR.x=(w-SCR.w0*SCR.k)/2
end
SCR.cx,SCR.cy=SCR.w/2,SCR.h/2
SCR.ex,SCR.ey=SCR.w-SCR.x,SCR.h-SCR.y
SCR.safeX,SCR.safeY,SCR.safeW,SCR.safeH=love.window.getSafeArea()
SCR.origin:setTransformation(0,0)
SCR.xOy:setTransformation(SCR.x,SCR.y,0,SCR.k)
SCR.xOy_m:setTransformation(w/2,h/2,0,SCR.k)
SCR.xOy_ul:setTransformation(0,0,0,SCR.k)
SCR.xOy_u:setTransformation(w/2,0,0,SCR.k)
SCR.xOy_ur:setTransformation(w,0,0,SCR.k)
SCR.xOy_l:setTransformation(0,h/2,0,SCR.k)
SCR.xOy_r:setTransformation(w,h/2,0,SCR.k)
SCR.xOy_dl:setTransformation(0,h,0,SCR.k)
SCR.xOy_d:setTransformation(w/2,h,0,SCR.k)
SCR.xOy_dr:setTransformation(w,h,0,SCR.k)
end
function SCR.info()
return {
("w0,h0 : %d, %d"):format(SCR.w0,SCR.h0),
("x,y : %d, %d"):format(SCR.x,SCR.y),
("cx,cy : %d, %d"):format(SCR.cx,SCR.cy),
("ex,ey : %d, %d"):format(SCR.ex,SCR.ey),
("w,h : %d, %d"):format(SCR.w,SCR.h),
("W,H : %d, %d"):format(SCR.W,SCR.H),
("safeX,safeY : %d, %d"):format(SCR.safeX,SCR.safeY),
("safeW,safeH : %d, %d"):format(SCR.safeW,SCR.safeH),
("k,dpi,rad : %.2f, %d, %.2f"):format(SCR.k,SCR.dpi,SCR.rad),
}
end
return SCR

169
Zframework/sfx.lua Normal file
View File

@@ -0,0 +1,169 @@
local type,rem=type,table.remove
local floor,rnd=math.floor,math.random
local sfxList={}
local packSetting={}
local Sources={}
local volume=1
local stereo=1
local noteVal={
C=1,c=1,
D=3,d=3,
E=5,e=5,
F=6,f=6,
G=8,g=8,
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)]
if tuneHeight then
tuneHeight=tuneHeight+(octave-1)*12
local s=tune:sub(2,2)
if s=='s' or s=='#' then
tuneHeight=tuneHeight+1
elseif s=='f' or s=='b' then
tuneHeight=tuneHeight-1
end
return tuneHeight
end
end
end
local SFX={}
function SFX.init(list)
assert(type(list)=='table',"Initialize SFX lib with a list of filenames!")
for i=1,#list do table.insert(sfxList,list[i]) end
end
function SFX.load(path)
local c=0
local missing=0
for i=1,#sfxList do
local fullPath=path..sfxList[i]..'.ogg'
if love.filesystem.getInfo(fullPath) then
if Sources[sfxList[i]] then
for j=1,#Sources[sfxList[i]] do
Sources[sfxList[i]][j]:release()
end
end
Sources[sfxList[i]]={love.audio.newSource(fullPath,'static')}
c=c+1
else
LOG("No SFX: "..sfxList[i]..'.ogg',.1)
missing=missing+1
end
end
LOG(c.."/"..#sfxList.." SFX files loaded")
LOG(missing.." SFX files missing")
if missing>0 then
MES.new('info',missing.." SFX files missing")
end
collectgarbage()
end
function SFX.loadSample(pack)
assert(type(pack)=='table',"Usage: SFX.loadsample([table])")
assert(pack.name,"No field: name")
assert(pack.path,"No field: path")
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
function SFX.getCount()
return #sfxList
end
function SFX.setVol(v)
assert(type(v)=='number' and v>=0 and v<=1,'Wrong volume')
volume=v
end
function SFX.setStereo(v)
assert(type(v)=='number' and v>=0 and v<=1,'Wrong stereo')
stereo=v
end
function SFX.getNoteName(note)
if note<1 then
return '---'
else
note=note-1
local octave=floor(note/12)+1
return noteName[note%12+1]..octave
end
end
function SFX.playSample(pack,...)-- vol-1, sampSet1, vol-2, sampSet2
if ... then
local arg={...}
local vol
for i=1,#arg do
local a=arg[i]
if type(a)=='number' and a<=1 then
vol=a
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)
end
end
end
end
local function _play(name,vol,pos,pitch)
if volume==0 or vol==0 then return end
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=MATH.clamp(pos,-1,1)*stereo
S:setPosition(pos,1-pos^2,0)
else
S:setPosition(0,0,0)
end
end
S:setVolume(vol^1.626)
S:setPitch(pitch and 1.0594630943592953^pitch or 1)
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)
end
function SFX.reset()
for _,L in next,Sources do
if type(L)=='table' then
for i=#L,1,-1 do
if not L[i]:isPlaying() then
rem(L,i)
end
end
end
end
end
return SFX

5675
Zframework/sha2.lua Normal file

File diff suppressed because it is too large Load Diff

428
Zframework/stringExtend.lua Normal file
View File

@@ -0,0 +1,428 @@
local data=love.data
local STRING={}
local assert,tostring,tonumber=assert,tostring,tonumber
local floorint,format=math.floor,string.format
local find,sub,gsub=string.find,string.sub,string.gsub
local rep,upper=string.rep,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']='%',
['6']='^',['7']='&',['8']='*',['9']='(',['0']=')',
['`']='~',['-']='_',['=']='+',
['[']='{',[']']='}',['\\']='|',
[';']=':',['\'']='"',
[',']='<',['.']='>',['/']='?',
}
function STRING.shiftChar(c)
return shiftMap[c] or upper(c)
end
end
-- [LOW PERFORMANCE!]
local upperData,lowerData,diaData
function STRING.upperUTF8(str)
for _,pair in next,upperData do
str=str:gsub(pair[1],pair[2])
end
return str
end
function STRING.lowerUTF8(str)
for _,pair in next,lowerData do
str=str:gsub(pair[1],pair[2])
end
return str
end
function STRING.remDiacritics(str)
for _,pair in next,diaData do
str=str:gsub(pair[1],pair[2])
end
return str
end
function STRING.trim(s)
if not s:find("%S") then return "" end
s=s:sub((s:find("%S"))):reverse()
return s:sub((s:find("%S"))):reverse()
end
function STRING.split(s,sep,regex)
local L={}
local p1,p2=1-- start,target
if regex then
while p1<=#s do
p2=find(s,sep,p1) or #s+1
L[#L+1]=sub(s,p1,p2-1)
p1=p2+#sep
end
else
while p1<=#s do
p2=find(s,sep,p1,true) or #s+1
L[#L+1]=sub(s,p1,p2-1)
p1=p2+#sep
end
end
return L
end
function STRING.simpEmailCheck(e)
e=STRING.split(e,"@")
if #e~=2 then return false end
if e[1]:sub(-1)=="." or e[2]:sub(-1)=="." then return false end
local e1,e2=STRING.split(e[1],"."),STRING.split(e[2],".")
if #e1*#e2==0 then return false end
for _,v in next,e1 do if #v==0 then return false end end
for _,v in next,e2 do if #v==0 then return false end end
return true
end
local MINUTE=60
local HOUR=3600
local DAY=86400
local YEAR=31536000 -- 365 days
local function convertSecondsToUnits(t) -- convert seconds to {seconds, minutes, hours, days, years}
local years=floorint(t/YEAR)
local remainder=t%YEAR
local days=floorint(remainder/DAY)
remainder=remainder%DAY
local hours=floorint(remainder/HOUR)
remainder=remainder%HOUR
local minutes=floorint(remainder/MINUTE)
local seconds=remainder%MINUTE
return seconds,minutes,hours,days,years
end
-- MM:SS
function STRING.time_simp(t)
return format("%02d:%02d",floorint(t/MINUTE),floorint(t%MINUTE))
end
local timeLetters={' y',' d',' h',' m',' s',' ms'}
-- Display 2 largest units of time.
function STRING.time_short(t)
-- Early returns to prevent nil values
if t<0 then return '-'..STRING.time_short(-t) end -- negative time
if t<1 then return math.floor(t*1000)..timeLetters[6] end -- 123 ms
if t<MINUTE then return math.floor(t)..timeLetters[5]..' '..math.floor((t%1)*1000)..timeLetters[6] end -- 12s 345ms
local timeUnits=TABLE.reverse({convertSecondsToUnits(t)})
-- floor seconds
timeUnits[#timeUnits]=floorint(timeUnits[#timeUnits])
local outputStr=''
for i=1,#timeUnits do
if timeUnits>0 then
return timeUnits[i]..timeLetters[i]..' '..timeUnits[i+1]..timeLetters[i+1]
end
end
end
function STRING.time(t)
local s,m,h,d,y=convertSecondsToUnits(t)
if t<MINUTE then
return format("%.3f″",t) -- example: 12.345″
elseif t<HOUR then
return format("%d%05.2f″",m,s) -- 123.45″
elseif t<DAY then
return format("%d:%.2d%04.1f″",h,m,s) -- 12:3456.7″
elseif t<YEAR then
return format("%dd %d:%.2d%.2d″",d,h,m,s) -- 123d 12:3456″
else
return format("%dy %dd %d:%.2d",y,d,h,m) -- 1y 234d 12:34
end
end
function STRING.time_ext(t)
local s,m,h,d,y=convertSecondsToUnits(t)
if t<MINUTE then
return format("%.5f″",t) -- 12.34567″
elseif t<HOUR then
return format("%d%06.3f″",m,s) -- 123.456″
elseif t<DAY then
return format("%d:%.2d%05.2f″",h,m,s) -- 12:3456.78″
elseif t<YEAR then
return format("%dd %d:%.2d%04.1f″",d,h,m,s) -- 123d 12:3456.7″
else
return format("%dy %dd %d:%.2d%.2d″",y,d,h,m,s) -- 1y 234d 12:3456″
end
end
function STRING.UTF8(n)-- Simple utf8 coding
assert(type(n)=='number',"Wrong type ("..type(n)..")")
assert(n>=0 and n<2^31,"Out of range ("..n..")")
if n<2^7 then return char(n)
elseif n<2^11 then return char(192+floorint(n/2^06),128+n%2^6)
elseif n<2^16 then return char(224+floorint(n/2^12),128+floorint(n/2^06)%2^6,128+n%2^6)
elseif n<2^21 then return char(240+floorint(n/2^18),128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6)
elseif n<2^26 then return char(248+floorint(n/2^24),128+floorint(n/2^18)%2^6,128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6)
elseif n<2^31 then return char(252+floorint(n/2^30),128+floorint(n/2^24)%2^6,128+floorint(n/2^18)%2^6,128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6)
end
end
do-- functions to shorted big numbers
local lg=math.log10
local units={"","K","M","B","T","Qa","Qt","Sx","Sp","Oc","No"}
local preUnits={"","U","D","T","Qa","Qt","Sx","Sp","O","N"}
local secUnits={"Dc","Vg","Tg","Qd","Qi","Se","St","Og","Nn","Ce"}-- Ce is next-level unit, but DcCe is not used so used here
for _,preU in next,preUnits do for _,secU in next,secUnits do table.insert(units,preU..secU) end end
function STRING.bigInt(t)
if t<1000 then
return tostring(t)
elseif t~=1e999 then
local e=floorint(lg(t)/3)
return(t/10^(e*3))..units[e+1]
else
return "INF"
end
end
local MIN_SI=-30 -- current lowest order of magnitude for SI units (quecto-; 10^-30)
local MAX_SI=30 -- current highest order of magnitude for SI units (quetta-; 10^30)
local SI_SHORT={
[-30]='q',[-27]='r',[-24]='y',[-21]='z',[-18]='a',[-15]='f',[-12]='p',
[-9]='n',[-6]='μ',[-3]='m',[0]='',[3]='k',[6]='M',[9]='G',
[12]='T',[15]='P',[18]='E',[21]='Z',[24]='Y',[27]='R',[30]='Q'
}
local SI_LONG={
[-30]='quecto',[-27]='ronto',[-24]='yocto',[-21]='zepto',[-18]='atto',[-15]='femto',[-12]='pico',
[-9]='nano',[-6]='micro',[-3]='milli',[0]='',[3]='kilo',[6]='mega',[9]='giga',
[12]='tera',[15]='peta',[18]='exa',[21]='zetta',[24]='yotta',[27]='ronna',[30]='quetta'
}
--[[
Converts a number into SI notation with letter prefixes.
NOTE: Only power-of-thousand prefixes; no deci-/centi-.
Arguments:
- num: The number to be converted to SI notation.
- unit: [optional] The unit to be concatenated at the end.
Example: STRING.SI(10^-9,"m") --> "1 nm"
]]
function STRING.SI(num, unit)
unit=unit or ''
local order=MATH.clamp(3*math.floor(math.log10(num)/3),MIN_SI,MAX_SI)
local prefix=SI_SHORT[order]
local scaledNum=num/10^order
local formattedNum=string.format('%.3f', scaledNum):gsub('%.?0+$','')
return formattedNum.." "..prefix..unit
end
--[[
Converts a number into SI notation with word prefixes.
NOTE: Only power-of-thousand prefixes; no deci-/centi-.
Arguments:
- num: The number to be converted to SI notation.
- unit: [optional] The unit to be concatenated at the end.
Example: STRING.SI(10^9,"hertz") --> "1 megahertz"
]]
function STRING.SILong(num, unit)
unit=unit or ''
local order=MATH.clamp(3*math.floor(math.log10(num)/3),MIN_SI,MAX_SI)
local prefix=SI_LONG[order]
local scaledNum=num/10^order
local formattedNum=string.format('%.3f', scaledNum):gsub('%.?0+$','')
return formattedNum.." "..prefix..unit
end
end
do-- function STRING.toBin, STRING.toOct, STRING.toHex(n,len)
function STRING.toBin(n,len)
local s=""
while n>0 do
s=(n%2)..s
n=floorint(n/2)
end
if len then
return rep("0",len-#s)..s
else
return s
end
end
function STRING.toOct(n,len)
local s=""
while n>0 do
s=(n%8)..s
n=floorint(n/8)
end
if len then
return rep("0",len-#s)..s
else
return s
end
end
local b16={[0]='0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}
function STRING.toHex(n,len)
local s=""
while n>0 do
s=b16[n%16]..s
n=floorint(n/16)
end
if len then
return rep("0",len-#s)..s
else
return s
end
end
end
function STRING.hexColor(str)--[LOW PERFORMENCE]
assert(type(str)=='string')
if str:sub(1,1)=="#" then str=str:sub(2) end
assert(#str<=8)
local r=(tonumber(str:sub(1,2),16) or 0)/255
local g=(tonumber(str:sub(3,4),16) or 0)/255
local b=(tonumber(str:sub(5,6),16) or 0)/255
local a=(tonumber(str:sub(7,8),16) or 255)/255
return r,g,b,a
end
do-- function STRING.urlEncode(s)
local rshift=bit.rshift
local b16={[0]='0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}
function STRING.urlEncode(s)
local out=""
for i=1,#s do
if s:sub(i,i):match("[a-zA-Z0-9]") then
out=out..s:sub(i,i)
else
local b=s:byte(i)
out=out.."%"..b16[rshift(b,4)]..b16[b%16]
end
end
return out
end
end
function STRING.vcsEncrypt(text,key)
local keyLen=#key
local result=""
local buffer=""
for i=0,#text-1 do
buffer=buffer..char((byte(text,i+1)-32+byte(key,i%keyLen+1))%95+32)
if #buffer==26 then
result=result..buffer
buffer=""
end
end
return result..buffer
end
function STRING.vcsDecrypt(text,key)
local keyLen=#key
local result=""
local buffer=""
for i=0,#text-1 do
buffer=buffer..char((byte(text,i+1)-32-byte(key,i%keyLen+1))%95+32)
if #buffer==26 then
result=result..buffer
buffer=""
end
end
return result..buffer
end
function STRING.digezt(text)-- Not powerful hash, just protect the original text
local out={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local seed=26
for i=1,#text do
local c=byte(text,i)
seed=(seed+c)%26
c=c+seed
local pos=c*i%16
local step=(c+i)%4+1
local times=2+(c%6)
for _=1,times do
out[pos+1]=(out[pos+1]+c)%256
pos=(pos+step)%16
end
end
local result=""
for i=1,16 do result=result..char(out[i]) end
return result
end
function STRING.readLine(str)
local p=str:find("\n")
if p then
return str:sub(1,p-1),str:sub(p+1)
else
return str,""
end
end
function STRING.readChars(str,n)
return sub(str,1,n),sub(str,n+1)
end
function STRING.packBin(s)
return data.encode('string','base64',data.compress('string','zlib',s))
end
function STRING.unpackBin(str)
local res
res,str=pcall(data.decode,'string','base64',str)
if not res then return end
res,str=pcall(data.decompress,'string','zlib',str)
if res then return str end
end
function STRING.packText(s)
return data.encode('string','base64',data.compress('string','gzip',s))
end
function STRING.unpackText(str)
local res
res,str=pcall(data.decode,'string','base64',str)
if not res then return end
res,str=pcall(data.decompress,'string','gzip',str)
if res then return str end
end
function STRING.packTable(t)
return STRING.packText(JSON.encode(t))
end
function STRING.unpackTable(t)
return JSON.decode(STRING.unpackText(t))
end
do
local function parseFile(fname)
local d
if love and love.filesystem and type(love.filesystem.read)=='function' then
d=love.filesystem.read(fname)
else
local f=io.open(fname,'r')
if f then
d=f:read('a')
f:close()
end
end
if not d then
print("ERROR: Failed to read the data from "..fname)
return {}
end
d=STRING.split(gsub(d,'\n',','),',')
for i=1,#d do
d[i]=STRING.split(d[i],'=')
end
return d
end
upperData=parseFile('Zframework/upcaser.txt')
lowerData=parseFile('Zframework/lowcaser.txt')
diaData=parseFile('Zframework/diacritics.txt')
end
return STRING

205
Zframework/sysFX.lua Normal file
View File

@@ -0,0 +1,205 @@
local gc=love.graphics
local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth
local gc_draw,gc_line=gc.draw,gc.line
local gc_rectangle,gc_circle=gc.rectangle,gc.circle
local max,min=math.max,math.min
local rnd=math.random
local ins,rem=table.insert,table.remove
local fx={}
local function _normUpdate(S,dt)
S.t=S.t+dt*S.rate
return S.t>1
end
local FXupdate={}
function FXupdate.badge(S,dt)
S.t=S.t+dt
if S.t<.2 then
S.x,S.y=S.x1-14,S.y1-14
elseif S.t<.8 then
local t=((S.t-.2)*1.6667)
t=(3-2*t)*t*t
S.x,S.y=S.x1*(1-t)+S.x2*t-14,S.y1*(1-t)+S.y2*t-14
else
S.x,S.y=S.x2-14,S.y2-14
end
return S.t>=1
end
FXupdate.attack=_normUpdate
FXupdate.tap=_normUpdate
FXupdate.ripple=_normUpdate
FXupdate.rectRipple=_normUpdate
FXupdate.shade=_normUpdate
function FXupdate.cell(S,dt)
if S.vx then
S.x=S.x+S.vx*S.rate
S.y=S.y+S.vy*S.rate
if S.ax then
S.vx=S.vx+S.ax*S.rate
S.vy=S.vy+S.ay*S.rate
end
end
S.t=S.t+dt*S.rate
return S.t>1
end
FXupdate.line=_normUpdate
local FXdraw={}
function FXdraw.badge(S)
gc_setColor(1,1,1,S.t<.2 and S.t*.6 or S.t<.8 and 1 or (1-S.t)*.6)
gc_draw(IMG.badgeIcon,S.x,S.y)
end
function FXdraw.attack(S)
gc_setColor(S.r*2,S.g*2,S.b*2,S.a*min(4-S.t*4,1))
gc_setLineWidth(S.wid)
local t1,t2=max(5*S.t-4,0),min(S.t*4,1)
gc_line(
S.x1*(1-t1)+S.x2*t1,
S.y1*(1-t1)+S.y2*t1,
S.x1*(1-t2)+S.x2*t2,
S.y1*(1-t2)+S.y2*t2
)
gc_setLineWidth(S.wid*.6)
t1,t2=max(4*S.t-3,0),min(S.t*5,1)
gc_line(
S.x1*(1-t1)+S.x2*t1,
S.y1*(1-t1)+S.y2*t1,
S.x1*(1-t2)+S.x2*t2,
S.y1*(1-t2)+S.y2*t2
)
end
function FXdraw.tap(S)
local t=S.t
gc_setColor(1,1,1,(1-t)*.4)
gc_circle('fill',S.x,S.y,30*(1-t)^.5)
end
function FXdraw.ripple(S)
local t=S.t
gc_setLineWidth(2)
gc_setColor(1,1,1,1-t)
gc_circle('line',S.x,S.y,t*(2-t)*S.r)
end
function FXdraw.rectRipple(S)
gc_setLineWidth(6)
gc_setColor(1,1,1,1-S.t)
local r=(10*S.t)^1.2
gc_rectangle('line',S.x-r,S.y-r,S.w+2*r,S.h+2*r)
end
function FXdraw.shade(S)
gc_setColor(S.r,S.g,S.b,1-S.t)
gc_rectangle('fill',S.x,S.y,S.w,S.h,2)
end
function FXdraw.cell(S)
gc_setColor(1,1,1,1-S.t)
gc_draw(S.image,S.x,S.y,nil,S.size,nil,S.cx,S.cy)
end
function FXdraw.line(S)
gc_setColor(1,1,1,S.a*(1-S.t))
gc_line(S.x1,S.y1,S.x2,S.y2)
end
local SYSFX={}
function SYSFX.update(dt)
for i=#fx,1,-1 do
if fx[i]:update(dt) then
rem(fx,i)
end
end
end
function SYSFX.draw()
for i=1,#fx do
fx[i]:draw()
end
end
function SYSFX.newBadge(x1,y1,x2,y2)
ins(fx,{
update=FXupdate.badge,
draw=FXdraw.badge,
t=0,
x=x1,y=y1,
x1=x1,y1=y1,
x2=x2,y2=y2,
})
end
function SYSFX.newAttack(rate,x1,y1,x2,y2,wid,r,g,b,a)
ins(fx,{
update=FXupdate.attack,
draw=FXdraw.attack,
t=0,
rate=rate,
x1=x1,y1=y1,-- Start pos
x2=x2,y2=y2,-- End pos
wid=wid,-- Line width
r=r,g=g,b=b,a=a,
})
end
function SYSFX.newTap(rate,x,y)
local T=
{
update=FXupdate.tap,
draw=FXdraw.tap,
t=0,
rate=rate,
x=x,y=y,
}
ins(fx,T)
end
function SYSFX.newRipple(rate,x,y,r)
ins(fx,{
update=FXupdate.ripple,
draw=FXdraw.ripple,
t=0,
rate=rate,
x=x,y=y,r=r,
})
end
function SYSFX.newRectRipple(rate,x,y,w,h)
ins(fx,{
update=FXupdate.rectRipple,
draw=FXdraw.rectRipple,
t=0,
rate=rate,
x=x,y=y,w=w,h=h,
})
end
function SYSFX.newShade(rate,x,y,w,h,r,g,b)
ins(fx,{
update=FXupdate.shade,
draw=FXdraw.shade,
t=0,
rate=rate,
x=x,y=y,w=w,h=h,
r=r or 1,g=g or 1,b=b or 1,
})
end
function SYSFX.newCell(rate,image,size,x,y,vx,vy,ax,ay)
ins(fx,{
update=FXupdate.cell,
draw=FXdraw.cell,
t=0,
rate=rate*(.9+rnd()*.2),
image=image,size=size,
cx=image:getWidth()*.5,cy=image:getHeight()*.5,
x=x,y=y,
vx=vx,vy=vy,
ax=ax,ay=ay,
})
end
function SYSFX.newLine(rate,x1,y1,x2,y2,r,g,b,a)
ins(fx,{
update=FXupdate.line,
draw=FXdraw.line,
t=0,
rate=rate,
x1=x1 or 0,y1=y1 or 0,
x2=x2 or x1 or 1280,y2=y2 or y1 or 720,
r=r or 1,g=g or 1,b=b or 1,a=a or 1,
})
end
return SYSFX

463
Zframework/tableExtend.lua Normal file
View File

@@ -0,0 +1,463 @@
local rnd=math.random
local min,max=math.min,math.max
local find=string.find
local ins,rem=table.insert,table.remove
local next,type=next,type
local TABLE={}
-----------------------[Making Tables]------------------------
-- Get a new filled table
function TABLE.new(val,count)
local L={}
for i=1,count do
L[i]=val
end
return L
end
-- Get a copy of [1~#] elements
function TABLE.shift(org,depth)
if not depth then depth=1e99 end
local L={}
for i=1,#org do
if type(org[i])=='table' and depth>0 then
L[i]=TABLE.shift(org[i],depth-1)
else
L[i]=org[i]
end
end
return L
end
-- Get a full copy of a table, depth=how many layers will be recreate, default to inf
function TABLE.copy(org,depth)
if not depth then depth=1e99 end
local L={}
for k,v in next,org do
if type(v)=='table' and depth>0 then
L[k]=TABLE.copy(v,depth-1)
else
L[k]=v
end
end
return L
end
-- Connect [1~#] elements of new to the end of org
function TABLE.connect(org,new)
local l0=#org
for i=1,#new do
org[l0+i]=new[i]
end
return org
end
-- Get a table of two lists connected
function TABLE.combine(L1,L2)
local l={}
local l0=#L1
for i=1,l0 do l[i]=L1[i] end
for i=1,#L2 do l[l0+i]=L2[i] end
return l
end
----------------------[Modifying Tables]----------------------
-- For all things in new, push to old
function TABLE.cover(new,old)
for k,v in next,new do
old[k]=v
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 org, delete them if it's in sub
function TABLE.subtract(org,sub)
for _,v in next,sub do
while true do
local p=TABLE.search(org,v)
if p then
rem(org,p)
else
break
end
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
if type(v)==type(old[k]) then
if type(v)=='table' then
TABLE.update(v,old[k])
else
old[k]=v
end
end
end
end
-- For all things in new if no val in old, push to old
function TABLE.complete(new,old)
for k,v in next,new do
if type(v)=='table' then
if old[k]==nil then old[k]={} end
TABLE.complete(v,old[k])
elseif old[k]==nil then
old[k]=v
end
end
end
-------------------[Removing Table Values]--------------------
-- Pop & return random [1~#] of table
function TABLE.popRandom(t)
local l=#t
if l>0 then
local r=rnd(l)
r,t[r]=t[r],t[l]
t[l]=nil
return r
end
end
-- Remove [1~#] of table
function TABLE.cut(G)
for i=1,#G do
G[i]=nil
end
end
-- Clear table
function TABLE.clear(G)
for k in next,G do
G[k]=nil
end
end
--------------------[Handling duplicates]---------------------
-- 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
--[[
Run length encoder. Input must be a list containing non-nil value(s).
Example:
- Input: {1, 1, 2, 2, 2, 1}
- Output: {{1, 2}, {2, 3}, {1, 1}}
- This means: "Two 1's in a row", "Three 2's in a row", "One 1 in a row"
]]
function TABLE.RLE(org)
local output={}
local cur=nil
local count=0
for i=1,#org do
local item=org[i]
if item==cur then
count=count+1
else
if cur then
ins(output,{cur,count})
end
cur=item
count=1
end
end
if cur then
ins(output,{cur,count})
end
return output
end
----------------------[Reversing Tables]----------------------
-- 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
----------------------[Table Comparison]----------------------
-- Check if tow list have same elements
function TABLE.compare(a,b)
if #a~=#b then return false end
if a==b then return true end
for i=1,#a do
if a[i]~=b[i] then return false end
end
return true
end
-- Check if tow table have same elements
function TABLE.equal(a,b)
if #a~=#b then return false end
if a==b then return true end
for k,v in next,a do
if b[k]~=v then return false end
end
return true
end
----------------------[Table Operations]----------------------
-- Find value in [1~#], like string.find
function TABLE.find(t,val,start)
for i=start or 1,#t do if t[i]==val then return i end end
end
-- Get subset of table, like string.sub
function TABLE.sub(t,i,j)
local subTable={}
for k=max(i,1),(j and min(j,#t) or #t) do
subTable[k-i+1]=t[k]
end
return subTable
end
-- Replace value in [1~#], like string.gsub
function TABLE.gsub(t,v_old,v_new,count,start)
if not start then start=1 end
if not count then count=1e99 end
while t[start] and count>0 do
if t[start]==v_old then
t[start]=v_new
count=count-1
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
end
-- Replace all value in t
function TABLE.replace(t,v_old,v_new)
for k,v in next,t do
if v==v_old then
t[k]=v_new
end
end
end
-- Re-index string value of a table
function TABLE.reIndex(org)
for k,v in next,org do
if type(v)=='string' then
org[k]=org[v]
end
end
end
-- Return table where keys and values are swapped (useful for hashmap)
function TABLE.kvSwap(t)
local output={}
for k,v in next,t do output[v]=k end
return output
end
--[[
Extracts a value from each sub-table in table.
Example input: ({{name='A'},{name='B'},{name='C'}}, 'name')
Output: {'A','B'}
]]
function TABLE.extract(t,keyName)
local output={}
for k,v in next,t do output[k]=v[keyName] end
return output
end
-- Get element count of table
function TABLE.getSize(t)
local size=0
for _ in next,t do size=size+1 end
return size
end
-----------------------[Table Rotation]-----------------------
-- Copy a rotated matrix table
function TABLE.rotate(cb,dir)
local icb={}
if dir=='R' then-- Rotate CW
for y=1,#cb[1] do
icb[y]={}
for x=1,#cb do
icb[y][x]=cb[x][#cb[1]-y+1]
end
end
elseif dir=='L' then-- Rotate CCW
for y=1,#cb[1] do
icb[y]={}
for x=1,#cb do
icb[y][x]=cb[#cb-x+1][y]
end
end
elseif dir=='F' then-- Rotate 180 degree
for y=1,#cb do
icb[y]={}
for x=1,#cb[1] do
icb[y][x]=cb[#cb-y+1][#cb[1]-x+1]
end
end
end
return icb
end
----------------------[Table Functions]-----------------------
-- Return a function that return a value of table
function TABLE.func_getVal(t,k)
return function() return t[k] end
end
-- Return a function that reverse a value of table
function TABLE.func_revVal(t,k)
return function() t[k]=not t[k] end
end
-- Return a function that set a value of table
function TABLE.func_setVal(t,k)
return function(v) t[k]=v end
end
-------------------------[Table Dump]-------------------------
-- Dump a simple lua table (no whitespaces)
do-- function TABLE.dumpDeflate(L,t)
local function dump(L)
if type(L)~='table' then return end
local s='{'
local count=1
for k,v in next,L do
local T=type(k)
if T=='number' then
if k==count then
k=''
count=count+1
else
k='['..k..']='
end
elseif T=='string' then
if find(k,'[^0-9a-zA-Z_]') then
k='[\''..k..'\']='
else
k=k..'='
end
elseif T=='boolean' then k='['..k..']='
else error("Error key type!")
end
T=type(v)
if T=='number' then v=tostring(v)
elseif T=='string' then v='\''..v..'\''
elseif T=='table' then v=dump(v)
elseif T=='boolean' then v=tostring(v)
else v='*'..tostring(v)
end
s=s..k..v..','
end
return s..'}'
end
TABLE.dumpDeflate=dump
end
-- Dump a simple lua table
do-- function TABLE.dump(L,t)
local tabs=setmetatable({
[0]='',
'\t',
},{__index=function(self,k)
if k>=626 then error("Too many tabs!") end
for i=#self+1,k do
self[i]=self[i-1]..'\t'
end
return self[k]
end})
local function dump(L,t)
local s='{\n'
if not t then
s='return {\n'
t=1
if type(L)~='table' then
return
end
end
local count=1
for k,v in next,L do
local T=type(k)
if T=='number' then
if k==count then
k=''
count=count+1
else
k='['..k..']='
end
elseif T=='string' then
if find(k,'[^0-9a-zA-Z_]') then
k='[\''..k..'\']='
else
k=k..'='
end
elseif T=='boolean' then k='['..k..']='
else k='[\'*'..tostring(k)..'\']='
end
T=type(v)
if T=='number' then v=tostring(v)
elseif T=='string' then v='\''..v..'\''
elseif T=='table' then v=dump(v,t+1)
elseif T=='boolean' then v=tostring(v)
else v='*'..tostring(v)
end
s=s..tabs[t]..k..v..',\n'
end
return s..tabs[t-1]..'}'
end
TABLE.dump=dump
end
return TABLE

83
Zframework/task.lua Normal file
View File

@@ -0,0 +1,83 @@
local rem=table.remove
local assert,resume,status=assert,coroutine.resume,coroutine.status
local rawset=rawset
local timer=love.timer.getTime
local TASK={}
-- Locks
local locks=setmetatable({},{
__index=function(self,k) rawset(self,k,-1e99)return -1e99 end,
__newindex=function(self,k) rawset(self,k,-1e99) end,
})
function TASK.lock(name,T)
if timer()>=locks[name] then
locks[name]=timer()+(T or 1e99)
return true
else
return false
end
end
function TASK.unlock(name)
locks[name]=-1e99
end
function TASK.getLock(name)
local v=locks[name]-timer()
return v>0 and v
end
function TASK.clearLock()
for k in next,locks do
locks[k]=nil
end
end
local tasks={}
function TASK.getCount()
return #tasks
end
local trigFrame=0
function TASK.update(dt)
trigFrame=trigFrame+dt*60
for _=1,trigFrame do
for i=#tasks,1,-1 do
local T=tasks[i]
if status(T.thread)=='dead' then
rem(tasks,i)
else
assert(resume(T.thread,dt/trigFrame))
end
end
end
trigFrame=1
end
function TASK.new(code,...)
local thread=coroutine.create(code)
assert(resume(thread,...))
if status(thread)~='dead' then
tasks[#tasks+1]={
thread=thread,
code=code,
args={...},
}
end
end
function TASK.removeTask_code(code)
for i=#tasks,1,-1 do
if tasks[i].code==code then
rem(tasks,i)
end
end
end
function TASK.removeTask_iterate(func,...)
for i=#tasks,1,-1 do
if func(tasks[i],...) then
rem(tasks,i)
end
end
end
function TASK.clear()
TABLE.cut(tasks)
end
return TASK

18
Zframework/test.lua Normal file
View File

@@ -0,0 +1,18 @@
local yield=coroutine.yield
local TEST={}
-- Wait for the scene swapping animation to finish
function TEST.yieldUntilNextScene()
while SCN.swapping do yield() end
end
function TEST.yieldN(frames)
for _=1,frames do yield() end
end
function TEST.yieldT(timeout)
local t=0
repeat t=t+yield() until t>=timeout
end
return TEST

152
Zframework/text.lua Normal file
View File

@@ -0,0 +1,152 @@
local getColor,setColor=GC.getColor,GC.setColor
local floor,rnd=math.floor,math.random
local ins,rem=table.insert,table.remove
local draw=GC.draw
local texts={}
local textFX={}
function textFX.appear(t)
draw(
t.text,t.x,t.y,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.sudden(t)
setColor(1,1,1,1-t.c)
draw(
t.text,t.x,t.y,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.fly(t)
draw(
t.text,t.x+(t.c-.5)^3*300,t.y,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.stretch(t)
draw(
t.text,t.x,t.y,
nil,
t.c<.3 and (.3-t.c)*1.6+1 or 1,1,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.drive(t)
draw(
t.text,t.x,t.y,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5,
t.c<.3 and (.3-t.c)*2 or 0,0
)
end
function textFX.spin(t)
draw(
t.text,t.x,t.y,
t.c<.3 and (.3-t.c)^2*4 or t.c<.8 and 0 or (t.c-.8)^2*-4,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.flicker(t)
local _,_,_,T=getColor()
setColor(1,1,1,T*(rnd()+.5))
draw(
t.text,t.x,t.y,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.zoomout(t)
draw(
t.text,t.x,t.y,
nil,
t.c^.5*.1+1,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.beat(t)
local k=t.c<.3 and 1.3-t.c^2/.3 or 1
draw(
t.text,t.x,t.y,
nil,
k,k,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
function textFX.score(t)
local _,_,_,T=getColor()
setColor(1,1,1,T*.5)
draw(
t.text,t.x,t.y-0-t.c^.2*50,
nil,
nil,nil,
t.text:getWidth()*.5,t.text:getHeight()*.5
)
end
local TEXT={}
function TEXT.clear()
texts={}
end
function TEXT.show(text,x,y,font,style,spd,stop)
ins(texts,{
c=0, -- Timer
text=GC.newText(FONT.get(floor(font/5)*5 or 40),text), -- String
x=x or 0, -- X
y=y or 0, -- Y
spd=(spd or 1), -- Timing speed(1=last 1 sec)
stop=stop, -- Stop time(sustained text)
draw=assert(textFX[style or 'appear'],"no text type:"..style),-- Draw method
})
end
function TEXT.getText(text,x,y,font,style,spd,stop)-- Another version of TEXT.show(), but only return text object, need manual management
return {
c=0,
text=GC.newText(FONT.get(floor(font/5)*5 or 40),text),
x=x or 0,
y=y or 0,
spd=(spd or 1),
stop=stop,
draw=textFX[style or 'appear'] or error("unavailable type:"..style),
}
end
function TEXT.update(dt,list)
if not list then
list=texts
end
for i=#list,1,-1 do
local t=list[i]
t.c=t.c+t.spd*dt
if t.stop then
if t.c>t.stop then
t.c=t.stop
end
end
if t.c>1 then
rem(list,i)
end
end
end
function TEXT.draw(list)
if not list then
list=texts
end
for i=1,#list do
local t=list[i]
local p=t.c
setColor(1,1,1,p<.2 and p*5 or p<.8 and 1 or 5-p*5)
t:draw()
end
end
return TEXT

26
Zframework/upcaser.txt Normal file
View File

@@ -0,0 +1,26 @@
a=A,b=B,c=C,d=D,e=E,f=F,g=G,h=H,i=I,j=J,k=K,l=L,m=M,n=N,o=O,p=P,q=Q,r=R,s=S,t=T,u=U,v=V,w=W,x=X,y=Y,z=Z
µ=Μ,ß=SS,à=À,á=Á,â=Â,ã=Ã,ä=Ä,å=Å,æ=Æ,ç=Ç,è=È,é=É,ê=Ê,ë=Ë,ì=Ì,í=Í,î=Î,ï=Ï,ð=Ð,ñ=Ñ,ò=Ò,ó=Ó,ô=Ô,õ=Õ,ö=Ö,ø=Ø,ù=Ù,ú=Ú,û=Û,ü=Ü,ý=Ý,þ=Þ,ÿ=Ÿ,ā=Ā,ă=Ă,ą=Ą,ć=Ć,ĉ=Ĉ,ċ=Ċ,č=Č,ď=Ď,đ=Đ,ē=Ē,ĕ=Ĕ,ė=Ė,ę=Ę,ě=Ě,ĝ=Ĝ,ğ=Ğ,ġ=Ġ,ģ=Ģ,ĥ=Ĥ,ħ=Ħ,ĩ=Ĩ,ī=Ī,ĭ=Ĭ,į=Į,ij=IJ,ĵ=Ĵ,ķ=Ķ,ĺ=Ĺ,ļ=Ļ,ľ=Ľ,ŀ=Ŀ,ł=Ł,ń=Ń,ņ=Ņ,ň=Ň,ŋ=Ŋ,ō=Ō,ŏ=Ŏ,ő=Ő,œ=Œ,ŕ=Ŕ,ŗ=Ŗ,ř=Ř,ś=Ś,ŝ=Ŝ,ş=Ş,š=Š,ţ=Ţ,ť=Ť,ŧ=Ŧ,ũ=Ũ,ū=Ū,ŭ=Ŭ,ů=Ů,ű=Ű,ų=Ų,ŵ=Ŵ,ŷ=Ŷ,ź=Ź,ż=Ż,ž=Ž
ƀ=Ƀ,ƃ=Ƃ,ƅ=Ƅ,ƈ=Ƈ,ƌ=Ƌ,ƒ=Ƒ,ƕ=Ƕ,ƙ=Ƙ,ƚ=Ƚ,ƞ=Ƞ,ơ=Ơ,ƣ=Ƣ,ƥ=Ƥ,ƨ=Ƨ,ƭ=Ƭ,ư=Ư,ƴ=Ƴ,ƶ=Ƶ,ƹ=Ƹ,ƽ=Ƽ,ƿ=Ƿ,dž=DŽ,lj=LJ,nj=NJ,ǎ=Ǎ,ǐ=Ǐ,ǒ=Ǒ,ǔ=Ǔ,ǖ=Ǖ,ǘ=Ǘ,ǚ=Ǚ,ǜ=Ǜ,ǝ=Ǝ,ǟ=Ǟ,ǡ=Ǡ,ǣ=Ǣ,ǥ=Ǥ,ǧ=Ǧ,ǩ=Ǩ,ǫ=Ǫ,ǭ=Ǭ,ǯ=Ǯ,dz=DZ,ǵ=Ǵ,ǹ=Ǹ,ǻ=Ǻ,ǽ=Ǽ,ǿ=Ǿ,ȁ=Ȁ,ȃ=Ȃ,ȅ=Ȅ,ȇ=Ȇ,ȉ=Ȉ,ȋ=Ȋ,ȍ=Ȍ,ȏ=Ȏ,ȑ=Ȑ,ȓ=Ȓ,ȕ=Ȕ,ȗ=Ȗ,ș=Ș,ț=Ț,ȝ=Ȝ,ȟ=Ȟ,ȣ=Ȣ,ȥ=Ȥ,ȧ=Ȧ,ȩ=Ȩ,ȫ=Ȫ,ȭ=Ȭ,ȯ=Ȯ,ȱ=Ȱ,ȳ=Ȳ,ȼ=Ȼ,ȿ=Ȿ,ɀ=Ɀ,ɂ=Ɂ,ɇ=Ɇ,ɉ=Ɉ,ɋ=Ɋ,ɍ=Ɍ,ɏ=Ɏ
ɐ=Ɐ,ɑ=Ɑ,ɒ=Ɒ,ɓ=Ɓ,ɔ=Ɔ,ɖ=Ɖ,ɗ=Ɗ,ə=Ə,ɛ=Ɛ,ɜ=,ɠ=Ɠ,ɡ=Ɡ,ɣ=Ɣ,ɥ=Ɥ,ɦ=Ɦ,ɨ=Ɨ,ɩ=Ɩ,ɪ=Ɪ,ɫ=Ɫ,ɬ=Ɬ,ɯ=Ɯ,ɱ=Ɱ,ɲ=Ɲ,ɵ=Ɵ,ɽ=Ɽ,ʀ=Ʀ,ʂ=Ʂ,ʃ=Ʃ,ʇ=Ʇ,ʈ=Ʈ,ʉ=Ʉ,ʊ=Ʊ,ʋ=Ʋ,ʌ=Ʌ,ʒ=Ʒ,ʝ=,ʞ=Ʞ
ͱ=Ͱ,ͳ=Ͳ,ͷ=Ͷ,ͻ=Ͻ,ͼ=Ͼ,ͽ=Ͽ,ά=Ά,έ=Έ,ή=Ή,ί=Ί,α=Α,β=Β,γ=Γ,δ=Δ,ε=Ε,ζ=Ζ,η=Η,θ=Θ,κ=Κ,λ=Λ,ν=Ν,ξ=Ξ,ο=Ο,π=Π,ρ=Ρ,σ=Σ,τ=Τ,υ=Υ,φ=Φ,χ=Χ,ψ=Ψ,ω=Ω,ϊ=Ϊ,ϋ=Ϋ,ό=Ό,ύ=Ύ,ώ=Ώ,ϗ=Ϗ,ϙ=Ϙ,ϛ=Ϛ,ϝ=Ϝ,ϟ=Ϟ,ϡ=Ϡ,ϣ=Ϣ,ϥ=Ϥ,ϧ=Ϧ,ϩ=Ϩ,ϫ=Ϫ,ϭ=Ϭ,ϯ=Ϯ,ϲ=Ϲ,ϳ=Ϳ,ϸ=Ϸ,ϻ=Ϻ
а=А,б=Б,в=В,г=Г,д=Д,е=Е,ж=Ж,з=З,и=И,й=Й,к=К,л=Л,м=М,н=Н,о=О,п=П,р=Р,с=С,т=Т,у=У,ф=Ф,х=Х,ц=Ц,ч=Ч,ш=Ш,щ=Щ,ъ=Ъ,ы=Ы,ь=Ь,э=Э,ю=Ю,я=Я,ѐ=Ѐ,ё=Ё,ђ=Ђ,ѓ=Ѓ,є=Є,ѕ=Ѕ,і=І,ї=Ї,ј=Ј,љ=Љ,њ=Њ,ћ=Ћ,ќ=Ќ,ѝ=Ѝ,ў=Ў,џ=Џ,ѡ=Ѡ,ѣ=Ѣ,ѥ=Ѥ,ѧ=Ѧ,ѩ=Ѩ,ѫ=Ѫ,ѭ=Ѭ,ѯ=Ѯ,ѱ=Ѱ,ѳ=Ѳ,ѵ=Ѵ,ѷ=Ѷ,ѹ=Ѹ,ѻ=Ѻ,ѽ=Ѽ,ѿ=Ѿ,ҁ=Ҁ,ҋ=Ҋ,ҍ=Ҍ,ҏ=Ҏ,ґ=Ґ,ғ=Ғ,ҕ=Ҕ,җ=Җ,ҙ=Ҙ,қ=Қ,ҝ=Ҝ,ҟ=Ҟ,ҡ=Ҡ,ң=Ң,ҥ=Ҥ,ҧ=Ҧ,ҩ=Ҩ,ҫ=Ҫ,ҭ=Ҭ,ү=Ү,ұ=Ұ,ҳ=Ҳ,ҵ=Ҵ,ҷ=Ҷ,ҹ=Ҹ,һ=Һ,ҽ=Ҽ,ҿ=Ҿ,ӂ=Ӂ,ӄ=Ӄ,ӆ=Ӆ,ӈ=Ӈ,ӊ=Ӊ,ӌ=Ӌ,ӎ=Ӎ,ӏ=Ӏ,ӑ=Ӑ,ӓ=Ӓ,ӕ=Ӕ,ӗ=Ӗ,ә=Ә,ӛ=Ӛ,ӝ=Ӝ,ӟ=Ӟ,ӡ=Ӡ,ӣ=Ӣ,ӥ=Ӥ,ӧ=Ӧ,ө=Ө,ӫ=Ӫ,ӭ=Ӭ,ӯ=Ӯ,ӱ=Ӱ,ӳ=Ӳ,ӵ=Ӵ,ӷ=Ӷ,ӹ=Ӹ,ӻ=Ӻ,ӽ=Ӽ,ӿ=Ӿ,ԁ=Ԁ,ԃ=Ԃ,ԅ=Ԅ,ԇ=Ԇ,ԉ=Ԉ,ԋ=Ԋ,ԍ=Ԍ,ԏ=Ԏ,ԑ=Ԑ,ԓ=Ԓ,ԕ=Ԕ,ԗ=Ԗ,ԙ=Ԙ,ԛ=Ԛ,ԝ=Ԝ,ԟ=Ԟ,ԡ=Ԡ,ԣ=Ԣ,ԥ=Ԥ,ԧ=Ԧ,ԩ=Ԩ,ԫ=Ԫ,ԭ=Ԭ,ԯ=Ԯ
ա=Ա,բ=Բ,գ=Գ,դ=Դ,ե=Ե,զ=Զ,է=Է,ը=Ը,թ=Թ,ժ=Ժ,ի=Ի,լ=Լ,խ=Խ,ծ=Ծ,կ=Կ,հ=Հ,ձ=Ձ,ղ=Ղ,ճ=Ճ,մ=Մ,յ=Յ,ն=Ն,շ=Շ,ո=Ո,չ=Չ,պ=Պ,ջ=Ջ,ռ=Ռ,ս=Ս,վ=Վ,տ=Տ,ր=Ր,ց=Ց,ւ=Ւ,փ=Փ,ք=Ք,օ=Օ,ֆ=Ֆ
ა=Ა,ბ=Ბ,გ=Გ,დ=Დ,ე=Ე,ვ=Ვ,ზ=Ზ,თ=Თ,ი=Ი,კ=Კ,ლ=Ლ,მ=Მ,ნ=Ნ,ო=Ო,პ=Პ,ჟ=Ჟ,რ=Რ,ს=Ს,ტ=Ტ,უ=Უ,ფ=Ფ,ქ=Ქ,ღ=Ღ,=Ყ,შ=Შ,ჩ=Ჩ,ც=Ც,ძ=Ძ,წ=Წ,ჭ=Ჭ,ხ=Ხ,ჯ=Ჯ,ჰ=Ჰ,ჱ=Ჱ,ჲ=Ჲ,ჳ=Ჳ,ჴ=Ჴ,ჵ=Ჵ,ჶ=Ჶ,ჷ=Ჷ,ჸ=Ჸ,ჹ=Ჹ,ჺ=Ჺ,ჽ=Ჽ,ჾ=Ჾ,=Ჿ
ᏸ=Ᏸ,ᏹ=Ᏹ,ᏺ=Ᏺ,ᏻ=,ᏼ=,ᏽ=Ᏽ
ᲈ=Ꙋ,ᵹ=Ᵹ,ᵽ=Ᵽ,ᶎ=Ᶎ,ḁ=Ḁ,ḃ=Ḃ,ḅ=Ḅ,ḇ=Ḇ,ḉ=Ḉ,ḋ=Ḋ,ḍ=Ḍ,ḏ=Ḏ,ḑ=Ḑ,ḓ=Ḓ,ḕ=Ḕ,ḗ=Ḗ,ḙ=Ḙ,ḛ=Ḛ,ḝ=Ḝ,ḟ=Ḟ,ḡ=Ḡ,ḣ=Ḣ,ḥ=Ḥ,ḧ=Ḧ,ḩ=Ḩ,ḫ=Ḫ,ḭ=Ḭ,ḯ=Ḯ,ḱ=Ḱ,ḳ=Ḳ,ḵ=Ḵ,ḷ=Ḷ,ḹ=Ḹ,ḻ=Ḻ,ḽ=Ḽ,ḿ=Ḿ,ṁ=Ṁ,ṃ=Ṃ,ṅ=Ṅ,ṇ=Ṇ,ṉ=Ṉ,ṋ=Ṋ,ṍ=Ṍ,ṏ=Ṏ,ṑ=Ṑ,ṓ=Ṓ,ṕ=Ṕ,ṗ=Ṗ,ṙ=Ṙ,ṛ=Ṛ,ṝ=Ṝ,ṟ=Ṟ,ṡ=Ṡ,ṣ=Ṣ,ṥ=Ṥ,ṧ=Ṧ,ṩ=Ṩ,ṫ=Ṫ,ṭ=Ṭ,ṯ=Ṯ,ṱ=Ṱ,ṳ=Ṳ,ṵ=Ṵ,ṷ=Ṷ,ṹ=Ṹ,ṻ=Ṻ,ṽ=Ṽ,ṿ=Ṿ,ẁ=Ẁ,ẃ=Ẃ,ẅ=Ẅ,ẇ=Ẇ,ẉ=Ẉ,ẋ=Ẋ,ẍ=Ẍ,ẏ=Ẏ,ẑ=Ẑ,ẓ=Ẓ,ẕ=Ẕ,ạ=Ạ,ả=Ả,ấ=Ấ,ầ=Ầ,ẩ=Ẩ,ẫ=Ẫ,ậ=Ậ,ắ=Ắ,ằ=Ằ,ẳ=Ẳ,ẵ=Ẵ,ặ=Ặ,ẹ=Ẹ,ẻ=Ẻ,ẽ=Ẽ,ế=Ế,ề=Ề,ể=Ể,ễ=Ễ,ệ=Ệ,ỉ=Ỉ,ị=Ị,ọ=Ọ,ỏ=Ỏ,ố=Ố,ồ=Ồ,ổ=Ổ,ỗ=Ỗ,ộ=Ộ,ớ=Ớ,ờ=Ờ,ở=Ở,ỡ=Ỡ,ợ=Ợ,ụ=Ụ,ủ=Ủ,ứ=Ứ,ừ=Ừ,ử=Ử,ữ=Ữ,ự=Ự,ỳ=Ỳ,ỵ=Ỵ,ỷ=Ỷ,ỹ=Ỹ,ỻ=Ỻ,ỽ=Ỽ,ỿ=Ỿ,ἀ=Ἀ,ἁ=Ἁ,ἂ=Ἂ,ἃ=Ἃ,ἄ=Ἄ,ἅ=Ἅ,ἆ=Ἆ,ἇ=Ἇ,ἐ=Ἐ,ἑ=Ἑ,ἒ=Ἒ,ἓ=Ἓ,ἔ=Ἔ,ἕ=Ἕ,ἠ=Ἠ,ἡ=Ἡ,ἢ=Ἢ,ἣ=Ἣ,ἤ=Ἤ,ἥ=Ἥ,ἦ=Ἦ,ἧ=Ἧ,ἰ=Ἰ,ἱ=Ἱ,ἲ=Ἲ,ἳ=Ἳ,ἴ=Ἴ,ἵ=Ἵ,ἶ=Ἶ,ἷ=Ἷ,ὀ=Ὀ,ὁ=Ὁ,ὂ=Ὂ,ὃ=Ὃ,ὄ=Ὄ,ὅ=Ὅ,ὑ=Ὑ,ὓ=Ὓ,ὕ=Ὕ,ὗ=Ὗ,ὠ=Ὠ,ὡ=Ὡ,ὢ=Ὢ,ὣ=Ὣ,ὤ=Ὤ,ὥ=Ὥ,ὦ=Ὦ,ὧ=Ὧ,ὰ=Ὰ,ά=Ά,ὲ=Ὲ,έ=Έ,ὴ=Ὴ,ή=Ή,ὶ=Ὶ,ί=Ί,ὸ=Ὸ,ό=Ό,ὺ=Ὺ,ύ=Ύ,ὼ=Ὼ,ώ=Ώ,ᾰ=Ᾰ,ᾱ=Ᾱ,ῐ=Ῐ,ῑ=Ῑ,ῠ=Ῠ,ῡ=Ῡ,ῥ=Ῥ,ⅎ=Ⅎ
=,ⅱ=Ⅱ,ⅲ=Ⅲ,ⅳ=Ⅳ,=,ⅵ=Ⅵ,ⅶ=Ⅶ,ⅷ=Ⅷ,ⅸ=Ⅸ,=,ⅺ=Ⅺ,ⅻ=Ⅻ,=,=,=,ⅿ=,ↄ=Ↄ
ⓐ=Ⓐ,ⓑ=Ⓑ,ⓒ=Ⓒ,ⓓ=Ⓓ,ⓔ=Ⓔ,ⓕ=Ⓕ,ⓖ=Ⓖ,ⓗ=Ⓗ,ⓘ=Ⓘ,ⓙ=Ⓙ,ⓚ=Ⓚ,ⓛ=Ⓛ,ⓜ=Ⓜ,ⓝ=Ⓝ,ⓞ=Ⓞ,ⓟ=Ⓟ,ⓠ=Ⓠ,ⓡ=Ⓡ,ⓢ=Ⓢ,ⓣ=Ⓣ,ⓤ=Ⓤ,ⓥ=Ⓥ,ⓦ=Ⓦ,ⓧ=Ⓧ,ⓨ=Ⓨ,ⓩ=Ⓩ
ⰰ=Ⰰ,ⰱ=Ⰱ,ⰲ=Ⰲ,ⰳ=Ⰳ,ⰴ=Ⰴ,ⰵ=Ⰵ,ⰶ=Ⰶ,ⰷ=Ⰷ,ⰸ=Ⰸ,ⰹ=Ⰹ,ⰺ=Ⰺ,ⰻ=Ⰻ,ⰼ=Ⰼ,ⰽ=Ⰽ,ⰾ=Ⰾ,ⰿ=Ⰿ,ⱀ=Ⱀ,ⱁ=Ⱁ,ⱂ=Ⱂ,ⱃ=Ⱃ,ⱄ=Ⱄ,ⱅ=Ⱅ,ⱆ=Ⱆ,ⱇ=Ⱇ,ⱈ=Ⱈ,ⱉ=Ⱉ,ⱊ=Ⱊ,ⱋ=Ⱋ,ⱌ=Ⱌ,ⱍ=Ⱍ,ⱎ=Ⱎ,ⱏ=Ⱏ,ⱐ=Ⱐ,ⱑ=Ⱑ,ⱒ=Ⱒ,ⱓ=Ⱓ,ⱔ=Ⱔ,ⱕ=Ⱕ,ⱖ=Ⱖ,ⱗ=Ⱗ,ⱘ=Ⱘ,ⱙ=Ⱙ,ⱚ=Ⱚ,ⱛ=Ⱛ,ⱜ=Ⱜ,ⱝ=Ⱝ,ⱞ=Ⱞ,ⱟ=Ⱟ
ⱡ=Ⱡ,ⱥ=Ⱥ,ⱦ=Ⱦ,ⱨ=Ⱨ,ⱪ=Ⱪ,ⱬ=Ⱬ,ⱳ=Ⱳ,ⱶ=Ⱶ
ⲁ=Ⲁ,ⲃ=Ⲃ,=Ⲅ,ⲇ=Ⲇ,ⲉ=Ⲉ,ⲋ=Ⲋ,ⲍ=Ⲍ,ⲏ=,ⲑ=Ⲑ,ⲓ=,ⲕ=,ⲗ=Ⲗ,ⲙ=,ⲛ=,ⲝ=Ⲝ,=,ⲡ=Ⲡ,=,=,ⲧ=,ⲩ=,ⲫ=Ⲫ,ⲭ=,ⲯ=Ⲯ,ⲱ=Ⲱ,ⲳ=Ⲳ,ⲵ=Ⲵ,ⲷ=Ⲷ,ⲹ=Ⲹ,ⲻ=,ⲽ=Ⲽ,ⲿ=Ⲿ,ⳁ=Ⳁ,ⳃ=Ⳃ,ⳅ=Ⳅ,ⳇ=,ⳉ=Ⳉ,ⳋ=,ⳍ=,ⳏ=Ⳏ,ⳑ=,ⳓ=,ⳕ=Ⳕ,ⳗ=Ⳗ,ⳙ=Ⳙ,ⳛ=Ⳛ,ⳝ=Ⳝ,ⳟ=Ⳟ,ⳡ=Ⳡ,ⳣ=Ⳣ,ⳬ=Ⳬ,ⳮ=Ⳮ,ⳳ=Ⳳ
ⴀ=Ⴀ,ⴁ=Ⴁ,ⴂ=Ⴂ,ⴃ=Ⴃ,ⴄ=Ⴄ,ⴅ=Ⴅ,ⴆ=Ⴆ,ⴇ=Ⴇ,ⴈ=Ⴈ,ⴉ=Ⴉ,ⴊ=Ⴊ,ⴋ=Ⴋ,ⴌ=Ⴌ,ⴍ=Ⴍ,ⴎ=Ⴎ,ⴏ=Ⴏ,ⴐ=Ⴐ,ⴑ=Ⴑ,ⴒ=Ⴒ,ⴓ=Ⴓ,ⴔ=Ⴔ,ⴕ=Ⴕ,ⴖ=Ⴖ,ⴗ=Ⴗ,ⴘ=Ⴘ,ⴙ=Ⴙ,ⴚ=Ⴚ,ⴛ=Ⴛ,ⴜ=Ⴜ,ⴝ=Ⴝ,ⴞ=Ⴞ,ⴟ=Ⴟ,ⴠ=Ⴠ,ⴡ=Ⴡ,ⴢ=Ⴢ,ⴣ=Ⴣ,ⴤ=Ⴤ,ⴥ=Ⴥ,ⴧ=Ⴧ
ⴭ=Ⴭ,ꙁ=Ꙁ,ꙃ=Ꙃ,ꙅ=,=Ꙇ,ꙉ=Ꙉ,ꙍ=Ꙍ,ꙏ=Ꙏ,ꙑ=Ꙑ,ꙓ=Ꙓ,ꙕ=Ꙕ,ꙗ=Ꙗ,ꙙ=Ꙙ,ꙛ=Ꙛ,ꙝ=Ꙝ,ꙟ=Ꙟ,ꙡ=Ꙡ,ꙣ=Ꙣ,ꙥ=Ꙥ,ꙧ=Ꙧ,ꙩ=Ꙩ,ꙫ=Ꙫ,ꙭ=Ꙭ,ꚁ=Ꚁ,ꚃ=Ꚃ,ꚅ=Ꚅ,ꚇ=Ꚇ,ꚉ=Ꚉ,ꚋ=Ꚋ,ꚍ=Ꚍ,ꚏ=Ꚏ,ꚑ=Ꚑ,ꚓ=Ꚓ,ꚕ=Ꚕ,ꚗ=Ꚗ,ꚙ=Ꚙ,ꚛ=Ꚛ,ꜣ=Ꜣ,ꜥ=Ꜥ,ꜧ=Ꜧ,ꜩ=Ꜩ,ꜫ=Ꜫ,ꜭ=Ꜭ,ꜯ=Ꜯ,ꜳ=Ꜳ,ꜵ=Ꜵ,ꜷ=Ꜷ,ꜹ=Ꜹ,ꜻ=Ꜻ,ꜽ=Ꜽ,ꜿ=Ꜿ,ꝁ=Ꝁ,ꝃ=Ꝃ,ꝅ=Ꝅ,ꝇ=Ꝇ,ꝉ=Ꝉ,ꝋ=Ꝋ,ꝍ=Ꝍ,ꝏ=Ꝏ,ꝑ=Ꝑ,ꝓ=Ꝓ,ꝕ=Ꝕ,ꝗ=Ꝗ,ꝙ=Ꝙ,ꝛ=,ꝝ=Ꝝ,ꝟ=Ꝟ,ꝡ=Ꝡ,ꝣ=Ꝣ,ꝥ=Ꝥ,ꝧ=Ꝧ,ꝩ=Ꝩ,ꝫ=,ꝭ=Ꝭ,ꝯ=,ꝺ=Ꝺ,ꝼ=Ꝼ,ꝿ=Ꝿ,ꞁ=Ꞁ,ꞃ=Ꞃ,ꞅ=Ꞅ,ꞇ=Ꞇ,=Ꞌ,ꞑ=Ꞑ,ꞓ=Ꞓ,ꞔ=Ꞔ,ꞗ=Ꞗ,=,ꞛ=Ꞛ,ꞝ=Ꞝ,=Ꞟ,ꞡ=Ꞡ,ꞣ=Ꞣ,ꞥ=Ꞥ,ꞧ=Ꞧ,ꞩ=Ꞩ,ꞵ=,ꞷ=Ꞷ,ꞹ=Ꞹ,ꞻ=Ꞻ,ꞽ=Ꞽ,ꞿ=Ꞿ,ꟁ=Ꟁ,ꟃ=Ꟃ,ꟈ=Ꟈ,ꟊ=Ꟊ,ꟑ=Ꟑ,ꟗ=Ꟗ,ꟙ=Ꟙ,ꟶ=Ꟶ,ꭓ=
ꭰ=,ꭱ=,ꭲ=,ꭳ=Ꭳ,ꭴ=Ꭴ,=,ꭶ=Ꭶ,ꭷ=Ꭷ,ꭸ=Ꭸ,ꭹ=,ꭺ=,ꭻ=,ꭼ=,ꭽ=Ꭽ,ꭾ=,ꭿ=Ꭿ,ꮀ=Ꮀ,=Ꮁ,ꮂ=Ꮂ,=,ꮄ=Ꮄ,ꮅ=Ꮅ,ꮆ=Ꮆ,ꮇ=,ꮈ=Ꮈ,ꮉ=Ꮉ,ꮊ=Ꮊ,ꮋ=,ꮌ=Ꮌ,ꮍ=,ꮎ=Ꮎ,ꮏ=Ꮏ,ꮐ=,ꮑ=Ꮑ,ꮒ=,=,ꮔ=Ꮔ,ꮕ=Ꮕ,ꮖ=Ꮖ,ꮗ=Ꮗ,ꮘ=Ꮘ,ꮙ=Ꮙ,ꮚ=Ꮚ,ꮛ=Ꮛ,ꮜ=Ꮜ,ꮝ=Ꮝ,ꮞ=,ꮟ=,ꮠ=Ꮠ,ꮡ=Ꮡ,ꮢ=,ꮣ=Ꮣ,ꮤ=,ꮥ=,ꮦ=Ꮦ,ꮧ=Ꮧ,ꮨ=Ꮨ,=,=,ꮫ=Ꮫ,ꮬ=Ꮬ,ꮭ=Ꮭ,ꮮ=,=,ꮰ=Ꮰ,ꮱ=Ꮱ,ꮲ=,ꮳ=Ꮳ,ꮴ=Ꮴ,ꮵ=Ꮵ,ꮶ=,ꮷ=,ꮸ=Ꮸ,ꮹ=Ꮹ,ꮺ=Ꮺ,ꮻ=Ꮻ,ꮼ=Ꮼ,ꮽ=Ꮽ,ꮾ=,ꮿ=Ꮿ
=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=,=
𐐨=𐐀,𐐩=𐐁,𐐪=𐐂,𐐫=𐐃,𐐬=𐐄,𐐭=𐐅,𐐮=𐐆,𐐯=𐐇,𐐰=𐐈,𐐱=𐐉,𐐲=𐐊,𐐳=𐐋,𐐴=𐐌,𐐵=𐐍,𐐶=𐐎,𐐷=𐐏,𐐸=𐐐,𐐹=𐐑,𐐺=𐐒,𐐻=𐐓,𐐼=𐐔,𐐽=𐐕,𐐾=𐐖,𐐿=𐐗,𐑀=𐐘,𐑁=𐐙,𐑂=𐐚,𐑃=𐐛,𐑄=𐐜,𐑅=𐐝,𐑆=𐐞,𐑇=𐐟,𐑈=𐐠,𐑉=𐐡,𐑊=𐐢,𐑋=𐐣,𐑌=𐐤,𐑍=𐐥,𐑎=𐐦,𐑏=𐐧
𐓘=𐒰,𐓙=𐒱,𐓚=𐒲,𐓛=𐒳,𐓜=𐒴,𐓝=𐒵,𐓞=𐒶,𐓟=𐒷,𐓠=𐒸,𐓡=𐒹,𐓢=𐒺,𐓣=𐒻,𐓤=𐒼,𐓥=𐒽,𐓦=𐒾,𐓧=𐒿,𐓨=𐓀,𐓩=𐓁,𐓪=𐓂,𐓫=𐓃,𐓬=𐓄,𐓭=𐓅,𐓮=𐓆,𐓯=𐓇,𐓰=𐓈,𐓱=𐓉,𐓲=𐓊,𐓳=𐓋,𐓴=𐓌,𐓵=𐓍,𐓶=𐓎,𐓷=𐓏,𐓸=𐓐,𐓹=𐓑,𐓺=𐓒,𐓻=𐓓
𐳀=𐲀,𐳁=𐲁,𐳂=𐲂,𐳃=𐲃,𐳄=𐲄,𐳅=𐲅,𐳆=𐲆,𐳇=𐲇,𐳈=𐲈,𐳉=𐲉,𐳊=𐲊,𐳋=𐲋,𐳌=𐲌,𐳍=𐲍,𐳎=𐲎,𐳏=𐲏,𐳐=𐲐,𐳑=𐲑,𐳒=𐲒,𐳓=𐲓,𐳔=𐲔,𐳕=𐲕,𐳖=𐲖,𐳗=𐲗,𐳘=𐲘,𐳙=𐲙,𐳚=𐲚,𐳛=𐲛,𐳜=𐲜,𐳝=𐲝,𐳞=𐲞,𐳟=𐲟,𐳠=𐲠,𐳡=𐲡,𐳢=𐲢,𐳣=𐲣,𐳤=𐲤,𐳥=𐲥,𐳦=𐲦,𐳧=𐲧,𐳨=𐲨,𐳩=𐲩,𐳪=𐲪,𐳫=𐲫,𐳬=𐲬,𐳭=𐲭,𐳮=𐲮,𐳯=𐲯,𐳰=𐲰,𐳱=𐲱,𐳲=𐲲
𑣀=𑢠,𑣁=𑢡,𑣂=𑢢,𑣃=𑢣,𑣄=𑢤,𑣅=𑢥,𑣆=𑢦,𑣇=𑢧,𑣈=𑢨,𑣉=𑢩,𑣊=𑢪,𑣋=𑢫,𑣌=𑢬,𑣍=𑢭,𑣎=𑢮,𑣏=𑢯,𑣐=𑢰,𑣑=𑢱,𑣒=𑢲,𑣓=𑢳,𑣔=𑢴,𑣕=𑢵,𑣖=𑢶,𑣗=𑢷,𑣘=𑢸,𑣙=𑢹,𑣚=𑢺,𑣛=𑢻,𑣜=𑢼,𑣝=𑢽,𑣞=𑢾,𑣟=𑢿
𖹠=𖹀,𖹡=𖹁,𖹢=𖹂,𖹣=𖹃,𖹤=𖹄,𖹥=𖹅,𖹦=𖹆,𖹧=𖹇,𖹨=𖹈,𖹩=𖹉,𖹪=𖹊,𖹫=𖹋,𖹬=𖹌,𖹭=𖹍,𖹮=𖹎,𖹯=𖹏,𖹰=𖹐,𖹱=𖹑,𖹲=𖹒,𖹳=𖹓,𖹴=𖹔,𖹵=𖹕,𖹶=𖹖,𖹷=𖹗,𖹸=𖹘,𖹹=𖹙,𖹺=𖹚,𖹻=𖹛,𖹼=𖹜,𖹽=𖹝,𖹾=𖹞,𖹿=𖹟
𞤢=𞤀,𞤣=𞤁,𞤤=𞤂,𞤥=𞤃,𞤦=𞤄,𞤧=𞤅,𞤨=𞤆,𞤩=𞤇,𞤪=𞤈,𞤫=𞤉,𞤬=𞤊,𞤭=𞤋,𞤮=𞤌,𞤯=𞤍,𞤰=𞤎,𞤱=𞤏,𞤲=𞤐,𞤳=𞤑,𞤴=𞤒,𞤵=𞤓,𞤶=𞤔,𞤷=𞤕,𞤸=𞤖,𞤹=𞤗,𞤺=𞤘,𞤻=𞤙,𞤼=𞤚,𞤽=𞤛,𞤾=𞤜,𞤿=𞤝,𞥀=𞤞,𞥁=𞤟,𞥂=𞤠,𞥃=𞤡
i̇=İ,ʼn=ʼN,ǰ=J̌,ΐ=Ϊ́,ΰ=Ϋ́,և=ԵՒ,ẖ=H̱,ẗ=T̈,ẘ=W̊,ẙ=Y̊,ẚ=Aʾ,ὐ=Υ̓,ὒ=Υ̓̀,ὔ=Υ̓́,ὖ=Υ̓͂,ᾀ=ἈΙ,ᾁ=ἉΙ,ᾂ=ἊΙ,ᾃ=ἋΙ,ᾄ=ἌΙ,ᾅ=ἍΙ,ᾆ=ἎΙ,ᾇ=ἏΙ,ᾐ=ἨΙ,ᾑ=ἩΙ,ᾒ=ἪΙ,ᾓ=ἫΙ,ᾔ=ἬΙ,ᾕ=ἭΙ,ᾖ=ἮΙ,ᾗ=ἯΙ,ᾠ=ὨΙ,ᾡ=ὩΙ,ᾢ=ὪΙ,ᾣ=ὫΙ,ᾤ=ὬΙ,ᾥ=ὭΙ,ᾦ=ὮΙ,ᾧ=ὯΙ,ᾲ=ᾺΙ,ᾳ=ΑΙ,ᾴ=ΆΙ,ᾶ=Α͂,ᾷ=Α͂Ι,ῂ=ῊΙ,ῃ=ΗΙ,ῄ=ΉΙ,ῆ=Η͂,ῇ=Η͂Ι,ῒ=Ϊ̀,ῖ=Ι͂,ῗ=Ϊ͂,ῢ=Ϋ̀,ῤ=Ρ̓,ῦ=Υ͂,ῧ=Ϋ͂,ῲ=ῺΙ,ῳ=ΩΙ,ῴ=ΏΙ,ῶ=Ω͂,ῷ=Ω͂Ι,ﬓ=ՄՆ,ﬔ=ՄԵ,ﬕ=ՄԻ,ﬖ=ՎՆ,ﬗ=ՄԽ

12
Zframework/vibrate.lua Normal file
View File

@@ -0,0 +1,12 @@
local level={0,0,.01,.016,.023,.03,.04,.05,.06,.07,.08,.09,.12,.15}
local vib=love.system.vibrate
return SYSTEM=='iOS' and
function(t)
t=level[t]
if t then vib(t<=.03 and 1 or t<=.09 and 2 or 3) end
end
or
function(t)
t=level[t]
if t then vib(t) end
end

144
Zframework/voice.lua Normal file
View File

@@ -0,0 +1,144 @@
local rnd=math.random
local volume=1
local diversion=0
local voiceQueue={free=0}
local VOC={
getCount=function() return 0 end,
getQueueCount=function() return 0 end,
load=function() error("Cannot load before init!") end,
getFreeChannel=NULL,
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
for i=1,#voiceQueue do
local Q=voiceQueue[i]
for j=1,#Q do
local s=Q[j]
if type(s)=='userdata' then
s:setVolume(volume)
end
end
end
end
function VOC.init(list)
VOC.init=nil
local rem=table.remove
local bank={}-- {vocName1={SRC1s},vocName2={SRC2s},...}
local Source={}
local count=#list function VOC.getCount() return count end
local function _loadVoiceFile(path,N,vocName)
local fullPath=path..vocName..'.ogg'
if love.filesystem.getInfo(fullPath) then
bank[vocName]={love.audio.newSource(fullPath,'stream')}
table.insert(Source[N],vocName)
return true
end
end
-- Load voice with string
local function _getVoice(str)
local L=bank[str]
local n=1
while L[n]:isPlaying() do
n=n+1
if not L[n] then
L[n]=L[1]:clone()
L[n]:seek(0)
break
end
end
return L[n]
end
function VOC.load(path)
for i=1,count do
Source[list[i]]={}
local n=0
repeat n=n+1 until not _loadVoiceFile(path,list[i],list[i]..'_'..n)
if n==1 then
if not _loadVoiceFile(path,list[i],list[i]) then
LOG("No VOC: "..list[i],.1)
end
end
if not Source[list[i]][1] then
Source[list[i]]=nil
end
end
function VOC.getQueueCount()
return #voiceQueue
end
function VOC.getQueueLength(chn)
return voiceQueue[chn] and #voiceQueue[chn]
end
function VOC.getFreeChannel()
local l=#voiceQueue
for i=1,l do
if #voiceQueue[i]==0 then return i end
end
voiceQueue[l+1]={s=0}
return l+1
end
function VOC.play(s,chn)
if volume>0 then
local _=Source[s]
if not _ then return end
if chn then
local L=voiceQueue[chn]
L[#L+1]=_[rnd(#_)]
L.s=1
-- Add to queue[chn]
else
voiceQueue[VOC.getFreeChannel()]={s=1,_[rnd(#_)]}
-- Create new channel & play
end
end
end
function VOC.update()
for i=#voiceQueue,1,-1 do
local Q=voiceQueue[i]
if Q.s==0 then-- Free channel, auto delete when >3
if i>3 then
rem(voiceQueue,i)
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]: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]:play()
Q.s=3
end
elseif Q.s==3 then-- Playing 12 same time
if not Q[1]:isPlaying() then
for j=1,#Q do
Q[j]=Q[j+1]
end
Q.s=Q[2] and 2 or 4
end
elseif Q.s==4 then-- Playing last
if not Q[1].isPlaying(Q[1]) then
Q[1]=nil
Q.s=0
end
end
end
end
end
end
return VOC

128
Zframework/wait.lua Normal file
View File

@@ -0,0 +1,128 @@
local WAIT={
state=false,
timer=false,
totalTimer=false,
enterTime=.2,
leaveTime=.2,
timeout=6,
coverColor={.1,.1,.1},
coverAlpha=.6,
arg=false,
}
local arcAlpha={1,.6,.4,.3}
local function defaultDraw(a,t)
GC.setLineWidth(SCR.h/26)
t=t*2.6
for i=1,4 do
GC.setColor(1,1,1,a*arcAlpha[i])
GC.arc('line','open',SCR.w/2,SCR.h/2,SCR.h/5,t+MATH.tau*(i/4),t+MATH.tau*((i+1)/4))
end
end
function WAIT.new(arg)
if WAIT.state then return end
assert(type(arg)=='table',"arg must be table")
assert(arg.init==nil or type(arg.init) =='function',"Field 'enter' must be function")
assert(arg.update==nil or type(arg.update) =='function',"Field 'update' must be function")
assert(arg.quit==nil or type(arg.quit) =='function',"Field 'leave' must be function")
assert(arg.draw==nil or type(arg.draw) =='function',"Field 'draw' must be function")
assert(arg.escapable==nil or type(arg.escapable) =='boolean', "Field 'escapable' must be boolean")
if arg.init then arg.init() end
WAIT.arg=arg
WAIT.state='enter'
WAIT.timer=0
WAIT.totalTimer=0
end
function WAIT.interrupt()
if WAIT.state and WAIT.state~='leave' then
WAIT.state='leave'
WAIT.timer=WAIT.leaveTime*WAIT.timer/WAIT.enterTime
end
end
function WAIT.update(dt)
if WAIT.state then
WAIT.totalTimer=WAIT.totalTimer+dt
if WAIT.arg.update then WAIT.arg.update(dt) end
if WAIT.state~='leave' and WAIT.totalTimer>=(WAIT.arg.timeout or WAIT.timeout) then
WAIT.interrupt()
end
if WAIT.state=='enter' then
WAIT.timer=math.min(WAIT.timer+dt,WAIT.enterTime)
if WAIT.timer>=WAIT.enterTime then WAIT.state='wait' end
elseif WAIT.state=='leave' then
WAIT.timer=WAIT.timer-dt
if WAIT.timer<=0 then
WAIT.state=false
if WAIT.arg.quit then WAIT.arg.quit() end
end
end
end
end
function WAIT.draw()
if WAIT.state then
local alpha=(
WAIT.state=='enter' and WAIT.timer/WAIT.enterTime or
WAIT.state=='wait' and 1 or
WAIT.state=='leave' and WAIT.timer/WAIT.leaveTime
)
GC.setColor(
WAIT.coverColor[1],
WAIT.coverColor[2],
WAIT.coverColor[3],
alpha*WAIT.coverAlpha
)
GC.rectangle('fill',0,0,SCR.w,SCR.h);
(WAIT.arg.draw or defaultDraw)(alpha,WAIT.totalTimer)
end
end
function WAIT.setEnterTime(t)
assert(type(t)=='number' and t>0,"Arg #1 must be number larger then 0")
WAIT.enterTime=t
end
function WAIT.setLeaveTime(t)
assert(type(t)=='number' and t>0,"Arg #1 must be number larger then 0")
WAIT.leaveTime=t
end
function WAIT.setTimeout(t)
assert(type(t)=='number' and t>0,"Arg #1 must be number larger then 0")
WAIT.timeout=t
end
function WAIT.setCoverColor(r,g,b)
if type(r)=='table' then
r,g,b=r[1],r[2],r[3]
end
if
type(r)=='number' and r>=0 and r<=1 and
type(g)=='number' and g>=0 and g<=1 and
type(b)=='number' and b>=0 and b<=1
then
WAIT.coverColor[1],WAIT.coverColor[2],WAIT.coverColor[3]=r,g,b
else
error("Arg must be r,g,b or {r,g,b}")
end
end
function WAIT.setCoverAlpha(a)
assert(type(a)=='number',"Arg #1 must be number between 0~1")
end
function WAIT.setDefaultDraw(f)
assert(type(f)=='function',"Arg #1 must be function")
defaultDraw=f
end
setmetatable(WAIT,{__call=function(self,arg)
self.new(arg)
end,__metatable=true})
return WAIT

194
Zframework/websocket.lua Normal file
View File

@@ -0,0 +1,194 @@
local host='127.0.0.1'
local port='80'
local path=''
-- lua + LÖVE threading websocket client
-- Original pure lua ver. by flaribbit and Particle_G
-- Threading version by MrZ
local type=type
local timer=love.timer.getTime
local TRD=love.thread.newThread("\n")
local TRD_isRunning=TRD.isRunning
local WS={}
local wsList=setmetatable({},{
__index=function(l,k)
local ws={
real=false,
status='dead',
lastPongTime=timer(),
sendTimer=0,
alertTimer=0,
pongTimer=0,
}
l[k]=ws
return ws
end
})
function WS.switchHost(_1,_2,_3)
for k in next,wsList do
WS.close(k)
end
host=_1
port=_2 or port
path=_3 or path
end
function WS.connect(name,subPath,head,timeout)
if head then
local l=""
for k,v in next,head do
l=l..(k..": "..v..'\r\n')
end
head=l
else
head=""
end
if wsList[name] and wsList[name].thread then
wsList[name].thread:release()
end
local ws={
real=true,
thread=love.thread.newThread('Zframework/websocket_thread.lua'),
triggerCHN=love.thread.newChannel(),
sendCHN=love.thread.newChannel(),
readCHN=love.thread.newChannel(),
lastPingTime=0,
lastPongTime=timer(),
pingInterval=6,
status='connecting',-- 'connecting', 'running', 'dead'
sendTimer=0,
alertTimer=0,
pongTimer=0,
}
wsList[name]=ws
ws.thread:start(ws.triggerCHN,ws.sendCHN,ws.readCHN)
ws.sendCHN:push(host)
ws.sendCHN:push(port)
ws.sendCHN:push(path..subPath)
ws.sendCHN:push(head)
ws.sendCHN:push(timeout or 2.6)
end
function WS.status(name)
local ws=wsList[name]
return ws.status or 'dead'
end
function WS.getTimers(name)
local ws=wsList[name]
return ws.pongTimer,ws.sendTimer,ws.alertTimer
end
function WS.setPingInterval(name,time)
local ws=wsList[name]
ws.pingInterval=math.max(time or 2.6,2.6)
end
function WS.alert(name)
local ws=wsList[name]
ws.alertTimer=2.6
end
local OPcode={
continue=0,
text=1,
binary=2,
close=8,
ping=9,
pong=10,
}
local OPname={
[0]='continue',
[1]='text',
[2]='binary',
[8]='close',
[9]='ping',
[10]='pong',
}
function WS.send(name,message,op)
if type(message)=='string' then
local ws=wsList[name]
if ws.real and ws.status=='running' then
ws.sendCHN:push(op and OPcode[op] or 2)-- 2=binary
ws.sendCHN:push(message)
ws.lastPingTime=timer()
ws.sendTimer=1
end
else
MES.new('error',"Attempt to send non-string value!")
MES.traceback()
end
end
function WS.read(name)
local ws=wsList[name]
if ws.real and ws.status~='connecting' and ws.readCHN:getCount()>=2 then
local op,message=ws.readCHN:pop(),ws.readCHN:pop()
if op==8 then-- 8=close
ws.status='dead'
elseif op==9 then-- 9=ping
WS.send(name,message or "",'pong')
end
ws.lastPongTime=timer()
ws.pongTimer=1
return message,OPname[op] or op
end
end
function WS.close(name)
local ws=wsList[name]
if ws.real then
ws.sendCHN:push(8)-- 8=close
ws.sendCHN:push("")
ws.status='dead'
end
end
function WS.update(dt)
local time=timer()
for name,ws in next,wsList do
if ws.real and ws.status~='dead' then
if TRD_isRunning(ws.thread) then
if ws.triggerCHN:getCount()==0 then
ws.triggerCHN:push(0)
end
if ws.status=='connecting' then
local mes=ws.readCHN:pop()
if mes then
if mes=='success' then
ws.status='running'
ws.lastPingTime=time
ws.lastPongTime=time
ws.pongTimer=1
else
ws.status='dead'
MES.new('warn',text.wsFailed:repD(mes))
end
end
elseif ws.status=='running' then
if time-ws.lastPingTime>ws.pingInterval then
WS.send(name,"",'pong')
end
if time-ws.lastPongTime>6+2*ws.pingInterval then
WS.close(name)
end
end
if ws.sendTimer>0 then ws.sendTimer=ws.sendTimer-dt end
if ws.pongTimer>0 then ws.pongTimer=ws.pongTimer-dt end
if ws.alertTimer>0 then ws.alertTimer=ws.alertTimer-dt end
else
ws.status='dead'
local err=ws.thread:getError()
if err then
MES.new('warn',text.wsClose:repD(err:match(":.-:(.-)\n")))
WS.alert(name)
end
end
end
end
end
return WS

View File

@@ -0,0 +1,189 @@
local triggerCHN,sendCHN,readCHN=...
local CHN_demand,CHN_getCount=triggerCHN.demand,triggerCHN.getCount
local CHN_push,CHN_pop=triggerCHN.push,triggerCHN.pop
local SOCK=require'socket'.tcp()
local JSON=require'Zframework.json'
do-- Connect
local host=CHN_demand(sendCHN)
local port=CHN_demand(sendCHN)
local path=CHN_demand(sendCHN)
local head=CHN_demand(sendCHN)
local timeout=CHN_demand(sendCHN)
SOCK:settimeout(timeout)
local res,err=SOCK:connect(host,port)
assert(res,err)
-- WebSocket handshake
SOCK:send(
'GET '..path..' HTTP/1.1\r\n'..
'Host: '..host..':'..port..'\r\n'..
'Connection: Upgrade\r\n'..
'Upgrade: websocket\r\n'..
'Sec-WebSocket-Version: 13\r\n'..
'Sec-WebSocket-Key: osT3F7mvlojIvf3/8uIsJQ==\r\n'..-- secKey
head..
'\r\n'
)
-- First line of HTTP
res,err=SOCK:receive('*l')
assert(res,err)
local code,ctLen
code=res:find(' ')
code=res:sub(code+1,code+3)
-- Get body length from headers and remove headers
repeat
res,err=SOCK:receive('*l')
assert(res,err)
if not ctLen and res:find('length') then
ctLen=tonumber(res:match('%d+'))
end
until res==''
-- Result
if ctLen then
if code=='101' then
CHN_push(readCHN,'success')
else
res,err=SOCK:receive(ctLen)
res=JSON.decode(assert(res,err))
error((code or "XXX")..":"..(res and res.reason or "Server Error"))
end
end
SOCK:settimeout(0)
end
local yield=coroutine.yield
local byte,char=string.byte,string.char
local band,bor,bxor=bit.band,bit.bor,bit.bxor
local shl,shr=bit.lshift,bit.rshift
local mask_key={1,14,5,14}
local mask_str=char(unpack(mask_key))
local function _send(op,message)
-- Message type
SOCK:send(char(bor(op,0x80)))
if message then
-- Length
local length=#message
if length>65535 then
SOCK:send(char(bor(127,0x80),0,0,0,0,band(shr(length,24),0xff),band(shr(length,16),0xff),band(shr(length,8),0xff),band(length,0xff)))
elseif length>125 then
SOCK:send(char(bor(126,0x80),band(shr(length,8),0xff),band(length,0xff)))
else
SOCK:send(char(bor(length,0x80)))
end
local msgbyte={byte(message,1,length)}
for i=1,length do
msgbyte[i]=bxor(msgbyte[i],mask_key[(i-1)%4+1])
end
return SOCK:send(mask_str..char(unpack(msgbyte)))
else
SOCK:send('\128'..mask_str)
return 0
end
end
local sendThread=coroutine.wrap(function()
while true do
while CHN_getCount(sendCHN)>=2 do
_send(CHN_pop(sendCHN),CHN_pop(sendCHN))
end
yield()
end
end)
local function _receive(sock,len)
local buffer=""
while true do
local r,e,p=sock:receive(len)
if r then
buffer=buffer..r
len=len-#r
elseif p then
buffer=buffer..p
len=len-#p
elseif e then
return nil,e
end
if len==0 then
return buffer
end
yield()
end
end
local readThread=coroutine.wrap(function()
local res,err
local op,fin
local lBuffer=""-- Long multi-pack buffer
while true do
-- Byte 0-1
res,err=_receive(SOCK,2)
assert(res,err)
op=band(byte(res,1),0x0f)
fin=band(byte(res,1),0x80)==0x80
-- Calculating data length
local length=band(byte(res,2),0x7f)
if length==126 then
res,err=_receive(SOCK,2)
assert(res,err)
length=shl(byte(res,1),8)+byte(res,2)
elseif length==127 then
local lenData
lenData,err=_receive(SOCK,8)
assert(res,err)
local _,_,_,_,_5,_6,_7,_8=byte(lenData,1,8)
length=shl(_5,24)+shl(_6,16)+shl(_7,8)+_8
end
res,err=_receive(SOCK,length)
assert(res,err)
-- React
if op==8 then-- 8=close
CHN_push(readCHN,8)-- close
if type(res)=='string' then
CHN_push(readCHN,res:sub(3))--[Warning] 2 bytes close code at start so :sub(3)
else
CHN_push(readCHN,"WS closed")
end
return
elseif op==0 then-- 0=continue
lBuffer=lBuffer..res
if fin then
CHN_push(readCHN,lBuffer)
lBuffer=""
end
else
CHN_push(readCHN,op)
if fin then
CHN_push(readCHN,res)
lBuffer=""
else
lBuffer=res
end
end
yield()
end
end)
local success,err
while true do-- Running
CHN_demand(triggerCHN)
success,err=pcall(sendThread)
if not success or err then break end
success,err=pcall(readThread)
if not success or err then break end
end
SOCK:close()
CHN_push(readCHN,8)-- close
CHN_push(readCHN,err or "Disconnected")
error()

View File

@@ -0,0 +1,19 @@
local love=love
local max,min=math.max,math.min
local trigDist=0
return function(y,key1,key2)
if y>0 then
trigDist=max(trigDist,0)+y^1.2
elseif y<0 then
if trigDist>0 then trigDist=0 end
trigDist=min(trigDist,0)-(-y)^1.2
end
while trigDist>=1 do
love.keypressed(key1 or 'up')
trigDist=trigDist-1
end
while trigDist<=-1 do
love.keypressed(key2 or 'down')
trigDist=trigDist+1
end
end

1525
Zframework/widget.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
SYSTEM=love._os
if SYSTEM=='OS X' then SYSTEM='macOS' end
MOBILE=SYSTEM=='Android' or SYSTEM=='iOS'
FNNS=SYSTEM:find'\79\83' -- What does FNSF stand for? IDK so don't ask me lol
local OS=love._os
if OS=='OS X' then OS='macOS' end
MOBILE=OS=='Android' or OS=='iOS'
FNNS=OS:find'\79\83' -- What does FNSF stand for? IDK so don't ask me lol
if SYSTEM=='Web' then
if OS=='Web' then
local oldRead=love.filesystem.read
function love.filesystem.read(name,size)
if love.filesystem.getInfo(name) then

View File

@@ -50,14 +50,11 @@ BGM.setMaxSources(5)
VOC.setDiversion(.62)
WIDGET.setOnChange(function()
if SCN.cur~='net_game' and SCN.cur~='custom_field' then
local colorList=THEME.getThemeColor()
if colorList then
for _,W in next,SCN.scenes[SCN.cur].widgetList do
if W.color then
W.color=colorList[math.random(#colorList)]
end
end
if SCN.cur=='net_game' or SCN.cur=='custom_field' then return end
local colorList=THEME.getThemeColor()
if colorList then
for _,W in next,SCN.scenes[SCN.cur].widgetList do
W.color=W.color and colorList[math.random(#colorList)]
end
end
end)
@@ -191,7 +188,7 @@ Z.setOnFnKeys({
Z.setOnGlobalKey('f11',function()
if not MOBILE then
SETTING.fullscreen=not SETTING.fullscreen
applySettings()
applySettings('fullscreen')
saveSettings()
end
end)
@@ -376,30 +373,6 @@ then
MES.new('error',"An error occured during loading, and some data was lost.")
end
-- Initialize fields, sequence, missions, gameEnv for cutsom game
local fieldData=loadFile('conf/customBoards','-string -canSkip')
if fieldData then
fieldData=STRING.split(fieldData,"!")
for i=1,#fieldData do
DATA.pasteBoard(fieldData[i],i)
end
else
FIELD[1]=DATA.newBoard()
end
local sequenceData=loadFile('conf/customSequence','-string -canSkip')
if sequenceData then
DATA.pasteSequence(sequenceData)
end
local missionData=loadFile('conf/customMissions','-string -canSkip')
if missionData then
DATA.pasteMission(missionData)
end
local customData=loadFile('conf/customEnv','-canSkip')
if customData and customData['version']==VERSION.code then
TABLE.complete(customData,CUSTOMENV)
end
TABLE.complete(require"parts.customEnv0",CUSTOMENV)
-- Update data
do
if type(STAT.version)~='number' then
@@ -426,6 +399,7 @@ do
end
if RANKS.bigbang then fs.remove('record/bigbang.rec') end
if RANKS.clearRush then fs.remove('record/clearRush.rec') end
if STAT.version<1715 then fs.remove('record/dig_quad_10l.rec') end
if STAT.version~=VERSION.code then
for k,v in next,MODE_UPDATE_MAP do
@@ -463,6 +437,7 @@ do
if not RANKS.sprint_10l then RANKS.sprint_10l=0 end
if RANKS.master_l then RANKS.master_n,RANKS.master_l=RANKS.master_l end
if RANKS.master_u then RANKS.master_h,RANKS.master_u=RANKS.master_u end
if RANKS.secret_grade then RANKS.construct_sg,RANKS.secret_grade=RANKS.secret_grade end
for _,v in next,VK_ORG do v.color=nil end
for name,rank in next,RANKS do
if type(name)=='number' or type(rank)~='number' then

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1,2 @@
If the modeicon you're looking for isn't here, try looking in /parts/scenes/load.lua.
We vectorized some of them for better scalability.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

View File

@@ -170,7 +170,7 @@ do
},-- T
function(P,d)
if P.gameEnv.easyFresh then
P:freshBlock('fresh')
P:freshBlockDelay()
end
if P.gameEnv.ospin then
local x,y=P.curX,P.curY
@@ -203,7 +203,7 @@ do
C.dir=dir
P.spinLast=2
P.stat.rotate=P.stat.rotate+1
P:freshBlock('move')
P:freshMoveBlock()
C.spinSeq=nil
return
end
@@ -328,23 +328,20 @@ do
[13]={'+0+0','+0+1','-1+0'},
[31]={'+0+0','+0-1','+1+0'},
},-- W
function(P,d)
if P.type=='human' then
SFX.play('rotate',nil,P:getCenterX()*.15)
end
local kickData=XspinList[d]
for test=1,#kickData do
local x,y=P.curX+kickData[test][1],P.curY+kickData[test][2]
if not P:ifoverlap(P.cur.bk,x,y) then
P.curX,P.curY=x,y
P.spinLast=1
P:freshBlock('move')
P.stat.rotate=P.stat.rotate+1
return
end
end
P:freshBlock('fresh')
end,-- X
{
[01]=XspinList[1],
[12]=XspinList[1],
[23]=XspinList[1],
[30]=XspinList[1],
[02]=XspinList[2],
[13]=XspinList[2],
[20]=XspinList[2],
[31]=XspinList[2],
[03]=XspinList[3],
[10]=XspinList[3],
[21]=XspinList[3],
[32]=XspinList[3],
},-- X
{
[01]={'+0+0','-1+0','-1+1','+0-3','-1+1','-1+2','+0+1'},
[10]={'+0+0','-1+0','+1-1','+0+3','+1-1','+1-2','+0+1'},
@@ -592,7 +589,7 @@ do
local F=_strToVec{'+0+0','+0-1','+0+1','+0+2'}
local list={
{[02]=L,[20]=R,[13]=R,[31]=L},-- Z
{[02]=R,[20]=L,[13]=L,[31]=R},-- S
{[02]=R,[20]=L,[13]=R,[31]=L},-- S
{[02]=L,[20]=R,[13]=L,[31]=R},-- J
{[02]=R,[20]=L,[13]=L,[31]=R},-- L
{[02]=F,[20]=F,[13]=L,[31]=R},-- T
@@ -602,20 +599,20 @@ do
{[02]=L,[20]=L,[13]=R,[31]=R},-- Z5
{[02]=R,[20]=R,[13]=L,[31]=L},-- S5
{[02]=L,[20]=R,[13]=L,[31]=R},-- P
{[02]=R,[20]=L,[13]=R,[31]=L},-- Q
{[02]=R,[20]=L,[13]=L,[31]=R},-- F
{[02]=R,[20]=L,[13]=L,[31]=R},-- Q
{[02]=R,[20]=L,[13]=R,[31]=L},-- F
{[02]=L,[20]=R,[13]=R,[31]=L},-- E
{[02]=F,[20]=F,[13]=L,[31]=R},-- T5
{[02]=F,[20]=F,[13]=L,[31]=R},-- U
{[02]=R,[20]=L,[13]=L,[31]=R},-- V
{[02]=R,[20]=L,[13]=L,[31]=R},-- W
{},-- W
{[02]=F,[20]=F,[13]=F,[31]=F},-- X
{[02]=L,[20]=R,[13]=R,[31]=L},-- J5
{[02]=L,[20]=R,[13]=L,[31]=R},-- J5
{[02]=R,[20]=L,[13]=L,[31]=R},-- L5
{[02]=L,[20]=R,[13]=R,[31]=L},-- R
{[02]=L,[20]=R,[13]=L,[31]=R},-- R
{[02]=R,[20]=L,[13]=L,[31]=R},-- Y
{[02]=L,[20]=R,[13]=R,[31]=L},-- N
{[02]=R,[20]=L,[13]=L,[31]=R},-- H
{[02]=R,[20]=L,[13]=R,[31]=L},-- H
{[02]=F,[20]=F,[13]=F,[31]=F},-- I5
{[02]=F,[20]=F,[13]=F,[31]=F},-- I3
@@ -631,6 +628,14 @@ do
list[i][01]=a; list[i][10]=b; list[i][03]=b; list[i][30]=a
list[i][12]=a; list[i][21]=b; list[i][32]=b; list[i][23]=a
end
list[17]={ -- Fix W
[01]=L,[32]=R,
[03]=L,[30]=R,
[10]=R,[23]=L,
[12]=L,[21]=R,
[02]=R,[20]=L,
[31]=L,[13]=R,
}
BiRS={
centerTex=GC.DO{10,10,
{'setCL',1,1,1,.6},
@@ -676,7 +681,7 @@ do
local t=P.freshTime
if not ifpre then
P:freshBlock('move')
P:freshMoveBlock()
end
if fdy>0 and P.freshTime==t and P.curY~=P.imgY then
P.freshTime=P.freshTime-1
@@ -685,7 +690,7 @@ do
local sfx
if ifpre then
sfx='prerotate'
elseif P:ifoverlap(icb,x,y+1) and P:ifoverlap(icb,x-1,y) and P:ifoverlap(icb,x+1,y) then
elseif y==P.ghoY and 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)
else

View File

@@ -19,9 +19,9 @@ function back.update()
t=t-1
if t==0 then
local S={
ang=6.2832*rnd(),
ang=MATH.tau*rnd(),
d=SCR.rad*1.05/2,
rotate=6.2832*rnd(),
rotate=MATH.tau*rnd(),
va=.05-rnd()*.1,
size=SCR.rad*(2+rnd()*3)/100,
texture=SKIN.lib[SETTING.skinSet][rnd(16)],

View File

@@ -21,7 +21,7 @@ function back.update()
x=SCR.w*rnd(),
y=SCR.h*-.05,
k=SCR.rad/200,
ang=rnd()*6.2832,
ang=rnd()*MATH.tau,
vy=.5+rnd()*.4,
vx=rnd()*.4-.2,
va=rnd()*.04-.02,

View File

@@ -21,8 +21,8 @@ function back.update()
ins(mino,{
block=TEXTURE.miniBlock[r],
color=BLOCK_COLORS[SETTING.skin[r]],
ang=6.2832*rnd(),
rotate=6.2832*rnd(),
ang=MATH.tau*rnd(),
rotate=MATH.tau*rnd(),
vr=.05-rnd()*.1,
d=0,
v=.5+rnd(),

View File

@@ -1,5 +1,5 @@
local L={
zChan={-- F0000~F003F
zChan={-- F0000 - F003F
none= 0xF0000,
normal= 0xF0001,
full= 0xF0002,
@@ -15,7 +15,7 @@ local L={
angry= 0xF000C,
shocked= 0xF000D,
ellipses= 0xF000E,
sweat_drop= 0xF000F,
sweatDrop= 0xF000F,
cry= 0xF0010,
cracked= 0xF0011,
qualified= 0xF0012,
@@ -24,7 +24,7 @@ local L={
thinking= 0xF0015,
spark= 0xF0016,
},
mino={-- F0040~F007F
mino={-- F0040 - F007F
Z=0xF0040,
S=0xF0041,
J=0xF0042,
@@ -57,7 +57,7 @@ local L={
I2=0xF005B,
O1=0xF005C,
},
icon={-- F0080~F00FF
icon={-- F0080 - F00FF
menu= 0xF0080,
music= 0xF0081,
language= 0xF0082,
@@ -154,8 +154,12 @@ local L={
rewind30= 0xF00DD,
foward10= 0xF00DE,
foward30= 0xF00DF,
fontUp= 0xF00E0,
fontDown= 0xF00E1,
erase= 0xF00E2,
auto= 0xF00E3,
},
key={-- F0100~F017F
key={-- F0100 - F017F
macCmd= 0xF0100,
macOpt= 0xF0101,
macCtrl= 0xF0102,
@@ -196,7 +200,7 @@ local L={
macPgdnAlt= 0xF0125,
iecPower= 0xF0126,
},
controller={-- F0180~F01FF
controller={-- F0180 - F01FF
xbox= 0xF0180,
lt= 0xF0181,
rt= 0xF0182,
@@ -236,7 +240,7 @@ local L={
psCreate= 0xF01A4,
psOption= 0xF01A5,
},
mahjong={-- F0200~F027F
mahjong={-- F0200 - F027F
m1= 0xF0200,
m2= 0xF0201,
m3= 0xF0202,
@@ -323,6 +327,67 @@ local L={
s1j= 0xF0253,
s1jBase= 0xF0254,
s1jComb= 0xF0255,
},
cards={-- F0300 - F070F
cardBack= 0xF0300,
clubA= 0xF0301,
club2= 0xF0302,
club3= 0xF0303,
club4= 0xF0304,
club5= 0xF0305,
club6= 0xF0306,
club7= 0xF0307,
club8= 0xF0308,
club9= 0xF0309,
club10= 0xF030A,
clubJ= 0xF030B,
clubC= 0xF030C,
clubQ= 0xF030D,
clubK= 0xF030E,
heartA= 0xF0401,
heart2= 0xF0402,
heart3= 0xF0403,
heart4= 0xF0404,
heart5= 0xF0405,
heart6= 0xF0406,
heart7= 0xF0407,
heart8= 0xF0408,
heart9= 0xF0409,
heart10= 0xF040A,
heartJ= 0xF040B,
heartC= 0xF040C,
heartQ= 0xF040D,
heartK= 0xF040E,
diamondA= 0xF0501,
diamond2= 0xF0502,
diamond3= 0xF0503,
diamond4= 0xF0504,
diamond5= 0xF0505,
diamond6= 0xF0506,
diamond7= 0xF0507,
diamond8= 0xF0508,
diamond9= 0xF0509,
diamond10= 0xF050A,
diamondJ= 0xF050B,
diamondC= 0xF050C,
diamondQ= 0xF050D,
diamondK= 0xF050E,
jokerBlack= 0xF050F,
clubA= 0xF0601,
club2= 0xF0602,
club3= 0xF0603,
club4= 0xF0604,
club5= 0xF0605,
club6= 0xF0606,
club7= 0xF0607,
club8= 0xF0608,
club9= 0xF0609,
club10= 0xF060A,
clubJ= 0xF060B,
clubC= 0xF060C,
clubQ= 0xF060D,
clubK= 0xF060E,
jokerWhite= 0xF060F,
}
}

View File

@@ -2,7 +2,7 @@ local floor=math.floor
local char,byte=string.char,string.byte
local ins=table.insert
local BAG,FIELD,MISSION,CUSTOMENV,GAME=BAG,FIELD,MISSION,CUSTOMENV,GAME
local GAME=GAME
local DATA={}
-- Sep symbol: 33 (!)
@@ -13,13 +13,13 @@ local DATA={}
Encode: A[B] sequence, A = block ID, B = repeat times, no B means do not repeat.
Example: "abcdefg" is [SZJLTOI], "a^aDb)" is [Z*63,Z*37,S*10]
]]
function DATA.copySequence()
function DATA.copySequence(bag)
local str=""
local count=1
for i=1,#BAG+1 do
if BAG[i+1]~=BAG[i] or count==64 then
str=str..char(96+BAG[i])
for i=1,#bag+1 do
if bag[i+1]~=bag[i] or count==64 then
str=str..char(96+bag[i])
if count>1 then
str=str..char(32+count)
count=1
@@ -32,7 +32,7 @@ function DATA.copySequence()
return str
end
function DATA.pasteSequence(str)
TABLE.cut(BAG)
local bag={}
local b,reg
for i=1,#str do
b=byte(str,i)
@@ -44,20 +44,20 @@ function DATA.pasteSequence(str)
end
else
if b>=97 and b<=125 then
ins(BAG,reg)
ins(bag,reg)
reg=b-96
elseif b>=34 and b<=96 then
for _=1,b-32 do
ins(BAG,reg)
ins(bag,reg)
end
reg=false
end
end
end
if reg then
ins(BAG,reg)
ins(bag,reg)
end
return true
return true,bag
end
local fieldMeta={__index=function(self,h)
@@ -69,8 +69,7 @@ end}
function DATA.newBoard(f)-- Generate a new board
return setmetatable(f and TABLE.shift(f) or{},fieldMeta)
end
function DATA.copyBoard(page)-- Copy the [page] board
local F=FIELD[page or 1]
function DATA.copyBoard(F)-- Copy the [page] board
local str=""
-- Encode field
@@ -84,21 +83,15 @@ function DATA.copyBoard(page)-- Copy the [page] board
end
return STRING.packBin(str)
end
function DATA.copyBoards()
function DATA.copyBoards(field)
local out={}
for i=1,#FIELD do
out[i]=DATA.copyBoard(i)
for i=1,#field do
out[i]=DATA.copyBoard(field[i])
end
return table.concat(out,"!")
end
function DATA.pasteBoard(str,page)-- Paste [str] data to [page] board
if not page then
page=1
end
if not FIELD[page] then
FIELD[page]=DATA.newBoard()
end
local F=FIELD[page]
function DATA.pasteBoard(str)-- Paste [str] data to [page] board
local F=DATA.newBoard()
-- Decode
str=STRING.unpackBin(str)
@@ -106,6 +99,7 @@ function DATA.pasteBoard(str,page)-- Paste [str] data to [page] board
local fX,fY=1,1-- *ptr for Field(r*10+(c-1))
local p=1
local lineLimit=126
while true do
local b=byte(str,p)-- 1byte
@@ -127,13 +121,13 @@ function DATA.pasteBoard(str,page)-- Paste [str] data to [page] board
fX=fX+1
else
fY=fY+1
if fY>60 then break end
if fY>lineLimit then break end
fX=1
end
p=p+1
end
return true
return true,F,#str>lineLimit*10
end
--[[
@@ -152,14 +146,14 @@ end
O1=61,O2=62,O3=63,O4=64,
I1=71,I2=72,I3=73,I4=74,
]]
function DATA.copyMission()
function DATA.copyMission(mission)
local _
local str=""
local count=1
for i=1,#MISSION+1 do
if MISSION[i+1]~=MISSION[i] or count==13 then
_=33+MISSION[i]
for i=1,#mission+1 do
if mission[i+1]~=mission[i] or count==13 then
_=33+mission[i]
str=str..char(_)
if count>1 then
str=str..char(113+count)
@@ -174,7 +168,7 @@ function DATA.copyMission()
end
function DATA.pasteMission(str)
local b
TABLE.cut(MISSION)
local mission={}
local reg
for i=1,#str do
b=byte(str,i)
@@ -187,28 +181,28 @@ function DATA.pasteMission(str)
else
if b>=34 and b<=114 then
if ENUM_MISSION[reg] then
ins(MISSION,reg)
ins(mission,reg)
reg=b-33
else
TABLE.cut(MISSION)
TABLE.cut(mission)
return
end
elseif b>=115 and b<=126 then
for _=1,b-113 do
ins(MISSION,reg)
ins(mission,reg)
end
reg=false
end
end
end
if reg then
ins(MISSION,reg)
ins(mission,reg)
end
return true
return true,mission
end
function DATA.copyQuestArgs()
local ENV=CUSTOMENV
function DATA.copyQuestArgs(custom_env)
local ENV=custom_env
local str=""..
ENV.holdCount..
(ENV.ospin and "O" or "Z")..
@@ -218,14 +212,37 @@ function DATA.copyQuestArgs()
end
function DATA.pasteQuestArgs(str)
if #str<4 then return end
local ENV=CUSTOMENV
ENV.holdCount= str:byte(1)-48
local ENV={}
ENV.holdCount= MATH.clamp(str:byte(1)-48,0,26)
ENV.ospin= str:byte(2)~=90
ENV.missionKill=str:byte(3)~=90
ENV.sequence= str:sub(4)
return true
if select(2,require"parts.player.seqGenerators"(ENV.sequence)) then
MES.new('warn',text.invalidSequence)
ENV.sequence='bag'
end
return true,ENV
end
local function _encode(t)
if t<128 then return char(t) end
local buffer2=char(t%128)
t=floor(t/128)
while t>=128 do
buffer2=char(128+t%128)..buffer2
t=floor(t/128)
end
return char(128+t)..buffer2
end
local function _decode(str,p)
local ret=0
repeat
local b=byte(str,p)
p=p+1
ret=ret*128+(b<128 and b or b-128)
until b<128
return ret,p
end
--[[
Replay file:
a zlib-compressed json table
@@ -250,7 +267,7 @@ end
]]
function DATA.dumpRecording(list,ptr)
local out=""
local buffer,buffer2=""
local buffer=""
if not ptr then ptr=1 end
local prevFrm=list[ptr-2] or 0
while list[ptr] do
@@ -263,31 +280,10 @@ function DATA.dumpRecording(list,ptr)
-- Encode time
local t=list[ptr]-prevFrm
prevFrm=list[ptr]
if t>=128 then
buffer2=char(t%128)
t=floor(t/128)
while t>=128 do
buffer2=char(128+t%128)..buffer2
t=floor(t/128)
end
buffer=buffer..char(128+t)..buffer2
else
buffer=buffer..char(t)
end
buffer=buffer.._encode(t)
-- Encode event
t=list[ptr+1]
if t>=128 then
buffer2=char(t%128)
t=floor(t/128)
while t>=128 do
buffer2=char(128+t%128)..buffer2
t=floor(t/128)
end
buffer=buffer..char(128+t)..buffer2
else
buffer=buffer..char(t)
end
buffer=buffer.._encode(list[ptr+1])
-- Step
ptr=ptr+2
@@ -299,66 +295,53 @@ function DATA.pumpRecording(str,L)
local p=1
local curFrm=L[#L-1] or 0
local code
while p<=len do
local code,event
-- Read delta time
code=0
local b=byte(str,p)
while b>=128 do
code=code*128+b-128
p=p+1
b=byte(str,p)
end
curFrm=curFrm+code*128+b
L[#L+1]=curFrm
p=p+1
code,p=_decode(str,p)
curFrm=curFrm+code
ins(L,curFrm)
local event=0
b=byte(str,p)
while b>=128 do
event=event*128+b-128
p=p+1
b=byte(str,p)
end
L[#L+1]=event*128+b
p=p+1
event,p=_decode(str,p)
ins(L,event)
end
end
do-- function DATA.saveReplay()
local noRecList={"custom","solo","round","techmino"}
local function _getModList()
local res={}
for _,v in next,GAME.mod do
if v.sel>0 then
ins(res,{v.no,v.sel})
for number,sel in next,GAME.mod do
if sel>0 then
ins(res,{MODOPT[number].no,sel})
end
end
return res
end
function DATA.saveReplay()
-- Filtering modes that cannot be saved
for _,v in next,noRecList do
if GAME.curModeName:find(v) then
MES.new('error',"Cannot save recording of this mode now!")
return
end
if GAME.initPlayerCount~=1 then
MES.new('error',"Cannot save recording of more than 1 player now!")
return
end
-- Write file
local fileName=os.date("replay/%Y_%m_%d_%H%M%S.rep")
if not love.filesystem.getInfo(fileName) then
local metadata={
date=os.date("%Y/%m/%d %H:%M:%S"),
mode=GAME.curModeName,
version=VERSION.string,
player=USERS.getUsername(USER.uid),
seed=GAME.seed,
setting=GAME.setting,
mod=_getModList(),
tasUsed=GAME.tasUsed,
}
if GAME.curMode.savePrivate then
metadata.private=GAME.curMode.savePrivate()
end
love.filesystem.write(fileName,
love.data.compress('string','zlib',
JSON.encode{
date=os.date("%Y/%m/%d %H:%M:%S"),
mode=GAME.curModeName,
version=VERSION.string,
player=USERS.getUsername(USER.uid),
seed=GAME.seed,
setting=GAME.setting,
mod=_getModList(),
tasUsed=GAME.tasUsed,
}.."\n"..
JSON.encode(metadata).."\n"..
DATA.dumpRecording(GAME.rep)
)
)
@@ -413,6 +396,9 @@ function DATA.parseReplayData(fileName,fileData,ifFull)
tasUsed=metaData.tasUsed,
}
if ifFull then rep.data=fileData end
if metaData.private then
rep.private=metaData.private
end
return rep
end
return DATA

View File

@@ -8,7 +8,7 @@ return {
for i=#P.field,1,-1 do
P.field[i],P.visTime[i]=nil
end
setField(P,D.finished+1)
setField(P,FIELD[D.finished+1])
SYSFX.newShade(1.4,P.absFieldX,P.absFieldY,300*P.size,610*P.size,.6,.8,.6)
SFX.play('warn_1')
else

View File

@@ -7,21 +7,21 @@ end
local function GetGravity(lvl)
lvl=lvl%256
return
lvl==0 and 48 or
lvl==1 and 43 or
lvl==2 and 38 or
lvl==3 and 33 or
lvl==4 and 28 or
lvl==5 and 23 or
lvl==6 and 18 or
lvl==7 and 13 or
lvl==8 and 8 or
lvl==9 and 6 or
lvl<13 and 5 or
lvl<16 and 4 or
lvl<19 and 3 or
lvl<29 and 2 or
1
lvl==0 and 48 or
lvl==1 and 43 or
lvl==2 and 38 or
lvl==3 and 33 or
lvl==4 and 28 or
lvl==5 and 23 or
lvl==6 and 18 or
lvl==7 and 13 or
lvl==8 and 8 or
lvl==9 and 6 or
lvl<13 and 5 or
lvl<16 and 4 or
lvl<19 and 3 or
lvl<29 and 2 or
1
end
return {
das=16,arr=6,

View File

@@ -7,21 +7,21 @@ end
local function GetGravity(lvl)
lvl=lvl%256
return
lvl==0 and 48 or
lvl==1 and 43 or
lvl==2 and 38 or
lvl==3 and 33 or
lvl==4 and 28 or
lvl==5 and 23 or
lvl==6 and 18 or
lvl==7 and 13 or
lvl==8 and 8 or
lvl==9 and 6 or
lvl<13 and 5 or
lvl<16 and 4 or
lvl<19 and 3 or
lvl<29 and 2 or
1
lvl==0 and 48 or
lvl==1 and 43 or
lvl==2 and 38 or
lvl==3 and 33 or
lvl==4 and 28 or
lvl==5 and 23 or
lvl==6 and 18 or
lvl==7 and 13 or
lvl==8 and 8 or
lvl==9 and 6 or
lvl<13 and 5 or
lvl<16 and 4 or
lvl<19 and 3 or
lvl<29 and 2 or
1
end
return {
das=16,arr=6,

View File

@@ -7,21 +7,21 @@ end
local function GetGravity(lvl)
lvl=lvl%256
return
lvl==0 and 48 or
lvl==1 and 43 or
lvl==2 and 38 or
lvl==3 and 33 or
lvl==4 and 28 or
lvl==5 and 23 or
lvl==6 and 18 or
lvl==7 and 13 or
lvl==8 and 8 or
lvl==9 and 6 or
lvl<13 and 5 or
lvl<16 and 4 or
lvl<19 and 3 or
lvl<29 and 2 or
1
lvl==0 and 48 or
lvl==1 and 43 or
lvl==2 and 38 or
lvl==3 and 33 or
lvl==4 and 28 or
lvl==5 and 23 or
lvl==6 and 18 or
lvl==7 and 13 or
lvl==8 and 8 or
lvl==9 and 6 or
lvl<13 and 5 or
lvl<16 and 4 or
lvl<19 and 3 or
lvl<29 and 2 or
1
end
return {
das=16,arr=6,

View File

@@ -0,0 +1,114 @@
local gc_setColor,gc_draw=love.graphics.setColor,love.graphics.draw
local ply_applyField=PLY.draw.applyField
local holePatterns={
{-1,21,-1,21,-1,21,-1,21,-1,21},
{21,-1,21,-1,21,-1,21,-1,21,-1}
}
local targetField={}
local function generateGuide(y,mirror)
local tfLength=#targetField
if tfLength>y then return end
mirror=mirror and 1 or 0
for i=tfLength,y do
table.insert(targetField,TABLE.shift(
holePatterns[(i+mirror)%2+1]
))
end
end
local function calculateRankPts(P)
local points=1
for y=1,#P.field do
local flag
for x=1,10 do
-- X guide is filled
if P.field[y][x]>0 and targetField[y][x]<0 then flag=true break end
-- Block guide is empty
if P.field[y][x]==0 and targetField[y][x]>0 then flag=true break end
end
if flag then break end
points=points+1
end
P.modeData.rankPts=points
P.modeData.maxRankPts=math.max(points,P.modeData.maxRankPts)
end
return {
fkey1=function(P) P.modeData.showGuide=not P.modeData.showGuide end,
fkey2=function(P)
P.modeData.mirror=not P.modeData.mirror
TABLE.cut(targetField)
generateGuide(#P.field+10,P.modeData.mirror)
calculateRankPts(P)
end,
mesDisp=function(P)
local D=P.modeData
mText(TEXTOBJ.grade,63,190)
mText(TEXTOBJ.line,63,310)
setFont(55)
GC.mStr(getConstructGrade(D.rankPts),63,125)
GC.mStr(D.rankPts-1,63,245)
-- Display highest grade
if D.maxRankPts>D.rankPts then
gc_setColor(COLOR.lX)
setFont(20)
GC.mStr(text.highest:repD(getConstructGrade(D.maxRankPts)),63,216)
GC.mStr(text.highest:repD(D.maxRankPts-1),63,336)
end
if not D.showGuide then return end
ply_applyField(P)
local mark=TEXTURE.puzzleMark
local firstMistake=nil
for y=1,D.rankPts+1 do
for x=1,10 do
local texture=targetField[y][x]
-- Missing blocks
if not P:solid(x,y) and texture>0 then
-- Missing block under overhang
if P:solid(x,y+1) then
firstMistake=firstMistake or y
gc_setColor(COLOR.R)
else
gc_setColor(COLOR.Z)
end
gc_draw(mark[texture],30*x-30,600-30*y)
elseif texture<0 then
-- X always gets displayed, color changes based on whether there is a block there
if P:solid(x,y) then
gc_setColor(COLOR.R)
firstMistake=firstMistake or y
elseif D.rankPts>y then
gc_setColor(COLOR.G)
else
gc_setColor(COLOR.Z)
end
gc_draw(mark[texture],30*x-30,600-30*y)
end
end
if y==firstMistake then
gc_setColor(1,0,0,.2*(math.sin(2*TIME())+1))
love.graphics.rectangle("fill",0,600-30*y,300,30)
end
end
PLY.draw.cancelField()
end,
task=function(P)
local D=P.modeData
D.rankPts=1
D.maxRankPts=1
D.showGuide=true
D.mirror=false
TABLE.cut(targetField)
generateGuide(10)
end,
hook_drop=function(P)
local D=P.modeData
calculateRankPts(P)
generateGuide(#P.field+10)
end
}

View File

@@ -0,0 +1,118 @@
local gc_setColor,gc_draw=love.graphics.setColor,love.graphics.draw
local ply_applyField=PLY.draw.applyField
local targetField={}
local function getOpenHole(y,mirror)
if mirror then y=y+9 end
return -math.abs(((y-1) % 18)-9)+10
end
local function generateGuide(y,mirror)
local l=#targetField
if l>y then
return
end
for i=l,y do
targetField[i] = {}
local h=getOpenHole(i,mirror)
for j=1,10 do
targetField[i][j]=h==j and 21 or -1
end
end
end
local function calculateRankPts(P)
local points=1
for y=1,#P.field do
local holePos=getOpenHole(y,P.modeData.mirror)
local flag
for x=1,10 do
if P.field[y][x]>0 and holePos~=x then flag=true break end
if P.field[y][x]==0 and holePos==x then flag=true break end
end
if flag then break end
if P:solid(holePos,y+1) then break end
points=points+1
end
P.modeData.rankPts=points
P.modeData.maxRankPts=math.max(points,P.modeData.maxRankPts)
end
return {
fkey1=function(P) P.modeData.showGuide=not P.modeData.showGuide end,
fkey2=function(P)
P.modeData.mirror=not P.modeData.mirror
TABLE.cut(targetField)
generateGuide(#P.field+10,P.modeData.mirror)
calculateRankPts(P)
end,
mesDisp=function(P)
local D=P.modeData
mText(TEXTOBJ.grade,63,190)
mText(TEXTOBJ.line,63,310)
setFont(55)
GC.mStr(getConstructGrade(D.rankPts),63,125)
GC.mStr(D.rankPts-1,63,245)
-- Display highest grade
if D.maxRankPts>D.rankPts then
gc_setColor(COLOR.lX)
setFont(20)
GC.mStr(text.highest:repD(getConstructGrade(D.maxRankPts)),63,216)
GC.mStr(text.highest:repD(D.maxRankPts-1),63,336)
end
if not D.showGuide then return end
ply_applyField(P)
local mark=TEXTURE.puzzleMark
local firstMistake=nil
for y=1,D.rankPts+1 do
for x=1,10 do
local texture=targetField[y][x]
-- Missing blocks
if not P:solid(x,y) and texture>0 then
-- Missing block under overhang
if P:solid(x,y+1) then
firstMistake=firstMistake or y
gc_setColor(COLOR.R)
else
gc_setColor(COLOR.Z)
end
gc_draw(mark[texture],30*x-30,600-30*y)
elseif texture<0 then
-- X always gets displayed, color changes based on whether there is a block there
if P:solid(x,y) then
gc_setColor(COLOR.R)
firstMistake=firstMistake or y
elseif D.rankPts>y then
gc_setColor(COLOR.G)
else
gc_setColor(COLOR.Z)
end
gc_draw(mark[texture],30*x-30,600-30*y)
end
end
if y==firstMistake then
gc_setColor(1,0,0,.2*(math.sin(2*TIME())+1))
love.graphics.rectangle("fill",0,600-30*y,300,30)
end
end
PLY.draw.cancelField()
end,
task=function(P)
local D=P.modeData
D.rankPts=1
D.showGuide=true
D.maxRankPts=1
D.mirror=false
TABLE.cut(targetField)
generateGuide(10,D.mirror)
end,
hook_drop=function(P)
local oldPts=P.modeData.rankPts
calculateRankPts(P)
if oldPts>P.modeData.rankPts+2 then
P:_showText("REGRET!!",0,-120,80,'beat',.8)
end
generateGuide(#P.field+10,P.modeData.mirror)
end
}

View File

@@ -0,0 +1,114 @@
local gc_setColor,gc_draw=love.graphics.setColor,love.graphics.draw
local ply_applyField=PLY.draw.applyField
local targetField={}
local function getOpenHole(y,mirror)
if mirror then y=y+9 end
return -math.abs(((y-1) % 18)-9)+10
end
local function generateGuide(y,mirror)
local l=#targetField
if l>y then
return
end
for i=l,y do
targetField[i] = {}
local h=getOpenHole(i,mirror)
for j=1,10 do
targetField[i][j]=h==j and -1 or 21
end
end
end
local function calculateRankPts(P)
local points=1
for y=1,#P.field do
local holePos=getOpenHole(y,P.modeData.mirror)
local flag
for x=1,10 do
if P.field[y][x]>0 and holePos==x then flag=true break end
if P.field[y][x]==0 and holePos~=x then flag=true break end
end
if flag then break end
if not P:solid(holePos,y+1) then break end
points=points+1
end
P.modeData.rankPts=points
P.modeData.maxRankPts=math.max(points,P.modeData.maxRankPts)
end
return {
fkey1=function(P) P.modeData.showGuide=not P.modeData.showGuide end,
fkey2=function(P)
P.modeData.mirror=not P.modeData.mirror
TABLE.cut(targetField)
generateGuide(#P.field+10,P.modeData.mirror)
calculateRankPts(P)
end,
mesDisp=function(P)
local D=P.modeData
mText(TEXTOBJ.grade,63,190)
mText(TEXTOBJ.line,63,310)
setFont(55)
GC.mStr(getConstructGrade(D.rankPts),63,125)
GC.mStr(D.rankPts-1,63,245)
-- Display highest grade
if D.maxRankPts>D.rankPts then
gc_setColor(COLOR.lX)
setFont(20)
GC.mStr(text.highest:repD(getConstructGrade(D.maxRankPts)),63,216)
GC.mStr(text.highest:repD(D.maxRankPts-1),63,336)
end
if not D.showGuide then return end
ply_applyField(P)
local mark=TEXTURE.puzzleMark
local firstMistake=nil
for y=1,D.rankPts+1 do
for x=1,10 do
local texture=targetField[y][x]
-- Missing blocks
if not P:solid(x,y) and texture>0 then
-- Missing block under overhang
if P:solid(x,y+1) then
firstMistake=firstMistake or y
gc_setColor(COLOR.R)
else
gc_setColor(COLOR.Z)
end
gc_draw(mark[texture],30*x-30,600-30*y)
elseif texture<0 then
-- X always gets displayed, color changes based on whether there is a block there
if P:solid(x,y) then
gc_setColor(COLOR.R)
firstMistake=firstMistake or y
elseif D.rankPts>y then
gc_setColor(COLOR.G)
else
gc_setColor(COLOR.Z)
end
gc_draw(mark[texture],30*x-30,600-30*y)
end
end
if y==firstMistake then
gc_setColor(1,0,0,.2*(math.sin(2*TIME())+1))
love.graphics.rectangle("fill",0,600-30*y,300,30)
end
end
PLY.draw.cancelField()
end,
task=function(P)
local D=P.modeData
D.rankPts=1
D.showGuide=true
D.maxRankPts=1
D.mirror=false
TABLE.cut(targetField)
generateGuide(10,D.mirror)
end,
hook_drop=function(P)
calculateRankPts(P)
generateGuide(#P.field+10,P.modeData.mirror)
end
}

View File

@@ -15,54 +15,54 @@ local cool_time={3120,3120,2940,2700,2700,2520,2520,2280,2280,0}
local regret_time= {5400,4500,4500,4080,3600,3600,3000,3000,3000,3000}
local function getGrav(l)
return
l<30 and 64 or
l<35 and 43 or
l<40 and 32 or
l<50 and 26 or
l<60 and 21 or
l<70 and 16 or
l<80 and 8 or
l<90 and 6 or
l<120 and 4 or
l<160 and 3 or
l<200 and 2 or
l<220 and 64 or
l<230 and 8 or
l<233 and 4 or
l<236 and 3 or
l<243 and 2 or
l<300 and 1 or
l<360 and 0.5 or
l<450 and 0.25 or
0
l<30 and 64 or
l<35 and 43 or
l<40 and 32 or
l<50 and 26 or
l<60 and 21 or
l<70 and 16 or
l<80 and 8 or
l<90 and 6 or
l<120 and 4 or
l<160 and 3 or
l<200 and 2 or
l<220 and 64 or
l<230 and 8 or
l<233 and 4 or
l<236 and 3 or
l<243 and 2 or
l<300 and 1 or
l<360 and 0.5 or
l<450 and 0.25 or
0
end
local function getLock(l)
return
l<900 and 30 or
l<1100 and 19 or
15
l<900 and 30 or
l<1100 and 19 or
15
end
local function getWait(l)
return
l<700 and 23 or
l<800 and 16 or
l<1000 and 12 or
l<1100 and 7 or
6
l<700 and 23 or
l<800 and 16 or
l<1000 and 12 or
l<1100 and 7 or
6
end
local function getFall(l)
return
l<500 and 25 or
l<600 and 18 or
l<700 and 12 or
l<800 and 8 or
4
l<500 and 25 or
l<600 and 18 or
l<700 and 12 or
l<800 and 8 or
4
end
local function getDas(l)
return
l<500 and 10 or
l<900 and 8 or
6
l<500 and 10 or
l<900 and 8 or
6
end
local function getCurrentGrade(D)
if not D.int_grade then D.int_grade=0 end
@@ -175,7 +175,7 @@ return {
GC.rectangle('line',0,240,126,80,4)
GC.setColor(.98,.98,.98,.4)
GC.rectangle('fill',0+2,240+2,126-4,80-4,2) -- draw time box
setFont(45) -- Draw time text
local t=(P.stat.frame-D.prevSectTime)/60
local T=("%.1f"):format(60-t)
@@ -261,7 +261,7 @@ return {
elseif s==5 then
if P.stat.frame>420*60 then
D.pt=500
P:win('finish')
P:torikanEnd(420)
return
else
BG.set('glow')

View File

@@ -40,7 +40,7 @@ return {
elseif s==5 then
if P.stat.frame>183*60 then
D.pt=500
P:win('finish')
P:torikanEnd(183)
return
else
E.bone=true

View File

@@ -38,9 +38,9 @@ return {
elseif s==4 then
BG.set('rainbow2')
elseif s==5 then
if P.stat.frame>260*60 then
if P.stat.time>260 then
D.pt=500
P:win('finish')
P:torikanEnd(260)
return
else
P.gameEnv.freshLimit=10

View File

@@ -1,69 +0,0 @@
local gc_setColor,gc_draw=love.graphics.setColor,love.graphics.draw
local ply_applyField=PLY.draw.applyField
local function getOpenHole(num)
return -math.abs(((num-1) % 18)-9)+10
end
local F={}
-- local ranks={"10","9","8","7","6","5","4","3","2","1","S1","S2","S3","S4","S5","S6","S7","S8","S9","GM","GM+","TM","TM+","TM+₂","TM+₃", "TM+₄","TM+₅"}
-- lines: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
local function generateGuide(num)
local l=#F
if l>num then
return
end
for i=l,num do
F[i] = {}
local h=getOpenHole(i)
for j=1,10 do
F[i][j]=h==j and -1 or 21
end
end
end
return {
fkey1=function(P) P.modeData.showGuide=not P.modeData.showGuide end,
mesDisp=function(P)
mText(TEXTOBJ.grade,63,190)
mText(TEXTOBJ.line,63,310)
setFont(55)
GC.mStr(getSecretGrade(P.modeData.rankPts),63,125)
GC.mStr(P.modeData.rankPts-1,63,245)
ply_applyField(P)
local mark=TEXTURE.puzzleMark
gc_setColor(1,1,1)
if P.modeData.showGuide then
for y=1,P.modeData.rankPts+1 do for x=1,10 do
local T=F[y][x]
if T~=0 then
gc_draw(mark[T],30*x-30,600-30*y)
end
end end
end
PLY.draw.cancelField()
end,
task=function(P)
P.modeData.rankPts=1
P.modeData.showGuide=true
generateGuide(10)
end,
hook_drop=function(P)
local D=P.modeData
D.rankPts=0
for i=1,#P.field do
local h=getOpenHole(i)
local flag
for j=1,10 do
if P.field[i][j]>0 and h==j then flag=true break end-- goto post_pts_calc
if P.field[i][j]==0 and h~=j then flag=true break end-- goto post_pts_calc
end
if flag then break end
if i==#P.field then break end-- goto post_pts_calc
if P.field[i+1][h]==0 then break end-- goto post_pts_calc
D.rankPts=D.rankPts+1
end
-- ::post_pts_calc::
generateGuide(D.rankPts+20)
end
}

View File

@@ -15,13 +15,13 @@ return {
if P.cur and not P:ifoverlap(P.cur.bk,P.curX-1,P.curY) then
P:createMoveFX('left')
P.curX=P.curX-1
P:freshBlock('move')
P:freshMoveBlock()
end
elseif r==2 then
if P.cur and not P:ifoverlap(P.cur.bk,P.curX-1,P.curY) then
P:createMoveFX('left')
P.curX=P.curX-1
P:freshBlock('move')
P:freshMoveBlock()
end
elseif r==3 then
P:act_rotRight()

View File

@@ -3,6 +3,7 @@ local function lockKey(P,keys)
P.keyAvailable[v]=false
VK.keys[v].ava=false
VK.release(v)
P:releaseKey(v)
end
end
local function unlockKey(P,keys)
@@ -15,7 +16,10 @@ local function lockMovement(P)
lockKey(P,{1,2})
end
local function lockRotation(P)
lockKey(P,{3,4,5})
lockKey(P,{3,4})
end
local function lockRotation180(P)
lockKey(P,{5})
end
local function unlock(P)
if P.cur and P.cur.name==6 and not P.gameEnv.skipOCheck then -- don't unlock rotation if O piece & no O-spin
@@ -39,37 +43,38 @@ local function onMove(P)
P.holdTime=0
lockKey(P,{8})
-- return if overhang
if P:_roofCheck() then return end
P.modeData.moveCount=P.modeData.moveCount+1
if P.modeData.moveCount>=2 then lockMovement(P) end
if not P:_roofCheck() then
P.modeData.moveCount=P.modeData.moveCount+1
if P.modeData.moveCount>=2 then lockMovement(P) end
end
end
local function onAutoMove(P)
if P:_roofCheck() then unlock(P) end
end
local function onRotate(P)
local function onRotate(P,dir)
if not P.cur then return end
P.holdTime=0
lockKey(P,{8})
-- return if overhang
if P:_roofCheck() then return end
P.modeData.rotations=P.modeData.rotations+1
if P.modeData.rotations>=2 then lockRotation(P) end
if not P:_roofCheck() then
P.modeData.rotations=P.modeData.rotations+(dir==2 and 2 or 1)
lockRotation180(P)
if P.modeData.rotations>=2 then
lockRotation(P)
end
end
end
return {
arr=0,
fineKill=true,
mesDisp=function(P)
setFont(45)
GC.mStr(("%d"):format(P.stat.atk),63,190)
GC.mStr(("%.2f"):format(P.stat.atk/P.stat.row),63,310)
mText(TEXTOBJ.atk,63,243)
mText(TEXTOBJ.eff,63,363)
setFont(55)
local r=40-P.stat.row
if r<0 then r=0 end
GC.mStr(r,63,265)
PLY.draw.drawTargetLine(P,r)
end,
task=function(P)
resetLock(P)
@@ -82,7 +87,7 @@ return {
end,
hook_drop=function(P)
resetLock(P)
if P.stat.atk>=100 then
if P.stat.row>=40 then
P:win('finish')
end
end,
@@ -90,6 +95,7 @@ return {
if P.gameEnv.skipOCheck then return end
if P.cur.name==6 then
lockRotation(P)
lockRotation180(P)
else
resetLock(P)
end
@@ -98,11 +104,12 @@ return {
if P.gameEnv.skipOCheck then return end
if P.cur.name==6 then
lockRotation(P)
lockRotation180(P)
else
resetLock(P)
end
end,
hook_left_manual=onMove, hook_right_manual=onMove,
hook_left_auto=onAutoMove, hook_right_auto=onAutoMove,
hook_rotLeft=onRotate, hook_rotRight=onRotate, hook_rot180=onRotate,
hook_rotate=onRotate
}

View File

@@ -26,7 +26,7 @@ return {
P.garbageBeneath=P.garbageBeneath-bonus
end
end
P:freshBlock('push')
P:freshBlockGhost()
end
end,
}

View File

@@ -26,7 +26,7 @@ return {
P.garbageBeneath=P.garbageBeneath-bonus
end
end
P:freshBlock('push')
P:freshBlockGhost()
end
end,
}

View File

@@ -1,116 +0,0 @@
local function lockKey(P,keys)
for _,v in next,keys do
P.keyAvailable[v]=false
VK.keys[v].ava=false
VK.release(v)
end
end
local function unlockKey(P,keys)
for _,v in next,keys do
P.keyAvailable[v]=true
VK.keys[v].ava=true
end
end
local function lockMovement(P)
lockKey(P,{1,2})
end
local function lockRotation(P)
lockKey(P,{3,4,5})
end
local function unlock(P)
if P.cur and P.cur.name==6 and not P.gameEnv.skipOCheck then -- don't unlock rotation if O piece & no O-spin
unlockKey(P,{1,2,6,7})
return
end
unlockKey(P,{1,2,3,4,5,6,7})
end
local function resetLock(P)
unlock(P)
unlockKey(P,{8})
P.modeData.moveCount=0
P.modeData.rotations=0
P.holdTime=1
end
local function onMove(P)
if not P.cur then return end
P.holdTime=0
lockKey(P,{8})
-- return if overhang
if P:_roofCheck() then return end
P.modeData.moveCount=P.modeData.moveCount+1
if P.modeData.moveCount>=2 then lockMovement(P) end
end
local function onAutoMove(P)
if P:_roofCheck() then unlock(P) end
end
local function onRotate(P)
if not P.cur then return end
P.holdTime=0
lockKey(P,{8})
-- return if overhang
if P:_roofCheck() then return end
P.modeData.rotations=P.modeData.rotations+1
if P.modeData.rotations>=2 then lockRotation(P) end
end
return {
arr=0,
fineKill=true,
mesDisp=function(P)
setFont(45)
GC.mStr(("%d"):format(P.stat.atk),63,190)
GC.mStr(("%.2f"):format(P.stat.atk/P.stat.row),63,310)
mText(TEXTOBJ.atk,63,243)
mText(TEXTOBJ.eff,63,363)
end,
task=function(P)
resetLock(P)
local RSname=P.RS.name
P.gameEnv.skipOCheck=(
string.find(RSname,'TRS') or
string.find(RSname,'BiRS') or
string.find(RSname,'ASC')
)
end,
hook_drop=function(P)
resetLock(P)
local C=P.lastPiece
if C.row>0 then
if not C.special then
P:lose()
return
end
end
if P.stat.atk>=100 then
P:win('finish')
end
end,
hook_spawn=function(P)
if P.gameEnv.skipOCheck then return end
if P.cur.name==6 then
lockRotation(P)
else
resetLock(P)
end
end,
hook_hold=function(P)
if P.gameEnv.skipOCheck then return end
if P.cur.name==6 then
lockRotation(P)
else
resetLock(P)
end
end,
hook_left_manual=onMove, hook_right_manual=onMove,
hook_left_auto=onAutoMove, hook_right_auto=onAutoMove,
hook_rotLeft=onRotate, hook_rotRight=onRotate, hook_rot180=onRotate
}

Binary file not shown.

Binary file not shown.

View File

@@ -115,7 +115,7 @@ do-- function applySettings()
light={.2,.8},
color={-.2,1.2},
}
function applySettings()
function applySettings(reason)
-- Apply language
text=LANG.get(SETTING.locale)
WIDGET.setLang(text.WidgetText)
@@ -157,9 +157,12 @@ do-- function applySettings()
SHADER.fieldSatur:send('k',m[2])
-- Apply BG
if reason=='fullscreen' then return end
if SETTING.bg=='on' then
BG.unlock()
BG.setDefault(SETTING.defaultBG)
BG.set()
if SETTING.lockBG then BG.lock() end
elseif SETTING.bg=='off' then
BG.unlock()
BG.set('fixColor',SETTING.bgAlpha,SETTING.bgAlpha,SETTING.bgAlpha)
@@ -192,13 +195,13 @@ local function getSmallNum(num)
local str=tostring(num)
local out=""
for i=1,#str do
out=out..smallDigits[tonumber(string.sub(str,i,i))]
out=out..smallDigits[tonumber(str:sub(i,i))]
end
return out
end
do -- Secret Grade
local r={"GM","GM+","TM","TM+"}
function getSecretGrade(index)
function getConstructGrade(index)
if index<11 then -- rank 10 - 1
return tostring(11-index)
elseif index<20 then -- S1 - S9 ranks
@@ -210,11 +213,11 @@ do -- Secret Grade
end
end
end
function getSecretGradeText(index)
function getConstructGradeText(index)
if index<11 then
return "Grade "..tostring(11-index)
else
return getSecretGrade(index)
return getConstructGrade(index)
end
end
@@ -424,8 +427,7 @@ function notEmptyLine(L)
end
end
end
function setField(P,page)
local F=FIELD[page]
function setField(P,F)
local height=0
for y=#F,1,-1 do
if notEmptyLine(F[y]) then
@@ -504,8 +506,8 @@ function mergeStat(stat,delta)-- Merge delta stat. to global stat.
end
end
function scoreValid()-- Check if any unranked mods are activated
for _,M in next,GAME.mod do
if M.unranked then
for number,sel in next,GAME.mod do
if sel>0 and MODOPT[number].unranked then
return false
end
end
@@ -994,17 +996,23 @@ do-- function dumpBasicConfig()
end
end
do-- function resetGameData(args)
local function task_showMods()
local time=0
while true do
coroutine.yield()
if time%20==0 then
local M=GAME.mod[time/20+1]
if not M then return end
local function task_showMods() -- TODO
coroutine.yield()
local counter=0
for number,sel in next,GAME.mod do
if sel>0 then
if counter==0 then
coroutine.yield()
else
for _=1,20 do
coroutine.yield()
end
end
local M=MODOPT[number]
SFX.play('collect',.2)
TEXT.show(M.id,640+(time/20%5-2)*80,26,45,'spin')
TEXT.show(M.id,640+(counter%5-2)*80,26,45,'spin')
counter=counter+1
end
time=time+1
end
end
local gameSetting={
@@ -1064,6 +1072,7 @@ do-- function resetGameData(args)
else
PLY.newPlayer(1)
end
GAME.initPlayerCount=#PLAYERS
freshPlayerPosition((args:find'q') and 'quick' or 'normal')
VK.restore()
@@ -1126,6 +1135,14 @@ do-- function checkWarning(P,dt)
end
end
end
function usingMod()
for _,sel in next,GAME.mod do
if sel>0 then
return true
end
end
return false
end
@@ -1176,6 +1193,9 @@ function drawWarning()
gc_pop()
end
end
function setModBackgroundColor()
gc_setColor(.42,.26,.62,.62+.26*math.sin(TIME()*12.6))
end
@@ -1210,8 +1230,7 @@ do-- function pressKey(k)
return cache[k]
end
end
do-- CUS/SETXXX(k)
local CUSTOMENV=CUSTOMENV
do-- SETXXX(k) & ROOMXXX(k)
local warnList={
'das','arr','dascut','dropcut','sddas','sdarr',
'ihs','irs','ims','RS',
@@ -1219,13 +1238,10 @@ do-- CUS/SETXXX(k)
'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 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
end

View File

@@ -293,6 +293,7 @@ MODE_UPDATE_MAP={
round_3="round_l",
round_4="round_n",
round_5="round_u",
secret_grade="construct_sg",
solo_1="solo_e",
solo_2="solo_h",
solo_3="solo_l",
@@ -326,8 +327,8 @@ MODE_UPDATE_MAP={
tsd_hard="tsd_h",
tsd_ultimate="tsd_u",
GM="master_ex",
master_beginner="master_l",
master_advance="master_u",
master_beginner="master_n",
master_advance="master_h",
master_phantasm="master_ph",
master_extra="master_ex",
}
@@ -495,7 +496,7 @@ do-- Mod data
}
for i=1,#MODOPT do
local M=MODOPT[i]
M.sel,M.time=0,0
M.time=0
M.color=COLOR[M.color]
end
end
@@ -506,37 +507,38 @@ do-- Game data tables
BAG={}-- Sequence for custom game
MISSION={}-- Clearing mission for custom game
GAME={-- Global game data
playing=false, -- If in-game
init=false, -- If need initializing game when enter scene-play
net=false, -- If play net game
playing=false, -- If in-game
init=false, -- If need initializing game when enter scene-play
net=false, -- If play net game
result=false, -- Game result (string)
rank=0, -- Rank reached
pauseTime=0, -- Time paused
pauseCount=0, -- Pausing count
warnLVL0=0, -- Warning level
warnLVL=0, -- Warning level (show)
result=false, -- Game result (string)
rank=0, -- Rank reached
pauseTime=0, -- Time paused
pauseCount=0, -- Pausing count
warnLVL0=0, -- Warning level
warnLVL=0, -- Warning level (show)
seed=1046101471, -- Game seed
curMode=false, -- Current gamemode object
mod={}, -- List of loaded mods
modeEnv=false, -- Current gamemode environment
setting={}, -- Game settings
rep={}, -- Recording list, key,time,key,time...
statSaved=true, -- If recording saved
recording=false, -- If recording
replaying=false, -- If replaying
saved=false, -- If recording saved
tasUsed=false, -- If tasMode used
seed=1046101471, -- Game seed
curMode=false, -- Current gamemode object
initPlayerCount=0, -- Player count when init game
mod=TABLE.new(0,#MODOPT),-- List of loaded mods
modeEnv=false, -- Current gamemode environment
setting={}, -- Game settings
rep={}, -- Recording list, key,time,key,time...
statSaved=true, -- If recording saved
recording=false, -- If recording
replaying=false, -- If replaying
saved=false, -- If recording saved
tasUsed=false, -- If tasMode used
prevBG=false, -- Previous background, for restore BG when quit setting page
prevBG=false, -- Previous background, for restore BG when quit setting page
-- Data for royale mode
stage=false, -- Game stage
mostBadge=false, -- Most badge owner
secBadge=false, -- Second badge owner
mostDangerous=false,-- Most dangerous player
secDangerous=false, -- Second dangerous player
stage=false, -- Game stage
mostBadge=false, -- Most badge owner
secBadge=false, -- Second badge owner
mostDangerous=false, -- Most dangerous player
secDangerous=false, -- Second dangerous player
}
ROYALEDATA={
powerUp=false,
@@ -657,6 +659,9 @@ do-- Userdata tables
msaa=0,
bg='on',
bgAlpha=.26,
defaultBG='space',
lockBG=false,
noTheme=false,
powerInfo=false,
clickFX=true,
warn=true,

View File

@@ -1,5 +1,6 @@
-- Automatically generated by a Python script, from a markdown source file.
-- The script can be found here: https://github.com/user670/techmino-dictionary-converter/blob/master/tool.py
-- **NOTE TO TRANSLATORS**: In English (and in this file) the symbol ~ (tilde) is commonly used to express "approximately". Some languages use this symbol to express ranges or "from/up to a value", but it is not commonly used in English.
return {
{"Translator Note 1",
"",
@@ -827,7 +828,7 @@ FNNS and {"Support 3",
{"Finesse",
"finesse",
"term",
"A technique that moves a block to the desired location with the minimum number of keypresses. Finesse usually doesn't consider locations that require soft drops to move into. Finesses save time and reduce the chances of misdropping a block.\nFinesse is an essential skill in playing Tetris. We recommend that you start learning finesse as soon as possible. You can find videos about finesse on YouTube. Start by learning the basics and then start practicing, keeping accuracy as the first priority before speeding up.\nNote that in Techmino, finesses are not exactly \"the theoretically simplest keystroke sequence it takes to move a block to a particular location.\" Finesses are only detected when a piece locks into a position that does not require soft drops, so the finesses strikes are not reset after a spin or a tuck. Techmino also counts holding a block when it's the same block you're controlling and holding a block after manipulating it as finesse errors.\nIn Techmino, the finesse rate (%) is defined as 100% (Perfect) when the number of keypresses is equal to or less than the standard reference, 50% (Great) when one keypress is above the standard, 25% (Bad) when two keypresses are above the standard, and 0% (Miss) when three or more keypresses are above the standard. A Bad or Miss breaks the finesse strike.\nFinesse is usually not considered in conditions such as locations that require soft drops, high gravity, or very high fields. The finesse counter also works the same in Techmino regardless of gravity (even in 20G modes), so the result may not be meaningful in high gravity conditions.",
"A technique that moves a block to the desired location with the minimum number of keypresses. Finesse usually doesn't consider locations that require soft drops to move into. Finesses save time and reduce the chances of misdropping a block.\nFinesse is an essential skill in playing Tetris. We recommend that you start learning finesse as soon as possible. You can find videos about finesse on YouTube. Start by learning the basics and then start practicing, keeping accuracy as the first priority before speeding up.\nNote that in Techmino, finesses are not exactly \"the theoretically simplest keystroke sequence it takes to move a block to a particular location.\" Finesses are only detected when a piece locks into a position that does not require soft drops, so the finesses strikes are not reset after a spin or a tuck.\nIn Techmino, the finesse rate (%) is defined as 100% (Perfect) when the number of keypresses is equal to or less than the standard reference, 50% (Great) when one keypress is above the standard, 25% (Bad) when two keypresses above, and 0% (Miss) when three or more keypresses above. A Bad or Miss breaks the finesse strike.\nFinesse is usually not considered in conditions such as locations that require soft drops, high gravity, or very high fields. The finesse counter also works the same in Techmino regardless of gravity (even in 20G modes), so the result may not be meaningful in high gravity conditions.",
},
{"'Researching'",
"scientificresearching",

View File

@@ -924,7 +924,7 @@ FNNS and {"サポート3",
{"Finesse(最適化)",
"finesse 最適化 効率 操作 初心者 下級者 上達 練習",
"term",
"ミスなく最低限の操作で目的の場所にミノを移動させる技術\n速さが上がり、ミスも減ります\nJstrisの「restart on finesse error」やTechminoの最適化失敗音を有効にすることで練習できます\n厳密にはTechminoの最適化判定は「理論上の最小入力数」ではなく、「ソフトドロップを必要としない位置の時、一定回数以下の入力かどうか」で判定しています\nそのためソフトドロップが必要なspinなどの時は最適化の判定を行っていません\nまたホールドと操作ミノが同じ時にホールドをしたり、ホールドする前のミノを無駄に動かしても最適化が失敗しているとは判定されません\nまたTechminoの最適化率は一定回数以下の場合100%、一定回数より入力が1多い場合50%、入力が2多い場合25%、入力が3以上多い場合0%とされています\nまた0Gと20Gで最適化の算出方法を変えていないため落下速度が速い時は不正確なことに注意してください",
"ミスなく最低限の操作で目的の場所にミノを移動させる技術\n速さが上がり、ミスも減ります\nJstrisの「restart on finesse error」やTechminoの最適化失敗音を有効にすることで練習できます\n厳密にはTechminoの最適化判定は「理論上の最小入力数」ではなく、「ソフトドロップを必要としない位置の時、一定回数以下の入力かどうか」で判定しています\nそのためソフトドロップが必要なspinなどの時は最適化の判定を行っていません\nまたTechminoの最適化率は一定回数以下の場合100%、一定回数より入力が1多い場合50%、入力が2多い場合25%、入力が3以上多い場合0%とされています\nまた0Gと20Gで最適化の算出方法を変えていないため落下速度が速い時は不正確なことに注意してください",
},
{"Doing Research",
"scientificresearch 研究 上達 練習 科研",

File diff suppressed because it is too large Load Diff

View File

@@ -457,7 +457,7 @@ FNNS and {"赞助3",
{"HPC",
"hc clear banqing banquanqing banquanxiao",
"term",
"Half-PC\nPC全消的外延“下方有剩余方块” 的全消特别地如果只消1行则必须不剩余玩家放置的方块能打出一些攻击和防御Techmino限定",
"Half PC\nPC全消的外延“下方有剩余方块” 的全消特别地如果只消1行则必须不剩余玩家放置的方块能打出一些攻击和防御Techmino限定",
},
-- # 术语(旋转相关)
{"Spin",
@@ -816,7 +816,7 @@ FNNS and {"赞助3",
{"极简(Finesse)",
"极简操作 最简操作 finesse jijiancaozuo zuijiancaozuo",
"term",
"用最少的按键数将方块移到想去的位置的技术大多数时候只考虑纯硬降的落点节约时间和减少Misdrop。\n\n该技能学习越早越好,建议先去找教程视频,看懂了然后自己多练习,开始以准确率第一,速度快慢不重要,熟练后自然就快了。\n\n注意Techmino使用的极简判定系统不是说完全和理论最少操作数一样而是不需要软降就能达到的位置才会按照标准出块方向和你的按键次数执行极简检测故在此不像js存在软降后误杀。但是多了一些新的条件比如【手上和Hold一样/已经按了超过3次按键后再hold后按键次数不重置让下一块极简失误\n极简率计算:\n没有超过标准极简法操作数的为Perfect计100%超出一步为Great计50%超出两步为Bad计25%两步以上为Miss计0%其中Bad和Miss会断连\n\n注1一般说的极简不考虑带软降/高重力/场地很高的情况,仅研究空中移动/旋转后硬降。绝对理想的“极简”建议使用“最少按键数/操作数”表达。\n注220G下极简系统和0G一样工作所以得到的数值不准确参考价值偏低。",
"用最少的按键数将方块移到想去的位置的技术大多数时候只考虑纯硬降的落点节约时间和减少Misdrop。\n\n该技能学习越早越好,建议先去找教程视频,看懂了然后自己多练习,开始以准确率第一,速度快慢不重要,熟练后自然就快了。\n\n注意Techmino使用的极简判定系统不是说完全和理论最少操作数一样而是不需要软降就能达到的位置才会按照标准出块方向和你的按键次数执行极简检测故在此不像js存在软降后误杀。但是多了一些新的条件比如已经按了超过3次按键后再hold后按键次数不重置让下一块极简失误\n极简率计算:\n没有超过标准极简法操作数的为Perfect计100%超出一步为Great计50%超出两步为Bad计25%两步以上为Miss计0%其中Bad和Miss会断连\n\n注1一般说的极简不考虑带软降/高重力/场地很高的情况,仅研究空中移动/旋转后硬降。绝对理想的“极简”建议使用“最少按键数/操作数”表达。\n注220G下极简系统和0G一样工作所以得到的数值不准确参考价值偏低。",
},
{"科研",
"keyan",

View File

@@ -28,7 +28,7 @@ return {
clear={"Single","Double","Triple","Techrash","Pentacrash","Hexacrash","Heptacrash","Octacrash","Nonacrash","Decacrash","Undecacrash","Dodecacrash","Tridecacrash","Tetradecacrash","Pentadecacrash","Hexadecacrash","Heptadecacrash","Octadecacrash","Nonadecacrash","Ultracrash","Impossicrash"},
cleared="$1 lines",
mini="Mini",b2b="B2B ",b3b="B2B2B ",
PC="Perfect Clear",HPC="Hemi-Perfect Clear",
PC="Perfect Clear",HPC="Half PC",
replaying="[Replay]",
tasUsing="[TAS]",
@@ -43,6 +43,7 @@ return {
infHeightOn="Infinite Height ON",
infHeightOff="Infinite Height OFF",
infHeightHint="Toggle with Function 1 key",
highest="(highest: $1)",
speedLV="Speed Level",
piece="Piece",line="Lines",atk="Attack",eff="Efficiency",
@@ -54,7 +55,7 @@ return {
win="Win!",
lose="Lose",
torikan="Ended",
finish="Finished",
gamewin="You Won",
gameover="Game Over",
@@ -66,10 +67,13 @@ return {
page="Page ",
ai_puzzle="AI is incompatible with puzzle game mode",
ai_mission="AI is incompatible with custom missions.",
ai_badPiece="AI is incompatible with custom sequences that contain non-tetrominoes.",
cc_fixed="CC is incompatible with fixed sequences.",
cc_swap="CC is incompatible when the hold mode is set to Swap.",
ai_prebag="AI is incompatible with custom sequences that contain non-tetrominoes.",
ai_mission="AI is incompatible with custom missions.",
cc_solid="CC is incompatible with filled line in the field.",
cc_field_too_high="CC is incompatible with fields higher than 40.",
switchSpawnSFX="Please turn on the block spawn SFX!",
needRestart="Restart to apply all changes.",
@@ -91,6 +95,8 @@ return {
dataCorrupted="Data corrupted",
pasteWrongPlace="Did you paste in the wrong place?",
noFile="File missing",
invalidSequence="Invalid sequence mode",
tooHighField="Field data exceeded 126 lines discarded",
nowPlaying="Now playing:",
@@ -233,7 +239,7 @@ return {
"Pieces:",
"Row/Dig:",
"Attack/DigAtk:",
"Received:",
"Rise(Receive-Offset):",
"Clears:",
"Spins:",
"B2B/B3B/PC/HPC:",
@@ -247,7 +253,7 @@ return {
"Play Time:",
"Key/Rot./Hold:",
"Block/Row/Atk.:",
"Recv./Res./Asc.:",
"Receive/Offset/Rise:",
"Dig/Dig Atk.:",
"Eff./Dig Eff.:",
"B2B/B3B:",
@@ -329,6 +335,7 @@ return {
"Miya",
"Xiaoya",
"Mono",
"Flore",
"MrZ",
"Trebor",
"",
@@ -412,7 +419,7 @@ B. Keyboard
C. Gamepad
- Press $10 to display Help
- Press $5 or $6 to scroll through the text, speed up with $11
- Press $5 or $6 to scroll through the text
- Press $7 to open the previous entry and $8 to open the next entry. Speed up with $11
- Hold $11 and press $6 to decrease the font size or $5 to increase
]]
@@ -498,7 +505,7 @@ C. Gamepad
capacity="Capacity",
create="Create",
ospin="O-spin",
ospin="TRS O-spin",
fineKill="100% Finesse",
b2bKill="No B2B Breaks",
lockout="Fail on Lock Out",
@@ -506,7 +513,7 @@ C. Gamepad
deepDrop="Deep Drop",
bone="Bone Blocks",
eventSet="Rule Set",
eventSet="Ruleset",
holdMode="Hold Mode",
nextCount="Next",
@@ -580,7 +587,11 @@ C. Gamepad
bg_on="Normal B.G.",
bg_off="No B.G.",
bg_custom="Use Custom B.G.",
bg_custom="Custom B.G.",
defaultBG="Default B.G.",
resetDbg="Reset to default",
lockBG="Lock B.G.",
noTheme="Disable theme",
blockSatur="Block Saturation",
fieldSatur="Field Saturation",
@@ -698,7 +709,7 @@ C. Gamepad
sequence="Edit Sequences (S)",
mission="Edit Missions (M)",
eventSet="Rule Set",
eventSet="Ruleset",
holdMode="Hold Mode",
nextCount="Next",
@@ -716,7 +727,7 @@ C. Gamepad
bufferLimit="Buffer Limit",
heightLimit="Height Limit",
ospin="O-Spin",
ospin="TRS O-Spin",
fineKill="100% Finesse",
b2bKill="No B2B Breaks",
lockout="Fail on Lock Out",
@@ -866,7 +877,9 @@ C. Gamepad
['sprintPenta']= {"Sprint", "PENTOMINO", "40L with the 18 pentominoes"},
['sprintMPH']= {"Sprint", "MPH", "Memoryless\nPreviewless\nHoldless"},
['sprint123']= {"Sprint", "M123", "40L with only monominoes, dominoes, and triminoes"},
['secret_grade']= {"Secret Grade", "", "Building a zigzag shape by following the guide!"},
['construct_sg']= {"Construct", "SECRET GRADE", "Build a zigzag pattern by following the guide!"},
['construct_checker']= {"Construct", "CHECKERBOARD", "Build a checkerboard pattern!"},
['construct_invsg']= {"Construct", "INV. SG", "Build an inverted zigzag pattern!"},
['dig_10l']= {"Dig", "10L", "Dig 10 garbage lines as fast as you can!"},
['dig_40l']= {"Dig", "40L", "Dig 40 garbage lines as fast as you can!"},
['dig_100l']= {"Dig", "100L", "Dig 100 garbage lines as fast as you can!"},
@@ -885,12 +898,12 @@ C. Gamepad
['solo_h']= {"Battle", "HARD", "Defeat the AI!"},
['solo_l']= {"Battle", "LUNATIC", "Defeat the AI!"},
['solo_u']= {"Battle", "ULTIMATE", "Defeat the AI!"},
['techmino49_e']= {"Tech 49", "EASY", "49-player battle.\nThe last one standing wins"},
['techmino49_h']= {"Tech 49", "HARD", "49-player battle.\nThe last one standing wins"},
['techmino49_u']= {"Tech 49", "ULTIMATE", "49-player battle.\nThe last one standing wins"},
['techmino99_e']= {"Tech 99", "EASY", "99-player battle.\nThe last one standing wins"},
['techmino99_h']= {"Tech 99", "HARD", "99-player battle.\nThe last one standing wins"},
['techmino99_u']= {"Tech 99", "ULTIMATE", "99-player battle.\nThe last one standing wins"},
['techmino49_e']= {"Tech VS 49", "EASY", "49-player battle.\nThe last one standing wins"},
['techmino49_h']= {"Tech VS 49", "HARD", "49-player battle.\nThe last one standing wins"},
['techmino49_u']= {"Tech VS 49", "ULTIMATE", "49-player battle.\nThe last one standing wins"},
['techmino99_e']= {"Tech VS 99", "EASY", "99-player battle.\nThe last one standing wins"},
['techmino99_h']= {"Tech VS 99", "HARD", "99-player battle.\nThe last one standing wins"},
['techmino99_u']= {"Tech VS 99", "ULTIMATE", "99-player battle.\nThe last one standing wins"},
['round_e']= {"Turn-Based", "EASY", "Take turns to play against the AI!"},
['round_n']= {"Turn-Based", "NORMAL", "Take turns to play against the AI!"},
['round_h']= {"Turn-Based", "HARD", "Take turns to play against the AI!"},
@@ -929,8 +942,8 @@ C. Gamepad
['survivor_u']= {"Survival", "ULTIMATE", "How long can you survive?"},
['attacker_h']= {"Attacker", "HARD", "Practice your attacking skills!"},
['attacker_u']= {"Attacker", "ULTIMATE", "Practice your attacking skills!"},
['defender_n']= {"Defender", "NORMAL", "Practice your defensing skills!"},
['defender_l']= {"Defender", "LUNATIC", "Practice your defensing skills!"},
['defender_n']= {"Defender", "NORMAL", "Practice your defending skills!"},
['defender_l']= {"Defender", "LUNATIC", "Practice your defending skills!"},
['dig_h']= {"Driller", "HARD", "Digging practice!"},
['dig_u']= {"Driller", "ULTIMATE", "Digging practice!"},
['c4wtrain_n']= {"C4W Training", "NORMAL", "Infinite combos"},
@@ -941,16 +954,14 @@ C. Gamepad
['pc_h']= {"PC Challenge", "HARD", "Get PCs within 100 lines!"},
['pc_l']= {"PC Challenge", "LUNATIC", "Get PCs within 100 lines!"},
['pc_inf']= {"Inf. PC Challenge", "", "Get PCs as much as you can"},
['tech_n']= {"Tech", "NORMAL", "Try to keep the\nBack-to-Back chain!"},
['tech_n_plus']= {"Tech", "NORMAL+", "Spins & PCs only"},
['tech_h']= {"Tech", "HARD", "Try to keep the\nBack-to-Back chain!"},
['tech_h_plus']= {"Tech", "HARD+", "Spins & PCs only"},
['tech_l']= {"Tech", "LUNATIC", "Try to keep the\nBack-to-Back chain!"},
['tech_l_plus']= {"Tech", "LUNATIC+", "Spins & PCs only"},
['tech_finesse']= {"Tech", "FINESSE", "No finesse errors!"},
['tech_finesse_f']= {"Tech", "FINESSE+", "No normal clears and finesse errors!"},
['tech_finesse_lock']= {"Tech", "FINESSE LOCK", "No finesse errors, combined with limited inputs!"},
['tech_finesse_lock_f']= {"Tech", "FINESSE+ LOCK", "No normal clears or finesse errors combined with limited inputs!"},
['tech_n']= {"Tech B2B", "NORMAL", "Try to keep the\nBack-to-Back chain!"},
['tech_n_plus']= {"Tech B2B", "NORMAL+", "Spins & PCs only"},
['tech_h']= {"Tech B2B", "HARD", "Try to keep the\nBack-to-Back chain!"},
['tech_h_plus']= {"Tech B2B", "HARD+", "Spins & PCs only"},
['tech_l']= {"Tech B2B", "LUNATIC", "Try to keep the\nBack-to-Back chain!"},
['tech_l_plus']= {"Tech B2B", "LUNATIC+", "Spins & PCs only"},
['tech_finesse']= {"Tech Finesse", "", "No finesse errors!"},
['tech_finesse_f']= {"Tech Finesse", "PLUS", "No normal clears and finesse errors!"},
['tsd_e']= {"TSD Challenge", "EASY", "T-Spin Doubles only!"},
['tsd_h']= {"TSD Challenge", "HARD", "T-Spin Doubles only!"},
['tsd_u']= {"TSD Challenge", "ULTIMATE", "T-Spin Doubles only!"},

View File

@@ -27,7 +27,7 @@ return {
clear={"Single","Doble","Triple","Techrash","Pentacrash","Hexacrash","Heptacrash","Octacrash","Nonacrash","Decacrash","Undecacrash","Dodecacrash","Tridecacrash","Tetradecacrash","Pentadecacrash","Hexadecacrash","Heptadecacrash","Octadecacrash","Nonadecacrash","Ultracrash","Impossicrash"},
cleared="$1 líneas",
mini="Mini",b2b="B2B ",b3b="B2B2B ",
PC="Perfect Clear",HPC="Half Clear",
PC="Perfect Clear",HPC="Half PC",
replaying="[Repetición]",
tasUsing="[TAS]",
@@ -42,6 +42,7 @@ return {
-- infHeightOn="Infinite Height ON",
-- infHeightOff="Infinite Height OFF",
-- infHeightHint="Toggle with Function 1 key",
-- highestGrade="(highest: $1)",
speedLV="Veloc. de juego",
piece="Piezas",line="Líneas",atk="Ataque",eff="Eficiencia",
@@ -53,7 +54,7 @@ return {
win="¡Victoria!",
lose="Derrota",
--torikan="Ended",
finish="Finalizado",
gamewin="Has ganado",
gameover="Fin del Juego",
@@ -65,10 +66,13 @@ return {
page="Página:",
-- ai_puzzle="AI is incompatible with puzzle game mode",
ai_mission="La IA no es compatible con misiones personalizadas.",
ai_badPiece="La IA no es compatible con piezas que no sean Tetrominos.",
cc_fixed="CC no es compatible con piezas prefijadas",
cc_swap="CC no es compatible con Swap Hold",
ai_prebag="La IA no es compatible con piezas que no sean Tetrominos.",
ai_mission="La IA no es compatible con misiones personalizadas.",
-- cc_solid="CC is incompatible with filled line in the field.",
-- cc_field_too_high="CC is incompatible with fields higher than 40.",
switchSpawnSFX="Habilita los sonidos de aparición de las piezas ;)",
needRestart="Reinicia para aplicar los cambios.",
@@ -90,6 +94,8 @@ return {
dataCorrupted="Los datos están corruptos.",
pasteWrongPlace="¿Pegaste en la carpeta correcta?",
noFile="Archivo no encontrado",
-- invalidSequence="Invalid sequence mode",
-- tooHighField="Field data exceeded 126 lines discarded",
nowPlaying="Reproduciendo:",
@@ -232,7 +238,7 @@ return {
"Piezas:",
"Altura/Limpiado:",
"Ataque Enviado:",
"Ataque Recibido:",
"Ataque Recibido:",-- "Rise(Receive-Offset):",
"Líneas Limpiadas:",
"Spins:",
"B2B/B3B/PC/HPC:",
@@ -246,7 +252,7 @@ return {
"Tiempo de Juego:",
"Tecla/Rot./Reserva:",
"Bloq./Lín./Atq.:",
"Rec./Off./Sub.:",
"Rec./Off./Sub.:",-- "Receive/Offset/Rise:",
"Limp./Atq. Limp.:",
"Effic./Eff.Limp.:",
"Slam/Gr.Slam:",
@@ -326,6 +332,7 @@ return {
"Miya",
"Xiaoya",
"Mono",
"Flore",
"MrZ",
"Trebor",
"",
@@ -459,7 +466,7 @@ return {
capacity="Capacidad",
create="Crear",
ospin="O-Spin",
ospin="TRS O-Spin",
fineKill="100% Finesse",
b2bKill="No Romper B2B ",
lockout="Derrota al ocurrir Lock Out",
@@ -542,6 +549,10 @@ return {
bg_on="Fondo Normal",
bg_off="Sin Fondo",
bg_custom="Fondo Personalizado",
-- defaultBG="Default B.G.",
-- resetDbg="Reset to default",
-- lockBG="Lock B.G.",
-- noTheme="Disable theme",
blockSatur="Saturac. de los Bloques",
fieldSatur="Saturac. del Tablero",
@@ -676,7 +687,7 @@ return {
bufferLimit="Búfer Límite",
heightLimit="Altura Límite",
ospin="O-Spin",
ospin="TRS O-Spin",
fineKill="100% Finesse",
b2bKill="No Romper B2B ",
lockout="Derrota al ocurrir Lock Out",
@@ -818,7 +829,9 @@ return {
['sprintPenta']= {"Sprint", "Pentominos", "¡Limpia 40 líneas con los 18 pentominos distintos!"},
['sprintMPH']= {"Sprint", "MPH", "Memoryless (sin memoria)\nPreviewless (sin pzas. siguientes)\nHoldless (sin reserva)."},
['sprint123']= {"Sprint", "M123", "Limpia 40 líneas con monominos, biminos y triminos"},
['secret_grade']= {"Secret Grade", "", "¡Arma dejando huecos en escalera, sigue la guía!"},
['construct_sg']= {"Construir", "SECRET GRADE", "¡Arma dejando huecos en escalera, sigue la guía!"},
-- ['construct_checker']= {"Construct", "CHECKERBOARD", "Build a checkerboard pattern!"},
-- ['construct_invsg']= {"Construct", "INV. SG", "Build an inverted zigzag pattern!"},
['dig_10l']= {"Dig", "10L", "¡Limpia 10 líneas de queso lo más rápido que puedas!"},
['dig_40l']= {"Dig", "40L", "¡Limpia 40 líneas de queso lo más rápido que puedas!"},
['dig_100l']= {"Dig", "100L", "¡Limpia 100 líneas de queso lo más rápido que puedas!"},
@@ -837,12 +850,18 @@ return {
['solo_h']= {"VS.", "Difícil", "¡Derrota a la CPU!"},
['solo_l']= {"VS.", "Lunático", "¡Derrota a la CPU!"},
['solo_u']= {"VS.", "Supremo", "¡Derrota a la CPU!"},
['techmino49_e']= {"Tech 49", "Fácil", "Batalla de 49 jugadores.\n¡El último en pie gana!"},
['techmino49_h']= {"Tech 49", "Difícil", "Batalla de 49 jugadores.\n¡El último en pie gana!"},
['techmino49_u']= {"Tech 49", "Supremo", "Batalla de 49 jugadores.\n¡El último en pie gana!"},
['techmino99_e']= {"Tech 99", "Fácil", "Batalla de 99 jugadores.\n¡El último en pie gana!"},
['techmino99_h']= {"Tech 99", "Difícil", "Batalla de 99 jugadores.\n¡El último en pie gana!"},
['techmino99_u']= {"Tech 99", "Supremo", "Batalla de 99 jugadores.\n¡El último en pie gana!"},
-- ['techmino49_e']= {"Tech VS 49", "EASY", "49-player battle.\nThe last one standing wins"},
-- ['techmino49_h']= {"Tech VS 49", "HARD", "49-player battle.\nThe last one standing wins"},
-- ['techmino49_u']= {"Tech VS 49", "ULTIMATE", "49-player battle.\nThe last one standing wins"},
-- ['techmino99_e']= {"Tech VS 99", "EASY", "99-player battle.\nThe last one standing wins"},
-- ['techmino99_h']= {"Tech VS 99", "HARD", "99-player battle.\nThe last one standing wins"},
-- ['techmino99_u']= {"Tech VS 99", "ULTIMATE", "99-player battle.\nThe last one standing wins"},
['techmino49_e']= {"Tech VS 49", "Fácil", "Batalla de 49 jugadores.\n¡El último en pie gana!"},
['techmino49_h']= {"Tech VS 49", "Difícil", "Batalla de 49 jugadores.\n¡El último en pie gana!"},
['techmino49_u']= {"Tech VS 49", "Supremo", "Batalla de 49 jugadores.\n¡El último en pie gana!"},
['techmino99_e']= {"Tech VS 99", "Fácil", "Batalla de 99 jugadores.\n¡El último en pie gana!"},
['techmino99_h']= {"Tech VS 99", "Difícil", "Batalla de 99 jugadores.\n¡El último en pie gana!"},
['techmino99_u']= {"Tech VS 99", "Supremo", "Batalla de 99 jugadores.\n¡El último en pie gana!"},
['round_e']= {"Por Turnos", "Fácil", "Modo ajedrez."},
['round_n']= {"Por Turnos", "Normal", "Modo ajedrez."},
['round_h']= {"Por Turnos", "Difícil", "Modo ajedrez."},
@@ -869,7 +888,7 @@ return {
-- ['blind_h']= {"Invisible", "INSTANT", "For the experienced"},
-- ['blind_l']= {"Invisible", "NO GHOST", "For professionals"},
-- ['blind_u']= {"Invisible", "NO FIELD", "Are you ready?"},
-- ['blind_wtf']= {"Invisible", "VOID", "You're not ready."},
-- ['blind_wtf']= {"Invisible", "VOID", "You're not ready."},
['blind_e']= {"A Ciegas", "Parcial", "Para novatos."},
['blind_n']= {"A Ciegas", "Total", "Para jugadores intermedios."},
['blind_h']= {"A Ciegas", "Inmediato", "Para jugadores experimentados."},
@@ -899,16 +918,15 @@ return {
['pc_h']= {"Desafío de PCs", "Difícil", "¡Consigue los PCs que puedas en 100 líneas!"},
['pc_l']= {"Desafío de PCs", "Lunático", "¡Consigue los PCs que puedas en 100 líneas!"},
['pc_inf']= {"Desafío de PCs Infinito", "", "Consigue todos los PCs que puedas."},
['tech_n']= {"Tech", "Normal", "¡Mantén el B2B!"},
['tech_n_plus']= {"Tech", "Normal+", "¡Sólo se permiten Spins y PCs!"},
['tech_h']= {"Tech", "Difícil", "¡Mantén el B2B!"},
['tech_h_plus']= {"Tech", "Difícil+", "¡Sólo se permiten Spins y PCs!"},
['tech_l']= {"Tech", "Lunático", "¡Mantén el B2B!"},
['tech_l_plus']= {"Tech", "Lunático+", "¡Sólo se permiten Spins y PCs!"},
['tech_n']= {"Tech B2B", "Normal", "¡Mantén el B2B!"},
['tech_n_plus']= {"Tech B2B", "Normal+", "¡Sólo se permiten Spins y PCs!"},
['tech_h']= {"Tech B2B", "Difícil", "¡Mantén el B2B!"},
['tech_h_plus']= {"Tech B2B", "Difícil+", "¡Sólo se permiten Spins y PCs!"},
['tech_l']= {"Tech B2B", "Lunático", "¡Mantén el B2B!"},
['tech_l_plus']= {"Tech B2B", "Lunático+", "¡Sólo se permiten Spins y PCs!"},
['tech_finesse']= {"Tech", "Finesse", "¡No cometas errores de Finesse!"},
-- ['tech_finesse_f']= {"Tech Finesse", "PLUS", "No normal clears and finesse errors!"},
['tech_finesse_f']= {"Tech", "Finesse+", "Sin errores de finesse,\n¡pero tampoco clears normales!"},
-- ['tech_finesse_lock']= {"Tech", "FINESSE LOCK", "No finesse errors, combined with limited inputs!"},
-- ['tech_finesse_lock_f']= {"Tech", "FINESSE+ LOCK", "No normal clears or finesse errors combined with limited inputs!"},
['tsd_e']= {"Desafío de TSD", "Fácil", "¡Sólo se permiten T-Spin Dobles!"},
['tsd_h']= {"Desafío de TSD", "Difícil", "¡Sólo se permiten T-Spin Dobles!"},
['tsd_u']= {"Desafío de TSD", "Supremo", "¡Sólo se permiten T-Spin Dobles!"},

View File

@@ -27,7 +27,7 @@ return {
clear={"Simple","Double","Triple","Techrash","Pentacrash","Hexacrash","Heptacrash","Octacrash","Nonacrash","Decacrash","Undecacrash","Dodecacrash","Tridecacrash","Tetradecacrash","Pentadecacrash","Hexadecacrash","Heptadecacrash","Octadecacrash","Nonadecacrash","Ultracrash","Impossicrash"},
-- cleared="$1 lines",
mini="Mini",b2b="B2B ",b3b="B2B2B ",
PC="Perfect Clear",HPC="Clear",
PC="Perfect Clear",HPC="Half PC",
replaying="[Replay]",
-- tasUsing="[TAS]",
@@ -42,6 +42,7 @@ return {
-- infHeightOn="Infinite Height ON",
-- infHeightOff="Infinite Height OFF",
-- infHeightHint="Toggle with Function 1 key",
-- highestGrade="(highest: $1)",
speedLV="niveau de vitesse",
piece="Pièce",line="Lignes",atk="Attaque",eff="Efficacité",
@@ -54,7 +55,7 @@ return {
win="Victoire!",
lose="Défaite",
--torikan="Ended",
finish="Terminé",
gamewin="Gagné !",
gameover="Fin du jeu",
@@ -66,10 +67,13 @@ return {
page="Page:",
-- ai_puzzle="AI is incompatible with puzzle game mode",
ai_mission="L'IA est incompatible avec les missions personnalisées.",
-- ai_badPiece="The AI is incompatible with custom sequences which have nontetromino.",'IA est incompatible avec les séquences personnalisées.",
cc_fixed="CC est incompatible avec les séquences fixes",
cc_swap="CC est incompatible avec le mode de maintien du swap",
-- ai_prebag="The AI is incompatible with custom sequences which have nontetromino.",'IA est incompatible avec les séquences personnalisées.",
ai_mission="L'IA est incompatible avec les missions personnalisées.",
-- cc_solid="CC is incompatible with filled line in the field.",
-- cc_field_too_high="CC is incompatible with fields higher than 40.",
switchSpawnSFX="Activez les effets sonores d'apparition des pièces pour jouer.",
needRestart="Redémarrez pour appliquer toutes les modifications.",
@@ -91,6 +95,8 @@ return {
dataCorrupted="Données corrompues",
-- pasteWrongPlace="Paste at wrong place?",
noFile="Fichier non trouvé",
-- invalidSequence="Invalid sequence mode",
-- tooHighField="Field data exceeded 126 lines discarded",
nowPlaying="En train de jouer :",
@@ -211,7 +217,7 @@ return {
"Pièce :",
"Lignes/Creuser :",
"Attaque/Attaque de creusage :",
"Reçu :",
"Reçu :",-- "Rise(Receive-Offset):",
"Nettoyages :",
"Spins :",
"B2B/B3B/PC/HPC : ",
@@ -225,7 +231,7 @@ return {
"Temps de jeu :",
"Touche/Rot./Réserve :",
"Bloc/Ligne/Atq. :",
"Reçu/Res./Asc. :",
"Reçu/Res./Asc. :",-- "Receive/Offset/Rise:",
"Attaque/Attaque de creusage :",
"Eff./Eff. de creusage :",
"B2B/B3B :",
@@ -304,6 +310,7 @@ return {
"Miya",
"Xiaoya",
"Mono",
"Flore",
"MrZ",
"Trebor",
"",
@@ -434,7 +441,7 @@ return {
-- capacity="Capacity",
create="Créer",
ospin="O-spin",
ospin="TRS O-spin",
fineKill="100% Finesse",
b2bKill="Sans perte de B2B",
-- lockout="Fail when lock out",
@@ -442,7 +449,7 @@ return {
-- deepDrop="Deep Drop",
bone="Crochets",
-- eventSet="Rule Set",
-- eventSet="Ruleset",
-- holdMode="Hold Mode",
nextCount="Prévisualisations de pièces",
@@ -518,6 +525,10 @@ return {
bg_on="Arrière-plan normal",
bg_off="Pas d'arrière-plan.",
bg_custom="Utiliser un arrière-plan personnalisé",
-- defaultBG="Default B.G.",
-- resetDbg="Reset to default",
-- lockBG="Lock B.G.",
-- noTheme="Disable theme",
-- blockSatur="Block Saturation",
-- fieldSatur="Field Saturation",
@@ -655,7 +666,7 @@ return {
-- bufferLimit="Buffer Limit",
-- heightLimit="Height Limit",
ospin="O-spin",
ospin="TRS O-spin",
fineKill="100% Finesse",
b2bKill="Sans perte de B2B",
-- lockout="Fail when lock out",
@@ -796,6 +807,10 @@ return {
['sprint_1000l']= {"Sprint", "1000L", "Nettoyez 1000 lignes !"},
['sprintPenta']= {"Sprint", "Pentomino", "40 lignes avec 18 pentominos."},
['sprintMPH']= {"Sprint", "MPH", "Memoryless\nPreviewless\nHoldless"},
-- ['sprint123']= {"Sprint", "M123", "40L with only monominoes, dominoes, and triminoes"},
-- ['construct_sg']= {"Construct", "SECRET GRADE", "Build a zigzag shape by following the guide!"},
-- ['construct_checker']= {"Construct", "CHECKERBOARD", "Build a checkerboard pattern!"},
-- ['construct_invsg']= {"Construct", "INV. SG", "Build an inverted zigzag pattern!"},
['dig_10l']= {"Dig", "10L", "Creusez 10 lignes"},
['dig_40l']= {"Dig", "40L", "Creusez 40 lignes"},
['dig_100l']= {"Dig", "100L", "Creusez 100 lignes"},
@@ -812,12 +827,18 @@ return {
['solo_h']= {"Battle", "DIFFICILE", "Battez l'IA !"},
['solo_l']= {"Battle", "LUNATIQUE", "Battez l'IA !"},
['solo_u']= {"Battle", "ULTIME", "Battez l'IA !"},
['techmino49_e']= {"Tech 49", "FACILE", "Bataille de 49 joueurs.\nLe dernier en vie gagne."},
['techmino49_h']= {"Tech 49", "DIFFICILE", "Bataille de 49 joueurs.\nLe dernier en vie gagne."},
['techmino49_u']= {"Tech 49", "ULTIME", "Bataille de 49 joueurs.\nLe dernier en vie gagne."},
['techmino99_e']= {"Tech 99", "FACILE", "Bataille de 99 joueurs.\nLe dernier en vie gagne."},
['techmino99_h']= {"Tech 99", "DIFFICILE", "Bataille de 99 joueurs.\nLe dernier en vie gagne."},
['techmino99_u']= {"Tech 99", "ULTIME", "Bataille de 99 joueurs.\nLe dernier en vie gagne."},
-- ['techmino49_e']= {"Tech VS 49", "EASY", "49-player battle.\nThe last one standing wins"},
-- ['techmino49_h']= {"Tech VS 49", "HARD", "49-player battle.\nThe last one standing wins"},
-- ['techmino49_u']= {"Tech VS 49", "ULTIMATE", "49-player battle.\nThe last one standing wins"},
-- ['techmino99_e']= {"Tech VS 99", "EASY", "99-player battle.\nThe last one standing wins"},
-- ['techmino99_h']= {"Tech VS 99", "HARD", "99-player battle.\nThe last one standing wins"},
-- ['techmino99_u']= {"Tech VS 99", "ULTIMATE", "99-player battle.\nThe last one standing wins"},
['techmino49_e']= {"Tech VS 49", "FACILE", "Bataille de 49 joueurs.\nLe dernier en vie gagne."},
['techmino49_h']= {"Tech VS 49", "DIFFICILE", "Bataille de 49 joueurs.\nLe dernier en vie gagne."},
['techmino49_u']= {"Tech VS 49", "ULTIME", "Bataille de 49 joueurs.\nLe dernier en vie gagne."},
['techmino99_e']= {"Tech VS 99", "FACILE", "Bataille de 99 joueurs.\nLe dernier en vie gagne."},
['techmino99_h']= {"Tech VS 99", "DIFFICILE", "Bataille de 99 joueurs.\nLe dernier en vie gagne."},
['techmino99_u']= {"Tech VS 99", "ULTIME", "Bataille de 99 joueurs.\nLe dernier en vie gagne."},
['round_e']= {"Tour à tour", "FACILE", "Mode échecs"},
['round_n']= {"Tour à tour", "NORMAL", "Mode échecs"},
['round_h']= {"Tour à tour", "DIFFICILE", "Mode échecs"},
@@ -866,20 +887,20 @@ return {
['c4wtrain_l']= {"Mode essai C4W", "LUNATIQUE", "Combos infinis."},
['pctrain_n']= {"Mode essai PC", "NORMAL", "Mode Perfect Clear simple"},
['pctrain_l']= {"Mode essai PC", "LUNATIQUE", "Mode Perfect Clear dur"},
['pc_n']= {"Défi PC", "NORMAL", "Obtenez un PC dans les prochaines 100 lignes !"},
['pc_h']= {"Défi PC", "DIFFICILE", "Obtenez un PC dans les prochaines 100 lignes !"},
['pc_l']= {"Défi PC", "LUNATIQUE", "Obtenez un PC dans les prochaines 100 lignes !"},
['pc_inf']= {"Défi PC infini","Infini", "Obtenez autant de PC que possible"},
['tech_n']= {"Tech", "NORMAL", "Gardez le B2B !"},
['tech_n_plus']= {"Tech", "NORMAL+", "Spin & PC uniquement"},
['tech_h']= {"Tech", "DIFFICILE", "Gardez le B2B !"},
['tech_h_plus']= {"Tech", "HARD+", "Spin & PC uniquement"},
['tech_l']= {"Tech", "LUNATIQUE", "Gardez le B2B !"},
['tech_l_plus']= {"Tech", "LUNATIQUE+", "Spin & PC uniquement"},
['pc_n']= {"Défi PC", "NORMAL", "Obtenez un PC dans les prochaines 100 lignes !"},
['pc_h']= {"Défi PC", "DIFFICILE", "Obtenez un PC dans les prochaines 100 lignes !"},
['pc_l']= {"Défi PC", "LUNATIQUE", "Obtenez un PC dans les prochaines 100 lignes !"},
['pc_inf']= {"Défi PC infini", "INFINI", "Obtenez autant de PC que possible"},
['tech_n']= {"Tech B2B", "NORMAL", "Gardez le B2B !"},
['tech_n_plus']= {"Tech B2B", "NORMAL+", "Spin & PC uniquement"},
['tech_h']= {"Tech B2B", "DIFFICILE", "Gardez le B2B !"},
['tech_h_plus']= {"Tech B2B", "HARD+", "Spin & PC uniquement"},
['tech_l']= {"Tech B2B", "LUNATIQUE", "Gardez le B2B !"},
['tech_l_plus']= {"Tech B2B", "LUNATIQUE+", "Spin & PC uniquement"},
-- ['tech_finesse']= {"Tech Finesse", "", "No finesse errors!"},
-- ['tech_finesse_f']= {"Tech Finesse", "PLUS", "No normal clears and finesse errors!"},
['tech_finesse']= {"Tech", "FINESSE", "Pas d'erreurs de finesse !"},
['tech_finesse_f']={"Tech", "FINESSE+", "Pas de nettoyages normaux,\nPas d'erreurs de finesse !"},
--['tech_finesse_lock']= {"Tech", "FINESSE LOCK", "No finesse errors, combined with limited inputs!"},
--['tech_finesse_lock_f']= {"Tech", "FINESSE+ LOCK", "No normal clears or finesse errors combined with limited inputs!"},
['tsd_e']= {"TSD Challenge", "FACILE", "T-spin doubles uniquement !"},
['tsd_h']= {"TSD Challenge", "DIFFICILE", "T-spin doubles uniquement !"},
['tsd_u']= {"TSD Challenge", "ULTIME", "T-spin doubles uniquement !"},

View File

@@ -44,6 +44,7 @@ return {
infHeightOn="Ketinggian Tak Terhingga ON",
infHeightOff="Ketinggian Tak Terhingga OFF",
infHeightHint="Ubah dengan tombol Fungsi 1",
highestGrade="(tertinggi: $1)",
speedLV="Kecptn lvl",
piece="Blok",line="Baris",atk="Baris Terkirim",eff="Efisiensi",
@@ -55,7 +56,7 @@ return {
win="Menang!",
lose="Kalah",
torikan="Tamat",
finish="Selesai",
gamewin="Anda menang!",
gameover="Tamat",
@@ -67,10 +68,13 @@ return {
page="Halaman: ",
-- ai_puzzle="AI is incompatible with puzzle game mode",
ai_mission="AI tidak cocok dengan misi terubah.",
ai_badPiece="AI tidak cocok dengan urutan terubah yang memiliki non-tetromino.",
cc_fixed="CC tidak cocok dengan urutan tetap.",
cc_swap="CC tidak cocok dengan mode simpan tukar.",
ai_prebag="AI tidak cocok dengan urutan terubah yang memiliki non-tetromino.",
ai_mission="AI tidak cocok dengan misi terubah.",
-- cc_solid="CC is incompatible with filled line in the field.",
-- cc_field_too_high="CC is incompatible with fields higher than 40.",
switchSpawnSFX="Nyalakan efek suara munculan blok!",
needRestart="Ulangi untuk menerapkan perubahan.",
@@ -92,6 +96,8 @@ return {
dataCorrupted="Data rusak",
pasteWrongPlace="Apakah Anda menempelkannya di tempat yang salah?",
noFile="File tidak ada",
-- invalidSequence="Invalid sequence mode",
-- tooHighField="Field data exceeded 126 lines discarded",
nowPlaying="Musik:",
@@ -233,7 +239,7 @@ return {
"Blok:",
"Baris/Baris Gali:",
"Serangan/Serangan Gali:",
"Diterima:",
"Diterima:",-- "Rise(Receive-Offset):",
"Baris:",
"Spins:",
"B2B/B3B/PC/Setengah PC:",
@@ -247,7 +253,7 @@ return {
"Jumlah Waktu Bermain:",
"Tombol/Putar/Simpan:",
"Blok/Baris/Serangan:",
"Serangan Diterima/Ditolak/Muncul:",
"Serangan Diterima/Ditolak/Muncul:",-- "Receive/Offset/Rise:",
"Gali/Serangan Gali:",
"Efisiensi/Efisiensi Gali:",
"B2B/B2B2B:",
@@ -327,6 +333,7 @@ return {
"Miya",
"Xiaoya",
"Mono",
"Flore",
"MrZ",
"Trebor",
"",
@@ -460,7 +467,7 @@ return {
capacity="Kapasitas",
create="Buat",
ospin="O-spin",
ospin="TRS O-spin",
fineKill="100% Efisiensi Tombol",
b2bKill="Akhirkan Permainan Jika Tidak B2B",
lockout="Akhirkan Permainan Jika Lock Out",
@@ -543,6 +550,10 @@ return {
bg_on="B.G. Normal",
bg_off="Tidak Ada B.G.",
bg_custom="Ubah B.G.",
-- defaultBG="Default B.G.",
-- resetDbg="Reset to default",
-- lockBG="Lock B.G.",
-- noTheme="Disable theme",
blockSatur="Kejenuhan Blok Aktif",
fieldSatur="Kejenuhan Blok Terkunci",
@@ -678,7 +689,7 @@ return {
bufferLimit="Batas Serangan",
heightLimit="Batas Tinggi",
ospin="O-Spin",
ospin="TRS O-spin",
fineKill="Maksimum Efisiensi Tombol",
b2bKill="Akhirkan Permainan Jika Tidak B2B",
lockout="Akhirkan Permainan Jika Lock Out",
@@ -828,7 +839,9 @@ return {
['sprintPenta']= {"Balapan", "PENTOMINO", "40L dengan pentomino!"},
['sprintMPH']= {"Balapan", "MPH", "Tanpa ingatan\nTanpa pratinjau\nTanpa simpan"},
['sprint123']= {"Balapan", "M123", "40L dengan hanya monomino, domino, dan trimino"},
['secret_grade']= {"Secret Grade", "", "Buatlah formasi lubang zigzag, menuruti panduannya!"},
['construct_sg']= {"Membangun", "SECRET GRADE", "Buatlah formasi lubang zigzag, menuruti panduannya!"},
['construct_checker']= {"Membangun", "KOTAK-KOTAK", "Buatlah pola kotak-kotak!"},
['construct_invsg']= {"Membangun", "SG TERBALIK", "Buatlah pola zigzag terbalik!"},
['dig_10l']= {"Gali", "10L", "Gali 10 baris!"},
['dig_40l']= {"Gali", "40L", "Gali 40 baris!"},
['dig_100l']= {"Gali", "100L", "Gali 100 baris!"},
@@ -847,12 +860,12 @@ return {
['solo_h']= {"Tarung", "SULIT", "Kalahkan AInya!"},
['solo_l']= {"Tarung", "GILA", "Kalahkan AInya!"},
['solo_u']= {"Tarung", "TERAKHIR", "Kalahkan AInya!"},
['techmino49_e']= {"Tech 49", "MUDAH", "Pertarungan dengan 49 pemain."},
['techmino49_h']= {"Tech 49", "SULIT", "Pertarungan dengan 49 pemain."},
['techmino49_u']= {"Tech 49", "TERAKHIR", "Pertarungan dengan 49 pemain."},
['techmino99_e']= {"Tech 99", "MUDAH", "Pertarungan dengan 99 pemain."},
['techmino99_h']= {"Tech 99", "SULIT", "Pertarungan dengan 99 pemain."},
['techmino99_u']= {"Tech 99", "TERAKHIR", "Pertarungan dengan 99 pemain."},
['techmino49_e']= {"Tech VS 49", "MUDAH", "Pertarungan dengan 49 pemain."},
['techmino49_h']= {"Tech VS 49", "SULIT", "Pertarungan dengan 49 pemain."},
['techmino49_u']= {"Tech VS 49", "TERAKHIR", "Pertarungan dengan 49 pemain."},
['techmino99_e']= {"Tech VS 99", "MUDAH", "Pertarungan dengan 99 pemain."},
['techmino99_h']= {"Tech VS 99", "SULIT", "Pertarungan dengan 99 pemain."},
['techmino99_u']= {"Tech VS 99", "TERAKHIR", "Pertarungan dengan 99 pemain."},
['round_e']= {"Giliran", "MUDAH", "Giliran main melawan AI!"},
['round_n']= {"Giliran", "NORMAL", "Giliran main melawan AI!"},
['round_h']= {"Giliran", "SULIT", "Giliran main melawan AI!"},
@@ -903,16 +916,14 @@ return {
['pc_h']= {"Tantangan PC", "SULIT", "Dapatkan PC sampai 100 baris!"},
['pc_l']= {"Tantangan PC", "GILA", "Dapatkan PC sampai 100 baris!"},
['pc_inf']= {"Tantangan PC", "TAK TERBATAS", "Dapatkan PC sebanyaknya!"},
['tech_n']= {"Tech", "NORMAL", "Coba jaga deret Back-To-Back!"},
['tech_n_plus']= {"Tech", "NORMAL+", "Hanya Spins & PCs dibolehkan"},
['tech_h']= {"Tech", "SULIT", "Coba jaga deret Back-To-Back!"},
['tech_h_plus']= {"Tech", "SULIT+", "Hanya Spins & PCs dibolehkan"},
['tech_l']= {"Tech", "GILA", "Coba jaga deret Back-To-Back!"},
['tech_l_plus']= {"Tech", "GILA+", "Hanya Spins & PCs dibolehkan"},
['tech_n']= {"Tech B2B", "NORMAL", "Coba jaga deret Back-To-Back!"},
['tech_n_plus']= {"Tech B2B", "NORMAL+", "Hanya Spins & PCs dibolehkan"},
['tech_h']= {"Tech B2B", "SULIT", "Coba jaga deret Back-To-Back!"},
['tech_h_plus']= {"Tech B2B", "SULIT+", "Hanya Spins & PCs dibolehkan"},
['tech_l']= {"Tech B2B", "GILA", "Coba jaga deret Back-To-Back!"},
['tech_l_plus']= {"Tech B2B", "GILA+", "Hanya Spins & PCs dibolehkan"},
['tech_finesse']= {"Tech", "EF. TOMBOL", "Efisiensi tombol harus maksimal!"},
['tech_finesse_f']= {"Tech", "EF. TOMBOL+", "Efisiensi tombol maksimal dan anda tidak boleh membuat baris normal!"},
['tech_finesse_lock']= {"Tech", "EF. TBL. TERBATAS", "Jumlah pemencetan tombol terbatas, dan efisiensinya harus maksimal!"},
['tech_finesse_lock_f']= {"Tech", "EF. TBL.+ TERBATAS", "Jumlah pemencetan tombol terbatas, efisiensi tombol harus maksimal, dan anda tidak boleh membuat baris normal!"},
['tsd_e']= {"Tantangan TSD", "MUDAH", "Hanya T-Spin Double dibolehkan!"},
['tsd_h']= {"Tantangan TSD", "SULIT", "Hanya T-Spin Double dibolehkan!"},
['tsd_u']= {"Tantangan TSD", "TERAKHIR", "Hanya T-Spin Double dibolehkan!"},

View File

@@ -44,6 +44,7 @@ return {
infHeightOn="高度無限!",
infHeightOff="高度制限あり",
infHeightHint="“ファンクション 1”キー",
highestGrade="(最高ランク: $1)",
speedLV="レベル",
piece="ミノ数",line="line数",atk="火力",eff="効率",
@@ -55,9 +56,9 @@ return {
win="Win!",
lose="Lose",
finish="Finished",
gamewin="You Won",
torikan="Ended",
finish="Finished!",
gamewin="You Win!",
gameover="Game Over",
pause="ポーズ",
@@ -67,10 +68,13 @@ return {
page="ページ:",
ai_puzzle="AIはパズルゲームモードに非対応です!",
ai_mission="AIはカスタムミッションに非対応です!",
ai_badPiece="AIは通常のテトロミ以外やミ順指定に非対応です!",
cc_fixed="CCはミ順の指定に非対応です!",
cc_swap="CCはホールドモード、Swapに非対応です!",
ai_prebag="AIは通常のテトロミ以外やミ順指定に非対応です!",
ai_mission="AIはカスタムミッションに非対応です!",
cc_solid="CCはLineが揃っている盤面に非対応です!",
cc_field_too_high="CCは高さ40以上の盤面に非対応です!",
switchSpawnSFX="ブロック出現時の効果音をONにしてください!",
needRestart="すべての変更を適用する為にリスタートしてください!",
@@ -92,6 +96,8 @@ return {
dataCorrupted="データが破損してます",
pasteWrongPlace="貼り付ける位置を間違っていませんか?",
noFile="ファイルが見つかりません",
invalidSequence="無効な出現法則です",
tooHighField="フィールドの高さは126段までです",
nowPlaying="再生中:",
@@ -259,11 +265,12 @@ return {
"これは“ただの”落ちものパズルゲームです。本当ですよ",
"C2/IO/JS/WWC/KOS等からアイデアを得ました",
"",
"LÖVE」搭載",
"powered by LÖVE",
"ご意見、ご感想、バグ報告など、全て大歓迎です!",
"ゲームは、必ず公式から入手してください",
"他から入手した場合は、安全性を保証しません",
"同時に制作者は、責任を負いません",
"Techminoは振動とインターネット接続の許可しか求めません",
"また制作者は、責任を負いません",
FNNS and "/" or "ゲームは無料ですが寄付してくださると嬉しいです!",
FNNS and "/" or "詳しくはZictionaryをご覧ください",
},
@@ -328,6 +335,7 @@ return {
"Miya",
"Xiaoya",
"Mono",
"Flore",
"MrZ",
"Trebor",
"",
@@ -416,7 +424,7 @@ B. キーボード
C. ゲームパッド
- $10でヘルプを表示
- $5または、$6でテキストをスクロール($11を押しながらだとスクロールが速くなります)
- $5または、$6でテキストをスクロール
- $7または、$8で項目をスクロール($11を押しながらだとスクロールが速くなります)
- $11を押しながら$6または、$5でフォントサイズ変更
]]
@@ -503,7 +511,7 @@ C. ゲームパッド
capacity="試合人数",
create="作成",
ospin="O-spin",
ospin="TRS O-spin",
fineKill="最適化のみ",
b2bKill="B2B継続",
lockout="盤面外設置禁止",
@@ -586,6 +594,10 @@ C. ゲームパッド
bg_on="通常背景",
bg_off="背景なし ",
bg_custom="カスタム背景",
defaultBG="デフォルト背景",
resetDbg="背景リセット",
lockBG="背景固定",
noTheme="テーマ無効化",
blockSatur="ブロックデザイン",
fieldSatur="設置ブロックデザイン",
@@ -721,7 +733,7 @@ C. ゲームパッド
bufferLimit="ダメージの保持上限",
heightLimit="致死ラインの高さ",
ospin="Oスピン",
ospin="TRS O-spin",
fineKill="最適化のみ",
b2bKill="B2B継続",
lockout="盤面外設置禁止",
@@ -862,117 +874,117 @@ C. ゲームパッド
},
},
modes={
['sprint_10l']= {"スプリント", "10L", "10line消せ!"},
['sprint_20l']= {"スプリント", "20L", "20line消せ!"},
['sprint_40l']= {"スプリント", "40L", "40line消せ!"},
['sprint_100l']= {"スプリント", "100L", "100line消せ!"},
['sprint_400l']= {"スプリント", "400L", "400line消せ!"},
['sprint_1000l']= {"スプリント", "1,000L", "1,000line消せ!"},
['sprintPenta']= {"スプリント", "PENTOMINO", "ペントミで40line"},
['sprintMPH']= {"スプリント", "MPH", "ミノ順なし\nネクストなし\nホールドなし!"},
['sprint123']= {"スプリント", "M123", "、ドミ、トリミで40line"},
['secret_grade']= {"裏GM", "", "ガイドに従ってジグザグに穴を作れ!"},
['dig_10l']= {"掘り", "10L", "10line下穴を掘れ!"},
['dig_40l']= {"掘り", "40L", "40line下穴を掘れ!"},
['dig_100l']= {"掘り", "100L", "100line下穴を掘れ!"},
['dig_400l']= {"掘り", "400L", "400line下穴を掘れ!"},
['dig_eff_10l']= {"掘り", "EFFICIENCY 10L", "最小のミ数で10line掘れ!"},
['dig_eff_40l']= {"掘り", "EFFICIENCY 40L", "最小のミ数で40line掘れ!"},
['dig_eff_100l']= {"掘り", "EFFICIENCY 100L","最小のミノで100line掘れ!"},
['dig_eff_400l']= {"掘り", "EFFICIENCY 400L","最小のミノで400line掘れ!"},
['dig_quad_10l']= {"掘り", "TECHRASH 10L", "techrashだけで10line下穴を掘れ!"},
['drought_n']= {"ドラウト", "100L", "Iミ一切無し!"},
['drought_l']= {"ドラウト+", "100L", "わったーふ◯っく!"},
['marathon_n']= {"マラソン", "NORMAL", "加速する中で200lineマラソン!"},
['marathon_h']= {"マラソン", "HARD", "高速の中で200lineマラソン!"},
['solo_e']= {"バトル", "EASY", "低レベルのAIに勝て!"},
['solo_n']= {"バトル", "NORMAL", "通常レベルのAIに勝て!"},
['solo_h']= {"バトル", "HARD", "レベルAIに勝て!"},
['solo_l']= {"バトル", "LUNATIC", "プロレベルAIに勝て!"},
['solo_u']= {"バトル", "ULTIMATE", "レベルAIに勝て!"},
['techmino49_e']= {"テック 49", "EASY", "レベルAIと49人で勝負\n最後の1人まで生き残れ!"},
['techmino49_h']= {"テック 49", "HARD", "レベルAIと49人で勝負\n最後の1人まで生き残れ!"},
['techmino49_u']= {"テック 49", "ULTIMATE", "神レベルAIと49人で勝負\n最後の1人まで生き残れ!"},
['techmino99_e']= {"テック 99", "EASY", "低レベルAIと99人で勝負\n最後の1人まで生き残れ!"},
['techmino99_h']= {"テック 99", "HARD", "高レベルAIと99人で勝負\n最後の1人まで生き残れ!"},
['techmino99_u']= {"テック 99", "ULTIMATE", "神レベルAIと99人で勝負\n最後の1人まで生き残れ!"},
['round_e']= {"ターン制", "EASY", "ターン制で低レベルAIと勝負!"},
['round_n']= {"ターン制", "NORMAL", "ターン制で通常レベルAIと勝負!"},
['round_h']= {"ターン制", "HARD", "ターン制で高レベルAIと勝負!"},
['round_l']= {"ターン制", "LUNATIC", "ターン制でプロレベルAIと勝負!"},
['round_u']= {"ターン制", "ULTIMATE", "ターン制で神レベルAIと勝負!"},
['big_n']= {"ビッグ", "NORMAL", "小さい盤面でプレイ!"},
['big_h']= {"ビッグ", "HARD", "小さい盤面でプレイ!"},
['master_n']= {"マスター", "NORMAL", "20G 初心者方へ"},
['master_h']= {"マスター", "HARD", "20G 中級者の方へ"},
['master_m']= {"マスター", "M21", "20G 上級者の方へ"},
['master_final']= {"マスター", "FINAL", "20G その先へ"},
['master_ph']= {"マスター", "PHANTASM", "???"},
['master_g']= {"マスター", "GRADED", "最高段位を取れ!"},
['master_ex']= {"グランドマスター", "EXTRA", "一瞬よりも短い永遠"},
['master_instinct']={"マスター", "INSTINCT", "もしミノが一切見えなくなったら?"},
['strategy_e']= {"ストラテジ", "EASY", "20Gでの素早い判断"},
['strategy_h']= {"ストラテジ", "HARD", "20Gでの素早い判断"},
['strategy_u']= {"ストラテジー", "ULTIMATE", "20Gでの素早い判断"},
['strategy_e_plus']={"ストラテジー", "EASY+", "20Gでの素早い判断"},
['strategy_h_plus']={"ストラテジー", "HARD+", "20Gでの素早い判断"},
['strategy_u_plus']={"ストラテジー", "ULTIMATE+", "20Gでの素早い判断"},
['blind_e']= {"インビジブル", "HALF", "初心者用"},
['blind_n']= {"インビジブル", "ALL", "中級者用"},
['blind_h']= {"インビジブル", "SUDDEN", "上級者用"},
['blind_l']= {"インビジブル", "SUDDEN+", "プロフェッショナル"},
['blind_u']= {"インビジブル", "?", "覚悟は良いかい?"},
['blind_wtf']= {"インビジブル", "WTF", "まだ覚悟が足りない"},
['classic_e']= {"クラシック", "EASY", "低速クラシック"},
['classic_h']= {"クラシック", "HARD", "通常速度クラシック"},
['classic_l']= {"クラシック", "LUNATIC", "中高速度クラシック"},
['classic_u']= {"クラシック", "ULTIMATE", "高速クラシック"},
['survivor_e']= {"サバイバル", "EASY", "どれだけ生き残れるかな?"},
['survivor_n']= {"サバイバル", "NORMAL", "どれだけ生き残れるかな?"},
['survivor_h']= {"サバイバル", "HARD", "どれだけ生き残れるかな?"},
['survivor_l']= {"サバイバル", "LUNATIC", "どれだけ生き残れるかな?"},
['survivor_u']= {"サバイバル", "ULTIMATE", "どれだけ生き残れるかな?"},
['attacker_h']= {"火力王", "HARD", "攻撃力を磨け!"},
['attacker_u']= {"火力王", "ULTIMATE", "攻撃力を磨け!"},
['defender_n']= {"相殺", "NORMAL", "防御力を磨け!"},
['defender_l']= {"相殺", "LUNATIC", "防御力を磨け!"},
['dig_h']= {"", "HARD", "掘りを磨け"},
['dig_u']= {"", "ULTIMATE", "掘りを磨け"},
['c4wtrain_n']= {"C4Wトレーニング", "NORMAL", "無限中開"},
['c4wtrain_l']= {"C4Wトレーニング", "LUNATIC", "無限中開"},
['pctrain_n']= {"パフェトレーニング", "NORMAL", "パフェの練習!"},
['pctrain_l']= {"パフェトレーニング", "LUNATIC", "もっと難しいパフェの練習!"},
['pc_n']= {"パフェチャレンジ", "NORMAL", "100Line以内で パフェをできるだけたくさん取れ!"},
['pc_h']= {"パフェチャレンジ", "HARD", "100Line以内で パフェをできるだけたくさん取れ!"},
['pc_l']= {"パフェチャレンジ", "LUNATIC", "100Line以内で パフェをできるだけたくさん取れ!"},
['pc_inf']= {"無限パフェチャレンジ", "", "できる限りたくさんのパフェを取れ!"},
['tech_n']= {"テクニック", "NORMAL", "B2Bを繋げ続けよう!"},
['tech_n_plus']= {"テクニック", "NORMAL+", "回転入れとパフェだけ!"},
['tech_h']= {"テクニック", "HARD", "B2Bを繋げ続けよう!"},
['tech_h_plus']= {"テクニック", "HARD+", "回転入れとパフェだけ!"},
['tech_l']= {"テクニック", "LUNATIC", "回転入れとパフェだけ!"},
['tech_l_plus']= {"テクニック", "LUNATIC+", "回転入れとパフェだけ!"},
['tech_finesse']= {"テクニック", "FINESSE", "最適化!"},
['tech_finesse_f']= {"テクニック", "FINESSE+", "最適化はそのまま、普通のline消去禁止!"},
['tech_finesse_lock']= {"テクニック", "FINESSE LOCK","限られた入力数で最適化!"},
['tech_finesse_lock_f']= {"Tech", "FINESSE+ LOCK","限られた入力数で最適化、ただし普通のline消去禁止!"},
['tsd_e']= {"TSDチャレンジ", "EASY", "TSDだけ!"},
['tsd_h']= {"TSDチャレンジ", "HARD", "TSDだけ!"},
['tsd_u']= {"TSDチャレンジ", "ULTIMATE", "TSDだけ!"},
['backfire_n']= {"バックファイヤー", "NORMAL", "撃った火力が戻ってくる!"},
['backfire_h']= {"バックファイヤー", "HARD", "撃った火力が戻ってくる!"},
['backfire_l']= {"バックファイヤー", "LUNATIC", "撃った火力が戻ってくる!"},
['backfire_u']= {"バックファイヤー", "ULTIMATE", "撃った火力が戻ってくる!"},
['sprintAtk']= {"スプリント", "100 Attack", "100line送れ!"},
['sprintEff']= {"スプリント", "Efficiency", "40lineの間にできるだけ火力を出せ!"},
['zen']= {'zen', "200", "時間制限なしで200line消去"},
['ultra']= {'ウルトラ', "EXTRA", "2分以内にできるだけ多くの点数を取る"},
['infinite']= {"無限", "", "サンドボックス"},
['infinite_dig']= {"無限: 掘り", "", "掘れ掘れ掘れ"},
['marathon_inf']= {"マラソン", "INFINITE", "ずっとマラソン"},
['sprint_10l']= {"スプリント", "10L", "10line消せ!"},
['sprint_20l']= {"スプリント", "20L", "20line消せ!"},
['sprint_40l']= {"スプリント", "40L", "40line消せ!"},
['sprint_100l']= {"スプリント", "100L", "100line消せ!"},
['sprint_400l']= {"スプリント", "400L", "400line消せ!"},
['sprint_1000l']= {"スプリント", "1,000L", "1,000line消せ!"},
['sprintPenta']= {"スプリント", "PENTOMINO", "ペントミで40line"},
['sprintMPH']= {"スプリント", "MPH", "ミノ順なし\nネクストなし\nホールドなし!"},
['sprint123']= {"スプリント", "M123", "、ドミ、トリミで40line"},
['construct_sg']= {"アート", "裏GM", "ガイドに従ってジグザグに穴を作れ!"},
['construct_checker']= {"アート", "市松模様", "Build a checkerboard pattern!"},
['construct_invsg']= {"アート", "反転裏GM", "Build an inverted zigzag pattern!"},
['dig_10l']= {"掘り", "10L", "10line下穴を掘れ!"},
['dig_40l']= {"掘り", "40L", "40line下穴を掘れ!"},
['dig_100l']= {"掘り", "100L", "100line下穴を掘れ!"},
['dig_400l']= {"掘り", "400L", "400line下穴を掘れ!"},
['dig_eff_10l']= {"掘り", "EFFICIENCY 10L", "少ないで10line掘れ!"},
['dig_eff_40l']= {"掘り", "EFFICIENCY 40L", "少ないで40line掘れ!"},
['dig_eff_100l']= {"掘り", "EFFICIENCY 100L","少ないミノで100line掘れ!"},
['dig_eff_400l']= {"掘り", "EFFICIENCY 400L","少ないミで400line掘れ!"},
['dig_quad_10l']= {"掘り", "TECHRASH 10L", "techrashだけで10line下穴を掘れ!"},
['drought_n']= {"ドラウト", "100L", "Iミ禁止!"},
['drought_l']= {"ドラウト+", "100L", "WTF!!!"},
['marathon_n']= {"マラソン", "NORMAL", "加速する中で200lineマラソン!"},
['marathon_h']= {"マラソン", "HARD", "ハイスピード200lineマラソン!"},
['solo_e']= {"バトル", "EASY", "レベルAIに勝て!"},
['solo_n']= {"バトル", "NORMAL", "通常レベルAIに勝て!"},
['solo_h']= {"バトル", "HARD", "レベルAIに勝て!"},
['solo_l']= {"バトル", "LUNATIC", "プロレベルAIに勝て!"},
['solo_u']= {"バトル", "ULTIMATE", "レベルAIに勝て!"},
['techmino49_e']= {"テック VS 49", "EASY", "49人対戦\n最後まで生き残れ!"},
['techmino49_h']= {"テック VS 49", "HARD", "49人対戦\n最後まで生き残れ!"},
['techmino49_u']= {"テック VS 49", "ULTIMATE", "49人対戦\n最後まで生き残れ!"},
['techmino99_e']= {"テック VS 99", "EASY", "99人対戦\n最後まで生き残れ!"},
['techmino99_h']= {"テック VS 99", "HARD", "99人対戦\n最後まで生き残れ!"},
['techmino99_u']= {"テック VS 99", "ULTIMATE", "99人対戦\n最後まで生き残れ!"},
['round_e']= {"ターン制", "EASY", "低レベルAIとターン制バトル!"},
['round_n']= {"ターン制", "NORMAL", "通常レベルAIとターン制バトル!"},
['round_h']= {"ターン制", "HARD", "高レベルAIとターン制バトル!"},
['round_l']= {"ターン制", "LUNATIC", "プロレベルAIとターン制バトル!"},
['round_u']= {"ターン制", "ULTIMATE", "神レベルAIとターン制バトル!"},
['big_n']= {"ビッグ", "NORMAL", "5×10の盤面でプレイ!"},
['big_h']= {"ビッグ", "HARD", "5×10の盤面でプレイ!"},
['master_n']= {"マスター", "NORMAL", "初心者のための20G"},
['master_h']= {"マスター", "HARD", "中級者のための20G"},
['master_m']= {"マスター", "M21", "上級者のための20G"},
['master_final']= {"マスター", "FINAL", "20G その先へ"},
['master_ph']= {"マスター", "PHANTASM", "???"},
['master_g']= {"マスター", "GRADED", "最高段位を取れ!"},
['master_ex']= {"グランドマスタ", "EXTRA", "一瞬にも満たない永遠"},
['master_instinct']= {"マスタ", "INSTINCT", "もしミノが見えなくなったら?"},
['strategy_e']= {"ストラテジー", "EASY", "20Gでの素早い判断"},
['strategy_h']= {"ストラテジー", "HARD", "20Gでの素早い判断"},
['strategy_u']= {"ストラテジー", "ULTIMATE", "20Gでの素早い判断"},
['strategy_e_plus']= {"ストラテジー", "EASY+", "20Gでの素早い判断"},
['strategy_h_plus']= {"ストラテジー", "HARD+", "20Gでの素早い判断"},
['strategy_u_plus']= {"ストラテジー", "ULTIMATE+", "20Gでの素早い判断"},
['blind_e']= {"インビジブル", "HALF", "初心者用"},
['blind_n']= {"インビジブル", "ALL", "中級者"},
['blind_h']= {"インビジブル", "SUDDEN", "上級者用"},
['blind_l']= {"インビジブル", "SUDDEN+", "プロフェッショナル用"},
['blind_u']= {"インビジブル", "?", "覚悟は良いかい?"},
['blind_wtf']= {"インビジブル", "WTF", "まだ覚悟が足りない"},
['classic_e']= {"クラシック", "EASY", "低速ゲーム(1980年版)"},
['classic_h']= {"クラシック", "HARD", "中速ゲーム(1980年版)"},
['classic_l']= {"クラシック", "LUNATIC", "高速ゲーム(1980年版)"},
['classic_u']= {"クラシック", "ULTIMATE", "超高速ゲーム(1980年版)"},
['survivor_e']= {"サバイバル", "EASY", "どれだけ生き残れるかな?"},
['survivor_n']= {"サバイバル", "NORMAL", "どれだけ生き残れるかな?"},
['survivor_h']= {"サバイバル", "HARD", "どれだけ生き残れるかな?"},
['survivor_l']= {"サバイバル", "LUNATIC", "どれだけ生き残れるかな?"},
['survivor_u']= {"サバイバル", "ULTIMATE", "どれだけ生き残れるかな?"},
['attacker_h']= {"火力", "HARD", "攻撃力を磨け!"},
['attacker_u']= {"火力", "ULTIMATE", "攻撃力を磨け!"},
['defender_n']= {"相殺", "NORMAL", "防御力を磨け!"},
['defender_l']= {"相殺", "LUNATIC", "防御力を磨け!"},
['dig_h']= {"掘王", "HARD", "掘りを磨"},
['dig_u']= {"掘王", "ULTIMATE", "掘りを磨"},
['c4wtrain_n']= {"C4Wトレーニング", "NORMAL", "無限中開け"},
['c4wtrain_l']= {"C4Wトレーニング", "LUNATIC", "無限中開け"},
['pctrain_n']= {"パフェトレーニング", "NORMAL", "パフェの練習!"},
['pctrain_l']= {"パフェトレーニング", "LUNATIC", "もっと難しいパフェの練習!"},
['pc_n']= {"パフェチャレンジ", "NORMAL", "100Line以内で パフェをたくさん取れ!"},
['pc_h']= {"パフェチャレンジ", "HARD", "100Line以内で パフェをたくさん取れ!"},
['pc_l']= {"パフェチャレンジ", "LUNATIC", "100Line以内で パフェをたくさん取れ!"},
['pc_inf']= {"無限パフェチャレンジ", "", "たくさんのパフェを取れ!"},
['tech_n']= {"テクニック B2B", "NORMAL", "B2Bを繋げ続けよう!"},
['tech_n_plus']= {"テクニック B2B", "NORMAL+", "回転入れとパフェだけ!"},
['tech_h']= {"テクニック B2B", "HARD", "B2Bを繋げ続けよう!"},
['tech_h_plus']= {"テクニック B2B", "HARD+", "回転入れとパフェだけ!"},
['tech_l']= {"テクニック B2B", "LUNATIC", "B2Bを繋げ続けよう!"},
['tech_l_plus']= {"テクニック B2B", "LUNATIC+", "回転入れとパフェだけ!"},
['tech_finesse']= {"テクニック 最適化", "", "最適化!"},
['tech_finesse_f']= {"テクニック 最適化", "PLUS", "通常Line消去抜きで最適化!"},
['tsd_e']= {"TSDチャレンジ", "EASY", "TSDだけ!"},
['tsd_h']= {"TSDチャレンジ", "HARD", "TSDだけ!"},
['tsd_u']= {"TSDチャレンジ", "ULTIMATE", "TSDだけ!"},
['backfire_n']= {"バックファイヤー", "NORMAL", "撃った火力が戻ってくる!"},
['backfire_h']= {"バックファイヤー", "HARD", "撃った火力が戻ってくる!"},
['backfire_l']= {"バックファイヤー", "LUNATIC", "撃った火力が戻ってくる!"},
['backfire_u']= {"バックファイヤー", "ULTIMATE", "撃った火力が戻ってくる!"},
['sprintAtk']= {"スプリント", "100 Attack", "100line送れ!"},
['sprintEff']= {"スプリント", "Efficiency", "40lineの間に火力を出せ!"},
['zen']= {'zen', "200", "時間制限なしで200line消去"},
['ultra']= {'ウルトラ', "EXTRA", "2分間でスコアアタック"},
['infinite']= {"無限", "", "サンドボックス"},
['infinite_dig']= {"無限: 掘り", "", "掘れ掘れ掘れ掘れ掘れ掘・・・"},
['marathon_inf']= {"マラソン", "INFINITE", "ずっと走れるね"},
['custom_clear']= {"カスタム", "NORMAL"},
['custom_puzzle']= {"カスタム", "PUZZLE"},
['custom_clear']= {"カスタム", "NORMAL"},
['custom_puzzle']= {"カスタム", "PUZZLE"},
},
getTip={refuseCopy=true,
":pog:",

View File

@@ -18,7 +18,7 @@ return {
clear={"Single","Double","Triple","Techrash","Pentacrash","Hexacrash","Heptacrash","Octacrash","Nonacrash","Decacrash","Undecacrash","Dodecacrash","Tridecacrash","Tetradecacrash","Pentadecacrash","Hexadecacrash","Heptadecacrash","Octadecacrash","Nonadecacrash","Ultracrash","Impossicrash"},
-- cleared="$1 lines",
mini="Mini",b2b="B2B ",b3b="B2B2B ",
PC="Perfect Clear",HPC="Clear",
PC="Perfect Clear",HPC="Meio PC",
replaying="[Replay]",
-- tasUsing="[TAS]",
@@ -33,6 +33,7 @@ return {
-- infHeightOn="Infinite Height ON",
-- infHeightOff="Infinite Height OFF",
-- infHeightHint="Toggle with Function 1 key",
-- highestGrade="(highest: $1)",
speedLV="Nível de velocidade",
piece="Peça",line="Linhas",atk="Ataque",eff="Eficiência",
@@ -44,7 +45,7 @@ return {
-- win="Win",
-- lose="Lose",
--torikan="Ended",
finish="Terminou",
gamewin="Você venceu!",
gameover="Fim de jogo",
@@ -56,10 +57,13 @@ return {
page="Página:",
-- ai_puzzle="AI is incompatible with puzzle game mode",
ai_mission="A inteligência é incompatível com missões costumizadas.",
-- ai_badPiece="The AI is incompatible with custom sequences which have nontetromino.", inteligência é incompatível com sequências fixas.",
-- cc_fixed="CC is incompatible with fixed sequences",
-- cc_swap="CC is incompatible with swap holdmode",
-- ai_prebag="The AI is incompatible with custom sequences which have nontetromino.", inteligência é incompatível com sequências fixas.",
ai_mission="A inteligência é incompatível com missões costumizadas.",
-- cc_solid="CC is incompatible with filled line in the field.",
-- cc_field_too_high="CC is incompatible with fields higher than 40.",
switchSpawnSFX="Switch on spawn SFX to play",
needRestart="Funciona após reiniciar",
@@ -81,6 +85,8 @@ return {
dataCorrupted="Data corrompida",
-- pasteWrongPlace="Paste at wrong place?",
-- noFile="File not found",
-- invalidSequence="Invalid sequence mode",
-- tooHighField="Field data exceeded 126 lines discarded",
VKTchW="Peso de toque",
VKOrgW="Peso da origem",
@@ -221,7 +227,7 @@ return {
"Peça:",
"Linha/Dig:",
"Ataque/DigAtk:",
"Recebido:",
"Recebido:",-- "Rise(Receive-Offset):",
"Limpas:",
"Giros:",
"B2B/B3B/PC/HPC:",
@@ -235,7 +241,7 @@ return {
"Tempo jogado:",
"Tecla/Rot./Segurar:",
"Blocos/Linhas/Ataque:",
"Recv./Res./Asc.:",
"Recv./Res./Asc.:",-- "Receive/Offset/Rise:",
"Dig/Dig Atk.:",
"Eff./Dig Eff.:",
"B2B/B3B:",
@@ -315,6 +321,7 @@ return {
"Miya",
"Xiaoya",
"Mono",
"Flore",
"MrZ",
"Trebor",
"",
@@ -448,7 +455,7 @@ return {
-- capacity="Capacity",
-- create="Create",
ospin="O-Spin",
ospin="TRS O-spin",
fineKill="100% Finesse",
b2bKill="Sem Quebrar B2B",
-- lockout="Fail when lock out",
@@ -456,7 +463,7 @@ return {
-- deepDrop="Deep Drop",
bone="Bone Blocks",
-- eventSet="Rule Set",
-- eventSet="Ruleset",
-- holdMode="Hold Mode",
nextCount="Prox.",
@@ -531,6 +538,10 @@ return {
-- bg_on="Normal B.G.",
-- bg_off="No B.G.",
-- bg_custom="Use Custom B.G.",
-- defaultBG="Default B.G.",
-- resetDbg="Reset to default",
-- lockBG="Lock B.G.",
-- noTheme="Disable theme",
-- blockSatur="Block Saturation",
-- fieldSatur="Field Saturation",
@@ -648,7 +659,7 @@ return {
sequence="Editar Sequência (S)",
mission="Editar Missão (M)",
-- eventSet="Rule Set",
-- eventSet="Ruleset",
-- holdMode="Hold Mode",
nextCount="Prox.",
@@ -666,7 +677,7 @@ return {
-- bufferLimit="Buffer Limit",
-- heightLimit="Height Limit",
ospin="O-Spin",
ospin="TRS O-spin",
fineKill="100% Finesse",
b2bKill="Sem Quebrar B2B",
-- lockout="Fail when lock out",
@@ -815,6 +826,10 @@ return {
['sprint_1000l']= {"Sprint", "1000L", "Limpe 1000 linhas!"},
['sprintPenta']= {"Sprint", "PENTOMINO", "Limpe 40 linhas com 18 pentominoes."},
['sprintMPH']= {"Sprint", "MPH", "SemMem.\nSemPrévia\nSemSegurar"},
-- ['sprint123']= {"Sprint", "M123", "40L with only monominoes, dominoes, and triminoes"},
-- ['construct_sg']= {"Construct", "SECRET GRADE", "Build a zigzag shape by following the guide!"},
-- ['construct_checker']= {"Construct", "CHECKERBOARD", "Build a checkerboard pattern!"},
-- ['construct_invsg']= {"Construct", "INV. SG", "Build an inverted zigzag pattern!"},
['dig_10l']= {"Cave", "10L", "Cave 10 linhas de lixo."},
['dig_40l']= {"Cave", "40L", "Cave 40 linhas de lixo."},
['dig_100l']= {"Cave", "100L", "Cave 100 linhas de lixo."},
@@ -829,14 +844,14 @@ return {
['solo_e']= {"Batalha", "FÁCIL", "Derrote a inteligência!"},
['solo_n']= {"Batalha", "NORMAL", "Derrote a inteligência!"},
['solo_h']= {"Batalha", "DIFÍCIL", "Derrote a inteligência!"},
['solo_l']= {"Batalha", "LUNÁTICO", "Defeat the AI!"},
['solo_u']= {"Batalha", "ULTIMATE", "Defeat the AI!"},
['techmino49_e']= {"Tech 49", "FÁCIL", "Batalha de 49 jogadores.\nO último vence"},
['techmino49_h']= {"Tech 49", "DIFÍCIL", "Batalha de 49 jogadores.\nO último vence."},
['techmino49_u']= {"Tech 49", "ULTIMATE", "Batalha de 49 jogadores.\nO último vence."},
['techmino99_e']= {"Tech 99", "FÁCIL", "Batalha de 99 jogadores.\nO último vence."},
['techmino99_h']= {"Tech 99", "DIFÍCIL", "Batalha de 99 jogadores.\nO último vence."},
['techmino99_u']= {"Tech 99", "ULTIMATE", "Batalha de 99 jogadores.\nO último vence."},
['solo_l']= {"Batalha", "LUNÁTICO", "Derrote a inteligência!"},
['solo_u']= {"Batalha", "ULTIMATE", "Derrote a inteligência!"},
['techmino49_e']= {"Tech VS 49", "FÁCIL", "Batalha de 49 jogadores.\nO último vence"},
['techmino49_h']= {"Tech VS 49", "DIFÍCIL", "Batalha de 49 jogadores.\nO último vence."},
['techmino49_u']= {"Tech VS 49", "ULTIMATE", "Batalha de 49 jogadores.\nO último vence."},
['techmino99_e']= {"Tech VS 99", "FÁCIL", "Batalha de 99 jogadores.\nO último vence."},
['techmino99_h']= {"Tech VS 99", "DIFÍCIL", "Batalha de 99 jogadores.\nO último vence."},
['techmino99_u']= {"Tech VS 99", "ULTIMATE", "Batalha de 99 jogadores.\nO último vence."},
['round_e']= {"Baseado Turnos", "FÁCIL", "Modo xadrez"},
['round_n']= {"Baseado Turnos", "NORMAL", "Modo xadrez"},
['round_h']= {"Baseado Turnos", "DIFÍCIL", "Modo xadrez"},
@@ -889,16 +904,16 @@ return {
['pc_h']= {"Desafio PC", "DIFÍCIL", "Obtenha PCs em 100 linhas!"},
['pc_l']= {"Desafio PC", "LUNÁTICO", "Obteha PCs em 100 linhas!"},
-- ['pc_inf']= {"Inf. PC Challenge","", "Get PCs as much as you can"},
['tech_n']= {"Tech", "NORMAL", "Não quebre o B2B!"},
['tech_n_plus']= {"Tech", "NORMAL+", "Apenas spins e PC"},
['tech_h']= {"Tech", "HARD", "Keep the B2B chain!"},
['tech_h_plus']= {"Tech", "HARD+", "Apenas spins e PC"},
['tech_l']= {"Tech", "LUNÁTICO", "Não quebre o B2B!"},
['tech_l_plus']= {"Tech", "LUNÁTICO+", "Apenas spins e PC"},
['tech_n']= {"Tech B2B", "NORMAL", "Não quebre o B2B!"},
['tech_n_plus']= {"Tech B2B", "NORMAL+", "Apenas spins e PC"},
['tech_h']= {"Tech B2B", "HARD", "Keep the B2B chain!"},
['tech_h_plus']= {"Tech B2B", "HARD+", "Apenas spins e PC"},
['tech_l']= {"Tech B2B", "LUNÁTICO", "Não quebre o B2B!"},
['tech_l_plus']= {"Tech B2B", "LUNÁTICO+", "Apenas spins e PC"},
-- ['tech_finesse']= {"Tech Finesse", "", "No finesse errors!"},
-- ['tech_finesse_f']= {"Tech Finesse", "PLUS", "No normal clears and finesse errors!"},
['tech_finesse']= {"Tech", "FINESSE", "Não erre a destreza!"},
['tech_finesse_f']= {"Tech", "FINESSE+", "Sem limpas normais,\nnão erre a destreza!"},
--['tech_finesse_lock']= {"Tech", "FINESSE LOCK", "No finesse errors, combined with limited inputs!"},
--['tech_finesse_lock_f']= {"Tech", "FINESSE+ LOCK", "No normal clears or finesse errors combined with limited inputs!"},
['tsd_e']= {"Desafio TSD", "FÁCIL", "Apenas T-spin-doubles!"},
['tsd_h']= {"Desafio TSD", "DIFÍCIL", "Apenas T-spin-doubles!"},
['tsd_u']= {"Desafio TSD", "ULTIMATE", "Apenas T-spin-doubles!"},

View File

@@ -41,10 +41,11 @@ return {
infHeightOn="∞↑ "..CHAR.icon.checkMark,
infHeightOff="∞↑ "..CHAR.icon.crossMark,
infHeightHint=CHAR.icon.checkMark.."/"..CHAR.icon.crossMark..": F₁",
highestGrade="(↑: $1)",
win=": )",
lose=": (",
torikan=": /",
finish="&",
gamewin=">>",
gameover="x",
@@ -56,10 +57,13 @@ return {
page=":",
ai_puzzle="X!!!",
ai_mission="X!!!",
ai_badPiece="X!!!",
cc_fixed="CC X!!!",
cc_swap="CC X!!!",
ai_prebag="X!!!",
ai_mission="X!!!",
cc_solid="CC X!!!",
cc_field_too_high="CC X!!!",
needRestart="!!*#R#*!!",
loadError_errorMode="'$1' ↑x!: no load mode '$2'",
@@ -80,6 +84,8 @@ return {
dataCorrupted="XXXXX",
pasteWrongPlace="_?X.",
-- noFile="File not found",
-- invalidSequence="Invalid sequence mode",
-- tooHighField="Field data exceeded 126 lines discarded",
nowPlaying="~:",
@@ -103,7 +109,7 @@ return {
"□:",
"-/↓:",
"→/↓→:",
":",
"↑(←-↓):",
"□↓:",
"~↓:",
"^^/^^^/#<>#/<>:",
@@ -216,7 +222,7 @@ return {
deepDrop="\\↓↓/",
bone="[]",
eventSet="Rule Set",
eventSet="Ruleset",
holdMode="? [ ]",
nextCount="",
@@ -291,6 +297,10 @@ return {
bg_on="__?__",
bg_off="__.__",
bg_custom="__!__",
defaultBG="__$0__",
resetDbg="R$0",
lockBG="__↓__",
noTheme="\\^_^/ "..CHAR.icon.crossMark,
blockSatur="==#0x",
fieldSatur="[]#0x",
@@ -406,7 +416,7 @@ return {
sequence="Edit Sequence (S)",
mission="Edit Mission (M)",
eventSet="Rule Set",
eventSet="Ruleset",
holdMode="? [ ]",
nextCount="",

View File

@@ -1,5 +1,4 @@
local C=COLOR
local all_month={"T01","T02","T03","T04","T05","T06","T07","T08","T09","T10","T11","T12"}
-- There are some strings, due to game's history, temproary be commented just in case
-- If it is not used anymore, it will be removed, in one day…
@@ -20,7 +19,7 @@ return {
sureReset="Nhấn thêm một lần nữa để đặt lại",
sureDelete="Nhấn thêm một lần nữa để xoá",
newDay="Một ngày mới, một khởi đầu mới!",
playedLong="Bạn chơi cũng lâu rồi. Hãy dành chút thời gian nghỉ ngơi đi",
playedLong="Bạn chơi hơi lâu rồi đó. Hãy dành chút thời gian nghỉ ngơi đi.",
playedTooMuch="Có lẽ bạn chơi quá nhiều rồi! Đặt máy xuống và nghỉ ngơi đi bạn!",
settingWarn="CẨN THẬN - Bạn vừa sửa một cài đặt quan trọng của game!",
settingWarn2="Cài đặt này sẽ có hiệu lực sau khi khởi động lại",
@@ -34,7 +33,7 @@ return {
clear={"Single","Double","Triple","Techrash","Pentacrash","Hexacrash","Heptacrash","Octacrash","Nonacrash","Decacrash","Undecacrash","Dodecacrash","Tridecacrash","Tetradecacrash","Pentadecacrash","Hexadecacrash","Heptadecacrash","Octadecacrash","Nonadecacrash","Ultracrash","Impossicrash"},
cleared="$1 hàng",
mini="Mini",b2b="B2B ",b3b="B2B2B ",
PC="Perfect Clear",HPC="Hemi-Perfect Clear",
PC="Perfect Clear",HPC="Half PC",
replaying="[Đang phát lại]",
tasUsing="[TAS]",
@@ -46,9 +45,10 @@ return {
maxspeed="TỐC ĐỘ TỐI ĐA!",
speedup="Tăng tốc nào!",
missionFailed="Nhiệm vụ thất bại",
infHeightOn="Infinite Height ON",
infHeightOff="Infinite Height OFF",
infHeightHint="Toggle with Function 1 key",
infHeightOn="Bảng cao vô tận: BẬT",
infHeightOff="Bảng cao vô tận: TẮT",
infHeightHint="Nhấn phím Chức năng 1 để bật/tắt",
highestGrade="(cao nhất: $1)",
speedLV="Tốc độ rơi",
piece="Gạch",line="Hàng",atk="Attack",eff="Efficiency",
@@ -60,7 +60,7 @@ return {
win="Thắng!",
lose="Thua",
torikan="Torikan!",
finish="Hoàn thành",
gamewin="Bạn đã thắng",
gameover="Kết thúc",
@@ -72,10 +72,13 @@ return {
page="Trang ",
ai_puzzle="AI không tương thích với chế độ Puzzle",
ai_mission="AI không tương thích với nhiệm vụ tuỳ chọn.",
ai_badPiece="AI không tương thích với trình xáo gạch chứa gạch không phải là tetromino.",
cc_fixed="CC không tương thích với trình xáo gạch cố định",
cc_swap="CC không tương thích với chế độ Hold là Chuyển",
ai_prebag="AI không tương thích với trình xáo gạch chứa gạch không phải là tetromino.",
ai_mission="AI không tương thích với nhiệm vụ tuỳ chọn.",
cc_solid="CC không tương thích với bảng có hàng đã lấp đầy.",
cc_field_too_high="CC không tương thích với bảng cao hơn 40 hàng!",
switchSpawnSFX="Vui lòng bật Spawn SFX để chơi!",
needRestart="Khởi động lại để áp dụng mọi thay đổi.",
@@ -97,6 +100,8 @@ return {
dataCorrupted="Dữ liệu bị hỏng",
pasteWrongPlace="Bạn có dán đúng nơi không đấy?",
noFile="Thiếu tệp",
invalidSequence="Tên trình xáo gạch không hợp lệ!",
tooHighField="BẢNG QUÁ CAO! Phần bảng cao hơn hàng 126 sẽ bị bỏ qua",
nowPlaying="Đang phát:",
@@ -212,9 +217,9 @@ return {
modInstruction="Hãy chọn modifier bạn muốn.\nMod cho phép bạn có thể tùy biến game, nhưng cũng có thể làm game sập.\nKể cả thế, hãy thoải mái và chơi theo cách của bạn!\nBạn có thể dùng bàn phím để chọn mod (giữ Shift để chọn lùi)\nĐiểm sẽ không được lưu lại khi dùng mod.",
modInfo={
next="NEXT\nGhi đè số gạch hiển thị ở cột NEXT",
next="NEXT\nGhi đè số gạch hiển thị ở hàng NEXT",
hold="HOLD\nGhi đè số lượng gạch được giữ ở cột HOLD",
hideNext="Hidden NEXT\nẨn số lượng gạch ở cột NEXT (Tính từ ô đầu tiên)",
hideNext="Hidden NEXT\nẨn số lượng gạch ở hàng NEXT (Tính từ ô đầu tiên)",
infHold="InfiniHold\nCho phép bạn HOLD vô số lần",
hideBlock="Hide Current Piece\nGạch đang rơi trong bảng sẽ bị tàng hình",
hideGhost="No Ghost\nBóng gạch sẽ bị tắt",
@@ -241,7 +246,7 @@ return {
"Số gạch:",
"Hàng/Đào:",
"Gửi/Gửi khi đào:",
"Nhận:",
"Chịu (Nhận-Phản):",
"Xóa:",
"Spin:",
"B2B/B3B/PC/HPC:",
@@ -250,12 +255,12 @@ return {
radar={"DEF","OFF","ATK","SEND","SPD","DIG"},
radarData={"DPM","ADPM","APM","SPM","L'PM","DPM"},
stat={
"Số lần bật trò chơi:",
"Số lần bật game:",
"Số ván đã chơi:",
"Thời gian chơi:",
"Phím/Xoay/Giữ:",
"Gạch/Hàng/Gửi:",
"Nhận/Phản/Đẩy:",
"Nhận/Phản/Chịu:",
"Đào/Gửi khi đào:",
"H.quả/H.quả khi Đào:",
"B2B/B3B:",
@@ -338,6 +343,7 @@ return {
"Miya",
"Xiaoya",
"Mono",
"Flore",
"MrZ",
"Trebor",
"",
@@ -420,8 +426,8 @@ B. Bàn phím: Nhấn…
C. Tay cầm chơi game (Gamepad):
- Nhấn $10 để hiển thị trợ giúp
- Nhấn $5 hoặc $6 để cuộn văn bản, giữ $11 để cuộn nhanh hơn
- Nhấn $7 để mở mục trước và $8 để mở mục tiếp theo
- Nhấn $5 hoặc $6 để cuộn văn bản
- Nhấn $7 để mở mục trước và $8 để mở mục tiếp theo, giữ $11 để lướt nhanh hơn
- Giữ $11 và nhấn $6 để giảm cỡ chữ hoặc $5 để tăng lên
]]
-- 1-4: Up, Down, Left, Right
@@ -506,7 +512,7 @@ C. Tay cầm chơi game (Gamepad):
capacity="Giới hạn số người",
create="Tạo phòng",
ospin="O-spin",
ospin="TRS O-spin",
fineKill="100% Finesse",
b2bKill="Không phá B2B",
lockout="Thua khi Lock Out",
@@ -514,7 +520,7 @@ C. Tay cầm chơi game (Gamepad):
deepDrop="Thả rơi sâu",
bone="Dùng skin []",
eventSet="Rule Set",
eventSet="Ruleset",
holdMode="Chế độ Hold",
nextCount="Next",
@@ -565,13 +571,13 @@ C. Tay cầm chơi game (Gamepad):
grid="Lưới",
lineNum="# hàng",
lockFX="H.ứng Khóa gạch",
dropFX="H.ứng Thả nhẹ",
moveFX="H.ứng Di chuyển",
clearFX="H.ứng Xóa hàng",
splashFX="H.ứng Gạch \"rụng\"",
lockFX="Hiệu ứng Khóa gạch",
dropFX="Hiệu ứng Thả nhẹ",
moveFX="Hiệu ứng Di chuyển",
clearFX="Hiệu ứng Xóa hàng 1",
splashFX="Hiệu ứng Xóa hàng 2",
shakeFX="Độ nảy bảng",
atkFX="H.ứng Tấn công",
atkFX="Hiệu ứng Tấn công",
frame="Tần suất cập nhật khung hình (%)",
@@ -583,7 +589,7 @@ C. Tay cầm chơi game (Gamepad):
highCam="Trượt bảng",
warn="Cảnh báo nguy hiểm",
clickFX="Click FX",
clickFX="Hiệu ứng nhấp chuột",
power="Hiện thanh pin",
clean="Vẽ nhanh",
fullscreen="Toàn màn hình",
@@ -593,6 +599,10 @@ C. Tay cầm chơi game (Gamepad):
bg_on="Ảnh nền thường",
bg_off="Không ảnh nền",
bg_custom="Ảnh nền tự chọn",
defaultBG="Nền mặc định",
resetDbg='Đặt lại',
lockBG="Khóa ảnh nền",
noTheme="Tắt theme",
blockSatur="Độ đậm gạch",
fieldSatur="Độ đậm bảng",
@@ -620,7 +630,7 @@ C. Tay cầm chơi game (Gamepad):
},
setting_control={
-- title="Cài đặt Điều khiển",
title="Đ.chỉnh thg. số",
title="Điều chỉnh thông số",
preview="Xem trước",
das="DAS",arr="ARR",
@@ -726,7 +736,7 @@ C. Tay cầm chơi game (Gamepad):
sequence="C. đặt Trình xáo gạch (S)",
mission="Cài đặt Nhiệm vụ (M)",
eventSet="Rule Set",
eventSet="Ruleset",
holdMode="Chế độ Hold",
nextCount="Next",
@@ -744,7 +754,7 @@ C. Tay cầm chơi game (Gamepad):
bufferLimit="Giới hạn nhận rác",
heightLimit="Giới hạn độ cao",
ospin="O-Spin",
ospin="TRS O-spin",
fineKill="100% Finesse",
b2bKill="Không phá B2B",
lockout="Thua khi Lock Out",
@@ -890,11 +900,13 @@ C. Tay cầm chơi game (Gamepad):
['sprint_40l']= {"Sprint", "40L", "Xoá 40 hàng!"},
['sprint_100l']= {"Sprint", "100L", "Xoá 100 hàng!"},
['sprint_400l']= {"Sprint", "400L", "Xoá 400 hàng!"},
['sprint_1000l']= {"Sprint", "1000L", "Xoá 1000 hàng!"},
['sprint_1000l']= {"Sprint", "1000L", "Xoá 1000 hàng!"},
['sprintPenta']= {"Sprint", "PENTOMINO", "Xoá 40 hàng với 18 pentomino"},
['sprintMPH']= {"Sprint", "MPH", "Memoryless\nPreviewless\nHoldless"},
['sprint123']= {"Sprint", "M123", "Xoá 40 hàng chỉ với monomino, domino, và trimino"},
['secret_grade']= {"Secret Grade", "", "Xây một đường lỗ theo hình dích dắc!"},
['construct_sg']= {"Construct", "SECRET GRADE", "Xây một đường lỗ theo hình dích dắc!"},
['construct_checker']= {"Construct", "CHECKERBOARD", "Xây tường theo kiểu xen kẽ!"},
['construct_invsg']= {"Construct", "SG ĐẢO NGƯỢC", "Xây một đường thẳng theo hình dích dắc!"},
['dig_10l']= {"Dig", "10L", "Đào 10 hàng rác càng nhanh càng tốt"},
['dig_40l']= {"Dig", "40L", "Đào 40 hàng rác càng nhanh càng tốt!"},
['dig_100l']= {"Dig", "100L", "Đào 100 hàng rác càng nhanh càng tốt!"},
@@ -905,7 +917,7 @@ C. Tay cầm chơi game (Gamepad):
['dig_eff_400l']= {"Dig", "EFFICIENCY 400L","Đào 400 hàng rác càng ít gạch càng tốt!"},
['dig_quad_10l']= {"Dig", "TECHRASH 10L", "Đào 10 hàng rác nhưng chỉ dùng techrash!"},
['drought_n']= {"Drought", "100L", "Không có thanh dài"},
['drought_l']= {"Drought+", "100L", "C L G T"},
['drought_l']= {"Drought+", "100L", "C L G T ? !"},
['marathon_n']= {"Marathon", "THƯỜNG", "Xoá 200 hàng với tốc độ nhanh dần"},
['marathon_h']= {"Marathon", "KHÓ", "Xoá 200 hàng với tốc độ cao"},
['solo_e']= {"Battle", "DỄ", "Đánh bại AI!"},
@@ -913,12 +925,12 @@ C. Tay cầm chơi game (Gamepad):
['solo_h']= {"Battle", "KHÓ", "Đánh bại AI!"},
['solo_l']= {"Battle", "RẤT KHÓ", "Đánh bại AI!"},
['solo_u']= {"Battle", "THÁCH ĐẤU", "Đánh bại AI!"},
['techmino49_e']= {"Tech 49", "DỄ", "Cuộc chiến 49 người.\nNgười trụ lại cuối cùng giành chiến thắng"},
['techmino49_h']= {"Tech 49", "KHÓ", "Cuộc chiến 49 người.\nNgười trụ lại cuối cùng giành chiến thắng"},
['techmino49_u']= {"Tech 49", "THÁCH ĐẤU", "Cuộc chiến 49 người.\nNgười trụ lại cuối cùng giành chiến thắng"},
['techmino99_e']= {"Tech 99", "DỄ", "Cuộc chiến 99 người.\nNgười trụ lại cuối cùng giành chiến thắng"},
['techmino99_h']= {"Tech 99", "KHÓ", "Cuộc chiến 99 người.\nNgười trụ lại cuối cùng giành chiến thắng"},
['techmino99_u']= {"Tech 99", "THÁCH ĐẤU", "Cuộc chiến 99 người.\nNgười trụ lại cuối cùng giành chiến thắng"},
['techmino49_e']= {"Tech VS 49", "DỄ", "Cuộc chiến 49 người.\nNgười trụ lại cuối cùng sẽ giành chiến thắng"},
['techmino49_h']= {"Tech VS 49", "KHÓ", "Cuộc chiến 49 người.\nNgười trụ lại cuối cùng sẽ giành chiến thắng"},
['techmino49_u']= {"Tech VS 49", "THÁCH ĐẤU", "Cuộc chiến 49 người.\nNgười trụ lại cuối cùng sẽ giành chiến thắng"},
['techmino99_e']= {"Tech VS 99", "DỄ", "Cuộc chiến 99 người.\nNgười trụ lại cuối cùng sẽ giành chiến thắng"},
['techmino99_h']= {"Tech VS 99", "KHÓ", "Cuộc chiến 99 người.\nNgười trụ lại cuối cùng sẽ giành chiến thắng"},
['techmino99_u']= {"Tech VS 99", "THÁCH ĐẤU", "Cuộc chiến 99 người.\nNgười trụ lại cuối cùng sẽ giành chiến thắng"},
['round_e']= {"Turn-Based", "DỄ", "Chơi theo lượt và đánh bại AI!"},
['round_n']= {"Turn-Based", "THƯỜNG", "Chơi theo lượt và đánh bại AI!"},
['round_h']= {"Turn-Based", "KHÓ", "Chơi theo lượt và đánh bại AI!"},
@@ -969,17 +981,15 @@ C. Tay cầm chơi game (Gamepad):
['pc_h']= {"PC Challenge", "KHÓ", "Lấy càng nhiều PC càng tốt trong 100 hàng!"},
['pc_l']= {"PC Challenge", "RẤT KHÓ", "Lấy càng nhiều PC càng tốt trong 100 hàng!"},
['pc_inf']= {"Inf. PC Challenge", "", "Lấy càng nhiều PC càng tốt"},
['tech_n']= {"Tech", "THƯỜNG", "Cố gắng không phá B2B!"},
['tech_n_plus']= {"Tech", "THƯỜNG+", "Chỉ được clear Spin hoặc PC"},
['tech_h']= {"Tech", "KHÓ", "Cố gắng không phá B2B!"},
['tech_h_plus']= {"Tech", "KHÓ+", "Chỉ được clear Spin hoặc PC"},
['tech_l']= {"Tech", "RẤT KHÓ", "Cố gắng không phá B2B!"},
['tech_l_plus']= {"Tech", "RẤT KHÓ+", "Chỉ được clear Spin hoặc PC"},
['tech_finesse']= {"Tech", "HOÀN HẢO", "Không được phép có lỗi di chuyển!"},
['tech_finesse_f']= {"Tech", "HOÀN HẢO+", "Không được phép có lỗi di chuyển hoặc loại Xoá hàng thường!"},
['tech_finesse_lock']= {"Tech", "FINESSE LOCK", "Không được mắc lỗi di chuyển hoặc bạn không thể điều khiển gạch!"},
['tech_finesse_lock_f']= {"Tech", "FINESSE+ LOCK", "Không được mắc lỗi di chuyển hoặc bạn không thể điều khiển gạch! Và không thể dùng kiểu xóa thường!"},
['tsd_e']= {"TSD Challenge", "DỄ", "Chỉ được làm T-Spin Double!"}, -- Chỉ được clear…
['tech_n']= {"Tech B2B", "THƯỜNG", "Cố gắng không phá B2B!"},
['tech_n_plus']= {"Tech B2B", "THƯỜNG+", "Chỉ được clear Spin hoặc PC"},
['tech_h']= {"Tech B2B", "KHÓ", "Cố gắng không phá B2B!"},
['tech_h_plus']= {"Tech B2B", "KHÓ+", "Chỉ được clear Spin hoặc PC"},
['tech_l']= {"Tech B2B", "RẤT KHÓ", "Cố gắng không phá B2B!"},
['tech_l_plus']= {"Tech B2B", "RẤT KHÓ+", "Chỉ được clear Spin hoặc PC"},
['tech_finesse']= {"Kỹ thuật di chuyển","", "Không được phép có lỗi di chuyển!"},
['tech_finesse_f']= {"Kỹ thuật di chuyển","Khg ĐƠN/ĐÔI/TAM","Không được phép có lỗi di chuyển hoặc kiểu Xoá hàng thường!"},
['tsd_e']= {"TSD Challenge", "DỄ", "Chỉ được làm T-Spin Double!"},
['tsd_h']= {"TSD Challenge", "KHÓ", "Chỉ được làm T-Spin Double!"},
['tsd_u']= {"TSD Challenge", "THÁCH ĐẤU", "Chỉ được làm T-Spin Double!"},
['backfire_n']= {"Backfire", "THƯỜNG", "Sống sót những hàng rác do chính bạn gửi"},
@@ -1034,7 +1044,7 @@ C. Tay cầm chơi game (Gamepad):
"B2B2B2B tồn tại hả?",
"Đừng để những thứ nhỏ nhặt làm bạn nản chí!",
"Đây không phải là lỗi, đây là tính năng!",
"Hệ thống xoay gạch của Techmino rất đẹp trai!",
"Hệ thống xoay gạch của Techmino rất đẹp trai!",
"Em rất tốt nhưng anh rất tiếc…", -- EXCELLENT! But… let's go better next time
"Đừng quên xem qua phần cài đặt!",
"Nếu bạn thấy có vấn đề gì, hãy lên trang GitHub báo lại cho chúng tôi!",
@@ -1054,7 +1064,7 @@ C. Tay cầm chơi game (Gamepad):
"Hãy tham gia Discord của chúng tôi!",
"l-=-1",
"Nổi lửa lên em, NỔI LỬA LÊN EM!",
"Việc giảm tần số khung hình sẽ mang trải nghiệm tệ hơn cho bạn",
"Việc giảm tần số khung hình sẽ mang đến trải nghiệm tệ hơn cho bạn",
"LrL RlR LLr RRl RRR LLL FFF RfR RRf rFF",
"Mix clear sắp ra mắt!",
"Hầu hết các biểu tượng của các nút được vẽ tay vào trong bảng Unicode Private Use Area",
@@ -1117,7 +1127,6 @@ C. Tay cầm chơi game (Gamepad):
{C.lP,"Con số bí mật: 626"},
{C.lR,"Z ",C.lG,"S ",C.lS,"J ",C.lO,"L ",C.lP,"T ",C.lY,"O ",C.lC,"I"},
{C.lY,"MÁT QUÁ!!"},
{C.N,"Lua",C.Z," No.1"},
{C.P,"T-spin!"},
{C.R,"DMCA là gì?"},
{C.R,"\"Luật sở hữu trí tuệ\""},
@@ -1128,7 +1137,7 @@ C. Tay cầm chơi game (Gamepad):
{C.Z,"Gì? ",C.lC,"Xspin?"},
-- Bắt đầu từ đoạn này là mình phải lấy từ bản tiếng Trung nhờ cả Bard lẫn GPT3.5 dịch hộ :|
-- TECHMINO FUN FACT
-- How do you pronounce Techmino?
"Phát âm từ Techmino như thế nào mới đúng?",
@@ -1141,14 +1150,13 @@ C. Tay cầm chơi game (Gamepad):
-- Techmino's birthday
"Ngày sinh nhật của Techmino? Hiện tại (đang giả định) là 26/T6.",
-- How to O-spin: Rotate 626 times in one second (mistaken)
"Cách O-spin? Nhấn phím xoay 626 lần (ĐÙA ĐẤY ĐỪNG TIN!)",
"Cách O-spin? Nhấn phím xoay 626 lần trong 1 giây (ĐÙA ĐẤY ĐỪNG TIN!)",
-- Hope you all like Z... Oh no, like Techmino
{"Mình mong các bạn sẽ thích ",C.W,"Z",C.Z,"… Ối! Không phải, thích ",C.G,"Techmino",C.Z," cơ! Nhầm nhầm nhầm!"},
-- 2021 was the year of Techmino's online debut.
"2021 là năm ra mắt chế độ trực tuyến của Techmino.",
-- The Chinese name of this game is 'Block Research Institute'.
"Tên chính thức của game là \"方块研究所\" (Block Research Institute).",
"Một tên khác của game này là \"Tiehu Minuo\"",
"Tên chính thức của game là \"方块研究所\"",
-- This game is not called Teachmino
"Tên game không phải là Teachmino!",
--
@@ -1171,37 +1179,37 @@ C. Tay cầm chơi game (Gamepad):
-- Don't play with your phone if your homework isn't finished.
"Đừng chơi điện thoại khi bài tập về nhà còn chưa hoàn thành.",
-- Enabling vibration on some mobile systems may cause severe lag."
"Bật rung trên điện thoại có thể khiến máy phải thở oxy.",
"Bật rung trên một số điện thoại có thể làm cho chúng… phải thở oxy!",
-- Eat the button? Really? I suggest you play it back to see if you pressed it and how long it took you to press it"
"Phím không ăn? Đùa à? Xem lại replay để chắc rằng ông đã nhấn và xem thử mất bao nhiêu thời gian để ông nhấn phím đó.",
"Phím không ăn? Hãy thử kiểm tra lại phím đi!",
-- Probably someone will read the tip
"Chắc chắn có người đang đọc cái dòng chữ nhỏ đang chạy ở dưới này.",
-- It seems like no one has reached a high level by playing with their feet yet.
"Hình như tới giờ chưa ai chơi xếp gạch giỏi bằng chân…",
-- Moderate gaming is good for the brain. Addiction to games is harmful. Plan your time
"Chơi game vừa phải có thể tốt cho bộ não. Nhưng nếu nghiện thì toeng! Nhớ quản lý thời gian nhé!",
"Chơi game vừa phải có thể tốt cho bộ não. Nhưng nếu nghiện thì toeng! Nhớ lập th!ời gian biểu nhé!",
-- The ability to dig is extremely important in battles!!!
"Khả năng đào xuống (downstacking) của bạn là RẤT QUAN TRỌNG trong chiến đấu!!!",
-- Skilled players of the Classic Tetris game are also formidable; don't underestimate them
"Xếp gạch cổ điển cũng không đơn giản gì như xếp gạch hiện đại đâu. Đừng có mà xem thường những người chơi hệ cổ điển!",
"Đừng có mà xem thường những người chơi xếp gạch cổ điển! Chơi cái đó cũng khó không khác gì chơi xếp gạch hiện đại đâu.",
-- Classic Tetris and Modern Tetris are two different games; being skilled in one doesn't mean you'll be skilled in the other. You have to start from scratch.
"Xếp gạch cổ điển và xếp gạch hiện đại là hai thể loại game khác nhau đấy! Giỏi một trong hai chưa chắc bạn giỏi cả bên còn lại đâu. Bạn phải học lại từ đầu đấy",
-- To protect the players' well-being, the game has a temporary and simplified anti-addiction system! (But you probably won't trigger it, haha)
"Để tránh việc người chơi nào đó chơi quá lâu, game đã có hệ thống chống nghiện đơn giản tạm thời (Nhưng bạn có lẽ sẽ không bao giờ kích hoạt chúng đâu, haha)",
-- Basic stacking and digging skills are crucial; those who neglect these two aspects often regret it (trust me)
{"Kỹ năng xếp lên vào đào xuống là 2 kỹ năng RẤT quan trọng; những ai (đã/từng) coi thường/bỏ bê hai khía cạnh này thường hay bị bón hành súp mặt lờ (tin ",C.W,"MrZ",C.Z," đi!)"},
{"Kỹ năng xếp lên vào đào xuống là 2 kỹ năng RẤT quan trọng; những ai (đã/từng) coi thường hay bỏ bê hai khía cạnh này thường hay bị bón hành súp mặt lờ (tin ",C.W,"MrZ",C.Z," đi!)"},
-- Even if you're topped out, don't give up; every line of garbage can potentially become your weapon.
"Ngay cả khi bạn sắp bị top out, đừng bỏ cuộc; vì từng hàng rác có tiềm năng trở thành vũ khí của bạn!",
"Đừng bỏ cuộc khi đống hàng rác đang làm bạn sắp bị top out, bởi bạn có thể biến chúng trở thành đòn phản công.",
-- The video shown above is not a recording; it's the robot playing in real-time.
"Cái ở trên là replay hả? Không, là AI đang chơi trong thời gian thực đấy!",
"Cái ở trên là bản ghi sẵn hả? Không, là AI đang chơi trong thời gian thực đấy!",
-- Extended gaming sessions will gradually deteriorate your performance! Remember to take breaks when playing for a long time~
"THường xuyên chơi game lâu có thể khiến bạn có thể bị đuối sức (cả thể chất và tinh thần, tệ nhất có thể bị stall). Hãy nhớ nghỉ giải lao sau khi chơi lâu nhé!",
"Thường xuyên chơi game lâu có thể khiến bạn có thể bị đuối sức (cả thể chất và tinh thần, tệ nhất có thể bị stall). Hãy nhớ nghỉ giải lao sau khi chơi lâu nhé!",
-- Be careful of tenosynovitis!
{C.R,"CẢNH BÁO! ",C.Z,"Bệnh viêm bao gân cổ tay!"},
-- The button with a question mark in the bottom-right corner is the game manual (assuming you haven't enabled the concise mode).
-- The button with a question mark in the bottom-right corner is the game manual (assuming you haven't enabled the Simple mode).
"Cái nút "..CHAR.icon.help.." ở góc phải dưới cùng trong menu (không bật chế độ Đơn giản) đấy hả? Nó là manual (hướng dẫn sử dụng) của game đấy!",
-- If you're new to blocks, just play more games; there isn't much specific targeted practice beyond 40 lines in two minutes
"Bạn mới tập chơi xếp gạch à? Nếu vậy cứ chơi nhiều lên. Không có nhiều mục tiêu luyện tập cụ thể ngoài xóa 40 hàng trong 2 phút.",
"Bạn mới tập chơi xếp gạch à? Nếu vậy cứ chơi nhiều lên. Không có nhiều mục tiêu luyện tập cụ thể ngoài xóa 40 hàng trong 2 phút đâu!",
--
"Hãy ra ngoài và chạm cỏ đi!",
--
@@ -1211,7 +1219,7 @@ C. Tay cầm chơi game (Gamepad):
--
-- Z SAID
-- I can't write cool music (crying)
{C.W,"Z: ",C.Z,"Tôi không thể nào viết một bản nhạc nào trông ngầu cả (sadge)."},
{C.W,"Z: ",C.Z,"Tôi không tài nào viết nổi một bản nhạc nào trông ngầu cả (sadge)."},
-- I haven't studied music composition. I just composed it myself. If you really think it's good, that's great!
{C.W,"Z: ",C.Z,"Tôi chưa từng học sáng tác nhạc, và tôi chỉ tự sáng tác chúng. Nếu bạn thấy những bản nhạc này hay, thật tuyệt!"},
-- What else can I write for tips?
@@ -1222,6 +1230,7 @@ C. Tay cầm chơi game (Gamepad):
{C.W,"Z: ",C.Z,"Tôi tự hỏi là có bao nhiêu người chơi game thực sự quan tâm ai viết ra nó."},
--
-- IT JOKES
{C.N,"Lua",C.Z," No.1"},
"git clone --recursive https://github.com/26F-Studio/Techmino.git",
"git commit -m \".\"",
"git pull = git fetch + git merge",
@@ -1232,10 +1241,9 @@ C. Tay cầm chơi game (Gamepad):
"git submodule update",
"git merge --rebase",
"git merge --squash",
"Lua No.1",
"sudo rm -rf /*",
"shutdown /s /t 0", -- Turn off computer completely (no Fast Boot)
"shutdown /s /t 0 /hybrid", -- Turn off computer with Fast Boot still activated
"shutdown /s /t 0 /hybrid", -- Turn off computer with Fast Boot
-- Techmino has reached the limit.
"Không thể mở Techmino vì bạn đã quá nghiện game này rồi :>",
-- Techmino.exe has stopped working.
@@ -1246,26 +1254,25 @@ C. Tay cầm chơi game (Gamepad):
--
-- CHANGELOG
{C.lW, "V0.0.091726",": ",C.Z, "Đã thêm hệ thống xoay TRS"},
{C.lW, "V0.7.9 " ,": ",C.Z, "Đã thêm ",C.yellow,"O-spin"},
{C.lW, "V0.7.9" ,": ",C.Z, "Đã thêm ",C.yellow,"O-spin"},
{C.lW, "V0.7.19" ,": ",C.Z, "Đã thêm hệ thống voice"},
{C.lW, "V0.7.22" ,": ",C.Z, "Đã thêm hiệu ứng rơi mượt"},
{C.lW, "V0.8.5 " ,": ",C.Z, "Đã thêm map và sắp xếp lại các chế độ"},
{C.lW, "V0.8.5" ,": ",C.Z, "Đã thêm map và sắp xếp lại các chế độ"},
{C.lW, "V0.8.19" ,": ",C.Z, "Đã thêm Pentomino"},
{C.lW, "V0.9.0 " ,": ",C.Z, "Đã thêm chế độ tự do và khả năng tùy biến chuỗi gạch"},
{C.lW, "V0.9.0" ,": ",C.Z, "Đã thêm chế độ tự do và khả năng tùy biến chuỗi gạch"},
{C.lW, "V0.10.0" ,": ",C.Z, "Đã thêm hệ thống replay"},
{C.lW, "V0.11.1" ,": ",C.Z, "Đã thêm Little Z Dictionary (Zictionary)"},
{C.lW, "V0.12.2" ,": ",C.Z, "Đã thêm hệ thống mod"},
{C.lW, "V0.13.0" ,": ",C.Z, "Thử nghiệm chế độ trực tuyến"},
{C.lW, "V0.13.2" ,": ",C.Z, "Đã thêm khả năng tùy biến chiều cao bảng"},
{C.lW, "V0.13.3" ,": ",C.Z, "Đã thêm console"},
{C.lW, "V0.14.5" ,": ",C.Z, "Đã thêm BGM đầu tiên được làm bởi cộng đồng"},
{C.lW, "V0.14.5" ,": ",C.Z, "Đã thêm BGM đầu tiên được remix bởi cộng đồng"},
{C.lW, "V0.15.5" ,": ",C.Z, "Đã thêm menu replay"},
{C.lW, "V0.16.0" ,": ",C.Z, "Đã thêm hệ thống xoay BiRS"},
{C.lW, "V0.16.2" ,": ",C.Z, "Đã thêm studio SFX với phong cách hit pad"},
{C.lW, "V0.17.0" ,": ",C.Z, "Đã thêm hõ trợ điều khiển bằng joystick"},
{C.lW, "V0.17.3" ,": ",C.Z, "Dừng phát triển Techmino, tập trung phát triển game mới"},
{C.lW, "V0.17.12" ,": ",C.Z, "Đã thêm ngôn ngữ tiếng Việt"},
{C.lW, "V0.17.15" ,": ",C.Z, "Hoàn thiện bản dịch tiếng Việt?"},
--
-- MATH FORMULAS
"(a+b)²=a²+2ab+b²",
@@ -1289,19 +1296,22 @@ C. Tay cầm chơi game (Gamepad):
"sin²α-sin²β=S(α+β)S(α-β)",
"sin2α=2SαCα",
--
-- FROM SEA
-- SEA'S JOKE
{C.W,"MrZ",C.Z," còn có một biệt danh dễ thương hơn, đó là ",C.W,"Z-Chan"},
"Có hơn 400 mẹo bạn có thể nhìn thấy ở đây, là cái dòng chữ này, nếu bạn đang chơi Techmino tiếng Trung",
{C.lSea,"Sea: ",C.Z,"Tui không có đủ mặn để viết joke. Nên một số câu đùa đang chạy ở đây được viết bởi ",C.yellow,"Shard Nguyễn",C.Z,". \"Em cảm ơn anh!\""},
{C.lSea,"Sea: ",C.Z,"Tui đang tự hỏi liệu còn bao nhiêu lỗi tui bỏ sót lúc dịch game không? Tính ra tui đã cập nhật đi cập nhật lại cũng 4-5 lần rồi."},
{"Cộng đồng Tetris ",C.R,"Việt ",C.lY,"Nam ",C.Z,": https://discord.gg/jX7BX9g"}, -- Tetris Vietnam
{"Cộng đồng Tetris ",C.R,"Việt ",C.lY,"Nam ",C.Z,": https://discord.gg/hoiphuhovietnam"}, -- Tetris Vietnam (TVN)
{"\"Tetris Việt Nam\"? Không, đó là \"Hội phụ hồ Việt Nam\""},
{C.W,"MrZ ",C.Z,"vẫn chưa biết chọn tên nào để đặt cho từ điển của Techmino. Hiện có 3 tên: \"Zictionary\", \"TetroDictionary\"\"Little Z Dictionary\""},
"Ủa tao nhớ game này tên là xếp hình mà? Ừ thì đúng nhưng để giữ độ đồng nhất và tránh bị cấn mồm thì nên gọi game này là game xếp gạch.",
"Mình xin phép ủng hộ cho player này. Ủng hộ càng nhiều tỉ lệ thắng càng cao!",
{"Aiiiii mua cần phô mai ủng hộ ",C.yellow,"Chủ tiệm phô mai",C.Z," không?"}, -- A joke
{"Hôm nay là ngày ",os.date("%d"),"/",all_month[tonumber(os.date("%m"))],"/",os.date("%Y")}, -- inspired from Nokia 1280, activating talking clock by holding * key at main menu
{"Aiiiii mua cần phô mai ủng hộ ",C.yellow,"Chủ tiệm phô mai",C.Z," không?"}, -- A joke in TVN
-- Who will you choose? A girl that can break up to you and make you sad
-- Or choose Katyusha that can warm your heart and 40ha land of enemy.
"Bạn sẽ chọn ai? Một em gái có thể chia tay và làm bạn buồn? Hay là chọn em Katyusha có thể làm ấm lòng bạn và 40ha đất kẻ thù?", -- Based on a comment in https://www.youtube.com/watch?v=nczdLwTyWmY
},
pumpkin="Tôi là một quả bí ngô",
}

View File

@@ -28,7 +28,7 @@ return {
clear={"Single","Double","Triple","Techrash","Pentacrash","Hexacrash","Heptacrash","Octacrash","Nonacrash","Decacrash","Undecacrash","Dodecacrash","Tridecacrash","Tetradecacrash","Pentadecacrash","Hexadecacrash","Heptadecacrash","Octadecacrash","Nonadecacrash","Ultracrash","Impossicrash"},
cleared="$1 lines",
mini="Mini",b2b="B2B ",b3b="B2B2B ",
PC="Perfect Clear",HPC="Half Clear",
PC="Perfect Clear",HPC="Half PC",
replaying="[回放]",
tasUsing="[TAS]",
@@ -43,6 +43,7 @@ return {
infHeightOn="无限高度 开",
infHeightOff="无限高度 关",
infHeightHint="用功能键1切换",
-- highestGrade="(highest: $1)",
speedLV="速度等级",
piece="块数",line="行数",atk="攻击",eff="效率",
@@ -54,7 +55,7 @@ return {
win="胜利",
lose="失败",
torikan="未达标",
finish="完成",
gamewin="胜利",
gameover="游戏结束",
@@ -66,10 +67,13 @@ return {
page="页面:",
ai_puzzle="不能同时开启AI和拼图模式",
ai_mission="不能同时开启AI和自定义任务",
ai_badPiece="不能同时开启AI和含有非四连块的自定义序列",
cc_fixed="不能同时开启CC和固定序列",
cc_swap="不能同时开启CC和swap的暂存模式",
ai_prebag="不能同时开启AI和含有非四连块的自定义序列",
ai_mission="不能同时开启AI和自定义任务",
cc_solid="开启CC时不能存在预先填满的行",
cc_field_too_high="开启CC时最高出块高度不能超过40",
switchSpawnSFX="请开启方块生成音效",
needRestart="重新开始以生效",
@@ -91,6 +95,8 @@ return {
dataCorrupted="数据损坏",
pasteWrongPlace="提醒:可能粘贴错地方了",
noFile="找不到文件",
invalidSequence="无效序列模式",
tooHighField="超过126行的场地数据已被丢弃",
nowPlaying="正在播放:",
@@ -233,7 +239,7 @@ return {
"落块:",
"消行/挖掘:",
"攻击/挖掘攻击:",
"上涨/接收/抵消:",
"上涨(接收-抵消):",
"消除:",
"Spin:",
"B2B/B3B/PC/HPC:",
@@ -297,7 +303,7 @@ return {
"LawrenceLiu",
"Gompyn",
"flaribbit",
"schh",
"scdhh",
"",
"视觉设计、UI和UX",
"MrZ",
@@ -327,6 +333,7 @@ return {
"Miya",
"Xiaoya",
"Mono",
"Flore",
"MrZ",
"Trebor",
"",
@@ -404,7 +411,7 @@ return {
手柄:
按 $10 to 显示帮助
按 $5 或 $6 滚动文本, 按 $11 加速
按 $5 或 $6 滚动文本
按 $7 打开上一词条,按 $8 打开下一词条。按住 $11 加速
按住 $11 并按 $6 缩小字号,或 $5 增大字号]]
-- 1-4: Up, Down, Left, Right
@@ -489,7 +496,7 @@ return {
capacity="房间容量",
create="创建",
ospin="O-spin",
ospin="TRS O-spin",
fineKill="强制极简",
b2bKill="强制B2B",
lockout="锁定在外时失败",
@@ -572,6 +579,10 @@ return {
bg_on="普通背景",
bg_off="关闭背景",
bg_custom="应用自定义背景",
defaultBG="默认背景",
resetDbg="改回默认",
lockBG="锁定背景",
noTheme="关闭节日主题",
blockSatur="方块饱和度",
fieldSatur="场地饱和度",
@@ -706,7 +717,7 @@ return {
bufferLimit="缓冲上限",
heightLimit="高度上限",
ospin="O-spin",
ospin="TRS O-spin",
fineKill="强制极简",
b2bKill="强制B2B",
lockout="锁定在外时失败",
@@ -853,7 +864,9 @@ return {
['sprint_100l']= {"竞速", "100L", "消除100行"},
['sprint_400l']= {"竞速", "400L", "消除400行"},
['sprint_1000l']= {"竞速", "1000L", "消除1000行"},
['secret_grade']= {"秘密段位", "", "按照提示完成经典的“大于号”拼图"},
['construct_sg']= {"拼花", "秘密段位", "按照提示完成经典的“大于号”拼图"},
['construct_checker']={"拼花", "棋盘", "按照提示搭建棋盘的图案"},
['construct_invsg']= {"拼花", "折线", "按照提示搭建折线图案"},
['sprintPenta']= {"竞速", "五连块", "伤脑筋十八块"},
['sprintMPH']= {"竞速", "MPH", "纯随机\n无预览\n无暂存"},
['sprint123']= {"竞速", "M123", "40L但只有1~3连块"},
@@ -886,8 +899,8 @@ return {
['round_h']= {"回合制", "困难", "下棋模式"},
['round_l']= {"回合制", "疯狂", "下棋模式"},
['round_u']= {"回合制", "极限", "下棋模式"},
['big_n']= {"大方块", "普通", "模拟5×10场地的玩法(标准尺寸的一半)"},
['big_h']= {"大方块", "困难", "模拟5×10场地的玩法(标准尺寸的一半)"},
['big_n']= {"大方块", "普通", "模拟5×10场地(标准尺寸的一半)"},
['big_h']= {"大方块", "困难", "模拟5×10场地(标准尺寸的一半)"},
['master_n']= {"大师", "普通", "20G初心者练习"},
['master_h']= {"大师", "困难", "上级者20G挑战"},
['master_m']= {"大师", "大师", "大师20G"},
@@ -939,8 +952,6 @@ return {
['tech_l_plus']= {"科研", "疯狂+", "仅允许spin与PC"},
['tech_finesse']= {"科研", "极简", "强制最简操作"},
['tech_finesse_f']= {"科研", "极简+", "禁止普通消除,强制最简操作"},
['tech_finesse_lock']= {"科研", "极简限制", "限制操作次数"},
['tech_finesse_lock_f']={"科研", "极简限制+", "限制操作次数禁止断B2B"},
['tsd_e']= {"TSD挑战", "简单", "你能连续做几个TSD"},
['tsd_h']= {"TSD挑战", "困难", "你能连续做几个TSD"},
['tsd_u']= {"TSD挑战", "极限", "你能连续做几个TSD"},

View File

@@ -42,6 +42,7 @@ return {
infHeightOn="infHeight=true",
infHeightOff="infHeight=false",
infHeightHint="F1:!infHeight",
highestGrade="(max=$1)",
speedLV="P.SpeedLV",
piece="P.Piece",line="P.Line",atk="P.ATK",eff="P.EFF",
@@ -53,7 +54,7 @@ return {
win="Win();",
lose="Lose();",
torikan="Torikan();",
finish="Result=Finish",
gamewin="Result=Win",
gameover="Result=GameOver",
@@ -65,10 +66,13 @@ return {
page="Page=",
cc_fixed=" if (AI==CC and Sequence==Fixed) then Error(); end",
cc_swap=" if (AI==CC and Hold.Mode==Swap) then Error(); end",
ai_prebag=" if (AI==true and Ctm.Seq~=Tetromino) then Error(); end",
ai_mission=" if (AI==true and Ctm.Mission~=true) then Error(); end",
ai_puzzle="assert(AI==true and Ctm.Mission~=true)",
ai_mission="assert(AI==true and Ctm.Mission~=true)",
ai_badPiece="assert(AI==true and Ctm.Seq~=Tetromino)",
cc_fixed="assert(AI==CC and Sequence==Fixed)",
cc_swap="assert(AI==CC and Hold.Mode==Swap)",
cc_solid="assert(AI==CC and filledLine in Field)",
cc_field_too_high="assert(AI==CC and Field.Height>=40)",
switchSpawnSFX="SpawnSFX=false",
needRestart="NeedRestart=true",
@@ -90,6 +94,8 @@ return {
dataCorrupted="Error.DataCorrupted();",
pasteWrongPlace="Error.PasteWrongPlace();",
noFile="Error.NoFile();",
invalidSequence="Error.InvalidSequenceMode();",
tooHighField="Error.TooHighField();",
nowPlaying="NowPlaying=",
@@ -99,7 +105,7 @@ return {
noScore="NoScore=true",
modeLocked="Locked=true",
unlockHint=" if (PreviousMode<=B) then Unlock();",
unlockHint="if (PreviousMode<=B) then Unlock();",
highScore="HighScore",
newRecord="NewRecord=true",
@@ -181,7 +187,7 @@ return {
"P.Pieces",
"P.Row/Dig",
"P.Atk/DAtk",
"P.Receive",
"P.Rise(P.Receive-P.Offset)",
"P.Clear",
"P.Spin",
"P.B2B/B3B;P.PC/HPC",
@@ -195,7 +201,7 @@ return {
"Stat.Time = ",
"Stat.Key/Rot/Hold = ",
"Stat.Block/Row/Atk = ",
"Stat.Recv/Res/Asc = ",
"Stat.Recv/Offset/Rise = ",
"Stat.Dig/DAtk = ",
"Stat.Eff/DEff = ",
"Stat.B2B/B3B = ",
@@ -245,7 +251,7 @@ return {
"LawrenceLiu",
"Gompyn",
"flaribbit",
"schh",
"scdhh",
"",
"视觉设计、UI和UX",
"MrZ",
@@ -275,6 +281,7 @@ return {
"Miya",
"Xiaoya",
"Mono",
"Flore",
"MrZ",
"Trebor",
"",
@@ -491,6 +498,10 @@ return {
bg_on="Set.BG(Normal);",
bg_off="Set.BG(Off);",
bg_custom="Set.BG(Ctm);",
-- defaultBG="Default B.G.",
-- resetDbg="Reset to default",
-- lockBG="Lock B.G.",
-- noTheme="Disable theme",
blockSatur="Set.BlockSatur",
fieldSatur="Set.FieldSatur",
@@ -766,116 +777,116 @@ return {
},
},
modes={
['sprint_10l']= {"Sprint(10L);", "", "消除10行"},
['sprint_20l']= {"Sprint(20L);", "", "消除20行"},
['sprint_40l']= {"Sprint(40L);", "", "消除40行"},
['sprint_100l']= {"Sprint(100L);", "", "消除100行"},
['sprint_400l']= {"Sprint(400L);", "", "消除400行"},
['sprint_1000l']= {"Sprint(1000L);", "", "消除1000行"},
['secret_grade']= {"SecretGrade();", "", "按照提示完成经典的“大于号”拼图"},
['sprintPenta']= {"Sprint(Penta);", "", "伤脑筋十八块"},
['sprintMPH']= {"Sprint(MPH);", "", "纯随机\n无预览\n无暂存"},
['sprint123']= {"Sprint(M123);", "", "40L但只有1~3连"},
['dig_10l']= {"Dig(10L);", "", "挖掘10行"},
['dig_40l']= {"Dig(40L);", "", "挖掘40行"},
['dig_100l']= {"Dig(100L);", "", "挖掘100"},
['dig_400l']= {"Dig(400L);", "", "挖掘400"},
['dig_eff_10l']= {"DigEff(10L);", "", "用尽量少的块数挖掘10行"},
['dig_eff_40l']= {"DigEff(40L);", "", "用尽量少的块数挖掘40行"},
['dig_eff_100l']= {"DigEff(100L);", "", "用尽量少的块数挖掘100"},
['dig_eff_400l']= {"DigEff(400L);", "", "用尽量少的块数挖掘400"},
['dig_quad_10l']= {"DigQuad(10L);", "", "挖掘10行但只能消四"},
['drought_n']= {"Drought(100L);", "", "你I没了"},
['drought_l']= {"DroughtP(100L);", "", "后 妈 发 牌"},
['marathon_n']= {"Marathon(Normal);", "", "200行加速马拉松"},
['marathon_h']= {"Marathon(Hard);", "", "200行高速马拉松"},
['solo_e']= {"Solo(Easy);", "", "打败AI"},
['solo_n']= {"Solo(Normal);", "", "打败AI"},
['solo_h']= {"Solo(Hard);", "", "打败AI"},
['solo_l']= {"Solo(Lunatic);", "", "打败AI"},
['solo_u']= {"Solo(Ultimate);", "", "打败AI"},
['techmino49_e']= {"Tech49(Easy);", "", "49人混战活到最后"},
['techmino49_h']= {"Tech49(Hard);", "", "49人混战活到最后"},
['techmino49_u']= {"Tech49(Ultimate);", "", "49人混战活到最后"},
['techmino99_e']= {"Tech99(Easy);", "", "99人混战活到最后"},
['techmino99_h']= {"Tech99(Hard);", "", "99人混战活到最后"},
['techmino99_u']= {"Tech99(Ultimate);", "", "99人混战活到最后"},
['round_e']= {"Round(Easy);", "", "下棋模式"},
['round_n']= {"Round(Normal);", "", "下棋模式"},
['round_h']= {"Round(Hard);", "", "下棋模式"},
['round_l']= {"Round(Lunatic);", "", "下棋模式"},
['round_u']= {"Round(Ultimate);", "", "下棋模式"},
['big_n']= {"Big(Normal);", "", "模拟10*5场地的玩法(标准尺寸的一半)"},
['big_h']= {"Big(Hard);", "", "模拟10*5场地的玩法(标准尺寸的一半)"},
['master_n']= {"Master(Normal);", "", "20G初心者练习"},
['master_h']= {"Master(Hard);", "", "上级者20G挑战"},
['master_m']= {"Master(M21);", "", "大师20G"},
['master_final']= {"Master(Final);", "", "究极20G:无法触及的终点"},
['master_ph']= {"Master(Phantasm);", "", "虚幻20G:"},
['master_g']= {"Master(Graded);", "", "20G段位考试"},
['master_ex']= {"Master(EX);", "", "成为方块大师"},
['master_instinct']={"Master(Instinct);", "", "当前块在出现后一小会后会隐形"},
['strategy_e']= {"Strategy(Easy);", "", "20G堆叠中速决策练习"},
['strategy_h']= {"Strategy(Hard);", "", "20G堆叠快速决策练习"},
['strategy_u']= {"Strategy(Ultimate);", "", "20G堆叠速决策练习"},
['strategy_e_plus']={"Strategy(EasyP);", "", "20G堆叠速决策练习\n无Hold"},
['strategy_h_plus']={"Strategy(HardP);", "", "20G堆叠速决策练习\n无Hold"},
['strategy_u_plus']={"Strategy(UltimateP);","", "20G堆叠速决策练习\n无Hold"},
['blind_e']= {"Blind(Slow);", "", "不强大脑"},
['blind_n']= {"Blind(Fast);", "", "挺强大脑"},
['blind_h']= {"Blind(Instant);", "", "强大脑"},
['blind_l']= {"Blind(NoGhost);", "", "强大脑"},
['blind_u']= {"Blind(NoField);", "", "你准备好了吗"},
['blind_wtf']= {"Blind(Voie);" , "", "还没准备好"},
['classic_e']= {"Classic(Easy);", "", "高速经典"},
['classic_h']= {"Classic(Hard);", "", "飞速经典"},
['classic_l']= {"Classic(Lunatic);", "", "速经典"},
['classic_u']= {"Classic(Ultimate);", "", "速经典"},
['survivor_e']= {"Surviver(Easy);", "", "你能存活多久?"},
['survivor_n']= {"Surviver(Normal);", "", "你能存活多久?"},
['survivor_h']= {"Surviver(Hard);", "", "你能存活多久?"},
['survivor_l']= {"Surviver(Lunatic);", "", "你能存活多久?"},
['survivor_u']= {"Surviver(Ultimate);", "", "你能存活多久?"},
['attacker_h']= {"Attacker(Hard);", "", "进攻练习"},
['attacker_u']= {"Attacker(Ultimate);", "", "进攻练习"},
['defender_n']= {"Defender(Normal);", "", "防守练习"},
['defender_l']= {"Defender(Lunatic);", "", "防守练习"},
['dig_h']= {"Dig(Hard);", "", "挖掘练习"},
['dig_u']= {"Dig(Ultimate);", "", "挖掘练习"},
['c4wtrain_n']= {"C4WTrain(Normal);", "", "无 限 连 击"},
['c4wtrain_l']= {"C4WTrain(Lunatic);", "", "无 限 连 击"},
['pctrain_n']= {"PCTrain(Normal);", "", "简易PC题库熟悉全清定式的组合"},
['pctrain_l']= {"PCTrain(Lunatic);", "", "困难PC题库强算力者进"},
['pc_n']= {"PC(Normal);", "", "100行内刷PC"},
['pc_h']= {"PC(Hard);", "", "100行内刷PC"},
['pc_l']= {"PC(Lunatic);", "", "100行内刷PC"},
['pc_inf']= {"PC(Inf);", "", "你能连续做多少PC"},
['tech_n']= {"Tech(Normal);", "", "禁止断B2B"},
['tech_n_plus']= {"Tech(NormalP);", "", "仅允许spin与PC"},
['tech_h']= {"Tech(Hard);", "", "禁止断B2B"},
['tech_h_plus']= {"Tech(HardP);", "", "仅允许spin与PC"},
['tech_l']= {"Tech(Lunatic);", "", "禁止断B2B"},
['tech_l_plus']= {"Tech(LunaticP);", "", "仅允许spin与PC"},
['tech_finesse']= {"Tech(Finesse);", "", "强制最简操作"},
['tech_finesse_f']= {"Tech(FinesseF);", "", "禁止普通消除,强制最简操作"},
['tech_finesse_lock']= {"Tech(FineeseLock);","", "限制操作次数"},
['tech_finesse_lock_f']={"Tech(FineeseLockF);","", "限制操作次数禁止断B2B"},
['tsd_e']= {"TSD(Easy);", "", "你能连续做几个TSD"},
['tsd_h']= {"TSD(Hard);", "", "你能连续做几个TSD"},
['tsd_u']= {"TSD(Ultimate);", "", "你能连续做几个TSD"},
['backfire_n']= {"Backfire(Normal);", "", "打出100攻击"},
['backfire_h']= {"Backfire(Hard);", "", "打出100攻击"},
['backfire_l']= {"Backfire(Lunatic);", "", "打出100攻击"},
['backfire_u']= {"Backfire(Ultimate);", "", "打出100攻击"},
['sprintAtk']= {"Sprint(100ATK);", "", "打出100攻击"},
['sprintEff']= {"Sprint(EFF);", "", "40行内打出更高的攻击"},
['zen']= {"Zen(200L);", "", "不限时200行"},
['ultra']= {"Ultra(EXTRA);", "", "在两分钟内尽可能拿到最多的分数"},
['infinite']= {"Infinite();", "", "沙盒"},
['infinite_dig']= {"InfDig();", "", "挖呀挖呀挖"},
['marathon_inf']= {"Marathon(Inf);", "", "无尽马拉松"},
['sprint_10l']= {"Sprint(10L);", "", "消除10行"},
['sprint_20l']= {"Sprint(20L);", "", "消除20行"},
['sprint_40l']= {"Sprint(40L);", "", "消除40行"},
['sprint_100l']= {"Sprint(100L);", "", "消除100行"},
['sprint_400l']= {"Sprint(400L);", "", "消除400行"},
['sprint_1000l']= {"Sprint(1000L);", "", "消除1000行"},
['construct_sg']= {"Construct(SecretGrade);", "", "按照提示完成经典的“大于号”拼图"},
['construct_checker']= {"Construct", "", "按照提示搭建棋盘的图案"},
['construct_invsg']= {"Construct", "", "按照提示搭建折线图案"},
['sprintPenta']= {"Sprint(Penta);", "", "伤脑筋十八"},
['sprintMPH']= {"Sprint(MPH);", "", "纯随机\n无预览\n无暂存"},
['sprint123']= {"Sprint(M123);", "", "40L但只有1~3连块"},
['dig_10l']= {"Dig(10L);", "", "挖掘10行"},
['dig_40l']= {"Dig(40L);", "", "挖掘40行"},
['dig_100l']= {"Dig(100L);", "", "挖掘100"},
['dig_400l']= {"Dig(400L);", "", "挖掘400"},
['dig_eff_10l']= {"DigEff(10L);", "", "用尽量少的块数挖掘10行"},
['dig_eff_40l']= {"DigEff(40L);", "", "用尽量少的块数挖掘40行"},
['dig_eff_100l']= {"DigEff(100L);", "", "用尽量少的块数挖掘100行"},
['dig_eff_400l']= {"DigEff(400L);", "", "用尽量少的块数挖掘400行"},
['dig_quad_10l']= {"DigQuad(10L);", "", "挖掘10行但只能消四"},
['drought_n']= {"Drought(100L);", "", "你I没了"},
['drought_l']= {"DroughtP(100L);", "", "后 妈 发 牌"},
['marathon_n']= {"Marathon(Normal);", "", "200行加速马拉松"},
['marathon_h']= {"Marathon(Hard);", "", "200行高速马拉松"},
['solo_e']= {"Solo(Easy);", "", "打败AI"},
['solo_n']= {"Solo(Normal);", "", "打败AI"},
['solo_h']= {"Solo(Hard);", "", "打败AI"},
['solo_l']= {"Solo(Lunatic);", "", "打败AI"},
['solo_u']= {"Solo(Ultimate);", "", "打败AI"},
['techmino49_e']= {"Tech49(Easy);", "", "49人混战活到最后"},
['techmino49_h']= {"Tech49(Hard);", "", "49人混战活到最后"},
['techmino49_u']= {"Tech49(Ultimate);", "", "49人混战活到最后"},
['techmino99_e']= {"Tech99(Easy);", "", "99人混战活到最后"},
['techmino99_h']= {"Tech99(Hard);", "", "99人混战活到最后"},
['techmino99_u']= {"Tech99(Ultimate);", "", "99人混战活到最后"},
['round_e']= {"Round(Easy);", "", "下棋模式"},
['round_n']= {"Round(Normal);", "", "下棋模式"},
['round_h']= {"Round(Hard);", "", "下棋模式"},
['round_l']= {"Round(Lunatic);", "", "下棋模式"},
['round_u']= {"Round(Ultimate);", "", "下棋模式"},
['big_n']= {"Big(Normal);", "", "模拟5x10场地(标准尺寸的一半)"},
['big_h']= {"Big(Hard);", "", "模拟5x10场地(标准尺寸的一半)"},
['master_n']= {"Master(Normal);", "", "20G初心者练习"},
['master_h']= {"Master(Hard);", "", "上级者20G挑战"},
['master_m']= {"Master(M21);", "", "大师20G"},
['master_final']= {"Master(Final);", "", "究极20G:无法触及的终点"},
['master_ph']= {"Master(Phantasm);", "", "虚幻20G:"},
['master_g']= {"Master(Graded);", "", "20G段位考试"},
['master_ex']= {"Master(EX);", "", "成为方块大师"},
['master_instinct']= {"Master(Instinct);", "", "当前块在出现后一小会后会隐形"},
['strategy_e']= {"Strategy(Easy);", "", "20G堆叠速决策练习"},
['strategy_h']= {"Strategy(Hard);", "", "20G堆叠速决策练习"},
['strategy_u']= {"Strategy(Ultimate);", "", "20G堆叠速决策练习"},
['strategy_e_plus']= {"Strategy(EasyP);", "", "20G堆叠速决策练习\n无Hold"},
['strategy_h_plus']= {"Strategy(HardP);", "", "20G堆叠快速决策练习\n无Hold"},
['strategy_u_plus']= {"Strategy(UltimateP);", "", "20G堆叠极速决策练习\n无Hold"},
['blind_e']= {"Blind(Slow);", "", "强大脑"},
['blind_n']= {"Blind(Fast);", "", "强大脑"},
['blind_h']= {"Blind(Instant);", "", "很强大脑"},
['blind_l']= {"Blind(NoGhost);", "", "最强大脑"},
['blind_u']= {"Blind(NoField);", "", "你准备好了吗"},
['blind_wtf']= {"Blind(Voie);" , "", "还没准备好"},
['classic_e']= {"Classic(Easy);", "", "速经典"},
['classic_h']= {"Classic(Hard);", "", "速经典"},
['classic_l']= {"Classic(Lunatic);", "", "极速经典"},
['classic_u']= {"Classic(Ultimate);", "", "光速经典"},
['survivor_e']= {"Survivor(Easy);", "", "你能存活多久?"},
['survivor_n']= {"Survivor(Normal);", "", "你能存活多久?"},
['survivor_h']= {"Survivor(Hard);", "", "你能存活多久?"},
['survivor_l']= {"Survivor(Lunatic);", "", "你能存活多久?"},
['survivor_u']= {"Survivor(Ultimate);", "", "你能存活多久?"},
['attacker_h']= {"Attacker(Hard);", "", "进攻练习"},
['attacker_u']= {"Attacker(Ultimate);", "", "进攻练习"},
['defender_n']= {"Defender(Normal);", "", "防守练习"},
['defender_l']= {"Defender(Lunatic);", "", "防守练习"},
['dig_h']= {"Dig(Hard);", "", "挖掘练习"},
['dig_u']= {"Dig(Ultimate);", "", "挖掘练习"},
['c4wtrain_n']= {"C4WTrain(Normal);", "", "无 限 连 击"},
['c4wtrain_l']= {"C4WTrain(Lunatic);", "", "无 限 连 击"},
['pctrain_n']= {"PCTrain(Normal);", "", "简易PC题库熟悉全清定式的组合"},
['pctrain_l']= {"PCTrain(Lunatic);", "", "困难PC题库强算力者进"},
['pc_n']= {"PC(Normal);", "", "100行内刷PC"},
['pc_h']= {"PC(Hard);", "", "100行内刷PC"},
['pc_l']= {"PC(Lunatic);", "", "100行内刷PC"},
['pc_inf']= {"PC(Inf);", "", "你能连续做多少PC"},
['tech_n']= {"Tech(Normal);", "", "禁止断B2B"},
['tech_n_plus']= {"Tech(NormalP);", "", "仅允许spin与PC"},
['tech_h']= {"Tech(Hard);", "", "禁止断B2B"},
['tech_h_plus']= {"Tech(HardP);", "", "仅允许spin与PC"},
['tech_l']= {"Tech(Lunatic);", "", "禁止断B2B"},
['tech_l_plus']= {"Tech(LunaticP);", "", "仅允许spin与PC"},
['tech_finesse']= {"Tech(Finesse);", "", "强制最简操作"},
['tech_finesse_f']= {"Tech(FinesseF);", "", "禁止普通消除,强制最简操作"},
['tsd_e']= {"TSD(Easy);", "", "你能连续做几个TSD"},
['tsd_h']= {"TSD(Hard);", "", "你能连续做几个TSD"},
['tsd_u']= {"TSD(Ultimate);", "", "你能连续做几个TSD"},
['backfire_n']= {"Backfire(Normal);", "", "打出100攻击"},
['backfire_h']= {"Backfire(Hard);", "", "打出100攻击"},
['backfire_l']= {"Backfire(Lunatic);", "", "打出100攻击"},
['backfire_u']= {"Backfire(Ultimate);", "", "打出100攻击"},
['sprintAtk']= {"Sprint(100ATK);", "", "打出100攻击"},
['sprintEff']= {"Sprint(EFF);", "", "40行内打出更高的攻击"},
['zen']= {"Zen(200L);", "", "不限时200行"},
['ultra']= {"Ultra(EXTRA);", "", "在两分钟内尽可能拿到最多的分数"},
['infinite']= {"Infinite();", "", "沙盒"},
['infinite_dig']= {"InfDig();", "", "挖呀挖呀挖"},
['marathon_inf']= {"Marathon(Inf);", "", "无尽马拉松"},
['custom_clear']= {"Ctm(Clear);", ""},
['custom_puzzle']= {"Ctm(Puzzle);", ""},
['custom_clear']= {"Custom(Clear);", ""},
['custom_puzzle']= {"Custom(Puzzle);", ""},
},
}

View File

@@ -28,7 +28,7 @@ return {
clear={"Single","Double","Triple","Techrash","Pentacrash","Hexacrash","Heptacrash","Octacrash","Nonacrash","Decacrash","Undecacrash","Dodecacrash","Tridecacrash","Tetradecacrash","Pentadecacrash","Hexadecacrash","Heptadecacrash","Octadecacrash","Nonadecacrash","Ultracrash","Impossicrash"},
cleared="$1 lines",
mini="Mini",b2b="B2B ",b3b="B2B2B ",
PC="Perfect Clear",HPC="Half Clear",
PC="Perfect Clear",HPC="Half PC",
replaying="[重播]",
tasUsing="[TAS]",
@@ -43,6 +43,7 @@ return {
infHeightOn="無限高度 開",
infHeightOff="無限高度 關",
infHeightHint="用功能鍵1切換",
-- highestGrade="(highest: $1)",
speedLV="速度等級",
piece="塊數",line="行數",atk="攻擊",eff="效率",
@@ -54,7 +55,7 @@ return {
win="勝利",
lose="失敗",
torikan="未達標",
finish="完成",
gamewin="勝利",
gameover="遊戲結束",
@@ -66,10 +67,13 @@ return {
page="頁面:",
ai_puzzle="不能同時開啟AI和拼圖模式",
ai_mission="不能同時開啟AI和自定義任務",
ai_badPiece="不能同時開啟AI和含有非四連方塊的自定義序列",
cc_fixed="不能同時開啟CC和固定序列",
cc_swap="不能同時開啟CC和swap的暫存模式",
ai_prebag="不能同時開啟AI和含有非四連方塊的自定義序列",
ai_mission="不能同時開啟AI和自定義任務",
cc_solid="開啟CC時不能存在預先填滿的行",
cc_field_too_high="開啓CC時最高出塊高度不能超過40",
switchSpawnSFX="請開啟方塊生成音效",
needRestart="重新啟動以應用所有更改",
@@ -91,6 +95,8 @@ return {
dataCorrupted="數據損壞",
pasteWrongPlace="提醒:可能黏貼錯地方了",
noFile="文件未找到",
invalidSequence="無效序列模式",
tooHighField="超過126行的場地數據已被丟棄",
nowPlaying="正在播放:",
@@ -195,7 +201,7 @@ return {
keySettingInstruction="點擊來設鍵位\n按esc來取消選中\n按退格鍵來清除選中",
keySettingInstruction="點擊來設鍵位\n按esc來取消選中\n按退格鍵來清除選中",
customBGhelp="把圖片檔案拖到這個視窗裏使用自定義背景",
customBGloadFailed="自定義背景的圖片檔案格式不支持",
@@ -233,7 +239,7 @@ return {
"塊數:",
"行清除/挖掘:",
"攻擊/挖掘攻擊:",
"上漲/接收/抵消:",
"上漲(接收-抵消):",
"清除:",
"Spin:",
"B2B/B3B/PC/HPC:",
@@ -261,7 +267,7 @@ return {
"使用LÖVE引擎",
"錯誤或者建議請附帶截圖發送到內測群或者作者電郵~",
"僅透過內測QQ群/discord伺服器進行免費下載/更新",
"從其他渠道獲得遊戲皆有被修改/加入廣告/植入病毒的風險,程只申請了振動&網路權限!",
"從其他渠道獲得遊戲皆有被修改/加入廣告/植入病毒的風險,程只申請了振動&網路權限!",
"若由於被修改的本遊戲產生的各種損失作者概不負責(我怎麼負責啊跟我有什麼關係)",
FNNS and "/" or "請從正規途徑獲得最新版,遊戲現為免費,不過有打賞當然感謝啦~",
FNNS and "/" or "更多資訊見小z詞典"
@@ -270,7 +276,7 @@ return {
"原作者 MrZ",
"電郵: 1046101471@qq.com",
"",
", 開發和設計",
", 開發和設計",
"MrZ",
"",
"音樂製作使用",
@@ -281,7 +287,7 @@ return {
"",
"[POWERED BY LÖVE]",
"",
"",
"",
"MrZ",
"ParticleG",
"Gompyn",
@@ -327,6 +333,7 @@ return {
"Miya",
"Xiaoya",
"Mono",
"Flore",
"MrZ",
"Trebor",
"",
@@ -389,7 +396,7 @@ return {
qplay="快速開始: ",
online="網路遊戲",
custom="自定義",
setting="",
setting="",
stat="統計數據",
dict="小Z辭典",
replays="錄影回放",
@@ -408,7 +415,7 @@ return {
unranked="成績無效",
},
pause={
setting="(S)",
setting="(S)",
replay="回放(P)",
save="保存(O)",
resume="繼續(esc)",
@@ -460,7 +467,7 @@ return {
capacity="房間容量",
create="創建",
ospin="O-spin",
ospin="TRS O-spin",
fineKill="100% finesse",
b2bKill="強制B2B",
lockout="鎖定在外時失敗",
@@ -482,14 +489,14 @@ return {
cancel="取消準備",
},
setting_game={
title="遊戲設",
title="遊戲設",
graphic="←畫面",
sound="音頻→",
style="風格",
ctrl="控制設",
key="鍵位設",
touch="觸控設",
ctrl="控制設",
key="鍵位設",
touch="觸控設",
showVK="顯示虛擬擊鍵",
reTime="開局等待時間",
RS="旋轉系統",
@@ -500,7 +507,7 @@ return {
simpMode="簡潔模式",
},
setting_video={
title="畫面設",
title="畫面設",
sound="←音頻",
game="遊戲→",
@@ -543,12 +550,16 @@ return {
bg_on="普通背景",
bg_off="無背景",
bg_custom="應用自定義背景",
defaultBG="默認背景",
resetDbg="改回默認",
lockBG="鎖定背景",
noTheme="關閉節日主題",
blockSatur="方塊飽和",
fieldSatur="場地飽和",
},
setting_sound={
title="音頻設",
title="音頻設",
game="←遊戲",
graphic="畫面→",
@@ -568,7 +579,7 @@ return {
apply="應用",
},
setting_control={
title="控制設",
title="控制設",
preview="預覽",
das="DAS",arr="ARR",
@@ -605,7 +616,7 @@ return {
},
setting_skin={
skinSet="方塊皮膚",
title="外觀設",
title="外觀設",
skinR="重置顏色",
faceR="重置方向",
},
@@ -653,7 +664,7 @@ return {
play_puzzle="開始-拼圖",
reset="重設所有(del)",
advance="更多設(A)",
advance="更多設(A)",
mod="Mods (F1)",
field="場地編輯(F)",
sequence="序列編輯(S)",
@@ -677,7 +688,7 @@ return {
bufferLimit="緩衝上限",
heightLimit="高度上限",
ospin="O-spin",
ospin="TRS O-spin",
fineKill="強制finesse",
b2bKill="強制B2B",
lockout="鎖定在外時失敗",
@@ -690,7 +701,7 @@ return {
subTitle="場地",
any="不定",
smart="",
smart="",
push="增加一行(K)",
del="清除行(L)",
@@ -761,7 +772,7 @@ return {
code="驗證碼",
password="密碼",
password2="確認密碼",
setPW="密碼",
setPW="密碼",
},
account={
title="賬戶",
@@ -808,7 +819,7 @@ return {
import="從剪貼板導入",
unlock="地圖進度",
data="統計數據",
setting="",
setting="",
vk="虛擬按鍵佈局",
couldSave="雲儲存(測試功能,謹慎使用)",
@@ -824,7 +835,9 @@ return {
['sprint_100l']= {"競速", "100L", "清除100行"},
['sprint_400l']= {"競速", "400L", "清除400行"},
['sprint_1000l']= {"競速", "1000L", "清除1000行"},
['secret_grade']= {"秘密段位", "", "按照提示完成經典的“大於號”拼圖"},
['construct_sg']= {"建設", "秘密段位", "按照提示完成經典的“大於號”拼圖"},
-- ['construct_checker']= {"Construct", "CHECKERBOARD", "Build a checkerboard pattern!"},
-- ['construct_invsg']= {"Construct", "INV. SG", "Build an inverted zigzag pattern!"},
['sprintPenta']= {"競速", "五連塊", "傷腦筋十八塊"},
['sprintMPH']= {"競速", "MPH", "純隨機\n無Next\n無Hold"},
['sprint123']= {"競速", "M123", "清除40行但只有一至三連塊"},
@@ -857,8 +870,8 @@ return {
['round_h']= {"回合制", "困難", "下棋模式"},
['round_l']= {"回合制", "瘋狂", "下棋模式"},
['round_u']= {"回合制", "極限", "下棋模式"},
['big_n']= {"大方塊", "普通", "類比10*5場地的玩法(標準尺寸的一半)"},
['big_h']= {"大方塊", "困難", "類比10*5場地的玩法(標準尺寸的一半)"},
['big_n']= {"大方塊", "普通", "類比5x10場地(標準尺寸的一半)"},
['big_h']= {"大方塊", "困難", "類比5x10場地(標準尺寸的一半)"},
['master_n']= {"大師", "普通", "20G初心者練習"},
['master_h']= {"大師", "困難", "上級者20G挑戰"},
['master_m']= {"大師", "大師", "大師20G"},
@@ -911,8 +924,6 @@ return {
['tech_l_plus']= {"科研", "瘋狂+", "僅允許spin與PC"},
['tech_finesse']= {"科研", "finesse", "強制finesse"},
['tech_finesse_f']= {"科研", "finesse+", "禁止普通清除強制finesse"},
['tech_finesse_lock']= {"科研", "finesse限制", "限制操作次數"},
['tech_finesse_lock_f']={"科研", "finesse限制+","限制操作次數禁止斷B2B"},
['tsd_e']= {"TSD挑戰", "簡單", "你能連續做幾個TSD"},
['tsd_h']= {"TSD挑戰", "困難", "你能連續做幾個TSD"},
['tsd_u']= {"TSD挑戰", "極限", "你能連續做幾個TSD"},

View File

@@ -1,4 +1,4 @@
Lối chơi:
Cách chơi:
Hệ thống sẽ cấp cho người chơi 7 loại tetromino (gạch 4 ô) bao gồm: Z, S, J, L, I, O, T;
và người chơi cần điều khiển chúng (di chuyển sang trái và phải; xoay 90 / 180 / 270 độ).
Cứ mỗi hàng được lấp đầy trong bảng bởi các viên gạch, chúng sẽ bị xóa ra khỏi bảng.
@@ -16,7 +16,7 @@ Cách phát hiện spin: bằng cách sử dụng hệ thống điểm.
+2 điểm nếu thỏa mãn quy tắc "bất động"
+1 điểm nếu chưa kiểm tra vị trí kick thứ hai trong bảng kick
* Chỉ cần điều kiện 1 hay điều kiện 2 thỏa mãn thì cú xoay đó được tính là Spin *
* Chỉ cần thỏa mãn quy tắc "3 góc" hoặc quy tắc "bất động" thì cú xoay đó được tính luôn là Spin *
— Spin sẽ là Mini nếu xảy ra 1 trong 3 điều kiện sau:
Tổng điểm sau khi kiểm tra các quy tắc chỉ được 2 điểm,
@@ -25,23 +25,23 @@ Cách phát hiện spin: bằng cách sử dụng hệ thống điểm.
Hệ thống tấn công:
Kiểu xóa Đặc biệt (Special clear):
Kiểu xóa Đặc biệt chỉ trường hợp bạn thực hiện Spin, Techrash, Techrash+ — xóa 5 hàng hoặc hơn, PC, HPC.
Kiểu xóa Đặc biệt có thể sạc đầy thanh B2B.
Kiểu xóa nâng cao (Special clear):
Kiểu xóa nâng cao chỉ trường hợp bạn thực hiện Spin, Techrash, Techrash+ — xóa 5 hàng hoặc hơn, PC, HPC.
Kiểu xóa nâng cao có thể sạc đầy thanh B2B.
Kiểu xóa Siêu cấp (Super clear):
Là Kiểu xóa Đặc biệt nhưng được thực hiện với B2B hoặc B3B
Là Kiểu xóa nâng cao nhưng được thực hiện với B2B hoặc B3B
Kiểu xóa Đặc biệt (spin): Gửi gấp đôi số hàng vừa xóa.
Kiểu xóa nâng cao (spin): Gửi gấp đôi số hàng vừa xóa.
— B2B gửi thêm 1/1/2/4/8 cho Spin Đơn/Spin Đôi/Spin Tam/Techrash/Techrash+
— B2B2B gửi thêm (số hàng xóa × 0.5) trên B2B và +1 hàng để đánh chặn
— Mini sẽ bị cắt chỉ còn ¼ so với giá trị ban đầu
Kiểu xóa Đặc biệt (Techrash/Techrash+ nhưng không spin):
Kiểu xóa nâng cao (Techrash/Techrash+ nhưng không spin):
— B2B gửi thêm 1 hàng
— B3B boost 50% tấn công và +1 hàng để đánh chặn
Kiểu xóa không Đặc biệt:
Kiểu xóa thường:
Single/Double/Triple (Đơn/Đôi/Tam) gửi 0/1/2 hàng
Combo (REN):
@@ -49,7 +49,7 @@ Hệ thống tấn công:
Hệ số này có giá trị là 25% (hoặc 10% nếu bạn vừa xóa duy nhất 1 hàng) của tổng combo bạn có (tối đa là 12).
Lưu ý: Combo chỉ được bắt đầu tính từ lần xóa thứ 2 liên tiếp, không phải lần xóa đầu.
Hemi Perfect Clear (xem trong Zictionary)
Half Perfect Clear (xem trong Zictionary)
+4 hàng để tấn công, +2 hàng để đánh chặn
Perfect Clear (All Clear): Gửi 8 → 16 hàng rác
@@ -64,7 +64,7 @@ Thanh Back to Back (B2B):
Một người chơi đang ở trang thái B3B khi thanh có hơn 800 điểm.
Nếu có hàng được xóa:
Kiểu xóa đặc biệt:
Kiểu xóa nâng cao:
— Spin Đơn/Đôi/Tam/Techrash/Techrash+ lần lượt cộng thêm 50/100/180/800/1000 (×50% nếu là Mini)
— Techrash +150 điểm, nếu xóa hơn 4 hàng cùng lúc, mỗi hàng từ hàng thứ 5 trở đi cộng thêm 50 điểm.
— PC từ thứ 4 trở đi có giá trị 800 điểm
@@ -84,7 +84,7 @@ Hệ thống tính điểm:
Khoảng thời gian chờ trước khi tấn công:
Một đòn sát thương sẽ không có hiệu lực ngay lập tức để người chơi có chút thời gian phản ứng trước khi tràn vào bảng.
Thời gian chờ của các đòn tấn công như sau, sắp xếp từ nhanh nhất tới lâu nhất:
Thời gian chờ của các đòn tấn công như sau, sắp xếp từ nhanh nhất tới lâu nhất:
— Double và Triple (Đôi và Tam) là nhanh nhất
— Theo sau là Techrash, Techrash+, spin; Mini, B2B và B3B có thêm thêm chút thời gian chờ nữa.
— Sát thương từ combo có thời gian chờ lâu nhất..
@@ -107,16 +107,24 @@ Chế độ Battle Royale:
3. K.O.s: Nhắm vào bot “đang thở máy, đang hấp hối”. Tự động nhắm lại sau mỗi giây.
4. Phản công: tấn công tất cả bot đang nhắm vào bạn. Bạn sẽ gửi tấn công đến tất cả bọn chúng.
Nếu bạn không nhắm vào mục tiêu nào, thì bạn sẽ tấn công một bot ngẫu nhiên (không nhắm)
Người cuối cùng trụ lại thành công sẽ là người chiến thắng sau khi đã loại được toàn bộ đối thủ.
Chế độ Tự do:
Bạn có thể tự do tùy chỉnh nhiều thông số (nhưng không bao gồm các hiệu ứng đặc biệt từ các chế độ khác).
Bạn có thể vẽ một cái bảng để tập xóa (Clear mode) hoặc tập xây theo mẫu (Puzzle mode)
Ở chế độ Puzzle, bạn có thể nhấn F1 để chọn có hiện mẫu hay không. Để thắng được thì toàn bộ…
Ở chế độ Puzzle, bạn có thể nhấn F1 để chọn có hiện bảng mẫu hay không.
Để thắng được game ở chế độ Puzzle thì toàn bộ…
ô có X thì không được có gạch;
ô trống thì có thể ở bất kỳ tình trạng nào;
ô có màu thì gạch đặt vào ô phải khớp màu;
ô có màu của hàng rác thì có thể là bất kỳ gạch nào (không phân biệt kiểu gạch) nhưng không thể là không khí.
Ở chế độ Clear: bạn chỉ cần xóa sạch toàn bộ gạch của bảng mẫu là được.
Chế độ Construct:
Ở chế độ này, bạn sẽ phải xây theo đúng mẫu: Secret Grade, họa tiết xen kẽ, hoặc Secret Grade đảo ngược
Điều kiện để thắng game ở chế độ Construct cũng tương tự như chế độ Puzzle trong chế độ Tự do.
Nhấn F1 để tắt/bật mẫu, F2 để lật dọc mẫu.

View File

@@ -1,137 +1,138 @@
return {
{name='sprint_10l', x=0, y=0, size=40,shape=1,icon="sprint1", unlock={'sprint_20l','sprint_40l'}},
{name='sprint_20l', x=-200, y=200, size=50,shape=1,icon="sprint1"},
{name='sprint_40l', x=0, y=-300, size=40,shape=1,icon="sprint2", unlock={'dig_10l','sprint_100l','marathon_n','sprintPenta','sprintMPH','sprint123','secret_grade'}},
{name='sprint_100l', x=-400, y=200, size=50,shape=1,icon="sprint2", unlock={'sprint_400l','drought_n'}},
{name='sprint_400l', x=-600, y=200, size=40,shape=1,icon="sprint3", unlock={'sprint_1000l'}},
{name='sprint_1000l', x=-800, y=200, size=40,shape=1,icon="sprint3"},
{name='sprint_10l', x=0, y=0, size=40,shape=1,icon="sprint1", unlock={'sprint_20l','sprint_40l'}},
{name='sprint_20l', x=-200, y=200, size=50,shape=1,icon="sprint1"},
{name='sprint_40l', x=0, y=-300, size=40,shape=1,icon="sprint2", unlock={'dig_10l','sprint_100l','marathon_n','sprintPenta','sprintMPH','sprint123'}},
{name='sprint_100l', x=-400, y=200, size=50,shape=1,icon="sprint2", unlock={'sprint_400l','drought_n'}},
{name='sprint_400l', x=-600, y=200, size=40,shape=1,icon="sprint3", unlock={'sprint_1000l'}},
{name='sprint_1000l', x=-800, y=200, size=40,shape=1,icon="sprint3"},
{name='sprint123', x=160, y=-400, size=40,shape=1,icon="sprint_tri"},
{name='sprintMPH', x=200, y=-260, size=40,shape=3,icon="sprint2"},
{name='sprintPenta', x=130, y=-140, size=40,shape=3,icon="sprint_pento"},
{name='sprint123', x=160, y=-400, size=40,shape=1,icon="sprint_tri"},
{name='sprintMPH', x=200, y=-260, size=40,shape=3,icon="sprint2"},
{name='sprintPenta', x=130, y=-140, size=40,shape=3,icon="sprint_pento"},
{name='secret_grade', x=-200, y=-400, size=40,shape=1,icon="secret_grade"},
{name='construct_sg', x=-500, y=-520, size=40,shape=1,icon="secret_grade",unlock={'construct_checker'}},
{name='construct_checker', x=-700, y=-520, size=40,shape=3,icon="secret_grade",unlock={'construct_invsg'}},
{name='construct_invsg', x=-900, y=-520, size=40,shape=2,icon="secret_grade"},
{name='drought_n', x=-600, y=400, size=40,shape=1,icon="drought", unlock={'drought_l'}},
{name='drought_l', x=-800, y=400, size=40,shape=1,icon="drought"},
{name='drought_n', x=-600, y=400, size=40,shape=1,icon="drought", unlock={'drought_l'}},
{name='drought_l', x=-800, y=400, size=40,shape=1,icon="drought"},
{name='dig_10l', x=-200, y=-200, size=40,shape=1,icon="dig_sprint", unlock={'dig_40l','dig_eff_10l'}},
{name='dig_40l', x=-400, y=-200, size=40,shape=1,icon="dig_sprint", unlock={'dig_100l'}},
{name='dig_100l', x=-600, y=-200, size=40,shape=1,icon="dig_sprint", unlock={'dig_400l'}},
{name='dig_400l', x=-800, y=-200, size=40,shape=1,icon="dig_sprint"},
{name='dig_10l', x=-400, y=-200, size=40,shape=1,icon="dig_sprint", unlock={'dig_40l','dig_eff_10l'}},
{name='dig_40l', x=-600, y=-200, size=40,shape=1,icon="dig_sprint", unlock={'dig_100l','dig_quad_10l'}},
{name='dig_100l', x=-800, y=-200, size=40,shape=1,icon="dig_sprint", unlock={'dig_400l'}},
{name='dig_400l', x=-1000,y=-200, size=40,shape=1,icon="dig_sprint"},
{name='dig_eff_10l', x=-400, y=0, size=40,shape=1,icon="dig_sprint", unlock={'dig_eff_40l'}},
{name='dig_eff_40l', x=-600, y=0, size=40,shape=1,icon="dig_sprint", unlock={'dig_eff_100l'}},
{name='dig_eff_100l', x=-800, y=0, size=40,shape=1,icon="dig_sprint", unlock={'dig_eff_400l'}},
{name='dig_eff_400l', x=-1000, y=0, size=40,shape=1,icon="dig_sprint"},
{name='dig_quad_10l', x=-800, y=-400, size=40,shape=1,icon="drought"},
{name='marathon_n', x=0, y=-600, size=60,shape=1,icon="marathon", unlock={'marathon_h','solo_e','round_e','big_n','blind_e','classic_e','survivor_e','c4wtrain_n','pctrain_n','sprintAtk','zen'}},
{name='marathon_h', x=0, y=-800, size=50,shape=1,icon="marathon", unlock={'master_n','strategy_e'}},
{name='dig_eff_10l', x=-600, y=0, size=40,shape=1,icon="dig_eff", unlock={'dig_eff_40l'}},
{name='dig_eff_40l', x=-800, y=0, size=40,shape=1,icon="dig_eff", unlock={'dig_eff_100l'}},
{name='dig_eff_100l', x=-1000,y=0, size=40,shape=1,icon="dig_eff", unlock={'dig_eff_400l'}},
{name='dig_eff_400l', x=-1200,y=0, size=40,shape=1,icon="dig_eff"},
{name='solo_e', x=-600, y=-1000, size=40,shape=1,icon="solo", unlock={'solo_n'}},
{name='solo_n', x=-800, y=-1000, size=40,shape=1,icon="solo", unlock={'solo_h'}},
{name='solo_h', x=-1000, y=-1000, size=40,shape=1,icon="solo", unlock={'solo_l','techmino49_e'}},
{name='solo_l', x=-1200, y=-1000, size=40,shape=1,icon="solo", unlock={'solo_u'}},
{name='solo_u', x=-1400, y=-1000, size=40,shape=1,icon="solo"},
{name='marathon_n', x=0, y=-600, size=60,shape=1,icon="marathon", unlock={'marathon_h','solo_e','round_e','big_n','blind_e','classic_e','survivor_e','c4wtrain_n','pctrain_n','sprintAtk','zen','construct_sg'}},
{name='marathon_h', x=0, y=-800, size=50,shape=1,icon="marathon", unlock={'master_n','strategy_e'}},
{name='techmino49_e', x=-1100, y=-1200, size=40,shape=1,icon="t49", unlock={'techmino49_h','techmino99_e'}},
{name='techmino49_h', x=-1100, y=-1400, size=40,shape=1,icon="t49", unlock={'techmino49_u'}},
{name='techmino49_u', x=-1100, y=-1600, size=40,shape=1,icon="t49"},
{name='techmino99_e', x=-1300, y=-1400, size=40,shape=1,icon="t99", unlock={'techmino99_h'}},
{name='techmino99_h', x=-1300, y=-1600, size=40,shape=1,icon="t99", unlock={'techmino99_u'}},
{name='techmino99_u', x=-1300, y=-1800, size=40,shape=1,icon="t99"},
{name='solo_e', x=-600, y=-1000,size=40,shape=1,icon="solo", unlock={'solo_n'}},
{name='solo_n', x=-800, y=-1000,size=40,shape=1,icon="solo", unlock={'solo_h'}},
{name='solo_h', x=-1000,y=-1000,size=40,shape=1,icon="solo", unlock={'solo_l','techmino49_e'}},
{name='solo_l', x=-1200,y=-1000,size=40,shape=1,icon="solo", unlock={'solo_u'}},
{name='solo_u', x=-1400,y=-1000,size=40,shape=1,icon="solo"},
{name='round_e', x=-600, y=-800, size=40,shape=1,icon="round", unlock={'round_n'}},
{name='round_n', x=-800, y=-800, size=40,shape=1,icon="round", unlock={'round_h'}},
{name='round_h', x=-1000, y=-800, size=40,shape=1,icon="round", unlock={'round_l'}},
{name='round_l', x=-1200, y=-800, size=40,shape=1,icon="round", unlock={'round_u'}},
{name='round_u', x=-1400, y=-800, size=40,shape=1,icon="round"},
{name='techmino49_e', x=-1100,y=-1200,size=40,shape=1,icon="t49", unlock={'techmino49_h','techmino99_e'}},
{name='techmino49_h', x=-1100,y=-1400,size=40,shape=1,icon="t49", unlock={'techmino49_u'}},
{name='techmino49_u', x=-1100,y=-1600,size=40,shape=1,icon="t49"},
{name='techmino99_e', x=-1300,y=-1400,size=40,shape=1,icon="t99", unlock={'techmino99_h'}},
{name='techmino99_h', x=-1300,y=-1600,size=40,shape=1,icon="t99", unlock={'techmino99_u'}},
{name='techmino99_u', x=-1300,y=-1800,size=40,shape=1,icon="t99"},
{name='big_n', x=-400, y=-400, size=40,shape=1,icon="big", unlock={'big_h'}},
{name='big_h', x=-600, y=-400, size=40,shape=1,icon="big",},
{name='round_e', x=-600, y=-800, size=40,shape=1,icon="round", unlock={'round_n'}},
{name='round_n', x=-800, y=-800, size=40,shape=1,icon="round", unlock={'round_h'}},
{name='round_h', x=-1000,y=-800, size=40,shape=1,icon="round", unlock={'round_l'}},
{name='round_l', x=-1200,y=-800, size=40,shape=1,icon="round", unlock={'round_u'}},
{name='round_u', x=-1400,y=-800, size=40,shape=1,icon="round"},
{name='master_n', x=0, y=-1000, size=40,shape=1,icon="master", unlock={'master_h','strategy_h'}},
{name='master_h', x=0, y=-1200, size=40,shape=3,icon="master", unlock={'master_final','master_ex','master_ph','master_m','master_g','strategy_u'}},
{name='master_m', x=100, y=-1550, size=40,shape=2,icon="master"},
{name='master_final', x=-100, y=-1550, size=40,shape=2,icon="master"},
{name='master_ph', x=-170, y=-1450, size=40,shape=2,icon="master"},
{name='master_g', x=0, y=-1600, size=40,shape=3,icon="master"},
{name='master_ex', x=170, y=-1450, size=40,shape=2,icon="master_ex"},
{name='big_n', x=-400, y=-400, size=40,shape=1,icon="big", unlock={'big_h'}},
{name='big_h', x=-600, y=-400, size=40,shape=1,icon="big"},
{name='strategy_e', x=-150, y=-1020, size=40,shape=3,icon="master", unlock={'strategy_e_plus'}},
{name='strategy_h', x=-150, y=-1150, size=35,shape=3,icon="master", unlock={'strategy_h_plus'}},
{name='strategy_u', x=-150, y=-1280, size=30,shape=2,icon="master", unlock={'strategy_u_plus'}},
{name='strategy_e_plus', x=-300, y=-1120, size=40,shape=3,icon="master"},
{name='strategy_h_plus', x=-300, y=-1250, size=35,shape=3,icon="master"},
{name='strategy_u_plus', x=-300, y=-1380, size=30,shape=2,icon="master"},
{name='master_n', x=0, y=-1000,size=40,shape=1,icon="master", unlock={'master_h','strategy_h'}},
{name='master_h', x=0, y=-1200,size=40,shape=3,icon="master", unlock={'master_final','master_ex','master_ph','master_m','master_g','strategy_u'}},
{name='master_m', x=100, y=-1550,size=40,shape=2,icon="master"},
{name='master_final', x=-100, y=-1550,size=40,shape=2,icon="master"},
{name='master_ph', x=-170, y=-1450,size=40,shape=2,icon="master"},
{name='master_g', x=0, y=-1600,size=40,shape=3,icon="master"},
{name='master_ex', x=170, y=-1450,size=40,shape=2,icon="master_ex"},
{name='blind_e', x=150, y=-700, size=40,shape=1,icon="hidden", unlock={'blind_n','master_instinct'}},
{name='blind_n', x=150, y=-800, size=40,shape=1,icon="hidden2", unlock={'blind_h'}},
{name='blind_h', x=150, y=-900, size=35,shape=1,icon="hidden3", unlock={'blind_l'}},
{name='blind_l', x=150, y=-1000, size=35,shape=3,icon="hidden4", unlock={'blind_u'}},
{name='blind_u', x=150, y=-1100, size=30,shape=3,icon="hidden4", unlock={'blind_wtf'}},
{name='blind_wtf', x=150, y=-1200, size=25,shape=2,icon="hidden5"},
{name='master_instinct', x=285, y=-835, size=40,shape=3,icon="hidden2"},
{name='strategy_e', x=-150, y=-1020,size=40,shape=3,icon="master", unlock={'strategy_e_plus'}},
{name='strategy_h', x=-150, y=-1150,size=35,shape=3,icon="master", unlock={'strategy_h_plus'}},
{name='strategy_u', x=-150, y=-1280,size=30,shape=2,icon="master", unlock={'strategy_u_plus'}},
{name='strategy_e_plus', x=-300, y=-1120,size=40,shape=3,icon="master"},
{name='strategy_h_plus', x=-300, y=-1250,size=35,shape=3,icon="master"},
{name='strategy_u_plus', x=-300, y=-1380,size=30,shape=2,icon="master"},
{name='classic_e', x=-200, y=-850, size=40,shape=1,icon="classic", unlock={'classic_h'}},
{name='classic_h', x=-300, y=-950, size=40,shape=3,icon="classic", unlock={'classic_l'}},
{name='classic_l', x=-400, y=-1050, size=35,shape=3,icon="classic", unlock={'classic_u'}},
{name='classic_u', x=-500, y=-1150, size=30,shape=2,icon="classic"},
{name='blind_e', x=150, y=-700, size=40,shape=1,icon="hidden", unlock={'blind_n','master_instinct'}},
{name='blind_n', x=150, y=-800, size=40,shape=1,icon="hidden2", unlock={'blind_h'}},
{name='blind_h', x=150, y=-900, size=35,shape=1,icon="hidden3", unlock={'blind_l'}},
{name='blind_l', x=150, y=-1000,size=35,shape=3,icon="hidden4", unlock={'blind_u'}},
{name='blind_u', x=150, y=-1100,size=30,shape=3,icon="hidden4", unlock={'blind_wtf'}},
{name='blind_wtf', x=150, y=-1200,size=25,shape=2,icon="hidden5"},
{name='master_instinct', x=285, y=-835, size=40,shape=3,icon="hidden2"},
{name='survivor_e', x=450, y=-600, size=40,shape=1,icon="survivor", unlock={'survivor_n'}},
{name='survivor_n', x=650, y=-600, size=40,shape=1,icon="survivor", unlock={'survivor_h','attacker_h','defender_n','dig_h'}},
{name='survivor_h', x=850, y=-600, size=40,shape=1,icon="survivor", unlock={'survivor_l'}},
{name='survivor_l', x=1050, y=-600, size=40,shape=3,icon="survivor", unlock={'survivor_u'}},
{name='survivor_u', x=1250, y=-600, size=40,shape=2,icon="survivor"},
{name='classic_e', x=-200, y=-850, size=40,shape=1,icon="classic", unlock={'classic_h'}},
{name='classic_h', x=-300, y=-950, size=40,shape=3,icon="classic", unlock={'classic_l'}},
{name='classic_l', x=-400, y=-1050,size=35,shape=3,icon="classic", unlock={'classic_u'}},
{name='classic_u', x=-500, y=-1150,size=30,shape=2,icon="classic"},
{name='attacker_h', x=450, y=-800, size=40,shape=1,icon="attack", unlock={'attacker_u'}},
{name='attacker_u', x=450, y=-1000, size=40,shape=1,icon="attack"},
{name='survivor_e', x=450, y=-600, size=40,shape=1,icon="survivor", unlock={'survivor_n'}},
{name='survivor_n', x=650, y=-600, size=40,shape=1,icon="survivor", unlock={'survivor_h','attacker_h','defender_n','dig_h'}},
{name='survivor_h', x=850, y=-600, size=40,shape=1,icon="survivor", unlock={'survivor_l'}},
{name='survivor_l', x=1050, y=-600, size=40,shape=3,icon="survivor", unlock={'survivor_u'}},
{name='survivor_u', x=1250, y=-600, size=40,shape=2,icon="survivor"},
{name='defender_n', x=650, y=-800, size=40,shape=1,icon="defend", unlock={'defender_l'}},
{name='defender_l', x=650, y=-1000, size=40,shape=1,icon="defend"},
{name='attacker_h', x=450, y=-800, size=40,shape=1,icon="attack", unlock={'attacker_u'}},
{name='attacker_u', x=450, y=-1000,size=40,shape=1,icon="attack"},
{name='dig_h', x=850, y=-800, size=40,shape=1,icon="dig", unlock={'dig_u'}},
{name='dig_u', x=850, y=-1000, size=40,shape=1,icon="dig"},
{name='defender_n', x=650, y=-800, size=40,shape=1,icon="defend", unlock={'defender_l'}},
{name='defender_l', x=650, y=-1000,size=40,shape=1,icon="defend"},
{name='c4wtrain_n', x=700, y=-450, size=40,shape=1,icon="pc", unlock={'c4wtrain_l'}},
{name='c4wtrain_l', x=900, y=-450, size=40,shape=1,icon="pc"},
{name='dig_h', x=850, y=-800, size=40,shape=1,icon="dig", unlock={'dig_u'}},
{name='dig_u', x=850, y=-1000,size=40,shape=1,icon="dig"},
{name='pctrain_n', x=700, y=-300, size=40,shape=1,icon="pc", unlock={'pctrain_l','pc_n'}},
{name='pctrain_l', x=900, y=-300, size=40,shape=1,icon="pc"},
{name='c4wtrain_n', x=700, y=-450, size=40,shape=1,icon="pc", unlock={'c4wtrain_l'}},
{name='c4wtrain_l', x=900, y=-450, size=40,shape=1,icon="pc"},
{name='pc_n', x=800, y=-140, size=40,shape=1,icon="pc", unlock={'pc_h'}},
{name='pc_h', x=950, y=-140, size=40,shape=3,icon="pc", unlock={'pc_l','pc_inf'}},
{name='pc_l', x=1100, y=-140, size=40,shape=3,icon="pc"},
{name='pc_inf', x=1100, y=-280, size=40,shape=2,icon="pc"},
{name='pctrain_n', x=700, y=-300, size=40,shape=1,icon="pc", unlock={'pctrain_l','pc_n'}},
{name='pctrain_l', x=900, y=-300, size=40,shape=1,icon="pc"},
{name='sprintAtk', x=500, y=-280, size=40,shape=1,icon="sprint2", unlock={'sprintEff','tech_n','tech_finesse','tech_finesse_lock','tsd_e','backfire_n'}},
{name='sprintEff', x=360, y=-150, size=40,shape=1,icon="sprint2"},
{name='pc_n', x=800, y=-140, size=40,shape=1,icon="pc", unlock={'pc_h'}},
{name='pc_h', x=950, y=-140, size=40,shape=3,icon="pc", unlock={'pc_l','pc_inf'}},
{name='pc_l', x=1100, y=-140, size=40,shape=3,icon="pc"},
{name='pc_inf', x=1100, y=-280, size=40,shape=2,icon="pc"},
{name='tech_n', x=400, y=20, size=40,shape=1,icon="tech", unlock={'tech_n_plus','tech_h'}},
{name='tech_n_plus', x=200, y=-10, size=40,shape=3,icon="tech"},
{name='tech_h', x=400, y=170, size=40,shape=1,icon="tech", unlock={'tech_h_plus','tech_l'}},
{name='tech_h_plus', x=200, y=140, size=35,shape=3,icon="tech"},
{name='tech_l', x=400, y=320, size=40,shape=1,icon="tech", unlock={'tech_l_plus'}},
{name='tech_l_plus', x=200, y=290, size=35,shape=3,icon="tech"},
{name='sprintAtk', x=500, y=-280, size=40,shape=1,icon="sprint2", unlock={'sprintEff','tech_n','tech_finesse','tsd_e','backfire_n'}},
{name='sprintEff', x=360, y=-150, size=40,shape=1,icon="sprint2"},
{name='tech_finesse', x=850, y=20, size=40,shape=1,icon="tech", unlock={'tech_finesse_f'}},
{name='tech_finesse_f', x=1050, y=20, size=40,shape=1,icon="tech"},
{name='tech_n', x=400, y=20, size=40,shape=1,icon="tech", unlock={'tech_n_plus','tech_h'}},
{name='tech_n_plus', x=200, y=-10, size=40,shape=3,icon="tech_plus"},
{name='tech_h', x=400, y=170, size=40,shape=1,icon="tech", unlock={'tech_h_plus','tech_l'}},
{name='tech_h_plus', x=200, y=140, size=35,shape=3,icon="tech_plus"},
{name='tech_l', x=400, y=320, size=40,shape=1,icon="tech", unlock={'tech_l_plus'}},
{name='tech_l_plus', x=200, y=290, size=35,shape=3,icon="tech_plus"},
{name='tech_finesse_lock', x=850, y=160, size=40,shape=1,icon="tech", unlock={'tech_finesse_lock_f'}},
{name='tech_finesse_lock_f', x=1050, y=170, size=40,shape=1,icon="tech"},
{name='tech_finesse', x=850, y=20, size=40,shape=1,icon="tech", unlock={'tech_finesse_f'}},
{name='tech_finesse_f', x=1050, y=20, size=40,shape=1,icon="tech_plus"},
{name='tsd_e', x=700, y=250, size=40,shape=1,icon="tsd", unlock={'tsd_h'}},
{name='tsd_h', x=860, y=310, size=40,shape=1,icon="tsd", unlock={'tsd_u'}},
{name='tsd_u', x=1050, y=320, size=40,shape=1,icon="tsd"},
{name='tsd_e', x=700, y=100, size=40,shape=1,icon="tsd", unlock={'tsd_h'}},
{name='tsd_h', x=860, y=160, size=40,shape=1,icon="tsd", unlock={'tsd_u'}},
{name='tsd_u', x=1050, y=170, size=40,shape=1,icon="tsd"},
{name='backfire_n', x=640, y=420, size=40,shape=1,icon="backfire", unlock={'backfire_h'}},
{name='backfire_h', x=790, y=450, size=40,shape=1,icon="backfire", unlock={'backfire_l'}},
{name='backfire_l', x=930, y=450, size=40,shape=3,icon="backfire", unlock={'backfire_u'}},
{name='backfire_u', x=1070, y=450, size=35,shape=2,icon="backfire"},
{name='backfire_n', x=640, y=270, size=40,shape=1,icon="backfire", unlock={'backfire_h'}},
{name='backfire_h', x=790, y=300, size=40,shape=1,icon="backfire", unlock={'backfire_l'}},
{name='backfire_l', x=930, y=300, size=40,shape=3,icon="backfire", unlock={'backfire_u'}},
{name='backfire_u', x=1070, y=300, size=35,shape=2,icon="backfire"},
{name='zen', x=-1000, y=-600, size=40,shape=1,icon="zen", unlock={'ultra','infinite','infinite_dig','marathon_inf'}},
{name='ultra', x=-1200, y=-600, size=40,shape=1,icon="ultra"},
{name='infinite', x=-1200, y=-400, size=40,shape=1,icon='infinite'},
{name='infinite_dig', x=-1000, y=-400, size=40,shape=1,icon="dig"},
{name='marathon_inf', x=-800, y=-400, size=40,shape=1,icon="marathon"}
{name='zen', x=-1200,y=-600, size=40,shape=1,icon="zen", unlock={'ultra','infinite','infinite_dig','marathon_inf'}},
{name='ultra', x=-1400,y=-600, size=40,shape=1,icon="ultra"},
{name='infinite', x=-1400,y=-400, size=40,shape=1,icon='infinite'},
{name='infinite_dig', x=-1200,y=-400, size=40,shape=1,icon="dig"},
{name='marathon_inf', x=-1000,y=-400, size=40,shape=1,icon="marathon"},
}

View File

@@ -13,11 +13,11 @@ return {
getRank=function(P)
local W=P.modeData.wave
return
W>=50 and 5 or
W>=40 and 4 or
W>=30 and 3 or
W>=20 and 2 or
W>=10 and 1 or
W>=5 and 0
W>=50 and 5 or
W>=40 and 4 or
W>=30 and 3 or
W>=20 and 2 or
W>=10 and 1 or
W>=5 and 0
end,
}

View File

@@ -12,11 +12,11 @@ return {
getRank=function(P)
local W=P.modeData.wave
return
W>=50 and 5 or
W>=40 and 4 or
W>=30 and 3 or
W>=20 and 2 or
W>=10 and 1 or
W>=5 and 0
W>=50 and 5 or
W>=40 and 4 or
W>=30 and 3 or
W>=20 and 2 or
W>=10 and 1 or
W>=5 and 0
end,
}

View File

@@ -21,14 +21,14 @@ return {
if L>=100 then
local T=P.stat.time
return
T<50 and 5 or
T<65 and 4 or
T<100 and 3 or
T<130 and 2 or
1
T<50 and 5 or
T<65 and 4 or
T<100 and 3 or
T<130 and 2 or
1
else
return
L>=50 and 0
L>=50 and 0
end
end,
}

View File

@@ -21,14 +21,14 @@ return {
if L>=100 then
local T=P.stat.time
return
T<55 and 5 or
T<70 and 4 or
T<110 and 3 or
T<150 and 2 or
1
T<55 and 5 or
T<70 and 4 or
T<110 and 3 or
T<150 and 2 or
1
else
return
L>=50 and 0
L>=50 and 0
end
end,
}

View File

@@ -20,14 +20,14 @@ return {
if L>=100 then
local T=P.stat.time
return
T<45 and 5 or
T<60 and 4 or
T<90 and 3 or
T<120 and 2 or
1
T<45 and 5 or
T<60 and 4 or
T<90 and 3 or
T<120 and 2 or
1
else
return
L>=50 and 0
L>=50 and 0
end
end,
}

View File

@@ -21,14 +21,14 @@ return {
if L>=100 then
local T=P.stat.time
return
T<60 and 5 or
T<80 and 4 or
T<120 and 3 or
T<180 and 2 or
1
T<60 and 5 or
T<80 and 4 or
T<120 and 3 or
T<180 and 2 or
1
else
return
L>=50 and 0
L>=50 and 0
end
end,
}

View File

@@ -15,14 +15,14 @@ return {
if L>=200 then
local T=P.stat.time
return
T<=120 and 5 or
T<=180 and 4 or
3
T<=120 and 5 or
T<=180 and 4 or
3
else
return
L>=100 and 2 or
L>=50 and 1 or
L>=10 and 0
L>=100 and 2 or
L>=50 and 1 or
L>=10 and 0
end
end,
}

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