Compare commits

..

66 Commits

Author SHA1 Message Date
MrZ_26
2226aa2e6b 中文方块维基名称调整 2024-11-09 14:12:09 +08:00
MrZ_26
12ae789239 更新词典关于方块中文维基的内容 2024-11-08 23:55:52 +08:00
Particle_G
2d7d02d5c9 CI update workflow (#1175)
(cherry picked from commit 64ddb09060)

Co-authored-by: MrZ_26 <1046101471@qq.com>
2024-11-08 01:59:37 +08:00
MrZ_26
64ddb09060 关于页面会显示当前love版本号了
(补法语缺一行文本)(补更新历史)
2024-11-06 09:36:27 +08:00
MrZ_26
0203bfa805 从地图删除三个策略堆叠模式(太无聊纯凑数) 2024-11-04 11:21:59 +08:00
SweetSea
c974db8c1c Update Vietnamese translation (#1172) 2024-11-04 11:03:37 +08:00
MrZ_26
8559b5e565 补充更新历史 2024-11-04 11:01:05 +08:00
MrZ_26
201ec079d6 修复晃动特效开大了20g模式会颠到吐 2024-11-04 11:01:05 +08:00
MrZ_26
f6e7e993f5 修msaa滑条在一些平台有问题 2024-11-04 11:01:05 +08:00
ParticleG
ff6a99999b Fix js.lua init error due to wrong grammar 2024-11-03 10:00:05 +08:00
MrZ_26
35aacc1a3e 整理代码 2024-11-03 01:51:46 +08:00
MrZ_26
59903397a7 Merge branch 'ci-test-jsapi' 2024-11-03 01:43:54 +08:00
MrZ_26
ca39c0ddf3 剪切板相关微调 2024-11-03 01:43:31 +08:00
ParticleG
6e6e1034b4 - Update timer 2024-11-03 01:36:15 +08:00
ParticleG
01efc218bf - Format codes 2024-11-03 01:07:11 +08:00
ParticleG
b4b602ae9d - Rename joystick related variables to joystick 2024-11-03 01:06:11 +08:00
ParticleG
bd395b2775 - Fix pop logic
- Retrieve data onlye when triggered
- Show message when thread is dead
- Push timer to thread
2024-11-03 01:05:39 +08:00
ParticleG
bf36bfb02b - Set setFreshInterval when system is web release 2024-11-03 01:03:38 +08:00
ParticleG
4bbf77b217 - 2024-11-02 22:12:58 +08:00
ParticleG
486e88e7bc - 2024-11-02 21:45:07 +08:00
SweetSea
46396efd09 Refactoring theme.lua (#1168) 2024-11-02 20:18:43 +08:00
ParticleG
57d42a84a4 - 2024-11-02 18:32:39 +08:00
ParticleG
eb7e20d157 - 2024-11-02 18:10:35 +08:00
ParticleG
725e8b35bd - 2024-11-02 17:55:32 +08:00
ParticleG
17a8181822 - 2024-11-02 17:45:27 +08:00
MrZ_26
6c40fb098f 整理皮肤顺序 2024-11-02 07:26:22 +08:00
MrZ_26
785935d26f 移除所有的Channel:demand,因为love.js不支持
整理代码
2024-11-02 05:52:36 +08:00
ParticleG
f2e064e900 - 2024-11-01 11:44:17 +08:00
ParticleG
2e537807a7 - Fix love.system not found 2024-11-01 11:41:32 +08:00
ParticleG
f4e04b32dd - Add compatible mode support 2024-11-01 11:03:52 +08:00
ParticleG
e1255e6124 - 2024-11-01 10:45:24 +08:00
ParticleG
37993c1669 - Add log 2024-11-01 10:14:45 +08:00
ParticleG
2fa653fffe - Log thread start 2024-11-01 10:03:11 +08:00
ParticleG
77fd519b43 - Add log 2024-11-01 08:48:25 +08:00
ParticleG
017f874f38 - Sanitize get and set 2024-11-01 08:25:24 +08:00
ParticleG
c642bd3857 - Fix missing dependence 2024-11-01 08:22:21 +08:00
ParticleG
bc831220b4 - Fix warnings in cards 2024-11-01 08:16:04 +08:00
ParticleG
b50147cace - Unify clipboard 2024-11-01 08:00:01 +08:00
ParticleG
eb3b8c9e9d - 2024-10-31 21:08:43 +08:00
ParticleG
9ddab37232 - Update source 2024-10-31 21:02:17 +08:00
ParticleG
cec7914c8f - fix error 2024-10-31 21:00:50 +08:00
ParticleG
b19a83ac23 - Update sources 2024-10-31 20:52:09 +08:00
ParticleG
1905960b51 - Remove print 2024-10-31 19:11:19 +08:00
ParticleG
68a806aa3e - Disable other actions 2024-10-31 19:07:32 +08:00
ParticleG
b555c44b8b - Update sources 2024-10-31 16:54:01 +08:00
ParticleG
d0c503e096 - Update sources 2024-10-31 16:37:28 +08:00
ParticleG
9ec2182252 - Wait until data is available 2024-10-31 16:23:07 +08:00
ParticleG
881d43c56d - Add logs 2024-10-31 12:18:27 +08:00
ParticleG
8a663e29a6 - Use dt in JS.retrieveData 2024-10-31 12:12:18 +08:00
ParticleG
7ae19b6431 - Add missing quotes 2024-10-31 11:58:10 +08:00
ParticleG
081b77f0e1 - Update index.html 2024-10-31 11:53:12 +08:00
ParticleG
b4a9766d14 - Add webdb.js 2024-10-31 11:51:57 +08:00
ParticleG
c33614a9cc - Add JS.retrieveData to main update loop 2024-10-31 11:45:24 +08:00
ParticleG
ae37fde4b9 - Use callbacks instead of await 2024-10-31 11:30:26 +08:00
ParticleG
64a86b260a - Replace clipboard methods if OS is Web 2024-10-31 11:22:13 +08:00
ParticleG
59cd9347c2 - Update.js.lua 2024-10-31 11:03:17 +08:00
ParticleG
0281d28a72 - Use compatible version 2024-10-31 10:51:56 +08:00
ParticleG
e517e6c65b - Disable test for now 2024-10-31 10:43:29 +08:00
ParticleG
95283b6fc7 - Update init.lua 2024-10-31 10:40:09 +08:00
ParticleG
1ed2cd468e - Update web build logics 2024-10-31 10:18:23 +08:00
ParticleG
eaba1cd0ab - Disable discordRPC when on Web env 2024-10-31 01:03:41 +08:00
ParticleG
421e74e357 - Update sourcces 2024-10-31 00:51:48 +08:00
ParticleG
6eba788236 - Update sources 2024-10-31 00:03:35 +08:00
ParticleG
d041461f0a - Change filename in game.js 2024-10-30 23:35:34 +08:00
ParticleG
13b277b35a - Update workflow 2024-10-30 23:25:33 +08:00
ParticleG
fc703f241c - Rename love package 2024-10-30 23:13:02 +08:00
57 changed files with 797 additions and 822 deletions

View File

@@ -1,32 +0,0 @@
name: 'get cc'
description: 'download cc into specific dir'
inputs:
tag:
required: false
default: "11.4.2"
platform:
required: true
dir:
required: false
default: '.'
repo:
required: false
default: 26F-Studio/cold_clear_ai_love2d_wrapper
temp-file:
required: false
default: temp.zip
runs:
using: "composite"
steps:
- run: |
echo "tag="$(if [ -z "${{ inputs.tag }}" ]
then curl -w '%{url_effective}' -I -L -s -S https://github.com/${{ inputs.repo }}/releases/latest -o /dev/null | grep -o '\<[^/]*$'
else echo ${{ inputs.tag }}
fi) >> $GITHUB_OUTPUT
id: get-tag
shell: bash
- uses: ./.github/actions/get-unzip
with:
url: https://github.com/${{ inputs.repo }}/releases/download/${{ steps.get-tag.outputs.tag }}/${{ inputs.platform }}.zip
dir: ${{ inputs.dir }}
temp-file: ${{ inputs.temp-file }}

BIN
.github/build/web/dev/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

112
.github/build/web/dev/index.html vendored Normal file
View File

@@ -0,0 +1,112 @@
<!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 Development</title>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" type="text/css" href="theme/love.css">
<script src="consolewrapper.js"></script>
<script src="webdb.js"></script>
</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: 128000000,
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>

View File

@@ -1,288 +0,0 @@
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}]});
})();

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 305 KiB

After

Width:  |  Height:  |  Size: 305 KiB

View File

@@ -5,9 +5,10 @@
<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="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" type="text/css" href="theme/love.css">
<script src="consolewrapper.js"></script>
<script src="webdb.js"></script>
</head>
<body>
<center>
@@ -61,7 +62,7 @@
var Module = {
arguments: ["./game.love"],
INITIAL_MEMORY: 536870912,
INITIAL_MEMORY: 128000000,
printErr: console.error.bind(console),
canvas: (function() {
var canvas = document.getElementById('canvas');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

View File

@@ -1,49 +0,0 @@
* {
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;
}

View File

@@ -8,9 +8,11 @@ on:
branches: [main]
env:
BUILD_ASSETS_FOLDER: ./.github/build
BUILD_TYPE: ${{ fromJSON('["dev", "release"]')[startsWith(github.ref, 'refs/tags/v')] }}
CORE_LOVE_PACKAGE_PATH: ./core.love
COLD_CLEAR_DOWNLOAD_URL: https://github.com/26F-Studio/cold_clear_ai_love2d_wrapper/releases/download/11.5
CORE_LOVE_ARTIFACT_NAME: core_love_package
CORE_LOVE_PACKAGE_PATH: ./core.love
jobs:
get-info:
@@ -25,7 +27,7 @@ jobs:
commit-hash: ${{ steps.git-info.outputs.commit-hash }}
base-name: ${{ steps.assemble-base-name.outputs.base-name }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install lua
run: |
sudo apt-get install lua5.3 -y
@@ -73,7 +75,7 @@ jobs:
OUTPUT_FOLDER: ./build
RELEASE_FOLDER: ./release
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Process app name
@@ -95,13 +97,13 @@ jobs:
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
uses: actions/upload-artifact@v4
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
path: ${{ env.CORE_LOVE_PACKAGE_PATH }}
- name: Add icon to love package
run: |
cp ./.github/build/linux/${{ env.BUILD_TYPE }}/icon.png media/image/icon.png
cp ${{ env.BUILD_ASSETS_FOLDER }}/linux/${{ env.BUILD_TYPE }}/icon.png media/image/icon.png
zip -u ${{ env.CORE_LOVE_PACKAGE_PATH }} media/image/icon.png
rm media/image/icon.png
- name: Rename love package
@@ -109,7 +111,7 @@ jobs:
mkdir -p ${{ env.OUTPUT_FOLDER }}
mv ${{ env.CORE_LOVE_PACKAGE_PATH }} ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.love
- name: Upload artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ needs.get-info.outputs.base-name }}_Core_love
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.love
@@ -132,8 +134,10 @@ jobs:
auto-test:
runs-on: ubuntu-22.04
needs: build-core
env:
APPIMAGE_PATH: ./love.AppImage
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Love actions for testing
@@ -142,34 +146,34 @@ jobs:
font-path: ./parts/fonts/proportional.otf
language-folder: ./parts/language
- name: Download core love package
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
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
curl --retry 5 https://github.com/love2d/love/releases/download/11.5/love-11.5-x86_64.AppImage -o ${{ env.APPIMAGE_PATH }}
chmod +x ${{ env.APPIMAGE_PATH }}
- name: Install dependencies
shell: bash
run: |
sudo apt-get update
sudo apt-get install pulseaudio pulseaudio-utils pavucontrol alsa-oss alsa-utils libfuse2 -y
sudo apt-get install alsa-oss alsa-utils libfuse2 pavucontrol pulseaudio pulseaudio-utils x11-xserver-utils xvfb -y
- name: Run automated test
uses: coactions/setup-xvfb@v1
with:
run: |
./love-11.4-x86_64.AppImage ${{ env.CORE_LOVE_PACKAGE_PATH }} --test
shell: bash
run: |
xvfb-run --auto-servernum ${{ env.APPIMAGE_PATH }} ${{ env.CORE_LOVE_PACKAGE_PATH }} --test
build-android:
runs-on: ubuntu-latest
needs: [get-info, build-core, auto-test]
if: github.event_name != 'pull_request'
env:
COLD_CLEAR_FOLDER: ./libAndroid
OUTPUT_FOLDER: ./build
RELEASE_FOLDER: ./release
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Process app name
@@ -178,6 +182,7 @@ jobs:
run: |
import os
import re
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
if "${{ env.BUILD_TYPE }}" == "dev":
f.write('bundle-id=org.f26_studio.' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '.snapshot\n')
@@ -186,17 +191,17 @@ jobs:
f.write('bundle-id=org.f26_studio.' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\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
uses: actions/download-artifact@v4
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Download ColdClear
uses: ./.github/actions/get-cc
uses: ./.github/actions/get-unzip
with:
platform: Android
dir: ./libAndroid
url: ${{ env.COLD_CLEAR_DOWNLOAD_URL }}/Android.zip
dir: ${{ env.COLD_CLEAR_FOLDER }}
- name: Build Android packages
id: build-packages
uses: love-actions/love-actions-android@main
uses: love-actions/love-actions-android@v2
with:
app-name: ${{ needs.get-info.outputs.app-name }}
bundle-id: ${{ steps.process-app-name.outputs.bundle-id }}
@@ -206,15 +211,15 @@ jobs:
keystore-key-password: ${{ secrets.ANDROID_KEYSTORE_KEYPASSWORD }}
keystore-store-password: ${{ secrets.ANDROID_KEYSTORE_STOREPASSWORD }}
love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }}
resource-path: ./.github/build/android/${{ env.BUILD_TYPE }}/res
extra-assets: ./libAndroid/
resource-path: ${{ env.BUILD_ASSETS_FOLDER }}/android/${{ env.BUILD_TYPE }}/res
extra-assets: ${{ env.COLD_CLEAR_FOLDER }}
custom-scheme: studio26f://oauth
product-name: ${{ steps.process-app-name.outputs.product-name }}
version-string: ${{ needs.get-info.outputs.version-string }}
version-code: ${{ needs.get-info.outputs.version-code }}
output-folder: ${{ env.OUTPUT_FOLDER }}
- name: Upload artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ needs.get-info.outputs.base-name }}_Android_release
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}-release.apk
@@ -238,10 +243,11 @@ jobs:
runs-on: ubuntu-latest
needs: [get-info, build-core, auto-test]
env:
COLD_CLEAR_FOLDER: ./ColdClear
OUTPUT_FOLDER: ./build
RELEASE_FOLDER: ./release
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Process app name
@@ -256,45 +262,45 @@ jobs:
f.write('bundle-id=org.26f-studio.' + product_name + '\n')
f.write('product-name=' + product_name + '\n')
- name: Download core love package
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Add icon to love package
run: |
cp ./.github/build/linux/${{ env.BUILD_TYPE }}/icon.png media/image/icon.png
cp ${{ env.BUILD_ASSETS_FOLDER }}/linux/${{ env.BUILD_TYPE }}/icon.png media/image/icon.png
zip -u ${{ env.CORE_LOVE_PACKAGE_PATH }} media/image/icon.png
rm media/image/icon.png
- name: Download ColdClear
uses: ./.github/actions/get-cc
uses: ./.github/actions/get-unzip
with:
platform: Linux
dir: ./ColdClear
url: ${{ env.COLD_CLEAR_DOWNLOAD_URL }}/Linux.zip
dir: ${{ env.COLD_CLEAR_FOLDER }}
- name: Process ColdClear
shell: bash
run: |
cd ./ColdClear
cd ${{ env.COLD_CLEAR_FOLDER }}
mkdir -p ./lib/lua/5.1
mv ./x64/CCloader.so ./lib/lua/5.1
- name: Build Linux packages
id: build-packages
uses: love-actions/love-actions-linux@v1
uses: love-actions/love-actions-linux@v2
with:
app-name: ${{ needs.get-info.outputs.app-name }}
bundle-id: ${{ steps.process-app-name.outputs.bundle-id }}
description: Techmino is fun!
version-string: ${{ needs.get-info.outputs.version-string }}
icon-path: ./.github/build/linux/${{ env.BUILD_TYPE }}/icon.png
icon-path: ${{ env.BUILD_ASSETS_FOLDER }}/linux/${{ env.BUILD_TYPE }}/icon.png
love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }}
lib-path: ./ColdClear/lib
lib-path: ${{ env.COLD_CLEAR_FOLDER }}/lib
product-name: ${{ steps.process-app-name.outputs.product-name }}
output-folder: ${{ env.OUTPUT_FOLDER }}
- name: Upload AppImage artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ needs.get-info.outputs.base-name }}_Linux_AppImage
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.AppImage
- name: Upload Debian artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ needs.get-info.outputs.base-name }}_Linux_Debian
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.deb
@@ -317,145 +323,58 @@ jobs:
name: ${{ needs.get-info.outputs.update-title }}
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
build-macos-portable:
runs-on: macos-latest
needs: [get-info, build-core, auto-test]
if: github.event_name != 'pull_request'
env:
OUTPUT_FOLDER: ./build
RELEASE_FOLDER: ./release
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Process app name
id: process-app-name
shell: python3 {0}
run: |
import os
import re
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
f.write('bundle-id=org.26f-studio.techmino\n')
f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\n')
- name: Download core love package
uses: actions/download-artifact@v3
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Download ColdClear
uses: ./.github/actions/get-cc
with:
platform: macOS
dir: ./ColdClear
- name: Process ColdClear
shell: bash
run: |
rm ./ColdClear/universal/libcold_clear.a
- name: 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
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-ref: "11.5"
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 }}
account-username: ${{ secrets.APPLE_ACCOUNT_USERNAME }}
account-password: ${{ secrets.APPLE_ACCOUNT_PASSWORD }}
team-id: "${{ secrets.APPLE_DEVELOPER_TEAM_ID }}"
developer-id-application-base64: ${{ secrets.APPLE_CERT_DEVELOPER_ID_APPLICATION }}
developer-id-application-password: ${{ secrets.APPLE_CERT_DEVELOPER_ID_APPLICATION_PWD }}
developer-id-installer-base64: ${{ secrets.APPLE_CERT_DEVELOPER_ID_INSTALLER }}
developer-id-installer-password: ${{ secrets.APPLE_CERT_DEVELOPER_ID_INSTALLER_PWD }}
dmg-background-path: ./.github/build/macOS/${{ env.BUILD_TYPE }}/dmg.png
dmg-icon-position: "239 203"
dmg-icon-size: "100"
dmg-link-position: "565 203"
dmg-text-size: "12"
dmg-volume-icon-path: ./.github/build/macOS/${{ env.BUILD_TYPE }}/dmg.icns
dmg-volume-name: ${{ steps.process-app-name.outputs.product-name }}
dmg-window-position: "200 120"
dmg-window-size: "800 500"
- name: Upload pkg artifact
uses: actions/upload-artifact@v3
with:
name: ${{ needs.get-info.outputs.base-name }}_macOS_portable_pkg
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.pkg
- name: Upload dmg artifact
uses: actions/upload-artifact@v3
with:
name: ${{ needs.get-info.outputs.base-name }}_macOS_portable_dmg
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.dmg
- name: Upload bare artifact
uses: actions/upload-artifact@v3
with:
name: ${{ needs.get-info.outputs.base-name }}_macOS_portable_bare
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.zip
- 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_portable.pkg
cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.dmg ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_macOS_portable.dmg
- 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_portable.pkg
${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_macOS_portable.dmg
body: ${{ needs.get-info.outputs.update-note }}
name: ${{ needs.get-info.outputs.update-title }}
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
build-web:
build-web-compat:
runs-on: ubuntu-latest
needs: [get-info, build-core, auto-test]
env:
MEMORY_LIMIT: 128000000
OUTPUT_FOLDER: ./build
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Download core love package
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Move core love package
- name: Build web packages
run: |
mv ${{ env.CORE_LOVE_PACKAGE_PATH }} ./.github/build/web/game.data
npx love.js ${{ env.CORE_LOVE_PACKAGE_PATH }} ${{ env.OUTPUT_FOLDER }} -t "${{ needs.get-info.outputs.app-name }}" -m ${{ env.MEMORY_LIMIT }} -c
- name: Move assets
run: |
mv ${{ env.BUILD_ASSETS_FOLDER }}/web/${{ env.BUILD_TYPE }}/* ${{ env.OUTPUT_FOLDER }}
- name: Initialize Love.js Api Player
run: |
pushd ${{ env.OUTPUT_FOLDER }}
wget https://raw.githubusercontent.com/MrcSnm/Love.js-Api-Player/refs/heads/master/consolewrapper.js
wget https://raw.githubusercontent.com/MrcSnm/Love.js-Api-Player/refs/heads/master/globalizeFS.js
wget https://raw.githubusercontent.com/MrcSnm/Love.js-Api-Player/refs/heads/master/webdb.js
node globalizeFS.js
rm globalizeFS.js
popd
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ needs.get-info.outputs.base-name }}_Web
path: ${{ env.OUTPUT_FOLDER }}
- name: Deploy to GitHub Pages
uses: crazy-max/ghaction-github-pages@v3
uses: crazy-max/ghaction-github-pages@v4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
build_dir: ./.github/build/web/
build_dir: ${{ env.OUTPUT_FOLDER }}
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/
target_branch: gh-pages
build-windows:
runs-on: windows-latest
needs: [get-info, build-core, auto-test]
env:
COLD_CLEAR_FOLDER: ./ColdClear
OUTPUT_FOLDER: ./build
RELEASE_FOLDER: ./release
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Process app name
@@ -467,20 +386,20 @@ jobs:
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
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
uses: actions/download-artifact@v4
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Download ColdClear
uses: ./.github/actions/get-cc
uses: ./.github/actions/get-unzip
with:
platform: Windows
dir: ./ColdClear
url: ${{ env.COLD_CLEAR_DOWNLOAD_URL }}/Windows.zip
dir: ${{ env.COLD_CLEAR_FOLDER }}
- name: Update Windows template
shell: python3 {0}
run: |
version_string = "${{ needs.get-info.outputs.version-string }}"
file_version = (f"{version_string.replace('.', ',')},0")
with open("./.github/build/windows/${{ env.BUILD_TYPE }}/template.rc", "r+", encoding="utf8") as file:
with open("${{ env.BUILD_ASSETS_FOLDER }}/windows/${{ env.BUILD_TYPE }}/template.rc", "r+", encoding="utf8") as file:
data = file.read()
data = data\
.replace("@Version", version_string)\
@@ -490,30 +409,30 @@ jobs:
file.write(data)
- name: Build Windows packages
id: build-packages
uses: love-actions/love-actions-windows@v1
uses: love-actions/love-actions-windows@v2
with:
icon-path: ./.github/build/windows/${{ env.BUILD_TYPE }}/icon.ico
rc-path: ./.github/build/windows/${{ env.BUILD_TYPE }}/template.rc
icon-path: ${{ env.BUILD_ASSETS_FOLDER }}/windows/${{ env.BUILD_TYPE }}/icon.ico
rc-path: ${{ env.BUILD_ASSETS_FOLDER }}/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 ./.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
extra-assets-x86: ${{ env.COLD_CLEAR_FOLDER }}/x86/CCloader.dll ${{ env.COLD_CLEAR_FOLDER }}/x86/cold_clear.dll ${{ env.BUILD_ASSETS_FOLDER }}/extraLibs/Windows_x64/discord-rpc.dll
extra-assets-x64: ${{ env.COLD_CLEAR_FOLDER }}/x64/CCloader.dll ${{ env.COLD_CLEAR_FOLDER }}/x64/cold_clear.dll ${{ env.BUILD_ASSETS_FOLDER }}/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/
installer-languages: ChineseSimplified.isl ChineseTraditional.isl English.isl Spanish.isl French.isl Indonesian.isl Japanese.isl Portuguese.isl
output-folder: ${{ env.OUTPUT_FOLDER }}
- name: Upload 32-bit artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ needs.get-info.outputs.base-name }}_Windows_x86
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_x86.zip
- name: Upload 64-bit artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ needs.get-info.outputs.base-name }}_Windows_x64
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_x64.zip
- name: Upload installer artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ needs.get-info.outputs.base-name }}_Windows_installer
path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_installer.exe
@@ -548,38 +467,58 @@ jobs:
build-core,
build-android,
build-linux,
build-macos-portable,
build-web,
build-web-compat,
build-windows,
]
env:
ACTION_TYPE: ${{ fromJSON('[["Development", "Pre-release"], ["Release", "Release"]]')[startsWith(github.ref, 'refs/tags/v')][startsWith(github.ref, 'refs/tags/pre')] }}
steps:
- name: Cleanup
uses: geekyeggo/delete-artifact@v2
uses: geekyeggo/delete-artifact@v5
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Send Discord message
if: github.event_name != 'pull_request'
uses: Sniddl/discord-commits@v1.6
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
message: "Github Actions for **${{ github.repository }}**."
embed: '{
"author":{
"name":"${{ needs.get-info.outputs.app-name }} [${{ env.ACTION_TYPE }}]",
"url":"https://github.com/${{ github.repository }}"
},
"title":"${{ needs.get-info.outputs.app-name }} (${{ needs.get-info.outputs.version-name }}) Build Result",
"description": "CI triggered at ${{ needs.get-info.outputs.commit-hash }}",
"url":"https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"thumbnail":{
"url":"https://raw.githubusercontent.com/${{ github.repository }}/main/.github/build/linux/${{ env.BUILD_TYPE }}/icon.png"
},
"color":36863,
"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**Linux:** ${{ needs.build-linux.result }}\n**macOS portable:** ${{ needs.build-macos-portable.result }}\n**Windows:** ${{ needs.build-windows.result }}"}
if: github.ref == 'refs/heads/main'
shell: python
run: |
import requests
import json
headers = {
'Content-Type': 'application/json',
}
payload = json.dumps({
"content": "Github Actions for **${{ github.repository }}**.",
"embeds": [
{
"author": {
"name": "${{ needs.get-info.outputs.app-name }} [${{ env.ACTION_TYPE }}]",
"url": "https://github.com/${{ github.repository }}"
},
"title": "${{ needs.get-info.outputs.app-name }} (${{ needs.get-info.outputs.version-name }}) Build Result",
"description": "CI triggered at ${{ needs.get-info.outputs.commit-hash }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"thumbnail": {
"url": "https://raw.githubusercontent.com/${{ github.repository }}/main/.github/build/linux/${{ env.BUILD_TYPE }}/icon.png"
},
"color": 36863,
"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**Linux:** ${{ needs.build-linux.result }}\n**Web compatible:** ${{ needs.build-web-compat.result }}\n**Windows:** ${{ needs.build-windows.result }}"
}
]
}
]
}'
})
requests.request("POST", "${{ secrets.DISCORD_WEBHOOK }}", headers=headers, data=payload)

103
Zframework/clipboard.lua Normal file
View File

@@ -0,0 +1,103 @@
local function _sanitize(content)
if type(content)=='boolean' then
content=content and 'true' or 'false'
end
if type(content)=='nil' then
content=''
end
if type(content)=='number' then
content=tostring(content)
end
if type(content)~='string' then
MES.new('error',"Invalid content type!")
MES.traceback()
return ''
end
return content
end
if SYSTEM~='Web' then
local get=love.system.getClipboardText
local set=love.system.setClipboardText
return {
get=function() return get() or '' end,
set=function(content) set(_sanitize(content)) end,
setFreshInterval=NULL,
_update=NULL,
}
end
if WEB_COMPAT_MODE then
local _clipboardBuffer=''
return {
get=function()
JS.newPromiseRequest(
JS.stringFunc(
[[
window.navigator.clipboard
.readText()
.then((text) => _$_(text))
.catch((e) => {
console.warn(e);
_$_('');
});
]]
),
function(data) _clipboardBuffer=data end,
function() _clipboardBuffer='' end,
3,
'getClipboardText'
)
if TASK.lock('clipboard_compat_interval',2.6) then
_clipboardBuffer=''
MES.new('warn',"Web-Compat mode, paste again to confirm",2.6)
end
return _clipboardBuffer
end,
set=function(str)
JS.callJS(JS.stringFunc(
[[
window.navigator.clipboard
.writeText('%s')
.then(() => console.log('Copied to clipboard'))
.catch((e) => console.warn(e));
]],
_sanitize(str)
))
end,
setFreshInterval=NULL,
_update=NULL,
}
end
local getCHN=love.thread.getChannel('CLIP_get')
local setCHN=love.thread.getChannel('CLIP_set')
local trigCHN=love.thread.getChannel('CLIP_trig')
local clipboard_thread=love.thread.newThread('Zframework/clipboard_thread.lua')
local isStarted,errorMessage=clipboard_thread:start()
if not isStarted then
MES.new("error",errorMessage,26)
end
local freshInterval=1
local timer=-.626
return {
get=function() return getCHN:peek() or '' end,
set=function(content) setCHN:push(_sanitize(content)) end,
setFreshInterval=function(val)
freshInterval=val
end,
_update=function(dt)
timer=timer+dt
if timer>freshInterval then
if isStarted and not clipboard_thread:isRunning() then
MES.new("warn",clipboard_thread:getError(),26)
isStarted=false
end
trigCHN:push(timer)
timer=0
end
end,
}

View File

@@ -0,0 +1,48 @@
local getCHN=love.thread.getChannel('CLIP_get')
local setCHN=love.thread.getChannel('CLIP_set')
local trigCHN=love.thread.getChannel('CLIP_trig')
JS=require'Zframework.js'
local sleep=require'love.timer'.sleep
local retrieving=false
while true do
if trigCHN:getCount()>0 then
local dt=trigCHN:pop()
if setCHN:getCount()>0 then
while setCHN:getCount()>1 do setCHN:pop() end
-- Set Clipboard
JS.callJS(JS.stringFunc(
[[
window.navigator.clipboard
.writeText('%s')
.then(() => console.log('Copied to clipboard'))
.catch((e) => console.warn(e));
]],
setCHN:pop()
))
end
-- Get Clipboard
if not retrieving then
JS.newPromiseRequest(
JS.stringFunc[[
window.navigator.clipboard
.readText()
.then((text) => _$_(text))
.catch((e)=>{});
]],
function(data)
while getCHN:getCount()>0 do getCHN:pop() end
getCHN:push(data)
retrieving=false
end,
function() retrieving=false end,
1,
'getClipboardText'
)
retrieving=true
end
JS.retrieveData(dt)
end
sleep(.001)
end

View File

@@ -1,5 +1,5 @@
local sendCHN=love.thread.getChannel('inputChannel')
local recvCHN=love.thread.getChannel('outputChannel')
local sendCHN=love.thread.getChannel('HTTP_inputChannel')
local recvCHN=love.thread.getChannel('HTTP_outputChannel')
local threads={}
local threadCount=0
@@ -9,11 +9,15 @@ local threadCode=[[
local http=require'socket.http'
local ltn12=require'ltn12'
local sendCHN=love.thread.getChannel('inputChannel')
local recvCHN=love.thread.getChannel('outputChannel')
local sendCHN=love.thread.getChannel('HTTP_inputChannel')
local recvCHN=love.thread.getChannel('HTTP_outputChannel')
local sleep=require'love.timer'.sleep
while true do
local arg=sendCHN:demand()
-- local arg=sendCHN:demand()
-- Warning: workaround for love.js
while sendCHN:getCount()==0 do sleep(.0626) end
local arg=sendCHN:pop()
if arg._destroy then
recvCHN:push{

View File

@@ -3,8 +3,16 @@
NONE={}function NULL() end PAPER=love.graphics.newCanvas(1,1)
EDITING=""
LOADED=false
---@type 'Windows'|'Android'|'Linux'|'iOS'|'macOS'|'Web'
SYSTEM=love.system.getOS()
if SYSTEM=='OS X' then SYSTEM='macOS' end
WEB_COMPAT_MODE=false
if SYSTEM=='OS X' then
SYSTEM='macOS'
elseif SYSTEM=='Web' then
WEB_COMPAT_MODE=not love.thread.newThread('\n'):start()
print('Web compatible mode: ', WEB_COMPAT_MODE)
end
-- Bit module
local success
@@ -72,6 +80,7 @@ do
end
-- Love-based modules (basic)
CLIPBOARD= require'Zframework.clipboard'
HTTP= require'Zframework.http'
WS= require'Zframework.websocket'
FILE= require'Zframework.file'
@@ -94,6 +103,10 @@ IMG= require'Zframework.image'
BGM= require'Zframework.bgm'
VOC= require'Zframework.voice'
if SYSTEM=='Web' then
JS=require'Zframework.js'
end
local ms,kb=love.mouse,love.keyboard
local KBisDown=kb.isDown
@@ -174,6 +187,7 @@ local function updatePowerInfo()
gc_pop()
gc.setCanvas()
end
-------------------------------------------------------------
local lastX,lastY=0,0-- Last click pos
local function _updateMousePos(x,y,dx,dy)
@@ -423,38 +437,38 @@ local dPadToKey={
start='return',
back='escape',
}
function love.joystickadded(JS)
function love.joystickadded(joystick)
table.insert(jsState,{
_id=JS:getID(),
_jsObj=JS,
_id=joystick:getID(),
_jsObj=joystick,
leftx=0,lefty=0,
rightx=0,righty=0,
triggerleft=0,triggerright=0
})
MES.new('info',"Joystick added")
end
function love.joystickremoved(JS)
function love.joystickremoved(joystick)
for i=1,#jsState do
if jsState[i]._jsObj==JS then
if jsState[i]._jsObj==joystick then
for j=1,#gamePadKeys do
if JS:isGamepadDown(gamePadKeys[j]) then
love.gamepadreleased(JS,gamePadKeys[j])
if joystick:isGamepadDown(gamePadKeys[j]) then
love.gamepadreleased(joystick,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)
love.gamepadaxis(joystick,'leftx',0)
love.gamepadaxis(joystick,'lefty',0)
love.gamepadaxis(joystick,'rightx',0)
love.gamepadaxis(joystick,'righty',0)
love.gamepadaxis(joystick,'triggerleft',-1)
love.gamepadaxis(joystick,'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
function love.gamepadaxis(joystick,axis,val)
if jsState[1] and joystick==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]
@@ -463,14 +477,14 @@ function love.gamepadaxis(JS,axis,val)
0
if newVal~=js[axis] then
if js[axis]==-1 then
love.gamepadreleased(JS,jsAxisEventName[axis][1])
love.gamepadreleased(joystick,jsAxisEventName[axis][1])
elseif js[axis]~=0 then
love.gamepadreleased(JS,jsAxisEventName[axis][2])
love.gamepadreleased(joystick,jsAxisEventName[axis][2])
end
if newVal==-1 then
love.gamepadpressed(JS,jsAxisEventName[axis][1])
love.gamepadpressed(joystick,jsAxisEventName[axis][1])
elseif newVal==1 then
love.gamepadpressed(JS,jsAxisEventName[axis][2])
love.gamepadpressed(joystick,jsAxisEventName[axis][2])
end
js[axis]=newVal
end
@@ -478,9 +492,9 @@ function love.gamepadaxis(JS,axis,val)
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])
love.gamepadpressed(joystick,jsAxisEventName[axis])
else
love.gamepadreleased(JS,jsAxisEventName[axis])
love.gamepadreleased(joystick,jsAxisEventName[axis])
end
js[axis]=newVal
end
@@ -720,6 +734,10 @@ function love.run()
-- UPDATE
STEP()
if SYSTEM == 'Web' then
JS.retrieveData(dt)
CLIPBOARD._update(dt)
end
if mouseShow then mouse_update(dt) end
if next(jsState) then gp_update(jsState[1],dt) end
VOC.update()

142
Zframework/js.lua Normal file
View File

@@ -0,0 +1,142 @@
local __requestQueue={}
local _requestCount=0
local _Request={
command="",
currentTime=0,
timeOut=2,
id='0',
}
local __defaultErrorFunction=nil
local isDebugActive=false
local JS={}
function JS.callJS(funcToCall)
print("callJavascriptFunction "..funcToCall)
end
--You can pass a set of commands here and, it is a syntactic sugar for executing many commands inside callJS, as it only calls a function
--If you pass arguments to the func beyond the string, it will perform automatically string.format
--Return statement is possible inside this structure
--This will return a string containing a function to be called by JS.callJS
function JS.stringFunc(str,...)
str="(function(){"..str.."})()"
if (#arg>0) then
str=str:format(unpack(arg))
end
str=str:gsub("[\n\t]","")
return str
end
--The call will store in the webDB the return value from the function passed it timeouts
local function retrieveJS(funcToCall,filename)
--Used for retrieveData function
JS.callJS(("FS.writeFile('%s/%s',%s);"):format(love.filesystem.getSaveDirectory(),filename,funcToCall))
end
--Call JS.newRequest instead
function _Request:new(isPromise,command,onDataLoaded,onError,timeout,id)
local obj={}
setmetatable(obj,self)
obj.command=command
obj.onError=onError or __defaultErrorFunction
if not isPromise then
retrieveJS(command,self.filename)
else
JS.callJS(command)
end
obj.onDataLoaded=onDataLoaded
obj.timeOut=(timeout==nil) and obj.timeOut or timeout
obj.id=id
obj.filename="__temp"..id
function obj:getData()
--Try to read from webdb
if love.filesystem.getInfo(self.filename) then
return love.filesystem.read(self.filename)
end
end
function obj:purgeData()
--Data must be purged for not allowing old data to be retrieved
love.filesystem.remove(self.filename)
end
function obj:update(dt)
self.timeOut=self.timeOut-dt
local retData=self:getData()
if ((retData~=nil and retData~="nil") or self.timeOut<=0) then
if (retData~=nil and retData:match("ERROR")==nil) then
if isDebugActive then
print("Data has been retrieved "..retData)
end
self.onDataLoaded(retData)
else
self.onError(self.id,retData)
end
self:purgeData()
return false
else
return true
end
end
return obj
end
--Place this function on love.update and set it to return if it returns false (This API is synchronous)
function JS.retrieveData(dt)
local isRetrieving=#__requestQueue~=0
local deadRequests={}
for i=1,#__requestQueue do
local isUpdating=__requestQueue[i]:update(dt)
if not isUpdating then
table.insert(deadRequests,i)
end
end
for i=1,#deadRequests do
if (isDebugActive) then
print("Request died: "..deadRequests[i])
end
table.remove(__requestQueue,deadRequests[i])
end
return isRetrieving
end
--May only be used for functions that don't return a promise
function JS.newRequest(funcToCall,onDataLoaded,onError,timeout,optionalId)
table.insert(__requestQueue,_Request:new(false,funcToCall,onDataLoaded,onError,timeout or 5,optionalId or _requestCount))
end
--This function can be handled manually (in JS code)
--How to: add the function call when your events resolve: FS.writeFile("Put love.filesystem.getSaveDirectory here", "Pass a string here (NUMBER DONT WORK"))
--Or it can be handled by Lua, it auto sets your data if you write the following command:
-- _$_(yourStringOrFunctionHere)
function JS.newPromiseRequest(funcToCall,onDataLoaded,onError,timeout,optionalId)
optionalId=optionalId or _requestCount
funcToCall=funcToCall:gsub("_$_%(","FS.writeFile('"..love.filesystem.getSaveDirectory().."/__temp"..optionalId.."', ")
table.insert(__requestQueue,_Request:new(true,funcToCall,onDataLoaded,onError,timeout or 5,optionalId))
end
--It receives the ID from ther request
--Don't try printing the request.command, as it will execute the javascript command
function JS.setDefaultErrorFunction(func)
__defaultErrorFunction=func
end
JS.setDefaultErrorFunction(function(id,error)
if (isDebugActive) then
local msg="Data could not be loaded for id:'"..id.."'"
if (error) then
msg=msg.."\nError: "..error
end
print(msg)
end
end)
JS.callJS(JS.stringFunc("__getWebDB('%s');","__LuaJSDB"))
return JS

View File

@@ -140,7 +140,7 @@ function profile.switch()
switch=not switch
if not switch then
profile.stop()
love.system.setClipboardText(profile.report())
CLIPBOARD.set(profile.report())
profile.reset()
return false
else

View File

@@ -189,7 +189,7 @@ do-- functions to shorted big numbers
function STRING.bigInt(t)
if t<1000 then
return tostring(t)
elseif t~=1e999 then
elseif t~=1/0 then
local e=floorint(lg(t)/3)
return(t/10^(e*3))..units[e+1]
else

View File

@@ -1,3 +1,4 @@
---@type love.Channel,love.Channel,love.Channel
local triggerCHN,sendCHN,readCHN=...
local CHN_demand,CHN_getCount=triggerCHN.demand,triggerCHN.getCount
@@ -5,13 +6,16 @@ local CHN_push,CHN_pop=triggerCHN.push,triggerCHN.pop
local SOCK=require'socket'.tcp()
local JSON=require'Zframework.json'
local sleep=require'love.timer'.sleep
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)
-- Warning: workaround for love.js, used to use CHN_demand instead
while CHN_getCount(sendCHN)<5 do sleep(.0626) end
local host=CHN_pop(sendCHN)
local port=CHN_pop(sendCHN)
local path=CHN_pop(sendCHN)
local head=CHN_pop(sendCHN)
local timeout=CHN_pop(sendCHN)
SOCK:settimeout(timeout)
local res,err=SOCK:connect(host,port)
@@ -186,7 +190,8 @@ end)
local success,err
while true do-- Running
CHN_demand(triggerCHN)
while CHN_getCount(triggerCHN)==0 do sleep(.0626) end
CHN_pop(triggerCHN)
success,err=pcall(sendThread)
if not success or err then break end
success,err=pcall(readThread)

View File

@@ -1,9 +1,9 @@
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
local 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
if OS=='Web' then
if system=='Web' then
local oldRead=love.filesystem.read
function love.filesystem.read(name,size)
if love.filesystem.getInfo(name) then
@@ -23,12 +23,13 @@ function love.conf(t)
local fileData=fs.read('conf/settings')
if fileData then
msaa=tonumber(fileData:match('"msaa":(%d+)')) or 0;
msaa=msaa==0 and 0 or 2*msaa
portrait=MOBILE and fileData:find('"portrait":true') and true
end
end
t.identity=identity -- Saving folder
t.version="11.4"
t.version="11.5"
t.gammacorrect=false
t.appendidentity=true -- Search files in source then in save directory
t.accelerometerjoystick=false -- Accelerometer=joystick on ios/android

View File

@@ -49,6 +49,10 @@ SCR.setSize(1280,720) -- Initialize Screen size
BGM.setMaxSources(5)
VOC.setDiversion(.62)
if SYSTEM == 'Web' and not WEB_COMPAT_MODE then
CLIPBOARD.setFreshInterval(.5)
end
WIDGET.setOnChange(function()
if SCN.cur=='net_game' or SCN.cur=='custom_field' then return end
local colorList=THEME.getThemeColor()
@@ -294,41 +298,41 @@ IMG.init{
},
}
SKIN.load{
{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'},
{name="Crystal (Scf)", path='media/image/skin/scf/crystal.png'},
{name="Smooth (MrZ)", path='media/image/skin/mrz/smooth.png'},
{name="Matte (MrZ)", path='media/image/skin/mrz/matte.png'},
{name="Glass (Scf)", path='media/image/skin/scf/glass.png'},
{name="Jelly (Miya)", path='media/image/skin/miya/jelly.png'},
{name="Simple (Scf)", path='media/image/skin/scf/simple.png'},
{name="Contrast (MrZ)", path='media/image/skin/mrz/contrast.png'},
{name="Plastic (MrZ)", path='media/image/skin/mrz/plastic.png'},
{name="Glow (MrZ)", path='media/image/skin/mrz/glow.png'},
{name="Bright (Scf)", path='media/image/skin/scf/bright.png'},
{name="Penta (Scf)", path='media/image/skin/scf/penta.png'},
{name="Bubble (Scf)", path='media/image/skin/scf/bubble.png'},
{name="Pure (MrZ)", path='media/image/skin/mrz/pure.png'},
{name="Letters (CHNO)", path='media/image/skin/chno/letters.png'},
{name="Kanji (CHNO)", path='media/image/skin/chno/kanji.png'},
{name="Pastel (CHNO)", path='media/image/skin/chno/pastel.png'},
{name="Classic", path='media/image/skin/unknown/classic.png'},
{name="Arcade (Asriel)", path='media/image/skin/asriel/arcade.png'},
{name="Shiny (CHNO)", path='media/image/skin/chno/shiny.png'},
{name="Brick (Notypey)", path='media/image/skin/notypey/brick.png'},
{name="Cartooncup (Earety)", path='media/image/skin/earety/cartooncup.png'},
{name="Paper (MrZ)", path='media/image/skin/mrz/paper.png'},
{name="Toy (Scf)", path='media/image/skin/scf/toy.png'},
{name="Polkadots (Scf)", path='media/image/skin/scf/polkadots.png'},
{name="Yinyang (Scf)", path='media/image/skin/scf/yinyang.png'},
{name="Minoes (Scf)", path='media/image/skin/scf/minoes.png'},
{name="Cardboard (Asriel, slimenergy)", path='media/image/skin/asriel/cardboard.png'},
{name="Ball (Shaw)", path='media/image/skin/shaw/ball.png'},
{name="Gem (Notypey)", path='media/image/skin/notypey/gem.png'},
{name="Pixel (CHNO)", path='media/image/skin/chno/pixel.png'},
{name="Retro (Notypey)", path='media/image/skin/notypey/retro.png'},
{name="Guidetris (xmiao, lusisi)", path='media/image/skin/guidetris_xmiao_lusisi.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
@@ -400,8 +404,12 @@ do
RANKS.rhythm_h=nil; fs.remove('record/rhythm_h.rec')
RANKS.rhythm_u=nil; fs.remove('record/rhythm_u.rec')
end
if RANKS.bigbang then fs.remove('record/bigbang.rec') end
if RANKS.clearRush then fs.remove('record/clearRush.rec') end
if RANKS.bigbang then RANKS.bigbang=nil; fs.remove('record/bigbang.rec') end
if RANKS.clearRush then RANKS.clearRush=nil; fs.remove('record/clearRush.rec') end
if RANKS.strategy_e then RANKS.strategy_e=nil; fs.remove('record/strategy_e.rec') end
if RANKS.strategy_h_plus then RANKS.strategy_h_plus=nil; fs.remove('record/strategy_h_plus.rec') end
if RANKS.strategy_u_plus then RANKS.strategy_u_plus=nil; fs.remove('record/strategy_u_plus.rec') end
if STAT.version<1715 then fs.remove('record/dig_quad_10l.rec') end
if STAT.version~=VERSION.code then
@@ -436,6 +444,7 @@ do
if SETTING.reTime>3 or SETTING.reTime<.5 then SETTING.reTime=2 end
if SETTING.locale=='zh_full' then SETTING.locale='zh' end
if SETTING.vocPack=='rin' then SETTING.vocPack='miku' end
if SETTING.msaa>4 then SETTING.msaa=4 end
if RANKS.infinite then RANKS.infinite=0 end
if RANKS.infinite_dig then RANKS.infinite_dig=0 end
if not RANKS.sprint_10l then RANKS.sprint_10l=0 end

View File

@@ -330,20 +330,20 @@ local L={
},
cards={-- F0300 - F070F
cardBack= 0xF0300,
clubA= 0xF0301,
club2= 0xF0302,
club3= 0xF0303,
club4= 0xF0304,
club5= 0xF0305,
club6= 0xF0306,
club7= 0xF0307,
club8= 0xF0308,
club9= 0xF0309,
club10= 0xF030A,
clubJ= 0xF030B,
clubC= 0xF030C,
clubQ= 0xF030D,
clubK= 0xF030E,
spadeA= 0xF0301,
spade2= 0xF0302,
spade3= 0xF0303,
spade4= 0xF0304,
spade5= 0xF0305,
spade6= 0xF0306,
spade7= 0xF0307,
spade8= 0xF0308,
spade9= 0xF0309,
spade10= 0xF030A,
spadeJ= 0xF030B,
spadeC= 0xF030C,
spadeQ= 0xF030D,
spadeK= 0xF030E,
heartA= 0xF0401,
heart2= 0xF0402,
heart3= 0xF0403,

View File

@@ -1,3 +1,7 @@
if SYSTEM=='Web' then
return {update=NULL}
end
local appId='1288557386700951554'
local ffi=require"ffi"
@@ -133,7 +137,7 @@ if RPC_C then
local function checkIntArg(arg,maxBits,argName,func,maybeNil)
maxBits=math.min(maxBits or 32,52) -- lua number (double) can only store integers < 2^53
local maxVal=2^(maxBits-1) -- assuming signed integers, which, for now, are the only ones in use
local maxVal=2^(maxBits-1) -- assuming signed integers, which, for now, are the only ones in use
assert(type(arg)=="number" and math.floor(arg)==arg
and arg<maxVal and arg>=-maxVal
or (maybeNil and arg==nil),

View File

@@ -164,7 +164,7 @@ do-- function applySettings()
BG.set()
if SETTING.lockBG then
BG.lock()
elseif reason=='lockBG' then -- Don't load theme too soon!
elseif reason=='lockBG' then -- We only reload theme again when at settings scene.
THEME.set(THEME.calculate(),GAME.playing)
end
elseif SETTING.bg=='off' then

View File

@@ -30,11 +30,11 @@ return {
"The official website of Techmino!\nYou can download the latest stable build of Techmino and change your profile there.\nClick on the globe icon to open the website in your browser.",
"http://studio26f.org",
},
{"Huiji Wiki",
"huiji wiki",
{"Chinese Tetris Wiki",
"china tetris wiki chinese tetris wiki",
"help",
"(灰机wiki)\nA Chinese Tetris wiki by Tetris enthusiasts from Chinese Tetris Research Community groups and affiliates. Most pages have been referenced and translated from Hard Drop Wiki and Tetris Wiki for now. Link in Simplified Chinese.",
"https://tetris.huijiwiki.com",
"A Chinese Tetris wiki by Tetris enthusiasts from Chinese Tetris Study Community groups and affiliates. Most pages have been referenced and translated from Hard Drop Wiki and Tetris Wiki for now. Link in Simplified Chinese.",
"http://tetriswiki.cn",
},
{"Hard Drop Wiki",
"harddrop hd wiki",

View File

@@ -29,12 +29,12 @@ return {
"Techminoの公式ホームページです!\n最新の安定版Techminoをダウンロードしたり、プロフィールを編集したりできます\n地球儀ボタンから是非アクセスしてください",
"http://studio26f.org",
},
{"灰机wiki",
"huiji wiki ウィキ うぃき 灰机 フイジ",
"help",
"huiji wiki\n\n中国のテトリス研究グループとそのサブグループに所属しているテトリスプレーヤー達が管理している中国のテトリスwikiです\n現在、大部分のページがHard drop wikiTetris wikiから参照、翻訳されたページになっています",
"https://tetris.huijiwiki.com",
},
-- {"Chinese Tetris Wiki",
-- "china tetris wiki",
-- "help",
-- "A Chinese Tetris wiki by Tetris enthusiasts from Chinese Tetris Research Community groups and affiliates. Most pages have been referenced and translated from Hard Drop Wiki and Tetris Wiki for now. Link in Simplified Chinese.",
-- "http://tetriswiki.cn",
-- },
{"HardDrop wiki",
"harddrop hd wiki ハードドロップ ハードロ ウィキ うぃき",
"help",

View File

@@ -77,7 +77,7 @@ Bạn muốn đóng góp vào bản dịch? Bạn có thể vào trang dự án
08. Bot: Cold Clear, ZZZbot
09. Wiki; các trang web bày setup & cung cấp câu đố, chia sẻ setup
09A. Wiki: Huiji Wiki, Wiki Hard Drop, tetris.wiki, Tetris Wiki Fandom
09A. Wiki: Chinese Tetris Wiki, Wiki Hard Drop, tetris.wiki, Tetris Wiki Fandom
09B. Bày setup: Four.lol, Tetris Hall, Tetris Template Collections, tetristemplate.info, 4-Wide Trainer
09C. Chia sẻ câu đố: TTT, TTPC, NAZO, TPO
09D. Chia sẻ setup: Fumen, Fumen bản Điện thoại
@@ -612,7 +612,7 @@ Xem mục tiếp theo để biết thêm.
{"Lockdown Delay",
"nhom05e1 lockdelay lockdowndelay lockdowntimer ld; thời gian chờ khóa gạch",
"term",
"Thời gian chờ khóa gạch, viết tắt là LD.\nGame sẽ chờ một khoảng thời gian, trước khi game khóa gạch (tức là gạch không thể điều khiển được nữa), bắt đầu tính từ khi gạch vừa chạm đất.\n\nTrong các game xếp gạch cổ điển, khoảng thời gian chờ này CHÍNH LÀ khoảng thời gian gạch cần có để di chuyển xuống 1 ô, và không có cơ chế nào để trì hoãn việc khóa gạch.\n\nTrong các game xếp gạch hiện đại, thời gian chờ được tính riêng và thoải mái hơn. Đi kèm với thời gian chờ dài hơn là cơ chế hoãn thời điểm khóa gạch, trong đó bạn có thể di chuyển hoặc xoay gạch bắt game chờ thêm một lúc nữa trước khi khóa gạch; nói theo cách khác, là đặt lại thời gian chờ (tối đa 15 lần trong hầu hết các game).",
"Thời gian chờ khóa gạch, viết tắt là LD.\nGame sẽ chờ một khoảng thời gian, trước khi game khóa gạch (tức là gạch không thể điều khiển được nữa), bắt đầu tính từ khi gạch vừa chạm đất.\n\nTrong các game xếp gạch cổ điển, khoảng thời gian chờ này CHÍNH LÀ khoảng thời gian gạch cần có để di chuyển xuống 1 ô, và không có cách nào để hoãn thời điểm khóa gạch.\n\nTrong các game xếp gạch hiện đại, thời gian chờ được tính riêng và thoải mái hơn. Đi kèm với thời gian chờ dài hơn là cơ chế hoãn thời điểm khóa gạch, trong đó bạn có thể di chuyển hoặc xoay gạch để đặt lại bộ đếm thời gian về 0, để bạn có thể hoãn thời điểm khóa gạch thêm một lúc nữa (tối đa 15 lần trong hầu hết các game).",
},
{"Spawn&ClearDelay",
"nhom05e1 spawndelay cleardelay; thời gian chờ gạch sinh ra; thời gian chờ xóa hàng",
@@ -632,7 +632,7 @@ Xem mục tiếp theo để biết thêm.
{"Death ARE",
"nhom05e1 die delay",
"term",
"Một cơ chế đặc biệt cho phép tránh game over trong một số trường hợp.\n\nDeath ARE sẽ được kích hoạt khi có một viên gạch chặn ngay tại vị trí xuất hiện của gạch mới (dẫn tới hiện tượng block out)\nKhi kích hoạt, spawn ARE sẽ được cộng với Death ARE để cho phép người chơi dùng IRS, IHS hoặc IMS.\n\nÝ tưởng về cơ chế này được đề xuất lần đầu bởi @NOT_A_ROBOT.",
"Một cơ chế đặc biệt cho phép tránh game over trong một số trường hợp.\n\nDeath ARE sẽ được kích hoạt khi có một viên gạch chặn ngay tại vị trí xuất hiện của gạch mới (dẫn tới hiện tượng block out)\nKhi kích hoạt, spawn ARE sẽ được cộng với Death ARE để cho phép người chơi có cơ hội dùng IRS, IHS IMS.\n\nÝ tưởng về cơ chế này được đề xuất lần đầu bởi @NOT_A_ROBOT.",
},
{">E2|Thg số đ.khiển",
"nhom05e2",
@@ -1436,12 +1436,12 @@ NHÓM 07: MỘT VÀI CƠ CHẾ VÀ CHẾ ĐỘ CỦA MỘT SỐ GAME
"",
""
},
{"Huiji Wiki",
"nhom09a huiji wiki",
"help",
"(灰机wiki)\n\nMột wiki về Tetris của những người đam mê Tetris từ các nhóm và chi nhánh của Cộng đồng Nghiên cứu Tetris Trung Quốc. Hiện tại hầu hết các trang đều được tham khảo và dịch từ Wiki Hard Drop Tetris Wiki. Liên kết sẽ dẫn bạn tới bản tiếng Trung giản thể.",
"https://tetris.huijiwiki.com",
},
-- {"Chinese Tetris Wiki",
-- "china tetris wiki",
-- "help",
-- "A Chinese Tetris wiki by Tetris enthusiasts from Chinese Tetris Research Community groups and affiliates. Most pages have been referenced and translated from Hard Drop Wiki and Tetris Wiki for now. Link in Simplified Chinese.",
-- "http://tetriswiki.cn",
-- },
{"Wiki Hard Drop",
"nhom09a harddrop hd wiki",
"help",

View File

@@ -23,11 +23,11 @@ return {
"Techmino的官网\n可以在上面下载游戏本体,或者修改头像以及个人信息。\n\n游戏作者的一些话强烈不建议在任何公开场合提及甚至宣传Techmino更不要随便对外发送我们的官网链接请务必只在私下里向有基础或真的很有兴趣入坑认真玩的玩家推荐不然很容易拉低社群质量破坏交流氛围比较难处理甚至有可能影响游戏的未来发展。为了保证游戏能够变得越来越好玩千万慎重考虑您对游戏的推广方式感谢您对Techmino的大力支持",
"http://studio26f.org",
},
{"灰机Wiki",
"huiji",
{"俄罗斯方块中文维基",
"zhongwenweiji wiki",
"help",
"俄罗斯方块中文维基,由一群来自俄罗斯方块研究群及下属群的方块同好建立的关于俄罗斯方块的中文百科全书。\n\n目前其大部分页面翻译和参考来自Hard Drop Wiki和Tetris Wiki",
"https://tetris.huijiwiki.com",
"http://tetriswiki.cn",
},
{"HardDrop Wiki",
"hd",

View File

@@ -920,12 +920,9 @@ C. Gamepad
['master_g']= {"Master", "GRADED", "Get the highest grade you can!"},
['master_ex']= {"GrandMaster", "EXTRA", "An eternity shorter than an instant"},
['master_instinct']= {"Master", "INSTINCT", "What if the active piece becomes invisible?"},
['strategy_e']= {"Strategy", "EASY", "Fast 20G decision"},
['strategy_e_plus']= {"Strategy", "EASY+", "Holdless strategy!"},
['strategy_h']= {"Strategy", "HARD", "Fast 20G decision"},
['strategy_u']= {"Strategy", "ULTIMATE", "Fast 20G decision"},
['strategy_e_plus']= {"Strategy", "EASY+", "Holdless strategy!"},
['strategy_h_plus']= {"Strategy", "HARD+", "Holdless strategy!"},
['strategy_u_plus']= {"Strategy", "ULTIMATE+", "Holdless strategy!"},
['blind_e']= {"Invisible", "SLOW", "For beginners"},
['blind_n']= {"Invisible", "FAST", "For intermediates"},
['blind_h']= {"Invisible", "INSTANT", "For the experienced"},

View File

@@ -877,12 +877,9 @@ return {
['master_g']= {"Master", "Con rangos", "¡Consigue el rango más alto que puedas!"},
['master_ex']= {"GrandMaster", "Extra", "Una eternidad que dura un instante."},
['master_instinct']={"Master", "Instintivo", "¿Y si la pieza activa es invisible?"},
['strategy_e']= {"Strategy", "Fácil", "Decisiones rápidas en 20G."},
['strategy_e_plus']={"Strategy", "Fácil+", "Lo mismo pero sin reserva!"},
['strategy_h']= {"Strategy", "Difícil", "Decisiones rápidas en 20G."},
['strategy_u']= {"Strategy", "Supremo", "Decisiones rápidas en 20G."},
['strategy_e_plus']={"Strategy", "Fácil+", "Lo mismo pero sin reserva!"},
['strategy_h_plus']={"Strategy", "Difícil+", "Lo mismo pero sin reserva!"},
['strategy_u_plus']={"Strategy", "Supremo+", "Lo mismo pero sin reserva!"},
-- ['blind_e']= {"Invisible", "SLOW", "For beginners"},
-- ['blind_n']= {"Invisible", "FAST", "For intermediates"},
-- ['blind_h']= {"Invisible", "INSTANT", "For the experienced"},

View File

@@ -242,6 +242,7 @@ return {
"Ceci est un simple jeu de blocs.",
"On y joue comme sur C2/IO/JS/WWC/KOS et autres.",
"",
"Propulsé par LÖVE",
"Vous pouvez envoyer des rapports de bogues ou des suggestions via le groupe de test ou l'email du créateur ~",
"Assurez-vous d'obtenir le jeu uniquement des canaux officiels,",
"Ne téléchargez pas ce jeu depuis une autre source au risque d'avoir des virus,",
@@ -850,12 +851,9 @@ return {
['master_final']= {"Master", "FINAL", "20G : Un point final impossible à atteindre !"},
-- ['master_ph']= {"Master", "FANTASMA", "20G: ???"},
['master_ex']= {"GrandMaster", "EXTRA", "Tentez de devenir un Grandmaster."},
['strategy_e']= {"Stratégie", "FACILE", "Décision rapide 20G"},
['strategy_e_plus']={"Stratégie", "FACILE+", "Stratégie sans retenue"},
['strategy_h']= {"Stratégie", "DIFFICILE", "Décision rapide 20G"},
['strategy_u']= {"Stratégie", "ULTIME", "Décision rapide 20G"},
['strategy_e_plus']={"Stratégie", "FACILE+", "Stratégie sans retenue"},
['strategy_h_plus']={"Stratégie", "DIFFICILE+", "Stratégie sans retenue"},
['strategy_u_plus']={"Stratégie", "ULTIME+", "Stratégie sans retenue"},
-- ['blind_e']= {"Invisible", "SLOW", "For beginners"},
-- ['blind_n']= {"Invisible", "FAST", "For intermediates"},
-- ['blind_h']= {"Invisible", "INSTANT", "For the experienced"},

View File

@@ -881,12 +881,9 @@ return {
['master_g']= {"Ahli", "BERTINGKAT", "Dapatkan tingkat tertinggi!"},
['master_ex']= {"Sangat Ahli", "EKSTRA", "Blok tidak kelihatan"},
['master_instinct']= {"Ahli", "INSTINK", "Bagaimana jika blok terkontrol tersembunyi?"},
['strategy_e']= {"Strategi", "MUDAH", "Keputusan 20G cepat"},
['strategy_e_plus']= {"Strategi", "MUDAH+", "Mode strategi, tetapi tanpa menyimpan"},
['strategy_h']= {"Strategi", "SULIT", "Keputusan 20G cepat"},
['strategy_u']= {"Strategi", "TERAKHIR", "Keputusan 20G cepat"},
['strategy_e_plus']= {"Strategi", "MUDAH+", "Mode strategi, tetapi tanpa menyimpan"},
['strategy_h_plus']= {"Strategi", "SULIT+", "Mode strategi, tetapi tanpa menyimpan"},
['strategy_u_plus']= {"Strategi", "TERAKHIR+", "Mode strategi, tetapi tanpa menyimpan"},
['blind_e']= {"Tak Terlihat", "PELAN", "Untuk pemula"},
['blind_n']= {"Tak Terlihat", "CEPAT", "Untuk amatir"},
['blind_h']= {"Tak Terlihat", "INSTAN", "Untuk orang berpengalaman"},

View File

@@ -925,12 +925,9 @@ C. ゲームパッド
['master_g']= {"マスター", "GRADED", "最高段位を取れ!"},
['master_ex']= {"グランドマスター", "EXTRA", "一瞬にも満たない永遠"},
['master_instinct']= {"マスター", "INSTINCT", "もしミノが見えなくなったら?"},
['strategy_e']= {"ストラテジー", "EASY", "20Gでの素早い判断"},
['strategy_e_plus']= {"ストラテジー", "EASY+", "20Gでの素早い判断"},
['strategy_h']= {"ストラテジー", "HARD", "20Gでの素早い判断"},
['strategy_u']= {"ストラテジー", "ULTIMATE", "20Gでの素早い判断"},
['strategy_e_plus']= {"ストラテジー", "EASY+", "20Gでの素早い判断"},
['strategy_h_plus']= {"ストラテジー", "HARD+", "20Gでの素早い判断"},
['strategy_u_plus']= {"ストラテジー", "ULTIMATE+", "20Gでの素早い判断"},
['blind_e']= {"インビジブル", "HALF", "初心者用"},
['blind_n']= {"インビジブル", "ALL", "中級者用"},
['blind_h']= {"インビジブル", "SUDDEN", "上級者用"},

View File

@@ -863,12 +863,9 @@ return {
['master_final']= {"Mestre", "FINAL", "20G: Final inalcançável!"},
['master_ph']= {"Mestre", "FANTASMA", "20G: ???"},
['master_ex']= {"GrandMaster", "EXTRA", "Para ser um Grand Master, aceite \nesse desafio."},
-- ['strategy_e']= {"Strategy", "EASY", "Fast 20G decision"},
-- ['strategy_e_plus']={"Strategy", "EASY+", "Holdless strategy"},
-- ['strategy_h']= {"Strategy", "HARD", "Fast 20G decision"},
-- ['strategy_u']= {"Strategy", "ULTIMATE", "Fast 20G decision"},
-- ['strategy_e_plus']={"Strategy", "EASY+", "Holdless strategy"},
-- ['strategy_h_plus']={"Strategy", "HARD+", "Holdless strategy"},
-- ['strategy_u_plus']={"Strategy", "ULTIMATE+", "Holdless strategy"},
-- ['blind_e']= {"Invisible", "SLOW", "For beginners"},
-- ['blind_n']= {"Invisible", "FAST", "For intermediates"},
-- ['blind_h']= {"Invisible", "INSTANT", "For the experienced"},

View File

@@ -923,12 +923,9 @@ C. Tay cầm chơi game (Gamepad):
['master_g']= {"Master", "GRADED", "Lấy điểm cao nhất có thể!"},
['master_ex']= {"GrandMaster", "EXTRA", "Cũng là lấy điểm cao nhất có thể nhưng mà gắt hơn!"},
['master_instinct']= {"Master", "INSTINCT", "Lấy điểm cao nhất có thể nhưng với gạch tàng hình!"},
['strategy_e']= {"Strategy", "DỄ", "Quyết định nhanh hoặc là thua"},
['strategy_e_plus']= {"Strategy", "DỄ+", "Quyết định nhanh và không được Hold!"},
['strategy_h']= {"Strategy", "KHÓ", "Quyết định nhanh hoặc là thua"},
['strategy_u']= {"Strategy", "THÁCH ĐẤU", "Quyết định nhanh hoặc là thua"},
['strategy_e_plus']= {"Strategy", "DỄ+", "Quyết định nhanh và không được Hold!"},
['strategy_h_plus']= {"Strategy", "KHÓ+", "Quyết định nhanh và không được Hold!"},
['strategy_u_plus']= {"Strategy", "THÁCH ĐẤU+", "Quyết định nhanh và không được Hold!"},
['blind_e']= {"Invisible", "DỄ", "Dành cho người mới"},
['blind_n']= {"Invisible", "THƯỜNG", "Dành cho người đã quen"},
['blind_h']= {"Invisible", "KHÓ", "Dành cho người đã có kinh nghiệm"},

View File

@@ -909,12 +909,9 @@ return {
['master_g']= {"大师", "段位考试", "20G段位考试"},
['master_ex']= {"宗师", "EX", "成为方块大师"},
['master_instinct']={"大师", "本能", "当前块在出现后一小会后会隐形"},
['strategy_e']= {"策略堆叠", "简单", "20G堆叠中速决策练习"},
['strategy_e_plus']={"策略堆叠", "简单+", "20G堆叠中速决策练习\n无Hold"},
['strategy_h']= {"策略堆叠", "困难", "20G堆叠快速决策练习"},
['strategy_u']= {"策略堆叠", "极限", "20G堆叠极速决策练习"},
['strategy_e_plus']={"策略堆叠", "简单+", "20G堆叠中速决策练习\n无Hold"},
['strategy_h_plus']={"策略堆叠", "困难+", "20G堆叠快速决策练习\n无Hold"},
['strategy_u_plus']={"策略堆叠", "极限+", "20G堆叠极速决策练习\n无Hold"},
['blind_e']= {"隐形", "半隐", "不强大脑"},
['blind_n']= {"隐形", "全隐", "挺强大脑"},
['blind_h']= {"隐形", "瞬隐", "很强大脑"},
@@ -1060,7 +1057,7 @@ return {
"游戏原声已上架网易云音乐",
"有建议的话可以反馈给作者~",
"这不是休闲游戏……别怪关卡要求太高,多练吧",
"中文方块百科全书tetris.huijiwiki.com",
"中文方块百科全书tetriswiki.cn",
"众所周知mac不能拿来玩游戏",
"作业没做完别玩手机",
"作者40行sub26了",

View File

@@ -828,12 +828,9 @@ return {
['master_g']= {"Master(Graded);", "", "20G段位考试"},
['master_ex']= {"Master(EX);", "", "成为方块大师"},
['master_instinct']= {"Master(Instinct);", "", "当前块在出现后一小会后会隐形"},
['strategy_e']= {"Strategy(Easy);", "", "20G堆叠中速决策练习"},
['strategy_e_plus']= {"Strategy(EasyP);", "", "20G堆叠中速决策练习\n无Hold"},
['strategy_h']= {"Strategy(Hard);", "", "20G堆叠快速决策练习"},
['strategy_u']= {"Strategy(Ultimate);", "", "20G堆叠极速决策练习"},
['strategy_e_plus']= {"Strategy(EasyP);", "", "20G堆叠中速决策练习\n无Hold"},
['strategy_h_plus']= {"Strategy(HardP);", "", "20G堆叠快速决策练习\n无Hold"},
['strategy_u_plus']= {"Strategy(UltimateP);", "", "20G堆叠极速决策练习\n无Hold"},
['blind_e']= {"Blind(Slow);", "", "不强大脑"},
['blind_n']= {"Blind(Fast);", "", "挺强大脑"},
['blind_h']= {"Blind(Instant);", "", "很强大脑"},

View File

@@ -880,12 +880,9 @@ return {
['master_g']= {"大師", "段位考試", "20G段位考試"},
['master_ex']= {"宗師", "EX", "成為方塊大師"},
['master_instinct']={"大師", "本能", "隱藏當前塊"},
['strategy_e']= {"策略堆疊", "簡單", "20G堆疊中速決策練習"},
['strategy_e_plus']={"策略堆疊", "簡單+", "20G堆疊中速決策練習\n無Hold"},
['strategy_h']= {"策略堆疊", "困難", "20G堆疊快速決策練習"},
['strategy_u']= {"策略堆疊", "極限", "20G堆疊極速決策練習"},
['strategy_e_plus']={"策略堆疊", "簡單+", "20G堆疊中速決策練習\n無Hold"},
['strategy_h_plus']={"策略堆疊", "困難+", "20G堆疊快速決策練習\n無Hold"},
['strategy_u_plus']={"策略堆疊", "極限+", "20G堆疊極速決策練習\n無Hold"},
['blind_e']= {"隱形", "半隱", "不強大腦"},
['blind_n']= {"隱形", "全隱", "挺強大腦"},
['blind_h']= {"隱形", "瞬隱", "很強大腦"},

View File

@@ -30,7 +30,7 @@ return {
{name='dig_eff_400l', x=-1200,y=0, size=40,shape=1,icon="dig_eff"},
{name='marathon_n', x=0, y=-600, size=60,shape=1,icon="marathon", unlock={'marathon_h','solo_e','round_e','big_n','blind_e','classic_e','survivor_e','c4wtrain_n','pctrain_n','sprintAtk','zen','construct_sg'}},
{name='marathon_h', x=0, y=-800, size=50,shape=1,icon="marathon", unlock={'master_n','strategy_e'}},
{name='marathon_h', x=0, y=-800, size=50,shape=1,icon="marathon", unlock={'master_n','strategy_e_plus'}},
{name='solo_e', x=-600, y=-1000,size=40,shape=1,icon="solo", unlock={'solo_n'}},
{name='solo_n', x=-800, y=-1000,size=40,shape=1,icon="solo", unlock={'solo_h'}},
@@ -62,12 +62,9 @@ return {
{name='master_g', x=0, y=-1600,size=40,shape=3,icon="master"},
{name='master_ex', x=170, y=-1450,size=40,shape=2,icon="master_ex"},
{name='strategy_e', x=-150, y=-1020,size=40,shape=3,icon="master", unlock={'strategy_e_plus'}},
{name='strategy_h', x=-150, y=-1150,size=35,shape=3,icon="master", unlock={'strategy_h_plus'}},
{name='strategy_u', x=-150, y=-1280,size=30,shape=2,icon="master", unlock={'strategy_u_plus'}},
{name='strategy_e_plus', x=-300, y=-1120,size=40,shape=3,icon="master"},
{name='strategy_h_plus', x=-300, y=-1250,size=35,shape=3,icon="master"},
{name='strategy_u_plus', x=-300, y=-1380,size=30,shape=2,icon="master"},
{name='strategy_e_plus', x=-150, y=-1020,size=40,shape=3,icon="master"},
{name='strategy_h', x=-150, y=-1150,size=35,shape=3,icon="master"},
{name='strategy_u', x=-150, y=-1280,size=30,shape=2,icon="master"},
{name='blind_e', x=150, y=-700, size=40,shape=1,icon="hidden", unlock={'blind_n','master_instinct'}},
{name='blind_n', x=150, y=-800, size=40,shape=1,icon="hidden2", unlock={'blind_h'}},

View File

@@ -1023,7 +1023,7 @@ function Player:freshBlockGhost()
if self.curY>self.ghoY then
self:createDropFX()
if ENV.shakeFX then
self.swingOffset.vy=.5
self.swingOffset.vy=MATH.clamp((self.curY-self.ghoY-2.6)/10,0,0.626)
end
self.curY=self.ghoY
end

View File

@@ -6,6 +6,9 @@ local scene={}
function scene.enter()
BG.set()
for i=1,#text.aboutTexts do
text.aboutTexts[i]=text.aboutTexts[i]:gsub("LÖVE%(?.*%)?",STRING.repD("LÖVE($1.$2)",love.getVersion()))
end
end
function scene.mouseDown(x,y)

View File

@@ -949,16 +949,16 @@ end
local combKey={
x=function()
love.system.setClipboardText(inputBox:getText())
CLIPBOARD.set(inputBox:getText())
inputBox:clear()
SFX.play('reach')
end,
c=function()
love.system.setClipboardText(inputBox:getText())
CLIPBOARD.set(inputBox:getText())
SFX.play('reach')
end,
v=function()
inputBox:addText(love.system.getClipboardText())
inputBox:addText(CLIPBOARD.get())
SFX.play('reach')
end,
}

View File

@@ -204,7 +204,7 @@ function scene.update(dt)
end
ct=ct-1
if ct==0 then
local t=love.system.getClipboardText()
local t=CLIPBOARD.get()
if type(t)=='string' then
t=t:lower():match("^s=(%d+)$")
t=t and tonumber(t) and tonumber(t)>0 and tonumber(t)<=8000 and floor(tonumber(t))

View File

@@ -181,7 +181,7 @@ function reset()
time=0
score=0
local t=love.system.getClipboardText()
local t=CLIPBOARD.get()
if type(t)=='string' then
t=t:lower():match("^s=(.+)")
t=t and tonumber(t) and tonumber(t)*2

View File

@@ -188,10 +188,10 @@ function scene.keyDown(key,isRep)
if #CUSTOMGAME_LOCAL.bag>0 then str=str..DATA.copySequence(CUSTOMGAME_LOCAL.bag) end
str=str.."!"
if #CUSTOMGAME_LOCAL.mission>0 then str=str..DATA.copyMission(CUSTOMGAME_LOCAL.mission) end
sys.setClipboardText(str.."!"..DATA.copyBoards(CUSTOMGAME_LOCAL.field).."!")
CLIPBOARD.set(str.."!"..DATA.copyBoards(CUSTOMGAME_LOCAL.field).."!")
MES.new('check',text.exportSuccess)
elseif key=='v' and kb.isDown('lctrl','rctrl') or key=='cV' then
local str=sys.getClipboardText()
local str=CLIPBOARD.get()
local args=str:sub((str:find(":") or 0)+1):split("!")
local hasTooHighField=false
repeat

View File

@@ -226,10 +226,10 @@ function scene.keyDown(key)
SFX.play('clear_4',.8)
SFX.play('fall',.8)
elseif key=='c' and kb.isDown('lctrl','rctrl') or key=='cC' then
sys.setClipboardText("Techmino Field:"..DATA.copyBoard(FIELD[page]))
CLIPBOARD.set("Techmino Field:"..DATA.copyBoard(FIELD[page]))
MES.new('check',text.exportSuccess)
elseif key=='v' and kb.isDown('lctrl','rctrl') or key=='cV' then
local str=sys.getClipboardText()
local str=CLIPBOARD.get()
local p=str:find(":")-- ptr*
if p then
if not str:sub(1,p-1):find("Field") then

View File

@@ -68,11 +68,11 @@ function scene.keyDown(key)
end
elseif key=='c' and kb.isDown('lctrl','rctrl') or key=='cC' then
if #MISSION>0 then
sys.setClipboardText("Techmino Target:"..DATA.copyMission(MISSION))
CLIPBOARD.set("Techmino Target:"..DATA.copyMission(MISSION))
MES.new('check',text.exportSuccess)
end
elseif key=='v' and kb.isDown('lctrl','rctrl') or key=='cV' then
local str=sys.getClipboardText()
local str=CLIPBOARD.get()
local p=str:find(":")-- ptr*
if p then
if not str:sub(1,p-1):find("Target") then

View File

@@ -1,4 +1,3 @@
local sys=love.system
local kb=love.keyboard
local sin=math.sin
@@ -76,11 +75,11 @@ function scene.keyDown(key)
scene.widgetList.sequence:scroll(kb.isDown('lshift','rshift') and -1 or 1)
elseif key=='c' and kb.isDown('lctrl','rctrl') or key=='cC' then
if #BAG>0 then
sys.setClipboardText("Techmino SEQ:"..DATA.copySequence(BAG))
CLIPBOARD.set("Techmino SEQ:"..DATA.copySequence(BAG))
MES.new('check',text.exportSuccess)
end
elseif key=='v' and kb.isDown('lctrl','rctrl') or key=='cV' then
local str=sys.getClipboardText()
local str=CLIPBOARD.get()
local p=str:find(":")-- ptr*
if p then
if not str:sub(1,p-1):find("SEQ") then

View File

@@ -124,7 +124,7 @@ end
local function _copy()
local t=_getList()[listBox.selected]
t=t.title_Org..":\n"..t.content_Org..(t.url and "\n[ "..t.url.." ]\n" or "\n")..text.dictNote
love.system.setClipboardText(t)
CLIPBOARD.set(t)
scene.widgetList.copy.hide=true
MES.new('info',text.copyDone)
end

View File

@@ -14,7 +14,7 @@ local function _submit()
end
end
local function _paste()
local t=love.system.getClipboardText()
local t=CLIPBOARD.get()
if t then
t=STRING.trim(t)
if #t==128 and t:match("[0-9A-Z]+") then

View File

@@ -97,7 +97,7 @@ function scene.keyDown(key)
if rep.available and rep.fileName then
local repStr=loadFile(rep.fileName,'-string')
if repStr then
love.system.setClipboardText(love.data.encode('string','base64',repStr))
CLIPBOARD.set(love.data.encode('string','base64',repStr))
MES.new('info',text.exportSuccess)
else
MES.new('error',text.replayBroken)
@@ -107,7 +107,7 @@ function scene.keyDown(key)
end
end
elseif key=='v' and kb.isDown('lctrl','rctrl') or key=='cV' then
local repStr=love.system.getClipboardText()
local repStr=CLIPBOARD.get()
local res,fileData=pcall(love.data.decode,'string','base64',repStr)
if res then
local fileName=os.date("replay/%Y_%m_%d_%H%M%S_import.rep")

View File

@@ -15,7 +15,7 @@ local function _setPW()
end
end
local function _paste()
local t=love.system.getClipboardText()
local t=CLIPBOARD.get()
if t then
t=STRING.trim(t)
if #t==8 and t:match("[0-9]+") then

View File

@@ -7,12 +7,12 @@ function scene.enter()
end
local function _dumpCB(T)
love.system.setClipboardText(STRING.packText(TABLE.dump(T)))
CLIPBOARD.set(STRING.packText(TABLE.dump(T)))
MES.new('check',text.exportSuccess)
end
local function _parseCB()
local _
local s=love.system.getClipboardText()
local s=CLIPBOARD.get()
-- Decode
s=STRING.unpackText(s)

View File

@@ -91,7 +91,14 @@ scene.widgetList={
WIDGET.newSwitch{name='clean', x=950,y=1100,lim=360,disp=SETval('cleanCanvas'), code=function() SETTING.cleanCanvas=not SETTING.cleanCanvas; applySettings() end},
WIDGET.newSwitch{name='fullscreen', x=950,y=1150,lim=360,disp=SETval('fullscreen'), code=function() SETTING.fullscreen=not SETTING.fullscreen; applySettings() end,hideF=function() return MOBILE end},
WIDGET.newSwitch{name='portrait', x=950,y=1150,lim=360,disp=SETval('portrait'), code=function() SETTING.portrait=not SETTING.portrait; saveSettings(); MES.new('warn',text.settingWarn2,6.26) end,hideF=function() return not MOBILE end},
WIDGET.newSlider{name='msaa', x=950,y=1220,lim=360,w=200,axis={0,4,1},show=_msaaShow,disp=function() return SETTING.msaa==0 and 0 or math.log(SETTING.msaa,2) end,code=function(v) SETTING.msaa=v==0 and 0 or 2^v; saveSettings(); if TASK.lock('warnMessage',6.26) then MES.new('warn',text.settingWarn2,6.26) end end},
WIDGET.newSlider{name='msaa', x=950,y=1220,lim=360,w=200,axis={0,4,1},disp=SETval('msaa'),show=_msaaShow,
code=function(v)
SETTING.msaa=v
if TASK.lock('warnMessage',6.26) then
MES.new('warn',text.settingWarn2,6.26)
end
end
},
WIDGET.newKey{name='bg_on', x=680,y=1290,w=200,h=60,font=25,code=function() SETTING.bg='on' ; applySettings() end},
WIDGET.newKey{name='bg_off', x=900,y=1290,w=200,h=60,font=25,code=function() SETTING.bg='off'; applySettings() end},
@@ -136,7 +143,7 @@ scene.widgetList={
},
WIDGET.newKey{name='bg_custom_base64',x=1010,y=1502.5,w=420,h=135,align='M',
code=function()
local okay,data=pcall(love.data.decode,"data","base64",love.system.getClipboardText())
local okay,data=pcall(love.data.decode,"data","base64",CLIPBOARD.get())
if okay and pcall(gc.newImage,data) then
love.filesystem.write('conf/customBG',data)
SETTING.bg='custom'

View File

@@ -164,7 +164,7 @@ scene.widgetList={
textBox,
WIDGET.newButton {name='home',x=1140,y= 90,w=170,h=80,sound='click',font=60,fText=CHAR.key.macHome,code=pressKey('home')},
WIDGET.newButton {name='endd',x=1140,y=190,w=170,h=80,sound='click',font=60,fText=CHAR.key.macEnd ,code=pressKey('end')},
WIDGET.newButton {name='copy',x=1140,y=290,w=170,h=80,sound='click',font=60,fText=CHAR.icon.copy ,code=function()love.system.setClipboardText(table.concat(textBox.texts,'\n'))end,color='lC'},
WIDGET.newButton {name='copy',x=1140,y=290,w=170,h=80,sound='click',font=60,fText=CHAR.icon.copy ,code=function()CLIPBOARD.set(table.concat(textBox.texts,'\n'))end,color='lC'},
logList,

View File

@@ -12,7 +12,7 @@ function THEME.calculate(Y,M,D)
Y,M,D=os.date('%Y'),os.date('%m'),os.date('%d')
end
-- Festival calculate within one statement
return
if not SETTING.noTheme then return
-- Christmas
M=='12' and math.abs(D-25)<4 and
'xmas' or
@@ -51,58 +51,54 @@ function THEME.calculate(Y,M,D)
(M=='03' or M=='04' or M=='05' or M=='06') and 'zday1' or
(M=='07' or M=='08' or M=='09' or M=='10') and 'zday2' or
(M=='11' or M=='12' or M=='01' or M=='02') and 'zday3'
) or
-- Normal
(
(M=='02' or M=='03' or M=='04') and 'season1' or
(M=='05' or M=='06' or M=='07') and 'season2' or
(M=='08' or M=='09' or M=='10') and 'season3' or
(M=='11' or M=='12' or M=='01') and 'season4'
)
end
-- If there is theme and theme is enabled, then we will never reach here
return -- Normal
(
(M=='02' or M=='03' or M=='04') and 'season1' or
(M=='05' or M=='06' or M=='07') and 'season2' or
(M=='08' or M=='09' or M=='10') and 'season3' or
(M=='11' or M=='12' or M=='01') and 'season4'
)
end
function THEME.set(theme,keepBGM,force)
if type(theme)=='string' and theme:sub(1,6)=='season' then
---@param theme string
---@param keepBGM boolean|false|nil
function THEME.set(theme,keepBGM)
if type(theme)~='string' then
return
elseif theme:sub(1,6)=='season' then
BG.setDefault(SETTING.defaultBG)
BGM.setDefault(({season1='null',season2='nil',season3='vacuum',season4='space'})[theme])
elseif not SETTING.noTheme or force then
if theme=='xmas' then
BG.setDefault('snow')
BGM.setDefault('xmas')
MES.new('info',"==Merry Christmas==")
elseif theme=='birth' then
BG.setDefault('firework')
BGM.setDefault('magicblock')
elseif theme=='sprfes' then
BG.setDefault('firework')
BGM.setDefault('spring festival')
MES.new('info',"★☆新年快乐☆★")
elseif theme=='halloween' then
BG.setDefault('glow')
BGM.setDefault('antispace')
MES.new('info',">>Happy halloween<<")
elseif theme=='zday1' then
BG.setDefault('lanterns')
BGM.setDefault('overzero')
elseif theme=='zday2' then
BG.setDefault('lanterns')
BGM.setDefault('jazz nihilism')
elseif theme=='zday3' then
BG.setDefault('lanterns')
BGM.setDefault('empty')
elseif theme=='fool' then
BG.setDefault('blockrain')
BGM.setDefault('how feeling')
elseif theme=='edm' then
BG.setDefault('lightning2')
BGM.setDefault('malate')
MES.new('music'," 红 色 电 音\n 极 地 大 冲 击\n 只要你敢触电——\n 7月14日、15日 天地人间完全放电\n不用麻醉一样情不自禁HI起来飞起来")
else
return
end
elseif theme=='xmas' then
BG.setDefault('snow')
BGM.setDefault('xmas')
MES.new('info',"==Merry Christmas==")
elseif theme=='birth' then
BG.setDefault('firework')
BGM.setDefault('magicblock')
elseif theme=='sprfes' then
BG.setDefault('firework')
BGM.setDefault('spring festival')
MES.new('info',"★☆新年快乐☆★")
elseif theme=='halloween' then
BG.setDefault('glow')
BGM.setDefault('antispace')
MES.new('info',">>Happy halloween<<")
elseif theme:sub(1,4)=='zday' then
BG.setDefault('lanterns')
BGM.setDefault(({zday1='overzero',zday2='jazz nihilism',zday3='empty'})[theme])
elseif theme=='fool' then
BG.setDefault('blockrain')
BGM.setDefault('how feeling')
elseif theme=='edm' then
BG.setDefault('lightning2')
BGM.setDefault('malate')
MES.new('music'," 红 色 电 音\n 极 地 大 冲 击\n 只要你敢触电——\n 7月14日、15日 天地人间完全放电\n不用麻醉一样情不自禁HI起来飞起来")
else
return THEME.set(THEME.calculate('0',os.date('%m'),'0'),keepBGM,true)
return
end
THEME.cur=theme

View File

@@ -8,8 +8,15 @@
DiscordRPC支持
主菜单添加公告页面
尝试重启联网
改动:
删除三个凑数的策略堆叠模式
3D方块效果微调
调整所有方块皮肤的名称
一些内部系统重构尝试支持Web端
修复:
晃动特效开大了20g下颠到吐
hisPool序列算法错误(会影响部分模式录像)
开了“关闭节日主题”时可能导致启动就卡死
0.17.19: 暂停 Break