Compare commits
67 Commits
Vietnamese
...
improve_mo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46cbd1f1a4 | ||
|
|
2226aa2e6b | ||
|
|
12ae789239 | ||
|
|
2d7d02d5c9 | ||
|
|
64ddb09060 | ||
|
|
0203bfa805 | ||
|
|
c974db8c1c | ||
|
|
8559b5e565 | ||
|
|
201ec079d6 | ||
|
|
f6e7e993f5 | ||
|
|
ff6a99999b | ||
|
|
35aacc1a3e | ||
|
|
59903397a7 | ||
|
|
ca39c0ddf3 | ||
|
|
6e6e1034b4 | ||
|
|
01efc218bf | ||
|
|
b4b602ae9d | ||
|
|
bd395b2775 | ||
|
|
bf36bfb02b | ||
|
|
4bbf77b217 | ||
|
|
486e88e7bc | ||
|
|
46396efd09 | ||
|
|
57d42a84a4 | ||
|
|
eb7e20d157 | ||
|
|
725e8b35bd | ||
|
|
17a8181822 | ||
|
|
6c40fb098f | ||
|
|
785935d26f | ||
|
|
f2e064e900 | ||
|
|
2e537807a7 | ||
|
|
f4e04b32dd | ||
|
|
e1255e6124 | ||
|
|
37993c1669 | ||
|
|
2fa653fffe | ||
|
|
77fd519b43 | ||
|
|
017f874f38 | ||
|
|
c642bd3857 | ||
|
|
bc831220b4 | ||
|
|
b50147cace | ||
|
|
eb3b8c9e9d | ||
|
|
9ddab37232 | ||
|
|
cec7914c8f | ||
|
|
b19a83ac23 | ||
|
|
1905960b51 | ||
|
|
68a806aa3e | ||
|
|
b555c44b8b | ||
|
|
d0c503e096 | ||
|
|
9ec2182252 | ||
|
|
881d43c56d | ||
|
|
8a663e29a6 | ||
|
|
7ae19b6431 | ||
|
|
081b77f0e1 | ||
|
|
b4a9766d14 | ||
|
|
c33614a9cc | ||
|
|
ae37fde4b9 | ||
|
|
64a86b260a | ||
|
|
59cd9347c2 | ||
|
|
0281d28a72 | ||
|
|
e517e6c65b | ||
|
|
95283b6fc7 | ||
|
|
1ed2cd468e | ||
|
|
eaba1cd0ab | ||
|
|
421e74e357 | ||
|
|
6eba788236 | ||
|
|
d041461f0a | ||
|
|
13b277b35a | ||
|
|
fc703f241c |
32
.github/actions/get-cc/action.yml
vendored
32
.github/actions/get-cc/action.yml
vendored
@@ -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
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
112
.github/build/web/dev/index.html
vendored
Normal 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>
|
||||
288
.github/build/web/game.js
vendored
288
.github/build/web/game.js
vendored
@@ -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}]});
|
||||
|
||||
})();
|
||||
22
.github/build/web/love.js
vendored
22
.github/build/web/love.js
vendored
File diff suppressed because one or more lines are too long
BIN
.github/build/web/love.wasm
vendored
BIN
.github/build/web/love.wasm
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 305 KiB After Width: | Height: | Size: 305 KiB |
@@ -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');
|
||||
BIN
.github/build/web/theme/bg.png
vendored
BIN
.github/build/web/theme/bg.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 7.0 KiB |
49
.github/build/web/theme/love.css
vendored
49
.github/build/web/theme/love.css
vendored
@@ -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;
|
||||
}
|
||||
323
.github/workflows/main.yml
vendored
323
.github/workflows/main.yml
vendored
@@ -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
103
Zframework/clipboard.lua
Normal 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,
|
||||
}
|
||||
48
Zframework/clipboard_thread.lua
Normal file
48
Zframework/clipboard_thread.lua
Normal 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
|
||||
@@ -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{
|
||||
|
||||
@@ -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
142
Zframework/js.lua
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
13
conf.lua
13
conf.lua
@@ -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
|
||||
|
||||
83
main.lua
83
main.lua
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -29,12 +29,12 @@ return {
|
||||
"Techminoの公式ホームページです!\n最新の安定版Techminoをダウンロードしたり、プロフィールを編集したりできます\n地球儀ボタンから是非アクセスしてください",
|
||||
"http://studio26f.org",
|
||||
},
|
||||
{"灰机wiki",
|
||||
"huiji wiki ウィキ うぃき 灰机 フイジ",
|
||||
"help",
|
||||
"huiji wiki\n\n中国のテトリス研究グループとそのサブグループに所属しているテトリスプレーヤー達が管理している中国のテトリスwikiです\n現在、大部分のページがHard drop wikiとTetris 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",
|
||||
|
||||
@@ -42,7 +42,7 @@ Bạn muốn đóng góp vào bản dịch? Bạn có thể vào trang dự án
|
||||
05B. Hệ thống xoay gạch: ARS, ASC, ASC+, BRS, BiRS, C2RS, C2sym, NRS, SRS, SRS+, TRS, XRS
|
||||
05C. Hệ thống điều khiển: IRS, IHS, IMS
|
||||
05D. Cách kiểu xáo gạch: Túi 7 gạch, His, EZ-Start, Reverb, C2
|
||||
(và vấn đề Drought của một vài kiểu xáo)
|
||||
(và hiện tượng Drought của một vài kiểu xáo)
|
||||
|
||||
05E. Thông số
|
||||
05E1. Thông số của game:
|
||||
@@ -50,7 +50,7 @@ Bạn muốn đóng góp vào bản dịch? Bạn có thể vào trang dự án
|
||||
- ARE, Line ARE, Death ARE
|
||||
- Lockdown Delay, Spawn & Clear delay
|
||||
05E2. Thông số điều khiển:
|
||||
DAS & ARR, DAS cut, Auto-lock cut, SDF
|
||||
DAS & ARR, DAS cut, Auto-lock cut, IRS cut, SDF
|
||||
05F. Điều khiển
|
||||
05F1. Tốc độ: LPM, PPS, BPM, KPM, KPP
|
||||
05F2. Kỹ thuật: Hypertapping, Rolling, Finesse
|
||||
@@ -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
|
||||
@@ -272,7 +272,7 @@ Khái niệm về trò chơi Tetris hay trò chơi xếp gạch "hiện đại"
|
||||
Nói chung, một game xếp gạch hiện đại thường sẽ bám sát theo Tetris Design Guideline (Bộ nguyên tắc thiết kế cho một game Tetris). Game nào thỏa mãn đa số các tiêu chí dưới đây có thể được coi là game xếp gạch hiện đại.
|
||||
|
||||
1. Phần có thể nhìn thấy được của bảng có kích thước 10 cột × 20 hàng, cùng với 2 - 3 hàng ẩn ở trên cùng. (Kích thước bảng thực tế ở trong mã nguồn game thường cố định ở 10 cột × 40 hàng).
|
||||
2. Gạch mới xuất hiện ở giữa trên cùng của vùng có thể nhìn thấy (thường là ở hàng 21-22). Mỗi gạch đều có màu sắc và hướng xuất hiện mặc định riêng. Với những gạch có chiều dài lẻ có thể lệch sang trái hoặc phải 1 ô.
|
||||
2. Gạch mới xuất hiện ở giữa trên cùng của vùng có thể nhìn thấy (thường là ở hàng 21-22). Mỗi gạch đều có màu sắc và hướng xuất hiện mặc định riêng. Với những gạch có chiều rộng là số lẻ (Z, S, J, L, T) có thể lệch sang trái hoặc phải 1 ô khi xuất hiện.
|
||||
3. Có một bộ xáo gạch như 7-Bag hay His được thiết kế để giảm hoặc tránh tình trạng Flood hay Drought.
|
||||
4. Có một hệ thống xoay, và cho phép xoay theo ít nhất 2 hướng. Ưu tiên hệ thống xoay SRS hoặc các biến thể tương tự.
|
||||
5. Có hệ thống chờ khóa gạch thích hợp.
|
||||
@@ -286,25 +286,25 @@ Nói chung, một game xếp gạch hiện đại thường sẽ bám sát theo
|
||||
{"Next (Kế / Tiếp)",
|
||||
"nhom05 preview",
|
||||
"term",
|
||||
"Là một hàng dùng để hiện chuỗi gạch sẽ lần lượt xuất hiện. Có một kỹ năng cần thiết đó là lên kế hoạch trước cách đặt các gạch từ hàng NEXT. Số lượng gạch bạn muốn lên kế hoạch là tùy thuộc vào bạn và có thể thay đổi tùy theo chế độ chơi và tình trạng bảng chơi hiện tại của bạn.",
|
||||
"Là một hàng dùng để hiện chuỗi gạch sẽ lần lượt xuất hiện.\n\nCó một kỹ năng cần thiết đó là lên kế hoạch trước cách đặt các gạch từ hàng NEXT. Số lượng gạch bạn muốn lên kế hoạch là tùy thuộc vào bạn và có thể thay đổi tùy theo chế độ chơi và tình trạng bảng chơi hiện tại của bạn.",
|
||||
},
|
||||
{"Hold (Giữ/Trữ/Cất)",
|
||||
"nhom05",
|
||||
"term",
|
||||
"Một chức năng cho phép bạn sử dụng gạch ở trong ô HOLD\n(hoặc gạch đầu tiên ở hàng NEXT nếu bạn chưa cất gạch trước đó)\nvà cất gạch đang rơi vào ô HOLD \n\nBình thường, Hold chỉ có thể được sử dụng 1 lần cho mỗi gạch.\n\nTrên thực tế, việc dùng Hold hay không cũng có ưu nhược của nó.\nNếu không dùng Hold:\n\t- Có thể giảm áp lực cho người chơi khi điều khiển gạch.\n\t- Đồng thời có thể giảm số phím cần nhấn trong game → có thể tăng KPS lên.\nTrên thực tế, đã có nhiều kỷ lục 40L được xác lập mà không cần Hold.\n\nNếu dùng Hold:\n\t- Hold có thể có ích trong nhiều trường hợp khác nhau (ví dụ như khi đang chơi ở tốc độ rơi cao).\n\t- Cho phép người chơi có thể làm được nhiều setup phức tạp hơn mà không đẩy thêm áp lực cho người chơi."
|
||||
"Một chức năng cho phép bạn sử dụng gạch ở trong ô HOLD và cất gạch đang rơi vào ô HOLD (hoặc cất gạch hiện tại vào HOLD và sử dụng gạch ở ô đầu tiên trong hàng NEXT, nếu như chưa có gạch nào trong ô HOLD)\n\nBình thường, Hold chỉ có thể được sử dụng 1 lần cho mỗi gạch.\n\nTrên thực tế, việc dùng Hold hay không cũng có ưu nhược của nó.\nNếu không dùng Hold:\n\t- Có thể giảm áp lực cho người chơi khi điều khiển gạch.\n\t- Đồng thời có thể giảm số phím cần nhấn trong game → có thể tăng KPS lên.\nTrên thực tế, đã có nhiều kỷ lục 40L được xác lập mà không cần Hold.\n\nNếu dùng Hold:\n\t- Hold có thể có ích trong nhiều trường hợp khác nhau (ví dụ như khi đang chơi ở tốc độ rơi cao).\n\t- Cho phép người chơi có thể làm được nhiều setup phức tạp hơn mà không đẩy thêm áp lực cho người chơi."
|
||||
},
|
||||
{"Hold tại chỗ",
|
||||
"nhom05 physicalhold physics inplacehold",
|
||||
"term",
|
||||
"*Chỉ có trên Techmino*\n\"Giữ ngay tại chỗ\".\n\nMột kiểu Hold đặc biệt cho phép gạch được lấy ra từ HOLD sẽ xuất hiện ngay tại vị trí mà gạch hiện tại đang rơi (khác với Hold thông thường khi mà gạch sẽ xuất hiện ở trên cùng của bảng).\nBạn có thể bật chức năng này trong Chế độ tự do.\n\nFun fact: người Trung gọi cái này là \"Physical Hold\"",
|
||||
"*Chỉ có trên Techmino*\n\"Giữ ngay tại chỗ\".\n\nMột kiểu Hold đặc biệt cho phép gạch được lấy ra từ ô HOLD sẽ xuất hiện ngay tại vị trí mà gạch hiện tại đang rơi (khác với Hold thông thường khi mà gạch sẽ xuất hiện ở trên cùng của bảng).\nBạn có thể bật chức năng này trong Chế độ tự do.\n\nFun fact: người Trung gọi cái này là \"Physical Hold\"",
|
||||
},
|
||||
{"Swap (Chuyển)",
|
||||
"nhom05 hold",
|
||||
"term",
|
||||
"Một biến thể khác của \"Hold\". Swap sẽ đổi gạch đang rơi với gạch tiếp theo trong NEXT. Bạn có thể bật chức năng này trong Chế độ tự do.",
|
||||
},
|
||||
{"Topping out",
|
||||
"nhom05 topout toppingout game over",
|
||||
{"Game over",
|
||||
"nhom05 topout toppingout",
|
||||
"term",
|
||||
[[
|
||||
Một tựa game xếp gạch hiện đại thường có 3 điều kiện để "game over":
|
||||
@@ -319,7 +319,7 @@ Techmino mặc định sẽ không kiểm tra điều kiện Lock out và Top ou
|
||||
{"Vùng đệm",
|
||||
"nhom05 invisible buffer zone",
|
||||
"term",
|
||||
"Tên tiếng Anh là \"Buffer Zone\". Chỉ bao gồm các hàng từ hàng 21-40 (nằm ở phía trên vùng nhìn thấy).\n\nTồn tại vùng này là vì sẽ có trường hợp hàng rác sẽ đẩy gạch trong bảng ra khỏi vùng nhìn thấy (dễ thấy nhất là Center 4-Wide).\nNhững ô gạch nào đi ra khỏi vùng nhìn thấy được sẽ đi vào vùng đệm và sẽ xuất hiện lại trong vùng nhìn thấy nếu bạn đã xóa đủ hàng.\n\nVùng đệm thường cao 20 ô (thường là do bảng đã bị cố định kích thước ở trong các dòng code), nhưng có game có vùng này cao vô hạn (ví dụ như trong chính Techmino luôn, khi bảng có thể mở rộng kích thước của nó).\n\nCác bạn có thể tìm hiểu thêm ở mục \"Vùng biến mất\".",
|
||||
"Tên tiếng Anh: \"Buffer Zone\". Chỉ bao gồm các hàng từ hàng 21-40 (nằm ở phía trên vùng nhìn thấy).\n\nTồn tại vùng này là vì sẽ có trường hợp hàng rác sẽ đẩy các hàng bên trên trong bảng ra khỏi vùng nhìn thấy (dễ thấy nhất khi dùng Center 4-Wide).\nNhững ô gạch nào đi ra khỏi vùng nhìn thấy được sẽ đi vào vùng đệm và sẽ xuất hiện lại trong vùng nhìn thấy nếu bạn đã xóa đủ hàng.\n\nVùng đệm thường cao 20 ô (thường là do bảng đã bị cố định kích thước ở trong các dòng code), nhưng có game có vùng này cao vô hạn (ví dụ như trong chính Techmino luôn, khi bảng có thể tự mở rộng kích thước của nó).\n\nCác bạn có thể tìm hiểu thêm ở mục \"Vùng biến mất\".",
|
||||
},
|
||||
{"Vùng biến mất",
|
||||
"nhom05 gone vanish zone",
|
||||
@@ -338,7 +338,7 @@ Tuy nhiên, mỗi game sẽ có cách xử lý khác nhau. Ví dụ:
|
||||
{">A|Gạch",
|
||||
"nhom05a",
|
||||
"",
|
||||
"Bạn có biết?\nGame này hỗ trợ và cho phép bạn chơi với 29 loại gạch khác nhau\n\n1 Mino | 1 Domino | 2 Trimino | 7 Tetromino | 18 Pentomino\n\nMino: gạch 1 ô\nDomino: gạch 2 ô\nTrimino: gạch 3 ô\nTetromino: gạch 4 ô\nPentomino: gạch 5 ô\n\nTechmino có Hexomino (gạch 5 ô) không?\nBây giờ thì chưa nhưng tương lai thì có thể có.",
|
||||
"Bạn có biết? Techmino hỗ trợ và cho phép bạn chơi với 29 loại gạch khác nhau, bao gồm: 1 Mino, 1 Domino, 2 Trimino, 7 Tetromino và 18 Pentomino\n\nMino: gạch 1 ô\nDomino: gạch 2 ô\nTrimino: gạch 3 ô\nTetromino: gạch 4 ô\nPentomino: gạch 5 ô\n\nTechmino có Hexomino (gạch 5 ô) không?\nBây giờ thì chưa nhưng tương lai thì có thể có.",
|
||||
},
|
||||
{"Hình dạng",
|
||||
"nhom05a hình dáng"..tetromino,
|
||||
@@ -450,7 +450,7 @@ So với XRS, BiRS dễ nhớ hơn vì chỉ dùng một bảng wall-kick; nhưn
|
||||
{"SRS",
|
||||
"nhom05b superrotationsystem",
|
||||
"term",
|
||||
"Super Rotation System | Hệ thống xoay Siêu Cấp\n\nHệ thống xoay này được sử dụng rất nhiều trong các game xếp gạch hiện đại và có rất nhiều hệ thống xoay do fan làm ra cũng dựa vào hệ thống này.\nCó tất cả 8 bảng wall-kick trong SRS, tương ứng với hai hướng xoay cho tất cả bốn tư thế của tất cả các gạch (không có trường hợp cho 180°). Nếu gạch đụng tường, đụng đáy, hay đè lên gạch khác sau khi xoay, hệ thống sẽ kiểm tra các vị trí xung quanh. Bạn có thể xem đầy đủ các bảng wall-kick của SRS trên Tetris Wiki.",
|
||||
"Super Rotation System | Hệ thống xoay Siêu Cấp\n\nHệ thống xoay này được sử dụng rất nhiều trong các game xếp gạch hiện đại và có rất nhiều hệ thống xoay do fan làm ra cũng dựa vào hệ thống này.\nCó tất cả 8 bảng wall-kick trong SRS, tương ứng với hai hướng xoay cho tất cả bốn tư thế của tất cả các gạch (không có trường hợp cho 180°). Nếu gạch đụng tường, đụng đáy, hay đè lên gạch khác sau khi xoay, hệ thống sẽ kiểm tra các vị trí xung quanh.\n\nBạn có thể xem đầy đủ các bảng wall-kick của SRS trên Tetris Wiki.",
|
||||
},
|
||||
{"SRS+",
|
||||
"nhom05b srsplus superrotationsystemplus",
|
||||
@@ -607,12 +607,12 @@ Xem mục tiếp theo để biết thêm.
|
||||
{"20G",
|
||||
"nhom05e1 trọng lực; ngay lập tức; gravity instantly",
|
||||
"term",
|
||||
"Tốc độ tối đa trong các game xếp gạch hiện đại.\n\nMặc dù nhìn qua thuật ngữ này thể hiện tốc độ rơi là 20 hàng / khung hình, nhưng thật ra chúng được dùng để chỉ tốc độ vô tận.\n\nHơn nữa, trong các chế độ 20G, game sẽ ưu tiên di chuyển gạch xuống đáy hơn là bất cứ thao tác di chuyển nào từ người chơi.\nLấy ví dụ: ngay cả khi ARR được đặt là 0, gạch vẫn cứ di chuyển một mạch xuống phía dưới một cách hồn nhiên giống như người chơi chưa nhấn gì.\nViệc này gây khó cho người chơi khi họ muốn gạch leo ra khỏi hố hoặc nhảy ra khỏi lỗ trong một số tình huống.",
|
||||
"Tốc độ tối đa trong các game xếp gạch hiện đại.\n\nMặc dù nhìn qua thuật ngữ này thể hiện tốc độ rơi là 20 hàng / khung hình, nhưng thật ra chúng được dùng để chỉ tốc độ vô tận.\n\nHơn nữa, trong các chế độ 20G, game sẽ ưu tiên di chuyển gạch xuống đáy hơn là bất cứ thao tác di chuyển nào từ người chơi.\nVà ngay cả khi ARR được đặt là 0, gạch vẫn cứ di chuyển một mạch xuống phía dưới một cách hồn nhiên giống như người chơi chưa nhấn gì, trước khi di chuyển theo thao tác của người chơi.\n\nViệc này gây khó khăn cho người chơi khi họ muốn gạch leo ra khỏi hố hoặc nhảy ra khỏi lỗ trong một số tình huống.",
|
||||
},
|
||||
{"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.\nĐây là khoảng thời gian ngay sau khi gạch chạm đất và trước khi gạch bị khóa (không thể điều khiển được nữa).\n\nTrong các game xếp gạch cổ điển, khoảng thời gian chờ này = 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 thong thả hơn, và trong game thường có cơ chế trì hoãn việc khóa gạch, trong đó bạn có thể di chuyển hoặc xoay gạch để đặ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",
|
||||
@@ -622,7 +622,7 @@ Xem mục tiếp theo để biết thêm.
|
||||
{"ARE",
|
||||
"nhom05e1 spawn appearance delay",
|
||||
"term",
|
||||
"Thời gian chờ xuất hiện gạch mới\nHay còn được biết với tên: Appearance Delay và Entry Delay.\n\n\"ARE\" chỉ khoảng thời gian sau khi gạch bị khóa và trước khi gạch mới xuất hiện\n\nP/s: Từ \"ARE\" không phải là từ viết tắt hay hay là một dạng của \"be\" trong tiếng Anh; nó bắt nguồn từ <あれ> (a-re) trong tiếng Nhật, có nghĩa là \"nó\" hoặc \"cái đó\" / \"cái kia\" / \"cái ấy\".",
|
||||
"Thời gian chờ xuất hiện gạch mới\nHay còn được biết với tên: Appearance Delay và Entry Delay.\n\n\"ARE\" chỉ khoảng thời gian sau khi gạch bị khóa và trước khi gạch mới xuất hiện\n\nP/s: Từ \"ARE\" không phải là từ viết tắt hay hay là một dạng của động từ \"be\" trong tiếng Anh; nó bắt nguồn từ <あれ> (a-re) trong tiếng Nhật, có nghĩa là \"nó\" hoặc \"cái đó\" / \"cái kia\" / \"cái ấy\".",
|
||||
},
|
||||
{"Line ARE",
|
||||
"nhom05e1 appearance delay",
|
||||
@@ -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 một khoảng thời gian bổ sung để 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 và 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",
|
||||
@@ -647,18 +647,18 @@ Xem mục tiếp theo để biết thêm.
|
||||
{"DAS & ARR",
|
||||
"nhom05e2 das và arr delayedautoshift autorepeatrate",
|
||||
"term",
|
||||
"DAS, hay Delayed Auto-shift, chỉ khoảng thời gian sau khi gạch di chuyển sang một hướng đã chọn 1 ô cho đến truớc khi gạch di chuển một cách tự động.\n\nARR, hay Auto-Repeat Rate, chỉ khoảng cách thời gian giữ 2 lần di chuyển sang 1 ô trong lúc gạch đang tự động di chuyển.\n\nDAS và ARR được tính bằng f (khung hình) (¹/₆₀ ở 60FPS). 1ms = 16²/₃ khung hình.",
|
||||
"DAS, hay Delayed Auto-shift, đây là khoảng thời gian ngay sau khi gạch đã di chuyển sang 1 ô và gạch đang đợi thời điểm gạch có thể bắt đầu quá trình di chuyển tự động.\n\nARR, hay Auto-Repeat Rate, chỉ khoảng cách thời gian giữa 2 lần di chuyển tự động 1 ô khi gạch đang trong quá trình di chuyển tự động.\n\nDAS và ARR được tính bằng f (khung hình) (¹/₆₀ ở 60FPS). 1ms = 16²/₃ khung hình.",
|
||||
},
|
||||
{"DAS cut",
|
||||
"nhom05e2 dascut dcd",
|
||||
"term",
|
||||
"Cơ chế đặc biệt sẽ được kích hoạt khi gạch mới xuất hiện. Khi kích hoạt, cơ chế này sẽ tăng DAS lên một chút để gạch không tự di chuyển ngay khi đang có phím được giữ.\n\nCác game khác có thể có tính năng tương tự nhưng cách hoạt động có thể khác nhau.",
|
||||
"Là cơ chế đặc biệt sẽ được kích hoạt khi gạch mới xuất hiện. Khi kích hoạt, cơ chế này sẽ tăng DAS lên một chút để gạch không tự di chuyển ngay khi đang có phím được giữ.\n\nCác game khác có thể có tính năng tương tự nhưng cách hoạt động có thể khác nhau.",
|
||||
},
|
||||
{"IRS cut",
|
||||
"irscut icd",
|
||||
"term",
|
||||
"Là cơ chế đặc biệt sẽ được kích hoạt khi gạch mới xuất hiện nhưng không có entry delay. Khi kích hoạt, game sẽ hoãn việc kích IRS một lúc để bạn có thời gian nhả phím xoay sau khi thả gạch xong.",
|
||||
},
|
||||
-- {"IRS cut",
|
||||
-- "irscut icd",
|
||||
-- "term",
|
||||
-- "A special delay applied to IRS when a new block is spawned. When entry delay is disabled, this will delay IRS from being applied, allowing you to release the rotation button in the period to avoid a misdrop.",
|
||||
-- },
|
||||
{"Auto-lock cut",
|
||||
"nhom05e2 autolockcut",
|
||||
"term",
|
||||
@@ -702,7 +702,7 @@ Xem mục tiếp theo để biết thêm.
|
||||
{"KPP",
|
||||
"nhom05f1 số lần nhấn mỗi gạch; số phím mỗi gạch",
|
||||
"term",
|
||||
"Keypresses per piece | Số lần nhấn mỗi viên gạch\nPhản ánh mức độ hiệu quả việc điều khiển gạch.\nCó thể giảm con số này bằng cách học Finesse",
|
||||
"Keypresses per piece | Số lần nhấn mỗi viên gạch\nPhản ánh mức độ hiệu quả việc điều khiển gạch.\n\nCó thể giảm con số này bằng cách học Finesse",
|
||||
},
|
||||
{">F2|K.th. đ.khiển",
|
||||
"nhom05f2",
|
||||
@@ -713,9 +713,9 @@ Xem mục tiếp theo để biết thêm.
|
||||
"nhom05f2 finesse lỗi di chuyển",
|
||||
"term",
|
||||
[[
|
||||
Một kỹ thuật di chuyển gạch vào vị trí mong muốn với chuỗi phím ngắn nhất có thể, giúp tiết kiệm thời gian và giảm khả năng misdrop.
|
||||
Là kỹ thuật điều khiển gạch nhanh nhất với chuỗi phím ngắn nhất có thể nhưng vẫn đảm bảo chính xác, giúp tiết kiệm thời gian và giảm khả năng misdrop.
|
||||
|
||||
Đây là một kỹ năng quan trọng nên bạn hãy học Finesse sớm nhất có thể. Bạn có thể thấy khá nhiều video hướng dẫn trên Youtube cũng như các trang hướng dẫn với hình minh họa trên Google. Hãy bắt đầu từ thứ cơ bản nhất, rồi luyện tập dần để tăng độ chính xác lên. Hãy nhớ ưu tiên chính xác hơn là tốc độ nhé.
|
||||
Đây là một kỹ năng quan trọng nên bạn hãy học Finesse sớm nhất có thể. Bạn có thể thấy nhiều video hướng dẫn trên Youtube cũng như các trang hướng dẫn với hình minh họa trên Google. Hãy bắt đầu từ thứ cơ bản nhất, rồi luyện tập dần để tăng độ chính xác lên. Hãy nhớ ưu tiên chính xác hơn là tốc độ nhé.
|
||||
|
||||
Bạn sẽ không bị mất Finesse khi bạn nhét gạch hay thực hiện Spin vì Techmino chỉ kiểm tra những vị trí không yêu cầu soft drop
|
||||
|
||||
@@ -734,7 +734,7 @@ Lưu ý:
|
||||
{"Hypertapping",
|
||||
"nhom05f2 hypertapper nhấn liên tục",
|
||||
"term",
|
||||
"Hypertapping (Nhấn liên tục)\n\nĐề cập tới một kỹ năng là khi bạn rung tay liên tục thay vì giữ phím.\n\nTrong các game xếp gạch cổ điển, thông số DAS rất cao và không thể điều chỉnh được, dẫn tới nhấn nút liên tục sẽ nhanh hơn so với giữ phím.\nBây giờ thì không cần vì các game xếp gạch hiện đại đã có DAS và ARR có thể điều chỉnh được (nếu có chăng không điều chỉnh được thì DAS cũng đã thấp hơn nhiều so với ngày trước)\n\nNhững người dùng kỹ năng này được gọi là \"hypertapper\"",
|
||||
"Hypertapping (Nhấn liên tục)\n\nĐề cập tới một kỹ năng là khi bạn rung tay liên tục thay vì giữ phím.\n\nTrong các game xếp gạch cổ điển, thông số DAS rất cao và không thể điều chỉnh được, dẫn tới nhấn nút liên tục sẽ nhanh hơn so với giữ phím.\nBây giờ thì không cần vì các game xếp gạch hiện đại đã có DAS và ARR có thể điều chỉnh được (nếu có chăng không điều chỉnh được thì DAS cũng đã thấp hơn nhiều so với ngày trước).\n\nNhững người dùng kỹ năng này được gọi là \"hypertapper\"",
|
||||
},
|
||||
{"Rolling",
|
||||
"nhom05f2",
|
||||
@@ -816,7 +816,7 @@ Trong hầu hết các game, tấn công và phòng thủ là tương đương n
|
||||
{"'Debt'",
|
||||
"nhom05g debt owe",
|
||||
"term",
|
||||
"Một thuật ngữ hay được sử dụng trong cộng đồng Tetris Trung Quốc.\n\n\"Debt\" đề cập đến tình huống mà bạn chỉ có thể tấn công KHI và CHỈ KHI setup được hoàn thành. Nên, khi đang làm một hoặc nhiều debt liên tiếp, người chơi bắt buộc phải để ý tới đối thủ để đảm bảo an toàn; còn không, bạn có thể bị bón hành sấp mặt.\n\nThuật ngữ này hay được sử dụng để diễn tả một số setup như TST tower.",
|
||||
"Một thuật ngữ hay được sử dụng trong cộng đồng Tetris Trung Quốc.\n\n\"Debt\" đề cập đến tình huống mà bạn chỉ có thể tấn công KHI và CHỈ KHI setup được hoàn thành. Nên, khi đang làm một hoặc nhiều debt liên tiếp, người chơi bắt buộc phải để ý tới đối thủ để đảm bảo an toàn; còn không, người chơi đó có thể bị bón hành sấp mặt.\n\nThuật ngữ này hay được sử dụng để diễn tả một số setup như TST tower.",
|
||||
},
|
||||
{"Passthrough",
|
||||
"nhom05g pingthrough",
|
||||
@@ -831,17 +831,17 @@ Trong hầu hết các game, tấn công và phòng thủ là tương đương n
|
||||
{">H|Mis-action",
|
||||
"nhom05h misaction misdrop mishold",
|
||||
"",
|
||||
"Misdrop: Vô tình thả rơi / đặt gạch vào nơi không mong muốn.\nMishold: Vô tình nhấn nhầm phím Hold. Việc này có thể dẫn đến việc dùng một viên gạch không mong muốn.\n\nCả misdrop và mishold có thể làm bạn mất cơ hội để làm PC"
|
||||
"Misdrop: Vô tình thả rơi / đặt gạch vào nơi không mong muốn.\nMishold: Vô tình nhấn nhầm phím Hold. Việc này có thể dẫn đến việc dùng một viên gạch không mong muốn.\n\nCả misdrop và mishold có thể làm bạn mất cơ hội để làm PC."
|
||||
},
|
||||
{">I|Spin",
|
||||
"nhom05i",
|
||||
"",
|
||||
"(Ở trong một số game)\n\nXoay gạch để di chuyển tới một vị trí mà bình thường sẽ không tiếp cận được. Ở một số game, thao tác này sẽ gửi thêm hàng rác hoặc là tăng thêm điểm. Mỗi game sẽ có cách kiểm tra Spin khác nhau."
|
||||
"(Ở nhiều game, đa số các game cổ điển không có cái này)\n\nXoay gạch để di chuyển tới một vị trí mà bình thường sẽ không tiếp cận được. Ở một số game, thao tác này sẽ gửi thêm hàng rác hoặc là tăng thêm điểm. Mỗi game sẽ có cách kiểm tra Spin khác nhau."
|
||||
},
|
||||
{"Mini",
|
||||
"nhom05i",
|
||||
"term",
|
||||
"Một kiểu spin (được cho là) dễ làm hơn so với spin thông thường (vì trong một số game cũ, chúng được gọi là \"Ez T-spin\").\nLượng điểm bổ sung và hàng rác đều ít hơn so với spin thông thường.\n\nMỗi game sẽ có các quy tắc khác nhau để kiểm tra và chúng có thể không trực quan.\nNhưng bạn chỉ cần nhớ mấy cái bố cục làm Mini-spin là được!",
|
||||
"Một kiểu spin (được cho là) dễ làm hơn so với spin thông thường (vì trong một số game cũ, chúng được gọi là \"Ez T-spin\").\nLượng điểm bổ sung và hàng rác đều ít hơn so với spin thông thường.\n\nMỗi game sẽ có các quy tắc khác nhau để kiểm tra và chúng có thể không trực quan.\nNhưng bạn chỉ cần nhớ mấy cách làm Mini-spin là được!",
|
||||
},
|
||||
{"All-spin",
|
||||
"nhom05i allspin",
|
||||
@@ -851,12 +851,12 @@ Trong hầu hết các game, tấn công và phòng thủ là tương đương n
|
||||
{"T-spin",
|
||||
"nhom05i tspin",
|
||||
"term",
|
||||
"Spin được thực hiện bởi Tetromino T.\n\nT-spin chủ yếu được phát hiện bởi \"quy luật 3 góc\".\nTức là, nếu 3 trong 4 góc của một hình chữ nhật (có tâm là tâm xoay của gạch T) bị đè bởi bất kỳ gạch nào, thì spin đó được tính là T-spin.\n\nNgoài quy tắc đó ra thì còn có một số quy tắc để phát hiện T-spin và phân biệt giữa T-spin và Mini T-spin.",
|
||||
"Spin được thực hiện bởi Tetromino T.\n\nT-spin chủ yếu được phát hiện bởi \"quy luật 3 góc\": nếu 3 trong 4 góc của hình vuông ngoại tiếp gạch T bị đè bởi bất kỳ gạch nào, thì spin đó được tính là T-spin.\n\nNgoài quy tắc đó ra thì còn có một số quy tắc để phát hiện T-spin và phân biệt giữa T-spin và Mini T-spin.",
|
||||
},
|
||||
{"O-Spin",
|
||||
"nhom05i ospin",
|
||||
"term",
|
||||
"Gạch O vốn dĩ \"tròn\", không đổi hình dạng khi xoay ở bất cứ hướng nào, nên nó không thể \"đá\" được. Do đó gạch O không tài nào leo ra khỏi \"lỗ\" hoặc \"hố\" nếu bị kẹt. Từ việc này, có một người đã làm một cái video fake cách làm O-spin trong Tetris 99 và Tetris Friends\n\nHiện tại có 2 hệ thống xoay hỗ trợ O-spin:\n\tXRS cho phép gạch O có thể \"teleport\" tới một cái lỗ.\n\tTRS cho phép gạch O \"teleport\" và \"biến hình\"",
|
||||
"Gạch O vốn dĩ \"tròn\", không đổi hình dạng khi xoay ở bất cứ hướng nào, nên nó không thể \"đá\" được. Do đó gạch O không tài nào leo ra khỏi \"lỗ\" hoặc \"hố\" nếu bị kẹt. Lợi dụng việc này, có người đã làm video fake cách làm O-spin trong Tetris 99 và Tetris Friends\n\nHiện tại có 2 hệ thống xoay hỗ trợ O-spin:\n\tXRS cho phép gạch O có thể \"teleport\" tới một cái lỗ.\n\tTRS cho phép gạch O \"teleport\" và \"biến hình\"",
|
||||
},
|
||||
{"Fin, Neo, Iso",
|
||||
"nhom05i fin neo iso",
|
||||
@@ -908,7 +908,7 @@ Trong hầu hết các game, tấn công và phòng thủ là tương đương n
|
||||
{"Back to Back",
|
||||
"nhom05j b2b btb backtoback",
|
||||
"term",
|
||||
"Hay còn gọi là B2B. Xóa 2 hoặc nhiều lần xóa theo kiểu nâng cao (như Tetris hay Spin) liên tiếp (nhưng không được kiểu xóa bình thường giữa chừng).\nKhông như combo, Back To Back sẽ không bị mất khi đặt gạch.\n\nỞ Techmino, B2B được tính bằng thanh năng lượng, chứ không tính theo số lần xóa kiểu đặc biệt.\nCũng trong Techmino, nhiều B2B liên tiếp được tính là Back-to-back-to-back (B3B) (xem mục B2B2B để biết thêm).\n\nTechmino cũng tính cả PC và HPC liên tiếp là B2B và B3B",
|
||||
"Hay còn gọi là B2B. Xóa 2 hoặc nhiều lần xóa theo kiểu nâng cao (như Tetris hay Spin) liên tiếp (nhưng không được xóa kiểu bình thường giữa chừng).\nKhông như combo, Back To Back sẽ không bị mất khi đặt gạch.\n\nỞ Techmino, B2B được tính bằng thanh năng lượng, chứ không tính theo số lần xóa kiểu đặc biệt.\nCũng trong Techmino, nhiều B2B liên tiếp được tính là Back-to-back-to-back (B3B) (xem mục B2B2B để biết thêm).\n\nTechmino cũng tính cả PC và HPC liên tiếp là B2B và B3B",
|
||||
},
|
||||
{"B2B2B",
|
||||
"nhom05j b3b backtobacktoback",
|
||||
@@ -918,7 +918,7 @@ Trong hầu hết các game, tấn công và phòng thủ là tương đương n
|
||||
{"All Clear",
|
||||
"nhom05j pc perfectclear ac allclear",
|
||||
"term",
|
||||
"Còn được biết tới là Perfect Clear (PC). Đây là thuật ngữ được dùng nhiều trong cộng đồng và cũng như được dùng trong Techmino\nXóa toàn bộ gạch ra khỏi bảng, không trừ gạch nào\n\n[Sea: còn có một từ ít dùng nữa, đó là \"Bravo\"]",
|
||||
"Còn được biết tới là Perfect Clear (PC). Đây là thuật ngữ được dùng nhiều trong cộng đồng và cũng như được dùng trong Techmino\nXóa toàn bộ gạch ra khỏi bảng, không trừ gạch nào\n\n[Sea: còn có một từ cổ cho thuật ngữ này nữa, đó là \"Bravo\"]",
|
||||
},
|
||||
{"HPC",
|
||||
"nhom05j hc halfperfectclear",
|
||||
@@ -952,7 +952,7 @@ Trông nó nhìn rất giống cục xương, nên đôi khi được gọi là
|
||||
Trong Techmino, bone block được mô tả là "một skin gạch duy nhất, lạ mắt mà tất cả các gạch đều sử dụng".
|
||||
Skin khác nhau sẽ có skin bone block khác nhau.
|
||||
|
||||
Cũng trong Techmino nhưng ở tiếng Việt, từ "gạch ]]..CHAR.icon.bone..[[" được dùng để chỉ bone block.
|
||||
Cũng trong Techmino (khi để ngôn ngữ là tiếng Việt), từ "gạch ]]..CHAR.icon.bone..[[" được dùng để chỉ bone block.
|
||||
]],
|
||||
},
|
||||
{"=[NHÓM 06]=",
|
||||
@@ -1429,19 +1429,19 @@ NHÓM 07: MỘT VÀI CƠ CHẾ VÀ CHẾ ĐỘ CỦA MỘT SỐ GAME
|
||||
{"=[NHÓM 09]=",
|
||||
"nhom09",
|
||||
"",
|
||||
"NHÓM 09: WIKI; CÁC TRANG WEB BÀY SETUP,\nCUNG CẤP CÂU ĐỐ & CHIA SẺ SETUP"
|
||||
"NHÓM 09: WIKI; CÁC TRANG WEB BÀY SETUP, CUNG CẤP CÂU ĐỐ & CHIA SẺ SETUP"
|
||||
},
|
||||
{">A|Wiki",
|
||||
"nhom09a",
|
||||
"",
|
||||
""
|
||||
},
|
||||
{"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 và 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",
|
||||
@@ -1451,7 +1451,7 @@ NHÓM 07: MỘT VÀI CƠ CHẾ VÀ CHẾ ĐỘ CỦA MỘT SỐ GAME
|
||||
{"Tetris.wiki",
|
||||
"nhom09a tetris wiki",
|
||||
"help",
|
||||
"Một wiki tập trung vào các nội dung liên quan đến Tetris. Wiki được tạo ra từ năm 2015 bởi Myndzi. Trong những năm qua, hàng nghìn đóng góp đã được thực hiện để ghi lại các game xếp gạch chính thức và các game do fan làm, các series, những cơ chế của game,… cũng như tạo ra những bài hướng dẫn để cải thiện trải nghiệm chơi.",
|
||||
"Một wiki tập trung vào các nội dung liên quan đến Tetris. Wiki được tạo ra từ năm 2015 bởi Myndzi, hiện đang được quản lý bởi Simon.lc. Trong những năm qua, hàng nghìn đóng góp đã được thực hiện để ghi lại các game xếp gạch chính thức và các game do fan làm, các series, những cơ chế của game,… cũng như tạo ra những bài hướng dẫn để cải thiện trải nghiệm chơi.",
|
||||
"https://tetris.wiki",
|
||||
},
|
||||
{"Tetris Wiki Fandom",
|
||||
@@ -1701,7 +1701,7 @@ Opener phải đạt 2 trong 4 tiêu chí sau
|
||||
{"BTPC",
|
||||
"nhom12a opener btcannon betacannon",
|
||||
"setup",
|
||||
"Phần tiếp theo của DT Cannon kết thúc bằng All Clear.\nĐể có thêm thông tin, bạn có thể nhấn nút hình địa cầu để mở bài ở trên wiki Hard Drop",
|
||||
"Phần tiếp theo của BT Cannon kết thúc bằng All Clear.\nĐể có thêm thông tin, bạn có thể nhấn nút hình địa cầu để mở bài ở trên wiki Hard Drop",
|
||||
"https://harddrop.com/wiki?search=bt_cannon",
|
||||
},
|
||||
{"TKI 3 Perfect Clear",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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", "上級者用"},
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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"},
|
||||
@@ -1125,7 +1122,7 @@ C. Tay cầm chơi game (Gamepad):
|
||||
-- Techmino's birthday
|
||||
"Ngày sinh nhật của Techmino? Hiện tại (đang giả định) là 26/T6.",
|
||||
-- How to O-spin: Rotate 626 times in one second (mistaken)
|
||||
"Cách O-spin? Nhấn phím xoay 626 lần trong 1 giây (ĐÙA ĐẤY ĐỪNG TIN!)",
|
||||
"Cách làm O-spin? Nhấn phím xoay 626 lần trong 1 giây (ĐÙA ĐẤY ĐỪNG TIN!)",
|
||||
-- 2021 was the year of Techmino's online debut.
|
||||
"2021 là năm ra mắt chế độ trực tuyến của Techmino.",
|
||||
-- The Chinese name of this game is 'Block Research Institute'.
|
||||
@@ -1133,15 +1130,15 @@ C. Tay cầm chơi game (Gamepad):
|
||||
-- This game is not called Teachmino
|
||||
"Tên game không phải là Teachmino!",
|
||||
--
|
||||
"Muốn game có thứ gì đó đặc biệt lúc mở game? Hãy chỉnh đồng hồ trên điện thoại vào một ngày đặc biệt nào đó đi!",
|
||||
"Hãy thử chỉnh đồng hồ trên điện thoại để xem xem, có thứ gì đó đặc biệt hay không?",
|
||||
-- O-spin is a lie!
|
||||
{C.Y,"O-spin",C.Z," is a ",C.R,"lie",C.Z,"!"},
|
||||
{C.Y,"O-spin",C.Z," là ",C.R,"lời nói dối",C.Z,"của em!"},
|
||||
-- techminohaowan
|
||||
"Hảo Techmino",
|
||||
--
|
||||
-- TIPS WHEN PLAYING
|
||||
-- Don't act weak! Don't act weak! Don't act weak!
|
||||
"Đừng tỏ ra yếu đuối! Đừng tỏ ra yếu đuối! ĐỪNG TỎ RA YẾU ĐUỐI!",
|
||||
"Đừng tỏ ra yếu đuối! Và KHÔNG BAO GIỜ tỏ ra yếu đuối!",
|
||||
-- Warning: No pretending to be weak.
|
||||
{C.R,"CẢNH BÁO! ",C.Z,"Đừng giả vờ yếu đuối"},
|
||||
-- "Meow!"
|
||||
@@ -1152,7 +1149,7 @@ C. Tay cầm chơi game (Gamepad):
|
||||
-- Don't play with your phone if your homework isn't finished.
|
||||
"Đừng chơi điện thoại khi bài tập về nhà còn chưa hoàn thành.",
|
||||
-- Enabling vibration on some mobile systems may cause severe lag."
|
||||
"Bật rung trên một số điện thoại có thể làm cho chúng… phải thở oxy!",
|
||||
"Bật rung có thể làm một số điện thoại phải thở oxy!",
|
||||
-- Eat the button? Really? I suggest you play it back to see if you pressed it and how long it took you to press it"
|
||||
"Phím không ăn? Hãy thử kiểm tra lại phím đi!",
|
||||
-- Probably someone will read the tip
|
||||
@@ -1160,31 +1157,43 @@ C. Tay cầm chơi game (Gamepad):
|
||||
-- It seems like no one has reached a high level by playing with their feet yet.
|
||||
"Hình như tới giờ chưa ai chơi xếp gạch giỏi bằng chân…",
|
||||
-- Moderate gaming is good for the brain. Addiction to games is harmful. Plan your time
|
||||
"Chơi game vừa phải có thể tốt cho bộ não. Nhưng nếu nghiện thì toang! Nhớ lập thời gian biểu nhé!",
|
||||
"Chơi game vừa phải có thể tốt cho bộ não. Nhưng nếu nghiện thì toang! Nhớ sắp xếp quỹ thời gian cho hợp lý nhé!",
|
||||
-- The ability to dig is extremely important in battles!!!
|
||||
"Khả năng đào xuống (downstacking) của bạn là RẤT QUAN TRỌNG trong chiến đấu!!!",
|
||||
-- Skilled players of the Classic Tetris game are also formidable; don't underestimate them
|
||||
"Đừng có mà xem thường những người chơi xếp gạch cổ điển! Chơi cái đó cũng khó không khác gì chơi xếp gạch hiện đại đâu.",
|
||||
-- Classic Tetris and Modern Tetris are two different games; being skilled in one doesn't mean you'll be skilled in the other. You have to start from scratch.
|
||||
"Xếp gạch cổ điển và xếp gạch hiện đại là hai thể loại game khác nhau đấy! Giỏi một trong hai chưa chắc bạn giỏi cả bên còn lại đâu. Bạn phải học lại từ đầu đấy",
|
||||
"Xếp gạch cổ điển và xếp gạch hiện đại là hai thể loại game khác nhau đấy! Giỏi một trong hai chưa chắc bạn giỏi cả bên còn lại đâu. Bạn đều phải học lại từ đầu đấy",
|
||||
-- To protect the players' well-being, the game has a temporary and simplified anti-addiction system! (But you probably won't trigger it, haha)
|
||||
"Để tránh việc người chơi nào đó chơi quá lâu, game đã có hệ thống chống nghiện đơn giản tạm thời (Nhưng bạn có lẽ sẽ không bao giờ kích hoạt chúng đâu, haha)",
|
||||
-- Basic stacking and digging skills are crucial; those who neglect these two aspects often regret it (trust me)
|
||||
{"Kỹ năng xếp lên vào đào xuống là 2 kỹ năng RẤT quan trọng; những ai coi thường hay bỏ bê hai khía cạnh này thường hay bị bón hành súp mặt lờ (tin ",C.W,"MrZ",C.Z," đi!)"},
|
||||
{"Kỹ năng xếp lên vào đào xuống là 2 kỹ năng RẤT quan trọng; những người hay bị bón hành hầu hết là những người đếch tôn trọng 2 kỹ năng này (tin lời ",C.W,"MrZ",C.Z," đi!)"},
|
||||
-- Even if you're topped out, don't give up; every line of garbage can potentially become your weapon.
|
||||
"Đừng bỏ cuộc khi đống hàng rác đang làm bạn sắp bị top out, bởi bạn có thể biến chúng trở thành đòn phản công.",
|
||||
-- The video shown above is not a recording; it's the robot playing in real-time.
|
||||
"Cái ở trên là bản ghi sẵn hả? Không, là AI đang chơi trong thời gian thực đấy!",
|
||||
"Cái ở trên là video hả? Không, là AI đang chơi trong thời gian thực đấy!",
|
||||
-- Extended gaming sessions will gradually deteriorate your performance! Remember to take breaks when playing for a long time~
|
||||
"Thường xuyên chơi game lâu có thể khiến bạn có thể bị đuối sức (cả thể chất và tinh thần, tệ nhất có thể bị stall). Hãy nhớ nghỉ giải lao sau khi chơi lâu nhé!",
|
||||
"Thường xuyên chơi game lâu có thể khiến bạn có thể bị đuối sức (cả thể chất và tinh thần, tệ nhất có thể bị stall). Hãy nhớ nghỉ giải lao sau khi chơi lâu nhé! Meow~",
|
||||
-- Be careful of tenosynovitis!
|
||||
{C.R,"CẢNH BÁO! ",C.Z,"Bệnh viêm bao gân cổ tay!"},
|
||||
-- The button with a question mark in the bottom-right corner is the game manual (assuming you haven't enabled the Simple mode).
|
||||
"Cái nút "..CHAR.icon.help.." ở góc phải dưới cùng trong menu (không bật chế độ Đơn giản) đấy hả? Nó là manual (hướng dẫn sử dụng) của game đấy!",
|
||||
"Cái nút "..CHAR.icon.help.." ở màn hình chính (giả sử bạn chưa mở Chế độ đơn giản) là hướng dẫn sử dụng của Techmino đấy!",
|
||||
-- If you're new to blocks, just play more games; there isn't much specific targeted practice beyond 40 lines in two minutes
|
||||
"Bạn mới tập chơi xếp gạch à? Nếu vậy cứ chơi nhiều lên. Không có nhiều mục tiêu luyện tập cụ thể ngoài xóa 40 hàng trong 2 phút đâu!",
|
||||
--
|
||||
"Hãy ra ngoài và chạm cỏ đi!",
|
||||
-- [SELF-MADE]
|
||||
{"Có thuật ngữ nào bạn không hiểu à? Hãy tra ",C.R,"Z",C.Z,"ictionary!"}, -- Have a term you don't understand? Check Zictionary
|
||||
-- Maybe saying this you may not believe, but there are many tips here never appear in English!
|
||||
{"Nói cái này có thể bạn không tin, ",C.lSea,"nhưng có nhiều mẹo trong số này bạn sẽ không bao giờ thấy ở trong Techmino tiếng Anh đâu!"},
|
||||
|
||||
--
|
||||
-- OTHER
|
||||
"\"Zictionary\" có 2 tên gọi khác, đó là: \"TetroDictionary\" và \"Little Z Dictionary\"",
|
||||
"\"Zictionary\" có thể có vài chỗ nghe bất tự nhiên. Cứ thoải mái gửi bản chỉnh sửa lên GitHub đi!",
|
||||
"Làm ơn đừng đánh đồng Korobeiniki với bản nhạc A. Hai nhạc này có sự khác biệt rất rõ trong giai điệu nhé!",
|
||||
"Làm cách nào để mời người khác cùng chơi xếp gạch với mình? Xếp gạch giờ không còn đơn giản như trước đây nữa...",
|
||||
|
||||
--
|
||||
-- MrZ
|
||||
{C.W,"uid:225238922"},
|
||||
@@ -1192,7 +1201,7 @@ C. Tay cầm chơi game (Gamepad):
|
||||
--
|
||||
-- Z SAID
|
||||
-- I can't write cool music (crying)
|
||||
{C.W,"Z: ",C.Z,"Tôi không tài nào viết nổi một bản nhạc nào trông ngầu cả (sadge)."},
|
||||
{C.W,"Z: ",C.Z,"Tôi không tài nào viết nổi một bản nhạc nào ra hồn cả (sadge)."},
|
||||
-- I haven't studied music composition. I just composed it myself. If you really think it's good, that's great!
|
||||
{C.W,"Z: ",C.Z,"Tôi chưa từng học sáng tác nhạc, và tôi chỉ tự sáng tác chúng. Nếu bạn thấy những bản nhạc này hay, thật tuyệt!"},
|
||||
-- What else can I write for tips?
|
||||
@@ -1222,7 +1231,7 @@ C. Tay cầm chơi game (Gamepad):
|
||||
"Techmino.exe hiện không phản hồi",
|
||||
"Techmino đã đột ngột dừng lại",
|
||||
-- If you have a real interest in programming, I recommend Lua. Easy installation, simple syntax, and fast execution speed. Stay away from boring school programming (haha)
|
||||
{"Nếu bạn thực sự có hứng thú trong lập trình, tôi đề xuất sử dụng Lua. Dễ cài đặt, cú pháp đơn giản, tốc độ thực thi nhanh. Hãy tránh xa những tiết học lập trình chán ngắt ở trên trường luôn đi! (haha) - ",C.W,"MrZ",C.Z," said."},
|
||||
{C.W,"MrZ:",C.Z,"Nếu bạn thực sự có hứng thú trong lập trình, tôi đề xuất sử dụng Lua. Dễ cài, cú pháp đơn giản, tốc độ thực thi nhanh. Hãy tránh xa những tiết học lập trình chán ngắt ở trên trường luôn đi! (haha)"},
|
||||
--
|
||||
-- CHANGELOG
|
||||
{C.lW, "V0.0.091726",": ",C.Z, "Thêm hệ thống xoay TRS"},
|
||||
@@ -1269,18 +1278,20 @@ C. Tay cầm chơi game (Gamepad):
|
||||
--
|
||||
-- SEA'S JOKE
|
||||
{C.W,"MrZ",C.Z," còn có một biệt danh dễ thương hơn, đó là ",C.W,"Z-Chan"},
|
||||
{C.W,"Z-Chan",C.Z," cute quá. Tui không cưỡng lại được... HELP!"},
|
||||
|
||||
{C.lSea,"Sea: ",C.Z,"Tui không có đủ mặn để viết joke. Nên một số câu đùa đang chạy ở đây được viết bởi ",C.yellow,"Shard Nguyễn",C.Z,". \"Em cảm ơn anh!\""},
|
||||
{C.lSea,"Sea: ",C.Z,"Tui đang tự hỏi liệu còn bao nhiêu lỗi tui bỏ sót lúc dịch game không? Tính ra tui đã cập nhật đi cập nhật lại cũng 4-5 lần rồi."},
|
||||
|
||||
{"Cộng đồng Tetris ",C.R,"Việt ",C.lY,"Nam ",C.Z,": https://discord.gg/jX7BX9g"}, -- Tetris Vietnam (TVN)
|
||||
{"\"Tetris Việt Nam\"? Không, đó là \"Hội phụ hồ Việt Nam\" (https://discord.gg/hoiphuhovietnam)"},
|
||||
"\"Zictionary\" có 2 tên gọi khác, đó là: \"TetroDictionary\" và \"Little Z Dictionary\"",
|
||||
"Mình xin phép ủng hộ cho player này. Ủng hộ càng nhiều tỉ lệ thắng càng cao!",
|
||||
{"Aiiiii mua cần phô mai ủng hộ ",C.yellow,"Chủ tiệm phô mai",C.Z," không?"}, -- A joke in TVN
|
||||
-- Who will you choose? A girl that can break up to you and make you sad
|
||||
-- Or choose Katyusha that can warm your heart and 40ha land of enemy.
|
||||
"Bạn sẽ chọn ai? Một em gái có thể chia tay và làm bạn buồn? Hay là chọn em Katyusha có thể làm ấm lòng bạn và 40ha đất kẻ thù?", -- Based on a comment in https://www.youtube.com/watch?v=nczdLwTyWmY
|
||||
"Bạn sẽ chọn ai? Một em gái nào đó có thể chia tay và làm bạn buồn? Hay là chọn em gái Katyusha có thể làm ấm lòng bạn và 40ha đất kẻ thù?", -- Based on a comment in https://www.youtube.com/watch?v=nczdLwTyWmY
|
||||
-- What if the ".io" domain disappear? What will happen with TETR.IO?
|
||||
"Nếu tên miền cấp quốc gia '.io' biến mất, chuyện gì sẽ xảy ra với TETR.IO?",
|
||||
},
|
||||
pumpkin="Tôi là một quả bí ngô",
|
||||
}
|
||||
|
||||
@@ -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了",
|
||||
|
||||
@@ -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);", "", "很强大脑"},
|
||||
|
||||
@@ -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']= {"隱形", "瞬隱", "很強大腦"},
|
||||
|
||||
@@ -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'}},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,6 +9,7 @@ local ms,kb,tc=love.mouse,love.keyboard,love.touch
|
||||
|
||||
local max,min=math.max,math.min
|
||||
local floor,abs=math.floor,math.abs
|
||||
local sin=math.sin
|
||||
|
||||
local mapCam={
|
||||
sel=false,-- Selected mode ID
|
||||
@@ -32,7 +33,7 @@ function scene.enter()
|
||||
grid=false
|
||||
BG.set()
|
||||
mapCam.zoomK=SCN.prev=='main' and 5 or 1
|
||||
visibleModes={}-- 1=unlocked, 2=locked but visible
|
||||
visibleModes={}-- 1=unlocked, 2=locked but close to a unlocked mode
|
||||
for name,M in next,MODES do
|
||||
if RANKS[name] and M.x then
|
||||
visibleModes[name]=1
|
||||
@@ -260,7 +261,7 @@ function scene.draw()
|
||||
gc_setLineWidth(8)
|
||||
gc_setColor(1,1,1,.2)
|
||||
for name,M in next,MODES do
|
||||
if R[name] and M.unlock and M.x then
|
||||
if M.unlock and M.x then
|
||||
for _=1,#M.unlock do
|
||||
local m=MODES[M.unlock[_]]
|
||||
gc_line(M.x,M.y,m.x,m.y)
|
||||
@@ -273,36 +274,43 @@ function scene.draw()
|
||||
gc_setLineWidth(4)
|
||||
for name,M in next,MODES do
|
||||
local unlocked=visibleModes[name]
|
||||
if unlocked then
|
||||
local rank=R[name]
|
||||
local S=M.size
|
||||
local rank=R[name]
|
||||
local S=M.size
|
||||
|
||||
-- Draw shapes on map
|
||||
if unlocked==1 then
|
||||
gc_setColor(baseRankColor[rank])
|
||||
_drawModeShape(M,S,'fill')
|
||||
end
|
||||
gc_setColor(1,1,sel==name and 0 or 1,unlocked==1 and .8 or .3)
|
||||
_drawModeShape(M,S,'line')
|
||||
-- Draw shapes on map
|
||||
if unlocked==1 then
|
||||
gc_setColor(baseRankColor[rank])
|
||||
_drawModeShape(M,S,'fill')
|
||||
end
|
||||
gc_setColor(
|
||||
1,1,sel==name and 0 or 1,
|
||||
unlocked==1 and .8 or unlocked==2 and sin(TIME()*4)*.35+.65 or .3
|
||||
)
|
||||
_drawModeShape(M,S,'line')
|
||||
|
||||
-- Icon
|
||||
local icon=M.icon
|
||||
if icon then
|
||||
gc_setColor(unlocked==1 and COLOR.lH or COLOR.dH)
|
||||
local length=icon:getWidth()*.5
|
||||
gc_draw(icon,M.x,M.y,nil,S/length,nil,length,length)
|
||||
end
|
||||
-- Icon
|
||||
local icon=M.icon
|
||||
if icon then
|
||||
gc_setColor(unlocked and COLOR.lH or COLOR.dH)
|
||||
local length=icon:getWidth()*.5
|
||||
gc_draw(icon,M.x,M.y,nil,S/length,nil,length,length)
|
||||
end
|
||||
|
||||
-- Rank
|
||||
if unlocked==1 then
|
||||
name=RANK_CHARS[rank]
|
||||
if name then
|
||||
gc_setColor(COLOR.dX)
|
||||
GC.mStr(name,M.x+M.size*.7,M.y-50-M.size*.7)
|
||||
gc_setColor(RANK_COLORS[rank])
|
||||
GC.mStr(name,M.x+M.size*.7+4,M.y-50-M.size*.7-4)
|
||||
end
|
||||
-- Rank
|
||||
if unlocked==1 then
|
||||
name=RANK_CHARS[rank]
|
||||
if name then
|
||||
gc_setColor(COLOR.dX)
|
||||
GC.mStr(name,M.x+M.size*.7,M.y-50-M.size*.7)
|
||||
gc_setColor(RANK_COLORS[rank])
|
||||
GC.mStr(name,M.x+M.size*.7+4,M.y-50-M.size*.7-4)
|
||||
end
|
||||
-- Lock
|
||||
elseif M.size then
|
||||
gc_setColor(COLOR.dX)
|
||||
gc_draw(IMG.lock,M.x-32+M.size*.7,M.y-20-M.size*.7,0,2)
|
||||
gc_setColor(unlocked==2 and COLOR.lH or COLOR.dH)
|
||||
gc_draw(IMG.lock,M.x-26+M.size*.7,M.y-26-M.size*.7,0,2)
|
||||
end
|
||||
end
|
||||
gc_pop()
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,8 +8,15 @@
|
||||
DiscordRPC支持
|
||||
主菜单添加公告页面
|
||||
尝试重启联网
|
||||
改动:
|
||||
删除三个凑数的策略堆叠模式
|
||||
3D方块效果微调
|
||||
调整所有方块皮肤的名称
|
||||
一些内部系统重构,尝试支持Web端
|
||||
修复:
|
||||
晃动特效开大了20g下颠到吐
|
||||
hisPool序列算法错误(会影响部分模式录像)
|
||||
开了“关闭节日主题”时可能导致启动就卡死
|
||||
|
||||
|
||||
0.17.19: 暂停 Break
|
||||
|
||||
Reference in New Issue
Block a user