Compare commits

...

241 Commits

Author SHA1 Message Date
MrZ626
707bcca368 Merge commit 'f8f115de10b4ef7818cf58bc03c9d75700e425b0' into test-new-mode-system 2021-12-27 14:26:32 +08:00
MrZ626
f8f115de10 更新字体 2021-12-27 14:19:39 +08:00
MrZ626
b07c4dc53a 优化滑条控件和列表框控件 2021-12-26 02:57:57 +08:00
NOT_A_ROBOT
6eeddba773 Remove dictionary and legal page conflict (#563)
Dictionary: "A 🤔 game developed using LÖVE."
Legal page: "TECHMINO is not a fan game of Tetris."

This commit edits the dictionary to follow the legal page into:
"A block stacker game developed using LÖVE."
2021-12-25 19:24:51 +08:00
MrZ626
0cfe4df468 新BGM:lounge(暂未使用, by Hailey (cudsys) & MrZ) 2021-12-24 18:50:30 +08:00
MrZ626
eb5c3c3be5 版本推进 2021-12-24 00:08:46 +08:00
MrZ626
a5b9206694 修正联网对战结算的l'pm公式算的其实是lpm 2021-12-24 00:06:43 +08:00
MrZ626
375e67bdc4 微调框架坐标系相关细节 2021-12-24 00:06:40 +08:00
MrZ626
724a576aa3 跟进框架更新 2021-12-23 21:03:11 +08:00
MrZ626
ed47dcb90c 框架新增onResize 2021-12-23 21:03:05 +08:00
MrZ626
64b08a5a4d 修复录像界面导入导出按钮隐藏状态错误 2021-12-23 14:00:30 +08:00
MrZ626
baed0153a2 两个节日主题颜色浅一些 2021-12-23 13:48:39 +08:00
MrZ626
46d95b33e4 播放立体声音效限制输入值范围 2021-12-23 13:19:01 +08:00
MrZ626
200d270fee 框架会给场景提供触摸id 2021-12-22 21:22:19 +08:00
MrZ626
a8628275a0 大量指数接近动画改为基于时间而不是帧 2021-12-22 02:19:42 +08:00
MrZ626
20a1d2bcc1 修正框架主循环刷新率控制 2021-12-21 23:35:32 +08:00
MrZ626
b887a1f096 版本推进 2021-12-21 23:24:01 +08:00
MrZ626
9bf0e9f28d 调整一行hpc判定为“消除后最高行是垃圾行”,避免自定义场地的空气行争议 2021-12-21 23:18:49 +08:00
MrZ626
dfc724767b 调整帧率控制算法 2021-12-21 10:23:16 +08:00
MrZ626
f0e66e9dc5 框架添加设置最大帧率的入口 2021-12-21 00:16:29 +08:00
MrZ626
0932335f0b 微调游戏设置菜单 2021-12-20 23:21:02 +08:00
MrZ626
a9b39e396a BGM.play新增预加载参数 2021-12-20 16:01:19 +08:00
MrZ626
2e0ceaae72 math扩展库新增interval方法 2021-12-20 16:01:19 +08:00
MrZ626
04f38d2eb6 微调新模式pr的小问题 2021-12-20 14:55:42 +08:00
MrZ626
fc1ed4dff6 修正英文词典小问题 2021-12-20 14:52:40 +08:00
NOT_A_ROBOT
f8935d3dd7 Add Master Instinct mode (#545)
* Add Inverse Invisible mode

A mode where the locked pieces doesn't become invisible, but your active piece does.

[NOTE: I haven't added the mode to the map yet because the mode selection screen is changing]

* Renamed to Master Instinct
2021-12-20 14:51:30 +08:00
user670
a86228677f Update dict_en.lua (#556)
- Un-confused the support entries. Patreon processing fee assumes your account uses the 5% tier.
- TGM games are arcade games, NOT Windows games, despite their now most common versions found in China are Windows ports.
2021-12-20 14:47:45 +08:00
MrZ626
79df9f7876 前两个tsd模式有很小的重力 2021-12-19 20:22:02 +08:00
MrZ626
12ea2d76be 修正profile模块小问题 2021-12-19 17:06:44 +08:00
MrZ626
485bd72241 重构bgm模块 2021-12-19 16:29:50 +08:00
MrZ626
7240275075 修复一个bgm模块小问题 2021-12-19 15:26:16 +08:00
MrZ626
29ef9b8d15 ai种子会根据id变化 2021-12-19 00:59:01 +08:00
MrZ626
97f4795d4e 修正一处框架修改没改完导致地图报错 2021-12-19 00:50:42 +08:00
MrZ626
226e45b24d 整理代码 2021-12-18 16:08:45 +08:00
MrZ626
d6ab7e72b2 调整某个无关紧要的小东西 2021-12-18 01:15:04 +08:00
MrZ626
168f44b8b3 修正一处框架方法名修改没改完 2021-12-18 00:37:32 +08:00
MrZ626
b73f646a4c 作者要求mono语音包暂时消失一段时间 2021-12-18 00:25:05 +08:00
MrZ626
36cefcc000 太空背景支持任意帧率 2021-12-17 21:57:33 +08:00
MrZ626
f901c25c87 修正一些地方move音效没改touch 2021-12-17 21:12:50 +08:00
MrZ626
6d8478b029 string扩展模块新增一个按字符数读+截取的方法 2021-12-17 13:54:27 +08:00
MrZ626
9bcb040019 bgm模块新增isPlaying方法 2021-12-16 17:47:57 +08:00
MrZ626
d977087fc0 调整上一个pr的小问题 2021-12-16 14:35:57 +08:00
C₂₉H₂₅N₃O₅
1a330771d7 大改词典 (#553)
* 大修中文词典

`dict_zh`:
* 术语和专有名词大小写修正
* 日期格式标准化
* 增加评论注释
* 增加更多关键词索引
* 删除重复内容
* 英文使用半角标点
* 修改一些写错的名词
* 修改一个链接

* Updated `dict_en`

`dict_en`
* Added more search indexes
* Added many contents from `dict_zh`, especially the games
* Changed some URL links (from the teatube version to the original websites, if possible)
* Corrected the names of the wrongly-spelled proper nouns
* Rearranged the order of some entries
2021-12-16 13:48:02 +08:00
MrZ626
9c8c9f2106 涉及框架的设置项统一应用,不再细分时机
修改errData的获得方式
WIDGET新增setOnChange方法,不再依赖THEME
2021-12-16 12:39:42 +08:00
MrZ626
0498beecdf 特化新的模式选择场景名 2021-12-16 03:04:35 +08:00
MrZ626
8e075adf8f 新增一个简易秒表小程序 2021-12-16 02:58:36 +08:00
MrZ626
60f2a0e647 更新ios无法自动退出的界面细节 2021-12-16 02:37:25 +08:00
MrZ626
b642f2b5c4 Merge branch 'main' into test-new-mode-system 2021-12-16 02:32:51 +08:00
MrZ626
2b80f72c6b 移除框架内几处对SETTING的依赖 2021-12-16 02:31:53 +08:00
MrZ626
462720881a 支持鼠标滚动模式列表 2021-12-16 02:07:49 +08:00
MrZ626
3dda0254a8 调整中文词典的游戏介绍词条及顺序
Co-authored-by: C₂₉H₂₅N₃O₅ <cgu52@wisc.edu>
2021-12-16 00:22:32 +08:00
MrZ626
054a52a445 版本推进 2021-12-15 14:28:44 +08:00
MrZ626
85242d808b 修复语言文件小问题 2021-12-15 14:28:05 +08:00
MrZ626
57241677a9 修复混战 2021-12-15 14:26:40 +08:00
MrZ626
6ccdee2a53 bgm模块新增瞬间开/关功能
字符串扩展模块不再直接修改全局的string,需要外部自己补充
2021-12-15 11:28:25 +08:00
MrFaq2018
a3d2b7b7f3 Update lang_es.lua (#552) 2021-12-14 12:41:45 +08:00
MrZ626
b7b28b4ae3 修复经典模式h和u难度没有干旱计数器 close #546 2021-12-13 03:52:19 +08:00
MrZ626
30748200dd 修复自定义场地界面按超过第三个的鼠标键会报错 2021-12-11 19:38:24 +08:00
MrZ626
c9f8240234 添加模式搜索的帮助文本 2021-12-10 13:22:42 +08:00
MrZ626
5c7082e886 修复不能deepdrop 2021-12-10 12:50:57 +08:00
MrZ626
9a3c889a9d 修改词典和tip 2021-12-10 09:31:07 +08:00
MrZ626
f41f58e13f 模式文件夹可以显示作者 2021-12-10 01:49:05 +08:00
MrZ626
e81f25c216 修正段位更新条件
模式列表显示获得的段位
2021-12-10 01:37:14 +08:00
MrZ626
36fc681fbf 项目名太长会压缩显示 2021-12-09 20:10:46 +08:00
MrZ626
87e5e29129 彩蛋模式补充进模式树 2021-12-09 20:10:17 +08:00
MrZ626
b432fdf90a 部分语言的模式说明添加换行 2021-12-09 19:43:36 +08:00
MrZ626
6e78a3fedd 选择模式后右侧显示排行榜等信息 2021-12-09 19:41:42 +08:00
MrZ626
24760801af 增加模式图标显示,等待添加素材 2021-12-09 17:26:37 +08:00
MrZ626
f5e8e0f7a5 Merge commit 'df089a2f04fc44774e8dc722cc5d9948f94e5de5' into HEAD 2021-12-09 17:26:31 +08:00
MrZ626
df089a2f04 框架新增1*1空白画布变量PAPER 2021-12-09 17:26:02 +08:00
user670
6600713f4b Update lang_en.lua (#540) 2021-12-09 16:04:24 +08:00
NOT_A_ROBOT
96dad762b2 Update lang_en.lua (#544)
BiRS now allows you to spin the O1 piece.
2021-12-09 16:03:57 +08:00
MrZ626
5470387685 优化滚动
增加触摸控制
2021-12-09 15:55:09 +08:00
MrZ626
fa64c868b9 调整一些tip 2021-12-09 15:21:51 +08:00
MrZ626
2f4a416353 整理代码
调整模式排列顺序
2021-12-09 15:13:09 +08:00
MrZ626
3dbafb042c 进一步优化 2021-12-09 15:04:17 +08:00
MrZ626
97e7b019dd TRS的N/H块补充一个踢墙 2021-12-09 03:21:28 +08:00
MrZ626
28103ad952 新模式选择菜单原型
删除模式图标
动态加载所有模式
2021-12-09 03:20:57 +08:00
MrZ626
1826ca6f2f fix 2021-12-09 01:03:27 +08:00
MrZ626
db490a6c6c FILE.load新增-lua方式(直接运行,无环境限制) 2021-12-09 01:01:24 +08:00
MrZ626
421fdef4f9 调整两个群友词条的关键词 2021-12-09 01:00:40 +08:00
MrZ626
d717ce842d 调整tip 2021-12-08 09:19:58 +08:00
MrZ626
f13c9792af 调整把按键添加到录像的时机
修复触发了自动保存的最后一个按键本身不会保存到录像里
2021-12-08 08:40:24 +08:00
MrZ626
41e7b8e0f4 版本推进 2021-12-07 22:43:53 +08:00
MrZ626
4bd723a7ee 整理代码 2021-12-07 22:43:48 +08:00
MrZ626
66d5bd5490 更多场景的大标题添加最大显示长度 2021-12-07 22:40:16 +08:00
MrZ626
351d0258b2 再优化miya立绘 2021-12-07 22:40:16 +08:00
NOT_A_ROBOT
26fb9a7052 Add Strategy+ (#539) 2021-12-07 22:39:00 +08:00
MrZ626
307fd637fa 换新miya立绘
给不同立绘添加不同点击动画
2021-12-07 20:25:14 +08:00
MrZ626
93fb716f89 fix 2021-12-07 17:10:02 +08:00
MrZ626
7b41551e2d xitonglai 2021-12-07 16:57:47 +08:00
MrZ626
4806af5f7d 重做关于页面,赞助二维码搬家 2021-12-07 16:00:52 +08:00
MrZ626
85cb55cdd0 文本控件也支持设置最大宽度了 2021-12-07 15:51:19 +08:00
MrZ626
27a9697e47 修改scene模块,支持在切换场景的时候传参了 2021-12-07 15:04:27 +08:00
MrZ626
7d230cc3b0 修正印尼语按钮文本错误 close #536 2021-12-07 14:33:04 +08:00
MrZ626
0db2fffad1 版本推进 2021-12-07 01:43:17 +08:00
MrZ626
2a3296a0e8 调整pixel皮肤,修改x块的默认色为黄色 2021-12-07 01:26:46 +08:00
MrZ626
941b875afa 再微调语言设置界面
整理代码
2021-12-07 01:05:46 +08:00
MrZ626
99155bb9cf 更新macOS安装包用图
Co-authored-by: C₂₉H₂₅N₃O₅ <cgu52@wisc.edu>
2021-12-07 01:00:22 +08:00
MrZ626
0701dd2ad3 新皮肤:pixel(by C₂₉H₂₅N₃O₅) 2021-12-07 00:59:19 +08:00
MrZ626
5570c19e1f 调整颜色表
调整语言选择菜单
整理代码
2021-12-07 00:59:19 +08:00
NOT_A_ROBOT
a728c91476 Add Indonesian Translation (#535)
- Added Indonesian language file
- Added Indonesian button in language select menu
- Added Indonesian variant of the word "language" on the language select menu
- Added credit to me for translating (applies to all languages)
2021-12-07 00:00:51 +08:00
MrZ626
6a43481067 优化小程序triple体验 2021-12-06 22:42:49 +08:00
MrZ626
29a049fe4e 版本推进 2021-12-06 22:20:59 +08:00
MrZ626
b5a9c8e1bb 修正一处手柄事件可能爆炸 2021-12-06 21:17:30 +08:00
MrZ626
bb9a35c161 修复云存档/读档的一处小问题 2021-12-06 21:11:54 +08:00
MrZ626
b25a345b42 更换click音效,音乐室播放按钮声音调整 2021-12-06 20:33:41 +08:00
MrZ626
b22b0e0194 修正文件加载模块参数识别的小问题 2021-12-06 19:52:00 +08:00
MrZ626
55cf95f218 修正策略堆叠模式评级标准不当 2021-12-06 19:24:24 +08:00
MrZ626
225ddbcfac 调整几个tip 2021-12-06 16:43:46 +08:00
MrZ626
9377090c7c 【bug风险较大,需要测试】解耦玩家代码中的部分混战模式代码 2021-12-06 16:00:46 +08:00
MrZ626
ed002ec2e1 略微降低master-h模式骨块出现后的难度 2021-12-06 13:49:51 +08:00
MrZ626
e33036d9ec 调整几个词条的关键词 2021-12-06 12:46:17 +08:00
MrZ626
ef03e7c009 layout菜单名改为style 2021-12-06 12:46:11 +08:00
MrZ626
aef4220ac0 修复自定义场地16号颜色的方块名位置显示错误
优化皮肤设置页面交互效果
2021-12-06 03:25:39 +08:00
MrZ626
46223e38cd STRING模块新增一个简易摘要算法,未来保护用户密码明文可能用到 2021-12-06 03:18:41 +08:00
MrZ626
4bafa4bffe 版本推进 2021-12-05 22:01:16 +08:00
MrZ626
2b3dd877dd 修正100攻击竞速模式没有重力 2021-12-05 18:13:47 +08:00
MrZ626
0553e5c45e 调整中文tip 2021-12-05 18:11:12 +08:00
MrZ626
4d93374cf6 微调暂停界面和语言选择界面 2021-12-05 00:54:42 +08:00
MrZ626
4e421bf9ba 微调一些场景细节 2021-12-04 22:29:38 +08:00
user670
8b2a9d7c01 Update lang_en.lua (#534) 2021-12-04 22:17:43 +08:00
C₂₉H₂₅N₃O₅
5a3244d345 更改语言选择界面布局 (#532)
* 再更改语言选择布局
2021-12-04 19:56:24 +08:00
MrZ626
f1b9d0c5e4 新增返回按钮音效 2021-12-03 17:15:32 +08:00
MrZ626
6493e0e623 创建button和key控件时的sound参数可以指定音效名了 2021-12-03 16:45:37 +08:00
MrZ626
e71ba17f9f 微调一个中文词典词条 2021-12-03 11:57:21 +08:00
MrZ626
e656363e20 录像回放菜单对键盘支持更好 2021-12-03 11:23:44 +08:00
MrZ626
0826a748ae 版本推进 2021-12-03 11:04:51 +08:00
MrZ626
a595fe99ef 大规模整理中文tip 2021-12-03 10:50:05 +08:00
MrZ626
9dbc7942e3 调整语言菜单标题 2021-12-03 10:49:57 +08:00
C₂₉H₂₅N₃O₅
845d8ae32e 字体增加谚文/语言滚动菜单丰富 (#530)
* 字体支持谚文显示

* 语言选择界面滚动菜单增加一堆语言
2021-12-03 08:26:09 +08:00
MrZ626
5c524e138c 语言选择菜单会轮流显示不同语言的“语言” 2021-12-02 22:06:06 +08:00
MrZ626
86d9265ff9 修复最后一个hold的死锁问题 close #528 2021-12-02 18:42:03 +08:00
MrZ626
6994a5d6d3 调整tip 2021-12-02 18:16:59 +08:00
MrZ626
e6213b00c1 修复无尽挖掘规则包会对非指定背景做不好的事情 close #525 2021-12-02 14:07:52 +08:00
MrZ626
43e2caa30e 修正进入登录场景时本地没保存过账户信息文件时会弹出文件读取错误 2021-12-02 10:04:38 +08:00
MrZ626
97ca245dfc 修复放录像的时候虚拟按键不会自己动 2021-12-02 08:58:35 +08:00
MrZ626
36de1c0751 版本推进 2021-12-02 01:40:01 +08:00
MrZ626
704341fd15 修正软降在sddas/sdarr很小的时候行为不正确 2021-12-02 01:33:50 +08:00
MrZ626
22b61bc9c3 修正暂停界面数据显示条件为>=180帧而不是>180帧
key控件微调
2021-12-02 00:30:21 +08:00
MrZ626
f4cbbc0a2a 修复cc看不到初始场地 2021-12-01 22:15:54 +08:00
MrZ626
dc99187b9d 修改三个音效名称 2021-12-01 22:03:22 +08:00
MrZ626
915598dec4 整理代码,SFX模块load时会提示缺失多少音效 2021-12-01 19:23:39 +08:00
MrZ626
e7b4518d73 【警告:需要测试】
调整玩家能hold/移动/旋转方块的条件
修复cc复活后小bug
整理代码
2021-12-01 15:46:12 +08:00
NOT_A_ROBOT
9603a78e87 Halved field height for Big mode (#520)
* Halved field height for big mode

Co-authored-by: MrZ_26 <1046101471@qq.com>
2021-12-01 09:29:57 +08:00
MrZ626
bd90e051d4 版本推进 2021-12-01 02:41:18 +08:00
MrZ626
26e66b313f 继续收拾各种ui相关
空心控件统一加上灰色背景方便观察
按钮样式调整
2021-12-01 02:40:11 +08:00
MrZ626
c534bbd12a 微调马拉松和混战的速度曲线 2021-12-01 00:39:21 +08:00
NOT_A_ROBOT
83b5e217e5 Add Big Mode (#515)
I even halved the gravity :)
2021-12-01 00:10:00 +08:00
MrZ626
c0adf5bf0b COLOR模块新增三个半透明灰色并大量应用
微调颜色V和lV的hue值
2021-11-30 23:36:04 +08:00
MrZ626
4ff737a4ac 减小语音随机偏差范围 2021-11-30 23:20:59 +08:00
MrZ626
5af0706c09 普通消1不再有single语音 2021-11-30 22:52:27 +08:00
MrZ626
4ccee0f1de 修改小程序trp的next生成 2021-11-30 22:31:46 +08:00
MrZ626
9b752d540e 修正慢速下落有拖影不好看
测试代码忘删
2021-11-30 22:24:58 +08:00
MrZ626
e860c7b7ec 大改重力和软降的结算逻辑,两个值接近的时候不会看起来不自然了 close #438 2021-11-30 19:40:53 +08:00
C29H25N3O5
8a1fd9531f 修复NH块搞反的问题 2021-11-30 15:54:54 +08:00
C29H25N3O5
5fd6e0ee99 再更新虚拟按键贴图, 使用Plex字体 2021-11-30 15:54:52 +08:00
MrZ626
53b2b81fe0 再新增几个tip 2021-11-30 14:51:04 +08:00
MrZ626
6ccc811b46 微调tip 2021-11-30 12:56:32 +08:00
MrZ626
962a61567a OS X系统名称字符串强制改为macOS close #513 2021-11-30 12:25:59 +08:00
MrZ626
58f05e1cec 控制台sudo命令改名su 2021-11-30 12:22:14 +08:00
MrZ626
6b426790c7 调整小程序triple 2021-11-30 11:49:48 +08:00
MrZ626
d4fc578673 词典添加穿透词条 2021-11-30 11:17:17 +08:00
MrZ626
51b567b8db app -list输出美化 2021-11-30 03:53:22 +08:00
MrZ626
07b47dee3f 版本推进 2021-11-30 01:50:13 +08:00
MrZ626
4431a906b9 整理代码 2021-11-30 01:44:21 +08:00
NOT_A_ROBOT
2bb6852e3e Added multiple bg and bgm to Strategy Mode (#506)
(excluding strategy_e for the bgm)
2021-11-30 01:42:39 +08:00
user670
1948ed3e16 Update gameTables.lua (#508)
On an XBox controller, B is on the right and A is on the bottom (unlike a Nintendo controller), and it makes more sense to default B to rotate right and A to rotate left.
2021-11-30 01:42:02 +08:00
MrZ626
81b5ccae30 修复检测第一次启动失败 2021-11-30 01:40:53 +08:00
MrZ626
5543ff0d29 新小程序:Triple 2021-11-30 01:40:49 +08:00
MrZ626
cd567e9e98 删除添加作者qq按钮 2021-11-30 01:40:01 +08:00
MrZ626
5d86925a8a 大多数菜单的二次确认统一用tryXXX管理 2021-11-30 01:40:01 +08:00
MrZ626
e3db564a4b 整理代码,返回需要二次确认的小程序统一用一个函数 2021-11-30 01:40:01 +08:00
MrZ626
a4293624ab 微调wine颜色 2021-11-29 22:24:17 +08:00
MrZ626
367e2dc81a 新增几个tip 2021-11-29 21:32:09 +08:00
MrZ626
9ec33c6eef 修改BGM: sugar fairy的作者标注 2021-11-29 21:32:08 +08:00
MrZ626
9c9b8d36f2 小程序mem平衡调整 2021-11-29 21:32:08 +08:00
MrZ626
4fc6f335c7 新增小程序:Memorize 2021-11-29 15:40:55 +08:00
MrZ626
d2f4123d08 修改两个有数字键盘的小程序的退格键图标 2021-11-29 15:40:40 +08:00
MrZ626
b29d352a1b 把主菜单快捷键加回来 2021-11-29 12:57:17 +08:00
MrZ626
cd5a71cd12 更新赞助名单 2021-11-29 12:49:58 +08:00
MrZ626
cdd68e985d 修正键位设置菜单里mac的del键符号错误 2021-11-29 11:15:24 +08:00
MrZ626
8cf4d4280c 修正Ospin变O后操作序列不清空
Ospin变远端朝下JL时允许水平可移动
2021-11-29 11:11:54 +08:00
C29H25N3O5
cd29bf8702 调整字体
* Monospaced字体简中字库使用大陆标准字形, 日语部分仍然使用日语字形
* 调整自定义图标手柄和键盘部分的文字字体
* 修复proportional字体a的变音符没对齐的问题
* 修复Monospaced字体ij连字的问题
* 修复Monospaced字体slash还用的是plex字体的问题
2021-11-28 18:24:11 -06:00
MrZ626
13d98be051 版本推进 2021-11-29 04:23:05 +08:00
MrZ626
a350ff3182 微调背景模块,自定义模式设置背景的时候访问不到特殊背景了 2021-11-29 04:22:08 +08:00
MrZ626
e0360cc7eb 修正一处模块更新错误(傻了) 2021-11-29 03:57:18 +08:00
MrZ626
4249a29b63 继续优化键位设置菜单 2021-11-29 03:38:05 +08:00
MrZ626
43b2a0a8c8 优化键位设置菜单各种键的显示 2021-11-29 03:27:57 +08:00
MrZ626
6d6584f99e 修改按钮音效,给复选框和选择器添加新音效 2021-11-29 02:48:41 +08:00
MrZ626
077c651226 微调键位设置菜单 2021-11-29 01:16:22 +08:00
MrZ626
3fc872aa76 微调几个隐藏模式入口点击范围 2021-11-28 22:39:49 +08:00
MrZ626
cb0b347a38 更新赞助名单 2021-11-28 22:21:10 +08:00
MrZ626
d08967c688 整理词典 2021-11-28 22:02:39 +08:00
MrZ626
3666c0caa9 修复更换自定义背景图片时没有更新尺寸 2021-11-28 20:52:12 +08:00
MrZ626
4ef179fccb 控制台场景向全局环境添加一个输出到控制台的函数 close #499 2021-11-28 19:51:46 +08:00
MrZ626
861f9b3caa 继续完善手柄控制 2021-11-28 19:40:26 +08:00
MrZ626
05292df456 模式地图上读取手柄按键时不再报错 2021-11-28 17:43:36 +08:00
MrZ626
9fed692223 控制台help命令输出美化 close #502 2021-11-28 16:53:24 +08:00
MrZ626
b1c04c1fea 修复自定义模式用按钮开始游戏会报错 2021-11-28 16:21:27 +08:00
MrZ626
bc9adc2cd3 调整扳机键的默认触发阈值 2021-11-28 16:16:44 +08:00
MrZ626
cdf149afca 略微优化自定义背景绘制性能 2021-11-28 05:20:29 +08:00
MrZ626
73145b4e5e 自定义背景拖入无法识别的格式时会提示 2021-11-28 05:20:18 +08:00
MrZ626
f8b9f30fd6 修改框架的光标默认位置 2021-11-28 05:02:29 +08:00
MrZ626
e6bc567b12 两种按钮上的文本也会挤压绘制了
优化控件绘制性能
修正两个背景设置按钮位置错误
调整之前忘了同步的语言
2021-11-28 04:56:55 +08:00
MrZ626
fe004a72f0 版本推进 2021-11-28 04:17:44 +08:00
MrZ626
0433fd3d9d 三个高难隐形使用不同模式图标 close #493 2021-11-28 04:10:42 +08:00
MrZ626
1c18060570 尝试修复地图菜单读取手柄摇杆位置错误 2021-11-28 04:06:37 +08:00
MrZ626
be54c0e641 关闭背景时亮度可调
新增自定义图片背景功能(可调透明度,目前仅电脑可用)
2021-11-28 03:56:53 +08:00
MrZ626
0be2eb9107 修正一处可能未改回材质缩放模式 2021-11-28 03:30:34 +08:00
MrZ626
4859faf1e7 创建控件允许留空code域,什么都不会发生 2021-11-28 02:03:52 +08:00
MrZ626
c25d40c67d 启动加载数据文件时允许不存在,不会提醒 close #495 2021-11-28 01:59:24 +08:00
MrZ626
b6c37a5c9f 框架keyDown事件机制微调,重构框架主循环和控件相关代码
可以用键盘和手柄控制光标(手柄不完善)
整理代码和部分语言文件细节
2021-11-27 23:16:21 +08:00
MrZ626
f6b4c1b109 整理代码,表示键盘按键的字符串使用单引号 2021-11-27 19:01:32 +08:00
MrZ626
841faeede4 版本推进 2021-11-27 14:33:38 +08:00
MrZ626
e61b9b23a0 修复右侧c/s/a+方向键不能触发控件功能 close #492 2021-11-27 14:33:36 +08:00
MrZ626
72a826ef0a 微调报错界面,日志使用等宽字体 2021-11-27 14:25:13 +08:00
MrZ626
f070b8f295 修正svg标题的小问题 2021-11-27 14:20:05 +08:00
MrZ626
1646b75520 修正TRS的v块1<->2比0<->3少一个踢墙 2021-11-27 14:20:05 +08:00
C29H25N3O5
241617e31a 微调字体
* Monospaced的CJK字体也调整为思源
* 改动几个新元素汉字
* 精简Monospaced字符集
2021-11-26 22:58:40 -06:00
MrZ626
5de2893e07 帮0.17前的版本自动调大1帧的das打断,尝试维持手感 2021-11-27 05:52:35 +08:00
MrZ626
030e894040 theme移出框架,大改通常bgm的配置 2021-11-27 05:35:55 +08:00
MrZ626
e7b9a4ba87 添加DRS_weak旋转系统 close #441 2021-11-27 05:09:02 +08:00
MrZ626
617bae67c6 修正matt的一些翻译修改和控制台代码 2021-11-27 04:47:48 +08:00
MattMayuga
64d2d08820 Update English translation (#487)
* Update readme.md

* Update lang_en.lua

* Update app_console.lua

* Update error.lua

* Add warnings and extended time to 10s for resetall

When you use the resetall command, you will now get a message that there is no way to recover the saved data when it is deleted.
2021-11-27 04:28:08 +08:00
NOT_A_ROBOT
037b33c99a Update theme list (#489) 2021-11-27 04:26:40 +08:00
MrZ626
afa69ce9a4 版本推进 2021-11-27 04:24:41 +08:00
MrZ626
3226c0c831 重构字体模块,支持多字体
控制台应用等宽字体
2021-11-27 04:24:40 +08:00
MrZ626
4e759cad4c ultra模式重开时会重新播放bgm 2021-11-27 02:05:48 +08:00
MrZ626
291795928d 更多的设置修改的时候会触发警告 2021-11-26 21:52:10 +08:00
MrZ626
a1315e7f7f 修复一处遗留逻辑hold和序列生成相关的错误 2021-11-26 21:24:34 +08:00
MrZ626
657bc2b4e0 修正加载文件的时候会因为没有应用语言没法弹出消息而报错 2021-11-26 14:15:42 +08:00
MrZ626
d8b12fc55d 版本推进 2021-11-26 01:48:23 +08:00
MrZ626
6d11367ea4 新BGM:malate(暂未使用) 2021-11-26 01:47:14 +08:00
MrZ626
eb9e741b4f 关于界面的对称40行入口换成堆积模式 2021-11-26 00:59:23 +08:00
MrZ626
c47546d501 微调一些玩家动作逻辑
修复零ARE+非零lineARE的时候ihs失效
2021-11-26 00:55:29 +08:00
MrZ626
11aa178fc1 ultra模式计时器样式改为数字 2021-11-25 19:58:22 +08:00
MrZ626
f3a88ef269 游戏内再次封装saveFile和loadFile函数
原本的FILE模块更独立,不基于全局text变量和报错信息而是直接报错
2021-11-25 17:38:09 +08:00
MrZ626
720dc2131f 字符串扩展模块给默认string库补充两个方法repD和sArg 2021-11-25 17:37:46 +08:00
MrZ626
701ef17ae1 大爆炸改名清版竞速 2021-11-25 14:03:36 +08:00
MrZ626
1a689a5f07 修正当前方块显示条件 2021-11-25 09:57:45 +08:00
232 changed files with 7149 additions and 4460 deletions

Binary file not shown.

View File

@@ -4,8 +4,9 @@ local BGs={
} }
local BGlist={'none'} local BGlist={'none'}
local BG={ local BG={
cur='none',
default='none', default='none',
locked=false,
cur='none',
init=false, init=false,
resize=false, resize=false,
update=NULL, update=NULL,
@@ -14,6 +15,8 @@ local BG={
discard=NULL, discard=NULL,
} }
function BG.lock()BG.locked=true end
function BG.unlock()BG.locked=false end
function BG.add(name,bg) function BG.add(name,bg)
BGs[name]=bg BGs[name]=bg
BGlist[#BGlist+1]=name BGlist[#BGlist+1]=name
@@ -21,6 +24,9 @@ end
function BG.getList() function BG.getList()
return BGlist return BGlist
end end
function BG.remList(name)
table.remove(BGlist,TABLE.find(BGlist,name))
end
function BG.send(...) function BG.send(...)
if BG.event then if BG.event then
BG.event(...) BG.event(...)
@@ -29,20 +35,20 @@ end
function BG.setDefault(bg) function BG.setDefault(bg)
BG.default=bg BG.default=bg
end end
function BG.set(background) function BG.set(name)
background=background or BG.default name=name or BG.default
if not BGs[background]or not SETTING.bg then return end if not BGs[name]or BG.locked then return end
if background~=BG.cur then if name~=BG.cur then
BG.discard() BG.discard()
BG.cur=background BG.cur=name
background=BGs[background] local bg=BGs[name]
BG.init= background.init or NULL BG.init= bg.init or NULL
BG.resize= background.resize or NULL BG.resize= bg.resize or NULL
BG.update= background.update or NULL BG.update= bg.update or NULL
BG.draw= background.draw or NULL BG.draw= bg.draw or NULL
BG.event= background.event or NULL BG.event= bg.event or NULL
BG.discard=background.discard or NULL BG.discard=bg.discard or NULL
BG.init() BG.init()
end end
return true return true

View File

@@ -1,44 +1,37 @@
local lastLoaded={} local lastLoaded={}
local maxLoadedCount=3 local maxLoadedCount=3
local nameList={}
local SourceObjList={} local SourceObjList={}
local volume=1 local volume=1
local BGM={ local BGM={
default=false, default=false,
getList=function()error("Cannot getList before initialize!")end,
getCount=function()return 0 end,
play=NULL,
stop=NULL,
onChange=NULL, onChange=NULL,
--nowPlay=[str:playing ID] --nowPlay=[str:playing ID]
--playing=[src:playing SRC] --playing=[src:playing SRC]
--lastPlayed=[str:lastPlayed ID] --lastPlayed=[str:lastPlayed ID]
} }
local function task_fadeOut(src)
while true do function BGM.getList()return nameList end
coroutine.yield() function BGM.getCount()return #nameList end
local v=src:getVolume()-.025*volume local function _addFile(name,path)
src:setVolume(v>0 and v or 0) if not SourceObjList[name]then
if v<=0 then table.insert(nameList,name)
src:pause() SourceObjList[name]={path=path,source=false}
return true
end
end end
end end
local function task_fadeIn(src) function BGM.load(name,path)
while true do if type(name)=='table'then
coroutine.yield() for k,v in next,name do
local v=volume _addFile(k,v)
v=math.min(v,src:getVolume()+.025*v)
src:setVolume(v)
if v>=volume then
return true
end end
else
_addFile(name,path)
end end
table.sort(nameList)
LOG(BGM.getCount().." BGM files added")
end end
local function check_curFadeOut(task,code,src)
return task.code==code and task.args[1]==src
end
local function _tryReleaseSources() local function _tryReleaseSources()
local n=#lastLoaded local n=#lastLoaded
while #lastLoaded>maxLoadedCount do while #lastLoaded>maxLoadedCount do
@@ -75,85 +68,115 @@ function BGM.setVol(v)
end end
end end
end end
function BGM.init(list)
BGM.init=nil
local simpList={} local function task_fadeOut(src)
for _,v in next,list do while true do
table.insert(simpList,v.name) coroutine.yield()
SourceObjList[v.name]={path=v.path,source=false} local v=src:getVolume()-.025*volume
end src:setVolume(v>0 and v or 0)
table.sort(simpList) if v<=0 then
function BGM.getList()return simpList end src:pause()
local count=#simpList return true
LOG(count.." BGM files added")
function BGM.getCount()return count end
local function _tryLoad(name)
if SourceObjList[name]then
if SourceObjList[name].source then
return true
elseif love.filesystem.getInfo(SourceObjList[name].path)then
SourceObjList[name].source=love.audio.newSource(SourceObjList[name].path,'stream')
SourceObjList[name].source:setLooping(true)
SourceObjList[name].source:setVolume(0)
table.insert(lastLoaded,1,name)
_tryReleaseSources()
return true
else
LOG("No BGM: "..SourceObjList[name],5)
end
elseif name then
LOG("No BGM: "..name,5)
end end
end end
function BGM.play(name) end
name=name or BGM.default local function task_fadeIn(src)
if not _tryLoad(name)then return end while true do
if volume==0 then coroutine.yield()
local v=volume
v=math.min(v,src:getVolume()+.025*v)
src:setVolume(v)
if v>=volume then
return true
end
end
end
local function check_curFadeOut(task,code,src)
return task.code==code and task.args[1]==src
end
local function _tryLoad(name)
if SourceObjList[name]then
if SourceObjList[name].source then
return true
elseif love.filesystem.getInfo(SourceObjList[name].path)then
SourceObjList[name].source=love.audio.newSource(SourceObjList[name].path,'stream')
SourceObjList[name].source:setVolume(0)
table.insert(lastLoaded,1,name)
_tryReleaseSources()
return true
else
LOG("No BGM: "..SourceObjList[name],5)
end
elseif name then
LOG("No BGM: "..name,5)
end
end
function BGM.play(name,args)
name=name or BGM.default
args=args or""
if not _tryLoad(name)or args:sArg('-preLoad')then return end
if volume==0 then
BGM.nowPlay=name
BGM.playing=SourceObjList[name].source
return true
end
if name and SourceObjList[name].source then
if BGM.nowPlay~=name then
if BGM.nowPlay then
if not args:sArg('-sdout')then
TASK.new(task_fadeOut,BGM.playing)
else
BGM.playing:pause()
end
end
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,SourceObjList[name].source)
TASK.removeTask_code(task_fadeIn)
BGM.nowPlay=name BGM.nowPlay=name
BGM.playing=SourceObjList[name].source BGM.playing=SourceObjList[name].source
return true if not args:sArg('-sdin')then
end BGM.playing:setVolume(0)
if name and SourceObjList[name].source then TASK.new(task_fadeIn,BGM.playing)
if BGM.nowPlay~=name then else
if BGM.nowPlay then BGM.playing:setVolume(volume)
TASK.new(task_fadeOut,BGM.playing)
end
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,SourceObjList[name].source)
TASK.removeTask_code(task_fadeIn)
TASK.new(task_fadeIn,SourceObjList[name].source)
BGM.nowPlay=name
BGM.playing=SourceObjList[name].source
BGM.lastPlayed=BGM.nowPlay
BGM.playing:seek(0)
BGM.playing:play() BGM.playing:play()
BGM.onChange(name)
end end
return true SourceObjList[name].source:setLooping(not args:sArg('-noloop'))
end BGM.lastPlayed=BGM.nowPlay
end BGM.playing:seek(0)
function BGM.seek(t)
if BGM.playing then
BGM.playing:seek(t)
end
end
function BGM.continue()
if BGM.lastPlayed then
BGM.nowPlay,BGM.playing=BGM.lastPlayed,SourceObjList[BGM.lastPlayed].source
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,SourceObjList[BGM.nowPlay].source)
TASK.removeTask_code(task_fadeIn)
TASK.new(task_fadeIn,BGM.playing)
BGM.playing:play() BGM.playing:play()
BGM.onChange(name)
end end
return true
end end
function BGM.stop() end
function BGM.seek(t)
if BGM.playing then
BGM.playing:seek(t)
end
end
function BGM.isPlaying()
return BGM.playing and BGM.playing:isPlaying()
end
function BGM.continue()
if BGM.lastPlayed then
BGM.nowPlay,BGM.playing=BGM.lastPlayed,SourceObjList[BGM.lastPlayed].source
TASK.removeTask_iterate(check_curFadeOut,task_fadeOut,SourceObjList[BGM.nowPlay].source)
TASK.removeTask_code(task_fadeIn) TASK.removeTask_code(task_fadeIn)
TASK.new(task_fadeIn,BGM.playing)
BGM.playing:play()
end
end
function BGM.stop(args)
args=args or""
TASK.removeTask_code(task_fadeIn)
if not args:sArg('-s')then
if BGM.nowPlay then if BGM.nowPlay then
TASK.new(task_fadeOut,BGM.playing) TASK.new(task_fadeOut,BGM.playing)
end end
BGM.nowPlay,BGM.playing=nil elseif BGM.playing then
BGM.playing:pause()
end end
BGM.nowPlay,BGM.playing=nil
end end
return BGM return BGM

View File

@@ -1,5 +1,5 @@
local abs=math.abs local abs=math.abs
local function hsv(h,s,v,a) local function hsv(h,s,v,a)--Color type, Color amount, Light
if s<=0 then return v,v,v,a end if s<=0 then return v,v,v,a end
h=h*6 h=h*6
local c=v*s local c=v*s
@@ -19,33 +19,33 @@ local COLOR={
red= {hsv(0.00, 0.89, 0.91)}, red= {hsv(0.00, 0.89, 0.91)},
fire= {hsv(0.04, 0.93, 0.94)}, fire= {hsv(0.04, 0.93, 0.94)},
orange= {hsv(0.09, 0.99, 0.96)}, orange= {hsv(0.09, 0.99, 0.96)},
yellow= {hsv(0.16, 0.82, 0.90)}, yellow= {hsv(0.15, 0.82, 0.90)},
lime= {hsv(0.18, 0.89, 0.88)}, lime= {hsv(0.20, 0.89, 0.88)},
jade= {hsv(0.23, 1.00, 0.82)}, jade= {hsv(0.25, 1.00, 0.82)},
green= {hsv(0.33, 1.00, 0.81)}, green= {hsv(0.33, 1.00, 0.81)},
aqua= {hsv(0.48, 1.00, 0.74)}, aqua= {hsv(0.47, 1.00, 0.76)},
cyan= {hsv(0.53, 1.00, 0.88)}, cyan= {hsv(0.53, 1.00, 0.88)},
navy= {hsv(0.56, 1.00, 1.00)}, navy= {hsv(0.56, 1.00, 1.00)},
sea= {hsv(0.61, 1.00, 1.00)}, sea= {hsv(0.61, 1.00, 1.00)},
blue= {hsv(0.64, 1.00, 0.95)}, blue= {hsv(0.64, 1.00, 0.95)},
violet= {hsv(0.73, 1.00, 0.91)}, violet= {hsv(0.74, 1.00, 0.91)},
purple= {hsv(0.80, 1.00, 0.81)}, purple= {hsv(0.80, 1.00, 0.81)},
magenta= {hsv(0.86, 1.00, 0.78)}, magenta= {hsv(0.86, 1.00, 0.78)},
wine= {hsv(0.94, 0.96, 0.91)}, wine= {hsv(0.92, 0.98, 0.91)},
lRed= {hsv(0.00, 0.38, 0.93)}, lRed= {hsv(0.00, 0.38, 0.93)},
lFire= {hsv(0.04, 0.45, 0.91)}, lFire= {hsv(0.04, 0.45, 0.91)},
lOrange= {hsv(0.10, 0.53, 0.92)}, lOrange= {hsv(0.10, 0.53, 0.92)},
lYellow= {hsv(0.15, 0.61, 0.95)}, lYellow= {hsv(0.14, 0.61, 0.95)},
lLime= {hsv(0.19, 0.66, 0.92)}, lLime= {hsv(0.20, 0.66, 0.92)},
lJade= {hsv(0.24, 0.56, 0.90)}, lJade= {hsv(0.26, 0.56, 0.90)},
lGreen= {hsv(0.34, 0.49, 0.89)}, lGreen= {hsv(0.34, 0.49, 0.89)},
lAqua= {hsv(0.49, 0.59, 0.85)}, lAqua= {hsv(0.47, 0.59, 0.86)},
lCyan= {hsv(0.51, 0.77, 0.88)}, lCyan= {hsv(0.51, 0.77, 0.88)},
lNavy= {hsv(0.54, 0.80, 0.95)}, lNavy= {hsv(0.54, 0.80, 0.95)},
lSea= {hsv(0.56, 0.72, 0.97)}, lSea= {hsv(0.57, 0.72, 0.97)},
lBlue= {hsv(0.64, 0.44, 0.96)}, lBlue= {hsv(0.64, 0.44, 0.96)},
lViolet= {hsv(0.73, 0.47, 0.95)}, lViolet= {hsv(0.72, 0.47, 0.95)},
lPurple= {hsv(0.80, 0.62, 0.89)}, lPurple= {hsv(0.80, 0.62, 0.89)},
lMagenta= {hsv(0.86, 0.61, 0.89)}, lMagenta= {hsv(0.86, 0.61, 0.89)},
lWine= {hsv(0.93, 0.57, 0.92)}, lWine= {hsv(0.93, 0.57, 0.92)},
@@ -53,13 +53,13 @@ local COLOR={
dRed= {hsv(0.00, 0.80, 0.48)}, dRed= {hsv(0.00, 0.80, 0.48)},
dFire= {hsv(0.04, 0.80, 0.34)}, dFire= {hsv(0.04, 0.80, 0.34)},
dOrange= {hsv(0.07, 0.80, 0.39)}, dOrange= {hsv(0.07, 0.80, 0.39)},
dYellow= {hsv(0.11, 0.80, 0.37)}, dYellow= {hsv(0.12, 0.80, 0.37)},
dLime= {hsv(0.17, 0.80, 0.26)}, dLime= {hsv(0.20, 0.80, 0.26)},
dJade= {hsv(0.31, 0.80, 0.27)}, dJade= {hsv(0.29, 0.80, 0.27)},
dGreen= {hsv(0.33, 0.80, 0.26)}, dGreen= {hsv(0.33, 0.80, 0.26)},
dAqua= {hsv(0.47, 0.80, 0.23)}, dAqua= {hsv(0.46, 0.80, 0.24)},
dCyan= {hsv(0.50, 0.80, 0.30)}, dCyan= {hsv(0.50, 0.80, 0.30)},
dNavy= {hsv(0.59, 0.80, 0.42)}, dNavy= {hsv(0.58, 0.80, 0.42)},
dSea= {hsv(0.64, 0.80, 0.40)}, dSea= {hsv(0.64, 0.80, 0.40)},
dBlue= {hsv(0.67, 0.80, 0.34)}, dBlue= {hsv(0.67, 0.80, 0.34)},
dViolet= {hsv(0.71, 0.80, 0.35)}, dViolet= {hsv(0.71, 0.80, 0.35)},
@@ -72,12 +72,17 @@ local COLOR={
gray= {hsv(0.02, 0.05, 0.65)}, gray= {hsv(0.02, 0.05, 0.65)},
lGray= {hsv(0.02, 0.06, 0.86)}, lGray= {hsv(0.02, 0.06, 0.86)},
white= {hsv(0.01, 0.02, 0.99)}, white= {hsv(0.01, 0.02, 0.99)},
xGray= {hsv(0.00, 0.00, 0.35,.8)},
lxGray= {hsv(0.00, 0.00, 0.62,.8)},
dxGray= {hsv(0.00, 0.00, 0.16,.8)},
} }
for k,v in next,{ 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', 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', 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', 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', D='black',dH='dGray',H='gray',lH='lGray',Z='white',
X='xGray',lX='lxGray',dX='dxGray',
--Remain letter: EIKQTUX --Remain letter: EIKQTUX
}do }do
COLOR[k]=COLOR[v] COLOR[k]=COLOR[v]

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,6 @@ local IMG={}
function IMG.init(list) function IMG.init(list)
IMG.init=nil IMG.init=nil
local null=love.graphics.newCanvas(1,1)
setmetatable(IMG,{__index=function(self,name) setmetatable(IMG,{__index=function(self,name)
if type(list[name])=='table'then if type(list[name])=='table'then
self[name]={} self[name]={}
@@ -13,7 +12,7 @@ function IMG.init(list)
self[name]=love.graphics.newImage(list[name]) self[name]=love.graphics.newImage(list[name])
else else
LOG("No IMG: "..name) LOG("No IMG: "..name)
self[name]=null self[name]=PAPER
end end
return self[name] return self[name]
end}) end})

View File

@@ -1,4 +1,4 @@
NONE={}function NULL()end NONE={}function NULL()end PAPER=love.graphics.newCanvas(1,1)
EDITING="" EDITING=""
LOADED=false LOADED=false
@@ -37,7 +37,6 @@ REQUIRE= require'Zframework.require'
TASK= require'Zframework.task' TASK= require'Zframework.task'
WS= require'Zframework.websocket' WS= require'Zframework.websocket'
LANG= require'Zframework.languages' LANG= require'Zframework.languages'
THEME= require'Zframework.theme'
--Love-based modules (basic) --Love-based modules (basic)
FILE= require'Zframework.file' FILE= require'Zframework.file'
@@ -61,6 +60,7 @@ BGM= require'Zframework.bgm'
VOC= require'Zframework.voice' VOC= require'Zframework.voice'
local ms,kb=love.mouse,love.keyboard local ms,kb=love.mouse,love.keyboard
local KBisDown=kb.isDown
local gc=love.graphics local gc=love.graphics
local gc_push,gc_pop,gc_clear,gc_discard=gc.push,gc.pop,gc.clear,gc.discard local gc_push,gc_pop,gc_clear,gc_discard=gc.push,gc.pop,gc.clear,gc.discard
@@ -72,11 +72,24 @@ local WIDGET,SCR,SCN=WIDGET,SCR,SCN
local xOy=SCR.xOy local xOy=SCR.xOy
local ITP=xOy.inverseTransformPoint local ITP=xOy.inverseTransformPoint
local mx,my,mouseShow=-20,-20,false local max,min=math.max,math.min
local devMode
local mx,my,mouseShow,cursorSpd=640,360,false,0
local jsState={}--map, joystickID->axisStates: {axisName->axisVal} local jsState={}--map, joystickID->axisStates: {axisName->axisVal}
local errData={}--list, each error create {mes={errMes strings},scene=sceneNameStr} local errData={}--list, each error create {mes={errMes strings},scene=sceneNameStr}
local devMode 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 batteryImg=GC.DO{31,20, local batteryImg=GC.DO{31,20,
{'fRect',1,0,26,2}, {'fRect',1,0,26,2},
@@ -94,17 +107,16 @@ local function updatePowerInfo()
gc_clear(0,0,0,.25) gc_clear(0,0,0,.25)
if state~='unknown'then if state~='unknown'then
gc_setLineWidth(4) gc_setLineWidth(4)
local charging=state=='charging'
if state=='nobattery'then if state=='nobattery'then
gc_setColor(1,1,1) gc_setColor(1,1,1)
gc_setLineWidth(2) gc_setLineWidth(2)
gc_line(74,SCR.safeX+5,100,22) gc_line(74,5,100,22)
elseif pow then elseif pow then
if charging then gc_setColor(0,1,0) if state=='charging'then gc_setColor(0,1,0)
elseif pow>50 then gc_setColor(1,1,1) elseif pow>50 then gc_setColor(1,1,1)
elseif pow>26 then gc_setColor(1,1,0) elseif pow>26 then gc_setColor(1,1,0)
elseif pow==26 then gc_setColor(.5,0,1) elseif pow==26 then gc_setColor(.5,0,1)
else gc_setColor(1,0,0) else gc_setColor(1,0,0)
end end
gc.rectangle('fill',76,6,pow*.22,14) gc.rectangle('fill',76,6,pow*.22,14)
if pow<100 then if pow<100 then
@@ -127,36 +139,81 @@ local function updatePowerInfo()
end end
------------------------------------------------------------- -------------------------------------------------------------
local lastX,lastY=0,0--Last click pos local lastX,lastY=0,0--Last click pos
local function _updateMousePos(x,y,dx,dy)
if SCN.swapping 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 _triggerMouseDown(x,y,k)
if devMode==1 then
print(("(%d,%d)<-%d,%d ~~(%d,%d)<-%d,%d"):format(
x,y,
x-lastX,y-lastY,
math.floor(x/10)*10,math.floor(y/10)*10,
math.floor((x-lastX)/10)*10,math.floor((y-lastY)/10)*10
))
end
if SCN.swapping then return end
if SCN.mouseDown then SCN.mouseDown(x,y,k)end
WIDGET.press(x,y,k)
lastX,lastY=x,y
if showClickFX then SYSFX.newTap(3,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) function love.mousepressed(x,y,k,touch)
if touch then return end if touch then return end
mouseShow=true mouseShow=true
mx,my=ITP(xOy,x,y) mx,my=ITP(xOy,x,y)
if devMode==1 then _triggerMouseDown(mx,my,k)
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 SETTING.clickFX then SYSFX.newTap(3,mx,my)end
end end
function love.mousemoved(x,y,dx,dy,touch) function love.mousemoved(x,y,dx,dy,touch)
if touch then return end if touch then return end
mouseShow=true mouseShow=true
mx,my=ITP(xOy,x,y) mx,my=ITP(xOy,x,y)
if SCN.swapping then return end _updateMousePos(mx,my,dx,dy)
dx,dy=dx/SCR.k,dy/SCR.k
if SCN.mouseMove then SCN.mouseMove(mx,my,dx,dy)end
if ms.isDown(1)then
WIDGET.drag(mx,my,dx/SCR.k,dy/SCR.k)
else
WIDGET.cursorMove(mx,my)
end
end end
function love.mousereleased(x,y,k,touch) function love.mousereleased(x,y,k,touch)
if touch or SCN.swapping then return end if touch or SCN.swapping then return end
@@ -191,13 +248,13 @@ function love.touchpressed(id,x,y)
x,y=ITP(xOy,x,y) x,y=ITP(xOy,x,y)
lastX,lastY=x,y lastX,lastY=x,y
WIDGET.cursorMove(x,y) WIDGET.cursorMove(x,y)
if SCN.touchDown then SCN.touchDown(x,y)end if SCN.touchDown then SCN.touchDown(x,y,id)end
if kb.hasTextInput()then kb.setTextInput(false)end if kb.hasTextInput()then kb.setTextInput(false)end
end end
function love.touchmoved(_,x,y,dx,dy) function love.touchmoved(id,x,y,dx,dy)
if SCN.swapping then return end if SCN.swapping then return end
x,y=ITP(xOy,x,y) x,y=ITP(xOy,x,y)
if SCN.touchMove then SCN.touchMove(x,y,dx/SCR.k,dy/SCR.k)end 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) WIDGET.drag(x,y,dx/SCR.k,dy/SCR.k)
end end
function love.touchreleased(id,x,y) function love.touchreleased(id,x,y)
@@ -210,40 +267,40 @@ function love.touchreleased(id,x,y)
WIDGET.unFocus() WIDGET.unFocus()
SCN.mainTouchID=false SCN.mainTouchID=false
end end
if SCN.touchUp then SCN.touchUp(x,y)end if SCN.touchUp then SCN.touchUp(x,y,id)end
if(x-lastX)^2+(y-lastY)^2<62 then if(x-lastX)^2+(y-lastY)^2<62 then
if SCN.touchClick then SCN.touchClick(x,y)end if SCN.touchClick then SCN.touchClick(x,y)end
if SETTING.clickFX then SYSFX.newTap(3,x,y)end if showClickFX then SYSFX.newTap(3,x,y)end
end end
end end
local fnKey={NULL,NULL,NULL,NULL,NULL,NULL,NULL} local fnKey={NULL,NULL,NULL,NULL,NULL,NULL,NULL}
local function noDevkeyPressed(key) local function noDevkeyPressed(key)
if key=="f1"then fnKey[1]() if key=='f1'then fnKey[1]()
elseif key=="f2"then fnKey[2]() elseif key=='f2'then fnKey[2]()
elseif key=="f3"then fnKey[3]() elseif key=='f3'then fnKey[3]()
elseif key=="f4"then fnKey[4]() elseif key=='f4'then fnKey[4]()
elseif key=="f5"then fnKey[5]() elseif key=='f5'then fnKey[5]()
elseif key=="f6"then fnKey[6]() elseif key=='f6'then fnKey[6]()
elseif key=="f7"then fnKey[7]() elseif key=='f7'then fnKey[7]()
elseif key=="f8"then devMode=nil MES.new('info',"DEBUG OFF",.2) elseif key=='f8'then devMode=nil MES.new('info',"DEBUG OFF",.2)
elseif key=="f9"then devMode=1 MES.new('info',"DEBUG 1") elseif key=='f9'then devMode=1 MES.new('info',"DEBUG 1")
elseif key=="f10"then devMode=2 MES.new('info',"DEBUG 2") elseif key=='f10'then devMode=2 MES.new('info',"DEBUG 2")
elseif key=="f11"then devMode=3 MES.new('info',"DEBUG 3") elseif key=='f11'then devMode=3 MES.new('info',"DEBUG 3")
elseif key=="f12"then devMode=4 MES.new('info',"DEBUG 4") elseif key=='f12'then devMode=4 MES.new('info',"DEBUG 4")
elseif devMode==2 then elseif devMode==2 then
local W=WIDGET.sel local W=WIDGET.sel
if W then if W then
if key=="left"then W.x=W.x-10 if key=='left'then W.x=W.x-10
elseif key=="right"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=='up'then W.y=W.y-10
elseif key=="down"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.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.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
elseif key=="]"then W.font=W.font+5 elseif key==']'then W.font=W.font+5
else return true else return true
end end
else else
@@ -257,22 +314,34 @@ function love.keypressed(key,_,isRep)
mouseShow=false mouseShow=false
if devMode and not noDevkeyPressed(key)then if devMode and not noDevkeyPressed(key)then
return return
elseif key=="f8"then elseif key=='f8'then
devMode=1 devMode=1
MES.new('info',"DEBUG ON",.2) MES.new('info',"DEBUG ON",.2)
elseif key=="f11"then elseif key=='f11'then
SETTING.fullscreen=not SETTING.fullscreen SETTING.fullscreen=not SETTING.fullscreen
applyFullscreen() applySettings()
saveSettings() saveSettings()
elseif not SCN.swapping then elseif not SCN.swapping then
if SCN.keyDown then if EDITING==""and(not SCN.keyDown or SCN.keyDown(key,isRep))then
if EDITING==""then local W=WIDGET.sel
SCN.keyDown(key,isRep) 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
_triggerMouseDown(mx,my,1)
end
else
if W and W.keypress then
W:keypress(key)
end
end end
elseif key=="escape"and not isRep then
SCN.back()
else
WIDGET.keyPressed(key,isRep)
end end
end end
end end
@@ -308,35 +377,38 @@ local dPadToKey={
back='escape', back='escape',
} }
function love.joystickadded(JS) function love.joystickadded(JS)
jsState[JS:getID()]={ table.insert(jsState,{
_loveJSObj=JS, _id=JS:getID(),
_jsObj=JS,
leftx=0,lefty=0, leftx=0,lefty=0,
rightx=0,righty=0, rightx=0,righty=0,
triggerleft=0,triggerright=0 triggerleft=0,triggerright=0
} })
MES.new('info',"Joystick added") MES.new('info',"Joystick added")
end end
function love.joystickremoved(JS) function love.joystickremoved(JS)
local js=jsState[JS:getID()] for i=1,#jsState do
if js then if jsState[i]._jsObj==JS then
for i=1,#gamePadKeys do for j=1,#gamePadKeys do
if JS:isGamepadDown(gamePadKeys[i])then if JS:isGamepadDown(gamePadKeys[j])then
love.gamepadreleased(JS,gamePadKeys[i]) love.gamepadreleased(JS,gamePadKeys[j])
end
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
love.gamepadaxis(JS,'leftx',0)
love.gamepadaxis(JS,'lefty',0)
love.gamepadaxis(JS,'rightx',0)
love.gamepadaxis(JS,'righty',0)
love.gamepadaxis(JS,'triggerleft',-1)
love.gamepadaxis(JS,'triggerright',-1)
jsState[JS:getID()]=nil
MES.new('info',"Joystick removed")
end end
end end
function love.gamepadaxis(JS,axis,val) function love.gamepadaxis(JS,axis,val)
local js=jsState[JS:getID()] if jsState[1]and JS==jsState[1]._jsObj then
if js then local js=jsState[1]
if axis=='leftx'or axis=='lefty'or axis=='rightx'or axis=='righty'then if axis=='leftx'or axis=='lefty'or axis=='rightx'or axis=='righty'then
local newVal=--range: [0,1] local newVal=--range: [0,1]
val>.4 and 1 or val>.4 and 1 or
@@ -356,7 +428,7 @@ function love.gamepadaxis(JS,axis,val)
js[axis]=newVal js[axis]=newVal
end end
elseif axis=='triggerleft'or axis=='triggerright'then elseif axis=='triggerleft'or axis=='triggerright'then
local newVal=val>-.3 and 1 or 0--range: [-1,1] local newVal=val>.3 and 1 or 0--range: [0,1]
if newVal~=js[axis]then if newVal~=js[axis]then
if newVal==1 then if newVal==1 then
love.gamepadpressed(JS,jsAxisEventName[axis]) love.gamepadpressed(JS,jsAxisEventName[axis])
@@ -368,13 +440,36 @@ function love.gamepadaxis(JS,axis,val)
end end
end end
end end
function love.gamepadpressed(_,i) function love.gamepadpressed(_,key)
mouseShow=false mouseShow=false
if SCN.swapping then return end if not SCN.swapping then
if SCN.gamepadDown then SCN.gamepadDown(i) local cursorCtrl
elseif SCN.keyDown then SCN.keyDown(dPadToKey[i]or i) if SCN.gamepadDown then
elseif i=="back"then SCN.back() cursorCtrl=SCN.gamepadDown(key)
else WIDGET.gamepadPressed(dPadToKey[i]or i) 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
_triggerMouseDown(mx,my,1)
else
if W and W.keypress then
W:keypress(key)
end
end
end
end end
end end
function love.gamepadreleased(_,i) function love.gamepadreleased(_,i)
@@ -396,14 +491,16 @@ function love.lowmemory()
MES.new('check',"[auto GC] low MEM 设备内存过低") MES.new('check',"[auto GC] low MEM 设备内存过低")
end end
end end
local onResize=NULL
function love.resize(w,h) function love.resize(w,h)
if SCR.w==w and SCR.h==h then return end
SCR.resize(w,h) SCR.resize(w,h)
if BG.resize then BG.resize(w,h)end if BG.resize then BG.resize(w,h)end
if SCN.resize then SCN.resize(w,h)end if SCN.resize then SCN.resize(w,h)end
WIDGET.resize(w,h) WIDGET.resize(w,h)
FONT.reset() FONT.reset()
onResize(w,h)
SHADER.warning:send('w',w*SCR.dpi)
end end
local onFocus=NULL local onFocus=NULL
@@ -514,7 +611,7 @@ local devColor={
} }
local WS=WS local WS=WS
local WSnames={'app','user','play','stream','chat','manage'} local WSnames={'app','user','play','stream','chat','manage'}
local wsBottomImage do local wsImg={}do
local L={78,18, local L={78,18,
{'clear',1,1,1,0}, {'clear',1,1,1,0},
{'setCL',1,1,1,.3}, {'setCL',1,1,1,.3},
@@ -524,31 +621,23 @@ local wsBottomImage do
table.insert(L,{'setCL',1,1,1,i*.005}) table.insert(L,{'setCL',1,1,1,i*.005})
table.insert(L,{'fRect',i,0,1,18}) table.insert(L,{'fRect',i,0,1,18})
end end
wsBottomImage=GC.DO(L) wsImg.bottom=GC.DO(L)
wsImg.dead=GC.DO{20,20,
{'rawFT',20},
{'setCL',1,.3,.3},
{'mText',"X",11,-1},
}
wsImg.connecting=GC.DO{20,20,
{'rawFT',20},
{'setLW',3},
{'mText',"C",11,-1},
}
wsImg.running=GC.DO{20,20,
{'rawFT',20},
{'setCL',.5,1,0},
{'mText',"R",11,-1},
}
end end
local ws_deadImg=GC.DO{20,20,
{'setFT',20},
{'setCL',1,.3,.3},
{'mText',"X",11,-1},
}
local ws_connectingImg=GC.DO{20,20,
{'setFT',20},
{'setLW',3},
{'mText',"C",11,-1},
}
local ws_runningImg=GC.DO{20,20,
{'setFT',20},
{'setCL',.5,1,0},
{'mText',"R",11,-1},
}
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 function showPowerInfo()return true end
local onQuit=NULL
function love.run() function love.run()
local love=love local love=love
@@ -560,12 +649,11 @@ function love.run()
local TASK_update=TASK.update local TASK_update=TASK.update
local SYSFX_update,SYSFX_draw=SYSFX.update,SYSFX.draw local SYSFX_update,SYSFX_draw=SYSFX.update,SYSFX.draw
local WIDGET_update,WIDGET_draw=WIDGET.update,WIDGET.draw local WIDGET_update,WIDGET_draw=WIDGET.update,WIDGET.draw
local STEP,WAIT=love.timer.step,love.timer.sleep local STEP,WAIT=love.timer.step,love.timer.sleep
local FPS,MINI=love.timer.getFPS,love.window.isMinimized local FPS,MINI=love.timer.getFPS,love.window.isMinimized
local PUMP,POLL=love.event.pump,love.event.poll local PUMP,POLL=love.event.pump,love.event.poll
local timer,SETTING,VERSION=love.timer.getTime,SETTING,VERSION local timer,VERSION=love.timer.getTime,VERSION
local frameTimeList={} local frameTimeList={}
local lastFrame=timer() local lastFrame=timer()
@@ -599,6 +687,8 @@ function love.run()
--UPDATE --UPDATE
STEP() STEP()
if mouseShow then mouse_update(dt)end
if next(jsState)then gp_update(jsState[1],dt)end
VOC.update() VOC.update()
BG.update(dt) BG.update(dt)
TEXT_update(dt) TEXT_update(dt)
@@ -612,11 +702,10 @@ function love.run()
--DRAW --DRAW
if not MINI()then if not MINI()then
FCT=FCT+SETTING.frameMul FCT=FCT+frameMul
if FCT>=100 then if FCT>=100 then
FCT=FCT-100 FCT=FCT-100
local safeX=SCR.safeX
gc_replaceTransform(SCR.origin) gc_replaceTransform(SCR.origin)
gc_setColor(1,1,1) gc_setColor(1,1,1)
BG.draw() BG.draw()
@@ -627,16 +716,14 @@ function love.run()
TEXT_draw() TEXT_draw()
--Draw cursor --Draw cursor
if mouseShow then if mouseShow then drawCursor(time,mx,my)end
drawCursor(time,mx,my)
end
gc_replaceTransform(SCR.xOy_ul)
MES_draw()
gc_replaceTransform(SCR.origin) gc_replaceTransform(SCR.origin)
MES_draw()
--Draw power info. --Draw power info.
if showPowerInfo()then if showPowerInfo then
gc_setColor(1,1,1) gc_setColor(1,1,1)
gc_draw(infoCanvas,safeX,0,0,SCR.k) gc_draw(infoCanvas,SCR.safeX,0,0,SCR.k)
end end
--Draw scene swapping animation --Draw scene swapping animation
@@ -647,10 +734,12 @@ function love.run()
end end
gc_replaceTransform(SCR.xOy_d) gc_replaceTransform(SCR.xOy_d)
--Draw Version string --Draw Version string
gc_setColor(.8,.8,.8,.4) gc_setColor(.9,.9,.9,.42)
FONT.set(20) FONT.set(20)
mStr(VERSION.string,0,-30) mStr(VERSION.string,0,-30)
gc_replaceTransform(SCR.xOy_dl) gc_replaceTransform(SCR.xOy_dl)
local safeX=SCR.safeX/SCR.k
--Draw FPS --Draw FPS
FONT.set(15) FONT.set(15)
gc_setColor(1,1,1) gc_setColor(1,1,1)
@@ -690,14 +779,14 @@ function love.run()
for i=1,6 do for i=1,6 do
local status=WS.status(WSnames[i]) local status=WS.status(WSnames[i])
gc_setColor(1,1,1) gc_setColor(1,1,1)
gc.draw(wsBottomImage,-79,20*i-139) gc.draw(wsImg.bottom,-79,20*i-139)
if status=='dead'then if status=='dead'then
gc_draw(ws_deadImg,-20,20*i-140) gc_draw(wsImg.dead,-20,20*i-140)
elseif status=='connecting'then elseif status=='connecting'then
gc_setColor(1,1,1,.5+.3*math.sin(time*6.26)) gc_setColor(1,1,1,.5+.3*math.sin(time*6.26))
gc_draw(ws_connectingImg,-20,20*i-140) gc_draw(wsImg.connecting,-20,20*i-140)
elseif status=='running'then elseif status=='running'then
gc_draw(ws_runningImg,-20,20*i-140) gc_draw(wsImg.running,-20,20*i-140)
end end
local t1,t2,t3=WS.getTimers(WSnames[i]) local t1,t2,t3=WS.getTimers(WSnames[i])
gc_setColor(.9,.9,.9,t1)gc.rectangle('fill',-60,20*i-122,-16,-16) gc_setColor(.9,.9,.9,t1)gc.rectangle('fill',-60,20*i-122,-16,-16)
@@ -708,13 +797,13 @@ function love.run()
gc_present() gc_present()
--SPEED UPUPUP! --SPEED UPUPUP!
if SETTING.cleanCanvas then gc_discard()end if discardCanvas then gc_discard()end
end end
end end
--Fresh power info. --Fresh power info.
if time-lastFreshPow>2.6 then if time-lastFreshPow>2.6 then
if showPowerInfo()then if showPowerInfo then
updatePowerInfo() updatePowerInfo()
lastFreshPow=time lastFreshPow=time
end end
@@ -732,31 +821,44 @@ function love.run()
end end
end end
--Keep 60fps
_=timer()-lastFrame _=timer()-lastFrame
if _<.0162 then WAIT(.0162-_)end if _<sleepInterval*.9626 then WAIT(sleepInterval*.9626-_)end
while timer()-lastFrame<1/60 do end while timer()-lastFrame<sleepInterval do end
end end
end end
local Z={} local Z={}
Z.js=jsState function Z.getJsState()return jsState end
Z.errData=errData function Z.getErr(i)
if i=='#'then
return errData[#errData]
elseif i then
return errData[i]
else
return errData
end
end
function Z.setIfPowerInfo(func)showPowerInfo=func 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. --[Warning] Color and line width is uncertain value, set it in the function.
function Z.setCursor(func)drawCursor=func end function Z.setCursor(func)drawCursor=func end
--Change F1~F7 events of devmode (F8 mode) --Change F1~F7 events of devmode (F8 mode)
function Z.setOnFnKeys(list) function Z.setOnFnKeys(list)
assert(type(list)=='table') assert(type(list)=='table',"Z.setOnFnKeys(list): list must be a table.")
for i=1,7 do fnKey[i]=type(list[i])=='function'and list[i]or NULL end for i=1,7 do fnKey[i]=type(list[i])=='function'and list[i]or NULL end
end end
function Z.setOnFocus(func)onFocus=type(func)=='function'and func or NULL end function Z.setOnFocus(func)onFocus=assert(type(func)=='function'and func,"Z.setOnFocus(func): func must be a function")end
function Z.setOnQuit(func)onQuit=type(func)=='function'and func or NULL end function Z.setOnResize(func)onResize=assert(type(func)=='function'and func,"Z.setOnResize(func): func must be a function")end
function Z.setOnQuit(func)onQuit=assert(type(func)=='function'and func,"Z.setOnQuit(func): func must be a function")end
return Z return Z

View File

@@ -20,4 +20,18 @@ function MATH.coin(a,b)
end end
end end
function MATH.interval(v,low,high)
if v<=low then
return low
elseif v>=high then
return high
else
return v
end
end
function MATH.expApproach(a,b,k)
return b+(a-b)*2.718281828459045^-k
end
return MATH return MATH

View File

@@ -140,11 +140,11 @@ function profile.switch()
switch=not switch switch=not switch
if not switch then if not switch then
profile.stop() profile.stop()
love.system.setClipboardText(PROFILE.report()) love.system.setClipboardText(profile.report())
PROFILE.reset() profile.reset()
return false return false
else else
PROFILE.start() profile.start()
return true return true
end end
end end

View File

@@ -15,6 +15,8 @@ local SCN={
draw=false, --Swap draw func draw=false, --Swap draw func
}, },
stack={},--Scene stack stack={},--Scene stack
prev=false,
args={},--Arguments from previous scene
scenes=scenes, scenes=scenes,
@@ -52,14 +54,15 @@ function SCN.swapUpdate(dt)
S.time=S.time-dt S.time=S.time-dt
if S.time<S.changeTime and S.time+dt>=S.changeTime then if S.time<S.changeTime and S.time+dt>=S.changeTime then
--Scene swapped this frame --Scene swapped this frame
SCN.init(S.tar,SCN.cur) SCN.prev=SCN.cur
SCN.init(S.tar)
SCN.mainTouchID=nil SCN.mainTouchID=nil
end end
if S.time<0 then if S.time<0 then
SCN.swapping=false SCN.swapping=false
end end
end end
function SCN.init(s,org) function SCN.init(s)
love.keyboard.setTextInput(false) love.keyboard.setTextInput(false)
local S=scenes[s] local S=scenes[s]
@@ -89,7 +92,7 @@ function SCN.init(s,org)
SCN.update=S.update SCN.update=S.update
SCN.draw=S.draw SCN.draw=S.draw
if S.sceneInit then if S.sceneInit then
S.sceneInit(org) S.sceneInit()
end end
end end
function SCN.push(tar,style) function SCN.push(tar,style)
@@ -165,11 +168,12 @@ local swap={
end end
}, },
}--Scene swapping animations }--Scene swapping animations
function SCN.swapTo(tar,style)--Parallel scene swapping, cannot back function SCN.swapTo(tar,style,...)--Parallel scene swapping, cannot back
if scenes[tar]then if scenes[tar]then
if not SCN.swapping and tar~=SCN.cur then if not SCN.swapping and tar~=SCN.cur then
style=style or'fade' style=style or'fade'
SCN.swapping=true SCN.swapping=true
SCN.args={...}
local S=SCN.stat local S=SCN.stat
S.tar,S.style=tar,style S.tar,S.style=tar,style
S.time=swap[style].duration S.time=swap[style].duration
@@ -180,15 +184,15 @@ function SCN.swapTo(tar,style)--Parallel scene swapping, cannot back
MES.new('warn',"No Scene: "..tar) MES.new('warn',"No Scene: "..tar)
end end
end end
function SCN.go(tar,style)--Normal scene swapping, can back function SCN.go(tar,style,...)--Normal scene swapping, can back
if scenes[tar]then if scenes[tar]then
SCN.push() SCN.push()
SCN.swapTo(tar,style) SCN.swapTo(tar,style,...)
else else
MES.new('warn',"No Scene: "..tar) MES.new('warn',"No Scene: "..tar)
end end
end end
function SCN.back() function SCN.back(...)
if SCN.swapping then return end if SCN.swapping then return end
--Leave scene --Leave scene
@@ -199,7 +203,7 @@ function SCN.back()
--Poll&Back to previous Scene --Poll&Back to previous Scene
local m=#SCN.stack local m=#SCN.stack
if m>0 then if m>0 then
SCN.swapTo(SCN.stack[m-1],SCN.stack[m]) SCN.swapTo(SCN.stack[m-1],SCN.stack[m],...)
SCN.stack[m],SCN.stack[m-1]=nil SCN.stack[m],SCN.stack[m-1]=nil
end end
end end

View File

@@ -1,5 +1,6 @@
local type,rem=type,table.remove local type,rem=type,table.remove
local int,rnd=math.floor,math.random local int,rnd=math.floor,math.random
local interval=MATH.interval
local sfxList={} local sfxList={}
local packSetting={} local packSetting={}
@@ -42,16 +43,28 @@ function SFX.init(list)
end end
function SFX.load(path) function SFX.load(path)
local c=0 local c=0
local missing=0
for i=1,#sfxList do for i=1,#sfxList do
local fullPath=path..sfxList[i]..'.ogg' local fullPath=path..sfxList[i]..'.ogg'
if love.filesystem.getInfo(fullPath)then 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')} Sources[sfxList[i]]={love.audio.newSource(fullPath,'static')}
c=c+1 c=c+1
else else
LOG("No SFX: "..sfxList[i]..'.ogg',.1) LOG("No SFX: "..sfxList[i]..'.ogg',.1)
missing=missing+1
end end
end end
LOG(c.."/"..#sfxList.." SFX files loaded") 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 end
function SFX.loadSample(pack) function SFX.loadSample(pack)
assert(type(pack)=='table',"Usage: SFX.loadsample([table])") assert(type(pack)=='table',"Usage: SFX.loadsample([table])")
@@ -128,7 +141,7 @@ local function _play(name,vol,pos,pitch)
S=S[n]--AU_SRC S=S[n]--AU_SRC
if S:getChannelCount()==1 then if S:getChannelCount()==1 then
if pos then if pos then
pos=pos*stereo pos=interval(pos,-1,1)*stereo
S:setPosition(pos,1-pos^2,0) S:setPosition(pos,1-pos^2,0)
else else
S:setPosition(0,0,0) S:setPosition(0,0,0)

View File

@@ -2,9 +2,25 @@ local data=love.data
local STRING={} local STRING={}
local assert,tostring,tonumber=assert,tostring,tonumber local assert,tostring,tonumber=assert,tostring,tonumber
local int,format=math.floor,string.format local int,format=math.floor,string.format
local find,sub,upper=string.find,string.sub,string.upper local find,sub,gsub,upper=string.find,string.sub,string.gsub,string.upper
local char,byte=string.char,string.byte 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) do--function STRING.shiftChar(c)
local shiftMap={ local shiftMap={
['1']='!',['2']='@',['3']='#',['4']='$',['5']='%', ['1']='!',['2']='@',['3']='#',['4']='$',['5']='%',
@@ -153,6 +169,25 @@ function STRING.vcsDecrypt(text,key)
end end
return result..buffer return result..buffer
end 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) function STRING.readLine(str)
local p=str:find("\n") local p=str:find("\n")
@@ -162,6 +197,9 @@ function STRING.readLine(str)
return str,"" return str,""
end end
end end
function STRING.readChars(str,n)
return sub(str,1,n),sub(str,n+1)
end
function STRING.packBin(s) function STRING.packBin(s)
return data.encode('string','base64',data.compress('string','zlib',s)) return data.encode('string','base64',data.compress('string','zlib',s))

View File

@@ -9,11 +9,11 @@ return function(y,key1,key2)
trigDist=min(trigDist,0)-(-y)^1.2 trigDist=min(trigDist,0)-(-y)^1.2
end end
while trigDist>=1 do while trigDist>=1 do
love.keypressed(key1 or"up") love.keypressed(key1 or'up')
trigDist=trigDist-1 trigDist=trigDist-1
end end
while trigDist<=-1 do while trigDist<=-1 do
love.keypressed(key2 or"down") love.keypressed(key2 or'down')
trigDist=trigDist+1 trigDist=trigDist+1
end end
end end

View File

@@ -13,13 +13,13 @@ local kb=love.keyboard
local timer=love.timer.getTime local timer=love.timer.getTime
local next=next local next=next
local int,ceil,abs=math.floor,math.ceil,math.abs local int,ceil=math.floor,math.ceil
local max,min=math.max,math.min local max,min=math.max,math.min
local sub,ins,rem=string.sub,table.insert,table.remove local sub,ins,rem=string.sub,table.insert,table.remove
local mDraw,mDraw_X,mDraw_Y=GC.draw,GC.simpX,GC.simpY
local xOy=SCR.xOy local xOy=SCR.xOy
local FONT=FONT local FONT=FONT
local mStr=GC.mStr local mStr=GC.mStr
local approach=MATH.expApproach
local downArrowIcon=GC.DO{40,25,{'fPoly',0,0,20,25,40,0}} local downArrowIcon=GC.DO{40,25,{'fPoly',0,0,20,25,40,0}}
local upArrowIcon=GC.DO{40,25,{'fPoly',0,25,20,0,40,25}} local upArrowIcon=GC.DO{40,25,{'fPoly',0,25,20,0,40,25}}
@@ -29,7 +29,7 @@ local clearIcon=GC.DO{40,40,
{'fRect',11,14,18,21}, {'fRect',11,14,18,21},
} }
local sureIcon=GC.DO{40,40, local sureIcon=GC.DO{40,40,
{'setFT',35}, {'rawFT',35},
{'mText',"?",20,0}, {'mText',"?",20,0},
} }
local smallerThen=GC.DO{20,20, local smallerThen=GC.DO{20,20,
@@ -46,7 +46,12 @@ local function _rectangleStencil()
gc.rectangle('fill',1,1,STW-2,STH-2) gc.rectangle('fill',1,1,STW-2,STH-2)
end end
local onChange=NULL
local WIDGET={} local WIDGET={}
function WIDGET.setOnChange(func)onChange=assert(type(func)=='function'and func,"WIDGET.setOnChange(func): func must be a function")end
local widgetMetatable={ local widgetMetatable={
__tostring=function(self) __tostring=function(self)
return self:getInfo() return self:getInfo()
@@ -73,24 +78,28 @@ function text:draw()
if self.alpha>0 then if self.alpha>0 then
local c=self.color local c=self.color
gc_setColor(c[1],c[2],c[3],self.alpha) gc_setColor(c[1],c[2],c[3],self.alpha)
local w=self.obj:getWidth()
local k=min(self.lim/self.obj:getWidth(),1)
if self.align=='M'then if self.align=='M'then
mDraw_X(self.obj,self.x,self.y) gc_draw(self.obj,self.x,self.y,nil,k,1,w*.5,0)
elseif self.align=='L'then elseif self.align=='L'then
gc_draw(self.obj,self.x,self.y) gc_draw(self.obj,self.x,self.y,nil,k,1)
elseif self.align=='R'then elseif self.align=='R'then
gc_draw(self.obj,self.x-self.obj:getWidth(),self.y) gc_draw(self.obj,self.x,self.y,nil,k,1,w,0)
end end
end end
end end
function WIDGET.newText(D)--name,x,y[,fText][,color][,font=30][,align='M'][,hideF][,hide] function WIDGET.newText(D)--name,x,y[,lim][,fText][,color][,font=30][,fType][,align='M'][,hideF][,hide]
local _={ local _={
name= D.name or"_", name= D.name or"_",
x= D.x, x= D.x,
y= D.y, y= D.y,
lim= D.lim or 1e99,
fText=D.fText, fText=D.fText,
color=D.color and(COLOR[D.color]or D.color)or COLOR.Z, color=D.color and(COLOR[D.color]or D.color)or COLOR.Z,
font= D.font or 30, font= D.font or 30,
fType=D.fType,
align=D.align or'M', align=D.align or'M',
hideF=D.hideF, hideF=D.hideF,
} }
@@ -139,7 +148,7 @@ function button:reset()
end end
function button:setObject(obj) function button:setObject(obj)
if type(obj)=='string'or type(obj)=='number'then if type(obj)=='string'or type(obj)=='number'then
self.obj=gc.newText(FONT.get(self.font),obj) self.obj=gc.newText(FONT.get(self.font,self.fType),obj)
elseif obj then elseif obj then
self.obj=obj self.obj=obj
end end
@@ -148,9 +157,9 @@ function button:isAbove(x,y)
local ATV=self.ATV local ATV=self.ATV
return return
x>self.x-ATV and x>self.x-ATV and
y>self.y-ATV and y>self.y and
x<self.x+self.w+2*ATV and x<self.x+self.w+2*ATV and
y<self.y+self.h+2*ATV y<self.y+self.h
end end
function button:getCenter() function button:getCenter()
return self.x+self.w*.5,self.y+self.h*.5 return self.x+self.w*.5,self.y+self.h*.5
@@ -171,45 +180,49 @@ function button:draw()
--Button --Button
gc_setColor(.15+r*.7,.15+g*.7,.15+b*.7,.9) gc_setColor(.15+r*.7,.15+g*.7,.15+b*.7,.9)
gc_rectangle('fill',x-ATV,y-ATV,w+2*ATV,h+2*ATV,3) gc_rectangle('fill',x-ATV,y,w+2*ATV,h,4)
gc_setLineWidth(2)
gc_setColor(.3+r*.7,.3+g*.7,.3+b*.7)
gc_rectangle('line',x-ATV,y,w+2*ATV,h,5)
if ATV>0 then if ATV>0 then
gc_setLineWidth(2)
gc_setColor(.97,.97,.97,ATV*.125) gc_setColor(.97,.97,.97,ATV*.125)
gc_rectangle('line',x-ATV+2,y-ATV+2,w+2*ATV-4,h+2*ATV-4,3) gc_rectangle('line',x-ATV,y,w+2*ATV,h,3)
end end
--Drawable --Drawable
local obj=self.obj local obj=self.obj
local y0=y+h*.5-ATV*.5 local ox,oy=obj:getWidth()*.5,obj:getHeight()*.5
local y0=y+h*.5
gc_setColor(1,1,1,.2+ATV*.05) gc_setColor(1,1,1,.2+ATV*.05)
if self.align=='M'then if self.align=='M'then
local x0=x+w*.5 local x0=x+w*.5
mDraw(obj,x0-1,y0-1) local kx=obj:type()=='Text'and min(w/ox/2,1)or 1
mDraw(obj,x0-1,y0+1) gc_draw(obj,x0-1,y0-1,nil,kx,1,ox,oy)
mDraw(obj,x0+1,y0-1) gc_draw(obj,x0-1,y0+1,nil,kx,1,ox,oy)
mDraw(obj,x0+1,y0+1) gc_draw(obj,x0+1,y0-1,nil,kx,1,ox,oy)
gc_draw(obj,x0+1,y0+1,nil,kx,1,ox,oy)
gc_setColor(r*.55,g*.55,b*.55) gc_setColor(r*.55,g*.55,b*.55)
mDraw(obj,x0,y0) gc_draw(obj,x0,y0,nil,kx,1,ox,oy)
elseif self.align=='L'then elseif self.align=='L'then
local edge=self.edge local edge=self.edge
mDraw_Y(obj,x+edge-1,y0-1) gc_draw(obj,x+edge-1,y0-1-oy)
mDraw_Y(obj,x+edge-1,y0+1) gc_draw(obj,x+edge-1,y0+1-oy)
mDraw_Y(obj,x+edge+1,y0-1) gc_draw(obj,x+edge+1,y0-1-oy)
mDraw_Y(obj,x+edge+1,y0+1) gc_draw(obj,x+edge+1,y0+1-oy)
gc_setColor(r*.55,g*.55,b*.55) gc_setColor(r*.55,g*.55,b*.55)
mDraw_Y(obj,x+edge,y0) gc_draw(obj,x+edge,y0-oy)
elseif self.align=='R'then elseif self.align=='R'then
local x0=x+w-self.edge-obj:getWidth() local x0=x+w-self.edge-ox*2
mDraw_Y(obj,x0-1,y0-1) gc_draw(obj,x0-1,y0-1-oy)
mDraw_Y(obj,x0-1,y0+1) gc_draw(obj,x0-1,y0+1-oy)
mDraw_Y(obj,x0+1,y0-1) gc_draw(obj,x0+1,y0-1-oy)
mDraw_Y(obj,x0+1,y0+1) gc_draw(obj,x0+1,y0+1-oy)
gc_setColor(r*.55,g*.55,b*.55) gc_setColor(r*.55,g*.55,b*.55)
mDraw_Y(obj,x0,y0) gc_draw(obj,x0,y0-oy)
end end
end end
function button:getInfo() function button:getInfo()
return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font) return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font,self.fType)
end end
function button:press(_,_,k) function button:press(_,_,k)
self.code(k) self.code(k)
@@ -217,15 +230,15 @@ function button:press(_,_,k)
SYSFX.newRectRipple( SYSFX.newRectRipple(
6, 6,
self.x-ATV, self.x-ATV,
self.y-ATV-WIDGET.scrollPos, self.y-WIDGET.scrollPos,
self.w+2*ATV, self.w+2*ATV,
self.h+2*ATV self.h
) )
if self.sound then if self.sound then
SFX.play('button') SFX.play(self.sound)
end end
end end
function WIDGET.newButton(D)--name,x,y,w[,h][,fText][,color][,font=30][,sound=true][,align='M'][,edge=0],code[,hideF][,hide] function WIDGET.newButton(D)--name,x,y,w[,h][,fText][,color][,font=30][,fType][,sound][,align='M'][,edge=0][,code][,hideF][,hide]
if not D.h then D.h=D.w end if not D.h then D.h=D.w end
local _={ local _={
name= D.name or"_", name= D.name or"_",
@@ -246,13 +259,21 @@ function WIDGET.newButton(D)--name,x,y,w[,h][,fText][,color][,font=30][,sound=tr
fText=D.fText, fText=D.fText,
color=D.color and(COLOR[D.color]or D.color)or COLOR.Z, color=D.color and(COLOR[D.color]or D.color)or COLOR.Z,
font= D.font or 30, font= D.font or 30,
fType=D.fType,
align=D.align or'M', align=D.align or'M',
edge= D.edge or 0, edge= D.edge or 0,
sound=D.sound~=false, code= D.code or NULL,
code= D.code,
hideF=D.hideF, hideF=D.hideF,
hide= D.hide, hide= D.hide,
} }
if D.sound==false then
_.sound=false
elseif type(D.sound)=='string'then
_.sound=D.sound
else
_.sound='button'
end
for k,v in next,button do _[k]=v end for k,v in next,button do _[k]=v end
setmetatable(_,widgetMetatable) setmetatable(_,widgetMetatable)
return _ return _
@@ -268,7 +289,7 @@ function key:reset()
end end
function key:setObject(obj) function key:setObject(obj)
if type(obj)=='string'or type(obj)=='number'then if type(obj)=='string'or type(obj)=='number'then
self.obj=gc.newText(FONT.get(self.font),obj) self.obj=gc.newText(FONT.get(self.font,self.fType),obj)
elseif obj then elseif obj then
self.obj=obj self.obj=obj
end end
@@ -298,48 +319,54 @@ function key:draw()
local align=self.align local align=self.align
local r,g,b=c[1],c[2],c[3] local r,g,b=c[1],c[2],c[3]
--Frame
if not self.noFrame then
gc_setColor(.2+r*.8,.2+g*.8,.2+b*.8,.7)
gc_setLineWidth(2)
gc_rectangle('line',x,y,w,h,3)
end
--Fill --Fill
if self.fShade then if self.fShade then
gc_setColor(r,g,b,ATV*.25) gc_setColor(r,g,b,ATV*.25)
if align=='M'then if align=='M'then
mDraw(self.fShade,x+w*.5,y+h*.5) gc_draw(self.fShade,x+w*.5-self.fShade:getWidth()*.5,y+h*.5-self.fShade:getHeight()*.5)
elseif align=='L'then elseif align=='L'then
mDraw_Y(self.fShade,x+self.edge,y+h*.5) gc_draw(self.fShade,x+self.edge,y+h*.5-self.fShade:getHeight()*.5)
elseif align=='R'then elseif align=='R'then
mDraw_Y(self.fShade,x+w-self.edge-self.fShade:getWidth(),y+h*.5) gc_draw(self.fShade,x+w-self.edge-self.fShade:getWidth(),y+h*.5-self.fShade:getHeight()*.5)
end end
else else
--Background
gc_setColor(0,0,0,.3)
gc_rectangle('fill',x,y,w,h,4)
--Frame
gc_setColor(.2+r*.8,.2+g*.8,.2+b*.8,.7)
gc_setLineWidth(2)
gc_rectangle('line',x,y,w,h,3)
--Shade
gc_setColor(1,1,1,ATV*.05) gc_setColor(1,1,1,ATV*.05)
gc_rectangle('fill',x,y,w,h,3) gc_rectangle('fill',x,y,w,h,3)
end end
--Drawable --Drawable
local obj=self.obj
local ox,oy=obj:getWidth()*.5,obj:getHeight()*.5
gc_setColor(r,g,b) gc_setColor(r,g,b)
if align=='M'then if align=='M'then
mDraw(self.obj,x+w*.5,y+h*.5) local kx=obj:type()=='Text'and min(w/ox/2,1)or 1
gc_draw(obj,x+w*.5,y+h*.5,nil,kx,1,ox,oy)
elseif align=='L'then elseif align=='L'then
mDraw_Y(self.obj,x+self.edge,y+h*.5) gc_draw(obj,x+self.edge,y-oy+h*.5)
elseif align=='R'then elseif align=='R'then
mDraw_Y(self.obj,x+w-self.edge-self.obj:getWidth(),y+h*.5) gc_draw(obj,x+w-self.edge-ox*2,y-oy+h*.5)
end end
end end
function key:getInfo() function key:getInfo()
return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font) return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font,self.fType)
end end
function key:press(_,_,k) function key:press(_,_,k)
self.code(k) self.code(k)
if self.sound then if self.sound then
SFX.play('key') SFX.play(self.sound)
end end
end end
function WIDGET.newKey(D)--name,x,y,w[,h][,fText][,fShade][,noFrame][,color][,font=30][,sound=true][,align='M'][,edge=0],code[,hideF][,hide] function WIDGET.newKey(D)--name,x,y,w[,h][,fText][,fShade][,color][,font=30][,fType][,sound][,align='M'][,edge=0][,code][,hideF][,hide]
if not D.h then D.h=D.w end if not D.h then D.h=D.w end
local _={ local _={
name= D.name or"_", name= D.name or"_",
@@ -359,16 +386,22 @@ function WIDGET.newKey(D)--name,x,y,w[,h][,fText][,fShade][,noFrame][,color][,fo
fText= D.fText, fText= D.fText,
fShade= D.fShade, fShade= D.fShade,
noFrame=D.noFrame,
color= D.color and(COLOR[D.color]or D.color)or COLOR.Z, color= D.color and(COLOR[D.color]or D.color)or COLOR.Z,
font= D.font or 30, font= D.font or 30,
sound= D.sound~=false, fType= D.fType,
align= D.align or'M', align= D.align or'M',
edge= D.edge or 0, edge= D.edge or 0,
code= D.code, code= D.code or NULL,
hideF= D.hideF, hideF= D.hideF,
hide= D.hide, hide= D.hide,
} }
if D.sound==false then
_.sound=false
elseif type(D.sound)=='string'then
_.sound=D.sound
else
_.sound='key'
end
for k,v in next,key do _[k]=v end for k,v in next,key do _[k]=v end
setmetatable(_,widgetMetatable) setmetatable(_,widgetMetatable)
return _ return _
@@ -408,6 +441,10 @@ function switch:draw()
local x,y=self.x,self.y local x,y=self.x,self.y
local ATV=self.ATV local ATV=self.ATV
--Background
gc_setColor(0,0,0,.3)
gc_rectangle('fill',x,y-25,50,50,4)
--Frame --Frame
gc_setLineWidth(2) gc_setLineWidth(2)
gc_setColor(1,1,1,.6+ATV*.1) gc_setColor(1,1,1,.6+ATV*.1)
@@ -430,15 +467,15 @@ function switch:draw()
gc_draw(obj,x-12-ATV,y,nil,min(self.lim/obj:getWidth(),1),1,obj:getWidth(),obj:getHeight()*.5) gc_draw(obj,x-12-ATV,y,nil,min(self.lim/obj:getWidth(),1),1,obj:getWidth(),obj:getHeight()*.5)
end end
function switch:getInfo() function switch:getInfo()
return("x=%d,y=%d,font=%d"):format(self.x,self.y,self.font) return("x=%d,y=%d,font=%d"):format(self.x,self.y,self.font,self.fType)
end end
function switch:press() function switch:press()
self.code() self.code()
if self.sound then if self.sound then
SFX.play('touch') SFX.play(self.disp()and'check'or'uncheck')
end end
end end
function WIDGET.newSwitch(D)--name,x,y[,lim][,fText][,color][,font=30][,sound=true][,disp],code[,hideF][,hide] function WIDGET.newSwitch(D)--name,x,y[,lim][,fText][,color][,font=30][,fType][,sound=true][,disp][,code][,hideF][,hide]
local _={ local _={
name= D.name or"_", name= D.name or"_",
@@ -453,9 +490,10 @@ function WIDGET.newSwitch(D)--name,x,y[,lim][,fText][,color][,font=30][,sound=tr
fText=D.fText, fText=D.fText,
color=D.color and(COLOR[D.color]or D.color)or COLOR.Z, color=D.color and(COLOR[D.color]or D.color)or COLOR.Z,
font= D.font or 30, font= D.font or 30,
fType=D.fType,
sound=D.sound~=false, sound=D.sound~=false,
disp= D.disp, disp= D.disp,
code= D.code, code= D.code or NULL,
hideF=D.hideF, hideF=D.hideF,
hide= D.hide, hide= D.hide,
} }
@@ -491,7 +529,7 @@ function slider:isAbove(x,y)
return x>self.x-10 and x<self.x+self.w+10 and y>self.y-25 and y<self.y+25 return x>self.x-10 and x<self.x+self.w+10 and y>self.y-25 and y<self.y+25
end end
function slider:getCenter() function slider:getCenter()
return self.x+self.w*(self.pos/self.unit),self.y return self.x+self.w*((self.pos-self.rangeL)/(self.rangeR-self.rangeL)),self.y
end end
function slider:update(dt) function slider:update(dt)
local ATV=self.ATV local ATV=self.ATV
@@ -505,7 +543,7 @@ function slider:update(dt)
if ATV>0 then self.ATV=max(ATV-dt*30,0)end if ATV>0 then self.ATV=max(ATV-dt*30,0)end
end end
if not self.hide then if not self.hide then
self.pos=self.pos*.7+self.disp()*.3 self.pos=approach(self.pos,self.disp(),dt*26)
end end
end end
function slider:draw() function slider:draw()
@@ -518,8 +556,8 @@ function slider:draw()
--Units --Units
if not self.smooth then if not self.smooth then
gc_setLineWidth(2) gc_setLineWidth(2)
for p=0,self.unit do for p=self.rangeL,self.rangeR,self.unit do
local X=x+(x2-x)*p/self.unit local X=x+(x2-x)*(p-self.rangeL)/(self.rangeR-self.rangeL)
gc_line(X,y+7,X,y-7) gc_line(X,y+7,X,y-7)
end end
end end
@@ -529,7 +567,7 @@ function slider:draw()
gc_line(x,y,x2,y) gc_line(x,y,x2,y)
--Block --Block
local cx=x+(x2-x)*self.pos/self.unit local cx=x+(x2-x)*(self.pos-self.rangeL)/(self.rangeR-self.rangeL)
local bx,by,bw,bh=cx-10-ATV*.5,y-16-ATV,20+ATV,32+2*ATV local bx,by,bw,bh=cx-10-ATV*.5,y-16-ATV,20+ATV,32+2*ATV
gc_setColor(.8,.8,.8) gc_setColor(.8,.8,.8)
gc_rectangle('fill',bx,by,bw,bh,3) gc_rectangle('fill',bx,by,bw,bh,3)
@@ -564,13 +602,16 @@ end
function slider:drag(x) function slider:drag(x)
if not x then return end if not x then return end
x=x-self.x x=x-self.x
local p=self.disp() local newPos=MATH.interval(x/self.w,0,1)
local P=x<0 and 0 or x>self.w and self.unit or x/self.w*self.unit local newVal
if not self.smooth then if not self.unit then
P=int(P+.5) newVal=(1-newPos)*self.rangeL+newPos*self.rangeR
else
newVal=newPos*(self.rangeR-self.rangeL)
newVal=self.rangeL+newVal-newVal%self.unit
end end
if p~=P then if newVal~=self.disp()then
self.code(P) self.code(newVal)
end end
if self.change and timer()-self.lastTime>.5 then if self.change and timer()-self.lastTime>.5 then
self.lastTime=timer() self.lastTime=timer()
@@ -583,8 +624,8 @@ function slider:release(x)
end end
function slider:scroll(n) function slider:scroll(n)
local p=self.disp() local p=self.disp()
local u=self.smooth and .01 or 1 local u=self.unit or .01
local P=n==-1 and max(p-u,0)or min(p+u,self.unit) local P=MATH.interval(p+u*n,self.rangeL,self.rangeR)
if p==P or not P then return end if p==P or not P then return end
self.code(P) self.code(P)
if self.change and timer()-self.lastTime>.18 then if self.change and timer()-self.lastTime>.18 then
@@ -593,9 +634,15 @@ function slider:scroll(n)
end end
end end
function slider:arrowKey(k) function slider:arrowKey(k)
self:scroll((k=="left"or k=="up")and -1 or 1) self:scroll((k=='left'or k=='up')and -1 or 1)
end end
function WIDGET.newSlider(D)--name,x,y,w[,lim][,fText][,color][,unit][,smooth][,font=30][,change],disp[,show],code,hide function WIDGET.newSlider(D)--name,x,y,w[,lim][,fText][,color][,axis][,smooth][,font=30][,fType][,change],disp[,show][,code],hide
if not D.axis then
D.axis={0,1,false}
D.smooth=true
elseif not D.axis[3]then
D.smooth=true
end
local _={ local _={
name= D.name or"_", name= D.name or"_",
@@ -614,32 +661,30 @@ function WIDGET.newSlider(D)--name,x,y,w[,lim][,fText][,color][,unit][,smooth][,
fText= D.fText, fText= D.fText,
color= D.color and(COLOR[D.color]or D.color)or COLOR.Z, color= D.color and(COLOR[D.color]or D.color)or COLOR.Z,
unit= D.unit or 1, rangeL=D.axis[1],
smooth=false, rangeR=D.axis[2],
unit= D.axis[3],
smooth=D.smooth,
font= D.font or 30, font= D.font or 30,
fType= D.fType,
change=D.change, change=D.change,
disp= D.disp, disp= D.disp,
code= D.code, code= D.code or NULL,
hideF= D.hideF, hideF= D.hideF,
hide= D.hide, hide= D.hide,
show= false, show= false,
} }
if D.smooth~=nil then
_.smooth=D.smooth
else
_.smooth=_.unit<=1
end
if D.show then if D.show then
if type(D.show)=='function'then if type(D.show)=='function'then
_.show=D.show _.show=D.show
else else
_.show=sliderShowFunc[D.show] _.show=sliderShowFunc[D.show]
end end
elseif D.show~=false then elseif D.show~=false then--Use default if nil
if _.unit<=1 then if _.unit and _.unit%1==0 then
_.show=sliderShowFunc.percent
else
_.show=sliderShowFunc.int _.show=sliderShowFunc.int
else
_.show=sliderShowFunc.percent
end end
end end
for k,v in next,slider do _[k]=v end for k,v in next,slider do _[k]=v end
@@ -691,6 +736,10 @@ function selector:draw()
local w=self.w local w=self.w
local ATV=self.ATV local ATV=self.ATV
--Background
gc_setColor(0,0,0,.3)
gc_rectangle('fill',x,y,w,60,4)
--Frame --Frame
gc_setColor(1,1,1,.6+ATV*.1) gc_setColor(1,1,1,.6+ATV*.1)
gc_setLineWidth(2) gc_setLineWidth(2)
@@ -744,7 +793,7 @@ function selector:press(x)
self.select=s self.select=s
self.selText=self.list[s] self.selText=self.list[s]
if self.sound then if self.sound then
SFX.play('prerotate') SFX.play('selector')
end end
end end
end end
@@ -764,14 +813,14 @@ function selector:scroll(n)
self.select=s self.select=s
self.selText=self.list[s] self.selText=self.list[s]
if self.sound then if self.sound then
SFX.play('prerotate') SFX.play('selector')
end end
end end
function selector:arrowKey(k) function selector:arrowKey(k)
self:scroll((k=="left"or k=="up")and -1 or 1) self:scroll((k=='left'or k=='up')and -1 or 1)
end end
function WIDGET.newSelector(D)--name,x,y,w[,fText][,color][,sound=true],list,disp,code,hide function WIDGET.newSelector(D)--name,x,y,w[,fText][,color][,sound=true],list,disp[,code],hide
local _={ local _={
name= D.name or"_", name= D.name or"_",
@@ -793,7 +842,7 @@ function WIDGET.newSelector(D)--name,x,y,w[,fText][,color][,sound=true],list,dis
font= 30, font= 30,
list= D.list, list= D.list,
disp= D.disp, disp= D.disp,
code= D.code, code= D.code or NULL,
hideF=D.hideF, hideF=D.hideF,
hide= D.hide, hide= D.hide,
} }
@@ -854,18 +903,24 @@ function inputBox:draw()
local x,y,w,h=self.x,self.y,self.w,self.h local x,y,w,h=self.x,self.y,self.w,self.h
local ATV=self.ATV local ATV=self.ATV
gc_setColor(1,1,1,ATV*.08) --Background
gc_rectangle('fill',x,y,w,h,3) gc_setColor(0,0,0,.4)
gc_rectangle('fill',x,y,w,h,4)
--Highlight
gc_setColor(1,1,1,ATV*.08*(math.sin(TIME()*4.2)*.2+.8))
gc_rectangle('fill',x,y,w,h,4)
--Frame
gc_setColor(1,1,1) gc_setColor(1,1,1)
gc_setLineWidth(3) gc_setLineWidth(3)
gc_rectangle('line',x,y,w,h,3) gc_rectangle('line',x,y,w,h,3)
--Drawable --Drawable
local f=self.font local f=self.font
FONT.set(f) FONT.set(f,self.fType)
if self.obj then if self.obj then
mDraw_Y(self.obj,x-12-self.obj:getWidth(),y+h*.5) gc_draw(self.obj,x-12-self.obj:getWidth(),y+h*.5-self.obj:getHeight()*.5)
end end
if self.secret then if self.secret then
y=y+h*.5-f*.2 y=y+h*.5-f*.2
@@ -892,21 +947,21 @@ end
function inputBox:keypress(k) function inputBox:keypress(k)
local t=self.value local t=self.value
if #t>0 and EDITING==""then if #t>0 and EDITING==""then
if k=="backspace"then if k=='backspace'then
local p=#t local p=#t
while t:byte(p)>=128 and t:byte(p)<192 do while t:byte(p)>=128 and t:byte(p)<192 do
p=p-1 p=p-1
end end
t=sub(t,1,p-1) t=sub(t,1,p-1)
SFX.play('lock') SFX.play('lock')
elseif k=="delete"then elseif k=='delete'then
t="" t=""
SFX.play('hold') SFX.play('hold')
end end
self.value=t self.value=t
end end
end end
function WIDGET.newInputBox(D)--name,x,y,w[,h][,font=30][,secret][,regex][,limit],hide function WIDGET.newInputBox(D)--name,x,y,w[,h][,font=30][,fType][,secret][,regex][,limit],hide
local _={ local _={
name= D.name or"_", name= D.name or"_",
@@ -922,6 +977,7 @@ function WIDGET.newInputBox(D)--name,x,y,w[,h][,font=30][,secret][,regex][,limit
}, },
font= D.font or int(D.h/7-1)*5, font= D.font or int(D.h/7-1)*5,
fType= D.fType,
secret=D.secret==true, secret=D.secret==true,
regex= D.regex, regex= D.regex,
limit= D.limit, limit= D.limit,
@@ -999,9 +1055,9 @@ function textBox:scroll(dir)
self:drag(nil,nil,nil,-dir*self.lineH) self:drag(nil,nil,nil,-dir*self.lineH)
end end
function textBox:arrowKey(k) function textBox:arrowKey(k)
if k=="up"then if k=='up'then
self:scroll(-1) self:scroll(-1)
elseif k=="down"then elseif k=='down'then
self:scroll(-1) self:scroll(-1)
end end
end end
@@ -1013,8 +1069,8 @@ function textBox:draw()
local lineH=self.lineH local lineH=self.lineH
--Background --Background
gc_setColor(0,0,0,.4) gc_setColor(0,0,0,.3)
gc_rectangle('fill',x,y,w,h,3) gc_rectangle('fill',x,y,w,h,4)
--Frame --Frame
gc_setLineWidth(2) gc_setLineWidth(2)
@@ -1022,7 +1078,7 @@ function textBox:draw()
gc_rectangle('line',x,y,w,h,3) gc_rectangle('line',x,y,w,h,3)
--Texts --Texts
FONT.set(self.font) FONT.set(self.font,self.fType)
gc_push('transform') gc_push('transform')
gc_translate(x,y) gc_translate(x,y)
@@ -1054,7 +1110,7 @@ end
function textBox:getInfo() function textBox:getInfo()
return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h) return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h)
end end
function WIDGET.newTextBox(D)--name,x,y,w,h[,font=30][,lineH][,fix],hide function WIDGET.newTextBox(D)--name,x,y,w,h[,font=30][,fType][,lineH][,fix],hide
local _={ local _={
name= D.name or"_", name= D.name or"_",
@@ -1076,6 +1132,7 @@ function WIDGET.newTextBox(D)--name,x,y,w,h[,font=30][,lineH][,fix],hide
h= D.h, h= D.h,
font= D.font or 30, font= D.font or 30,
fType=D.fType,
fix= D.fix, fix= D.fix,
texts={}, texts={},
hideF=D.hideF, hideF=D.hideF,
@@ -1153,7 +1210,7 @@ function listBox:press(x,y)
if self.list[y]then if self.list[y]then
if self.selected~=y then if self.selected~=y then
self.selected=y self.selected=y
SFX.play('click',.4) SFX.play('selector',.8,0,12)
end end
end end
end end
@@ -1176,6 +1233,14 @@ function listBox:arrowKey(dir)
end end
end end
end end
function listBox:select(i)
self.selected=i
if self.selected<int(self.scrollPos/self.lineH)+2 then
self:drag(nil,nil,nil,1e99)
elseif self.selected>int(self.scrollPos/self.lineH)+self.capacity-1 then
self:drag(nil,nil,nil,-1e99)
end
end
function listBox:draw() function listBox:draw()
local x,y,w,h=self.x,self.y,self.w,self.h local x,y,w,h=self.x,self.y,self.w,self.h
local list=self.list local list=self.list
@@ -1186,6 +1251,10 @@ function listBox:draw()
gc_push('transform') gc_push('transform')
gc_translate(x,y) gc_translate(x,y)
--Background
gc_setColor(0,0,0,.4)
gc_rectangle('fill',0,0,w,h,4)
--Frame --Frame
gc_setColor(WIDGET.sel==self and COLOR.lN or COLOR.Z) gc_setColor(WIDGET.sel==self and COLOR.lN or COLOR.Z)
gc_setLineWidth(2) gc_setLineWidth(2)
@@ -1214,7 +1283,7 @@ end
function listBox:getInfo() function listBox:getInfo()
return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h) return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h)
end end
function WIDGET.newListBox(D)--name,x,y,w,h,lineH[,hideF][,hide][,drawF] function WIDGET.newListBox(D)--name,x,y,w,h,lineH,drawF[,hideF][,hide]
local _={ local _={
name= D.name or"_", name= D.name or"_",
@@ -1271,16 +1340,7 @@ function WIDGET.setWidgetList(list)
for i=1,#list do for i=1,#list do
list[i]:reset() list[i]:reset()
end end
if SCN.cur~='custom_field'then onChange()
local colorList=THEME.getThemeColor()
if not colorList then return end
local rnd=math.random
for _,W in next,list do
if W.color and not W.fText then
W.color=colorList[rnd(#colorList)]
end
end
end
end end
end end
function WIDGET.setScrollHeight(height) function WIDGET.setScrollHeight(height)
@@ -1373,59 +1433,6 @@ function WIDGET.release(x,y)
W:release(x,y+WIDGET.scrollPos) W:release(x,y+WIDGET.scrollPos)
end end
end end
function WIDGET.keyPressed(k,isRep)
local W=WIDGET.sel
if k=="space"or k=="return"then
if not isRep then
WIDGET.press()
end
elseif k=="up"or k=="down"or k=="left"or k=="right"then
if kb.isDown("lshift","lalt","lctrl")then
--Control some widgets with arrowkeys when hold shift/ctrl/alt
if W and W.arrowKey then W:arrowKey(k)end
else
if not W then
for _,w in next,WIDGET.active do
if not w.hide and w.isAbove then
WIDGET.focus(w)
return
end
end
elseif W.getCenter then
local WX,WY=W:getCenter()
local dir=(k=="right"or k=="down")and 1 or -1
local tar
local minDist=1e99
local swap_xy=k=="up"or k=="down"
if swap_xy then WX,WY=WY,WX end--note that we do not swap them back later
for _,W1 in ipairs(WIDGET.active)do
if W~=W1 and W1.resCtr and not W1.hide then
local L=W1.resCtr
for j=1,#L,2 do
local x,y=L[j],L[j+1]
if swap_xy then x,y=y,x end--note that we do not swap them back later
local dist=(x-WX)*dir
if dist>10 then
dist=dist+abs(y-WY)*6.26
if dist<minDist then
minDist=dist
tar=W1
end
end
end
end
end
if tar then
WIDGET.focus(tar)
end
end
end
else
if W and W.keypress then
W:keypress(k)
end
end
end
function WIDGET.textinput(texts) function WIDGET.textinput(texts)
local W=WIDGET.sel local W=WIDGET.sel
if W and W.type=='inputBox'then if W and W.type=='inputBox'then
@@ -1433,41 +1440,10 @@ function WIDGET.textinput(texts)
WIDGET.sel.value=WIDGET.sel.value..texts WIDGET.sel.value=WIDGET.sel.value..texts
SFX.play('touch') SFX.play('touch')
else else
SFX.play('finesseError',.3) SFX.play('drop_cancel')
end end
end end
end end
local keyMirror={
dpup="up",
dpdown="down",
dpleft="left",
dpright="right",
start="return",
back="escape",
}
function WIDGET.gamepadPressed(i)
if i=="start"then
WIDGET.press()
elseif i=="a"or i=="b"then
local W=WIDGET.sel
if W then
if W.type=='button'or W.type=='key'then
WIDGET.press()
elseif W.type=='slider'then
local p=W.disp()
local P=i=="left"and(p>0 and p-1)or p<W.unit and p+1
if p==P or not P then return end
W.code(P)
if W.change and timer()-W.lastTime>.18 then
W.lastTime=timer()
W.change()
end
end
end
elseif i=="dpup"or i=="dpdown"or i=="dpleft"or i=="dpright"then
WIDGET.keyPressed(keyMirror[i])
end
end
function WIDGET.update(dt) function WIDGET.update(dt)
for _,W in next,WIDGET.active do for _,W in next,WIDGET.active do

148
main.lua
View File

@@ -23,14 +23,14 @@ local fs=love.filesystem
VERSION=require"version" VERSION=require"version"
TIME=love.timer.getTime TIME=love.timer.getTime
YIELD=coroutine.yield YIELD=coroutine.yield
SYSTEM=love.system.getOS() SYSTEM=love.system.getOS()if SYSTEM=='OS X'then SYSTEM='macOS'end
FNSF=SYSTEM:find'\79\83'--What does FNSF stand for? IDK so don't ask me lol FNNS=SYSTEM:find'\79\83'--What does FNSF stand for? IDK so don't ask me lol
MOBILE=SYSTEM=='Android'or SYSTEM=='iOS' MOBILE=SYSTEM=='Android'or SYSTEM=='iOS'
SAVEDIR=fs.getSaveDirectory() SAVEDIR=fs.getSaveDirectory()
--Global Vars & Settings --Global Vars & Settings
SFXPACKS={'chiptune'} SFXPACKS={'chiptune'}
VOCPACKS={'miya','mono','xiaoya','miku'} VOCPACKS={'miya',--[['mono',]]'xiaoya','miku'}
FIRSTLAUNCH=false FIRSTLAUNCH=false
DAILYLAUNCH=false DAILYLAUNCH=false
@@ -50,11 +50,30 @@ local _LOADTIME_=TIME()
--Load modules --Load modules
Z=require'Zframework' Z=require'Zframework'
FONT.load('parts/fonts/proportional.ttf') FONT.load{
norm='parts/fonts/proportional.ttf',
mono='parts/fonts/monospaced.ttf',
}
FONT.setDefault('norm')
FONT.setFallback('norm')
SCR.setSize(1280,720)--Initialize Screen size SCR.setSize(1280,720)--Initialize Screen size
BGM.setMaxSources(5) BGM.setMaxSources(5)
BGM.setChange(function(name)MES.new('music',text.nowPlaying..name,5)end) BGM.setChange(function(name)MES.new('music',text.nowPlaying..name,5)end)
VOC.setDiversion(1) VOC.setDiversion(.62)
WIDGET.setOnChange(function()
if SCN.cur~='custom_field'then
local colorList=THEME.getThemeColor()
if not colorList then return end
local rnd=math.random
for _,W in next,SCN.scenes[SCN.cur].widgetList do
if W.color then
W.color=colorList[rnd(#colorList)]
end
end
end
end)
table.insert(_LOADTIMELIST_,("Load Zframework: %.3fs"):format(TIME()-_LOADTIME_)) table.insert(_LOADTIMELIST_,("Load Zframework: %.3fs"):format(TIME()-_LOADTIME_))
@@ -65,6 +84,9 @@ mStr=GC.mStr
mText=GC.simpX mText=GC.simpX
mDraw=GC.draw mDraw=GC.draw
Snd=SFX.playSample Snd=SFX.playSample
string.repD=STRING.repD
string.sArg=STRING.sArg
string.split=STRING.split
--Delete all naked files (from too old version) --Delete all naked files (from too old version)
FILE.clear('') FILE.clear('')
@@ -93,6 +115,7 @@ for _,v in next,fs.getDirectoryItems('parts/shaders')do
end end
end end
THEME= require'parts.theme'
LINE= require'parts.line' LINE= require'parts.line'
DATA= require'parts.data' DATA= require'parts.data'
@@ -105,20 +128,17 @@ BOT= require'parts.bot'
RSlist= require'parts.RSlist'DSCP=RSlist.TRS.centerPos RSlist= require'parts.RSlist'DSCP=RSlist.TRS.centerPos
PLY= require'parts.player' PLY= require'parts.player'
NETPLY= require'parts.netPlayer' NETPLY= require'parts.netPlayer'
MODES= require'parts.modes' MODETREE= require'parts.modeTree'
setmetatable(TEXTURE,{__index=function(self,k) setmetatable(TEXTURE,{__index=function(self,k)
MES.new('warn',"No texture called: "..k) MES.new('warn',"No texture called: "..k)
self[k]=love.graphics.newCanvas(1,1) self[k]=PAPER
return self[k] return self[k]
end}) end})
table.insert(_LOADTIMELIST_,("Load Parts: %.3fs"):format(TIME()-_LOADTIME_)) table.insert(_LOADTIMELIST_,("Load Parts: %.3fs"):format(TIME()-_LOADTIME_))
--Init Zframework --Init Zframework
Z.setIfPowerInfo(function()
return SETTING.powerInfo and LOADED
end)
do--Z.setCursor do--Z.setCursor
local normImg=GC.DO{16,16, local normImg=GC.DO{16,16,
{'fCirc',8,8,4}, {'fCirc',8,8,4},
@@ -164,6 +184,9 @@ Z.setOnFnKeys({
function()for k,v in next,_G do print(k,v)end end, function()for k,v in next,_G do print(k,v)end end,
function()if love['_openConsole']then love['_openConsole']()end end, function()if love['_openConsole']then love['_openConsole']()end end,
}) })
Z.setOnResize(function(w,_)
SHADER.warning:send('w',w*SCR.dpi)
end)
do--Z.setOnFocus do--Z.setOnFocus
local function task_autoSoundOff() local function task_autoSoundOff()
while true do while true do
@@ -205,15 +228,15 @@ end
Z.setOnQuit(destroyPlayers) Z.setOnQuit(destroyPlayers)
--Load settings and statistics --Load settings and statistics
TABLE.cover (FILE.load('conf/user')or{},USER) TABLE.cover (loadFile('conf/user','-canSkip')or{},USER)
TABLE.cover (FILE.load('conf/unlock')or{},RANKS) TABLE.cover (loadFile('conf/unlock','-canSkip')or{},RANKS)
TABLE.update(FILE.load('conf/settings')or{},SETTING) TABLE.update(loadFile('conf/settings','-canSkip')or{},SETTING)
TABLE.coverR(FILE.load('conf/data')or{},STAT) TABLE.coverR(loadFile('conf/data','-canSkip')or{},STAT)
TABLE.cover (FILE.load('conf/key')or{},KEY_MAP) TABLE.cover (loadFile('conf/key','-canSkip')or{},KEY_MAP)
TABLE.cover (FILE.load('conf/virtualkey')or{},VK_ORG) TABLE.cover (loadFile('conf/virtualkey','-json -canSkip')or{},VK_ORG)
--Initialize fields, sequence, missions, gameEnv for cutsom game --Initialize fields, sequence, missions, gameEnv for cutsom game
local fieldData=FILE.load('conf/customBoards','string') local fieldData=loadFile('conf/customBoards','-string -canSkip')
if fieldData then if fieldData then
fieldData=STRING.split(fieldData,"!") fieldData=STRING.split(fieldData,"!")
for i=1,#fieldData do for i=1,#fieldData do
@@ -222,15 +245,15 @@ if fieldData then
else else
FIELD[1]=DATA.newBoard() FIELD[1]=DATA.newBoard()
end end
local sequenceData=FILE.load('conf/customSequence','string') local sequenceData=loadFile('conf/customSequence','-string -canSkip')
if sequenceData then if sequenceData then
DATA.pasteSequence(sequenceData) DATA.pasteSequence(sequenceData)
end end
local missionData=FILE.load('conf/customMissions','string') local missionData=loadFile('conf/customMissions','-string -canSkip')
if missionData then if missionData then
DATA.pasteMission(missionData) DATA.pasteMission(missionData)
end end
local customData=FILE.load('conf/customEnv') local customData=loadFile('conf/customEnv','-canSkip')
if customData and customData['version']==VERSION.code then if customData and customData['version']==VERSION.code then
TABLE.complete(customData,CUSTOMENV) TABLE.complete(customData,CUSTOMENV)
end end
@@ -248,14 +271,17 @@ IMG.init{
speedLimit='media/image/mess/speedLimit.png',--Not used, for future C2-mode speedLimit='media/image/mess/speedLimit.png',--Not used, for future C2-mode
pay1='media/image/mess/pay1.png', pay1='media/image/mess/pay1.png',
pay2='media/image/mess/pay2.png', pay2='media/image/mess/pay2.png',
drought='media/image/mess/drought.png',
miyaCH='media/image/characters/miya.png', miyaCH1='media/image/characters/miya1.png',
miyaF1='media/image/characters/miya_f1.png', miyaCH2='media/image/characters/miya2.png',
miyaF2='media/image/characters/miya_f2.png', miyaCH3='media/image/characters/miya3.png',
miyaF3='media/image/characters/miya_f3.png', miyaCH4='media/image/characters/miya4.png',
miyaF4='media/image/characters/miya_f4.png', miyaHeart='media/image/characters/miya_heart.png',
miyaGlow='media/image/characters/miya_glow.png',
monoCH='media/image/characters/mono.png', monoCH='media/image/characters/mono.png',
xiaoyaCH='media/image/characters/xiaoya.png', xiaoyaCH='media/image/characters/xiaoya.png',
xiaoyaOmino='media/image/characters/xiaoya_Omino.png',
mikuCH='media/image/characters/miku.png', mikuCH='media/image/characters/miku.png',
electric='media/image/characters/electric.png', electric='media/image/characters/electric.png',
hbm='media/image/characters/hbm.png', hbm='media/image/characters/hbm.png',
@@ -272,7 +298,7 @@ IMG.init{
SKIN.load{ SKIN.load{
{name="crystal_scf",path='media/image/skin/crystal_scf.png'}, {name="crystal_scf",path='media/image/skin/crystal_scf.png'},
{name="matte_mrz",path='media/image/skin/matte_mrz.png'}, {name="matte_mrz",path='media/image/skin/matte_mrz.png'},
{name="shiny_cho",path='media/image/skin/shiny_cho.png'}, {name="shiny_chno",path='media/image/skin/shiny_chno.png'},
{name="contrast_mrz",path='media/image/skin/contrast_mrz.png'}, {name="contrast_mrz",path='media/image/skin/contrast_mrz.png'},
{name="polkadots_scf",path='media/image/skin/polkadots_scf.png'}, {name="polkadots_scf",path='media/image/skin/polkadots_scf.png'},
{name="toy_scf",path='media/image/skin/toy_scf.png'}, {name="toy_scf",path='media/image/skin/toy_scf.png'},
@@ -295,6 +321,7 @@ SKIN.load{
{name="classic",path='media/image/skin/classic_unknown.png'}, {name="classic",path='media/image/skin/classic_unknown.png'},
{name="ball_shaw",path='media/image/skin/ball_shaw.png'}, {name="ball_shaw",path='media/image/skin/ball_shaw.png'},
{name="retro_notypey",path='media/image/skin/retro_notypey.png'}, {name="retro_notypey",path='media/image/skin/retro_notypey.png'},
{name="pixel_chno",path='media/image/skin/pixel_chno.png'},
{name="textbone_mrz",path='media/image/skin/textbone_mrz.png'}, {name="textbone_mrz",path='media/image/skin/textbone_mrz.png'},
{name="coloredbone_mrz",path='media/image/skin/coloredbone_mrz.png'}, {name="coloredbone_mrz",path='media/image/skin/coloredbone_mrz.png'},
{name="wtf",path='media/image/skin/wtf_mrz.png'}, {name="wtf",path='media/image/skin/wtf_mrz.png'},
@@ -310,11 +337,11 @@ SFX.init((function()--[Warning] Not loading files here, just get the list of sou
end end
return L return L
end)()) end)())
BGM.init((function() BGM.load((function()
local L={} local L={}
for _,v in next,fs.getDirectoryItems('media/music')do for _,v in next,fs.getDirectoryItems('media/music')do
if isSafeFile('media/music/'..v,"Dangerous file : %SAVE%/media/music/"..v)then if isSafeFile('media/music/'..v,"Dangerous file : %SAVE%/media/music/"..v)then
table.insert(L,{name=v:sub(1,-5),path='media/music/'..v}) L[v:sub(1,-5)]='media/music/'..v
end end
end end
return L return L
@@ -333,12 +360,13 @@ VOC.init{
LANG.init('zh', LANG.init('zh',
{ {
zh=require'parts.language.lang_zh', zh=require'parts.language.lang_zh',
zh_full=require'parts.language.lang_zh_full',
zh_trad=require'parts.language.lang_zh_trad', zh_trad=require'parts.language.lang_zh_trad',
zh_full=require'parts.language.lang_zh_full',
en=require'parts.language.lang_en', en=require'parts.language.lang_en',
fr=require'parts.language.lang_fr', fr=require'parts.language.lang_fr',
es=require'parts.language.lang_es', es=require'parts.language.lang_es',
pt=require'parts.language.lang_pt', pt=require'parts.language.lang_pt',
id=require'parts.language.lang_id',
zh_grass=require'parts.language.lang_zh_grass', zh_grass=require'parts.language.lang_zh_grass',
zh_yygq=require'parts.language.lang_yygq', zh_yygq=require'parts.language.lang_yygq',
symbol=require'parts.language.lang_symbol', symbol=require'parts.language.lang_symbol',
@@ -373,6 +401,7 @@ for _,v in next,fs.getDirectoryItems('parts/backgrounds')do
BG.add(name,require('parts.backgrounds.'..name)) BG.add(name,require('parts.backgrounds.'..name))
end end
end end
BG.remList('none')BG.remList('gray')BG.remList('custom')
--Load scene files from SOURCE ONLY --Load scene files from SOURCE ONLY
for _,v in next,fs.getDirectoryItems('parts/scenes')do for _,v in next,fs.getDirectoryItems('parts/scenes')do
if isSafeFile('parts/scenes/'..v)then if isSafeFile('parts/scenes/'..v)then
@@ -381,24 +410,6 @@ for _,v in next,fs.getDirectoryItems('parts/scenes')do
LANG.addScene(sceneName) LANG.addScene(sceneName)
end end
end end
--Load mode files
for i=1,#MODES do
local m=MODES[i]--Mode template
if isSafeFile('parts/modes/'..m.name)then
TABLE.complete(require('parts.modes.'..m.name),MODES[i])
MODES[m.name],MODES[i]=MODES[i]
end
end
for _,v in next,fs.getDirectoryItems('parts/modes')do
if isSafeFile('parts/modes/'..v)and not MODES[v:sub(1,-5)]then
local M={name=v:sub(1,-5)}
local modeData=require('parts.modes.'..M.name)
if modeData.env then
TABLE.complete(modeData,M)
MODES[M.name]=M
end
end
end
table.insert(_LOADTIMELIST_,("Load Files: %.3fs"):format(TIME()-_LOADTIME_)) table.insert(_LOADTIMELIST_,("Load Files: %.3fs"):format(TIME()-_LOADTIME_))
@@ -407,7 +418,6 @@ do
local needSave local needSave
if not fs.getInfo('conf/data')then if not fs.getInfo('conf/data')then
FIRSTLAUNCH=true
needSave=true needSave=true
end end
if type(STAT.version)~='number'then if type(STAT.version)~='number'then
@@ -439,7 +449,6 @@ do
if RANKS.tsd_u then if RANKS.tsd_u then
RANKS.tsd_u=0 RANKS.tsd_u=0
end end
needSave=true
end end
if STAT.version==1601 then if STAT.version==1601 then
RANKS.round_e=nil RANKS.round_e=nil
@@ -453,6 +462,13 @@ do
fs.remove('record/round_l.rec') fs.remove('record/round_l.rec')
fs.remove('record/round_u.rec') fs.remove('record/round_u.rec')
end end
if STAT.version<1700 and SETTING.dascut<5 then
SETTING.dascut=SETTING.dascut+1
needSave=true
end
if SETTING.vocPack=='mono'then
SETTING.vocPack='miya'
end
if RANKS.stack_e then if RANKS.stack_e then
RANKS.stack_e=nil RANKS.stack_e=nil
RANKS.stack_h=nil RANKS.stack_h=nil
@@ -477,6 +493,10 @@ do
fs.remove('record/rhythm_h.rec') fs.remove('record/rhythm_h.rec')
fs.remove('record/rhythm_u.rec') fs.remove('record/rhythm_u.rec')
end end
if RANKS.bigbang then
RANKS.clearRush,RANKS.bigbang=RANKS.bigbang
fs.remove('record/bigbang.rec')
end
if STAT.version~=VERSION.code then if STAT.version~=VERSION.code then
for k,v in next,MODE_UPDATE_MAP do for k,v in next,MODE_UPDATE_MAP do
if RANKS[k]then if RANKS[k]then
@@ -504,6 +524,9 @@ do
if type(SETTING.skinSet)=='number'then SETTING.skinSet='crystal_scf'end if type(SETTING.skinSet)=='number'then SETTING.skinSet='crystal_scf'end
if not TABLE.find({8,10,13,17,22,29,37,47,62,80,100},SETTING.frameMul)then SETTING.frameMul=100 end if not TABLE.find({8,10,13,17,22,29,37,47,62,80,100},SETTING.frameMul)then SETTING.frameMul=100 end
if SETTING.cv then SETTING.vocPack,SETTING.cv=SETTING.cv end if SETTING.cv then SETTING.vocPack,SETTING.cv=SETTING.cv end
if type(SETTING.bg)~='string'then SETTING.bg='on'end
if SETTING.skin[18]==10 then SETTING.skin[18]=4 end
if SETTING.reTime>3 or SETTING.reTime<.5 then SETTING.reTime=2 end
if RANKS.infinite then RANKS.infinite=0 end if RANKS.infinite then RANKS.infinite=0 end
if RANKS.infinite_dig then RANKS.infinite_dig=0 end if RANKS.infinite_dig then RANKS.infinite_dig=0 end
if not RANKS.sprint_10l then RANKS.sprint_10l=0 end if not RANKS.sprint_10l then RANKS.sprint_10l=0 end
@@ -514,26 +537,8 @@ do
if type(name)=='number'or type(rank)~='number'then if type(name)=='number'or type(rank)~='number'then
RANKS[name]=nil RANKS[name]=nil
needSave=true needSave=true
else
local M=MODES[name]
if M and M.unlock and rank>0 then
for _,unlockName in next,M.unlock do
if not RANKS[unlockName]then
RANKS[unlockName]=0
needSave=true
end
end
end
if not(M and M.x)then
RANKS[name]=nil
needSave=true
end
end end
end end
if not MODES[STAT.lastPlay]then
STAT.lastPlay='sprint_10l'
needSave=true
end
if needSave then if needSave then
saveStats() saveStats()
@@ -543,7 +548,8 @@ do
end end
end end
--First start for phones --First start
FIRSTLAUNCH=STAT.run==0
if FIRSTLAUNCH and MOBILE then if FIRSTLAUNCH and MOBILE then
SETTING.VKSwitch=true SETTING.VKSwitch=true
SETTING.powerInfo=true SETTING.powerInfo=true
@@ -551,7 +557,7 @@ if FIRSTLAUNCH and MOBILE then
end end
--Apply system setting --Apply system setting
applyAllSettings() applySettings()
--Load replays --Load replays
for _,fileName in next,fs.getDirectoryItems('replay')do for _,fileName in next,fs.getDirectoryItems('replay')do
@@ -629,9 +635,9 @@ if TABLE.find(arg,'--test')then
TASK.new(function() TASK.new(function()
while true do while true do
YIELD() YIELD()
if Z.errData[1]then break end if Z.getErr(1)then break end
end end
LOG("\27[91m\27[1mAutomatic Test Failed :(\27[0m\nThe error message is:\n"..table.concat(Z.errData[1].mes,"\n").."\27[91m\nAborting\27[0m") LOG("\27[91m\27[1mAutomatic Test Failed :(\27[0m\nThe error message is:\n"..table.concat(Z.getErr(1).mes,"\n").."\27[91m\nAborting\27[0m")
TEST.yieldN(60) TEST.yieldN(60)
love.event.quit(1) love.event.quit(1)
end) end)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 41 KiB

BIN
media/music/lounge.ogg Normal file

Binary file not shown.

BIN
media/music/malate.ogg Normal file

Binary file not shown.

View File

@@ -97,12 +97,12 @@ do
{131,2,2, 0, 0,0},{131,2,2,-1,-1,0},{131,2,2,-1, 0,0},--S {131,2,2, 0, 0,0},{131,2,2,-1,-1,0},{131,2,2,-1, 0,0},--S
{131,1,2,-1, 0,0},{131,1,2, 0,-1,0},{131,1,2, 0, 0,0},--Z(misOrder) {131,1,2,-1, 0,0},{131,1,2, 0,-1,0},{131,1,2, 0, 0,0},--Z(misOrder)
{313,2,2, 0, 0,0},{313,2,2,-1,-1,0},{313,2,2,-1, 0,0},--S(misOrder) {313,2,2, 0, 0,0},{313,2,2,-1,-1,0},{313,2,2,-1, 0,0},--S(misOrder)
{331,3,2, 0,-1,0},--J(farDown) {331,3,2, 0,-1,1},--J(farDown)
{113,4,2,-1,-1,0},--L(farDown) {113,4,2,-1,-1,1},--L(farDown)
{113,3,2,-1,-1,0},{113,3,0, 0, 0,0},--J {113,3,2,-1,-1,0},{113,3,0, 0, 0,0},--J
{331,4,2, 0,-1,0},{331,4,0,-1, 0,0},--L {331,4,2, 0,-1,0},{331,4,0,-1, 0,0},--L
{222,7,2,-1, 0,2},{222,7,2,-2, 0,2},{222,7,2, 0, 0,2},--I
{222,7,0,-1, 1,1},{222,7,0,-2, 1,1},{222,7,0, 0, 1,1},--I(high) {222,7,0,-1, 1,1},{222,7,0,-2, 1,1},{222,7,0, 0, 1,1},--I(high)
{222,7,2,-1, 0,2},{222,7,2,-2, 0,2},{222,7,2, 0, 0,2},--I(low)
{121,6,0, 1,-1,2},{112,6,0, 2,-1,2},{122,6,0, 1,-2,2},--O {121,6,0, 1,-1,2},{112,6,0, 2,-1,2},{122,6,0, 1,-2,2},--O
{323,6,0,-1,-1,2},{332,6,0,-2,-1,2},{322,6,0,-1,-2,2},--O {323,6,0,-1,-1,2},{332,6,0,-2,-1,2},{322,6,0,-1,-2,2},--O
}--{keys, ID, dir, dx, dy, freeLevel (0=immovable, 1=U/D-immovable, 2=free)} }--{keys, ID, dir, dx, dy, freeLevel (0=immovable, 1=U/D-immovable, 2=free)}
@@ -204,6 +204,7 @@ do
P.spinLast=2 P.spinLast=2
P.stat.rotate=P.stat.rotate+1 P.stat.rotate=P.stat.rotate+1
P:freshBlock('move') P:freshBlock('move')
C.spinSeq=nil
return return
end end
end end
@@ -304,8 +305,8 @@ do
[10]={'+0+0','+0+1','+1+0','+0-2','+1-2'}, [10]={'+0+0','+0+1','+1+0','+0-2','+1-2'},
[03]={'+0+0','+0-1','+0+1','+0+2'}, [03]={'+0+0','+0-1','+0+1','+0+2'},
[30]={'+0+0','+0-1','+0+1','+0-2'}, [30]={'+0+0','+0-1','+0+1','+0-2'},
[12]={'+0+0','+0-1','+0+1'}, [12]={'+0+0','+0-1','+0+1','+0+2'},
[21]={'+0+0','+0-1','+0-2'}, [21]={'+0+0','+0-1','+0-2','+0-2'},
[32]={'+0+0','+1+0','-1+0'}, [32]={'+0+0','+1+0','-1+0'},
[23]={'+0+0','-1+0','+1+0'}, [23]={'+0+0','-1+0','+1+0'},
[02]={'+0+0','-1+1','+1-1'}, [02]={'+0+0','-1+1','+1-1'},
@@ -375,8 +376,8 @@ do
},--R },--R
false,--Y false,--Y
{ {
[01]={'+0+0','-1+0','-1+1','+0+1','+1+0','-1+2','-2+0','+0-2'}, [01]={'+0+0','-1+0','-1+1','+0+1','+1+0','+1+1','-1+2','-2+0','+0-2'},
[10]={'+0+0','+1+0','-1+0','+0-1','+1-1','+1-2','+2+0','+0+2'}, [10]={'+0+0','+1+0','-1+0','+0-1','-1-1','+1-1','+1-2','+2+0','+0+2'},
[03]={'+0+0','-1+0','+1-1','+0-2','+0-3','+1+0','+1-2','+1-3','+0+1','-1+1'}, [03]={'+0+0','-1+0','+1-1','+0-2','+0-3','+1+0','+1-2','+1-3','+0+1','-1+1'},
[30]={'+0+0','-1+0','+1-1','+1-2','+1+0','+0-2','+1-3','-1+2','+0+3','-1+3'}, [30]={'+0+0','-1+0','+1-1','+1-2','+1+0','+0-2','+1-3','-1+2','+0+3','-1+3'},
[12]={'+0+0','-1+0','+1-1','-1-1','+1-2','+1+0','+0-2','+1-3','-1+2','+0+3','-1+3'}, [12]={'+0+0','-1+0','+1-1','-1-1','+1-2','+1+0','+0-2','+1-3','-1+2','+0+3','-1+3'},
@@ -761,6 +762,68 @@ do
ARS_Z.kickTable[25]=upSet ARS_Z.kickTable[25]=upSet
end end
local DRS_weak
do
local centerPos=TABLE.copy(defaultCenterPos)
centerPos[1]={[0]={1,1},{1,0},{1,1},{1,1}}--Z
centerPos[2]={[0]={1,1},{1,0},{1,1},{1,1}}--S
centerPos[3]={[0]={1,1},{1,0},{1,1},{1,1}}--L
centerPos[4]={[0]={1,1},{1,0},{1,1},{1,1}}--J
centerPos[5]={[0]={1,1},{1,0},{1,1},{1,1}}--T
centerPos[7]={[0]={.5,1.5},{1.5,-.5},{.5,1.5},{1.5,.5}}--I
centerPos[10]={[0]={1,1},{1,0},{1,1},{1,0}}--P
centerPos[11]={[0]={1,1},{1,1},{1,1},{1,1}}--Q
centerPos[15]={[0]={1,1},{1,0},{1,1},{1,1}}--U
centerPos[16]={[0]={1,1},{1,1},{1,1},{1,1}}--V
centerPos[19]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--J5
centerPos[20]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--L5
centerPos[21]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--R
centerPos[22]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--Y
centerPos[23]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--N
centerPos[24]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--H
centerPos[26]={[0]={0,1},{0,0},{0,1},{0,0}}--I3
centerPos[28]={[0]={0,1},{0,0},{0,1},{0,0}}--I2
local L={'+0+0','-1+0','+1+0','+0-1','-1-1','+1-1'}
local R={'+0+0','+1+0','-1+0','+0-1','+1-1','-1-1'}
local Z={
[01]=R,[10]=L,[03]=L,[30]=R,
[12]=R,[21]=L,[32]=L,[23]=R,
[02]=R,[20]=L,[13]=L,[31]=R,
}
local S=_reflect(Z)
DRS_weak={
centerTex=GC.DO{10,10,
{'setLW',2},
{'dRect',1,1,8,8},
{'fRect',3,3,4,4},
},
centerPos=centerPos,
kickTable={
Z,S,--Z,S
Z,S,--J,L
Z,--T
noKickSet,--O
Z,--I
Z,S,--Z5,S5
Z,S,--P,Q
Z,S,--F,E
Z,Z,Z,Z,--T5,U,V,W
noKickSet,--X
Z,S,--J5,L5
Z,S,--R,Y
Z,S,--N,H
Z,--I5
Z,Z,--I3,C
Z,Z,--I2,O1
}
}
end
local ASC local ASC
do do
local L={'+0+0','+1+0','+0-1','+1-1','+0-2','+1-2','+2+0','+2-1','+2-2','-1+0','-1-1','+0+1','+1+1','+2+1','-1-2','-2+0','+0+2','+1+2','+2+2','-2-1','-2-2'} local L={'+0+0','+1+0','+0-1','+1-1','+0-2','+1-2','+2+0','+2-1','+2-2','-1+0','-1-1','+0+1','+1+1','+2+1','-1-2','-2+0','+0+2','+1+2','+2+2','-2-1','-2-2'}
@@ -934,6 +997,7 @@ local RSlist={
SRS_X=SRS_X, SRS_X=SRS_X,
BiRS=BiRS, BiRS=BiRS,
ARS_Z=ARS_Z, ARS_Z=ARS_Z,
DRS_weak=DRS_weak,
ASC=ASC, ASC=ASC,
ASC_plus=ASC_plus, ASC_plus=ASC_plus,
C2=C2, C2=C2,

View File

@@ -0,0 +1,31 @@
--Secret custom background
local gc_clear,gc_setColor=love.graphics.clear,love.graphics.setColor
local back={}
local image=false
local alpha=.26
local mx,my,k
function back.init()
back.resize()
end
function back.resize()
mx,my=SCR.w*.5,SCR.h*.5
if image then
k=math.max(SCR.w/image:getWidth(),SCR.h/image:getHeight())
end
end
function back.draw()
gc_clear(.1,.1,.1)
if image then
gc_setColor(1,1,1,alpha)
mDraw(image,mx,my,nil,k)
end
end
function back.event(a,img)
if a then alpha=a end
if img then image=img end
back.resize()
end
return back

View File

@@ -0,0 +1,11 @@
--Customizable grey background
local gc=love.graphics
local back={}
local brightness=.26
function back.draw()
gc.clear(brightness,brightness,brightness)
end
function back.event(b)
brightness=b
end
return back

View File

@@ -22,12 +22,12 @@ function back.resize(w,h)
S[i+4]=(rnd()-.5)*.01*s--Vy S[i+4]=(rnd()-.5)*.01*s--Vy
end end
end end
function back.update() function back.update(dt)
local S=stars local S=stars
--Star moving --Star moving
for i=1,1260,5 do for i=1,1260,5 do
S[i+1]=(S[i+1]+S[i+3])%W S[i+1]=(S[i+1]+S[i+3]*dt*60)%W
S[i+2]=(S[i+2]+S[i+4])%H S[i+2]=(S[i+2]+S[i+4]*dt*60)%H
end end
end end
function back.draw() function back.draw()

View File

@@ -25,8 +25,8 @@ function bot_cc:revive()
self.P:loadAI(self.data) self.P:loadAI(self.data)
end end
function bot_cc:pushNewNext(id) function bot_cc:pushNewNext(id)
self.ccBot:addNext(rem(self.nexts,1)) self.ccBot:addNext(rem(self.bufferedNexts,1))
ins(self.nexts,id) ins(self.bufferedNexts,id)
end end
function bot_cc:thread() function bot_cc:thread()
local P,keys=self.P,self.keys local P,keys=self.P,self.keys

View File

@@ -12,7 +12,7 @@ local baseBot={
function baseBot.update(bot) function baseBot.update(bot)
local P=bot.P local P=bot.P
local keys=bot.keys local keys=bot.keys
if P.control and P.waiting==0 then if P.control and P.cur then
bot.delay=bot.delay-1 bot.delay=bot.delay-1
if not keys[1]then if not keys[1]then
if bot.runningThread then if bot.runningThread then
@@ -85,7 +85,7 @@ function BOT.new(P,data)
if data.type=="CC"then if data.type=="CC"then
P:setRS('SRS') P:setRS('SRS')
bot.keys={} bot.keys={}
bot.nexts={} bot.bufferedNexts={}
bot.delay=data.delay bot.delay=data.delay
bot.delay0=data.delay bot.delay0=data.delay
if P.gameEnv.holdCount>1 then if P.gameEnv.holdCount>1 then
@@ -109,20 +109,25 @@ function BOT.new(P,data)
return return
self.ccBot[k]and function(_,...)self.ccBot[k](self.ccBot,...)end or self.ccBot[k]and function(_,...)self.ccBot[k](self.ccBot,...)end or
cc_lua[k]and function(_,...)cc_lua[k](self,...)end or cc_lua[k]and function(_,...)cc_lua[k](self,...)end or
baseBot[k]and baseBot[k]or assert(baseBot[k],"No CC action called "..k)
error("No actions called "..k)
end}) end})
for i,B in next,P.nextQueue do local pushed=0
if i<=data.next then if P.cur then
bot:addNext(P.cur.id)
pushed=pushed+1
end
for _,B in next,P.nextQueue do
if pushed<=data.next then
bot:addNext(B.id) bot:addNext(B.id)
pushed=pushed+1
else else
ins(bot.nexts,B.id) ins(bot.bufferedNexts,B.id)
end end
end end
bot.runningThread=coroutine.wrap(cc_lua.thread) bot.runningThread=coroutine.wrap(cc_lua.thread)
bot.runningThread(bot) bot.runningThread(bot)
elseif data.type=="9S"or true then--9s or else else--if data.type=="9S"then--9s or else
TABLE.cover(baseBot,bot) TABLE.cover(baseBot,bot)
TABLE.cover(require"parts.bot.bot_9s",bot) TABLE.cover(require"parts.bot.bot_9s",bot)
P:setRS('TRS') P:setRS('TRS')

View File

@@ -0,0 +1,18 @@
return{
hook_drop=function(P)
if P.lastPiece.row>0 then
for _=1,#P.clearedRow do
local h=#P.field
P.field[h+1]=LINE.new(20)
P.visTime[h+1]=LINE.new(20)
for i=3,7 do P.field[h+1][i]=0 end
end
if P.combo>P.modeData.maxCombo then
P.modeData.maxCombo=P.combo
end
if P.stat.row>=200 then
P:win('finish')
end
end
end
}

39
parts/eventsets/big_h.lua Normal file
View File

@@ -0,0 +1,39 @@
return
{
drop=1,
wait=8,
fall=20,
fieldH=10,
mesDisp=function(P)
PLY.draw.drawProgress(P.stat.row,P.modeData.target)
PLY.draw.drawTargetLine(P,200-P.stat.row)
end,
task=function(P)
local F=P.field
for i=1,24 do
F[i]=LINE.new(20)
P.visTime[i]=LINE.new(20)
for x=3,7 do F[i][x]=0 end
end
P.modeData.target=50
end,
hook_drop=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target==50 then
P.gameEnv.drop=.5
P.modeData.target=100
SFX.play('reach')
elseif P.modeData.target==100 then
P.gameEnv.drop=.25
P.modeData.target=150
SFX.play('reach')
elseif P.modeData.target==150 then
P:set20G(true)
P.modeData.target=200
SFX.play('reach')
else
P:win('finish')
end
end
end
}

33
parts/eventsets/big_n.lua Normal file
View File

@@ -0,0 +1,33 @@
local dropSpeed={100,80,60,48,36,28,20,16,12,10,8,6,4,2,2,1,1,.5,.5}
return
{
drop=120,
wait=8,
fall=20,
fieldH=10,
mesDisp=function(P)
PLY.draw.drawProgress(P.stat.row,P.modeData.target)
PLY.draw.drawTargetLine(P,200-P.stat.row)
end,
task=function(P)
local F=P.field
for i=1,24 do
F[i]=LINE.new(20)
P.visTime[i]=LINE.new(20)
for x=3,7 do F[i][x]=0 end
end
P.modeData.target=10
end,
hook_drop=function(P)
if P.stat.row>=P.modeData.target then
if P.modeData.target==200 then
P:win('finish')
else
P.gameEnv.drop=dropSpeed[P.modeData.target/10]
P.modeData.target=P.modeData.target+10
SFX.play('reach')
end
end
end
}

View File

@@ -10,7 +10,7 @@ return{
end end
setField(P,D.finished+1) setField(P,D.finished+1)
SYSFX.newShade(1.4,P.absFieldX,P.absFieldY,300*P.size,610*P.size,.6,.8,.6) SYSFX.newShade(1.4,P.absFieldX,P.absFieldY,300*P.size,610*P.size,.6,.8,.6)
SFX.play('blip_1') SFX.play('warn_1')
else else
P:win('finish') P:win('finish')
end end

View File

@@ -28,7 +28,7 @@ return{
end end
setFont(50) setFont(50)
mStr(P.modeData.drought,63,130) mStr(P.modeData.drought,63,130)
mDraw(MODES.drought_l.icon,63,200,nil,.5) mDraw(IMG.drought,63,200,nil,.5)
end end
end, end,
task=function(P) task=function(P)
@@ -41,15 +41,15 @@ return{
if D.target==110 then if D.target==110 then
P.gameEnv.drop,P.gameEnv.lock=5,5 P.gameEnv.drop,P.gameEnv.lock=5,5
P.gameEnv.sddas,P.gameEnv.sdarr=5,5 P.gameEnv.sddas,P.gameEnv.sdarr=5,5
SFX.play('blip_2',.7) SFX.play('warn_2',.7)
elseif D.target==140 then elseif D.target==140 then
P.gameEnv.drop,P.gameEnv.lock=4,4 P.gameEnv.drop,P.gameEnv.lock=4,4
P.gameEnv.sddas,P.gameEnv.sdarr=4,4 P.gameEnv.sddas,P.gameEnv.sdarr=4,4
SFX.play('blip_2',.7) SFX.play('warn_2',.7)
elseif D.target==170 then elseif D.target==170 then
P.gameEnv.drop,P.gameEnv.lock=3,3 P.gameEnv.drop,P.gameEnv.lock=3,3
P.gameEnv.sddas,P.gameEnv.sdarr=3,3 P.gameEnv.sddas,P.gameEnv.sdarr=3,3
SFX.play('blip_2',.7) SFX.play('warn_2',.7)
elseif D.target==200 then elseif D.target==200 then
P:win('finish') P:win('finish')
return return

View File

@@ -28,7 +28,7 @@ return{
end end
setFont(50) setFont(50)
mStr(P.modeData.drought,63,130) mStr(P.modeData.drought,63,130)
mDraw(MODES.drought_l.icon,63,200,nil,.5) mDraw(IMG.drought,63,200,nil,.5)
end end
end, end,
task=function(P) task=function(P)
@@ -36,11 +36,12 @@ return{
end, end,
hook_drop=function(P) hook_drop=function(P)
local D=P.modeData local D=P.modeData
D.drought=P.lastPiece.id==7 and 0 or D.drought+1
if P.stat.row>=D.target then if P.stat.row>=D.target then
if D.target==110 then if D.target==110 then
P.gameEnv.drop,P.gameEnv.lock=2,2 P.gameEnv.drop,P.gameEnv.lock=2,2
P.gameEnv.sddas,P.gameEnv.sdarr=2,2 P.gameEnv.sddas,P.gameEnv.sdarr=2,2
SFX.play('blip_1') SFX.play('warn_1')
elseif D.target==200 then elseif D.target==200 then
P:win('finish') P:win('finish')
return return

View File

@@ -28,7 +28,7 @@ return{
end end
setFont(50) setFont(50)
mStr(P.modeData.drought,63,130) mStr(P.modeData.drought,63,130)
mDraw(MODES.drought_l.icon,63,200,nil,.5) mDraw(IMG.drought,63,200,nil,.5)
end end
end, end,
task=function(P) task=function(P)
@@ -36,6 +36,7 @@ return{
end, end,
hook_drop=function(P) hook_drop=function(P)
local D=P.modeData local D=P.modeData
D.drought=P.lastPiece.id==7 and 0 or D.drought+1
if P.stat.row>=D.target then if P.stat.row>=D.target then
if D.target==100 then if D.target==100 then
P:win('finish') P:win('finish')

View File

@@ -1,4 +1,4 @@
local dropSpeed={50,40,30,24,18,14,10,8,6,5,4,3,2,1,1,.5,.5,.25,.25} local dropSpeed={50,40,30,24,18,13,9,6,4,3,2,2,1,1,.5,.5,.5,.25,.25}
return return
{ {

View File

@@ -20,7 +20,7 @@ return{
D.pt=D.pt+s D.pt=D.pt+s
if D.pt%100==99 then if D.pt%100==99 then
SFX.play('blip_1') SFX.play('warn_1')
elseif D.pt>=D.target then--Level up! elseif D.pt>=D.target then--Level up!
s=D.target/100--range from 1 to 9 s=D.target/100--range from 1 to 9
local E=P.gameEnv local E=P.gameEnv

View File

@@ -1,6 +1,6 @@
local death_lock={12,11,10,9,8, 7,7,7,7,6} local death_lock={12,11,10,9,8, 8,8,7,7,6}
local death_wait={10,9, 8, 7,6, 6,6,5,5,4} local death_wait={10,9, 8, 7,6, 7,6,6,5,5}
local death_fall={10,9, 8, 7,6, 6,5,5,4,4} local death_fall={10,9, 8, 7,6, 7,6,5,5,5}
return{ return{
drop=0, drop=0,
@@ -24,7 +24,7 @@ return{
D.pt=D.pt+s D.pt=D.pt+s
if D.pt%100==99 then if D.pt%100==99 then
SFX.play('blip_1') SFX.play('warn_1')
elseif D.pt>=D.target then--Level up! elseif D.pt>=D.target then--Level up!
s=D.target/100 s=D.target/100
local E=P.gameEnv local E=P.gameEnv

View File

@@ -0,0 +1,80 @@
local inv_lock={60,50,45,40,37, 34,32,30,28,26}
local inv_wait={12,11,11,10,10, 10,10, 9, 9, 9}
local inv_fall={18,16,14,13,12, 12,11,11,10,10}
local inv_hide={20,17,14,11, 8, 5, 3, 2, 1, 0}
local hidetimer=0
local held=false
return{
drop=0,
lock=inv_lock[1],
wait=inv_wait[1],
fall=inv_fall[1],
ghost=false,
noTele=true,
das=10,arr=1,
mesDisp=function(P)
PLY.draw.drawProgress(P.modeData.pt,P.modeData.target)
end,
hook_drop=function(P)
local D=P.modeData
local c=#P.clearedRow
if c==0 and D.pt%100==99 then return end
local s=c<3 and c+1 or c==3 and 5 or 7
if P.combo>7 then s=s+2
elseif P.combo>3 then s=s+1
end
D.pt=D.pt+s
held=false
if D.pt<1000 then
hidetimer=0-inv_wait[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1]
if c>0 then hidetimer=hidetimer-inv_fall[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1]end
end
if D.pt%100==99 then
SFX.play('warn_1')
elseif D.pt>=D.target then--Level up!
s=D.target/100
local E=P.gameEnv
E.lock=inv_lock[s]
E.wait=inv_wait[s]
E.fall=inv_fall[s]
if s==2 then
E.das=8
elseif s==4 then
BG.set('rgb')
elseif s==5 then
E.das=7
elseif s==7 then
E.das=6
BGM.play('far')
elseif s==10 then
D.pt=1000
P:win('finish')
return
end
D.target=D.target+100
P:stageComplete(s)
SFX.play('reach')
end
end,
task=function(P)
P.modeData.pt=0
P.modeData.target=100
while true do
YIELD()
if P.holdTime==0 and P.waiting<=0 and not held then
hidetimer=0
held=true
end
hidetimer=hidetimer+1
if hidetimer>inv_hide[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1]then
P.gameEnv.block=false
else
P.gameEnv.block=true
end
end
end,
}

View File

@@ -24,7 +24,7 @@ return{
D.pt=D.pt+s D.pt=D.pt+s
if D.pt%100==99 then if D.pt%100==99 then
SFX.play('blip_1') SFX.play('warn_1')
elseif D.pt>=D.target then--Level up! elseif D.pt>=D.target then--Level up!
s=D.target/100 s=D.target/100
local E=P.gameEnv local E=P.gameEnv

View File

@@ -147,7 +147,7 @@ return
P.modeData.target=260 P.modeData.target=260
p=260 p=260
SFX.play('blip_2') SFX.play('warn_2')
SFX.play('reach') SFX.play('reach')
else else
p=260 p=260

View File

@@ -5,15 +5,22 @@ local setFont=setFont
local PLAYERS,PLY_ALIVE=PLAYERS,PLY_ALIVE local PLAYERS,PLY_ALIVE=PLAYERS,PLY_ALIVE
return{ return{
layout='royale',
fkey1=function(P)
P:changeAtkMode(P.atkMode<3 and P.atkMode+2 or 5-P.atkMode)
P.swappingAtkMode=45
end,
mesDisp=function(P) mesDisp=function(P)
setFont(35) setFont(35)
mStr(#PLY_ALIVE.."/"..#PLAYERS,63,175) mStr(#PLY_ALIVE.."/"..#PLAYERS,63,175)
mStr(P.modeData.ko,80,215) mStr(P.modeData.ko,80,215)
gc_draw(TEXTOBJ.ko,60-TEXTOBJ.ko:getWidth(),222) gc_draw(TEXTOBJ.ko,60-TEXTOBJ.ko:getWidth(),222)
setFont(20) setFont(20)
gc_setColor(1,.5,0,.6) gc_setColor(1,.5,0,.6)
gc_print(P.badge,103,227) gc_print(P.badge,103,227)
gc_setColor(.97,.97,.97) gc_setColor(.97,.97,.97)
setFont(25) setFont(25)
mStr(text.powerUp[P.strength],63,290) mStr(text.powerUp[P.strength],63,290)
gc_setColor(1,1,1) gc_setColor(1,1,1)

View File

@@ -18,8 +18,15 @@ return
if P.modeData.target==200 then if P.modeData.target==200 then
P:win('finish') P:win('finish')
else else
if P.modeData.target==100 then if P.modeData.target==40 then
BG.set('rainbow')
elseif P.modeData.target==80 then
BG.set('rainbow2')
elseif P.modeData.target==100 then
BG.set('glow')
P.modeData.lock=6 P.modeData.lock=6
elseif P.modeData.target==120 then
BG.set('lightning')
end end
P.gameEnv.wait=waitSpeed[P.modeData.target/10] P.gameEnv.wait=waitSpeed[P.modeData.target/10]
P.modeData.target=P.modeData.target+10 P.modeData.target=P.modeData.target+10

View File

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

View File

@@ -18,8 +18,16 @@ return
if P.modeData.target==200 then if P.modeData.target==200 then
P:win('finish') P:win('finish')
else else
if P.modeData.target==100 then if P.modeData.target==40 then
BG.set('rainbow')
elseif P.modeData.target==80 then
BG.set('rainbow2')
elseif P.modeData.target==100 then
BG.set('glow')
P.modeData.lock=5 P.modeData.lock=5
BGM.play('secret8th remix')
elseif P.modeData.target==120 then
BG.set('lightning')
end end
P.gameEnv.wait=waitSpeed[P.modeData.target/10] P.gameEnv.wait=waitSpeed[P.modeData.target/10]
P.modeData.target=P.modeData.target+10 P.modeData.target=P.modeData.target+10

View File

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

View File

@@ -18,8 +18,16 @@ return
if P.modeData.target==200 then if P.modeData.target==200 then
P:win('finish') P:win('finish')
else else
if P.modeData.target==100 then if P.modeData.target==40 then
BG.set('rainbow')
elseif P.modeData.target==80 then
BG.set('rainbow2')
elseif P.modeData.target==100 then
BG.set('glow')
P.modeData.lock=4 P.modeData.lock=4
BGM.play('secret7th remix')
elseif P.modeData.target==120 then
BG.set('lightning')
end end
P.gameEnv.wait=waitSpeed[P.modeData.target/10] P.gameEnv.wait=waitSpeed[P.modeData.target/10]
P.modeData.target=P.modeData.target+10 P.modeData.target=P.modeData.target+10

View File

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

View File

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

Binary file not shown.

Binary file not shown.

View File

@@ -7,6 +7,7 @@ local gc_draw,gc_rectangle,gc_line,gc_printf=gc.draw,gc.rectangle,gc.line,gc.pri
local ins,rem=table.insert,table.remove local ins,rem=table.insert,table.remove
local int,rnd=math.floor,math.random local int,rnd=math.floor,math.random
local approach=MATH.expApproach
local SETTING,GAME,SCR=SETTING,GAME,SCR local SETTING,GAME,SCR=SETTING,GAME,SCR
local PLAYERS=PLAYERS local PLAYERS=PLAYERS
@@ -15,39 +16,102 @@ local playSFX=SFX.play
--System --System
do--function tryBack()
local sureTime=-1e99
function tryBack()
if TIME()-sureTime<1 then
sureTime=-1e99
return true
else
sureTime=TIME()
MES.new('warn',text.sureQuit)
end
end
end
do--function tryReset()
local sureTime=-1e99
function tryReset()
if TIME()-sureTime<1 then
sureTime=-1e99
return true
else
sureTime=TIME()
MES.new('warn',text.sureReset)
end
end
end
do--function tryDelete()
local sureTime=-1e99
function tryDelete()
if TIME()-sureTime<1 then
sureTime=-1e99
return true
else
sureTime=TIME()
MES.new('warn',text.sureDelete)
end
end
end
do--function loadFile(name,args), function saveFile(data,name,args)
local t=setmetatable({},{__index=function()return"'$1' loading failed: $2"end})
function loadFile(name,args)
local text=text or t
if not args then args=''end
local res,mes=pcall(FILE.load,name,args)
if res then
return mes
else
if mes:find'open error'then
MES.new('error',text.loadError_open:repD(name,""))
elseif mes:find'unknown mode'then
MES.new('error',text.loadError_errorMode:repD(name,args))
elseif mes:find'no file'then
if not args:sArg'-canSkip'then
MES.new('error',text.loadError_noFile:repD(name,""))
end
elseif mes then
MES.new('error',text.loadError_other:repD(name,mes))
else
MES.new('error',text.loadError_unknown:repD(name,""))
end
end
end
function saveFile(data,name,args)
local text=text or t
local res,mes=pcall(FILE.save,data,name,args)
if res then
return true
else
MES.new('error',
mes:find'duplicate'and
text.saveError_duplicate:repD(name)or
mes:find'encode error'and
text.saveError_encode:repD(name)or
mes and
text.saveError_other:repD(name,mes)or
text.saveError_unknown:repD(name)
)
end
end
end
function isSafeFile(file,mes) function isSafeFile(file,mes)
if love.filesystem.getRealDirectory(file)~=SAVEDIR then local path=love.filesystem.getRealDirectory(file)
if path and path~=SAVEDIR then
return true return true
elseif mes then elseif mes then
MES.new('warn',mes) MES.new('warn',mes)
end end
end end
function saveStats() function saveStats()
return FILE.save(STAT,'conf/data') return saveFile(STAT,'conf/data')
end end
function saveProgress() function saveProgress()
return FILE.save(RANKS,'conf/unlock') return saveFile(RANKS,'conf/unlock')
end end
function saveSettings() function saveSettings()
return FILE.save(SETTING,'conf/settings') return saveFile(SETTING,'conf/settings')
end end
function applyLanguage() do--function applySettings()
text=LANG.get(SETTING.locale)
WIDGET.setLang(text.WidgetText)
for k,v in next,TEXTOBJ do
if rawget(text,k)then
v:set(text[k])
end
end
end
function applyCursor()
love.mouse.setVisible(SETTING.sysCursor)
end
function applyFullscreen()
love.window.setFullscreen(SETTING.fullscreen)
love.resize(gc.getWidth(),gc.getHeight())
end
do--function applyBlockSatur,applyFieldSatur(mode)
local saturateValues={ local saturateValues={
normal={0,1}, normal={0,1},
soft={.2,.7}, soft={.2,.7},
@@ -55,29 +119,79 @@ do--function applyBlockSatur,applyFieldSatur(mode)
light={.2,.8}, light={.2,.8},
color={-.2,1.2}, color={-.2,1.2},
} }
function applyBlockSatur(mode) function applySettings()
local m=saturateValues[mode]or saturateValues.normal --Apply fullscreen
love.window.setFullscreen(SETTING.fullscreen)
love.resize(gc.getWidth(),gc.getHeight())
--Apply Zframework setting
Z.setClickFX(SETTING.clickFX)
Z.setFrameMul(SETTING.frameMul)
Z.setPowerInfo(SETTING.powerInfo)
Z.setCleanCanvas(SETTING.cleanCanvas)
--Apply VK shape
VK.setShape(SETTING.VKSkin)
--Apply sound
love.audio.setVolume(SETTING.mainVol)
BGM.setVol(SETTING.bgm)
SFX.setVol(SETTING.sfx)
VOC.setVol(SETTING.voc)
--Apply saturs
local m
m=saturateValues[SETTING.blockSatur]or saturateValues.normal
SHADER.blockSatur:send('b',m[1]) SHADER.blockSatur:send('b',m[1])
SHADER.blockSatur:send('k',m[2]) SHADER.blockSatur:send('k',m[2])
end m=saturateValues[SETTING.fieldSatur]or saturateValues.normal
function applyFieldSatur(mode)
local m=saturateValues[mode]or saturateValues.normal
SHADER.fieldSatur:send('b',m[1]) SHADER.fieldSatur:send('b',m[1])
SHADER.fieldSatur:send('k',m[2]) SHADER.fieldSatur:send('k',m[2])
--Apply language
text=LANG.get(SETTING.locale)
WIDGET.setLang(text.WidgetText)
for k,v in next,TEXTOBJ do
if rawget(text,k)then
v:set(text[k])
end
end
--Apply cursor
love.mouse.setVisible(SETTING.sysCursor)
--Apply BG
if SETTING.bg=='on'then
BG.unlock()
BG.set()
elseif SETTING.bg=='off'then
BG.unlock()
BG.set('gray')
BG.send(SETTING.bgAlpha)
BG.lock()
elseif SETTING.bg=='custom'then
if love.filesystem.getInfo('conf/customBG')then
local res,image=pcall(gc.newImage,love.filesystem.newFile('conf/customBG'))
if res then
BG.unlock()
BG.set('custom')
gc.setDefaultFilter('linear','linear')
BG.send(SETTING.bgAlpha,image)
gc.setDefaultFilter('nearest','nearest')
BG.lock()
else
MES.new('error',text.customBGloadFailed)
end
else--Switch off when custom BG not found
SETTING.bg='off'
BG.unlock()
BG.set('gray')
BG.send(SETTING.bgAlpha)
BG.lock()
end
end
end end
end end
function applyAllSettings()
love.window.setFullscreen(SETTING.fullscreen)
love.audio.setVolume(SETTING.mainVol)
VK.setShape(SETTING.VKSkin)
BGM.setVol(SETTING.bgm)
SFX.setVol(SETTING.sfx)
VOC.setVol(SETTING.voc)
applyBlockSatur(SETTING.blockSatur)
applyFieldSatur(SETTING.fieldSatur)
applyLanguage()
applyCursor()
end
--Royale mode --Royale mode
function randomTarget(P)--Return a random opponent for P function randomTarget(P)--Return a random opponent for P
@@ -145,17 +259,17 @@ function royaleLevelup()
BGM.play('cruelty') BGM.play('cruelty')
end end
elseif GAME.stage==4 then elseif GAME.stage==4 then
spd=10 spd=8
for _,P in next,PLY_ALIVE do for _,P in next,PLY_ALIVE do
P.gameEnv.pushSpeed=3 P.gameEnv.pushSpeed=3
end end
elseif GAME.stage==5 then elseif GAME.stage==5 then
spd=5 spd=4
for _,P in next,PLY_ALIVE do for _,P in next,PLY_ALIVE do
P.gameEnv.garbageSpeed=1 P.gameEnv.garbageSpeed=1
end end
elseif GAME.stage==6 then elseif GAME.stage==6 then
spd=3 spd=2
if PLAYERS[1].alive then if PLAYERS[1].alive then
BGM.play('final') BGM.play('final')
end end
@@ -166,7 +280,7 @@ function royaleLevelup()
if GAME.curMode.name:find("_u")then if GAME.curMode.name:find("_u")then
for i=1,#PLY_ALIVE do for i=1,#PLY_ALIVE do
local P=PLY_ALIVE[i] local P=PLY_ALIVE[i]
P.gameEnv.drop=int(P.gameEnv.drop*.3) P.gameEnv.drop=int(P.gameEnv.drop*.4)
if P.gameEnv.drop==0 then if P.gameEnv.drop==0 then
P.curY=P.ghoY P.curY=P.ghoY
P:set20G(true) P:set20G(true)
@@ -263,16 +377,16 @@ function setField(P,page)
end end
end end
end end
function freshDate(mode) function freshDate(args)
if not mode then if not args then
mode="" args=""
end end
local date=os.date("%Y/%m/%d") local date=os.date("%Y/%m/%d")
if STAT.date~=date then if STAT.date~=date then
STAT.date=date STAT.date=date
STAT.todayTime=0 STAT.todayTime=0
getItem('zTicket',1) getItem('zTicket',1)
if not mode:find'q'then if not args:find'q'then
MES.new('info',text.newDay) MES.new('info',text.newDay)
end end
saveStats() saveStats()
@@ -384,12 +498,9 @@ end
function loadGame(mode,ifQuickPlay,ifNet)--Load a mode and go to game scene function loadGame(mode,ifQuickPlay,ifNet)--Load a mode and go to game scene
freshDate() freshDate()
if legalGameTime()then if legalGameTime()then
if not MODES[mode]and love.filesystem.getRealDirectory('parts/modes/'..mode)~=SAVEDIR then if not MODES[mode].available then
MODES[mode]=require('parts.modes.'..mode) MES.new('error',"Unavailable mode: "..mode)
MODES[mode].name=mode return
end
if MODES[mode].score then
STAT.lastPlay=mode
end end
GAME.playing=true GAME.playing=true
GAME.init=true GAME.init=true
@@ -430,29 +541,9 @@ function gameOver()--Save record
GAME.rank=R GAME.rank=R
end end
if not GAME.replaying and M.score and scoreValid()then if not GAME.replaying and M.score and scoreValid()then
if RANKS[M.name]then--Old rank exist if not RANKS[M.name]or R>RANKS[M.name]then--Old rank exist
local needSave RANKS[M.name]=R
if R>RANKS[M.name]then saveProgress()
RANKS[M.name]=R
needSave=true
end
if R>0 then
if M.unlock then
for i=1,#M.unlock do
local m=M.unlock[i]
local n=MODES[m].name
if not RANKS[n]then
if MODES[m].x then
RANKS[n]=0
end
needSave=true
end
end
end
end
if needSave then
saveProgress()
end
end end
local D=M.score(P) local D=M.score(P)
local L=M.records local L=M.records
@@ -475,7 +566,7 @@ function gameOver()--Save record
D.date=os.date("%Y/%m/%d %H:%M") D.date=os.date("%Y/%m/%d %H:%M")
ins(L,p+1,D) ins(L,p+1,D)
if L[11]then L[11]=nil end if L[11]then L[11]=nil end
FILE.save(L,('record/%s.rec'):format(M.name),'l') saveFile(L,('record/%s.rec'):format(M.name),'-luaon')
end end
end end
end end
@@ -664,7 +755,7 @@ do--function resetGameData(args)
BGM.play(type(bgm)=='string'and bgm or type(bgm)=='table'and bgm[math.random(#bgm)]) BGM.play(type(bgm)=='string'and bgm or type(bgm)=='table'and bgm[math.random(#bgm)])
TEXT.clear() TEXT.clear()
if GAME.modeEnv.royaleMode then if GAME.modeEnv.eventSet=='royale'then
for i=1,#PLAYERS do for i=1,#PLAYERS do
PLAYERS[i]:changeAtk(randomTarget(PLAYERS[i])) PLAYERS[i]:changeAtk(randomTarget(PLAYERS[i]))
end end
@@ -685,7 +776,7 @@ do--function resetGameData(args)
end end
do--function checkWarning() do--function checkWarning()
local max=math.max local max=math.max
function checkWarning() function checkWarning(dt)
local P1=PLAYERS[1] local P1=PLAYERS[1]
if P1.alive then if P1.alive then
if P1.frameRun%26==0 then if P1.frameRun%26==0 then
@@ -705,13 +796,13 @@ do--function checkWarning()
end end
local _=GAME.warnLVL local _=GAME.warnLVL
if _<GAME.warnLVL0 then if _<GAME.warnLVL0 then
_=_*.95+GAME.warnLVL0*.05 _=approach(_,GAME.warnLVL0,dt*6)
elseif _>0 then elseif _>0 then
_=max(_-.026,0) _=max(_-.026,0)
end end
GAME.warnLVL=_ GAME.warnLVL=_
if GAME.warnLVL>1.126 and P1.frameRun%30==0 then if GAME.warnLVL>1.126 and P1.frameRun%30==0 then
SFX.fplay('warning',SETTING.sfx_warn) SFX.fplay('warn_beep',SETTING.sfx_warn)
end end
elseif GAME.warnLVL>0 then elseif GAME.warnLVL>0 then
GAME.warnLVL=max(GAME.warnLVL-.026,0) GAME.warnLVL=max(GAME.warnLVL-.026,0)
@@ -749,7 +840,7 @@ do--function drawSelfProfile()
--Draw avatar --Draw avatar
gc_setLineWidth(2) gc_setLineWidth(2)
gc_setColor(.3,.3,.3,.8)gc_rectangle('fill',0,0,-300,80) gc_setColor(COLOR.X)gc_rectangle('fill',0,0,-300,80)
gc_setColor(1,1,1)gc_rectangle('line',-300,0,300,80,5) gc_setColor(1,1,1)gc_rectangle('line',-300,0,300,80,5)
gc_rectangle('line',-73,7,66,66,2) gc_rectangle('line',-73,7,66,66,2)
gc_draw(selfAvatar,-72,8,nil,.5) gc_draw(selfAvatar,-72,8,nil,.5)
@@ -828,8 +919,11 @@ end
do--CUS/SETXXX(k) do--CUS/SETXXX(k)
local CUSTOMENV=CUSTOMENV local CUSTOMENV=CUSTOMENV
local warnList={ local warnList={
'ims','RS','FTLock','frameMul','highCam', 'das','arr','dascut','dropcut','sddas','sdarr',
'ihs','irs','ims','RS',
'FTLock','frameMul','highCam',
'VKSwitch','VKIcon','VKTrack','VKDodge', 'VKSwitch','VKIcon','VKTrack','VKDodge',
'simpMode',
} }
function CUSval(k)return function()return CUSTOMENV[k]end end function CUSval(k)return function()return CUSTOMENV[k]end end
function ROOMval(k)return function()return ROOMENV[k]end end function ROOMval(k)return function()return ROOMENV[k]end end

View File

@@ -13,6 +13,13 @@ BLOCK_COLORS={
COLOR.dH,COLOR.D,COLOR.lY,COLOR.H,COLOR.lH,COLOR.dV,COLOR.dR,COLOR.dG, COLOR.dH,COLOR.D,COLOR.lY,COLOR.H,COLOR.lH,COLOR.dV,COLOR.dR,COLOR.dG,
} }
RANK_CHARS={'B','A','S','U','X'}for i=1,#RANK_CHARS do RANK_CHARS[i]=CHAR.icon['rank'..RANK_CHARS[i]]end RANK_CHARS={'B','A','S','U','X'}for i=1,#RANK_CHARS do RANK_CHARS[i]=CHAR.icon['rank'..RANK_CHARS[i]]end
RANK_BASE_COLORS={
{.1,.2,.3},
{.3,.42,.32},
{.45,.44,.15},
{.42,.25,.2},
{.42,.15,.4},
}
RANK_COLORS={ RANK_COLORS={
{.8,.86,.9}, {.8,.86,.9},
{.6,.9,.7}, {.6,.9,.7},
@@ -594,7 +601,7 @@ do--Userdata tables
FTLock=true, FTLock=true,
--System --System
reTime=4, reTime=2,
allowTAS=false, allowTAS=false,
autoPause=true, autoPause=true,
menuPos='middle', menuPos='middle',
@@ -603,11 +610,13 @@ do--Userdata tables
autoLogin=true, autoLogin=true,
simpMode=false, simpMode=false,
sysCursor=true, sysCursor=true,
maxFPS=60,
frameMul=100,
locale='zh', locale='zh',
skinSet='crystal_scf', skinSet='crystal_scf',
skin={ skin={
1,7,11,3,14,4,9, 1,7,11,3,14,4,9,
1,7,2,6,10,2,13,5,9,15,10,11,3,12,2,16,8,4, 1,7,2,6,10,2,13,5,9,15,4,11,3,12,2,16,8,4,
10,13,2,8 10,13,2,8
}, },
face={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, face={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
@@ -625,7 +634,6 @@ do--Userdata tables
splashFX=2, splashFX=2,
shakeFX=2, shakeFX=2,
atkFX=2, atkFX=2,
frameMul=100,
cleanCanvas=false, cleanCanvas=false,
blockSatur='normal', blockSatur='normal',
fieldSatur='normal', fieldSatur='normal',
@@ -637,7 +645,8 @@ do--Userdata tables
highCam=true, highCam=true,
nextPos=true, nextPos=true,
fullscreen=true, fullscreen=true,
bg=true, bg='on',
bgAlpha=.26,
powerInfo=false, powerInfo=false,
clickFX=true, clickFX=true,
warn=true, warn=true,
@@ -674,7 +683,7 @@ do--Userdata tables
r=0, r=0,
}, },
joystick={ joystick={
dpleft=1,dpright=2,a=3,b=4,y=5, dpleft=1,dpright=2,b=3,a=4,y=5,
dpup=6,dpdown=7,rightshoulder=8,x=9, dpup=6,dpdown=7,rightshoulder=8,x=9,
leftshoulder=0, leftshoulder=0,
}, },
@@ -721,4 +730,45 @@ do--Userdata tables
date=false, date=false,
todayTime=0, todayTime=0,
} }
end end
do--Mode data tables
MODES=setmetatable({},{__index=function(self,name)
local M
if love.filesystem.getInfo('parts/modes/'..name..'.lua')and love.filesystem.getRealDirectory('parts/modes/'..name..'.lua')~=SAVEDIR then
M=require('parts.modes.'..name)
M.available=true
M.name=name
do--Check if need slowmark
for k in next,M.env do
if
k=='mindas'or k=='minarr'or
k=='das'or k=='arr'or
k=='minsdarr'
then
M.slowMark=true
break
end
end
end
if M.score then
M.records=loadFile("record/"..name..".rec",'-luaon -canSkip')or{}
end
else
M={
available=false,
}
MES.new('error',"Failed to load mode file: "..name)
end
self[name]=M
return M
end})
MODEICON=setmetatable({},{__index=function(self,k)
if isSafeFile('media/image/modeicon/'..k..'.png')then
local img=love.graphics.newImage('media/image/modeicon/'..k..'.png')
self[k]=img
return img
else
return PAPER
end
end})
end

View File

@@ -1,20 +1,20 @@
local HDsearch="https://harddrop.com/wiki?search=" local HDsearch="https://harddrop.com/wiki?search="
local HDwiki="\nVisit HD Wiki for more information" local HDwiki="\nVisit Hard Drop Wiki for more information."
return{ return{
{"Translator Note 1", {"Translator Note 1",
"", "",
"help", "help",
"This translation of the TetroDictionary is provided by me, User670 (Discord: User670#9501).\n\nThe translation may not completely reflect the contents of the original Chinese text.\n\nCorrected by C₂₉H₂₅N₃O₅ (Discord: C29H25N3O5#1606).\n\nTo view the list of contributors or make contributions, feel free to visit the GitHub page.", "This translation of the TetroDictionary is maly provided by User670 (Discord: User670#9501).\n\nThe translation may not completely reflect the contents of the original Chinese text.\n\nTo view the list of contributors or make contributions, feel free to visit the GitHub page.",
"https://github.com/26F-Studio/Techmino/blob/main/parts/language/dict_en.lua", "https://github.com/26F-Studio/Techmino/blob/main/parts/language/dict_en.lua",
}, },
{"Official Website", {"Official Website",
"official website homepage", "official website homepage mainpage",
"help", "help",
"The official website of Techmino!\nYou can modify your profile on it", "The official website of Techmino!\nYou can modify your profile on it",
"http://home.techmino.org", "http://home.techmino.org",
}, },
{"To New Players", {"To New Players",
"guide newbie noob", "guide newbie noob readme",
"help", "help",
"To new players that want to get better at the game:\n\tTwo principles:\n\t1. find a version with good controls (e.g. Techmino, Tetr.io, Tetris Online, Jstris, Tetr.js). Do not use those version used for programming practice.\n\t2. Build foundations in your skills (stable Techrashes using next queue to aid decisions), don't go for fancy T-Spins from the start.\n\n\tTwo main techniques:\n\t1. familiarize yourself with spawn locations of pieces, and the controls to move the piece into each location\n\t2. Plan ahead of where to put the pieces\nHere is a article written by a well-known player in Chinese Tetris community talking about advices to new players. Click the globe to read the translated article by User670.", "To new players that want to get better at the game:\n\tTwo principles:\n\t1. find a version with good controls (e.g. Techmino, Tetr.io, Tetris Online, Jstris, Tetr.js). Do not use those version used for programming practice.\n\t2. Build foundations in your skills (stable Techrashes using next queue to aid decisions), don't go for fancy T-Spins from the start.\n\n\tTwo main techniques:\n\t1. familiarize yourself with spawn locations of pieces, and the controls to move the piece into each location\n\t2. Plan ahead of where to put the pieces\nHere is a article written by a well-known player in Chinese Tetris community talking about advices to new players. Click the globe to read the translated article by User670.",
"https://github.com/user670/temp/blob/master/tips_to_those_new_to_top.md", "https://github.com/user670/temp/blob/master/tips_to_those_new_to_top.md",
@@ -39,7 +39,7 @@ return{
{"Four.lol", {"Four.lol",
"four wiki", "four wiki",
"help", "help",
"An website containing collections of various openings with simple UI and very detailed consecutive PC analyses (Not recommended for new players as you may have to memorize many techniques).", "An website containing collections of various openings with simple UI and very detailed consecutive PC analyses (Not recommended for new players, as you may have to memorize many techniques).",
"https://four.lol", "https://four.lol",
}, },
{"Tetris Wiki Fandom", {"Tetris Wiki Fandom",
@@ -59,7 +59,7 @@ return{
{"Github Repository", {"Github Repository",
"githubrepository sourcecode", "githubrepository sourcecode",
"org", "org",
"Techmino's Github repository. Stars are appreciated.", "Techmino's official Github repository. Stars are appreciated.",
"https://github.com/26F-Studio/Techmino", "https://github.com/26F-Studio/Techmino",
}, },
{"Communities", {"Communities",
@@ -69,7 +69,7 @@ return{
"https://discord.gg/harddrop" "https://discord.gg/harddrop"
}, },
{"Mew", {"Mew",
"mew tieba forum", "mew tieba forum reddit",
"org", "org",
"The Mew forum owned by Chinese Tetris Research Community, and was founded in the second half of 2021. Mew is a Chinese social media that can be thought of a combination of Discord and Reddit, with many channels in a big community. Users can chat in the channels or submit posts to the channel. Mew also has a function called \"Library\" which allows storing documentations systematically. The Tetris Mew forum is currently under construction and not too much contents are available (2/Nov/2021).", "The Mew forum owned by Chinese Tetris Research Community, and was founded in the second half of 2021. Mew is a Chinese social media that can be thought of a combination of Discord and Reddit, with many channels in a big community. Users can chat in the channels or submit posts to the channel. Mew also has a function called \"Library\" which allows storing documentations systematically. The Tetris Mew forum is currently under construction and not too much contents are available (2/Nov/2021).",
"https://mew.fun/n/tetris", "https://mew.fun/n/tetris",
@@ -80,217 +80,244 @@ return{
"Google \"Tetris Online Poland\" for the Poland server.\nClick on the globe icon for information about the Tetris Online Study server.", "Google \"Tetris Online Poland\" for the Poland server.\nClick on the globe icon for information about the Tetris Online Study server.",
"http://teatube.ltd/tos", "http://teatube.ltd/tos",
}, },
{"P\97\116\114\101\111\110", {"Support 1",
"p\97\116\114\101\111\110 support", "support wechat vx alipay zfb",
"org", "org",
"Techmino's P\97\116\114\101\111\110 Page", FNNS and "This feature is restricted due to platform policy restrictions. You may discuss about this feature in our Discord server." or "To donate to Techmino via WeChat Pay or Alipay, type \"support\" in console.",
FNSF and"https://www.youtube.com/watch?v=DVl0IiUKX_g"or"https://www.p\97\116\114\101\111\110.com/techmino",
}, },
{"Support 2",
"support afdian",
"org",
FNNS and "This feature is restricted due to platform policy restrictions. You may discuss about this feature in our Discord server. The URL in this entry is a rickroll, by the way." or "To donate to Techmino via Aifadian, use the globe icon on the bottom right to open URL. Aifadian charges 6% transaction fee off your purchase.",
FNNS and"https://youtu.be/dQw4w9WgXcQ"or"https://afdian.net/@MrZ_26",
},
{"Support 3",
"support p\97\116\114\101\111\110",
"org",
FNNS and "This feature is restricted due to platform policy restrictions. You may discuss about this feature in our Discord server. The URL in this entry is a rickroll, by the way." or "To donate to Techmino via P\97\116\114\101\111\110, use the globe icon on the bottom right to open URL. P\97\116\114\101\111\110 charges 7.9% + 0.30 USD transaction fee off your purchase that is greater than 3 USD.",
FNNS and"https://youtu.be/dQw4w9WgXcQ"or"https://www.p\97\116\114\101\111\110.com/techmino",
},
--Games --Games
{"TTT", {"TTT",
"ttt tetris trainer tres bien", "ttt tetris trainer tres bien",
"game", "game",
"*Web-based, no mobile support | Single-player*\nTetris Trainer Tres-Bien. A hands-on tutorial of advanced techniques in modern Tetris.\nRecommended for players that can complete a 40-line Sprint with all Tetris line clears and no hold.\nCovered topics include T-Spin, finesse, SRS, and some battle setups.\nLink translated to Simplified Chinese; originally in Japanese.", "Tetris Trainer Très-Bien. A hands-on tutorial of advanced techniques in modern Tetris.\nRecommended for players that can complete a 40-line Sprint with all Tetris line clears and no hold.\nCovered topics include T-Spin, finesse, SRS, and some battle setups.\nLink in Japanese.",
"http://teatube.ltd/ttt", "http://taninkona.web.fc2.com/ttt/",
}, },
{"TTPC", {"TTPC",
"ttpc tetris perfect clear challenge", "ttpc tetris perfect clear challenge",
"game", "game",
"*Web-based, no mobile support | Single-player*\nTetris Perfect Clear Challenge. The PC opener tutorial for SRS+7 Bag.\nRecommended for players that have completed TTT. You need to know SRS to play this.\nIncludes only the basic PC opener.\nLink translated to Simplified Chinese; originally in Japanese.", "Tetris Perfect Clear Challenge. The PC opener tutorial for SRS and 7-Bag.\nRecommended for players that have completed TTT. You need to know SRS to play this.\nIncludes only the basic PC opener.\nLink translated to Simplified Chinese; originally in Japanese.",
"http://teatube.ltd/ttpc", "http://teatube.ltd/ttpc",
}, },
{"NAZO", {"NAZO",
"nazo", "nazo",
"game", "game",
"*Web-based, no mobile support | Single-player*\nAll sorts of SRS puzzles. Recommended for players that have completed TTT.\nHas T-Spin and all spin puzzles of all difficulties.\nLink translated to Simplified Chinese; originally in Japanese.", "All sorts of SRS puzzles. Recommended for players that have completed TTT.\nHas T-Spin and all spin puzzles of all difficulties.\nLink translated to Simplified Chinese; originally in Japanese.",
"http://teatube.ltd/nazo", "http://teatube.ltd/nazo",
}, },
{"Side Note 1",
"note nb NB DM notice",
"game",
"The following contents are some brief introductions about some official and fan-made Tetris games with high popularity. We make absolutely no guarantees that they would cover every Tetris game. Also, the author of this game has made some comments on some of these games. Notice that they are just personal opinions and cannot be used to judge the qualities of these games. To better differentiate between the facts and opinions, all the commentary contents are enclosed with square brackets and are separated from the main contents.",
},
{"King of Stackers", {"King of Stackers",
"kos kingofstackers", "kos kingofstackers",
"game", "game",
"*Web-based | Multiplayer*\nTurn-based battle Tetris game.", "Browser Game | Multiplayer | Mobile Support\nKoS for short. A turn-based battle Tetris game. In this game, the players can place seven tetrominoes in his or her turn, and garbage lines can enter the field only if the player places a block that does not clear a line. This game requires careful thinking and there are multiple modes with different attack mechanics.",
"https://kingofstackers.com/games.php", "https://kingofstackers.com/games.php",
}, },
{"Tetr.js", {"Tetr.js",
"tetrjs tetr.js", "tetrjs tetr.js",
"game", "game",
"*Web-based | Single-player*\nHas newbie-friendly custom modes (most common features). Only a few on-screen control schemes are available to mobile.\nLink to Farter's Dig Mod, which itself is a mod of another version. Also has another mod called Tetr.js Enhanced.", "Browser Game | Singleplayer | Mobile Support\nA browser-based Tetris game. It has many professional tunings and many modes, but the visuals are simple and there are barely any animations; besides that, only a few on-screen control schemes are available to mobile.\nLink to Farter's Dig Mod, which itself is a mod of another version. Also has another mod called Tetr.js Enhanced (You can find the link on Tetris Wiki).",
"http://farter.cn/t", "http://farter.cn/t",
}, },
{"Tetra Legends", {"Tetra Legends",
"tl tetralegends", "tl tetralegends",
"game", "game",
"*Web-based, no mobile support | Single-player*\nFeature-rich game with fancy visuals, also visualized some data that are otherwise hidden in other games, although controls aren't exactly the most comfortable. Has a rhythm mode.\nIt can be slow to load the game for the first time.", "Browser Game | Singleplayer | No Mobile Support\nOr TL for short. It has many single-player modes, two hidden rhythm modes, and visualizes many hidden mechanics with rich animations. The development of this game was halted for multiple reasons in December 2020.",
"https://tetralegends.app", "https://tetralegends.app",
}, },
{"Ascension", {"Ascension",
"asc ascension", "asc ascension ASC",
"game", "game",
"Or ASC for short. A cross-platform web-based Tetris game using its own rotation system called ASC. It may take a very long time when first loading this game. It also has many single-player modes (The \"Stack\" mode in Techmino was inspired by Ascension). Battle mode is currently in the testing phase (08/20/2021).", "Browser Game | Singleplayer/Multiplayer\nOr ASC for short. It uses its own rotation system (also called ASC) and has many single-player modes. Battle modes are currently under beta testing (15/Dec/2021). The Stack mode in this game was also inspired by Ascension. ",
"https://asc.winternebs.com", "https://asc.winternebs.com",
}, },
{"Jstris", {"Jstris",
"js jstris", "js jstris",
"game", "game",
"*Web-based | Single-player and multiplayer*\nBasic web-based battle Tetris game.", "Browser Game | Singleplayer/Multiplayer | Mobile Support\nOr JS for short. It has some single-player modes with multiple customizable parameters, Adjustable virtual keys layouts for mobile, but it doesn't have any animation. ",
"https://jstris.jezevec10.com", "https://jstris.jezevec10.com",
}, },
{"TETR.IO", {"TETR.IO",
"io tetrio tetr.io", "io tetrio tetr.io",
"game", "game",
"*Web-based, no mobile support | Single-player and multiplayer*\nFancy online battling Tetris game.", "Browser Game | Singleplayer/Multiplayer\nOr IO for short. It has a ranking system and custom game with many adjustable parameters. Also, it provides desktop clients for improved performances and no ads.\n[It seems that Safari cannot open this game.]",
"https://tetr.io", "https://tetr.io",
}, },
{"Nuketris", {"Nuketris",
"nuketris", "nuketris",
"game", "game",
"*Web-based | Single-player and multiplayer*\nA block stacker game with 1-vs-1 ranked mode and a few single-player modes. A PC is recommended for playing this game.", "*Browser Game | Singleplayer/Multiplayer\nA block stacker game with 1V1 ranked matches and basic single-player modes.",
"https://nuketris.herokuapp.com", "https://nuketris.herokuapp.com",
}, },
{"WWC", {"Worldwide Combos",
"wwc worldwidecombos", "wwc worldwidecombos",
"game", "game",
"*Web-based | Multiplayer*\nWorldwide Combos, a web-based worldwide 1-vs-1 battle Tetris game.", "Browser Game | Singleplayer/Multiplayer\nOr WWC for short. It has worldwide 1V1 ranked matches, recorded battles (which means that your opponent doesn't have to be a real person), many different rulesets, and bomb-handicapped garbage lines.",
"https://worldwidecombos.com", "https://worldwidecombos.com",
}, },
{"Tetris Friends", {"Tetris Friends",
"tf tetrisfriends notrisfoes", "tf tetrisfriends notrisfoes",
"game", "game",
"*Web-based, no mobile support | Single-player and multiplayer*\nA now-defunct web-based Tetris game; used to be a decent battle game. An unofficial private server known as Notris Foes exists.\nBuilt using Flash, which might require workarounds to play or cannot run at all on your devices.", "Browser Game | Singleplayer/Multiplayer\n or TF for short, a now-defunct official Tetris game. Used to be a popular game but now nobody plays it because the website was shut down. However an unofficial private server known as \"Notris Foes\" still exists and you will need to download desktop client for full experiences.",
}, },
{"tetris.com", {"tetris.com",
"tetris online official", "tetris online official",
"game", "game",
"The Tetris game on tetris.com. It only has one mode marathon, and you can control the game with your mouse.", "Browser Game | Singleplayer\nThe Tetris game on tetris.com. It only has one mode marathon, and you can control the game with your mouse.",
}, },
{"Tetris Gems", {"Tetris Gems",
"tetris online official gem", "tetris online official gem",
"game", "game",
"Another Tetris game from tetris.com. It has the gravity mechanism, and each game lasts for 1 minute. There are three kinds of gem blocks with different abilities.", "Browser Game | Singleplayer\nAnother Tetris game from tetris.com. It has the gravity mechanism, and each game lasts for 1 minute. There are three kinds of gem blocks with different abilities.",
}, },
{"Tetris Mind Bender", {"Tetris Mind Bender",
"tetris online official gem", "tetris online official gem",
"game", "game",
"Another Tetris game from tetris.com. It introduced \"Mind Bender\" minoes on the basis of marathon mode. Clearing a line with a Mind Bender mino will give you either a good or bad effect.", "Browser Game | Singleplayer\nAnother Tetris game from tetris.com. It introduced \"Mind Bender\" minoes on the basis of marathon mode. Clearing a line with a Mind Bender mino will give you either a good or bad effect.",
},
{"Techmino",
"techmino",
"game",
"Cross-Platform | Singleplayer/Multiplayer\nOr Tech for short. A block stacker game developed using LÖVE. It has many single-player modes and many customizable parameters, and online multiplayer modes are gradually being developed.",
},
{"Falling Lightblocks",
"fl fallinglightblocks",
"game",
"Browser Game/iOS/Android | Singleplayer/Multiplayer\n A cross-platform Tetris game that can be played in portrait and landscape modes. It has fixed DAS and line clear ARE. Has some customizable controls on mobile. Most of the game modes are designed based on NES classic Tetris, but there are some modern-ish modes. Battles are half turn-based, half real-time, and garbage cannot be buffered or canceled.",
"https://golfgl.de/lightblocks/",
},
{"Cambridge",
"cambridge",
"game",
"Cross-Platform | Singleplayer\n A Tetris game developed using LÖVE and is dedicated to creating a robust, easily customizable platform for creating new, custom game modes. Originally made by Joe Zeng, but Milla took over the development on 08/Oct/2020 starting from V0.1.5.\n — Tetris Wiki",
},
{"Nanamino",
"nanamino",
"game",
"Windows/Android | Singleplayer\nA developing fan game which has a interesting original rotation system.",
}, },
{"TGM", {"TGM",
"tgm tetrisgrandmaster tetristhegrandmaster", "tgm tetrisgrandmaster tetristhegrandmaster",
"game", "game",
"*Arcade | Single-player*\nTetris The Grand Master, an arcade Tetris series that can run on Microsoft Windows. Titles like S13 or GM come from this series.\n\nTGM3 is the most well-known game in this series.", "Arcade | Singleplayer/Local Multiplayer\nTetris The Grand Master, an arcade Tetris series. Titles like S13 and GM come from this series.\n\nTGM3 is the most well-known game in this series.",
}, },
{"DTET", {"DTET",
"dtet", "dtet",
"game", "game",
"*Windows | Single-player*\nA game based on TGM's Classic rule with 20G and a powerful rotation system. Decent controls, but has no customization other than control mappings. The game is a bit hard to find now and you might need to manually install required DLLs.", "Windows | Singleplayer\nA game based on TGM's Classic rule with 20G and a powerful rotation system. Decent controls, but has no customization other than control mappings. The game is a bit hard to find now and you may need to manually install required DLLs.",
}, },
{"Heboris", {"Heboris",
"hb heboris", "hb heboris",
"game", "game",
"*Windows*\nA game with Arcade-ish play style, simulates some modes of many Tetris games.", "Windows | Singleplayer\nA game with Arcade-ish play style, capable of simulating many modes of other Tetris games.",
}, },
{"Texmaster", {"Texmaster",
"txm texmaster", "txm texmaster",
"game", "game",
"*Windows | Single-player*\nA game with all modes from TGM which you can use to practice. Has better controls than actual TGM. The world rule is slightly different, however (eg, instant-lock soft drops, and slightly different kick tables)", "Windows | Singleplayer\nA game with all modes from TGM which you can use to practice. The world rule is slightly different, however (e.g. instant-lock soft drops and slightly different kick tables).",
},
{"Cambridge",
"cambridge",
"game",
"*Windows, macOS, Linux | Single-player*\nA Lua-based game engine dedicated to creating a robust, easily customizable platform for creating new, custom game modes. It was originally made by Joe Zeng, and starting with version 0.1.5 on October 8, 2020, Milla took over development of the game.\n--Tetris Wiki",
}, },
{"Tetris99", {"Tetris Effect",
"tec tetriseffectconnected",
"game",
"PS/Oculus Quest/Xbox/NS/Windows | Singleplayer/Multiplayer\nOr TE(C) for short. An official Tetris game with fancy graphics and soundtracks that react to your input. The basic version (without the word \"Connected\") only has singleplayer modes. The extended version, Tetris Effect Connected, features four online battle modes, Connected (VS), Zone Battle, Score Attack, and Classic Score Attack.",
},
{"Tetris 99",
"t99 tetris99", "t99 tetris99",
"game", "game",
"*Nintendo Switch | Multiplayer*\nA game famous for its 99-player battle royale mode and has many interesting strategies not present on traditional battle Tetris games. Also has limited single-player modes like Marathon and bot matches available as DLC.", "Nintendo Switch | Singleplayer/Multiplayer\nA game famous for its 99-player battle royale mode and has many interesting strategies not present on traditional battle Tetris games. Also has limited single-player modes like Marathon and bot matches available as DLC.",
}, },
{"Puyo Puyo Tetris", {"Puyo Puyo Tetris",
"ppt puyopuyotetris", "ppt puyopuyotetris",
"game", "game",
"*Multiple platforms | Single-player and multiplayer*\nA game that combines two games, Tetris and Puyo Puyo, and can battle between those two games. Has many modes for both single-player and online. The PC/Steam version has worse controls and horrible online experience, so it is not recommended.", "PS/NS/Xbox/Windows | Singleplayer/multiplayer\nA game that combines two games, Tetris and Puyo Puyo, and can battle between those two games. Has many modes for both single-player and online\n\n[The Steam PC version has worse controls and horrible online experience.]",
}, },
{"Tetris Online", {"Tetris Online",
"top tetrisonline", "top tetrisonline",
"game", "game",
"*Windows | Single-player and multiplayer*\nA now-defunct Japanese Tetris game with both online and single-player modes. Allows custom DAS/ARR but neither can be set to 0. Minor input delay. Private servers exist and is decent for new players to get started.", "Windows | Singleplayer/Multiplayer*\nA now-defunct Japanese Tetris game with both online and single-player modes. Allows custom DAS/ARR but neither can be set to 0. Minor input delay. Private servers do exist and is decent for new players to get started.",
}, },
{"Tetris Effect", {"Tetra Online",
"te tetriseffect", "TO tetraonline",
"game", "game",
"*PS4, Windows, Xbox | Single-player*\nFancy graphics and soundtrack that react to your actions. Not-so-good controls. You can have a go if you are into the visuals, but not exactly worth it if you are just trying to play some Tetris.", "Windows/macOS/Linux | Singleplayer/Multiplayer\nTO for short. A Tetris game developed by Dr Ocelot and Mine. The delays are AREs are intentionally set to high values, and players who get used to Tetris games with no delays may not get used to this game.\nThe game was removed from Steam on 09/Dec/2020 due to a DMCA notice filed by TTC.\nHowever, an offline build can still be downloaded on GitHub.",
}, "https://github.com/Juan-Cartes/Tetra-Offline/releases/tag/1.0",
{"Techmino",
"techmino",
"game",
"*Windows, macOS, Android, Linux, iOS/iPadOS | Single-player and multiplayer*\nA game with many modes and loads of customization. Low input delay, decent controls.",
}, },
{"Cultris II", {"Cultris II",
"c2 cultris2 cultrisii", "c2 cultris2 cultrisii",
"game", "game",
"*Windows, macOS, Linux | Single-player and multiplayer*\nA game focusing on speed. Has no hold and limited lockdown timer (like old school Tetris), but has customizable DAS/ARR. The main gimmick is its timer-based combos and emphasizes on speed, combo setups and digging.", "Windows/OS X | Singleplayer/Multiplayer\nC2 for short. Designed based on classic Tetris, Cultris II supports customizable DAS and ARR. The battle mode is focused on time-based combos, which challenges players speed, n-wide setups, and downstacking skills.\n[The Mac version was not being maintained for a long time. Any macOS build newer than macOS Catalina cannot run this game at all.]",
}, },
{"Nullpomino", {"Nullpomino",
"np nullpomino", "np nullpomino",
"game", "game",
"*Windows | Single-player and multiplayer*\nProfessional Tetris game with extreme room for customization. You can customize almost every aspect of the game. However, this is not a beginner-friendly game (you can get lost in the menus quite easily).", "Windows/macOS/Linux | Singleplayer/Multiplayer\nOr NP for short. A high-customizable professional Tetris game. Nearly every parameter in the game can be adjusted.\n[But the UI was outdated, and this game requires full-keyboard controls. New players may have some problems getting used to it. Also, it seems that macOS Monterey cannot run this game.]",
},
{"Touhoumino",
"touhoumino",
"game",
"*Windows | Single-player*\nA Nullpomino mod with elements from Touhou Project. It is fun to play but difficult. Recommended for players with at least half-decent skills otherwise you don't even know how you die",
},
{"Nanamino",
"nanamino",
"game",
"*Windows, Android | Single-player*\nDeveloping game, has a interesting rotation system",
}, },
{"Misamino", {"Misamino",
"misamino", "misamino",
"game", "game",
"*Windows | Single-player?*\nLocal 1-vs-1 game, mainly plays turn-based mode. You can write your own bot for it (though you need to learn its API if you do).\nMisamino is also the name of its built-in bot. Said bot is also the core for the Puyo Puyo Tetris bot, Zetris.", "Windows | Single-player\nLocal 1V1 game, mainly plays turn-based mode. You can write your own bot for it (though you need to learn its API if you do).\nMisamino is also the name of its built-in bot.",
}, },
{"Tetris Journey", {"Touhoumino",
"huanyouji tetrisjourney mobile phone", "touhoumino",
"game", "game",
"An official mobile Tetris game developed by Tencent (available only in China). It has level modes, battle modes, and some single-player modes. You can customize the sizes and positions of the virtual keys but you cannot adjust DAS or ARR.\n The battle mode lasts for 2 minutes and if both player did not top out, the one who sent more attacks wins." "Windows | Singleplayer\nA fan-made Tetris game, basically Nullpomino with elements from Touhou Project. The \"Spellcards\" from Touhou was introduced in the game, and you can only get bonus scores if you can reach the target score within the given period of time.\n[Recommended for players with at least half-decent skills otherwise you don't even know how you die.]",
}, },
{"Tetris Blitz", {"Tetris Blitz",
"blitz ea mobile phone", "blitz ea mobile phone",
"game", "game",
"A mobile Tetris game by Electronic Arts (EA). It has the gravity mechanism, and each game lasts for 2 minutes. A bunch of minoes fall down to the field at the beginning of the game, and you can enter the \"Frenzy\" mode by performing line clears continuously. There are many different power-ups available. Also, this game has no top-out mechanism. When an incoming block overlaps with existing blocks in the field, the top lines will be cleared automatically. \n\nThis game is no longer available since April 2020.", "iOS/Android | Singleplayer\nA mobile Tetris game by Electronic Arts (EA). It has the gravity mechanism, and each game lasts for 2 minutes. A bunch of minoes fall down to the field at the beginning of the game, and you can enter the \"Frenzy\" mode by performing line clears continuously. There are many different power-ups available. Also, this game has no top-out mechanism. When an incoming block overlaps with existing blocks in the field, the top lines will be cleared automatically. \n\nThis game is no longer available since April 2020.",
}, },
{"Tetris (EA)", {"Tetris (EA)",
"tetris ea galaxy universe cosmos mobile phone", "tetris ea galaxy universe cosmos mobile phone",
"game", "game",
"Another mobile Tetris game by EA. It has two control modes Swipe and One-Touch. It also has a Galaxy Mode besides the Marathon Mode (with gravity mechanism), and the goal of this mode is to clear all Galaxy minoes before the sequence runs out.\n\nThis game is no longer available since April 2020." "iOS/Android | Singleplayer/Multiplayer?\nAnother mobile Tetris game by EA. It has two control modes Swipe and One-Touch. It also has a Galaxy Mode besides the Marathon Mode (with gravity mechanism), and the goal of this mode is to clear all Galaxy minoes before the sequence runs out.\n\nThis game is no longer available since April 2020."
}, },
{"Tetris (N3TWORK)", {"Tetris (N3TWORK)",
"tetris n3twork mobile phone", "tetris n3twork mobile phone",
"game", "game",
"The latest mobile Tetris from N3TWORK Inc. It has a 3-minute ultra mode, a marathon mode and a 100-player Royale mode. The UI is great but its controls are not so good.", "iOS/Android | Singleplayer\nThe mobile Tetris game from N3TWORK Inc. It has a 3-minute ultra mode, a marathon mode and a 100-player Royale mode.\n[The UI is great but its controls are not so good.]",
}, },
{"Tetris Beat", {"Tetris Beat",
"tetris beat n3twork rhythm", "tetris beat n3twork rhythm",
"game", "game",
"A mobile Tetris game from N3TWORK. It has a \"Beat\" mode besides the Marathon mode, but in this game you only have drop the blocks in rhythm with the BGM. The effects are very heavy and the controls are not so good." "iOS | Singleplayer\nA mobile Tetris game from N3TWORK. It has a \"Beat\" mode besides the Marathon mode, but in this game you only have drop the blocks in rhythm with the BGM.\n[The effects are very heavy and the controls are not so good.]"
},
{"Tetris Journey",
"tetrisjourney mobile phone huanyouji",
"game",
"iOS/Android | Singleplayer\nAn official mobile Tetris game developed by Tencent (available exclusively in China). It has level modes, battle modes, and some single-player modes. You can customize the sizes and positions of the virtual keys but you cannot adjust DAS or ARR.\n The battle mode lasts for 2 minutes and if both player did not top out, the one who sent more attacks wins."
}, },
{"JJ Tetris", {"JJ Tetris",
"jjtetris", "jjtetris",
"game", "game",
"*Android | Multiplayer*\n(JJ块)\nA casual game on JJ Card Games (JJ棋牌). Portrait screen, low input delay, smooth controls. Customizable DAS/ARR and toggle-able 20G soft drop, limited control scheme customization. No hold nor B2B, no garbage buffer nor cancelling. Every attack sends at most 4 lines, combos are more powerful, otherwise similar to modern Tetris.", "Android | Multiplayer\n(JJ块)\nA casual game on JJ Card Games (JJ棋牌). Portrait screen, low input delay, smooth controls. Customizable DAS/ARR and toggle-able 20G soft drop, limited control scheme customization. No hold nor B2B, no garbage buffer nor cancelling. Every attack sends at most 4 lines, combos are more powerful, otherwise similar to modern Tetris.",
},
{"Falling lightblock",
"fl fallinglightblock",
"game",
"*Android, iOS, Web | Single-player and multiplayer*\nA game that supports many platforms. Has delays that cannot be adjusted. Can, to some extent, customize controls on mobile. Most of the modes are similar to classic Tetris, but modern-ish modes also exist. Battles are half-turn-based-half-real-time, and garbage cannot be buffered or cancelled.",
"https://golfgl.de/lightblocks/",
}, },
{"Huopin Tetris", {"Huopin Tetris",
"huopin qq", "huopin qq",
"game", "game",
"*Windows | Multiplayer*\n(火拼俄罗斯)\n\nThe Tetris game on Tencent Game Center, 12-wide board, DAS/ARR the same as your typing, 1 next, no hold. Can only send garbage through Tetris (sends 3 lines) and Triple (sends 2 lines). Garbage is checker-board-shaped and is very difficult to dig through.", "Windows | Multiplayer\n(火拼俄罗斯)\n\nThe Tetris game on Tencent Game Center, 12-wide board, DAS/ARR the same as your typing, 1 next, no hold. Can only send garbage through Tetris (sends 3 lines) and Triple (sends 2 lines). Garbage is checker-board-shaped and is nearly impossible to dig through.",
}, },
--Terms --Terms
@@ -744,7 +771,7 @@ return{
"For advanced players who want to play faster, the recommended values are DAS 4-6f (67-100ms), ARR 0f (0ms). (At 0ms ARR, pieces will instantly snap to the wall once you get past DAS.)\n\nThe ideal configuration strategy for advanced players is to minimize DAS while still being able to reliably control whether to tap or hold, and to set to ARR to 0 if possible, or as low as possible otherwise.", "For advanced players who want to play faster, the recommended values are DAS 4-6f (67-100ms), ARR 0f (0ms). (At 0ms ARR, pieces will instantly snap to the wall once you get past DAS.)\n\nThe ideal configuration strategy for advanced players is to minimize DAS while still being able to reliably control whether to tap or hold, and to set to ARR to 0 if possible, or as low as possible otherwise.",
}, },
{"DAS cut", {"DAS cut",
"dascut", "dascut dcd",
"term", "term",
"Techmino exclusive: in Techmino, the DAS timer can be cleared or discharged for a short time when the player starts to control a new piece. This can reduce the case where a piece instantly starts moving if spawned with a direction button held.\n\nOther games may have a similar feature but may function differently.", "Techmino exclusive: in Techmino, the DAS timer can be cleared or discharged for a short time when the player starts to control a new piece. This can reduce the case where a piece instantly starts moving if spawned with a direction button held.\n\nOther games may have a similar feature but may function differently.",
}, },
@@ -788,6 +815,16 @@ return{
"term", "term",
"Vibrate your finger on the controller to achieve faster sideways movement speed than holding it.\nIt is most commonly used on classic Tetris where DAS is rather slow. In most cases, you do not need to hypertap in modern Tetris games, because their DAS is often fast enough.", "Vibrate your finger on the controller to achieve faster sideways movement speed than holding it.\nIt is most commonly used on classic Tetris where DAS is rather slow. In most cases, you do not need to hypertap in modern Tetris games, because their DAS is often fast enough.",
}, },
{"Rolling",
"rolling",
"term",
"Another method of fast-tapping in high-gravity (around 1G) modes (with slow DAS/ARR setting).\nWhen you perform rolling, you fix the position of one hand and the controller, and then tap the back of the controller with fingers on your other hand repeatedly. This method allows even faster moving speeds than hypertapping (see \"Hypertapping\" for more)and requires much less effort.\nThis method was first discovered by Cheez-fish and he has once achieved a tapping speed of more than 20 Hz.",
},
{"Passthrough",
"passthrough pingthrough",
"term",
"",--TODO
},
{"Tetris OL attack", {"Tetris OL attack",
"top tetrisonlineattack", "top tetrisonlineattack",
"term", "term",
@@ -854,12 +891,12 @@ return{
"Any input device takes some time for the input to reach the game. This delay can range from a few milliseconds to a few dozen milliseconds.\nIf input delay is too long, the controls can feel uncomfortable.\nThis delay is often due to the performance of the hardware and software used, and often out of your control. Turn on performance mode (or turn off power saving mode) on your device, and turn on gaming mode on your monitor/TV (if you have one), may help reducing input delay.", "Any input device takes some time for the input to reach the game. This delay can range from a few milliseconds to a few dozen milliseconds.\nIf input delay is too long, the controls can feel uncomfortable.\nThis delay is often due to the performance of the hardware and software used, and often out of your control. Turn on performance mode (or turn off power saving mode) on your device, and turn on gaming mode on your monitor/TV (if you have one), may help reducing input delay.",
}, },
{"Cold Clear", {"Cold Clear",
"cc coldclear", "cc coldclear ai bot",
"term", "term",
"A Tetris bot. Originally built for Puyo Puyo Tetris, thus can be less powerful on Techmino.", "A Tetris bot. Originally built for Puyo Puyo Tetris, thus can be less powerful on Techmino.",
}, },
{"ZZZbot", {"ZZZbot",
"zzzbot", "zzzbot ai bot",
"term", "term",
"A Tetris bot. Built by the Chinese Tetris player 奏之章 (Zou Zhi Zhang) and has decent performance in many games", "A Tetris bot. Built by the Chinese Tetris player 奏之章 (Zou Zhi Zhang) and has decent performance in many games",
}, },
@@ -1021,9 +1058,9 @@ return{
--Savedata managing --Savedata managing
{"Console", {"Console",
"console cmd commamd minglinghang kongzhitai", "console cmd commamd minglinghang kongzhitai terminal",
"command", "command",
"Techmino has a console that enables debugging/advanced features.\nTo access the console, repeatedly tap the Techmino logo or press the C key on the keyboard on the main menu.\n\nCareless actions in the console may result in corrupting or losing saved data. Proceed at your own risk.", "Techmino has a console that enables debugging/advanced features.\nTo access the console, repeatedly tap (or click) the Techmino logo or press the C key on the keyboard on the main menu.\n\nCareless actions in the console may result in corrupting or losing saved data. Proceed at your own risk.",
}, },
{"Reset setting", {"Reset setting",
"reset setting", "reset setting",
@@ -1127,7 +1164,7 @@ return{
{"Jonas", {"Jonas",
"jonas", "jonas",
"name", "name",
"One of the top players in Classic Tetris.\nFour-times-in-a-row champion of CTWC.\n\n(1981-2021)", "(1981-2021) One of the top players in Classic Tetris.\nFour-times-in-a-row champion of CTWC.",
}, },
{"Joseph", {"Joseph",
"joseph", "joseph",
@@ -1167,10 +1204,10 @@ return{
{"TetroDictionary", {"TetroDictionary",
"zictionary tetrodictionary littlez", "zictionary tetrodictionary littlez",
"name", "name",
"(or Zictionary for short) The name of this dictionary!\nIt includes brief introductions on many common terms in Tetris.\nIt used to be a chatbot in our QQ group, which was used to answer new player's FAQs. The entries in the Tetrodictionary were also inherited from the database in the chatbot.", "(or Zictionary for short) The name of this dictionary!\nIt includes brief introductions on many common terms in Tetris.\nIt used to be a chatbot in our QQ group, which was used to answer new player's FAQs. The entries in the Tetrodictionary were also inherited from the database in the chatbot.\nThe contents in the TetroDictionary was adapted from a variety of sources such as Tetris Wiki and Hard Drop Wiki.",
}, },
{"MrZ", {"MrZ",
"mrz_26", "mrz_26 t026 t626",
"name", "name",
"Tetris Research community member, the author of Techmino.\nPersonal bests: Sprint 25.95 seconds, MPH Sprint 57 seconds, #8 on Jstris leaderboards, X rank on TETR.IO, cleared TGM3 (World rule, Shirase gold 1300).", "Tetris Research community member, the author of Techmino.\nPersonal bests: Sprint 25.95 seconds, MPH Sprint 57 seconds, #8 on Jstris leaderboards, X rank on TETR.IO, cleared TGM3 (World rule, Shirase gold 1300).",
"https://space.bilibili.com/225238922", "https://space.bilibili.com/225238922",
@@ -1179,7 +1216,7 @@ return{
{"Circu1ation", {"Circu1ation",
"circu1ation", "circu1ation",
"name", "name",
"One of the top players. First one to achieve sub-20 Sprint in China, X rank on TETR.IO.", "One of the top players. First one to achieve sub-20 40L Sprint in China, X rank on TETR.IO.",
"https://space.bilibili.com/557547205", "https://space.bilibili.com/557547205",
}, },
{"Farter", {"Farter",
@@ -1260,7 +1297,7 @@ return{
"https://space.bilibili.com/109356367", "https://space.bilibili.com/109356367",
}, },
{"ZXC", {"ZXC",
"zxc thtsod", "zxc thtsod flag ctf",
"name", "name",
"Also known as ThTsOd.\nTetris Research community member.\nA technical player.", "Also known as ThTsOd.\nTetris Research community member.\nA technical player.",
"https://space.bilibili.com/4610502", "https://space.bilibili.com/4610502",
@@ -1284,7 +1321,7 @@ return{
"https://space.bilibili.com/471341780", "https://space.bilibili.com/471341780",
}, },
{"思竣", {"思竣",
"sijun", "sijun acm oi",
"name", "name",
"(Sī Jùn)\n\nTetris Research community member.\nLots of mental computation power.", "(Sī Jùn)\n\nTetris Research community member.\nLots of mental computation power.",
"https://space.bilibili.com/403250559", "https://space.bilibili.com/403250559",

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