Compare commits
403 Commits
v0.17.11
...
d38ff06262
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d38ff06262 | ||
|
|
45a4b10d11 | ||
|
|
fe12f397cc | ||
|
|
801f67b194 | ||
|
|
e331c8f446 | ||
|
|
51897584a7 | ||
|
|
1cf3d101aa | ||
|
|
1830e849d8 | ||
|
|
e3f246aa00 | ||
|
|
1eb679cf24 | ||
|
|
1963dc9fb9 | ||
|
|
396293c8af | ||
|
|
fbf6e910a3 | ||
|
|
9e4e861c32 | ||
|
|
d0b99a16c9 | ||
|
|
347e81c11c | ||
|
|
6b2a376dfe | ||
|
|
51e0ab7c48 | ||
|
|
87fd26ab89 | ||
|
|
0b1cee99bd | ||
|
|
4768df6867 | ||
|
|
423d502aa4 | ||
|
|
a74e9033b3 | ||
|
|
dc6b7de15f | ||
|
|
74f67d0216 | ||
|
|
d47f073d53 | ||
|
|
7407911914 | ||
|
|
9672a4fe57 | ||
|
|
6c6ff26586 | ||
|
|
ca6f701084 | ||
|
|
5793b7ca38 | ||
|
|
dee6ba95f2 | ||
|
|
67aef1dbe3 | ||
|
|
90f41a20a3 | ||
|
|
5f5dd48ee8 | ||
|
|
a8e0574f44 | ||
|
|
40f148b6b3 | ||
|
|
0eb37666f8 | ||
|
|
b73a653332 | ||
|
|
49f1b747b2 | ||
|
|
2c75f0bc9c | ||
|
|
97e17edfae | ||
|
|
f7e4e47466 | ||
|
|
8779abef9a | ||
|
|
4d1caa7fe0 | ||
|
|
78f3c31db1 | ||
|
|
3c852f17a0 | ||
|
|
8737a00b44 | ||
|
|
fff2c49f2e | ||
|
|
35c19a4d50 | ||
|
|
137e707c63 | ||
|
|
d2e9439e38 | ||
|
|
39cd7e4c1a | ||
|
|
57f2b9541d | ||
|
|
9d4065a05a | ||
|
|
424a3b3bee | ||
|
|
de3e1fcdc7 | ||
|
|
59f390de93 | ||
|
|
26287c8f35 | ||
|
|
24d552ba2b | ||
|
|
f1a08ca325 | ||
|
|
28ec031afa | ||
|
|
d9f606c56f | ||
|
|
de4b73cf83 | ||
|
|
beff0c9d99 | ||
|
|
4a76a929f5 | ||
|
|
aa981160e8 | ||
|
|
24a95a36d0 | ||
|
|
583819e8c4 | ||
|
|
4c193efd41 | ||
|
|
8208e7b132 | ||
|
|
7e2bd7b08d | ||
|
|
29258e2a35 | ||
|
|
9e028bc907 | ||
|
|
75ae05ffc9 | ||
|
|
aec0b91039 | ||
|
|
34df33c9b0 | ||
|
|
1674902727 | ||
|
|
4fd497fe9f | ||
|
|
f01594b4d1 | ||
|
|
6f74693811 | ||
|
|
94d2af685c | ||
|
|
0b7c2fa59b | ||
|
|
137f9a0d55 | ||
|
|
23d8a34991 | ||
|
|
3f0d4d3cdf | ||
|
|
ac8f62de03 | ||
|
|
e518d91237 | ||
|
|
c054a76504 | ||
|
|
d3406c4a48 | ||
|
|
3c0266ec02 | ||
|
|
4f4683d06c | ||
|
|
3441002758 | ||
|
|
c2bf291029 | ||
|
|
d70b04e7fb | ||
|
|
a979b6dbe6 | ||
|
|
523b7ce443 | ||
|
|
4e74c9456f | ||
|
|
7719b74b09 | ||
|
|
9d3aeeb50b | ||
|
|
0459248556 | ||
|
|
288a6ba80a | ||
|
|
97eee39266 | ||
|
|
8c4446edac | ||
|
|
e49f8d428a | ||
|
|
6ece5c5f1f | ||
|
|
b75359472b | ||
|
|
1c190d9544 | ||
|
|
1859e5bb96 | ||
|
|
7e1a5805b3 | ||
|
|
6c63fe0ab7 | ||
|
|
85fc9f660e | ||
|
|
2d12ab3a19 | ||
|
|
efeb24b0f0 | ||
|
|
bce60ee6c7 | ||
|
|
b0a47f5222 | ||
|
|
58bac2e290 | ||
|
|
84361c3c31 | ||
|
|
d0a7a28349 | ||
|
|
10b8891cf0 | ||
|
|
3a4fcf6d7b | ||
|
|
19ea76dc4c | ||
|
|
42942d1ac4 | ||
|
|
43215dfc4b | ||
|
|
5728194085 | ||
|
|
1501ebd92a | ||
|
|
0299fde47b | ||
|
|
11680aedbb | ||
|
|
ad0b73ff62 | ||
|
|
f38746ff96 | ||
|
|
8ee7a90eca | ||
|
|
d06d8ebfff | ||
|
|
d3e4d7e62c | ||
|
|
80ff30401a | ||
|
|
4657515b61 | ||
|
|
90dcb9ee1a | ||
|
|
4e606f4e91 | ||
|
|
9c0dc60746 | ||
|
|
58a5749d67 | ||
|
|
7d0d89f1a6 | ||
|
|
f92121f093 | ||
|
|
2604d034fe | ||
|
|
3219da77e9 | ||
|
|
ea3fb8d09e | ||
|
|
074cefed5d | ||
|
|
7070e620c8 | ||
|
|
1cd62bb163 | ||
|
|
529b8d453d | ||
|
|
486eaeae52 | ||
|
|
a2931ea290 | ||
|
|
edb79e8156 | ||
|
|
ed5abe0350 | ||
|
|
3806f02fa5 | ||
|
|
7d23fe4acb | ||
|
|
c650f268f6 | ||
|
|
55800e307f | ||
|
|
2671306688 | ||
|
|
20e9d085c0 | ||
|
|
1c1a006723 | ||
|
|
a0fd8ff8b6 | ||
|
|
720325fcb9 | ||
|
|
fcc1cba0cf | ||
|
|
8ca17ba3ff | ||
|
|
942a789b9e | ||
|
|
293e687077 | ||
|
|
9929b888c5 | ||
|
|
e66e30c14a | ||
|
|
adf225e192 | ||
|
|
4c2dc5e8df | ||
|
|
e1a9222b1f | ||
|
|
86c673ee51 | ||
|
|
6aefa078da | ||
|
|
dd743035f5 | ||
|
|
045deb6954 | ||
|
|
8e169dbde8 | ||
|
|
90c428cf44 | ||
|
|
7ac2c282f6 | ||
|
|
ad58d38ab1 | ||
|
|
5ea940d8c4 | ||
|
|
c0dca683b5 | ||
|
|
35635e10ea | ||
|
|
dfda067c08 | ||
|
|
09117c4704 | ||
|
|
6083fd5097 | ||
|
|
e104297b13 | ||
|
|
ed3927a8c3 | ||
|
|
b62b907dc8 | ||
|
|
769125a894 | ||
|
|
6ccebcfe17 | ||
|
|
8894701806 | ||
|
|
012e0f76a9 | ||
|
|
d58f41b999 | ||
|
|
95916a8736 | ||
|
|
69a288db8f | ||
|
|
bb8a436604 | ||
|
|
b58f266561 | ||
|
|
4e41024ba8 | ||
|
|
323f8a72aa | ||
|
|
bb4d3023c6 | ||
|
|
2208a0fca0 | ||
|
|
3cd6f3d726 | ||
|
|
8b1d72fb54 | ||
|
|
73480d4a53 | ||
|
|
120dc453d7 | ||
|
|
eae441b18b | ||
|
|
4f411ef78e | ||
|
|
35ec325990 | ||
|
|
54758fd705 | ||
|
|
e7183025cf | ||
|
|
03a2e9356c | ||
|
|
0eb4212e40 | ||
|
|
22ca0a7c96 | ||
|
|
33fc59bd5c | ||
|
|
2f6a3392e6 | ||
|
|
75f3efc6a0 | ||
|
|
19e4185668 | ||
|
|
bbb5fd7832 | ||
|
|
8806ef602e | ||
|
|
6f5ebb1a4d | ||
|
|
7cd03d0ec0 | ||
|
|
00c5233f3b | ||
|
|
a08328ae02 | ||
|
|
a6a20b4332 | ||
|
|
732e2aee79 | ||
|
|
74c3d3a313 | ||
|
|
3ebc93e65c | ||
|
|
7e8e67694d | ||
|
|
00f63a2ec5 | ||
|
|
5a30e4aec9 | ||
|
|
43aecd2467 | ||
|
|
622ae07b32 | ||
|
|
63f443d4e4 | ||
|
|
688328e6b0 | ||
|
|
0fc4957b21 | ||
|
|
5e88b23980 | ||
|
|
986f0dcdc8 | ||
|
|
48f2ee9252 | ||
|
|
73d6eaaffc | ||
|
|
b1dbc4fa87 | ||
|
|
2e9ea4a783 | ||
|
|
86654689da | ||
|
|
b4901add94 | ||
|
|
8df321b84d | ||
|
|
6a4dfb47bb | ||
|
|
dd8a810591 | ||
|
|
009858e2f8 | ||
|
|
a475232432 | ||
|
|
bc99af5b1b | ||
|
|
337293dbe1 | ||
|
|
5e0be0d463 | ||
|
|
cf147be119 | ||
|
|
c695596285 | ||
|
|
7b97aefa0a | ||
|
|
a3e5ccffa4 | ||
|
|
452fa65748 | ||
|
|
7141f46948 | ||
|
|
481ea06e61 | ||
|
|
3aa60d3488 | ||
|
|
e152e147e2 | ||
|
|
c7b8dd24c7 | ||
|
|
9e384f80cb | ||
|
|
7f8e3e647a | ||
|
|
df79e99fa5 | ||
|
|
03eafb4881 | ||
|
|
e795604721 | ||
|
|
9769b33deb | ||
|
|
51968741ea | ||
|
|
29a5cc63df | ||
|
|
3f0bf28fbe | ||
|
|
2321176712 | ||
|
|
974308c843 | ||
|
|
1d59cba316 | ||
|
|
f3e1e9b4b2 | ||
|
|
5c38bb64a0 | ||
|
|
3fbff37095 | ||
|
|
540099a944 | ||
|
|
cd0b276885 | ||
|
|
519f84a804 | ||
|
|
e44462bb78 | ||
|
|
9da2300194 | ||
|
|
91c39ee7ef | ||
|
|
58ea85e6b9 | ||
|
|
7be2aa50dd | ||
|
|
ed9417dcdf | ||
|
|
36522c7146 | ||
|
|
20b74ea78a | ||
|
|
0ae9cbcdaa | ||
|
|
c9cbb0e363 | ||
|
|
0595ce4ea6 | ||
|
|
2e2427f942 | ||
|
|
aeddee2f03 | ||
|
|
43037f8043 | ||
|
|
0c3d552d08 | ||
|
|
a1b6b8a434 | ||
|
|
5061cad389 | ||
|
|
9d3fb33d10 | ||
|
|
4d82b182a6 | ||
|
|
78ac05efba | ||
|
|
02cf7b71c1 | ||
|
|
091bb23488 | ||
|
|
0ec0d80e9c | ||
|
|
d4d87ccb5c | ||
|
|
c782c1fcdd | ||
|
|
f7cdae8677 | ||
|
|
f41a68de0d | ||
|
|
aefde7a777 | ||
|
|
3dc61688c4 | ||
|
|
7e00a7d764 | ||
|
|
8304cf655b | ||
|
|
c25e7a3d5b | ||
|
|
b7b02ab5bc | ||
|
|
b3a8724538 | ||
|
|
ed11faf909 | ||
|
|
b5cd0be057 | ||
|
|
d31954d1e2 | ||
|
|
96bf30fcab | ||
|
|
e7fc5a676e | ||
|
|
4a81d8beb0 | ||
|
|
c583769cf3 | ||
|
|
2ff2fd1940 | ||
|
|
6d45cf978a | ||
|
|
4c3b80a1fb | ||
|
|
d22aa81fa0 | ||
|
|
1e76b8e533 | ||
|
|
886adc3534 | ||
|
|
aff7ffb2c4 | ||
|
|
4ec1f7c5c8 | ||
|
|
7772c9b424 | ||
|
|
514f0a17b5 | ||
|
|
91adc0d153 | ||
|
|
48425f5549 | ||
|
|
fd36562a7d | ||
|
|
4b36442170 | ||
|
|
dad6beca05 | ||
|
|
ee61c5aa8d | ||
|
|
1001588f97 | ||
|
|
25df3d819b | ||
|
|
a2e5656747 | ||
|
|
65fc0339b9 | ||
|
|
0421654c50 | ||
|
|
bdaa42f6df | ||
|
|
9984c3ecb5 | ||
|
|
5e7c2309ac | ||
|
|
9dda8555a0 | ||
|
|
43046a3cf3 | ||
|
|
fe29cc532d | ||
|
|
7dd73ef8d3 | ||
|
|
ad6bd7be4b | ||
|
|
c276b700fa | ||
|
|
98f73aa4c0 | ||
|
|
1db854618e | ||
|
|
940a1bc3f8 | ||
|
|
7463dd96dc | ||
|
|
48e96998a9 | ||
|
|
95a1b03cc5 | ||
|
|
ee4dfa7f51 | ||
|
|
06f403e9d7 | ||
|
|
c03f3f727c | ||
|
|
ed4ba1dc38 | ||
|
|
9663f8c316 | ||
|
|
2478df1242 | ||
|
|
1b54dd3b90 | ||
|
|
001014c70e | ||
|
|
1a444a9e98 | ||
|
|
d5397333d5 | ||
|
|
e1001c74c5 | ||
|
|
5f664c04d6 | ||
|
|
525fa4c25e | ||
|
|
f513760153 | ||
|
|
5faa929bb0 | ||
|
|
5c4557a7b7 | ||
|
|
69a84c035e | ||
|
|
f8277e1c8a | ||
|
|
48f3d293bd | ||
|
|
00bf828ef4 | ||
|
|
d1cef7ed84 | ||
|
|
17bb8dbe6d | ||
|
|
6166c03eab | ||
|
|
303f32f5b2 | ||
|
|
f7e4d7b30d | ||
|
|
f2957dff7b | ||
|
|
cadbe38a8f | ||
|
|
e55d117371 | ||
|
|
1f3d8a212b | ||
|
|
035f30d7e1 | ||
|
|
3202aa18b1 | ||
|
|
0c5d2bdf1a | ||
|
|
db25475c21 | ||
|
|
11b1c23be0 | ||
|
|
5b1eef890d | ||
|
|
3a3d062e5c | ||
|
|
2ee9ed237f | ||
|
|
9159661945 | ||
|
|
0efd2c8044 | ||
|
|
0aaa5822fb | ||
|
|
2b258aeaed | ||
|
|
0d7a80f2b5 | ||
|
|
d433d98c04 | ||
|
|
180dc12460 | ||
|
|
dd1d0b4126 | ||
|
|
f1517fad1a | ||
|
|
62ed279f07 | ||
|
|
6925d59f87 |
31
.editorconfig
Normal file
@@ -0,0 +1,31 @@
|
||||
# EmmyLuaCodeStyle
|
||||
[*.lua]
|
||||
max_line_length = 26000
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
space_around_table_field_list = false
|
||||
space_before_attribute = false
|
||||
space_before_function_call_open_parenthesis = false
|
||||
space_before_closure_open_parenthesis = false
|
||||
space_before_function_call_single_arg = false
|
||||
space_before_open_square_bracket = false
|
||||
space_inside_function_call_parentheses = false
|
||||
space_inside_function_param_list_parentheses = false
|
||||
space_inside_square_brackets = false
|
||||
space_around_table_append_operator = false
|
||||
space_before_inline_comment = false
|
||||
space_around_math_operator = false
|
||||
space_around_logical_operator = false
|
||||
space_around_assign_operator = false
|
||||
space_after_comma = false
|
||||
space_after_comma_in_for_statement = false
|
||||
space_around_concat_operator = false
|
||||
align_call_args = false
|
||||
align_function_params = false
|
||||
align_continuous_assign_statement = true
|
||||
align_continuous_rect_table_field = true
|
||||
align_if_branch = false
|
||||
align_array_table = true
|
||||
ignore_spaces_inside_function_call = true
|
||||
line_space_after_function_statement = keep
|
||||
trailing_table_separator = smart
|
||||
16
.github/500stars/README.md
vendored
Normal 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
|
After Width: | Height: | Size: 1.3 MiB |
2
.github/actions/get-cc/action.yml
vendored
@@ -3,7 +3,7 @@ description: 'download cc into specific dir'
|
||||
inputs:
|
||||
tag:
|
||||
required: false
|
||||
default: "11.4"
|
||||
default: "11.4.2"
|
||||
platform:
|
||||
required: true
|
||||
dir:
|
||||
|
||||
BIN
.github/build/extraLibs/Windows_x64/discord-rpc.dll
vendored
Normal file
BIN
.github/build/extraLibs/Windows_x86/discord-rpc.dll
vendored
Normal file
12
.github/build/iOS/love.patch
vendored
@@ -8,7 +8,7 @@ index c1932555..552e432e 100644
|
||||
**/
|
||||
-void vibrate();
|
||||
+void vibrate(const double seconds);
|
||||
|
||||
|
||||
/**
|
||||
* Enable mix mode (e.g. with background music apps) and playback with a muted device.
|
||||
diff --git a/src/common/ios.mm b/src/common/ios.mm
|
||||
@@ -18,16 +18,16 @@ index 7730991e..4ba8e708 100644
|
||||
@@ -36,6 +36,8 @@
|
||||
#include <SDL_video.h>
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
|
||||
+#include <sys/utsname.h>
|
||||
+
|
||||
static NSArray *getLovesInDocuments();
|
||||
static bool deleteFileInDocuments(NSString *filename);
|
||||
|
||||
|
||||
@@ -391,10 +393,40 @@ std::string getExecutablePath()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-void vibrate()
|
||||
+void vibrate(const double seconds)
|
||||
{
|
||||
@@ -73,7 +73,7 @@ index c8af8596..ae7a5e32 100644
|
||||
@@ -140,6 +140,10 @@ enum DoneAction
|
||||
DONE_RESTART,
|
||||
};
|
||||
|
||||
|
||||
+extern "C" {
|
||||
+ int luaopen_CCloader(lua_State *L);
|
||||
+}
|
||||
@@ -84,7 +84,7 @@ index c8af8596..ae7a5e32 100644
|
||||
@@ -158,6 +162,9 @@ static DoneAction runlove(int argc, char **argv, int &retval)
|
||||
lua_State *L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
|
||||
|
||||
+ // Init CCloader
|
||||
+ luaopen_CCloader(L);
|
||||
+
|
||||
|
||||
BIN
.github/build/web/favicon.ico
vendored
Normal file
|
After Width: | Height: | Size: 305 KiB |
288
.github/build/web/game.js
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
|
||||
var Module;
|
||||
|
||||
if (typeof Module === 'undefined') Module = eval('(function() { try { return Module || {} } catch(e) { return {} } })()');
|
||||
|
||||
if (!Module.expectedDataFileDownloads) {
|
||||
Module.expectedDataFileDownloads = 0;
|
||||
Module.finishedDataFileDownloads = 0;
|
||||
}
|
||||
Module.expectedDataFileDownloads++;
|
||||
(function() {
|
||||
var loadPackage = function(metadata) {
|
||||
|
||||
var PACKAGE_PATH;
|
||||
if (typeof window === 'object') {
|
||||
PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/');
|
||||
} else if (typeof location !== 'undefined') {
|
||||
// worker
|
||||
PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/');
|
||||
} else {
|
||||
throw 'using preloaded data can only be done on a web page or in a web worker';
|
||||
}
|
||||
var PACKAGE_NAME = 'game.data';
|
||||
var REMOTE_PACKAGE_BASE = 'game.data';
|
||||
if (typeof Module['locateFilePackage'] === 'function' && !Module['locateFile']) {
|
||||
Module['locateFile'] = Module['locateFilePackage'];
|
||||
Module.printErr('warning: you defined Module.locateFilePackage, that has been renamed to Module.locateFile (using your locateFilePackage for now)');
|
||||
}
|
||||
var REMOTE_PACKAGE_NAME = typeof Module['locateFile'] === 'function' ?
|
||||
Module['locateFile'](REMOTE_PACKAGE_BASE) :
|
||||
((Module['filePackagePrefixURL'] || '') + REMOTE_PACKAGE_BASE);
|
||||
|
||||
var REMOTE_PACKAGE_SIZE = metadata.remote_package_size;
|
||||
var PACKAGE_UUID = metadata.package_uuid;
|
||||
|
||||
function fetchRemotePackage(packageName, packageSize, callback, errback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', packageName, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.onprogress = function(event) {
|
||||
var url = packageName;
|
||||
var size = packageSize;
|
||||
if (event.total) size = event.total;
|
||||
if (event.loaded) {
|
||||
if (!xhr.addedTotal) {
|
||||
xhr.addedTotal = true;
|
||||
if (!Module.dataFileDownloads) Module.dataFileDownloads = {};
|
||||
Module.dataFileDownloads[url] = {
|
||||
loaded: event.loaded,
|
||||
total: size
|
||||
};
|
||||
} else {
|
||||
Module.dataFileDownloads[url].loaded = event.loaded;
|
||||
}
|
||||
var total = 0;
|
||||
var loaded = 0;
|
||||
var num = 0;
|
||||
for (var download in Module.dataFileDownloads) {
|
||||
var data = Module.dataFileDownloads[download];
|
||||
total += data.total;
|
||||
loaded += data.loaded;
|
||||
num++;
|
||||
}
|
||||
total = Math.ceil(total * Module.expectedDataFileDownloads/num);
|
||||
if (Module['setStatus']) Module['setStatus']('Downloading data... (' + loaded + '/' + total + ')');
|
||||
} else if (!Module.dataFileDownloads) {
|
||||
if (Module['setStatus']) Module['setStatus']('Downloading data...');
|
||||
}
|
||||
};
|
||||
xhr.onerror = function(event) {
|
||||
throw new Error("NetworkError for: " + packageName);
|
||||
}
|
||||
xhr.onload = function(event) {
|
||||
if (xhr.status == 200 || xhr.status == 304 || xhr.status == 206 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
|
||||
var packageData = xhr.response;
|
||||
callback(packageData);
|
||||
} else {
|
||||
throw new Error(xhr.statusText + " : " + xhr.responseURL);
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
};
|
||||
|
||||
function handleError(error) {
|
||||
console.error('package error:', error);
|
||||
};
|
||||
|
||||
function runWithFS() {
|
||||
|
||||
function assert(check, msg) {
|
||||
if (!check) throw msg + new Error().stack;
|
||||
}
|
||||
|
||||
function DataRequest(start, end, crunched, audio) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.crunched = crunched;
|
||||
this.audio = audio;
|
||||
}
|
||||
DataRequest.prototype = {
|
||||
requests: {},
|
||||
open: function(mode, name) {
|
||||
this.name = name;
|
||||
this.requests[name] = this;
|
||||
Module['addRunDependency']('fp ' + this.name);
|
||||
},
|
||||
send: function() {},
|
||||
onload: function() {
|
||||
var byteArray = this.byteArray.subarray(this.start, this.end);
|
||||
|
||||
this.finish(byteArray);
|
||||
|
||||
},
|
||||
finish: function(byteArray) {
|
||||
var that = this;
|
||||
|
||||
Module['FS_createDataFile'](this.name, null, byteArray, true, true, true); // canOwn this data in the filesystem, it is a slide into the heap that will never change
|
||||
Module['removeRunDependency']('fp ' + that.name);
|
||||
|
||||
this.requests[this.name] = null;
|
||||
}
|
||||
};
|
||||
|
||||
var files = metadata.files;
|
||||
for (i = 0; i < files.length; ++i) {
|
||||
new DataRequest(files[i].start, files[i].end, files[i].crunched, files[i].audio).open('GET', files[i].filename);
|
||||
}
|
||||
|
||||
|
||||
var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
|
||||
var IDB_RO = "readonly";
|
||||
var IDB_RW = "readwrite";
|
||||
var DB_NAME = "EM_PRELOAD_CACHE";
|
||||
var DB_VERSION = 1;
|
||||
var METADATA_STORE_NAME = 'METADATA';
|
||||
var PACKAGE_STORE_NAME = 'PACKAGES';
|
||||
function openDatabase(callback, errback) {
|
||||
try {
|
||||
var openRequest = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
} catch (e) {
|
||||
return errback(e);
|
||||
}
|
||||
openRequest.onupgradeneeded = function(event) {
|
||||
var db = event.target.result;
|
||||
|
||||
if(db.objectStoreNames.contains(PACKAGE_STORE_NAME)) {
|
||||
db.deleteObjectStore(PACKAGE_STORE_NAME);
|
||||
}
|
||||
var packages = db.createObjectStore(PACKAGE_STORE_NAME);
|
||||
|
||||
if(db.objectStoreNames.contains(METADATA_STORE_NAME)) {
|
||||
db.deleteObjectStore(METADATA_STORE_NAME);
|
||||
}
|
||||
var metadata = db.createObjectStore(METADATA_STORE_NAME);
|
||||
};
|
||||
openRequest.onsuccess = function(event) {
|
||||
var db = event.target.result;
|
||||
callback(db);
|
||||
};
|
||||
openRequest.onerror = function(error) {
|
||||
errback(error);
|
||||
};
|
||||
};
|
||||
|
||||
/* Check if there's a cached package, and if so whether it's the latest available */
|
||||
function checkCachedPackage(db, packageName, callback, errback) {
|
||||
var transaction = db.transaction([METADATA_STORE_NAME], IDB_RO);
|
||||
var metadata = transaction.objectStore(METADATA_STORE_NAME);
|
||||
|
||||
var getRequest = metadata.get("metadata/" + packageName);
|
||||
getRequest.onsuccess = function(event) {
|
||||
var result = event.target.result;
|
||||
if (!result) {
|
||||
return callback(false);
|
||||
} else {
|
||||
return callback(PACKAGE_UUID === result.uuid);
|
||||
}
|
||||
};
|
||||
getRequest.onerror = function(error) {
|
||||
errback(error);
|
||||
};
|
||||
};
|
||||
|
||||
function fetchCachedPackage(db, packageName, callback, errback) {
|
||||
var transaction = db.transaction([PACKAGE_STORE_NAME], IDB_RO);
|
||||
var packages = transaction.objectStore(PACKAGE_STORE_NAME);
|
||||
|
||||
var getRequest = packages.get("package/" + packageName);
|
||||
getRequest.onsuccess = function(event) {
|
||||
var result = event.target.result;
|
||||
callback(result);
|
||||
};
|
||||
getRequest.onerror = function(error) {
|
||||
errback(error);
|
||||
};
|
||||
};
|
||||
|
||||
function cacheRemotePackage(db, packageName, packageData, packageMeta, callback, errback) {
|
||||
var transaction_packages = db.transaction([PACKAGE_STORE_NAME], IDB_RW);
|
||||
var packages = transaction_packages.objectStore(PACKAGE_STORE_NAME);
|
||||
|
||||
var putPackageRequest = packages.put(packageData, "package/" + packageName);
|
||||
putPackageRequest.onsuccess = function(event) {
|
||||
var transaction_metadata = db.transaction([METADATA_STORE_NAME], IDB_RW);
|
||||
var metadata = transaction_metadata.objectStore(METADATA_STORE_NAME);
|
||||
var putMetadataRequest = metadata.put(packageMeta, "metadata/" + packageName);
|
||||
putMetadataRequest.onsuccess = function(event) {
|
||||
callback(packageData);
|
||||
};
|
||||
putMetadataRequest.onerror = function(error) {
|
||||
errback(error);
|
||||
};
|
||||
};
|
||||
putPackageRequest.onerror = function(error) {
|
||||
errback(error);
|
||||
};
|
||||
};
|
||||
|
||||
function processPackageData(arrayBuffer) {
|
||||
Module.finishedDataFileDownloads++;
|
||||
assert(arrayBuffer, 'Loading data file failed.');
|
||||
assert(arrayBuffer instanceof ArrayBuffer, 'bad input to processPackageData');
|
||||
var byteArray = new Uint8Array(arrayBuffer);
|
||||
var curr;
|
||||
|
||||
// copy the entire loaded file into a spot in the heap. Files will refer to slices in that. They cannot be freed though
|
||||
// (we may be allocating before malloc is ready, during startup).
|
||||
if (Module['SPLIT_MEMORY']) Module.printErr('warning: you should run the file packager with --no-heap-copy when SPLIT_MEMORY is used, otherwise copying into the heap may fail due to the splitting');
|
||||
var ptr = Module['getMemory'](byteArray.length);
|
||||
Module['HEAPU8'].set(byteArray, ptr);
|
||||
DataRequest.prototype.byteArray = Module['HEAPU8'].subarray(ptr, ptr+byteArray.length);
|
||||
|
||||
var files = metadata.files;
|
||||
for (i = 0; i < files.length; ++i) {
|
||||
DataRequest.prototype.requests[files[i].filename].onload();
|
||||
}
|
||||
Module['removeRunDependency']('datafile_game.data');
|
||||
|
||||
};
|
||||
Module['addRunDependency']('datafile_game.data');
|
||||
|
||||
if (!Module.preloadResults) Module.preloadResults = {};
|
||||
|
||||
function preloadFallback(error) {
|
||||
console.error(error);
|
||||
console.error('falling back to default preload behavior');
|
||||
fetchRemotePackage(REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE, processPackageData, handleError);
|
||||
};
|
||||
|
||||
openDatabase(
|
||||
function(db) {
|
||||
checkCachedPackage(db, PACKAGE_PATH + PACKAGE_NAME,
|
||||
function(useCached) {
|
||||
Module.preloadResults[PACKAGE_NAME] = {fromCache: useCached};
|
||||
if (useCached) {
|
||||
console.info('loading ' + PACKAGE_NAME + ' from cache');
|
||||
fetchCachedPackage(db, PACKAGE_PATH + PACKAGE_NAME, processPackageData, preloadFallback);
|
||||
} else {
|
||||
console.info('loading ' + PACKAGE_NAME + ' from remote');
|
||||
fetchRemotePackage(REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE,
|
||||
function(packageData) {
|
||||
cacheRemotePackage(db, PACKAGE_PATH + PACKAGE_NAME, packageData, {uuid:PACKAGE_UUID}, processPackageData,
|
||||
function(error) {
|
||||
console.error(error);
|
||||
processPackageData(packageData);
|
||||
});
|
||||
}
|
||||
, preloadFallback);
|
||||
}
|
||||
}
|
||||
, preloadFallback);
|
||||
}
|
||||
, preloadFallback);
|
||||
|
||||
if (Module['setStatus']) Module['setStatus']('Downloading...');
|
||||
|
||||
}
|
||||
if (Module['calledRun']) {
|
||||
runWithFS();
|
||||
} else {
|
||||
if (!Module['preRun']) Module['preRun'] = [];
|
||||
Module["preRun"].push(runWithFS); // FS is not initialized yet, wait for it
|
||||
}
|
||||
|
||||
}
|
||||
loadPackage({"package_uuid":"80826f15-f924-4428-a8c4-e984743417c6","remote_package_size":62246034,"files":[{"filename":"/game.love","crunched":0,"start":0,"end":62246034,"audio":false}]});
|
||||
|
||||
})();
|
||||
111
.github/build/web/index.html
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, minimum-scale=1, maximum-scale=1">
|
||||
<title>Techmino</title>
|
||||
|
||||
<!-- Load custom style sheet -->
|
||||
<link rel="stylesheet" type="text/css" href="theme/love.css">
|
||||
</head>
|
||||
<body>
|
||||
<center>
|
||||
<div>
|
||||
<h1>Techmino</h1>
|
||||
<canvas id="loadingCanvas" oncontextmenu="event.preventDefault()" width="800" height="600"></canvas>
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
|
||||
</div>
|
||||
</center>
|
||||
|
||||
<script type='text/javascript'>
|
||||
function goFullScreen(){
|
||||
var canvas = document.getElementById("canvas");
|
||||
if(canvas.requestFullScreen)
|
||||
canvas.requestFullScreen();
|
||||
else if(canvas.webkitRequestFullScreen)
|
||||
canvas.webkitRequestFullScreen();
|
||||
else if(canvas.mozRequestFullScreen)
|
||||
canvas.mozRequestFullScreen();
|
||||
}
|
||||
function FullScreenHook(){
|
||||
var canvas = document.getElementById("canvas");
|
||||
canvas.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
|
||||
canvas.height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
|
||||
}
|
||||
var loadingContext = document.getElementById('loadingCanvas').getContext('2d');
|
||||
function drawLoadingText(text) {
|
||||
var canvas = loadingContext.canvas;
|
||||
|
||||
loadingContext.fillStyle = "rgb(142, 195, 227)";
|
||||
loadingContext.fillRect(0, 0, canvas.scrollWidth, canvas.scrollHeight);
|
||||
|
||||
loadingContext.font = '2em arial';
|
||||
loadingContext.textAlign = 'center'
|
||||
loadingContext.fillStyle = "rgb( 11, 86, 117 )";
|
||||
loadingContext.fillText(text, canvas.scrollWidth / 2, canvas.scrollHeight / 2);
|
||||
|
||||
loadingContext.fillText("Powered By Emscripten.", canvas.scrollWidth / 2, canvas.scrollHeight / 4);
|
||||
loadingContext.fillText("Powered By LÖVE.", canvas.scrollWidth / 2, canvas.scrollHeight / 4 * 3);
|
||||
}
|
||||
|
||||
window.onload = function () { window.focus(); };
|
||||
window.onclick = function () { window.focus(); };
|
||||
|
||||
window.addEventListener("keydown", function(e) {
|
||||
// space and arrow keys
|
||||
if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}, false);
|
||||
|
||||
var Module = {
|
||||
arguments: ["./game.love"],
|
||||
INITIAL_MEMORY: 536870912,
|
||||
printErr: console.error.bind(console),
|
||||
canvas: (function() {
|
||||
var canvas = document.getElementById('canvas');
|
||||
|
||||
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
|
||||
// application robust, you may want to override this behavior before shipping!
|
||||
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
|
||||
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
|
||||
|
||||
return canvas;
|
||||
})(),
|
||||
setStatus: function(text) {
|
||||
if (text) {
|
||||
drawLoadingText(text);
|
||||
} else if (Module.remainingDependencies === 0) {
|
||||
document.getElementById('loadingCanvas').style.display = 'none';
|
||||
document.getElementById('canvas').style.visibility = 'visible';
|
||||
}
|
||||
},
|
||||
totalDependencies: 0,
|
||||
remainingDependencies: 0,
|
||||
monitorRunDependencies: function(left) {
|
||||
this.remainingDependencies = left;
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
|
||||
}
|
||||
};
|
||||
Module.setStatus('Downloading...');
|
||||
window.onerror = function(event) {
|
||||
// TODO: do not warn on ok events like simulating an infinite loop or exitStatus
|
||||
Module.setStatus('Exception thrown, see JavaScript console');
|
||||
Module.setStatus = function(text) {
|
||||
if (text) Module.printErr('[post-exception status] ' + text);
|
||||
};
|
||||
};
|
||||
|
||||
var applicationLoad = function(e) {
|
||||
Love(Module);
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="game.js"></script>
|
||||
<script async type="text/javascript" src="love.js" onload="applicationLoad(this)"></script>
|
||||
<footer>
|
||||
<p>Built with <a href="https://github.com/Davidobot/love.js">love.js</a> <button onclick="goFullScreen();">Go Fullscreen</button><br>Hint: Reload the page if screen is blank</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
22
.github/build/web/love.js
vendored
Normal file
BIN
.github/build/web/love.wasm
vendored
Normal file
BIN
.github/build/web/theme/bg.png
vendored
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
49
.github/build/web/theme/love.css
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: arial;
|
||||
color: rgb( 11, 86, 117 );
|
||||
}
|
||||
|
||||
body {
|
||||
background-image: url(bg.png);
|
||||
background-repeat: no-repeat;
|
||||
font-family: arial;
|
||||
margin: 0;
|
||||
padding: none;
|
||||
background-color: rgb( 154, 205, 237 );
|
||||
color: rgb( 28, 78, 104 );
|
||||
}
|
||||
|
||||
footer {
|
||||
font-family: arial;
|
||||
font-size: 12px;
|
||||
padding-left: 10px;
|
||||
position:absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
a:link {
|
||||
color: rgb( 233, 73, 154 );
|
||||
}
|
||||
a:visited {
|
||||
color: rgb( 110, 30, 71 );
|
||||
}
|
||||
a:hover {
|
||||
color: rgb( 252, 207, 230 );
|
||||
}
|
||||
|
||||
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
|
||||
#canvas {
|
||||
padding-right: 0;
|
||||
display: block;
|
||||
border: 0px none;
|
||||
visibility: hidden;
|
||||
}
|
||||
BIN
.github/donate/donate.png
vendored
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
381
.github/workflows/main.yml
vendored
@@ -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')
|
||||
@@ -70,8 +72,6 @@ jobs:
|
||||
env:
|
||||
OUTPUT_FOLDER: ./build
|
||||
RELEASE_FOLDER: ./release
|
||||
outputs:
|
||||
download-url: ${{ steps.transfer.outputs.download-url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -92,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,23 +128,9 @@ jobs:
|
||||
body: ${{ needs.get-info.outputs.update-note }}
|
||||
name: ${{ needs.get-info.outputs.update-title }}
|
||||
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
|
||||
- name: Get transfer
|
||||
shell: bash
|
||||
run: |
|
||||
curl -sL --retry 5 https://raw.githubusercontent.com/Mikubill/transfer/master/install.sh | sh
|
||||
- name: Upload to WeTransfer
|
||||
id: transfer
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
max_attempts: 5
|
||||
retry_wait_seconds: 10
|
||||
timeout_minutes: 5
|
||||
command: |
|
||||
./transfer wet -s -p 16 --no-progress ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.love 2>&1>./wetransfer.log
|
||||
echo "download-url=$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" >> $GITHUB_OUTPUT
|
||||
|
||||
auto-test:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-core
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -155,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
|
||||
@@ -163,8 +168,6 @@ jobs:
|
||||
env:
|
||||
OUTPUT_FOLDER: ./build
|
||||
RELEASE_FOLDER: ./release
|
||||
outputs:
|
||||
download-url: ${{ steps.transfer.outputs.download-url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -230,117 +233,6 @@ jobs:
|
||||
body: ${{ needs.get-info.outputs.update-note }}
|
||||
name: ${{ needs.get-info.outputs.update-title }}
|
||||
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
|
||||
- name: Get transfer
|
||||
shell: bash
|
||||
run: |
|
||||
curl -sL --retry 5 https://raw.githubusercontent.com/Mikubill/transfer/master/install.sh | sh
|
||||
- name: Upload to WeTransfer
|
||||
id: transfer
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
max_attempts: 5
|
||||
retry_wait_seconds: 10
|
||||
timeout_minutes: 5
|
||||
command: |
|
||||
./transfer wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log
|
||||
echo "download-url=$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" >> $GITHUB_OUTPUT
|
||||
|
||||
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
|
||||
outputs:
|
||||
download-url: ${{ steps.transfer.outputs.download-url }}
|
||||
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') }}
|
||||
- name: Get transfer
|
||||
shell: bash
|
||||
run: |
|
||||
wget -qO- https://github.com/Mikubill/transfer/releases/download/v0.4.17/transfer_0.4.17_darwin_amd64.tar.gz | tar xvz
|
||||
- name: Upload to WeTransfer
|
||||
id: transfer
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
max_attempts: 5
|
||||
retry_wait_seconds: 10
|
||||
timeout_minutes: 5
|
||||
command: |
|
||||
./transfer wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log
|
||||
echo "download-url=$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" >> $GITHUB_OUTPUT
|
||||
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -348,8 +240,6 @@ jobs:
|
||||
env:
|
||||
OUTPUT_FOLDER: ./build
|
||||
RELEASE_FOLDER: ./release
|
||||
outputs:
|
||||
download-url: ${{ steps.transfer.outputs.download-url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -426,120 +316,6 @@ jobs:
|
||||
body: ${{ needs.get-info.outputs.update-note }}
|
||||
name: ${{ needs.get-info.outputs.update-title }}
|
||||
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
|
||||
- name: Get transfer
|
||||
shell: bash
|
||||
run: |
|
||||
curl -sL --retry 5 https://raw.githubusercontent.com/Mikubill/transfer/master/install.sh | sh
|
||||
- name: Upload to WeTransfer
|
||||
id: transfer
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
max_attempts: 5
|
||||
retry_wait_seconds: 10
|
||||
timeout_minutes: 5
|
||||
command: |
|
||||
./transfer wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log
|
||||
echo "download-url=$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" >> $GITHUB_OUTPUT
|
||||
|
||||
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
|
||||
outputs:
|
||||
download-url: ${{ steps.transfer.outputs.download-url }}
|
||||
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') }}
|
||||
- name: Get transfer
|
||||
shell: bash
|
||||
run: |
|
||||
wget -qO- https://github.com/Mikubill/transfer/releases/download/v0.4.17/transfer_0.4.17_darwin_amd64.tar.gz | tar xvz
|
||||
- name: Upload to WeTransfer
|
||||
id: transfer
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
max_attempts: 5
|
||||
retry_wait_seconds: 10
|
||||
timeout_minutes: 5
|
||||
command: |
|
||||
./transfer wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log
|
||||
echo "download-url=$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" >> $GITHUB_OUTPUT
|
||||
|
||||
build-macos-portable:
|
||||
runs-on: macos-latest
|
||||
@@ -548,8 +324,6 @@ jobs:
|
||||
env:
|
||||
OUTPUT_FOLDER: ./build
|
||||
RELEASE_FOLDER: ./release
|
||||
outputs:
|
||||
download-url: ${{ steps.transfer.outputs.download-url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -576,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
|
||||
@@ -584,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 }}
|
||||
@@ -638,20 +419,34 @@ jobs:
|
||||
body: ${{ needs.get-info.outputs.update-note }}
|
||||
name: ${{ needs.get-info.outputs.update-title }}
|
||||
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
|
||||
- name: Get transfer
|
||||
shell: bash
|
||||
run: |
|
||||
wget -qO- https://github.com/Mikubill/transfer/releases/download/v0.4.17/transfer_0.4.17_darwin_amd64.tar.gz | tar xvz
|
||||
- name: Upload to WeTransfer
|
||||
id: transfer
|
||||
uses: nick-fields/retry@v2
|
||||
|
||||
build-web:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [get-info, build-core, auto-test]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
max_attempts: 5
|
||||
retry_wait_seconds: 10
|
||||
timeout_minutes: 5
|
||||
command: |
|
||||
./transfer wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log
|
||||
echo "download-url=$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" >> $GITHUB_OUTPUT
|
||||
submodules: recursive
|
||||
- name: Download core love package
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
|
||||
- name: Move core love package
|
||||
run: |
|
||||
mv ${{ env.CORE_LOVE_PACKAGE_PATH }} ./.github/build/web/game.data
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: crazy-max/ghaction-github-pages@v3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
build_dir: ./.github/build/web/
|
||||
keep_history: false
|
||||
target_branch: web-dev
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ needs.get-info.outputs.base-name }}_Web_PWA
|
||||
path: ./.github/build/web/
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
@@ -659,8 +454,6 @@ jobs:
|
||||
env:
|
||||
OUTPUT_FOLDER: ./build
|
||||
RELEASE_FOLDER: ./release
|
||||
outputs:
|
||||
download-url: ${{ steps.transfer.outputs.download-url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -702,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/
|
||||
@@ -744,25 +537,6 @@ jobs:
|
||||
body: ${{ needs.get-info.outputs.update-note }}
|
||||
name: ${{ needs.get-info.outputs.update-title }}
|
||||
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
|
||||
- name: Get transfer
|
||||
env:
|
||||
TEMP_PATH: ./temp.zip
|
||||
shell: bash
|
||||
run: |
|
||||
curl -sL --retry 5 https://github.com/Mikubill/transfer/releases/download/v0.4.17/transfer_0.4.17_windows_amd64.zip -o ${{ env.TEMP_PATH }}
|
||||
7z x ${{ env.TEMP_PATH }} -o./
|
||||
rm ${{ env.TEMP_PATH }}
|
||||
- name: Upload to WeTransfer
|
||||
id: transfer
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
max_attempts: 5
|
||||
retry_wait_seconds: 10
|
||||
shell: pwsh
|
||||
timeout_minutes: 5
|
||||
command: |
|
||||
./transfer.exe wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log
|
||||
"download-url=$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" >> $env:GITHUB_OUTPUT
|
||||
|
||||
post-build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -773,49 +547,21 @@ jobs:
|
||||
auto-test,
|
||||
build-core,
|
||||
build-android,
|
||||
build-ios,
|
||||
build-linux,
|
||||
build-macos-appstore,
|
||||
build-macos-portable,
|
||||
build-web,
|
||||
build-windows,
|
||||
]
|
||||
env:
|
||||
ACTION_TYPE: ${{ fromJSON('[["Development", "Pre-release"], ["Release", "Release"]]')[startsWith(github.ref, 'refs/tags/v')][startsWith(github.ref, 'refs/tags/pre')] }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Cleanup
|
||||
uses: geekyeggo/delete-artifact@v2
|
||||
with:
|
||||
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
|
||||
- name: Display summary
|
||||
shell: bash
|
||||
run: |
|
||||
echo "# Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Version: ${{ needs.get-info.outputs.version-string }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Package Name: ${{ needs.get-info.outputs.base-name }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Download links: " >> $GITHUB_STEP_SUMMARY
|
||||
- name: Display download links
|
||||
shell: python3 {0}
|
||||
run: |
|
||||
import os
|
||||
with open(os.getenv('GITHUB_STEP_SUMMARY'), 'a') as f:
|
||||
if "${{ needs.build-core.result }}" == "success":
|
||||
f.write("- Bare love packages: [WeTransfer](${{ needs.build-core.outputs.download-url }})\n")
|
||||
if "${{ needs.build-android.result }}" == "success":
|
||||
f.write("- Android packages: [WeTransfer](${{ needs.build-android.outputs.download-url }})\n")
|
||||
if "${{ needs.build-ios.result }}" == "success":
|
||||
f.write("- iOS packages: [WeTransfer](${{ needs.build-ios.outputs.download-url }})\n")
|
||||
if "${{ needs.build-linux.result }}" == "success":
|
||||
f.write("- Linux packages: [WeTransfer](${{ needs.build-linux.outputs.download-url }})\n")
|
||||
if "${{ needs.build-macos-appstore.result }}" == "success":
|
||||
f.write("- macOS packages(App Store version): [WeTransfer](${{ needs.build-macos-appstore.outputs.download-url }})\n")
|
||||
if "${{ needs.build-macos-portable.result }}" == "success":
|
||||
f.write("- macOS packages(Portable version): [WeTransfer](${{ needs.build-macos-portable.outputs.download-url }})\n")
|
||||
if "${{ needs.build-windows.result }}" == "success":
|
||||
f.write("- Windows packages: [WeTransfer](${{ needs.build-windows.outputs.download-url }})\n")
|
||||
- name: Send Discord message
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: Sniddl/discord-commits@v1.5
|
||||
uses: Sniddl/discord-commits@v1.6
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
message: "Github Actions for **${{ github.repository }}**."
|
||||
@@ -834,7 +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":"Download Links","value":"**Core:** ${{ needs.build-core.outputs.download-url}}\n**Android:** ${{ needs.build-android.outputs.download-url }}\n**iOS:** ${{ needs.build-ios.outputs.download-url }}\n**Linux:** ${{ needs.build-linux.outputs.download-url }}\n**macOS App Store:** ${{ needs.build-macos-appstore.outputs.download-url }}\n**macOS portable:** ${{ needs.build-macos-portable.outputs.download-url }}\n**Windows:** ${{ needs.build-windows.outputs.download-url}}"}
|
||||
{"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
@@ -1,5 +1,4 @@
|
||||
.vscode
|
||||
libAndroid
|
||||
*.ini
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "Zframework"]
|
||||
path = Zframework
|
||||
url = https://github.com/26F-Studio/Zframework.git
|
||||
57
Zframework/background.lua
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,Ǵ=ǵ,Ǹ=ǹ,Ǻ=ǻ,Ǽ=ǽ,Ǿ=ǿ,Ȁ=ȁ,Ȃ=ȃ,Ȅ=ȅ,Ȇ=ȇ,Ȉ=ȉ,Ȋ=ȋ,Ȍ=ȍ,Ȏ=ȏ,Ȑ=ȑ,Ȓ=ȓ,Ȕ=ȕ,Ȗ=ȗ,Ș=ș,Ț=ț,Ȝ=ȝ,Ȟ=ȟ,Ȣ=ȣ,Ȥ=ȥ,Ȧ=ȧ,Ȩ=ȩ,Ȫ=ȫ,Ȭ=ȭ,Ȯ=ȯ,Ȱ=ȱ,Ȳ=ȳ,Ȼ=ȼ,Ȿ=ȿ,Ɀ=ɀ,Ɂ=ɂ,Ɇ=ɇ,Ɉ=ɉ,Ɋ=ɋ,Ɍ=ɍ,Ɏ=ɏ
|
||||
Ɐ=ɐ,Ɑ=ɑ,Ɒ=ɒ,Ɓ=ɓ,Ɔ=ɔ,Ɖ=ɖ,Ɗ=ɗ,Ə=ə,Ɛ=ɛ,Ɜ=ɜ,Ɠ=ɠ,Ɡ=ɡ,Ɣ=ɣ,Ɥ=ɥ,Ɦ=ɦ,Ɨ=ɨ,Ɩ=ɩ,Ɪ=ɪ,Ɫ=ɫ,Ɬ=ɬ,Ɯ=ɯ,Ɱ=ɱ,Ɲ=ɲ,Ɵ=ɵ,Ɽ=ɽ,Ʀ=ʀ,Ʂ=ʂ,Ʃ=ʃ,Ʇ=ʇ,Ʈ=ʈ,Ʉ=ʉ,Ʊ=ʊ,Ʋ=ʋ,Ʌ=ʌ,Ʒ=ʒ,Ʝ=ʝ,Ʞ=ʞ
|
||||
Ͱ=ͱ,Ͳ=ͳ,Ͷ=ͷ,Ͻ=ͻ,Ͼ=ͼ,Ͽ=ͽ,Ά=ά,Έ=έ,Ή=ή,Ί=ί,Α=α,Β=β,Γ=γ,Δ=δ,Ε=ε,Ζ=ζ,Η=η,Θ=θ,Κ=κ,Λ=λ,Ν=ν,Ξ=ξ,Ο=ο,Π=π,Ρ=ρ,Σ=σ,Τ=τ,Υ=υ,Φ=φ,Χ=χ,Ψ=ψ,Ω=ω,Ϊ=ϊ,Ϋ=ϋ,Ό=ό,Ύ=ύ,Ώ=ώ,Ϗ=ϗ,Ϙ=ϙ,Ϛ=ϛ,Ϝ=ϝ,Ϟ=ϟ,Ϡ=ϡ,Ϣ=ϣ,Ϥ=ϥ,Ϧ=ϧ,Ϩ=ϩ,Ϫ=ϫ,Ϭ=ϭ,Ϯ=ϯ,Ϲ=ϲ,Ϳ=ϳ,Ϸ=ϸ,Ϻ=ϻ
|
||||
А=а,Б=б,В=в,Г=г,Д=д,Е=е,Ж=ж,З=з,И=и,Й=й,К=к,Л=л,М=м,Н=н,О=о,П=п,Р=р,С=с,Т=т,У=у,Ф=ф,Х=х,Ц=ц,Ч=ч,Ш=ш,Щ=щ,Ъ=ъ,Ы=ы,Ь=ь,Э=э,Ю=ю,Я=я,Ѐ=ѐ,Ё=ё,Ђ=ђ,Ѓ=ѓ,Є=є,Ѕ=ѕ,І=і,Ї=ї,Ј=ј,Љ=љ,Њ=њ,Ћ=ћ,Ќ=ќ,Ѝ=ѝ,Ў=ў,Џ=џ,Ѡ=ѡ,Ѣ=ѣ,Ѥ=ѥ,Ѧ=ѧ,Ѩ=ѩ,Ѫ=ѫ,Ѭ=ѭ,Ѯ=ѯ,Ѱ=ѱ,Ѳ=ѳ,Ѵ=ѵ,Ѷ=ѷ,Ѹ=ѹ,Ѻ=ѻ,Ѽ=ѽ,Ѿ=ѿ,Ҁ=ҁ,Ҋ=ҋ,Ҍ=ҍ,Ҏ=ҏ,Ґ=ґ,Ғ=ғ,Ҕ=ҕ,Җ=җ,Ҙ=ҙ,Қ=қ,Ҝ=ҝ,Ҟ=ҟ,Ҡ=ҡ,Ң=ң,Ҥ=ҥ,Ҧ=ҧ,Ҩ=ҩ,Ҫ=ҫ,Ҭ=ҭ,Ү=ү,Ұ=ұ,Ҳ=ҳ,Ҵ=ҵ,Ҷ=ҷ,Ҹ=ҹ,Һ=һ,Ҽ=ҽ,Ҿ=ҿ,Ӂ=ӂ,Ӄ=ӄ,Ӆ=ӆ,Ӈ=ӈ,Ӊ=ӊ,Ӌ=ӌ,Ӎ=ӎ,Ӏ=ӏ,Ӑ=ӑ,Ӓ=ӓ,Ӕ=ӕ,Ӗ=ӗ,Ә=ә,Ӛ=ӛ,Ӝ=ӝ,Ӟ=ӟ,Ӡ=ӡ,Ӣ=ӣ,Ӥ=ӥ,Ӧ=ӧ,Ө=ө,Ӫ=ӫ,Ӭ=ӭ,Ӯ=ӯ,Ӱ=ӱ,Ӳ=ӳ,Ӵ=ӵ,Ӷ=ӷ,Ӹ=ӹ,Ӻ=ӻ,Ӽ=ӽ,Ӿ=ӿ,Ԁ=ԁ,Ԃ=ԃ,Ԅ=ԅ,Ԇ=ԇ,Ԉ=ԉ,Ԋ=ԋ,Ԍ=ԍ,Ԏ=ԏ,Ԑ=ԑ,Ԓ=ԓ,Ԕ=ԕ,Ԗ=ԗ,Ԙ=ԙ,Ԛ=ԛ,Ԝ=ԝ,Ԟ=ԟ,Ԡ=ԡ,Ԣ=ԣ,Ԥ=ԥ,Ԧ=ԧ,Ԩ=ԩ,Ԫ=ԫ,Ԭ=ԭ,Ԯ=ԯ
|
||||
Ա=ա,Բ=բ,Գ=գ,Դ=դ,Ե=ե,Զ=զ,Է=է,Ը=ը,Թ=թ,Ժ=ժ,Ի=ի,Լ=լ,Խ=խ,Ծ=ծ,Կ=կ,Հ=հ,Ձ=ձ,Ղ=ղ,Ճ=ճ,Մ=մ,Յ=յ,Ն=ն,Շ=շ,Ո=ո,Չ=չ,Պ=պ,Ջ=ջ,Ռ=ռ,Ս=ս,Վ=վ,Տ=տ,Ր=ր,Ց=ց,Ւ=ւ,Փ=փ,Ք=ք,Օ=օ,Ֆ=ֆ
|
||||
Ა=ა,Ბ=ბ,Გ=გ,Დ=დ,Ე=ე,Ვ=ვ,Ზ=ზ,Თ=თ,Ი=ი,Კ=კ,Ლ=ლ,Მ=მ,Ნ=ნ,Ო=ო,Პ=პ,Ჟ=ჟ,Რ=რ,Ს=ს,Ტ=ტ,Უ=უ,Ფ=ფ,Ქ=ქ,Ღ=ღ,Ყ=ყ,Შ=შ,Ჩ=ჩ,Ც=ც,Ძ=ძ,Წ=წ,Ჭ=ჭ,Ხ=ხ,Ჯ=ჯ,Ჰ=ჰ,Ჱ=ჱ,Ჲ=ჲ,Ჳ=ჳ,Ჴ=ჴ,Ჵ=ჵ,Ჶ=ჶ,Ჷ=ჷ,Ჸ=ჸ,Ჹ=ჹ,Ჺ=ჺ,Ჽ=ჽ,Ჾ=ჾ,Ჿ=ჿ
|
||||
Ᏸ=ᏸ,Ᏹ=ᏹ,Ᏺ=ᏺ,Ᏻ=ᏻ,Ᏼ=ᏼ,Ᏽ=ᏽ
|
||||
Ꙋ=ᲈ,Ᵹ=ᵹ,Ᵽ=ᵽ,Ᶎ=ᶎ,Ḁ=ḁ,Ḃ=ḃ,Ḅ=ḅ,Ḇ=ḇ,Ḉ=ḉ,Ḋ=ḋ,Ḍ=ḍ,Ḏ=ḏ,Ḑ=ḑ,Ḓ=ḓ,Ḕ=ḕ,Ḗ=ḗ,Ḙ=ḙ,Ḛ=ḛ,Ḝ=ḝ,Ḟ=ḟ,Ḡ=ḡ,Ḣ=ḣ,Ḥ=ḥ,Ḧ=ḧ,Ḩ=ḩ,Ḫ=ḫ,Ḭ=ḭ,Ḯ=ḯ,Ḱ=ḱ,Ḳ=ḳ,Ḵ=ḵ,Ḷ=ḷ,Ḹ=ḹ,Ḻ=ḻ,Ḽ=ḽ,Ḿ=ḿ,Ṁ=ṁ,Ṃ=ṃ,Ṅ=ṅ,Ṇ=ṇ,Ṉ=ṉ,Ṋ=ṋ,Ṍ=ṍ,Ṏ=ṏ,Ṑ=ṑ,Ṓ=ṓ,Ṕ=ṕ,Ṗ=ṗ,Ṙ=ṙ,Ṛ=ṛ,Ṝ=ṝ,Ṟ=ṟ,Ṡ=ṡ,Ṣ=ṣ,Ṥ=ṥ,Ṧ=ṧ,Ṩ=ṩ,Ṫ=ṫ,Ṭ=ṭ,Ṯ=ṯ,Ṱ=ṱ,Ṳ=ṳ,Ṵ=ṵ,Ṷ=ṷ,Ṹ=ṹ,Ṻ=ṻ,Ṽ=ṽ,Ṿ=ṿ,Ẁ=ẁ,Ẃ=ẃ,Ẅ=ẅ,Ẇ=ẇ,Ẉ=ẉ,Ẋ=ẋ,Ẍ=ẍ,Ẏ=ẏ,Ẑ=ẑ,Ẓ=ẓ,Ẕ=ẕ,Ạ=ạ,Ả=ả,Ấ=ấ,Ầ=ầ,Ẩ=ẩ,Ẫ=ẫ,Ậ=ậ,Ắ=ắ,Ằ=ằ,Ẳ=ẳ,Ẵ=ẵ,Ặ=ặ,Ẹ=ẹ,Ẻ=ẻ,Ẽ=ẽ,Ế=ế,Ề=ề,Ể=ể,Ễ=ễ,Ệ=ệ,Ỉ=ỉ,Ị=ị,Ọ=ọ,Ỏ=ỏ,Ố=ố,Ồ=ồ,Ổ=ổ,Ỗ=ỗ,Ộ=ộ,Ớ=ớ,Ờ=ờ,Ở=ở,Ỡ=ỡ,Ợ=ợ,Ụ=ụ,Ủ=ủ,Ứ=ứ,Ừ=ừ,Ử=ử,Ữ=ữ,Ự=ự,Ỳ=ỳ,Ỵ=ỵ,Ỷ=ỷ,Ỹ=ỹ,Ỻ=ỻ,Ỽ=ỽ,Ỿ=ỿ,Ἀ=ἀ,Ἁ=ἁ,Ἂ=ἂ,Ἃ=ἃ,Ἄ=ἄ,Ἅ=ἅ,Ἆ=ἆ,Ἇ=ἇ,Ἐ=ἐ,Ἑ=ἑ,Ἒ=ἒ,Ἓ=ἓ,Ἔ=ἔ,Ἕ=ἕ,Ἠ=ἠ,Ἡ=ἡ,Ἢ=ἢ,Ἣ=ἣ,Ἤ=ἤ,Ἥ=ἥ,Ἦ=ἦ,Ἧ=ἧ,Ἰ=ἰ,Ἱ=ἱ,Ἲ=ἲ,Ἳ=ἳ,Ἴ=ἴ,Ἵ=ἵ,Ἶ=ἶ,Ἷ=ἷ,Ὀ=ὀ,Ὁ=ὁ,Ὂ=ὂ,Ὃ=ὃ,Ὄ=ὄ,Ὅ=ὅ,Ὑ=ὑ,Ὓ=ὓ,Ὕ=ὕ,Ὗ=ὗ,Ὠ=ὠ,Ὡ=ὡ,Ὢ=ὢ,Ὣ=ὣ,Ὤ=ὤ,Ὥ=ὥ,Ὦ=ὦ,Ὧ=ὧ,Ὰ=ὰ,Ά=ά,Ὲ=ὲ,Έ=έ,Ὴ=ὴ,Ή=ή,Ὶ=ὶ,Ί=ί,Ὸ=ὸ,Ό=ό,Ὺ=ὺ,Ύ=ύ,Ὼ=ὼ,Ώ=ώ,Ᾰ=ᾰ,Ᾱ=ᾱ,Ῐ=ῐ,Ῑ=ῑ,Ῠ=ῠ,Ῡ=ῡ,Ῥ=ῥ,Ⅎ=ⅎ
|
||||
Ⅰ=ⅰ,Ⅱ=ⅱ,Ⅲ=ⅲ,Ⅳ=ⅳ,Ⅴ=ⅴ,Ⅵ=ⅵ,Ⅶ=ⅶ,Ⅷ=ⅷ,Ⅸ=ⅸ,Ⅹ=ⅹ,Ⅺ=ⅺ,Ⅻ=ⅻ,Ⅼ=ⅼ,Ⅽ=ⅽ,Ⅾ=ⅾ,Ⅿ=ⅿ,Ↄ=ↄ
|
||||
Ⓐ=ⓐ,Ⓑ=ⓑ,Ⓒ=ⓒ,Ⓓ=ⓓ,Ⓔ=ⓔ,Ⓕ=ⓕ,Ⓖ=ⓖ,Ⓗ=ⓗ,Ⓘ=ⓘ,Ⓙ=ⓙ,Ⓚ=ⓚ,Ⓛ=ⓛ,Ⓜ=ⓜ,Ⓝ=ⓝ,Ⓞ=ⓞ,Ⓟ=ⓟ,Ⓠ=ⓠ,Ⓡ=ⓡ,Ⓢ=ⓢ,Ⓣ=ⓣ,Ⓤ=ⓤ,Ⓥ=ⓥ,Ⓦ=ⓦ,Ⓧ=ⓧ,Ⓨ=ⓨ,Ⓩ=ⓩ
|
||||
Ⰰ=ⰰ,Ⰱ=ⰱ,Ⰲ=ⰲ,Ⰳ=ⰳ,Ⰴ=ⰴ,Ⰵ=ⰵ,Ⰶ=ⰶ,Ⰷ=ⰷ,Ⰸ=ⰸ,Ⰹ=ⰹ,Ⰺ=ⰺ,Ⰻ=ⰻ,Ⰼ=ⰼ,Ⰽ=ⰽ,Ⰾ=ⰾ,Ⰿ=ⰿ,Ⱀ=ⱀ,Ⱁ=ⱁ,Ⱂ=ⱂ,Ⱃ=ⱃ,Ⱄ=ⱄ,Ⱅ=ⱅ,Ⱆ=ⱆ,Ⱇ=ⱇ,Ⱈ=ⱈ,Ⱉ=ⱉ,Ⱊ=ⱊ,Ⱋ=ⱋ,Ⱌ=ⱌ,Ⱍ=ⱍ,Ⱎ=ⱎ,Ⱏ=ⱏ,Ⱐ=ⱐ,Ⱑ=ⱑ,Ⱒ=ⱒ,Ⱓ=ⱓ,Ⱔ=ⱔ,Ⱕ=ⱕ,Ⱖ=ⱖ,Ⱗ=ⱗ,Ⱘ=ⱘ,Ⱙ=ⱙ,Ⱚ=ⱚ,Ⱛ=ⱛ,Ⱜ=ⱜ,Ⱝ=ⱝ,Ⱞ=ⱞ,Ⱟ=ⱟ
|
||||
Ⱡ=ⱡ,Ⱥ=ⱥ,Ⱦ=ⱦ,Ⱨ=ⱨ,Ⱪ=ⱪ,Ⱬ=ⱬ,Ⱳ=ⱳ,Ⱶ=ⱶ
|
||||
Ⲁ=ⲁ,Ⲃ=ⲃ,Ⲅ=ⲅ,Ⲇ=ⲇ,Ⲉ=ⲉ,Ⲋ=ⲋ,Ⲍ=ⲍ,Ⲏ=ⲏ,Ⲑ=ⲑ,Ⲓ=ⲓ,Ⲕ=ⲕ,Ⲗ=ⲗ,Ⲙ=ⲙ,Ⲛ=ⲛ,Ⲝ=ⲝ,Ⲟ=ⲟ,Ⲡ=ⲡ,Ⲣ=ⲣ,Ⲥ=ⲥ,Ⲧ=ⲧ,Ⲩ=ⲩ,Ⲫ=ⲫ,Ⲭ=ⲭ,Ⲯ=ⲯ,Ⲱ=ⲱ,Ⲳ=ⲳ,Ⲵ=ⲵ,Ⲷ=ⲷ,Ⲹ=ⲹ,Ⲻ=ⲻ,Ⲽ=ⲽ,Ⲿ=ⲿ,Ⳁ=ⳁ,Ⳃ=ⳃ,Ⳅ=ⳅ,Ⳇ=ⳇ,Ⳉ=ⳉ,Ⳋ=ⳋ,Ⳍ=ⳍ,Ⳏ=ⳏ,Ⳑ=ⳑ,Ⳓ=ⳓ,Ⳕ=ⳕ,Ⳗ=ⳗ,Ⳙ=ⳙ,Ⳛ=ⳛ,Ⳝ=ⳝ,Ⳟ=ⳟ,Ⳡ=ⳡ,Ⳣ=ⳣ,Ⳬ=ⳬ,Ⳮ=ⳮ,Ⳳ=ⳳ
|
||||
Ⴀ=ⴀ,Ⴁ=ⴁ,Ⴂ=ⴂ,Ⴃ=ⴃ,Ⴄ=ⴄ,Ⴅ=ⴅ,Ⴆ=ⴆ,Ⴇ=ⴇ,Ⴈ=ⴈ,Ⴉ=ⴉ,Ⴊ=ⴊ,Ⴋ=ⴋ,Ⴌ=ⴌ,Ⴍ=ⴍ,Ⴎ=ⴎ,Ⴏ=ⴏ,Ⴐ=ⴐ,Ⴑ=ⴑ,Ⴒ=ⴒ,Ⴓ=ⴓ,Ⴔ=ⴔ,Ⴕ=ⴕ,Ⴖ=ⴖ,Ⴗ=ⴗ,Ⴘ=ⴘ,Ⴙ=ⴙ,Ⴚ=ⴚ,Ⴛ=ⴛ,Ⴜ=ⴜ,Ⴝ=ⴝ,Ⴞ=ⴞ,Ⴟ=ⴟ,Ⴠ=ⴠ,Ⴡ=ⴡ,Ⴢ=ⴢ,Ⴣ=ⴣ,Ⴤ=ⴤ,Ⴥ=ⴥ,Ⴧ=ⴧ
|
||||
Ⴭ=ⴭ,Ꙁ=ꙁ,Ꙃ=ꙃ,Ꙅ=ꙅ,Ꙇ=ꙇ,Ꙉ=ꙉ,Ꙍ=ꙍ,Ꙏ=ꙏ,Ꙑ=ꙑ,Ꙓ=ꙓ,Ꙕ=ꙕ,Ꙗ=ꙗ,Ꙙ=ꙙ,Ꙛ=ꙛ,Ꙝ=ꙝ,Ꙟ=ꙟ,Ꙡ=ꙡ,Ꙣ=ꙣ,Ꙥ=ꙥ,Ꙧ=ꙧ,Ꙩ=ꙩ,Ꙫ=ꙫ,Ꙭ=ꙭ,Ꚁ=ꚁ,Ꚃ=ꚃ,Ꚅ=ꚅ,Ꚇ=ꚇ,Ꚉ=ꚉ,Ꚋ=ꚋ,Ꚍ=ꚍ,Ꚏ=ꚏ,Ꚑ=ꚑ,Ꚓ=ꚓ,Ꚕ=ꚕ,Ꚗ=ꚗ,Ꚙ=ꚙ,Ꚛ=ꚛ,Ꜣ=ꜣ,Ꜥ=ꜥ,Ꜧ=ꜧ,Ꜩ=ꜩ,Ꜫ=ꜫ,Ꜭ=ꜭ,Ꜯ=ꜯ,Ꜳ=ꜳ,Ꜵ=ꜵ,Ꜷ=ꜷ,Ꜹ=ꜹ,Ꜻ=ꜻ,Ꜽ=ꜽ,Ꜿ=ꜿ,Ꝁ=ꝁ,Ꝃ=ꝃ,Ꝅ=ꝅ,Ꝇ=ꝇ,Ꝉ=ꝉ,Ꝋ=ꝋ,Ꝍ=ꝍ,Ꝏ=ꝏ,Ꝑ=ꝑ,Ꝓ=ꝓ,Ꝕ=ꝕ,Ꝗ=ꝗ,Ꝙ=ꝙ,Ꝛ=ꝛ,Ꝝ=ꝝ,Ꝟ=ꝟ,Ꝡ=ꝡ,Ꝣ=ꝣ,Ꝥ=ꝥ,Ꝧ=ꝧ,Ꝩ=ꝩ,Ꝫ=ꝫ,Ꝭ=ꝭ,Ꝯ=ꝯ,Ꝺ=ꝺ,Ꝼ=ꝼ,Ꝿ=ꝿ,Ꞁ=ꞁ,Ꞃ=ꞃ,Ꞅ=ꞅ,Ꞇ=ꞇ,Ꞌ=ꞌ,Ꞑ=ꞑ,Ꞓ=ꞓ,Ꞔ=ꞔ,Ꞗ=ꞗ,Ꞙ=ꞙ,Ꞛ=ꞛ,Ꞝ=ꞝ,Ꞟ=ꞟ,Ꞡ=ꞡ,Ꞣ=ꞣ,Ꞥ=ꞥ,Ꞧ=ꞧ,Ꞩ=ꞩ,Ꞵ=ꞵ,Ꞷ=ꞷ,Ꞹ=ꞹ,Ꞻ=ꞻ,Ꞽ=ꞽ,Ꞿ=ꞿ,Ꟁ=ꟁ,Ꟃ=ꟃ,Ꟈ=ꟈ,Ꟊ=ꟊ,Ꟑ=ꟑ,Ꟗ=ꟗ,Ꟙ=ꟙ,Ꟶ=ꟶ,Ꭓ=ꭓ
|
||||
Ꭰ=ꭰ,Ꭱ=ꭱ,Ꭲ=ꭲ,Ꭳ=ꭳ,Ꭴ=ꭴ,Ꭵ=ꭵ,Ꭶ=ꭶ,Ꭷ=ꭷ,Ꭸ=ꭸ,Ꭹ=ꭹ,Ꭺ=ꭺ,Ꭻ=ꭻ,Ꭼ=ꭼ,Ꭽ=ꭽ,Ꭾ=ꭾ,Ꭿ=ꭿ,Ꮀ=ꮀ,Ꮁ=ꮁ,Ꮂ=ꮂ,Ꮃ=ꮃ,Ꮄ=ꮄ,Ꮅ=ꮅ,Ꮆ=ꮆ,Ꮇ=ꮇ,Ꮈ=ꮈ,Ꮉ=ꮉ,Ꮊ=ꮊ,Ꮋ=ꮋ,Ꮌ=ꮌ,Ꮍ=ꮍ,Ꮎ=ꮎ,Ꮏ=ꮏ,Ꮐ=ꮐ,Ꮑ=ꮑ,Ꮒ=ꮒ,Ꮓ=ꮓ,Ꮔ=ꮔ,Ꮕ=ꮕ,Ꮖ=ꮖ,Ꮗ=ꮗ,Ꮘ=ꮘ,Ꮙ=ꮙ,Ꮚ=ꮚ,Ꮛ=ꮛ,Ꮜ=ꮜ,Ꮝ=ꮝ,Ꮞ=ꮞ,Ꮟ=ꮟ,Ꮠ=ꮠ,Ꮡ=ꮡ,Ꮢ=ꮢ,Ꮣ=ꮣ,Ꮤ=ꮤ,Ꮥ=ꮥ,Ꮦ=ꮦ,Ꮧ=ꮧ,Ꮨ=ꮨ,Ꮩ=ꮩ,Ꮪ=ꮪ,Ꮫ=ꮫ,Ꮬ=ꮬ,Ꮭ=ꮭ,Ꮮ=ꮮ,Ꮯ=ꮯ,Ꮰ=ꮰ,Ꮱ=ꮱ,Ꮲ=ꮲ,Ꮳ=ꮳ,Ꮴ=ꮴ,Ꮵ=ꮵ,Ꮶ=ꮶ,Ꮷ=ꮷ,Ꮸ=ꮸ,Ꮹ=ꮹ,Ꮺ=ꮺ,Ꮻ=ꮻ,Ꮼ=ꮼ,Ꮽ=ꮽ,Ꮾ=ꮾ,Ꮿ=ꮿ
|
||||
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
|
||||
𐐀=𐐨,𐐁=𐐩,𐐂=𐐪,𐐃=𐐫,𐐄=𐐬,𐐅=𐐭,𐐆=𐐮,𐐇=𐐯,𐐈=𐐰,𐐉=𐐱,𐐊=𐐲,𐐋=𐐳,𐐌=𐐴,𐐍=𐐵,𐐎=𐐶,𐐏=𐐷,𐐐=𐐸,𐐑=𐐹,𐐒=𐐺,𐐓=𐐻,𐐔=𐐼,𐐕=𐐽,𐐖=𐐾,𐐗=𐐿,𐐘=𐑀,𐐙=𐑁,𐐚=𐑂,𐐛=𐑃,𐐜=𐑄,𐐝=𐑅,𐐞=𐑆,𐐟=𐑇,𐐠=𐑈,𐐡=𐑉,𐐢=𐑊,𐐣=𐑋,𐐤=𐑌,𐐥=𐑍,𐐦=𐑎,𐐧=𐑏
|
||||
𐒰=𐓘,𐒱=𐓙,𐒲=𐓚,𐒳=𐓛,𐒴=𐓜,𐒵=𐓝,𐒶=𐓞,𐒷=𐓟,𐒸=𐓠,𐒹=𐓡,𐒺=𐓢,𐒻=𐓣,𐒼=𐓤,𐒽=𐓥,𐒾=𐓦,𐒿=𐓧,𐓀=𐓨,𐓁=𐓩,𐓂=𐓪,𐓃=𐓫,𐓄=𐓬,𐓅=𐓭,𐓆=𐓮,𐓇=𐓯,𐓈=𐓰,𐓉=𐓱,𐓊=𐓲,𐓋=𐓳,𐓌=𐓴,𐓍=𐓵,𐓎=𐓶,𐓏=𐓷,𐓐=𐓸,𐓑=𐓹,𐓒=𐓺,𐓓=𐓻
|
||||
𐲀=𐳀,𐲁=𐳁,𐲂=𐳂,𐲃=𐳃,𐲄=𐳄,𐲅=𐳅,𐲆=𐳆,𐲇=𐳇,𐲈=𐳈,𐲉=𐳉,𐲊=𐳊,𐲋=𐳋,𐲌=𐳌,𐲍=𐳍,𐲎=𐳎,𐲏=𐳏,𐲐=𐳐,𐲑=𐳑,𐲒=𐳒,𐲓=𐳓,𐲔=𐳔,𐲕=𐳕,𐲖=𐳖,𐲗=𐳗,𐲘=𐳘,𐲙=𐳙,𐲚=𐳚,𐲛=𐳛,𐲜=𐳜,𐲝=𐳝,𐲞=𐳞,𐲟=𐳟,𐲠=𐳠,𐲡=𐳡,𐲢=𐳢,𐲣=𐳣,𐲤=𐳤,𐲥=𐳥,𐲦=𐳦,𐲧=𐳧,𐲨=𐳨,𐲩=𐳩,𐲪=𐳪,𐲫=𐳫,𐲬=𐳬,𐲭=𐳭,𐲮=𐳮,𐲯=𐳯,𐲰=𐳰,𐲱=𐳱,𐲲=𐳲
|
||||
𑢠=𑣀,𑢡=𑣁,𑢢=𑣂,𑢣=𑣃,𑢤=𑣄,𑢥=𑣅,𑢦=𑣆,𑢧=𑣇,𑢨=𑣈,𑢩=𑣉,𑢪=𑣊,𑢫=𑣋,𑢬=𑣌,𑢭=𑣍,𑢮=𑣎,𑢯=𑣏,𑢰=𑣐,𑢱=𑣑,𑢲=𑣒,𑢳=𑣓,𑢴=𑣔,𑢵=𑣕,𑢶=𑣖,𑢷=𑣗,𑢸=𑣘,𑢹=𑣙,𑢺=𑣚,𑢻=𑣛,𑢼=𑣜,𑢽=𑣝,𑢾=𑣞,𑢿=𑣟
|
||||
𖹀=𖹠,𖹁=𖹡,𖹂=𖹢,𖹃=𖹣,𖹄=𖹤,𖹅=𖹥,𖹆=𖹦,𖹇=𖹧,𖹈=𖹨,𖹉=𖹩,𖹊=𖹪,𖹋=𖹫,𖹌=𖹬,𖹍=𖹭,𖹎=𖹮,𖹏=𖹯,𖹐=𖹰,𖹑=𖹱,𖹒=𖹲,𖹓=𖹳,𖹔=𖹴,𖹕=𖹵,𖹖=𖹶,𖹗=𖹷,𖹘=𖹸,𖹙=𖹹,𖹚=𖹺,𖹛=𖹻,𖹜=𖹼,𖹝=𖹽,𖹞=𖹾,𖹟=𖹿
|
||||
𞤀=𞤢,𞤁=𞤣,𞤂=𞤤,𞤃=𞤥,𞤄=𞤦,𞤅=𞤧,𞤆=𞤨,𞤇=𞤩,𞤈=𞤪,𞤉=𞤫,𞤊=𞤬,𞤋=𞤭,𞤌=𞤮,𞤍=𞤯,𞤎=𞤰,𞤏=𞤱,𞤐=𞤲,𞤑=𞤳,𞤒=𞤴,𞤓=𞤵,𞤔=𞤶,𞤕=𞤷,𞤖=𞤸,𞤗=𞤹,𞤘=𞤺,𞤙=𞤻,𞤚=𞤼,𞤛=𞤽,𞤜=𞤾,𞤝=𞤿,𞤞=𞥀,𞤟=𞥁,𞤠=𞥂,𞤡=𞥃
|
||||
İ=i̇,ʼN=ʼn,J̌=ǰ,Ϊ́=ΐ,Ϋ́=ΰ,ԵՒ=և,H̱=ẖ,T̈=ẗ,W̊=ẘ,Y̊=ẙ,Aʾ=ẚ,Υ̓=ὐ,Υ̓̀=ὒ,Υ̓́=ὔ,Υ̓͂=ὖ,ἈΙ=ᾀ,ἉΙ=ᾁ,ἊΙ=ᾂ,ἋΙ=ᾃ,ἌΙ=ᾄ,ἍΙ=ᾅ,ἎΙ=ᾆ,ἏΙ=ᾇ,ἨΙ=ᾐ,ἩΙ=ᾑ,ἪΙ=ᾒ,ἫΙ=ᾓ,ἬΙ=ᾔ,ἭΙ=ᾕ,ἮΙ=ᾖ,ἯΙ=ᾗ,ὨΙ=ᾠ,ὩΙ=ᾡ,ὪΙ=ᾢ,ὫΙ=ᾣ,ὬΙ=ᾤ,ὭΙ=ᾥ,ὮΙ=ᾦ,ὯΙ=ᾧ,ᾺΙ=ᾲ,ΑΙ=ᾳ,ΆΙ=ᾴ,Α͂=ᾶ,Α͂Ι=ᾷ,ῊΙ=ῂ,ΗΙ=ῃ,ΉΙ=ῄ,Η͂=ῆ,Η͂Ι=ῇ,Ϊ̀=ῒ,Ι͂=ῖ,Ϊ͂=ῗ,Ϋ̀=ῢ,Ρ̓=ῤ,Υ͂=ῦ,Ϋ͂=ῧ,ῺΙ=ῲ,ΩΙ=ῳ,ΏΙ=ῴ,Ω͂=ῶ,Ω͂Ι=ῷ,ՄՆ=ﬓ,ՄԵ=ﬔ,ՄԻ=ﬕ,ՎՆ=ﬖ,ՄԽ=ﬗ
|
||||
88
Zframework/mathExtend.lua
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
428
Zframework/stringExtend.lua
Normal 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) -- 1′23.45″
|
||||
elseif t<DAY then
|
||||
return format("%d:%.2d′%04.1f″",h,m,s) -- 12:34′56.7″
|
||||
elseif t<YEAR then
|
||||
return format("%dd %d:%.2d′%.2d″",d,h,m,s) -- 123d 12:34′56″
|
||||
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) -- 1′23.456″
|
||||
elseif t<DAY then
|
||||
return format("%d:%.2d′%05.2f″",h,m,s) -- 12:34′56.78″
|
||||
elseif t<YEAR then
|
||||
return format("%dd %d:%.2d′%04.1f″",d,h,m,s) -- 123d 12:34′56.7″
|
||||
else
|
||||
return format("%dy %dd %d:%.2d′%.2d″",y,d,h,m,s) -- 1y 234d 12:34′56″
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,ǵ=Ǵ,ǹ=Ǹ,ǻ=Ǻ,ǽ=Ǽ,ǿ=Ǿ,ȁ=Ȁ,ȃ=Ȃ,ȅ=Ȅ,ȇ=Ȇ,ȉ=Ȉ,ȋ=Ȋ,ȍ=Ȍ,ȏ=Ȏ,ȑ=Ȑ,ȓ=Ȓ,ȕ=Ȕ,ȗ=Ȗ,ș=Ș,ț=Ț,ȝ=Ȝ,ȟ=Ȟ,ȣ=Ȣ,ȥ=Ȥ,ȧ=Ȧ,ȩ=Ȩ,ȫ=Ȫ,ȭ=Ȭ,ȯ=Ȯ,ȱ=Ȱ,ȳ=Ȳ,ȼ=Ȼ,ȿ=Ȿ,ɀ=Ɀ,ɂ=Ɂ,ɇ=Ɇ,ɉ=Ɉ,ɋ=Ɋ,ɍ=Ɍ,ɏ=Ɏ
|
||||
ɐ=Ɐ,ɑ=Ɑ,ɒ=Ɒ,ɓ=Ɓ,ɔ=Ɔ,ɖ=Ɖ,ɗ=Ɗ,ə=Ə,ɛ=Ɛ,ɜ=Ɜ,ɠ=Ɠ,ɡ=Ɡ,ɣ=Ɣ,ɥ=Ɥ,ɦ=Ɦ,ɨ=Ɨ,ɩ=Ɩ,ɪ=Ɪ,ɫ=Ɫ,ɬ=Ɬ,ɯ=Ɯ,ɱ=Ɱ,ɲ=Ɲ,ɵ=Ɵ,ɽ=Ɽ,ʀ=Ʀ,ʂ=Ʂ,ʃ=Ʃ,ʇ=Ʇ,ʈ=Ʈ,ʉ=Ʉ,ʊ=Ʊ,ʋ=Ʋ,ʌ=Ʌ,ʒ=Ʒ,ʝ=Ʝ,ʞ=Ʞ
|
||||
ͱ=Ͱ,ͳ=Ͳ,ͷ=Ͷ,ͻ=Ͻ,ͼ=Ͼ,ͽ=Ͽ,ά=Ά,έ=Έ,ή=Ή,ί=Ί,α=Α,β=Β,γ=Γ,δ=Δ,ε=Ε,ζ=Ζ,η=Η,θ=Θ,κ=Κ,λ=Λ,ν=Ν,ξ=Ξ,ο=Ο,π=Π,ρ=Ρ,σ=Σ,τ=Τ,υ=Υ,φ=Φ,χ=Χ,ψ=Ψ,ω=Ω,ϊ=Ϊ,ϋ=Ϋ,ό=Ό,ύ=Ύ,ώ=Ώ,ϗ=Ϗ,ϙ=Ϙ,ϛ=Ϛ,ϝ=Ϝ,ϟ=Ϟ,ϡ=Ϡ,ϣ=Ϣ,ϥ=Ϥ,ϧ=Ϧ,ϩ=Ϩ,ϫ=Ϫ,ϭ=Ϭ,ϯ=Ϯ,ϲ=Ϲ,ϳ=Ϳ,ϸ=Ϸ,ϻ=Ϻ
|
||||
а=А,б=Б,в=В,г=Г,д=Д,е=Е,ж=Ж,з=З,и=И,й=Й,к=К,л=Л,м=М,н=Н,о=О,п=П,р=Р,с=С,т=Т,у=У,ф=Ф,х=Х,ц=Ц,ч=Ч,ш=Ш,щ=Щ,ъ=Ъ,ы=Ы,ь=Ь,э=Э,ю=Ю,я=Я,ѐ=Ѐ,ё=Ё,ђ=Ђ,ѓ=Ѓ,є=Є,ѕ=Ѕ,і=І,ї=Ї,ј=Ј,љ=Љ,њ=Њ,ћ=Ћ,ќ=Ќ,ѝ=Ѝ,ў=Ў,џ=Џ,ѡ=Ѡ,ѣ=Ѣ,ѥ=Ѥ,ѧ=Ѧ,ѩ=Ѩ,ѫ=Ѫ,ѭ=Ѭ,ѯ=Ѯ,ѱ=Ѱ,ѳ=Ѳ,ѵ=Ѵ,ѷ=Ѷ,ѹ=Ѹ,ѻ=Ѻ,ѽ=Ѽ,ѿ=Ѿ,ҁ=Ҁ,ҋ=Ҋ,ҍ=Ҍ,ҏ=Ҏ,ґ=Ґ,ғ=Ғ,ҕ=Ҕ,җ=Җ,ҙ=Ҙ,қ=Қ,ҝ=Ҝ,ҟ=Ҟ,ҡ=Ҡ,ң=Ң,ҥ=Ҥ,ҧ=Ҧ,ҩ=Ҩ,ҫ=Ҫ,ҭ=Ҭ,ү=Ү,ұ=Ұ,ҳ=Ҳ,ҵ=Ҵ,ҷ=Ҷ,ҹ=Ҹ,һ=Һ,ҽ=Ҽ,ҿ=Ҿ,ӂ=Ӂ,ӄ=Ӄ,ӆ=Ӆ,ӈ=Ӈ,ӊ=Ӊ,ӌ=Ӌ,ӎ=Ӎ,ӏ=Ӏ,ӑ=Ӑ,ӓ=Ӓ,ӕ=Ӕ,ӗ=Ӗ,ә=Ә,ӛ=Ӛ,ӝ=Ӝ,ӟ=Ӟ,ӡ=Ӡ,ӣ=Ӣ,ӥ=Ӥ,ӧ=Ӧ,ө=Ө,ӫ=Ӫ,ӭ=Ӭ,ӯ=Ӯ,ӱ=Ӱ,ӳ=Ӳ,ӵ=Ӵ,ӷ=Ӷ,ӹ=Ӹ,ӻ=Ӻ,ӽ=Ӽ,ӿ=Ӿ,ԁ=Ԁ,ԃ=Ԃ,ԅ=Ԅ,ԇ=Ԇ,ԉ=Ԉ,ԋ=Ԋ,ԍ=Ԍ,ԏ=Ԏ,ԑ=Ԑ,ԓ=Ԓ,ԕ=Ԕ,ԗ=Ԗ,ԙ=Ԙ,ԛ=Ԛ,ԝ=Ԝ,ԟ=Ԟ,ԡ=Ԡ,ԣ=Ԣ,ԥ=Ԥ,ԧ=Ԧ,ԩ=Ԩ,ԫ=Ԫ,ԭ=Ԭ,ԯ=Ԯ
|
||||
ա=Ա,բ=Բ,գ=Գ,դ=Դ,ե=Ե,զ=Զ,է=Է,ը=Ը,թ=Թ,ժ=Ժ,ի=Ի,լ=Լ,խ=Խ,ծ=Ծ,կ=Կ,հ=Հ,ձ=Ձ,ղ=Ղ,ճ=Ճ,մ=Մ,յ=Յ,ն=Ն,շ=Շ,ո=Ո,չ=Չ,պ=Պ,ջ=Ջ,ռ=Ռ,ս=Ս,վ=Վ,տ=Տ,ր=Ր,ց=Ց,ւ=Ւ,փ=Փ,ք=Ք,օ=Օ,ֆ=Ֆ
|
||||
ა=Ა,ბ=Ბ,გ=Გ,დ=Დ,ე=Ე,ვ=Ვ,ზ=Ზ,თ=Თ,ი=Ი,კ=Კ,ლ=Ლ,მ=Მ,ნ=Ნ,ო=Ო,პ=Პ,ჟ=Ჟ,რ=Რ,ს=Ს,ტ=Ტ,უ=Უ,ფ=Ფ,ქ=Ქ,ღ=Ღ,ყ=Ყ,შ=Შ,ჩ=Ჩ,ც=Ც,ძ=Ძ,წ=Წ,ჭ=Ჭ,ხ=Ხ,ჯ=Ჯ,ჰ=Ჰ,ჱ=Ჱ,ჲ=Ჲ,ჳ=Ჳ,ჴ=Ჴ,ჵ=Ჵ,ჶ=Ჶ,ჷ=Ჷ,ჸ=Ჸ,ჹ=Ჹ,ჺ=Ჺ,ჽ=Ჽ,ჾ=Ჾ,ჿ=Ჿ
|
||||
ᏸ=Ᏸ,ᏹ=Ᏹ,ᏺ=Ᏺ,ᏻ=Ᏻ,ᏼ=Ᏼ,ᏽ=Ᏽ
|
||||
ᲈ=Ꙋ,ᵹ=Ᵹ,ᵽ=Ᵽ,ᶎ=Ᶎ,ḁ=Ḁ,ḃ=Ḃ,ḅ=Ḅ,ḇ=Ḇ,ḉ=Ḉ,ḋ=Ḋ,ḍ=Ḍ,ḏ=Ḏ,ḑ=Ḑ,ḓ=Ḓ,ḕ=Ḕ,ḗ=Ḗ,ḙ=Ḙ,ḛ=Ḛ,ḝ=Ḝ,ḟ=Ḟ,ḡ=Ḡ,ḣ=Ḣ,ḥ=Ḥ,ḧ=Ḧ,ḩ=Ḩ,ḫ=Ḫ,ḭ=Ḭ,ḯ=Ḯ,ḱ=Ḱ,ḳ=Ḳ,ḵ=Ḵ,ḷ=Ḷ,ḹ=Ḹ,ḻ=Ḻ,ḽ=Ḽ,ḿ=Ḿ,ṁ=Ṁ,ṃ=Ṃ,ṅ=Ṅ,ṇ=Ṇ,ṉ=Ṉ,ṋ=Ṋ,ṍ=Ṍ,ṏ=Ṏ,ṑ=Ṑ,ṓ=Ṓ,ṕ=Ṕ,ṗ=Ṗ,ṙ=Ṙ,ṛ=Ṛ,ṝ=Ṝ,ṟ=Ṟ,ṡ=Ṡ,ṣ=Ṣ,ṥ=Ṥ,ṧ=Ṧ,ṩ=Ṩ,ṫ=Ṫ,ṭ=Ṭ,ṯ=Ṯ,ṱ=Ṱ,ṳ=Ṳ,ṵ=Ṵ,ṷ=Ṷ,ṹ=Ṹ,ṻ=Ṻ,ṽ=Ṽ,ṿ=Ṿ,ẁ=Ẁ,ẃ=Ẃ,ẅ=Ẅ,ẇ=Ẇ,ẉ=Ẉ,ẋ=Ẋ,ẍ=Ẍ,ẏ=Ẏ,ẑ=Ẑ,ẓ=Ẓ,ẕ=Ẕ,ạ=Ạ,ả=Ả,ấ=Ấ,ầ=Ầ,ẩ=Ẩ,ẫ=Ẫ,ậ=Ậ,ắ=Ắ,ằ=Ằ,ẳ=Ẳ,ẵ=Ẵ,ặ=Ặ,ẹ=Ẹ,ẻ=Ẻ,ẽ=Ẽ,ế=Ế,ề=Ề,ể=Ể,ễ=Ễ,ệ=Ệ,ỉ=Ỉ,ị=Ị,ọ=Ọ,ỏ=Ỏ,ố=Ố,ồ=Ồ,ổ=Ổ,ỗ=Ỗ,ộ=Ộ,ớ=Ớ,ờ=Ờ,ở=Ở,ỡ=Ỡ,ợ=Ợ,ụ=Ụ,ủ=Ủ,ứ=Ứ,ừ=Ừ,ử=Ử,ữ=Ữ,ự=Ự,ỳ=Ỳ,ỵ=Ỵ,ỷ=Ỷ,ỹ=Ỹ,ỻ=Ỻ,ỽ=Ỽ,ỿ=Ỿ,ἀ=Ἀ,ἁ=Ἁ,ἂ=Ἂ,ἃ=Ἃ,ἄ=Ἄ,ἅ=Ἅ,ἆ=Ἆ,ἇ=Ἇ,ἐ=Ἐ,ἑ=Ἑ,ἒ=Ἒ,ἓ=Ἓ,ἔ=Ἔ,ἕ=Ἕ,ἠ=Ἠ,ἡ=Ἡ,ἢ=Ἢ,ἣ=Ἣ,ἤ=Ἤ,ἥ=Ἥ,ἦ=Ἦ,ἧ=Ἧ,ἰ=Ἰ,ἱ=Ἱ,ἲ=Ἲ,ἳ=Ἳ,ἴ=Ἴ,ἵ=Ἵ,ἶ=Ἶ,ἷ=Ἷ,ὀ=Ὀ,ὁ=Ὁ,ὂ=Ὂ,ὃ=Ὃ,ὄ=Ὄ,ὅ=Ὅ,ὑ=Ὑ,ὓ=Ὓ,ὕ=Ὕ,ὗ=Ὗ,ὠ=Ὠ,ὡ=Ὡ,ὢ=Ὢ,ὣ=Ὣ,ὤ=Ὤ,ὥ=Ὥ,ὦ=Ὦ,ὧ=Ὧ,ὰ=Ὰ,ά=Ά,ὲ=Ὲ,έ=Έ,ὴ=Ὴ,ή=Ή,ὶ=Ὶ,ί=Ί,ὸ=Ὸ,ό=Ό,ὺ=Ὺ,ύ=Ύ,ὼ=Ὼ,ώ=Ώ,ᾰ=Ᾰ,ᾱ=Ᾱ,ῐ=Ῐ,ῑ=Ῑ,ῠ=Ῠ,ῡ=Ῡ,ῥ=Ῥ,ⅎ=Ⅎ
|
||||
ⅰ=Ⅰ,ⅱ=Ⅱ,ⅲ=Ⅲ,ⅳ=Ⅳ,ⅴ=Ⅴ,ⅵ=Ⅵ,ⅶ=Ⅶ,ⅷ=Ⅷ,ⅸ=Ⅸ,ⅹ=Ⅹ,ⅺ=Ⅺ,ⅻ=Ⅻ,ⅼ=Ⅼ,ⅽ=Ⅽ,ⅾ=Ⅾ,ⅿ=Ⅿ,ↄ=Ↄ
|
||||
ⓐ=Ⓐ,ⓑ=Ⓑ,ⓒ=Ⓒ,ⓓ=Ⓓ,ⓔ=Ⓔ,ⓕ=Ⓕ,ⓖ=Ⓖ,ⓗ=Ⓗ,ⓘ=Ⓘ,ⓙ=Ⓙ,ⓚ=Ⓚ,ⓛ=Ⓛ,ⓜ=Ⓜ,ⓝ=Ⓝ,ⓞ=Ⓞ,ⓟ=Ⓟ,ⓠ=Ⓠ,ⓡ=Ⓡ,ⓢ=Ⓢ,ⓣ=Ⓣ,ⓤ=Ⓤ,ⓥ=Ⓥ,ⓦ=Ⓦ,ⓧ=Ⓧ,ⓨ=Ⓨ,ⓩ=Ⓩ
|
||||
ⰰ=Ⰰ,ⰱ=Ⰱ,ⰲ=Ⰲ,ⰳ=Ⰳ,ⰴ=Ⰴ,ⰵ=Ⰵ,ⰶ=Ⰶ,ⰷ=Ⰷ,ⰸ=Ⰸ,ⰹ=Ⰹ,ⰺ=Ⰺ,ⰻ=Ⰻ,ⰼ=Ⰼ,ⰽ=Ⰽ,ⰾ=Ⰾ,ⰿ=Ⰿ,ⱀ=Ⱀ,ⱁ=Ⱁ,ⱂ=Ⱂ,ⱃ=Ⱃ,ⱄ=Ⱄ,ⱅ=Ⱅ,ⱆ=Ⱆ,ⱇ=Ⱇ,ⱈ=Ⱈ,ⱉ=Ⱉ,ⱊ=Ⱊ,ⱋ=Ⱋ,ⱌ=Ⱌ,ⱍ=Ⱍ,ⱎ=Ⱎ,ⱏ=Ⱏ,ⱐ=Ⱐ,ⱑ=Ⱑ,ⱒ=Ⱒ,ⱓ=Ⱓ,ⱔ=Ⱔ,ⱕ=Ⱕ,ⱖ=Ⱖ,ⱗ=Ⱗ,ⱘ=Ⱘ,ⱙ=Ⱙ,ⱚ=Ⱚ,ⱛ=Ⱛ,ⱜ=Ⱜ,ⱝ=Ⱝ,ⱞ=Ⱞ,ⱟ=Ⱟ
|
||||
ⱡ=Ⱡ,ⱥ=Ⱥ,ⱦ=Ⱦ,ⱨ=Ⱨ,ⱪ=Ⱪ,ⱬ=Ⱬ,ⱳ=Ⱳ,ⱶ=Ⱶ
|
||||
ⲁ=Ⲁ,ⲃ=Ⲃ,ⲅ=Ⲅ,ⲇ=Ⲇ,ⲉ=Ⲉ,ⲋ=Ⲋ,ⲍ=Ⲍ,ⲏ=Ⲏ,ⲑ=Ⲑ,ⲓ=Ⲓ,ⲕ=Ⲕ,ⲗ=Ⲗ,ⲙ=Ⲙ,ⲛ=Ⲛ,ⲝ=Ⲝ,ⲟ=Ⲟ,ⲡ=Ⲡ,ⲣ=Ⲣ,ⲥ=Ⲥ,ⲧ=Ⲧ,ⲩ=Ⲩ,ⲫ=Ⲫ,ⲭ=Ⲭ,ⲯ=Ⲯ,ⲱ=Ⲱ,ⲳ=Ⲳ,ⲵ=Ⲵ,ⲷ=Ⲷ,ⲹ=Ⲹ,ⲻ=Ⲻ,ⲽ=Ⲽ,ⲿ=Ⲿ,ⳁ=Ⳁ,ⳃ=Ⳃ,ⳅ=Ⳅ,ⳇ=Ⳇ,ⳉ=Ⳉ,ⳋ=Ⳋ,ⳍ=Ⳍ,ⳏ=Ⳏ,ⳑ=Ⳑ,ⳓ=Ⳓ,ⳕ=Ⳕ,ⳗ=Ⳗ,ⳙ=Ⳙ,ⳛ=Ⳛ,ⳝ=Ⳝ,ⳟ=Ⳟ,ⳡ=Ⳡ,ⳣ=Ⳣ,ⳬ=Ⳬ,ⳮ=Ⳮ,ⳳ=Ⳳ
|
||||
ⴀ=Ⴀ,ⴁ=Ⴁ,ⴂ=Ⴂ,ⴃ=Ⴃ,ⴄ=Ⴄ,ⴅ=Ⴅ,ⴆ=Ⴆ,ⴇ=Ⴇ,ⴈ=Ⴈ,ⴉ=Ⴉ,ⴊ=Ⴊ,ⴋ=Ⴋ,ⴌ=Ⴌ,ⴍ=Ⴍ,ⴎ=Ⴎ,ⴏ=Ⴏ,ⴐ=Ⴐ,ⴑ=Ⴑ,ⴒ=Ⴒ,ⴓ=Ⴓ,ⴔ=Ⴔ,ⴕ=Ⴕ,ⴖ=Ⴖ,ⴗ=Ⴗ,ⴘ=Ⴘ,ⴙ=Ⴙ,ⴚ=Ⴚ,ⴛ=Ⴛ,ⴜ=Ⴜ,ⴝ=Ⴝ,ⴞ=Ⴞ,ⴟ=Ⴟ,ⴠ=Ⴠ,ⴡ=Ⴡ,ⴢ=Ⴢ,ⴣ=Ⴣ,ⴤ=Ⴤ,ⴥ=Ⴥ,ⴧ=Ⴧ
|
||||
ⴭ=Ⴭ,ꙁ=Ꙁ,ꙃ=Ꙃ,ꙅ=Ꙅ,ꙇ=Ꙇ,ꙉ=Ꙉ,ꙍ=Ꙍ,ꙏ=Ꙏ,ꙑ=Ꙑ,ꙓ=Ꙓ,ꙕ=Ꙕ,ꙗ=Ꙗ,ꙙ=Ꙙ,ꙛ=Ꙛ,ꙝ=Ꙝ,ꙟ=Ꙟ,ꙡ=Ꙡ,ꙣ=Ꙣ,ꙥ=Ꙥ,ꙧ=Ꙧ,ꙩ=Ꙩ,ꙫ=Ꙫ,ꙭ=Ꙭ,ꚁ=Ꚁ,ꚃ=Ꚃ,ꚅ=Ꚅ,ꚇ=Ꚇ,ꚉ=Ꚉ,ꚋ=Ꚋ,ꚍ=Ꚍ,ꚏ=Ꚏ,ꚑ=Ꚑ,ꚓ=Ꚓ,ꚕ=Ꚕ,ꚗ=Ꚗ,ꚙ=Ꚙ,ꚛ=Ꚛ,ꜣ=Ꜣ,ꜥ=Ꜥ,ꜧ=Ꜧ,ꜩ=Ꜩ,ꜫ=Ꜫ,ꜭ=Ꜭ,ꜯ=Ꜯ,ꜳ=Ꜳ,ꜵ=Ꜵ,ꜷ=Ꜷ,ꜹ=Ꜹ,ꜻ=Ꜻ,ꜽ=Ꜽ,ꜿ=Ꜿ,ꝁ=Ꝁ,ꝃ=Ꝃ,ꝅ=Ꝅ,ꝇ=Ꝇ,ꝉ=Ꝉ,ꝋ=Ꝋ,ꝍ=Ꝍ,ꝏ=Ꝏ,ꝑ=Ꝑ,ꝓ=Ꝓ,ꝕ=Ꝕ,ꝗ=Ꝗ,ꝙ=Ꝙ,ꝛ=Ꝛ,ꝝ=Ꝝ,ꝟ=Ꝟ,ꝡ=Ꝡ,ꝣ=Ꝣ,ꝥ=Ꝥ,ꝧ=Ꝧ,ꝩ=Ꝩ,ꝫ=Ꝫ,ꝭ=Ꝭ,ꝯ=Ꝯ,ꝺ=Ꝺ,ꝼ=Ꝼ,ꝿ=Ꝿ,ꞁ=Ꞁ,ꞃ=Ꞃ,ꞅ=Ꞅ,ꞇ=Ꞇ,ꞌ=Ꞌ,ꞑ=Ꞑ,ꞓ=Ꞓ,ꞔ=Ꞔ,ꞗ=Ꞗ,ꞙ=Ꞙ,ꞛ=Ꞛ,ꞝ=Ꞝ,ꞟ=Ꞟ,ꞡ=Ꞡ,ꞣ=Ꞣ,ꞥ=Ꞥ,ꞧ=Ꞧ,ꞩ=Ꞩ,ꞵ=Ꞵ,ꞷ=Ꞷ,ꞹ=Ꞹ,ꞻ=Ꞻ,ꞽ=Ꞽ,ꞿ=Ꞿ,ꟁ=Ꟁ,ꟃ=Ꟃ,ꟈ=Ꟈ,ꟊ=Ꟊ,ꟑ=Ꟑ,ꟗ=Ꟗ,ꟙ=Ꟙ,ꟶ=Ꟶ,ꭓ=Ꭓ
|
||||
ꭰ=Ꭰ,ꭱ=Ꭱ,ꭲ=Ꭲ,ꭳ=Ꭳ,ꭴ=Ꭴ,ꭵ=Ꭵ,ꭶ=Ꭶ,ꭷ=Ꭷ,ꭸ=Ꭸ,ꭹ=Ꭹ,ꭺ=Ꭺ,ꭻ=Ꭻ,ꭼ=Ꭼ,ꭽ=Ꭽ,ꭾ=Ꭾ,ꭿ=Ꭿ,ꮀ=Ꮀ,ꮁ=Ꮁ,ꮂ=Ꮂ,ꮃ=Ꮃ,ꮄ=Ꮄ,ꮅ=Ꮅ,ꮆ=Ꮆ,ꮇ=Ꮇ,ꮈ=Ꮈ,ꮉ=Ꮉ,ꮊ=Ꮊ,ꮋ=Ꮋ,ꮌ=Ꮌ,ꮍ=Ꮍ,ꮎ=Ꮎ,ꮏ=Ꮏ,ꮐ=Ꮐ,ꮑ=Ꮑ,ꮒ=Ꮒ,ꮓ=Ꮓ,ꮔ=Ꮔ,ꮕ=Ꮕ,ꮖ=Ꮖ,ꮗ=Ꮗ,ꮘ=Ꮘ,ꮙ=Ꮙ,ꮚ=Ꮚ,ꮛ=Ꮛ,ꮜ=Ꮜ,ꮝ=Ꮝ,ꮞ=Ꮞ,ꮟ=Ꮟ,ꮠ=Ꮠ,ꮡ=Ꮡ,ꮢ=Ꮢ,ꮣ=Ꮣ,ꮤ=Ꮤ,ꮥ=Ꮥ,ꮦ=Ꮦ,ꮧ=Ꮧ,ꮨ=Ꮨ,ꮩ=Ꮩ,ꮪ=Ꮪ,ꮫ=Ꮫ,ꮬ=Ꮬ,ꮭ=Ꮭ,ꮮ=Ꮮ,ꮯ=Ꮯ,ꮰ=Ꮰ,ꮱ=Ꮱ,ꮲ=Ꮲ,ꮳ=Ꮳ,ꮴ=Ꮴ,ꮵ=Ꮵ,ꮶ=Ꮶ,ꮷ=Ꮷ,ꮸ=Ꮸ,ꮹ=Ꮹ,ꮺ=Ꮺ,ꮻ=Ꮻ,ꮼ=Ꮼ,ꮽ=Ꮽ,ꮾ=Ꮾ,ꮿ=Ꮿ
|
||||
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
|
||||
𐐨=𐐀,𐐩=𐐁,𐐪=𐐂,𐐫=𐐃,𐐬=𐐄,𐐭=𐐅,𐐮=𐐆,𐐯=𐐇,𐐰=𐐈,𐐱=𐐉,𐐲=𐐊,𐐳=𐐋,𐐴=𐐌,𐐵=𐐍,𐐶=𐐎,𐐷=𐐏,𐐸=𐐐,𐐹=𐐑,𐐺=𐐒,𐐻=𐐓,𐐼=𐐔,𐐽=𐐕,𐐾=𐐖,𐐿=𐐗,𐑀=𐐘,𐑁=𐐙,𐑂=𐐚,𐑃=𐐛,𐑄=𐐜,𐑅=𐐝,𐑆=𐐞,𐑇=𐐟,𐑈=𐐠,𐑉=𐐡,𐑊=𐐢,𐑋=𐐣,𐑌=𐐤,𐑍=𐐥,𐑎=𐐦,𐑏=𐐧
|
||||
𐓘=𐒰,𐓙=𐒱,𐓚=𐒲,𐓛=𐒳,𐓜=𐒴,𐓝=𐒵,𐓞=𐒶,𐓟=𐒷,𐓠=𐒸,𐓡=𐒹,𐓢=𐒺,𐓣=𐒻,𐓤=𐒼,𐓥=𐒽,𐓦=𐒾,𐓧=𐒿,𐓨=𐓀,𐓩=𐓁,𐓪=𐓂,𐓫=𐓃,𐓬=𐓄,𐓭=𐓅,𐓮=𐓆,𐓯=𐓇,𐓰=𐓈,𐓱=𐓉,𐓲=𐓊,𐓳=𐓋,𐓴=𐓌,𐓵=𐓍,𐓶=𐓎,𐓷=𐓏,𐓸=𐓐,𐓹=𐓑,𐓺=𐓒,𐓻=𐓓
|
||||
𐳀=𐲀,𐳁=𐲁,𐳂=𐲂,𐳃=𐲃,𐳄=𐲄,𐳅=𐲅,𐳆=𐲆,𐳇=𐲇,𐳈=𐲈,𐳉=𐲉,𐳊=𐲊,𐳋=𐲋,𐳌=𐲌,𐳍=𐲍,𐳎=𐲎,𐳏=𐲏,𐳐=𐲐,𐳑=𐲑,𐳒=𐲒,𐳓=𐲓,𐳔=𐲔,𐳕=𐲕,𐳖=𐲖,𐳗=𐲗,𐳘=𐲘,𐳙=𐲙,𐳚=𐲚,𐳛=𐲛,𐳜=𐲜,𐳝=𐲝,𐳞=𐲞,𐳟=𐲟,𐳠=𐲠,𐳡=𐲡,𐳢=𐲢,𐳣=𐲣,𐳤=𐲤,𐳥=𐲥,𐳦=𐲦,𐳧=𐲧,𐳨=𐲨,𐳩=𐲩,𐳪=𐲪,𐳫=𐲫,𐳬=𐲬,𐳭=𐲭,𐳮=𐲮,𐳯=𐲯,𐳰=𐲰,𐳱=𐲱,𐳲=𐲲
|
||||
𑣀=𑢠,𑣁=𑢡,𑣂=𑢢,𑣃=𑢣,𑣄=𑢤,𑣅=𑢥,𑣆=𑢦,𑣇=𑢧,𑣈=𑢨,𑣉=𑢩,𑣊=𑢪,𑣋=𑢫,𑣌=𑢬,𑣍=𑢭,𑣎=𑢮,𑣏=𑢯,𑣐=𑢰,𑣑=𑢱,𑣒=𑢲,𑣓=𑢳,𑣔=𑢴,𑣕=𑢵,𑣖=𑢶,𑣗=𑢷,𑣘=𑢸,𑣙=𑢹,𑣚=𑢺,𑣛=𑢻,𑣜=𑢼,𑣝=𑢽,𑣞=𑢾,𑣟=𑢿
|
||||
𖹠=𖹀,𖹡=𖹁,𖹢=𖹂,𖹣=𖹃,𖹤=𖹄,𖹥=𖹅,𖹦=𖹆,𖹧=𖹇,𖹨=𖹈,𖹩=𖹉,𖹪=𖹊,𖹫=𖹋,𖹬=𖹌,𖹭=𖹍,𖹮=𖹎,𖹯=𖹏,𖹰=𖹐,𖹱=𖹑,𖹲=𖹒,𖹳=𖹓,𖹴=𖹔,𖹵=𖹕,𖹶=𖹖,𖹷=𖹗,𖹸=𖹘,𖹹=𖹙,𖹺=𖹚,𖹻=𖹛,𖹼=𖹜,𖹽=𖹝,𖹾=𖹞,𖹿=𖹟
|
||||
𞤢=𞤀,𞤣=𞤁,𞤤=𞤂,𞤥=𞤃,𞤦=𞤄,𞤧=𞤅,𞤨=𞤆,𞤩=𞤇,𞤪=𞤈,𞤫=𞤉,𞤬=𞤊,𞤭=𞤋,𞤮=𞤌,𞤯=𞤍,𞤰=𞤎,𞤱=𞤏,𞤲=𞤐,𞤳=𞤑,𞤴=𞤒,𞤵=𞤓,𞤶=𞤔,𞤷=𞤕,𞤸=𞤖,𞤹=𞤗,𞤺=𞤘,𞤻=𞤙,𞤼=𞤚,𞤽=𞤛,𞤾=𞤜,𞤿=𞤝,𞥀=𞤞,𞥁=𞤟,𞥂=𞤠,𞥃=𞤡
|
||||
i̇=İ,ʼn=ʼN,ǰ=J̌,ΐ=Ϊ́,ΰ=Ϋ́,և=ԵՒ,ẖ=H̱,ẗ=T̈,ẘ=W̊,ẙ=Y̊,ẚ=Aʾ,ὐ=Υ̓,ὒ=Υ̓̀,ὔ=Υ̓́,ὖ=Υ̓͂,ᾀ=ἈΙ,ᾁ=ἉΙ,ᾂ=ἊΙ,ᾃ=ἋΙ,ᾄ=ἌΙ,ᾅ=ἍΙ,ᾆ=ἎΙ,ᾇ=ἏΙ,ᾐ=ἨΙ,ᾑ=ἩΙ,ᾒ=ἪΙ,ᾓ=ἫΙ,ᾔ=ἬΙ,ᾕ=ἭΙ,ᾖ=ἮΙ,ᾗ=ἯΙ,ᾠ=ὨΙ,ᾡ=ὩΙ,ᾢ=ὪΙ,ᾣ=ὫΙ,ᾤ=ὬΙ,ᾥ=ὭΙ,ᾦ=ὮΙ,ᾧ=ὯΙ,ᾲ=ᾺΙ,ᾳ=ΑΙ,ᾴ=ΆΙ,ᾶ=Α͂,ᾷ=Α͂Ι,ῂ=ῊΙ,ῃ=ΗΙ,ῄ=ΉΙ,ῆ=Η͂,ῇ=Η͂Ι,ῒ=Ϊ̀,ῖ=Ι͂,ῗ=Ϊ͂,ῢ=Ϋ̀,ῤ=Ρ̓,ῦ=Υ͂,ῧ=Ϋ͂,ῲ=ῺΙ,ῳ=ΩΙ,ῴ=ΏΙ,ῶ=Ω͂,ῷ=Ω͂Ι,ﬓ=ՄՆ,ﬔ=ՄԵ,ﬕ=ՄԻ,ﬖ=ՎՆ,ﬗ=ՄԽ
|
||||
12
Zframework/vibrate.lua
Normal 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
@@ -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
@@ -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
@@ -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
|
||||
199
Zframework/websocket_thread.lua
Normal 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()
|
||||
19
Zframework/wheelScroll.lua
Normal 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
74
conf.lua
@@ -1,6 +1,16 @@
|
||||
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 OS=='Web' then
|
||||
local oldRead=love.filesystem.read
|
||||
function love.filesystem.read(name,size)
|
||||
if love.filesystem.getInfo(name) then
|
||||
return oldRead(name,size)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function love.conf(t)
|
||||
local identity='Techmino'
|
||||
@@ -20,45 +30,13 @@ function love.conf(t)
|
||||
t.identity=identity -- Saving folder
|
||||
t.version="11.4"
|
||||
t.gammacorrect=false
|
||||
t.appendidentity=true -- Search files in source then in save directory
|
||||
t.appendidentity=true -- Search files in source then in save directory
|
||||
t.accelerometerjoystick=false -- Accelerometer=joystick on ios/android
|
||||
if t.audio then
|
||||
t.audio.mic=false
|
||||
t.audio.mixwithsystem=true
|
||||
end
|
||||
|
||||
local W=t.window
|
||||
W.title="Techmino "..require "version".string
|
||||
if portrait then
|
||||
W.width,W.height=720,1280
|
||||
W.minwidth,W.minheight=360,640
|
||||
else
|
||||
W.width,W.height=1280,720
|
||||
W.minwidth,W.minheight=640,360
|
||||
end
|
||||
|
||||
W.vsync=0 -- Unlimited FPS
|
||||
W.msaa=msaa -- Multi-sampled antialiasing
|
||||
W.depth=0 -- Bits/samp of depth buffer
|
||||
W.stencil=1 -- Bits/samp of stencil buffer
|
||||
W.display=1 -- Monitor ID
|
||||
W.highdpi=true -- High-dpi mode for the window on a Retina display
|
||||
W.x,W.y=nil,nil -- Position of the window
|
||||
|
||||
if fs.getInfo('media/image/icon.png') then
|
||||
W.icon='media/image/icon.png'
|
||||
end
|
||||
|
||||
if MOBILE then
|
||||
W.borderless=true
|
||||
W.resizable=false
|
||||
W.fullscreen=true
|
||||
else
|
||||
W.borderless=false
|
||||
W.resizable=true
|
||||
W.fullscreen=false
|
||||
end
|
||||
|
||||
local M=t.modules
|
||||
M.window,M.system,M.event,M.thread=true,true,true,true
|
||||
M.timer,M.math,M.data=true,true,true
|
||||
@@ -66,4 +44,28 @@ function love.conf(t)
|
||||
M.graphics,M.font,M.image=true,true,true
|
||||
M.mouse,M.touch,M.keyboard,M.joystick=true,true,true,true
|
||||
M.physics=false
|
||||
|
||||
local W=t.window
|
||||
W.vsync=0 -- Unlimited FPS
|
||||
W.msaa=msaa -- Multi-sampled antialiasing
|
||||
W.depth=0 -- Bits/samp of depth buffer
|
||||
W.stencil=1 -- Bits/samp of stencil buffer
|
||||
W.display=1 -- Monitor ID
|
||||
W.highdpi=true -- High-dpi mode for the window on a Retina display
|
||||
W.x,W.y=nil,nil -- Position of the window
|
||||
W.borderless=MOBILE -- Display window frame
|
||||
W.resizable=not MOBILE -- Whether window is resizable
|
||||
|
||||
W.fullscreentype=MOBILE and "exclusive" or "desktop" -- Fullscreen type
|
||||
if portrait then
|
||||
W.width,W.height=720,1280
|
||||
W.minwidth,W.minheight=360,640
|
||||
else
|
||||
W.width,W.height=1280,720
|
||||
W.minwidth,W.minheight=640,360
|
||||
end
|
||||
W.title="Techmino "..require"version".string -- Window title
|
||||
if fs.getInfo('media/image/icon.png') then
|
||||
W.icon='media/image/icon.png'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
310
main.lua
@@ -10,10 +10,8 @@
|
||||
Instructions:
|
||||
1. I made a framework called Zframework, *most* code in Zframework are not directly relevant to game;
|
||||
2. "xxx" are texts for reading by player, 'xxx' are string values just used in program;
|
||||
3. Some goto statement are used for better performance. All goto-labes have detailed names so don't be afraid;
|
||||
4. Except "gcinfo" function of lua itself, other "gc" are short for "graphics";
|
||||
]]--
|
||||
|
||||
3. Except "gcinfo" function of lua itself, other "gc" are short for "graphics";
|
||||
]]
|
||||
|
||||
-- Var leak check
|
||||
-- setmetatable(_G,{__newindex=function(self,k,v) print('>>'..k..string.rep(" ",26-#k),debug.traceback():match("\n.-\n\t(.-): "))rawset(self,k,v) end})
|
||||
@@ -25,7 +23,7 @@ TIME=love.timer.getTime
|
||||
|
||||
-- Global Vars & Settings
|
||||
SFXPACKS={'chiptune'}
|
||||
VOCPACKS={'miya','mono','xiaoya','miku'}
|
||||
VOCPACKS={'miya','mono','xiaoya','flore','neuro','miku','zundamon'}
|
||||
FIRSTLAUNCH=false
|
||||
DAILYLAUNCH=false
|
||||
|
||||
@@ -47,19 +45,16 @@ FONT.load{
|
||||
FONT.setDefault('norm')
|
||||
FONT.setFallback('norm')
|
||||
|
||||
SCR.setSize(1280,720)-- Initialize Screen size
|
||||
SCR.setSize(1280,720) -- Initialize Screen size
|
||||
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)
|
||||
@@ -102,30 +97,32 @@ CHAR=require'parts.char'
|
||||
require'parts.gameTables'
|
||||
require'parts.gameFuncs'
|
||||
|
||||
THEME= require'parts.theme'
|
||||
LINE= require'parts.line'
|
||||
DATA= require'parts.data'
|
||||
THEME =require'parts.theme'
|
||||
LINE =require'parts.line'
|
||||
DATA =require'parts.data'
|
||||
|
||||
TEXTURE= require'parts.texture'
|
||||
SKIN= require'parts.skin'
|
||||
USERS= require'parts.users'
|
||||
NET= require'parts.net'
|
||||
VK= require'parts.virtualKey'
|
||||
BOT= require'parts.bot'
|
||||
RSlist= require'parts.RSlist'; DSCP=RSlist.TRS.centerPos
|
||||
PLY= require'parts.player'
|
||||
NETPLY= require'parts.netPlayer'
|
||||
MODES= require'parts.modes'
|
||||
TEXTURE=require'parts.texture'
|
||||
SKIN =require'parts.skin'
|
||||
USERS =require'parts.users'
|
||||
NET =require'parts.net'
|
||||
VK =require'parts.virtualKey'
|
||||
BOT =require'parts.bot'
|
||||
RSlist =require'parts.RSlist'; DSCP=RSlist.TRS.centerPos
|
||||
PLY =require'parts.player'
|
||||
NETPLY =require'parts.netPlayer'
|
||||
MODES =require'parts.modes'
|
||||
|
||||
setmetatable(TEXTURE,{__index=function(self,k)
|
||||
MES.new('warn',"No texture called: "..k)
|
||||
self[k]=PAPER
|
||||
return self[k]
|
||||
end})
|
||||
setmetatable(TEXTURE,{
|
||||
__index=function(self,k)
|
||||
MES.new('warn',"No texture called: "..k)
|
||||
self[k]=PAPER
|
||||
return self[k]
|
||||
end,
|
||||
})
|
||||
|
||||
-- Load mode files
|
||||
for i=1,#MODES do
|
||||
local m=MODES[i]-- Mode template
|
||||
local m=MODES[i] -- Mode template
|
||||
if FILE.isSafe('parts/modes/'..m.name) then
|
||||
TABLE.complete(require('parts.modes.'..m.name),MODES[i])
|
||||
MODES[m.name],MODES[i]=MODES[i]
|
||||
@@ -145,7 +142,7 @@ end
|
||||
table.insert(_LOADTIMELIST_,("Load Modules: %.3fs"):format(TIME()-_LOADTIME_))
|
||||
|
||||
-- Initialize Zframework
|
||||
do-- Z.setCursor
|
||||
do -- Z.setCursor
|
||||
local normImg=GC.DO{16,16,
|
||||
{'fCirc',8,8,4},
|
||||
{'setCL',1,1,1,.7},
|
||||
@@ -191,18 +188,18 @@ Z.setOnFnKeys({
|
||||
Z.setOnGlobalKey('f11',function()
|
||||
if not MOBILE then
|
||||
SETTING.fullscreen=not SETTING.fullscreen
|
||||
applySettings()
|
||||
applySettings('fullscreen')
|
||||
saveSettings()
|
||||
end
|
||||
end)
|
||||
Z.setVersionText(VERSION.string)
|
||||
Z.setDebugInfo{
|
||||
{"Cache",gcinfo},
|
||||
{"Tasks",TASK.getCount},
|
||||
{"Cache", gcinfo},
|
||||
{"Tasks", TASK.getCount},
|
||||
{"Voices",VOC.getQueueCount},
|
||||
{"Audios",love.audio.getSourceCount},
|
||||
}
|
||||
do-- Z.setOnFocus
|
||||
do -- Z.setOnFocus
|
||||
local function task_autoSoundOff()
|
||||
while true do
|
||||
coroutine.yield()
|
||||
@@ -271,7 +268,10 @@ IMG.init{
|
||||
monoCH='media/image/characters/mono.png',
|
||||
xiaoyaCH='media/image/characters/xiaoya.png',
|
||||
xiaoyaOmino='media/image/characters/xiaoya_Omino.png',
|
||||
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',
|
||||
@@ -294,46 +294,48 @@ 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
|
||||
SFX.init((function()--[Warning] Not loading files here, just get the list of sound needed
|
||||
SFX.init((function() --[Warning] Not loading files here, just get the list of sound needed
|
||||
local L={}
|
||||
for _,v in next,fs.getDirectoryItems('media/effect/chiptune/') do
|
||||
if FILE.isSafe('media/effect/chiptune/'..v,"Dangerous file : %SAVE%/media/effect/chiptune/"..v) then
|
||||
if FILE.isSafe('media/effect/chiptune/'..v) then
|
||||
table.insert(L,v:sub(1,-5))
|
||||
end
|
||||
end
|
||||
@@ -342,7 +344,7 @@ end)())
|
||||
BGM.init((function()
|
||||
local L={}
|
||||
for _,v in next,fs.getDirectoryItems('media/music') do
|
||||
if FILE.isSafe('media/music/'..v,"Dangerous file : %SAVE%/media/music/"..v) then
|
||||
if FILE.isSafe('media/music/'..v) then
|
||||
L[v:sub(1,-5)]='media/music/'..v
|
||||
end
|
||||
end
|
||||
@@ -350,7 +352,7 @@ BGM.init((function()
|
||||
end)())
|
||||
VOC.init{
|
||||
'zspin','sspin','jspin','lspin','tspin','ospin','ispin','pspin','qspin','fspin','espin','uspin','vspin','wspin','xspin','rspin','yspin','nspin','hspin','cspin',
|
||||
'single','double','triple','techrash','pentacrash','hexacrash',
|
||||
'single','double','triple','techrash','pentacrash','hexacrash','heptacrash','octacrash','nonacrash','decacrash','undecacrash','dodecacrash','tridecacrash','tetradecacrash','pentadecacrash','hexadecacrash','heptadecacrash','octadecacrash','nonadecacrash','ultracrash','impossicrash',
|
||||
'mini','b2b','b3b',
|
||||
'perfect_clear','half_clear',
|
||||
'win','lose','bye',
|
||||
@@ -363,41 +365,17 @@ table.insert(_LOADTIMELIST_,("Initialize Modules: %.3fs"):format(TIME()-_LOADTIM
|
||||
-- Load settings and statistics
|
||||
if
|
||||
not (
|
||||
pcall(TABLE.cover, loadFile('conf/user', '-json -canSkip') or loadFile('conf/user', '-luaon -canSkip') or{},USER) and
|
||||
pcall(TABLE.cover, loadFile('conf/unlock', '-json -canSkip') or loadFile('conf/unlock', '-luaon -canSkip') or{},RANKS) and
|
||||
pcall(TABLE.update,loadFile('conf/settings', '-json -canSkip') or loadFile('conf/settings', '-luaon -canSkip') or{},SETTING) and
|
||||
pcall(TABLE.coverR,loadFile('conf/data', '-json -canSkip') or loadFile('conf/data', '-luaon -canSkip') or{},STAT) and
|
||||
pcall(TABLE.cover, loadFile('conf/key', '-json -canSkip') or loadFile('conf/key', '-luaon -canSkip') or{},KEY_MAP) and
|
||||
pcall(TABLE.cover, loadFile('conf/virtualkey','-json -canSkip') or loadFile('conf/virtualkey','-luaon -canSkip') or{},VK_ORG)
|
||||
pcall(TABLE.cover, loadFile('conf/user', '-json -canSkip') or loadFile('conf/user', '-luaon -canSkip') or {},USER) and
|
||||
pcall(TABLE.cover, loadFile('conf/unlock', '-json -canSkip') or loadFile('conf/unlock', '-luaon -canSkip') or {},RANKS) and
|
||||
pcall(TABLE.update,loadFile('conf/settings', '-json -canSkip') or loadFile('conf/settings', '-luaon -canSkip') or {},SETTING) and
|
||||
pcall(TABLE.coverR,loadFile('conf/data', '-json -canSkip') or loadFile('conf/data', '-luaon -canSkip') or {},STAT) and
|
||||
pcall(TABLE.cover, loadFile('conf/key', '-json -canSkip') or loadFile('conf/key', '-luaon -canSkip') or {},KEY_MAP) and
|
||||
pcall(TABLE.cover, loadFile('conf/virtualkey','-json -canSkip') or loadFile('conf/virtualkey','-luaon -canSkip') or {},VK_ORG)
|
||||
)
|
||||
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
|
||||
@@ -424,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
|
||||
@@ -448,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
|
||||
@@ -461,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
|
||||
@@ -502,24 +483,27 @@ LANG.init('zh',
|
||||
ja=require'parts.language.lang_ja',
|
||||
symbol=require'parts.language.lang_symbol',
|
||||
zh_code=require'parts.language.lang_zh_code',
|
||||
vi=require'parts.language.lang_vi',
|
||||
-- 1. Add language file to LANG folder;
|
||||
-- 2. Require it;
|
||||
-- 3. Add a button in parts/scenes/lang.lua;
|
||||
},
|
||||
{
|
||||
block=BLOCK_NAMES
|
||||
block=BLOCK_NAMES,
|
||||
},
|
||||
(function()
|
||||
local tipMeta={__call=function(L) return L[math.random(#L)] end}
|
||||
return function(L)
|
||||
if type(rawget(L,'getTip'))=='table' then setmetatable(L.getTip,tipMeta) end
|
||||
setmetatable(L,{__index=function(self,k)
|
||||
local mes="No Text ("..SETTING.locale.."): "..k
|
||||
LOG(mes)
|
||||
MES.new('warn',mes)
|
||||
self[k]="["..k.."]"
|
||||
return self[k]
|
||||
end})
|
||||
setmetatable(L,{
|
||||
__index=function(self,k)
|
||||
local mes="No Text ("..SETTING.locale.."): "..k
|
||||
LOG(mes)
|
||||
MES.new('warn',mes)
|
||||
self[k]="["..k.."]"
|
||||
return self[k]
|
||||
end,
|
||||
})
|
||||
end
|
||||
end)()
|
||||
)
|
||||
@@ -531,7 +515,9 @@ for _,v in next,fs.getDirectoryItems('parts/backgrounds') do
|
||||
BG.add(name,require('parts.backgrounds.'..name))
|
||||
end
|
||||
end
|
||||
BG.remList('none')BG.remList('gray')BG.remList('custom')
|
||||
BG.remList('none')
|
||||
BG.remList('gray')
|
||||
BG.remList('custom')
|
||||
|
||||
-- Load scene files from SOURCE ONLY
|
||||
for _,v in next,fs.getDirectoryItems('parts/scenes') do
|
||||
@@ -558,66 +544,78 @@ applySettings()
|
||||
-- Load replays
|
||||
for _,fileName in next,fs.getDirectoryItems('replay') do
|
||||
if fileName:sub(12,12):match("[a-zA-Z]") then
|
||||
local date,mode,version,player,seed,setting,mod
|
||||
local fileData=fs.read('replay/'..fileName)
|
||||
date, fileData=STRING.readLine(fileData)date=date:gsub("[a-zA-Z]","")
|
||||
mode, fileData=STRING.readLine(fileData)mode=MODE_UPDATE_MAP[mode] or mode
|
||||
version,fileData=STRING.readLine(fileData)
|
||||
player, fileData=STRING.readLine(fileData) if player=="Local Player" then player="Stacker" end
|
||||
local success
|
||||
success,fileData=pcall(love.data.decompress,'string','zlib',fileData)
|
||||
if not success then goto BREAK_cannotParse end
|
||||
seed, fileData=STRING.readLine(fileData)
|
||||
setting,fileData=STRING.readLine(fileData)setting=JSON.decode(setting)
|
||||
mod, fileData=STRING.readLine(fileData)mod=JSON.decode(mod)
|
||||
if
|
||||
not setting or
|
||||
not mod or
|
||||
not mode or
|
||||
#mode==0
|
||||
then goto BREAK_cannotParse end
|
||||
repeat
|
||||
local date,mode,version,player,seed,setting,mod
|
||||
local success,fileData=true,fs.read('replay/'..fileName)
|
||||
date,fileData=STRING.readLine(fileData)
|
||||
date=date:gsub("[a-zA-Z]","")
|
||||
mode,fileData=STRING.readLine(fileData)
|
||||
mode=MODE_UPDATE_MAP[mode] or mode
|
||||
version,fileData=STRING.readLine(fileData)
|
||||
player,fileData=STRING.readLine(fileData)
|
||||
if player=="Local Player" then player="Stacker" end
|
||||
success,fileData=pcall(love.data.decompress,'string','zlib',fileData)
|
||||
if not success then break end
|
||||
seed,fileData=STRING.readLine(fileData)
|
||||
setting,fileData=STRING.readLine(fileData)
|
||||
setting=JSON.decode(setting)
|
||||
mod,fileData=STRING.readLine(fileData)
|
||||
mod=JSON.decode(mod)
|
||||
if
|
||||
not setting or
|
||||
not mod or
|
||||
not mode or
|
||||
#mode==0
|
||||
then
|
||||
break
|
||||
end
|
||||
|
||||
fs.remove('replay/'..fileName)
|
||||
local newName=fileName:sub(1,10)..fileName:sub(15)
|
||||
fs.write('replay/'..newName,
|
||||
love.data.compress('string','zlib',
|
||||
JSON.encode{
|
||||
date=date,
|
||||
mode=mode,
|
||||
version=version,
|
||||
player=player,
|
||||
seed=seed,
|
||||
setting=setting,
|
||||
mod=mod,
|
||||
}.."\n"..
|
||||
fileData
|
||||
fs.remove('replay/'..fileName)
|
||||
local newName=fileName:sub(1,10)..fileName:sub(15)
|
||||
fs.write('replay/'..newName,
|
||||
love.data.compress('string','zlib',
|
||||
JSON.encode{
|
||||
date=date,
|
||||
mode=mode,
|
||||
version=version,
|
||||
player=player,
|
||||
seed=seed,
|
||||
setting=setting,
|
||||
mod=mod,
|
||||
}.."\n"..
|
||||
fileData
|
||||
)
|
||||
)
|
||||
)
|
||||
fileName=newName
|
||||
fileName=newName
|
||||
until true
|
||||
end
|
||||
::BREAK_cannotParse::
|
||||
local rep=DATA.parseReplay('replay/'..fileName)
|
||||
table.insert(REPLAY,rep)
|
||||
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
|
||||
|
||||
LOG("\27[92m\27[1mAutomatic Test Started\27[0m")
|
||||
BGM.setVol(0)SFX.setVol(0)
|
||||
BGM.setVol(0)
|
||||
SFX.setVol(0)
|
||||
love.keypressed('space')
|
||||
TEST.yieldUntilNextScene()
|
||||
|
||||
|
||||
BIN
media/effect/chiptune/mod_off.ogg
Normal file
BIN
media/effect/chiptune/mod_on.ogg
Normal file
BIN
media/image/characters/flore.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
media/image/characters/neuro.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
media/image/characters/zundamon.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 483 B After Width: | Height: | Size: 933 B |
|
Before Width: | Height: | Size: 207 B After Width: | Height: | Size: 1.3 KiB |
BIN
media/image/modeicon/hidden3.png
Normal file
|
After Width: | Height: | Size: 950 B |
BIN
media/image/modeicon/hidden4.png
Normal file
|
After Width: | Height: | Size: 925 B |
BIN
media/image/modeicon/hidden5.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
2
media/image/modeicon/note.txt
Normal 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.
|
||||
|
Before Width: | Height: | Size: 489 B |
|
Before Width: | Height: | Size: 488 B |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 395 B |
|
Before Width: | Height: | Size: 457 B |
|
Before Width: | Height: | Size: 1.2 KiB |
BIN
media/image/skin/asriel/arcade.png
Executable file
|
After Width: | Height: | Size: 20 KiB |
BIN
media/image/skin/asriel/cardboard.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
media/image/skin/chno/kanji.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
media/image/skin/chno/letters.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 613 B After Width: | Height: | Size: 613 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 91 B After Width: | Height: | Size: 91 B |