Compare commits

...

242 Commits

Author SHA1 Message Date
MrZ_26
d38ff06262 修万圣节日期判定 2024-11-01 10:57:44 +08:00
MrZ_26
45a4b10d11 修关闭节日主题时特定条件无法启动 2024-11-01 03:26:37 +08:00
MrZ_26
fe12f397cc 修复皮肤设置的初始值相关 2024-11-01 00:40:27 +08:00
MrZ_26
801f67b194 Revert "fixed default skin not showing up"
This reverts commit 1cf3d101aa.
2024-11-01 00:37:35 +08:00
MrZ_26
e331c8f446 .gitignore删除一个不用的文件夹 2024-11-01 00:35:51 +08:00
MrZ_26
51897584a7 调整一个tip说法 2024-11-01 00:35:51 +08:00
Fab052
1cf3d101aa fixed default skin not showing up 2024-10-31 16:54:58 +01:00
Fab052
1830e849d8 better 3D effect (#1166) 2024-10-30 00:14:28 +08:00
Fab052
e3f246aa00 removed missing skin from main.lua (#1165) 2024-10-30 00:14:06 +08:00
Fab052
1eb679cf24 Reorganized the skins + Added "Arcade" and "Cardboard" skins (#1164) 2024-10-29 08:24:36 +08:00
Fuwuwuwu
1963dc9fb9 Fix the issue of AI passing Hold parameters in custom mode (#1163)
* 自定义游戏传递hold开关的问题

表现:即使holdCount为0,CC依旧会开启hold,导致无法预期运行,场地逐渐混乱
原因:传递了错误的类型(boolean->number)

* 非正常hold模式时,AI不启用hold

相关问题#1106
2024-10-29 08:24:10 +08:00
SweetSea
396293c8af Custom image from clipboard (#1157)
* Update how text renders in button and key

* Add a placeholder

* Add actual code and language entry

* Sort buttons

* Alter to keep original behaviour

* I HAVE OCD

* Add back legacy behaviour
2024-10-26 23:44:31 +08:00
Imple Lee
fbf6e910a3 Fix a wierd bug (#1161)
Steps to reproduce:
1. reset everything in custom game
2. restart the game
3. go to the custom game scene
4. go to the custom sequence scene and set a new sequence
5. leave the scene and go to the custom game scene
6. begin to clear

Unintended behavior:
The sequence is still the default old sequence (7-bag)

Intended behavior:
The sequence is the newly set sequence

Rationale:
The bug was introduced by #1139.
It deleted a line of `apply_locals()` which I wrongly believed that the custom modes will execute initialize() anyway, but it turned out initialization is only executed during the start of the whole game.
The patch is simple: just add back the deleted line
2024-10-18 00:45:37 +08:00
MrZ_26
9e4e861c32 继续加赞助名单 2024-10-14 23:51:46 +08:00
MrZ_26
d0b99a16c9 修单机录像播不出 2024-10-14 23:36:31 +08:00
MrZ_26
347e81c11c 更新赞助名单 2024-10-14 23:15:40 +08:00
MrZ_26
6b2a376dfe legals文件添加discord-rpc.dll的说明 2024-10-13 00:46:11 +08:00
MrZ_26
51e0ab7c48 赞助名单更新 2024-10-02 10:31:51 +08:00
MrZ_26
87fd26ab89 赞助名单更新 2024-09-29 20:14:36 +08:00
MrZ_26
0b1cee99bd 尝试添加一个paypal赞助按钮 2024-09-29 19:14:54 +08:00
MrZ_26
4768df6867 修discordRPC加载失败会崩溃 2024-09-27 03:50:12 +08:00
MrZ_26
423d502aa4 添加discordRPC支持 2024-09-26 02:29:24 +08:00
MrZ_26
a74e9033b3 添加500star贺图到仓库readme 2024-09-13 19:08:20 +08:00
SweetSea
dc6b7de15f Fix multiplayer chat (#1154)
* Fix multiplayer chat

* Update wrapping text param.

* Update net_game.lua
2024-09-11 20:31:41 +08:00
Imple Lee
74f67d0216 fix macos portable ci (#1152)
* use love 11.5 for macos portable

* use xcode 15.3
2024-09-04 17:03:16 +08:00
MrZ_26
d47f073d53 补irs打断的设置文本
补更新历史
2024-09-03 21:21:22 +08:00
MrZ_26
7407911914 Merge Electra's enhanced IRS 2024-09-03 21:12:32 +08:00
MrZ_26
9672a4fe57 修之前删goto的时候导致hisPool算法爆炸(也不知道怎么爆的,不管了) 2024-09-03 20:57:05 +08:00
Imple Lee
6c6ff26586 fix auto test: use appimage love instead of tar gz (#1141)
* use appimage love instead of tar gz

* install libfuse2 to run appimage

* replace deprecated `GabrielBB/xvfb-action`
2024-08-30 05:34:39 +08:00
Imple Lee
ca6f701084 Fix auto test (#1140)
* remove unnecessary space

* restart automatic test
2024-08-26 12:13:19 +08:00
Imple Lee
5793b7ca38 fix custom games (#1139)
Now you can `play custom_` without going through the `customGame` scene
2024-08-26 11:20:48 +08:00
MrZ_26
dee6ba95f2 修播放录像时初始化流程没重置暂停次数 close #1131 2024-08-20 01:28:39 +08:00
呵呵です
67aef1dbe3 更新tip里的40l世界纪录 close #1136 (#1137) 2024-08-20 01:26:37 +08:00
MrZ_26
90f41a20a3 整理ws模块代码 2024-08-13 21:54:11 +08:00
MrZ_26
5f5dd48ee8 修直接写在模式的env里的特殊事件不会被加载 2024-08-12 12:04:29 +08:00
MrZ_26
a8e0574f44 规则包加载流程优化 2024-08-11 13:18:36 +08:00
MrZ_26
40f148b6b3 更新赞助名单 2024-08-11 13:12:34 +08:00
MrZ_26
0eb37666f8 更新历史和版本号 2024-08-10 19:03:13 +08:00
MrZ_26
b73a653332 Merge branch 'eventsystem2' 2024-08-10 18:55:05 +08:00
MrZ_26
49f1b747b2 尝试修复新的事件系统允许自由参数数量导致网络卡顿情况且录像流最后的事件是攻击时frameRun有机会超过stream内数据导致玩家操作冻结(wtf so complicated, thank you Electra!) 2024-08-10 18:51:41 +08:00
Electra
2c75f0bc9c Simple solution? 2024-08-10 04:47:35 -04:00
MrZ_26
97e17edfae 继续修新的事件系统问题 2024-08-10 16:05:25 +08:00
MrZ_26
f7e4e47466 再修新的事件系统问题 2024-08-10 15:09:27 +08:00
MrZ_26
8779abef9a 修新的事件系统问题 2024-08-10 14:42:18 +08:00
MrZ_26
4d1caa7fe0 重构玩家交互事件系统,尝试支持可通过网络传递的自定义事件 2024-08-10 12:55:58 +08:00
MrZ_26
78f3c31db1 联网房间内的准备按钮不再会试图将自己设置为Gamer状态(已经是了) 2024-08-09 05:23:38 +08:00
MrZ_26
3c852f17a0 补翻译说明 2024-08-09 01:12:49 +08:00
MrZ_26
8737a00b44 补越南语翻译 2024-08-09 01:08:25 +08:00
MrZ_26
fff2c49f2e 补越南语翻译 2024-08-09 00:03:13 +08:00
MrZ_26
35c19a4d50 spinren加一些注释的代码 2024-08-08 23:43:01 +08:00
MrZ_26
137e707c63 删除ffa按钮
tech league改名galaxim,调整一些联网菜单文本
2024-08-08 23:42:41 +08:00
MrZ_26
d2e9439e38 更新版本号 2024-08-08 23:06:38 +08:00
MrZ_26
39cd7e4c1a 调整服务器url 2024-08-08 23:06:07 +08:00
MrZ_26
57f2b9541d 修几个词条不能通过拼音搜索 2024-08-08 16:14:53 +08:00
MrZ_26
9d4065a05a 修复创建联网房间时有好几个选项并没有生效 2024-08-08 16:08:51 +08:00
MrZ_26
424a3b3bee 公告页面信息更详细一点 2024-08-07 13:52:46 +08:00
MrZ_26
de3e1fcdc7 实现公告场景的功能 2024-08-07 13:25:03 +08:00
MrZ_26
59f390de93 尝试启用公告功能 2024-08-07 12:54:06 +08:00
MrZ_26
26287c8f35 更新历史和版本号 2024-08-07 12:19:53 +08:00
MrZ_26
24d552ba2b Merge branch 'ci-web-revive' 2024-08-07 12:11:32 +08:00
MrZ_26
f1a08ca325 更新历史和版本号 2024-08-03 13:05:09 +08:00
MrZ_26
28ec031afa 修time_short函数一个错误 2024-08-03 03:18:31 +08:00
MrZ_26
d9f606c56f 修地图布局
修spinren模式翻译
调整一些模式的外框形状
2024-08-03 01:45:37 +08:00
MrZ_26
de4b73cf83 spinren模式结束时显示的文本会根据是否到10行变化 2024-08-02 23:22:16 +08:00
MrZ_26
beff0c9d99 调整翻译文本,所有模式的“难度”一栏更统一 2024-08-02 23:04:49 +08:00
MrZ_26
4a76a929f5 更新spinren模式的越南语翻译 2024-08-02 22:58:32 +08:00
MrZ_26
aa981160e8 spinren少出两行防止一直响警报 2024-08-02 22:49:51 +08:00
MrZ_26
24a95a36d0 微调地图布局 2024-08-02 22:41:05 +08:00
MrZ_26
583819e8c4 优化spin模式并加到大地图,微调相关区域布局 2024-08-02 22:37:39 +08:00
Imple Lee
4c193efd41 add a new mode: spin ren (#1129)
* add a new mode: spin ren

* remove some bad cases; rework of the weights

* fix lines

* add different orientation for 323

* harder ranking

* Fix the map to sijun's mode

This mode can be used as big-bang!
2024-08-02 21:53:10 +08:00
MrZ_26
8208e7b132 修第三季度音乐名称打错 2024-08-01 21:47:25 +08:00
MrZ_26
7e2bd7b08d 词典里的“全消四”改成“全程消四” 2024-07-24 19:07:54 +08:00
MrZ_26
29258e2a35 添加edm节日 2024-07-14 04:10:26 +08:00
MrZ_26
9e028bc907 微调c4w普通模式评分标准 2024-07-04 02:07:00 +08:00
MrZ_26
75ae05ffc9 c4w普通的计分完全改为竞速 close #1126
改版本号和更新历史
2024-07-03 17:55:04 +08:00
MrZ_26
aec0b91039 补充更新历史 2024-07-03 16:29:00 +08:00
MrZ_26
34df33c9b0 readme更新官网链接,加一个techmino hub链接 2024-07-02 17:55:57 +08:00
MrZ_26
1674902727 修字体一个小问题 2024-07-01 23:14:13 +08:00
C₂₉H₂₅N₃O₅
4fd497fe9f Used more mathematically correct symbols for app calc (#1127) 2024-07-01 22:51:14 +08:00
MrZ_26
f01594b4d1 修背景锁定处理顺序问题 close #1122
Co-authored-by: SweetSea <106439598+SweetSea-ButImNotSweet@users.noreply.github.com>
2024-06-17 10:18:23 +08:00
MrZ_26
6f74693811 更新tip里的40l世界纪录 close #1121 2024-06-14 08:18:43 +08:00
MrZ_26
94d2af685c 消四挖掘模式非挖掘的消四越多会导致评分降低 2024-06-11 17:30:45 +08:00
MrZ_26
0b7c2fa59b 补两个中文文本
修改更新历史
2024-06-06 17:59:07 +08:00
MrZ_26
137f9a0d55 修两个消四模式的评级不等号错误 2024-06-06 17:38:05 +08:00
MrZ_26
23d8a34991 修highestGrade文本小问题 2024-06-03 10:15:34 +08:00
MrZ_26
3f0d4d3cdf 更新版本号和更新历史 2024-05-22 01:19:20 +08:00
MrZ_26
ac8f62de03 微调readme 2024-05-22 01:19:20 +08:00
C₂₉H₂₅N₃O₅
e518d91237 Font tweaks (#1116)
- Changed the glyph for the numeral `3` from “round-top” to “flat top” to increase eligibility
- Removed the serif on the bottom of the numeral “1” for cleaner looks
2024-05-20 22:44:35 +08:00
MrZ_26
c054a76504 修birs英文词条 2024-05-18 11:47:49 +08:00
MrZ_26
d3406c4a48 修一个TABLE.reverse用法错误 2024-05-13 15:12:23 +08:00
MrZ_26
3c0266ec02 补充更新历史 2024-04-29 11:16:56 +08:00
MrZ_26
4f4683d06c 更强大的ARS_Z fix #1094 2024-04-29 11:05:15 +08:00
MrZ_26
3441002758 c4w普通的模式说明改为和100l相同 close #1082 2024-04-29 10:45:37 +08:00
KonSola5
c2bf291029 Implementation of some of my English suggestions (#1104)
* Update lang_en.lua

* Some small changes

* "Block -> Piece" and some extras
2024-04-24 18:15:45 +08:00
MrZ_26
d70b04e7fb 优化neuro立绘动画和俊达萌身高 2024-04-24 05:14:27 +08:00
Petalzu
a979b6dbe6 Add neuro voicepack (#1109)
* update neuro

* change image & fix delay

change the image of neuro and fix delay between two vocal

* add vocal

* change vocal&image
2024-04-24 04:11:18 +08:00
MrZ_26
523b7ce443 给SRS+和SRS-X加一点注释说明 2024-04-24 04:08:37 +08:00
NOT_A_ROBOT
4e74c9456f Fix decay rate issues in Master Graded, close #1092 (#1113) 2024-04-24 04:06:41 +08:00
Imple Lee
7719b74b09 fix text in dig quad 10l (#1103)
fix #1102
2024-04-24 03:38:15 +08:00
Nguyễn Quốc Hưng
9d3aeeb50b Update Vietnamese translation and fix cannot entering Zictionary's manual (#1111)
* Fix cannot enter manual

* Update Zictionary

* Update dict_vi.lua

* Update dict_vi
2024-04-24 03:37:56 +08:00
MrZ_26
0459248556 修文本小bug 2024-04-16 00:16:20 +08:00
MrZ_26
288a6ba80a 微调readme 2024-04-07 04:56:09 +08:00
MrZ_26
97eee39266 调整词典中的BiRS词条(日文和越南文待其他人修改)
微调注释
2024-04-06 19:15:09 +08:00
MrZ_26
8c4446edac 修复拼花模式段位计算的1行偏差
补充拼花模式说明文本 close #1099
2024-03-03 14:27:36 +08:00
MrZ_26
e49f8d428a 修主菜单按钮问题 2024-03-01 09:10:37 +08:00
MrZ_26
6ece5c5f1f 版本推进 2024-02-29 15:48:48 +08:00
MrZ_26
b75359472b 主菜单联网游戏按钮和公告按钮改为不可用 2024-02-29 15:48:34 +08:00
MrZ_26
1c190d9544 暂停界面可以按自定义的重开键 2024-02-29 15:41:56 +08:00
SweetSea
1859e5bb96 Fix Disable theme and Lock B.G.'s weird behaviors (#1091) 2024-02-22 11:07:41 +08:00
Nguyễn Quốc Hưng
7e1a5805b3 Fix ASC and ASC+ (#1089) 2024-02-08 19:43:38 +08:00
Nguyễn Quốc Hưng
6c63fe0ab7 Cleaning-up Vietnamese translation (#1086)
* Cleaning-up the translation

* Update app_piano.lua

* Update lang_vi.lua

* Update TVN's link

I feel bruh because I should at least realize that TVN server lost it's Tier-3 boost a long time ago

* Small changes in dict.lua

* Small update in dict_vi.lua

* Remove a redundant "do-end"
2024-02-08 19:42:36 +08:00
C₂₉H₂₅N₃O₅
85fc9f660e Symbol update and more (#1085)
* Symbol update and more

- Added a few more icon symbols
- Added symbols for playing cards
- Added a note for translators in the English Zictionary
2024-01-22 00:03:08 +08:00
C6H12O6 + NaCl + H2O
2d12ab3a19 Log viewer (#1076)
* Add scene

* Modify app_console.lua

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

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

* Small update theme.lua

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

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

* Shorten theme code, and edit the UI

* Add lock BG

* Add VI translation

* Fullscreen patch

* Modify UI

* Small change

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

* Small changes

* Update lang_vi.lua

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

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

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

* localize various settings in custom game

* rename fields to lower cases

* fix many bugs

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

* Pull from Shard Nguyen

* Change a mode's description

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

* Update dict_vi.lua

* Update Zictionary (mostly fix broken sentences)

* Update game's translation

* Update modes

* Update dictionary

* Minor change

* Update stat translation

* Minor change

* Minor change

* Update lang_vi.lua

* Minor changes

* Fix misunderstanding help text

* Update scene

* Rename _utf8lower to needLowerUTF8

* Add titleLowered and keywordsLowered

* Fix stuck color again

---------

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

* Add note about vectorized modeicons

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

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

* Replace all loop, make a text object list

* Add auto-garbage cleaner and remove 2 unnecessary variable

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

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

* Replace all loop, make a text object list

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

* Update manual_vi.txt

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

16
.github/500stars/README.md vendored Normal file
View File

@@ -0,0 +1,16 @@
# Techmino - 500-star Banner
Created by NOT_A_ROBOT
13 September, 2024
**Don't forget to attribute me when using this.**
The image already includes sufficient attribution, so if you just don't crop that out, you shouldn't need to explicitly mention them.
## Attribution
Created by NOT_A_ROBOT
GitHub logo (on Z-character's screen) by GitHub
Background (space stars) originally by MrZ, ported to JS by NOT_A_ROBOT for rendering
Block skin (featured in the background) by Scf, slightly modified to make it darker
Z-character drawn by 葉枭, designed by MrZ
Techmino by MrZ and many contributors
Techmino is fun! https://github.com/26F-Studio/Techmino

BIN
.github/500stars/exported.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Binary file not shown.

BIN
.github/donate/donate.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -39,7 +39,9 @@ jobs:
os.execute('echo "version-string=' .. version.string:gsub("%a", "") .. '" >> $GITHUB_OUTPUT')
os.execute('echo "version-code=' .. tostring(version.code) .. '" >> $GITHUB_OUTPUT')
local note = require 'parts.updateLog'
local f = io.open("updateLog.txt", 'r')
local note = f:read("*a")
f:close()
local p1 = note:find("\n%d") + 1
local p2 = note:find("\n", p1) - 1
os.execute('echo "update-title=' .. note:sub(p1, p2) .. '" >> $GITHUB_OUTPUT')
@@ -90,7 +92,7 @@ jobs:
- name: Build core love package
uses: love-actions/love-actions-core@v1
with:
build-list: ./media/ ./parts/ ./Zframework/ ./conf.lua ./main.lua ./version.lua
build-list: ./media/ ./parts/ ./Zframework/ ./conf.lua ./main.lua ./version.lua ./legals.md ./license.txt
package-path: ${{ env.CORE_LOVE_PACKAGE_PATH }}
- name: Upload core love package
uses: actions/upload-artifact@v3
@@ -128,7 +130,7 @@ jobs:
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
auto-test:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: build-core
steps:
- uses: actions/checkout@v3
@@ -139,6 +141,25 @@ jobs:
with:
font-path: ./parts/fonts/proportional.otf
language-folder: ./parts/language
- name: Download core love package
uses: actions/download-artifact@v3
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Download love
shell: bash
run: |
curl -OL --retry 5 https://github.com/love2d/love/releases/download/11.4/love-11.4-x86_64.AppImage
chmod +x love-11.4-x86_64.AppImage
- name: Prepare PulseAudio and AppImage
shell: bash
run: |
sudo apt-get update
sudo apt-get install pulseaudio pulseaudio-utils pavucontrol alsa-oss alsa-utils libfuse2 -y
- name: Run automated test
uses: coactions/setup-xvfb@v1
with:
run: |
./love-11.4-x86_64.AppImage ${{ env.CORE_LOVE_PACKAGE_PATH }} --test
build-android:
runs-on: ubuntu-latest
@@ -213,87 +234,6 @@ jobs:
name: ${{ needs.get-info.outputs.update-title }}
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
build-ios:
runs-on: macos-latest
needs: [get-info, build-core, auto-test]
if: github.event_name != 'pull_request'
env:
OUTPUT_FOLDER: ./build
RELEASE_FOLDER: ./release
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Process app name
id: process-app-name
shell: python3 {0}
run: |
import os
import re
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
f.write('bundle-id=org.26f-studio.techmino\n')
f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\n')
- name: Download core love package
uses: actions/download-artifact@v3
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Download ColdClear
uses: ./.github/actions/get-cc
with:
platform: iOS
dir: ./ColdClear
- name: Build iOS packages
id: build-packages
uses: love-actions/love-actions-ios@v1
with:
app-name: ${{ needs.get-info.outputs.app-name }}
bundle-id: ${{ steps.process-app-name.outputs.bundle-id }}
copyright: "Copyright © 2019-2023 26F-Studio. Some Rights Reserved."
icon-path: ./.github/build/iOS/${{ env.BUILD_TYPE }}/icon
love-patch: ./.github/build/iOS/love.patch
love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }}
libs-path: ./ColdClear/arm64/
product-name: ${{ steps.process-app-name.outputs.product-name }}
version-string: ${{ needs.get-info.outputs.version-string }}
output-folder: ${{ env.OUTPUT_FOLDER }}
apple-development-base64: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_BASE64 }}
apple-development-password: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_PWD }}
api-key: ${{ secrets.APPLE_API_KEY }}
api-key-id: ${{ secrets.APPLE_API_KEY_ID }}
api-issuer-id: ${{ secrets.APPLE_API_ISSUER_ID }}
team-id: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }}
apple-id: ${{ secrets.APPLE_APPLE_ID }}
external-test: ${{ startsWith(github.ref, 'refs/tags/pre') }}
store-release: ${{ startsWith(github.ref, 'refs/tags/v') }}
- name: Upload logs artifact
uses: actions/upload-artifact@v3
with:
name: ${{ needs.get-info.outputs.base-name }}_iOS_logs
path: |
${{ env.OUTPUT_FOLDER }}/DistributionSummary.plist
${{ env.OUTPUT_FOLDER }}/ExportOptions.plist
${{ env.OUTPUT_FOLDER }}/Packaging.log
- name: Upload ipa artifact
uses: actions/upload-artifact@v3
with:
name: ${{ needs.get-info.outputs.base-name }}_iOS_ipa
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.ipa
- name: Prepare for release
if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }}
shell: bash
run: |
mkdir -p ${{ env.RELEASE_FOLDER }}
cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.ipa ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_iOS.ipa
- name: Upload release
if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }}
uses: ncipollo/release-action@v1
with:
allowUpdates: true
artifacts: ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_iOS.ipa
body: ${{ needs.get-info.outputs.update-note }}
name: ${{ needs.get-info.outputs.update-title }}
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
build-linux:
runs-on: ubuntu-latest
needs: [get-info, build-core, auto-test]
@@ -377,90 +317,6 @@ jobs:
name: ${{ needs.get-info.outputs.update-title }}
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
build-macos-appstore:
runs-on: macos-latest
needs: [get-info, build-core, auto-test]
if: github.event_name != 'pull_request'
env:
OUTPUT_FOLDER: ./build
RELEASE_FOLDER: ./release
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Process app name
id: process-app-name
shell: python3 {0}
run: |
import os
import re
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
f.write('bundle-id=org.26f-studio.techmino\n')
f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\n')
- name: Download core love package
uses: actions/download-artifact@v3
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Download ColdClear
uses: ./.github/actions/get-cc
with:
platform: macOS
dir: ./ColdClear
- name: Process ColdClear
shell: bash
run: |
rm ./ColdClear/universal/libcold_clear.a
- name: Build macOS packages
id: build-packages
uses: love-actions/love-actions-macos-appstore@v1
with:
app-name: ${{ needs.get-info.outputs.app-name }}
bundle-id: ${{ steps.process-app-name.outputs.bundle-id }}
copyright: "Copyright © 2019-2023 26F-Studio. Some Rights Reserved."
icon-path: ./.github/build/macOS/${{ env.BUILD_TYPE }}/icon.icns
love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }}
libs-path: ./ColdClear/universal/
product-name: ${{ steps.process-app-name.outputs.product-name }}
version-string: ${{ needs.get-info.outputs.version-string }}
output-folder: ${{ env.OUTPUT_FOLDER }}
apple-development-base64: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_BASE64 }}
apple-development-password: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_PWD }}
api-key: ${{ secrets.APPLE_API_KEY }}
api-key-id: ${{ secrets.APPLE_API_KEY_ID }}
api-issuer-id: ${{ secrets.APPLE_API_ISSUER_ID }}
team-id: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }}
apple-id: ${{ secrets.APPLE_APPLE_ID }}
external-test: ${{ startsWith(github.ref, 'refs/tags/pre') }}
store-release: ${{ startsWith(github.ref, 'refs/tags/v') }}
- name: Upload logs artifact
uses: actions/upload-artifact@v3
with:
name: ${{ needs.get-info.outputs.base-name }}_macOS_appstore_logs
path: |
${{ env.OUTPUT_FOLDER }}/DistributionSummary.plist
${{ env.OUTPUT_FOLDER }}/ExportOptions.plist
${{ env.OUTPUT_FOLDER }}/Packaging.log
- name: Upload pkg artifact
uses: actions/upload-artifact@v3
with:
name: ${{ needs.get-info.outputs.base-name }}_macOS_appstore_pkg
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.pkg
- name: Prepare for release
if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }}
shell: bash
run: |
mkdir -p ${{ env.RELEASE_FOLDER }}
cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.pkg ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_macOS_appstore.pkg
- name: Upload release
if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }}
uses: ncipollo/release-action@v1
with:
allowUpdates: true
artifacts: ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_macOS_appstore.pkg
body: ${{ needs.get-info.outputs.update-note }}
name: ${{ needs.get-info.outputs.update-title }}
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
build-macos-portable:
runs-on: macos-latest
needs: [get-info, build-core, auto-test]
@@ -494,6 +350,12 @@ jobs:
shell: bash
run: |
rm ./ColdClear/universal/libcold_clear.a
- name: Use Xcode 15.3
# Xcode 15.4 segfaults
# see https://forums.developer.apple.com/forums/thread/757398
uses: mobiledevops/xcode-select-version-action@v1
with:
xcode-select-version: 15.3
- name: Build macOS packages
id: build-packages
uses: love-actions/love-actions-macos-portable@v1
@@ -502,6 +364,7 @@ jobs:
bundle-id: ${{ steps.process-app-name.outputs.bundle-id }}
copyright: "Copyright © 2019-2023 26F-Studio. Some Rights Reserved."
icon-path: ./.github/build/macOS/${{ env.BUILD_TYPE }}/icon.icns
love-ref: "11.5"
love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }}
libs-path: ./ColdClear/universal/
product-name: ${{ steps.process-app-name.outputs.product-name }}
@@ -632,8 +495,8 @@ jobs:
icon-path: ./.github/build/windows/${{ env.BUILD_TYPE }}/icon.ico
rc-path: ./.github/build/windows/${{ env.BUILD_TYPE }}/template.rc
love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }}
extra-assets-x86: ./ColdClear/x86/CCloader.dll ./ColdClear/x86/cold_clear.dll
extra-assets-x64: ./ColdClear/x64/CCloader.dll ./ColdClear/x64/cold_clear.dll
extra-assets-x86: ./ColdClear/x86/CCloader.dll ./ColdClear/x86/cold_clear.dll ./.github/build/extraLibs/Windows_x64/discord-rpc.dll
extra-assets-x64: ./ColdClear/x64/CCloader.dll ./ColdClear/x64/cold_clear.dll ./.github/build/extraLibs/Windows_x86/discord-rpc.dll
product-name: ${{ steps.process-app-name.outputs.product-name }}
app-id: ${{ secrets.WINDOWS_APP_ID }}
project-website: https://www.studio26f.org/
@@ -684,9 +547,7 @@ jobs:
auto-test,
build-core,
build-android,
build-ios,
build-linux,
build-macos-appstore,
build-macos-portable,
build-web,
build-windows,
@@ -719,6 +580,6 @@ jobs:
"fields":[
{"name":"Version","value":"${{ needs.get-info.outputs.version-string }}","inline": true},
{"name":"Package Name","value":"${{ needs.get-info.outputs.base-name }}","inline": true},
{"name":"Status","value":"**Automatic Test:** ${{ needs.auto-test.result }}\n**Core:** ${{ needs.build-core.result }}\n**Android:** ${{ needs.build-android.result }}\n**iOS:** ${{ needs.build-ios.result }}\n**Linux:** ${{ needs.build-linux.result }}\n**macOS App Store:** ${{ needs.build-macos-appstore.result }}\n**macOS portable:** ${{ needs.build-macos-portable.result }}\n**Windows:** ${{ needs.build-windows.result }}"}
{"name":"Status","value":"**Automatic Test:** ${{ needs.auto-test.result }}\n**Core:** ${{ needs.build-core.result }}\n**Android:** ${{ needs.build-android.result }}\n**Linux:** ${{ needs.build-linux.result }}\n**macOS portable:** ${{ needs.build-macos-portable.result }}\n**Windows:** ${{ needs.build-windows.result }}"}
]
}'

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
.vscode
libAndroid
*.ini
.DS_Store
Thumbs.db

3
.gitmodules vendored
View File

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

Submodule Zframework deleted from c498582a8c

57
Zframework/background.lua Normal file
View File

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

350
Zframework/bgm.lua Normal file
View File

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

366
Zframework/bitop.lua Normal file
View File

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

235
Zframework/color.lua Normal file
View File

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

48
Zframework/diacritics.txt Normal file
View File

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

112
Zframework/file.lua Normal file
View File

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

59
Zframework/font.lua Normal file
View File

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

184
Zframework/gcExtend.lua Normal file
View File

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

191
Zframework/http.lua Normal file
View File

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

25
Zframework/image.lua Normal file
View File

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

934
Zframework/init.lua Normal file
View File

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

340
Zframework/json.lua Normal file
View File

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

57
Zframework/languages.lua Normal file
View File

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

20
Zframework/log.lua Normal file
View File

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

26
Zframework/lowcaser.txt Normal file
View File

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

88
Zframework/mathExtend.lua Normal file
View File

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

153
Zframework/message.lua Normal file
View File

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

157
Zframework/profile.lua Normal file
View File

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

47
Zframework/require.lua Normal file
View File

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

223
Zframework/scene.lua Normal file
View File

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

73
Zframework/screen.lua Normal file
View File

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

169
Zframework/sfx.lua Normal file
View File

@@ -0,0 +1,169 @@
local type,rem=type,table.remove
local floor,rnd=math.floor,math.random
local sfxList={}
local packSetting={}
local Sources={}
local volume=1
local stereo=1
local noteVal={
C=1,c=1,
D=3,d=3,
E=5,e=5,
F=6,f=6,
G=8,g=8,
A=10,a=10,
B=12,b=12,
}
local noteName={'C','C#','D','D#','E','F','F#','G','G#','A','A#','B'}
local function _getTuneHeight(tune)
local octave=tonumber(tune:sub(-1,-1))
if octave then
local tuneHeight=noteVal[tune:sub(1,1)]
if tuneHeight then
tuneHeight=tuneHeight+(octave-1)*12
local s=tune:sub(2,2)
if s=='s' or s=='#' then
tuneHeight=tuneHeight+1
elseif s=='f' or s=='b' then
tuneHeight=tuneHeight-1
end
return tuneHeight
end
end
end
local SFX={}
function SFX.init(list)
assert(type(list)=='table',"Initialize SFX lib with a list of filenames!")
for i=1,#list do table.insert(sfxList,list[i]) end
end
function SFX.load(path)
local c=0
local missing=0
for i=1,#sfxList do
local fullPath=path..sfxList[i]..'.ogg'
if love.filesystem.getInfo(fullPath) then
if Sources[sfxList[i]] then
for j=1,#Sources[sfxList[i]] do
Sources[sfxList[i]][j]:release()
end
end
Sources[sfxList[i]]={love.audio.newSource(fullPath,'static')}
c=c+1
else
LOG("No SFX: "..sfxList[i]..'.ogg',.1)
missing=missing+1
end
end
LOG(c.."/"..#sfxList.." SFX files loaded")
LOG(missing.." SFX files missing")
if missing>0 then
MES.new('info',missing.." SFX files missing")
end
collectgarbage()
end
function SFX.loadSample(pack)
assert(type(pack)=='table',"Usage: SFX.loadsample([table])")
assert(pack.name,"No field: name")
assert(pack.path,"No field: path")
local num=1
while love.filesystem.getInfo(pack.path..'/'..num..'.ogg') do
Sources[pack.name..num]={love.audio.newSource(pack.path..'/'..num..'.ogg','static')}
num=num+1
end
local base=(_getTuneHeight(pack.base) or 37)-1
local top=base+num-1
packSetting[pack.name]={base=base,top=top}
LOG((num-1).." "..pack.name.." samples loaded")
end
function SFX.getCount()
return #sfxList
end
function SFX.setVol(v)
assert(type(v)=='number' and v>=0 and v<=1,'Wrong volume')
volume=v
end
function SFX.setStereo(v)
assert(type(v)=='number' and v>=0 and v<=1,'Wrong stereo')
stereo=v
end
function SFX.getNoteName(note)
if note<1 then
return '---'
else
note=note-1
local octave=floor(note/12)+1
return noteName[note%12+1]..octave
end
end
function SFX.playSample(pack,...)-- vol-1, sampSet1, vol-2, sampSet2
if ... then
local arg={...}
local vol
for i=1,#arg do
local a=arg[i]
if type(a)=='number' and a<=1 then
vol=a
else
local base=packSetting[pack].base
local top=packSetting[pack].top
local tune=type(a)=='string' and _getTuneHeight(a) or a-- Absolute tune in number
local playTune=tune+rnd(-2,2)
if playTune<=base then-- Too low notes
playTune=base+1
elseif playTune>top then-- Too high notes
playTune=top
end
SFX.play(pack..playTune-base,vol,nil,tune-playTune)
end
end
end
end
local function _play(name,vol,pos,pitch)
if volume==0 or vol==0 then return end
local S=Sources[name]-- Source list
if not S then return end
local n=1
while S[n]:isPlaying() do
n=n+1
if not S[n] then
S[n]=S[1]:clone()
S[n]:seek(0)
break
end
end
S=S[n]-- AU_SRC
if S:getChannelCount()==1 then
if pos then
pos=MATH.clamp(pos,-1,1)*stereo
S:setPosition(pos,1-pos^2,0)
else
S:setPosition(0,0,0)
end
end
S:setVolume(vol^1.626)
S:setPitch(pitch and 1.0594630943592953^pitch or 1)
S:play()
end
SFX.fplay=_play-- Play sounds without apply module's volume setting
function SFX.play(name,vol,pos,pitch)
_play(name,(vol or 1)*volume,pos,pitch)
end
function SFX.reset()
for _,L in next,Sources do
if type(L)=='table' then
for i=#L,1,-1 do
if not L[i]:isPlaying() then
rem(L,i)
end
end
end
end
end
return SFX

5675
Zframework/sha2.lua Normal file

File diff suppressed because it is too large Load Diff

428
Zframework/stringExtend.lua Normal file
View File

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

205
Zframework/sysFX.lua Normal file
View File

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

463
Zframework/tableExtend.lua Normal file
View File

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

83
Zframework/task.lua Normal file
View File

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

18
Zframework/test.lua Normal file
View File

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

152
Zframework/text.lua Normal file
View File

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

26
Zframework/upcaser.txt Normal file
View File

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

12
Zframework/vibrate.lua Normal file
View File

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

144
Zframework/voice.lua Normal file
View File

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

128
Zframework/wait.lua Normal file
View File

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

192
Zframework/websocket.lua Normal file
View File

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

View File

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

View File

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

1546
Zframework/widget.lua Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -12,6 +12,8 @@ Lua is free software distributed under the terms of the MIT license. Copyright
json.lua is copyrighted by rxi. © 2022 rxi.
discord-rpc.dll is copyrighted by Discord, Inc. © 2017 Discord, Inc.
IBM Plex is copyrighted by the International Business Machines Corporation. IBM and IBM Plex are trademarks of IBM Corp, registered in many jurisdictions worldwide. IBM Plex is licensed under the SIL Open Font License, Version 1.1.

129
main.lua
View File

@@ -23,7 +23,7 @@ TIME=love.timer.getTime
-- Global Vars & Settings
SFXPACKS={'chiptune'}
VOCPACKS={'miya','mono','xiaoya','flore','miku','zundamon'}
VOCPACKS={'miya','mono','xiaoya','flore','neuro','miku','zundamon'}
FIRSTLAUNCH=false
DAILYLAUNCH=false
@@ -50,14 +50,11 @@ BGM.setMaxSources(5)
VOC.setDiversion(.62)
WIDGET.setOnChange(function()
if SCN.cur~='net_game' and SCN.cur~='custom_field' then
local colorList=THEME.getThemeColor()
if colorList then
for _,W in next,SCN.scenes[SCN.cur].widgetList do
if W.color then
W.color=colorList[math.random(#colorList)]
end
end
if SCN.cur=='net_game' or SCN.cur=='custom_field' then return end
local colorList=THEME.getThemeColor()
if colorList then
for _,W in next,SCN.scenes[SCN.cur].widgetList do
W.color=W.color and colorList[math.random(#colorList)]
end
end
end)
@@ -191,7 +188,7 @@ Z.setOnFnKeys({
Z.setOnGlobalKey('f11',function()
if not MOBILE then
SETTING.fullscreen=not SETTING.fullscreen
applySettings()
applySettings('fullscreen')
saveSettings()
end
end)
@@ -274,6 +271,7 @@ IMG.init{
floreCH='media/image/characters/flore.png',
mikuCH='media/image/characters/miku.png',
zundamonCH='media/image/characters/zundamon.png',
neuroCH='media/image/characters/neuro.png',
z={
character='media/image/characters/z_character.png',
screen1='media/image/characters/z_screen1.png',
@@ -296,39 +294,41 @@ IMG.init{
},
}
SKIN.load{
{name="crystal_scf", path='media/image/skin/crystal_scf.png'},
{name="matte_mrz", path='media/image/skin/matte_mrz.png'},
{name="shiny_chno", path='media/image/skin/shiny_chno.png'},
{name="contrast_mrz", path='media/image/skin/contrast_mrz.png'},
{name="polkadots_scf", path='media/image/skin/polkadots_scf.png'},
{name="toy_scf", path='media/image/skin/toy_scf.png'},
{name="smooth_mrz", path='media/image/skin/smooth_mrz.png'},
{name="simple_scf", path='media/image/skin/simple_scf.png'},
{name="glass_scf", path='media/image/skin/glass_scf.png'},
{name="penta_scf", path='media/image/skin/penta_scf.png'},
{name="bubble_scf", path='media/image/skin/bubble_scf.png'},
{name="minoes_scf", path='media/image/skin/minoes_scf.png'},
{name="pure_mrz", path='media/image/skin/pure_mrz.png'},
{name="bright_scf", path='media/image/skin/bright_scf.png'},
{name="glow_mrz", path='media/image/skin/glow_mrz.png'},
{name="plastic_mrz", path='media/image/skin/plastic_mrz.png'},
{name="paper_mrz", path='media/image/skin/paper_mrz.png'},
{name="yinyang_scf", path='media/image/skin/yinyang_scf.png'},
{name="cartooncup_earety", path='media/image/skin/cartooncup_earety.png'},
{name="jelly_miya", path='media/image/skin/jelly_miya.png'},
{name="guidetris_xmiao_lusisi",path='media/image/skin/guidetris_xmiao_lusisi.png'},
{name="brick_notypey", path='media/image/skin/brick_notypey.png'},
{name="gem_notypey", path='media/image/skin/gem_notypey.png'},
{name="classic", path='media/image/skin/classic_unknown.png'},
{name="ball_shaw", path='media/image/skin/ball_shaw.png'},
{name="retro_notypey", path='media/image/skin/retro_notypey.png'},
{name="pixel_chno", path='media/image/skin/pixel_chno.png'},
{name="pastel_chno", path='media/image/skin/pastel_chno.png'},
{name="letters_chno", path='media/image/skin/letters_chno.png'},
{name="kanji_chno", path='media/image/skin/kanji_chno.png'},
{name="textbone_mrz", path='media/image/skin/textbone_mrz.png'},
{name="coloredbone_mrz", path='media/image/skin/coloredbone_mrz.png'},
{name="wtf", path='media/image/skin/wtf_mrz.png'},
{name="Arcade (Asriel)", path='media/image/skin/asriel/arcade.png'},
{name="Cardboard (Asriel, slimenergy)", path='media/image/skin/asriel/cardboard.png'},
{name="Crystal (Scf)", path='media/image/skin/scf/crystal.png'},
{name="Matte (MrZ)", path='media/image/skin/mrz/matte.png'},
{name="Shiny (CHNO)", path='media/image/skin/chno/shiny.png'},
{name="Contrast (MrZ)", path='media/image/skin/mrz/contrast.png'},
{name="Polkadots (Scf)", path='media/image/skin/scf/polkadots.png'},
{name="Toy (Scf)", path='media/image/skin/scf/toy.png'},
{name="Smooth (MrZ)", path='media/image/skin/mrz/smooth.png'},
{name="Simple (Scf)", path='media/image/skin/scf/simple.png'},
{name="Glass (Scf)", path='media/image/skin/scf/glass.png'},
{name="Penta (Scf)", path='media/image/skin/scf/penta.png'},
{name="Bubble (Scf)", path='media/image/skin/scf/bubble.png'},
{name="Minoes (Scf)", path='media/image/skin/scf/minoes.png'},
{name="pure (MrZ)", path='media/image/skin/mrz/pure.png'},
{name="bright (Scf)", path='media/image/skin/scf/bright.png'},
{name="Glow (MrZ)", path='media/image/skin/mrz/glow.png'},
{name="Plastic (MrZ)", path='media/image/skin/mrz/plastic.png'},
{name="paper (MrZ)", path='media/image/skin/mrz/paper.png'},
{name="Yinyang (Scf)", path='media/image/skin/scf/yinyang.png'},
{name="Cartooncup (Earety)", path='media/image/skin/earety/cartooncup.png'},
{name="Jelly (Miya)", path='media/image/skin/miya/jelly.png'},
{name="guidetris (xmiao, lusisi)",path='media/image/skin/guidetris_xmiao_lusisi.png'},
{name="brick (Notypey)", path='media/image/skin/notypey/brick.png'},
{name="Gem (Notypey)", path='media/image/skin/notypey/gem.png'},
{name="Classic", path='media/image/skin/unknown/classic.png'},
{name="Ball (Shaw)", path='media/image/skin/shaw/ball.png'},
{name="Retro (Notypey)", path='media/image/skin/notypey/retro.png'},
{name="Pixel (CHNO)", path='media/image/skin/chno/pixel.png'},
{name="Pastel (CHNO)", path='media/image/skin/chno/pastel.png'},
{name="Letters (CHNO)", path='media/image/skin/chno/letters.png'},
{name="Kanji (CHNO)", path='media/image/skin/chno/kanji.png'},
{name="Textbone (MrZ)", path='media/image/skin/mrz/textbone.png'},
{name="Coloredbone (MrZ)", path='media/image/skin/mrz/coloredbone.png'},
{name="WTF (MrZ)", path='media/image/skin/mrz/wtf.png'},
}
-- Initialize sound libs
@@ -376,30 +376,6 @@ then
MES.new('error',"An error occured during loading, and some data was lost.")
end
-- Initialize fields, sequence, missions, gameEnv for cutsom game
local fieldData=loadFile('conf/customBoards','-string -canSkip')
if fieldData then
fieldData=STRING.split(fieldData,"!")
for i=1,#fieldData do
DATA.pasteBoard(fieldData[i],i)
end
else
FIELD[1]=DATA.newBoard()
end
local sequenceData=loadFile('conf/customSequence','-string -canSkip')
if sequenceData then
DATA.pasteSequence(sequenceData)
end
local missionData=loadFile('conf/customMissions','-string -canSkip')
if missionData then
DATA.pasteMission(missionData)
end
local customData=loadFile('conf/customEnv','-canSkip')
if customData and customData['version']==VERSION.code then
TABLE.complete(customData,CUSTOMENV)
end
TABLE.complete(require"parts.customEnv0",CUSTOMENV)
-- Update data
do
if type(STAT.version)~='number' then
@@ -426,6 +402,7 @@ do
end
if RANKS.bigbang then fs.remove('record/bigbang.rec') end
if RANKS.clearRush then fs.remove('record/clearRush.rec') end
if STAT.version<1715 then fs.remove('record/dig_quad_10l.rec') end
if STAT.version~=VERSION.code then
for k,v in next,MODE_UPDATE_MAP do
@@ -450,7 +427,8 @@ do
for _,v in next,SETTING.skin do if v<1 or v>17 then v=17 end end
if not RSlist[SETTING.RS] then SETTING.RS='TRS' end
if SETTING.ghostType=='greyCell' then SETTING.ghostType='grayCell' end
if type(SETTING.skinSet)=='number' then SETTING.skinSet='crystal_scf' end
if type(SETTING.skinSet)=='number' then SETTING.skinSet='Crystal (Scf)' end
if string.find(SETTING.skinSet,"_") then SETTING.skinSet='Crystal (Scf)' end
if not TABLE.find({8,10,13,17,22,29,37,47,62,80,100},SETTING.frameMul) then SETTING.frameMul=100 end
if SETTING.cv then SETTING.vocPack,SETTING.cv=SETTING.cv end
if type(SETTING.bg)~='string' then SETTING.bg='on' end
@@ -463,6 +441,7 @@ do
if not RANKS.sprint_10l then RANKS.sprint_10l=0 end
if RANKS.master_l then RANKS.master_n,RANKS.master_l=RANKS.master_l end
if RANKS.master_u then RANKS.master_h,RANKS.master_u=RANKS.master_u end
if RANKS.secret_grade then RANKS.construct_sg,RANKS.secret_grade=RANKS.secret_grade end
for _,v in next,VK_ORG do v.color=nil end
for name,rank in next,RANKS do
if type(name)=='number' or type(rank)~='number' then
@@ -615,18 +594,22 @@ for _,fileName in next,fs.getDirectoryItems('replay') do
end
table.sort(REPLAY,function(a,b) return a.fileName>b.fileName end)
AUTHURL="https://studio26f.org/oauth?product=techmino"
AUTHHOST="cafuuchino1.3322.org:8081"
WS.switchHost('cafuuchino1.3322.org','10026','/techmino/ws/v1')
HTTP.setHost("cafuuchino1.3322.org:10026")
AUTHURL="https://www.studio26f.org/oauth?product=techmino"
AUTHHOST="www.studio26f.org:8080"
WS.switchHost('www.studio26f.org','8081','/techmino/ws/v1')
HTTP.setHost("www.studio26f.org:8081")
HTTP.setThreadCount(1)
-- Discord RPC
DiscordRPC=require'parts.discordRPC'
DiscordRPC.update()
table.insert(_LOADTIMELIST_,("Load Resources: %.3fs"):format(TIME()-_LOADTIME_))
for i=1,#_LOADTIMELIST_ do LOG(_LOADTIMELIST_[i]) end
-- Launch testing task if launch param received
if TABLE.find(arg,'-- test') then
if TABLE.find(arg,'--test') then
TASK.new(function()
while not LOADED do coroutine.yield() end

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 613 B

After

Width:  |  Height:  |  Size: 613 B

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 91 B

After

Width:  |  Height:  |  Size: 91 B

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 837 B

After

Width:  |  Height:  |  Size: 837 B

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
media/vocal/neuro/air.ogg Normal file

Binary file not shown.

Binary file not shown.

BIN
media/vocal/neuro/b2b.ogg Normal file

Binary file not shown.

BIN
media/vocal/neuro/b3b.ogg Normal file

Binary file not shown.

BIN
media/vocal/neuro/bye.ogg Normal file

Binary file not shown.

BIN
media/vocal/neuro/clear.ogg Normal file

Binary file not shown.

Binary file not shown.

BIN
media/vocal/neuro/cspin.ogg Normal file

Binary file not shown.

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