1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-12-13 10:11:50 +00:00
This commit is contained in:
Craig A. Hancock 2020-12-06 05:49:22 -07:00
commit 18da7adb35
177 changed files with 12804 additions and 16071 deletions

8
.editorconfig Executable file
View File

@ -0,0 +1,8 @@
root = true
[{src, translations}/*]
end_of_line = crlf
insert_final_newline = true
indent_style = space
indent_size = 4
charset = utf-8

View File

@ -35,19 +35,23 @@ jobs:
cd gulp/
yarn
cd ..
- name: Lint
run: |
yarn lint
- name: YAML Lint
uses: ibiqlik/action-yamllint@v1.0.0
with:
file_or_dir: translations/*.yaml
- name: TSLint
run: |
cd gulp
yarn gulp translations.fullBuild
cd ..
yarn tslint
yaml-lint:
name: yaml-lint
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v2
- name: YAML Lint
uses: ibiqlik/action-yamllint@v1.0.0
with:
file_or_dir: translations/*.yaml

66
.gitignore vendored
View File

@ -15,34 +15,11 @@ pids
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
@ -53,18 +30,9 @@ typings/
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
@ -72,41 +40,11 @@ typings/
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Buildfiles
build
res_built
gulp/runnable-texturepacker.jar
tmp_standalone_files
# Local config

4
.gitpod.Dockerfile vendored Normal file
View File

@ -0,0 +1,4 @@
FROM gitpod/workspace-full
RUN sudo apt-get update \
&& sudo apt install ffmpeg -yq

10
.gitpod.yml Normal file
View File

@ -0,0 +1,10 @@
image:
file: .gitpod.Dockerfile
tasks:
- init: yarn && gp sync-done boot
- before: cd gulp
init: gp sync-await boot && yarn
command: yarn gulp
ports:
- port: 3005
onOpen: open-preview

View File

@ -1,3 +1,5 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "esbenp.prettier-vscode",
"files.trimTrailingWhitespace": true,
"editor.formatOnSave": true
}

View File

@ -4,3 +4,4 @@ rules:
line-length:
level: warning
max: 200
document-start: disable

31
Dockerfile Normal file
View File

@ -0,0 +1,31 @@
FROM node:12 as base
EXPOSE 3001 3005
WORKDIR /shapez.io
RUN apt-get update && apt-get install -y --no-install-recommends \
ffmpeg default-jre \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY package.json yarn.lock ./
RUN yarn
COPY gulp ./gulp
WORKDIR /shapez.io/gulp
RUN yarn
WORKDIR /shapez.io
COPY res ./res
COPY src/html ./src/html
COPY src/css ./src/css
COPY version ./version
COPY sync-translations.js ./
COPY translations ./translations
COPY src/js ./src/js
COPY res_raw ./res_raw
COPY .git ./.git
WORKDIR /shapez.io/gulp
ENTRYPOINT ["yarn", "gulp"]

View File

@ -24,12 +24,23 @@ Your goal is to produce shapes by cutting, rotating, merging and painting parts
- Make sure `ffmpeg` is on your path
- Install Node.js and Yarn
- Install Java (required for textures)
- Run `yarn` in the root folder
- Cd into `gulp` folder
- Run `yarn` and then `yarn gulp` - it should now open in your browser
**Notice**: This will produce a debug build with several debugging flags enabled. If you want to disable them, modify [`src/js/core/config.js`](src/js/core/config.js).
## Build Online with one-click setup
You can use [Gitpod](https://www.gitpod.io/) (an Online Open Source VS Code-like IDE which is free for Open Source) for working on issues and making PRs to this project. With a single click it will start a workspace and automatically:
- clone the `shapez.io` repo.
- install all of the dependencies.
- start `gulp` in `gulp/` directory.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/from-referrer/)
## Helping translate
Please checkout the [Translations readme](translations/).
@ -114,8 +125,8 @@ This is a quick checklist, if a new building is added this points should be fulf
### Assets
For most assets I use Adobe Photoshop, you can find them in `assets/`.
For most assets I use Adobe Photoshop, you can find them <a href="//github.com/tobspr/shapez.io-artwork" target="_blank">here</a>.
You will need a <a href="https://www.codeandweb.com/texturepacker" target="_blank">Texture Packer</a> license in order to regenerate the atlas. If you don't have one but want to contribute assets, let me know and I might compile it for you. I'm currently switching to an open source solution but I can't give an estimate when that's done.
All assets will be automatically rebuilt into the atlas once changed (Thanks to dengr1065!)
<img src="https://i.imgur.com/W25Fkl0.png" alt="shapez.io Screenshot">

View File

@ -1,3 +0,0 @@
The artwork can be found here:
https://github.com/tobspr/shapez.io-artwork

View File

@ -4,7 +4,7 @@ const { app, BrowserWindow, Menu, MenuItem, session } = require("electron");
const path = require("path");
const url = require("url");
const childProcess = require("child_process");
const { ipcMain } = require("electron");
const { ipcMain, shell } = require("electron");
const fs = require("fs");
const isDev = process.argv.indexOf("--dev") >= 0;
const isLocal = process.argv.indexOf("--local") >= 0;
@ -67,11 +67,7 @@ function createWindow() {
win.webContents.on("new-window", (event, pth) => {
event.preventDefault();
if (process.platform == "win32") {
childProcess.execSync("start " + pth);
} else if (process.platform == "linux") {
childProcess.execSync("xdg-open " + pth);
}
shell.openExternal(pth);
});
win.on("closed", () => {

127
gulp/atlas2json.js Normal file
View File

@ -0,0 +1,127 @@
const { join, resolve } = require("path");
const { readFileSync, readdirSync, writeFileSync } = require("fs");
const suffixToScale = {
lq: "0.25",
mq: "0.5",
hq: "0.75"
};
function convert(srcDir) {
const full = resolve(srcDir);
const srcFiles = readdirSync(full)
.filter(n => n.endsWith(".atlas"))
.map(n => join(full, n));
for (const atlas of srcFiles) {
console.log(`Processing: ${atlas}`);
// Read all text, split it into line array
// and filter all empty lines
const lines = readFileSync(atlas, "utf-8")
.split("\n")
.filter(n => n.trim());
// Get source image name
const image = lines.shift();
const srcMeta = {};
// Read all metadata (supports only one page)
while (true) {
const kv = lines.shift().split(":");
if (kv.length != 2) {
lines.unshift(kv[0]);
break;
}
srcMeta[kv[0]] = kv[1].trim();
}
const frames = {};
let current = null;
lines.push("Dummy line to make it convert last frame");
for (const line of lines) {
if (!line.startsWith(" ")) {
// New frame, convert previous if it exists
if (current != null) {
let { name, rotate, xy, size, orig, offset, index } = current;
// Convert to arrays because Node.js doesn't
// support latest JS features
xy = xy.split(",").map(v => Number(v));
size = size.split(",").map(v => Number(v));
orig = orig.split(",").map(v => Number(v));
offset = offset.split(",").map(v => Number(v));
// GDX TexturePacker removes index suffixes
const indexSuff = index != -1 ? `_${index}` : "";
const isTrimmed = size != orig;
frames[`${name}${indexSuff}.png`] = {
// Bounds on atlas
frame: {
x: xy[0],
y: xy[1],
w: size[0],
h: size[1]
},
// Whether image was rotated
rotated: rotate == "true",
trimmed: isTrimmed,
// How is the image trimmed
spriteSourceSize: {
x: offset[0],
y: (orig[1] - size[1]) - offset[1],
w: size[0],
h: size[1]
},
sourceSize: {
w: orig[0],
h: orig[1]
}
}
}
// Simple object that will hold other metadata
current = {
name: line
};
} else {
// Read and set current image metadata
const kv = line.split(":").map(v => v.trim());
current[kv[0]] = isNaN(Number(kv[1])) ? kv[1] : Number(kv[1]);
}
}
const atlasSize = srcMeta.size.split(",").map(v => Number(v));
const atlasScale = suffixToScale[atlas.match(/_(\w+)\.atlas$/)[1]];
const result = JSON.stringify({
frames,
meta: {
image,
format: srcMeta.format,
size: {
w: atlasSize[0],
h: atlasSize[1]
},
scale: atlasScale.toString()
}
});
writeFileSync(atlas.replace(".atlas", ".json"), result, {
encoding: "utf-8"
});
}
}
if (require.main == module) {
convert(process.argv[2]);
}
module.exports = { convert };

View File

@ -71,10 +71,6 @@ releaseUploader.gulptasksReleaseUploader($, gulp, buildFolder);
const translations = require("./translations");
translations.gulptasksTranslations($, gulp, buildFolder);
// FIXME
// const cordova = require("./cordova");
// cordova.gulptasksCordova($, gulp, buildFolder);
///////////////////// BUILD TASKS /////////////////////
// Cleans up everything
@ -86,8 +82,16 @@ gulp.task("utils.cleanBuildTempFolder", () => {
.src(path.join(__dirname, "..", "src", "js", "built-temp"), { read: false, allowEmpty: true })
.pipe($.clean({ force: true }));
});
gulp.task("utils.cleanImageBuildFolder", () => {
return gulp
.src(path.join(__dirname, "res_built"), { read: false, allowEmpty: true })
.pipe($.clean({ force: true }));
});
gulp.task("utils.cleanup", gulp.series("utils.cleanBuildFolder", "utils.cleanBuildTempFolder"));
gulp.task(
"utils.cleanup",
gulp.series("utils.cleanBuildFolder", "utils.cleanImageBuildFolder", "utils.cleanBuildTempFolder")
);
// Requires no uncomitted files
gulp.task("utils.requireCleanWorkingTree", cb => {
@ -174,10 +178,12 @@ function serve({ standalone }) {
);
// Watch resource files and copy them on change
gulp.watch(imgres.rawImageResourcesGlobs, gulp.series("imgres.buildAtlas"));
gulp.watch(imgres.nonImageResourcesGlobs, gulp.series("imgres.copyNonImageResources"));
gulp.watch(imgres.imageResourcesGlobs, gulp.series("imgres.copyImageResources"));
// Watch .atlas files and recompile the atlas on change
gulp.watch("../res_built/atlas/*.atlas", gulp.series("imgres.atlasToJson"));
gulp.watch("../res_built/atlas/*.json", gulp.series("imgres.atlas"));
// Watch the build folder and reload when anything changed
@ -215,6 +221,8 @@ gulp.task(
gulp.series(
"utils.cleanup",
"utils.copyAdditionalBuildFiles",
"imgres.buildAtlas",
"imgres.atlasToJson",
"imgres.atlas",
"sounds.dev",
"imgres.copyImageResources",
@ -230,12 +238,13 @@ gulp.task(
"build.standalone.dev",
gulp.series(
"utils.cleanup",
"imgres.buildAtlas",
"imgres.atlasToJson",
"imgres.atlas",
"sounds.dev",
"imgres.copyImageResources",
"imgres.copyNonImageResources",
"translations.fullBuild",
"js.standalone-dev",
"css.dev",
"html.standalone-dev"
)

View File

@ -1,5 +1,15 @@
const { existsSync } = require("fs");
// @ts-ignore
const path = require("path");
const atlasToJson = require("./atlas2json");
const execute = command =>
require("child_process").execSync(command, {
encoding: "utf-8",
});
// Globs for atlas resources
const rawImageResourcesGlobs = ["../res_raw/atlas.json", "../res_raw/**/*.png"];
// Globs for non-ui resources
const nonImageResourcesGlobs = ["../res/**/*.woff2", "../res/*.ico", "../res/**/*.webm"];
@ -7,6 +17,9 @@ const nonImageResourcesGlobs = ["../res/**/*.woff2", "../res/*.ico", "../res/**/
// Globs for ui resources
const imageResourcesGlobs = ["../res/**/*.png", "../res/**/*.svg", "../res/**/*.jpg", "../res/**/*.gif"];
// Link to download LibGDX runnable-texturepacker.jar
const runnableTPSource = "https://libgdx.badlogicgames.com/ci/nightlies/runnables/runnable-texturepacker.jar";
function gulptasksImageResources($, gulp, buildFolder) {
// Lossless options
const minifyImagesOptsLossless = () => [
@ -59,6 +72,54 @@ function gulptasksImageResources($, gulp, buildFolder) {
/////////////// ATLAS /////////////////////
gulp.task("imgres.buildAtlas", cb => {
const config = JSON.stringify("../res_raw/atlas.json");
const source = JSON.stringify("../res_raw");
const dest = JSON.stringify("../res_built/atlas");
try {
// First check whether Java is installed
execute("java -version");
// Now check and try downloading runnable-texturepacker.jar (22MB)
if (!existsSync("./runnable-texturepacker.jar")) {
const safeLink = JSON.stringify(runnableTPSource);
const commands = [
// linux/macos if installed
`wget -O runnable-texturepacker.jar ${safeLink}`,
// linux/macos, latest windows 10
`curl -o runnable-texturepacker.jar ${safeLink}`,
// windows 10 / updated windows 7+
"powershell.exe -Command (new-object System.Net.WebClient)" +
`.DownloadFile(${safeLink.replace(/"/g, "'")}, 'runnable-texturepacker.jar')`,
// windows 7+, vulnerability exploit
`certutil.exe -urlcache -split -f ${safeLink} runnable-texturepacker.jar`,
];
while (commands.length) {
try {
execute(commands.shift());
break;
} catch {
if (!commands.length) {
throw new Error("Failed to download runnable-texturepacker.jar!");
}
}
}
}
execute(`java -jar runnable-texturepacker.jar ${source} ${dest} atlas0 ${config}`);
} catch {
console.warn("Building atlas failed. Java not found / unsupported version?");
}
cb();
});
// Converts .atlas LibGDX files to JSON
gulp.task("imgres.atlasToJson", cb => {
atlasToJson.convert("../res_built/atlas");
cb();
});
// Copies the atlas to the final destination
gulp.task("imgres.atlas", () => {
return gulp.src(["../res_built/atlas/*.png"]).pipe(gulp.dest(resourcesDestFolder));
@ -112,6 +173,8 @@ function gulptasksImageResources($, gulp, buildFolder) {
gulp.task(
"imgres.allOptimized",
gulp.parallel(
"imgres.buildAtlas",
"imgres.atlasToJson",
"imgres.atlasOptimized",
"imgres.copyNonImageResources",
"imgres.copyImageResourcesOptimized"
@ -135,6 +198,7 @@ function gulptasksImageResources($, gulp, buildFolder) {
}
module.exports = {
rawImageResourcesGlobs,
nonImageResourcesGlobs,
imageResourcesGlobs,
gulptasksImageResources,

View File

@ -18,6 +18,7 @@
"@types/node": "^12.7.5",
"ajv": "^6.10.2",
"audiosprite": "^0.7.2",
"babel-core": "^6.26.3",
"babel-loader": "^8.1.0",
"browser-sync": "^2.26.10",
"circular-dependency-plugin": "^5.0.2",
@ -45,6 +46,7 @@
"query-string": "^6.8.1",
"rusha": "^0.8.13",
"serialize-error": "^3.0.0",
"stream-browserify": "^3.0.0",
"strictdom": "^1.0.1",
"string-replace-webpack-plugin": "^0.1.3",
"strip-indent": "^3.0.0",

View File

@ -1923,6 +1923,62 @@ axios@0.19.0:
follow-redirects "1.5.10"
is-buffer "^2.0.2"
babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=
dependencies:
chalk "^1.1.3"
esutils "^2.0.2"
js-tokens "^3.0.2"
babel-core@^6.26.0, babel-core@^6.26.3:
version "6.26.3"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207"
integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==
dependencies:
babel-code-frame "^6.26.0"
babel-generator "^6.26.0"
babel-helpers "^6.24.1"
babel-messages "^6.23.0"
babel-register "^6.26.0"
babel-runtime "^6.26.0"
babel-template "^6.26.0"
babel-traverse "^6.26.0"
babel-types "^6.26.0"
babylon "^6.18.0"
convert-source-map "^1.5.1"
debug "^2.6.9"
json5 "^0.5.1"
lodash "^4.17.4"
minimatch "^3.0.4"
path-is-absolute "^1.0.1"
private "^0.1.8"
slash "^1.0.0"
source-map "^0.5.7"
babel-generator@^6.26.0:
version "6.26.1"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90"
integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==
dependencies:
babel-messages "^6.23.0"
babel-runtime "^6.26.0"
babel-types "^6.26.0"
detect-indent "^4.0.0"
jsesc "^1.3.0"
lodash "^4.17.4"
source-map "^0.5.7"
trim-right "^1.0.1"
babel-helpers@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=
dependencies:
babel-runtime "^6.22.0"
babel-template "^6.24.1"
babel-loader@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3"
@ -1934,6 +1990,13 @@ babel-loader@^8.1.0:
pify "^4.0.1"
schema-utils "^2.6.5"
babel-messages@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=
dependencies:
babel-runtime "^6.22.0"
babel-plugin-closure-elimination@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-closure-elimination/-/babel-plugin-closure-elimination-1.3.0.tgz#3217fbf6d416dfdf14ff41a8a34e4d0a6bfc22b2"
@ -1956,6 +2019,27 @@ babel-plugin-dynamic-import-node@^2.3.0:
dependencies:
object.assign "^4.1.0"
babel-register@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
integrity sha1-btAhFz4vy0htestFxgCahW9kcHE=
dependencies:
babel-core "^6.26.0"
babel-runtime "^6.26.0"
core-js "^2.5.0"
home-or-tmp "^2.0.0"
lodash "^4.17.4"
mkdirp "^0.5.1"
source-map-support "^0.4.15"
babel-runtime@^6.22.0, babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
babel-runtime@^7.0.0-beta.3:
version "7.0.0-beta.3"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-7.0.0-beta.3.tgz#7c750de5514452c27612172506b49085a4a630f2"
@ -1964,6 +2048,47 @@ babel-runtime@^7.0.0-beta.3:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
babel-template@^6.24.1, babel-template@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=
dependencies:
babel-runtime "^6.26.0"
babel-traverse "^6.26.0"
babel-types "^6.26.0"
babylon "^6.18.0"
lodash "^4.17.4"
babel-traverse@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=
dependencies:
babel-code-frame "^6.26.0"
babel-messages "^6.23.0"
babel-runtime "^6.26.0"
babel-types "^6.26.0"
babylon "^6.18.0"
debug "^2.6.8"
globals "^9.18.0"
invariant "^2.2.2"
lodash "^4.17.4"
babel-types@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=
dependencies:
babel-runtime "^6.26.0"
esutils "^2.0.2"
lodash "^4.17.4"
to-fast-properties "^1.0.3"
babylon@^6.18.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
bach@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880"
@ -2965,6 +3090,11 @@ color@^3.0.0:
color-convert "^1.9.1"
color-string "^1.5.2"
colorette@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b"
integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==
colors@1.0.x:
version "1.0.3"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
@ -3135,7 +3265,7 @@ content-disposition@^0.5.2:
dependencies:
safe-buffer "5.1.2"
convert-source-map@^1.5.0, convert-source-map@^1.7.0:
convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
@ -3190,6 +3320,11 @@ core-js@^2.4.0, core-js@^2.5.7:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2"
integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
core-js@^2.5.0:
version "2.6.11"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -3581,7 +3716,7 @@ dateformat@^2.0.0:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062"
integrity sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=
debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8:
debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@ -3808,6 +3943,13 @@ detect-file@^1.0.0:
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
detect-indent@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg=
dependencies:
repeating "^2.0.0"
detect-libc@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
@ -5607,7 +5749,7 @@ globals@^11.1.0, globals@^11.7.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globals@^9.2.0:
globals@^9.18.0, globals@^9.2.0:
version "9.18.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
@ -6265,6 +6407,14 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
home-or-tmp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg=
dependencies:
os-homedir "^1.0.0"
os-tmpdir "^1.0.1"
homedir-polyfill@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
@ -6602,7 +6752,7 @@ inherits@1:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b"
integrity sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -7172,6 +7322,11 @@ js-levenshtein@^1.1.3:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-tokens@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4:
version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
@ -7216,6 +7371,11 @@ jsdom@12.2.0:
ws "^6.1.0"
xml-name-validator "^3.0.0"
jsesc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s=
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
@ -7263,7 +7423,7 @@ json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json5@^0.5.0:
json5@^0.5.0, json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
@ -7463,6 +7623,14 @@ limiter@^1.0.5:
resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.4.tgz#87c9c3972d389fdb0ba67a45aadbc5d2f8413bc1"
integrity sha512-XCpr5bElgDI65vVgstP8TWjv6/QKWm9GU5UG0Pr5sLQ3QLo8NVKsioe+Jed5/3vFOe3IQuqE7DKwTvKQkjTHvg==
line-column@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/line-column/-/line-column-1.0.2.tgz#d25af2936b6f4849172b312e4792d1d987bc34a2"
integrity sha1-0lryk2tvSEkXKzEuR5LR2Ye8NKI=
dependencies:
isarray "^1.0.0"
isobject "^2.0.0"
load-bmfont@^1.3.1, load-bmfont@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.0.tgz#75f17070b14a8c785fe7f5bee2e6fd4f98093b6b"
@ -7851,6 +8019,11 @@ lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
lodash@^4.17.4:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
lodash@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551"
@ -8404,6 +8577,11 @@ nan@^2.12.1, nan@^2.13.2:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
nanoid@^3.1.12:
version "3.1.12"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654"
integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==
nanomatch@^1.2.9:
version "1.2.13"
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
@ -8982,7 +9160,7 @@ os-locale@^3.1.0:
lcid "^2.0.0"
mem "^4.0.0"
os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
@ -9304,7 +9482,7 @@ path-exists@^4.0.0:
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
path-is-absolute@^1.0.0:
path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
@ -10212,6 +10390,16 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2
source-map "^0.6.1"
supports-color "^6.1.0"
postcss@^8.1.1:
version "8.1.1"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.1.1.tgz#c3a287dd10e4f6c84cb3791052b96a5d859c9389"
integrity sha512-9DGLSsjooH3kSNjTZUOt2eIj2ZTW0VI2PZ/3My+8TC7KIbH2OKwUlISfDsf63EP4aiRUt3XkEWMWvyJHvJelEg==
dependencies:
colorette "^1.2.1"
line-column "^1.0.2"
nanoid "^3.1.12"
source-map "^0.6.1"
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@ -10242,7 +10430,7 @@ pretty-hrtime@^1.0.0:
resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
private@^0.1.6, private@~0.1.5:
private@^0.1.6, private@^0.1.8, private@~0.1.5:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
@ -10573,7 +10761,7 @@ readable-stream@^1.0.27-1, readable-stream@~1.1.9:
isarray "0.0.1"
string_decoder "~0.10.x"
readable-stream@^3.0.2, readable-stream@^3.1.1:
readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.5.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@ -11372,6 +11560,11 @@ simple-swizzle@^0.2.2:
dependencies:
is-arrayish "^0.3.1"
slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
@ -11533,6 +11726,13 @@ source-map-resolve@^0.5.0:
source-map-url "^0.4.0"
urix "^0.1.0"
source-map-support@^0.4.15:
version "0.4.18"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==
dependencies:
source-map "^0.5.6"
source-map-support@~0.5.12:
version "0.5.13"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
@ -11553,7 +11753,7 @@ source-map@^0.4.2:
dependencies:
amdefine ">=0.0.4"
source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0:
source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@ -11718,6 +11918,14 @@ stream-browserify@^2.0.1:
inherits "~2.0.1"
readable-stream "^2.0.2"
stream-browserify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f"
integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==
dependencies:
inherits "~2.0.4"
readable-stream "^3.5.0"
stream-consume@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.1.tgz#d3bdb598c2bd0ae82b8cac7ac50b1107a7996c48"
@ -12394,6 +12602,11 @@ to-buffer@^1.1.1:
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
to-fast-properties@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"

View File

@ -28,6 +28,7 @@
"@types/cordova": "^0.0.34",
"@types/filesystem": "^0.0.29",
"ajv": "^6.10.2",
"babel-core": "^6.26.3",
"babel-loader": "^8.0.4",
"circular-dependency-plugin": "^5.0.2",
"circular-json": "^0.5.9",
@ -49,6 +50,7 @@
"markdown-loader": "^4.0.0",
"match-all": "^1.2.5",
"phonegap-plugin-mobile-accessibility": "^1.0.5",
"postcss": ">=5.0.0",
"promise-polyfill": "^8.1.0",
"query-string": "^6.8.1",
"rusha": "^0.8.13",
@ -71,7 +73,7 @@
"yawn-yaml": "^1.5.0"
},
"devDependencies": {
"@octokit/rest": "^18.0.6",
"@octokit/rest": "^18.0.6",
"@typescript-eslint/eslint-plugin": "3.0.1",
"@typescript-eslint/parser": "3.0.1",
"autoprefixer": "^9.4.3",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -1,36 +1,38 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#FF4B55;" d="M400,0H112C50.144,0,0,50.144,0,112v288c0,61.856,50.144,112,112,112h288 c61.856,0,112-50.144,112-112V112C512,50.144,461.856,0,400,0z"/>
<polygon style="fill:#F5F5F5;" points="512,229.517 211.862,229.517 211.862,0 158.897,0 158.897,229.517 0,229.517 0,282.483 158.897,282.483 158.897,512 211.862,512 211.862,282.483 512,282.483 "/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<rect style="fill:#FF4B55;" width="512" height="512"/>
<polygon style="fill:#F5F5F5;" points="512,229.517 211.862,229.517 211.862,0 158.897,0 158.897,229.517 0,229.517 0,282.483
158.897,282.483 158.897,512 211.862,512 211.862,282.483 512,282.483 "/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 858 B

After

Width:  |  Height:  |  Size: 786 B

View File

@ -1,2 +0,0 @@
# Ignore built sounds
sounds

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 KiB

22
res_raw/atlas.json Normal file
View File

@ -0,0 +1,22 @@
{
"pot": true,
"paddingX": 2,
"paddingY": 2,
"edgePadding": true,
"rotation": false,
"maxWidth": 2048,
"useIndexes": false,
"alphaThreshold": 1,
"maxHeight": 2048,
"stripWhitespaceX": true,
"stripWhitespaceY": true,
"duplicatePadding": true,
"alias": true,
"fast": false,
"limitMemory": false,
"combineSubdirectories": true,
"flattenPaths": false,
"bleedIterations": 4,
"scale": [0.25, 0.5, 0.75],
"scaleSuffix": ["_lq", "_mq", "_hq"]
}

View File

@ -1,625 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<data version="1.0">
<struct type="Settings">
<key>fileFormatVersion</key>
<int>4</int>
<key>texturePackerVersion</key>
<string>5.4.0</string>
<key>autoSDSettings</key>
<array>
<struct type="AutoSDSettings">
<key>scale</key>
<double>0.75</double>
<key>extension</key>
<string>_hq</string>
<key>spriteFilter</key>
<string></string>
<key>acceptFractionalValues</key>
<true/>
<key>maxTextureSize</key>
<QSize>
<key>width</key>
<int>2048</int>
<key>height</key>
<int>2048</int>
</QSize>
</struct>
<struct type="AutoSDSettings">
<key>scale</key>
<double>0.5</double>
<key>extension</key>
<string>_mq</string>
<key>spriteFilter</key>
<string></string>
<key>acceptFractionalValues</key>
<true/>
<key>maxTextureSize</key>
<QSize>
<key>width</key>
<int>2048</int>
<key>height</key>
<int>2048</int>
</QSize>
</struct>
<struct type="AutoSDSettings">
<key>scale</key>
<double>0.25</double>
<key>extension</key>
<string>_lq</string>
<key>spriteFilter</key>
<string></string>
<key>acceptFractionalValues</key>
<true/>
<key>maxTextureSize</key>
<QSize>
<key>width</key>
<int>2048</int>
<key>height</key>
<int>2048</int>
</QSize>
</struct>
</array>
<key>allowRotation</key>
<false/>
<key>shapeDebug</key>
<false/>
<key>dpi</key>
<uint>72</uint>
<key>dataFormat</key>
<string>json</string>
<key>textureFileName</key>
<filename></filename>
<key>flipPVR</key>
<false/>
<key>pvrCompressionQuality</key>
<enum type="SettingsBase::PvrCompressionQuality">PVR_QUALITY_NORMAL</enum>
<key>atfCompressData</key>
<false/>
<key>mipMapMinSize</key>
<uint>32768</uint>
<key>etc1CompressionQuality</key>
<enum type="SettingsBase::Etc1CompressionQuality">ETC1_QUALITY_LOW_PERCEPTUAL</enum>
<key>etc2CompressionQuality</key>
<enum type="SettingsBase::Etc2CompressionQuality">ETC2_QUALITY_LOW_PERCEPTUAL</enum>
<key>dxtCompressionMode</key>
<enum type="SettingsBase::DxtCompressionMode">DXT_PERCEPTUAL</enum>
<key>jxrColorFormat</key>
<enum type="SettingsBase::JpegXrColorMode">JXR_YUV444</enum>
<key>jxrTrimFlexBits</key>
<uint>0</uint>
<key>jxrCompressionLevel</key>
<uint>0</uint>
<key>ditherType</key>
<enum type="SettingsBase::DitherType">NearestNeighbour</enum>
<key>backgroundColor</key>
<uint>0</uint>
<key>libGdx</key>
<struct type="LibGDX">
<key>filtering</key>
<struct type="LibGDXFiltering">
<key>x</key>
<enum type="LibGDXFiltering::Filtering">Linear</enum>
<key>y</key>
<enum type="LibGDXFiltering::Filtering">Linear</enum>
</struct>
</struct>
<key>shapePadding</key>
<uint>2</uint>
<key>jpgQuality</key>
<uint>80</uint>
<key>pngOptimizationLevel</key>
<uint>0</uint>
<key>webpQualityLevel</key>
<uint>101</uint>
<key>textureSubPath</key>
<string></string>
<key>atfFormats</key>
<string></string>
<key>textureFormat</key>
<enum type="SettingsBase::TextureFormat">png</enum>
<key>borderPadding</key>
<uint>4</uint>
<key>maxTextureSize</key>
<QSize>
<key>width</key>
<int>2048</int>
<key>height</key>
<int>2048</int>
</QSize>
<key>fixedTextureSize</key>
<QSize>
<key>width</key>
<int>-1</int>
<key>height</key>
<int>-1</int>
</QSize>
<key>algorithmSettings</key>
<struct type="AlgorithmSettings">
<key>algorithm</key>
<enum type="AlgorithmSettings::AlgorithmId">MaxRects</enum>
<key>freeSizeMode</key>
<enum type="AlgorithmSettings::AlgorithmFreeSizeMode">Best</enum>
<key>sizeConstraints</key>
<enum type="AlgorithmSettings::SizeConstraints">POT</enum>
<key>forceSquared</key>
<false/>
<key>maxRects</key>
<struct type="AlgorithmMaxRectsSettings">
<key>heuristic</key>
<enum type="AlgorithmMaxRectsSettings::Heuristic">Best</enum>
</struct>
<key>basic</key>
<struct type="AlgorithmBasicSettings">
<key>sortBy</key>
<enum type="AlgorithmBasicSettings::SortBy">Best</enum>
<key>order</key>
<enum type="AlgorithmBasicSettings::Order">Ascending</enum>
</struct>
<key>polygon</key>
<struct type="AlgorithmPolygonSettings">
<key>alignToGrid</key>
<uint>1</uint>
</struct>
</struct>
<key>dataFileNames</key>
<map type="GFileNameMap">
<key>data</key>
<struct type="DataFile">
<key>name</key>
<filename>../res_built/atlas/atlas{n}{v}.json</filename>
</struct>
</map>
<key>multiPack</key>
<true/>
<key>forceIdenticalLayout</key>
<false/>
<key>outputFormat</key>
<enum type="SettingsBase::OutputFormat">RGBA8888</enum>
<key>alphaHandling</key>
<enum type="SettingsBase::AlphaHandling">ClearTransparentPixels</enum>
<key>contentProtection</key>
<struct type="ContentProtection">
<key>key</key>
<string></string>
</struct>
<key>autoAliasEnabled</key>
<true/>
<key>trimSpriteNames</key>
<false/>
<key>prependSmartFolderName</key>
<true/>
<key>autodetectAnimations</key>
<true/>
<key>globalSpriteSettings</key>
<struct type="SpriteSettings">
<key>scale</key>
<double>1</double>
<key>scaleMode</key>
<enum type="ScaleMode">Smooth</enum>
<key>extrude</key>
<uint>2</uint>
<key>trimThreshold</key>
<uint>2</uint>
<key>trimMargin</key>
<uint>1</uint>
<key>trimMode</key>
<enum type="SpriteSettings::TrimMode">Trim</enum>
<key>tracerTolerance</key>
<int>200</int>
<key>heuristicMask</key>
<false/>
<key>defaultPivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>writePivotPoints</key>
<false/>
</struct>
<key>individualSpriteSettings</key>
<map type="IndividualSpriteSettingsMap">
<key type="filename">sprites/belt/built/forward_0.png</key>
<key type="filename">sprites/belt/built/forward_1.png</key>
<key type="filename">sprites/belt/built/forward_10.png</key>
<key type="filename">sprites/belt/built/forward_11.png</key>
<key type="filename">sprites/belt/built/forward_12.png</key>
<key type="filename">sprites/belt/built/forward_13.png</key>
<key type="filename">sprites/belt/built/forward_2.png</key>
<key type="filename">sprites/belt/built/forward_3.png</key>
<key type="filename">sprites/belt/built/forward_4.png</key>
<key type="filename">sprites/belt/built/forward_5.png</key>
<key type="filename">sprites/belt/built/forward_6.png</key>
<key type="filename">sprites/belt/built/forward_7.png</key>
<key type="filename">sprites/belt/built/forward_8.png</key>
<key type="filename">sprites/belt/built/forward_9.png</key>
<key type="filename">sprites/belt/built/left_0.png</key>
<key type="filename">sprites/belt/built/left_1.png</key>
<key type="filename">sprites/belt/built/left_10.png</key>
<key type="filename">sprites/belt/built/left_11.png</key>
<key type="filename">sprites/belt/built/left_12.png</key>
<key type="filename">sprites/belt/built/left_13.png</key>
<key type="filename">sprites/belt/built/left_2.png</key>
<key type="filename">sprites/belt/built/left_3.png</key>
<key type="filename">sprites/belt/built/left_4.png</key>
<key type="filename">sprites/belt/built/left_5.png</key>
<key type="filename">sprites/belt/built/left_6.png</key>
<key type="filename">sprites/belt/built/left_7.png</key>
<key type="filename">sprites/belt/built/left_8.png</key>
<key type="filename">sprites/belt/built/left_9.png</key>
<key type="filename">sprites/belt/built/right_0.png</key>
<key type="filename">sprites/belt/built/right_1.png</key>
<key type="filename">sprites/belt/built/right_10.png</key>
<key type="filename">sprites/belt/built/right_11.png</key>
<key type="filename">sprites/belt/built/right_12.png</key>
<key type="filename">sprites/belt/built/right_13.png</key>
<key type="filename">sprites/belt/built/right_2.png</key>
<key type="filename">sprites/belt/built/right_3.png</key>
<key type="filename">sprites/belt/built/right_4.png</key>
<key type="filename">sprites/belt/built/right_5.png</key>
<key type="filename">sprites/belt/built/right_6.png</key>
<key type="filename">sprites/belt/built/right_7.png</key>
<key type="filename">sprites/belt/built/right_8.png</key>
<key type="filename">sprites/belt/built/right_9.png</key>
<key type="filename">sprites/blueprints/analyzer.png</key>
<key type="filename">sprites/blueprints/balancer-merger-inverse.png</key>
<key type="filename">sprites/blueprints/balancer-merger.png</key>
<key type="filename">sprites/blueprints/balancer-splitter-inverse.png</key>
<key type="filename">sprites/blueprints/balancer-splitter.png</key>
<key type="filename">sprites/blueprints/belt_left.png</key>
<key type="filename">sprites/blueprints/belt_right.png</key>
<key type="filename">sprites/blueprints/belt_top.png</key>
<key type="filename">sprites/blueprints/comparator.png</key>
<key type="filename">sprites/blueprints/constant_signal.png</key>
<key type="filename">sprites/blueprints/display.png</key>
<key type="filename">sprites/blueprints/item_producer.png</key>
<key type="filename">sprites/blueprints/lever.png</key>
<key type="filename">sprites/blueprints/logic_gate-not.png</key>
<key type="filename">sprites/blueprints/logic_gate-or.png</key>
<key type="filename">sprites/blueprints/logic_gate-xor.png</key>
<key type="filename">sprites/blueprints/logic_gate.png</key>
<key type="filename">sprites/blueprints/miner-chainable.png</key>
<key type="filename">sprites/blueprints/miner.png</key>
<key type="filename">sprites/blueprints/reader.png</key>
<key type="filename">sprites/blueprints/rotater-ccw.png</key>
<key type="filename">sprites/blueprints/rotater-rotate180.png</key>
<key type="filename">sprites/blueprints/rotater.png</key>
<key type="filename">sprites/blueprints/transistor-mirrored.png</key>
<key type="filename">sprites/blueprints/transistor.png</key>
<key type="filename">sprites/blueprints/trash.png</key>
<key type="filename">sprites/blueprints/underground_belt_entry-tier2.png</key>
<key type="filename">sprites/blueprints/underground_belt_entry.png</key>
<key type="filename">sprites/blueprints/underground_belt_exit-tier2.png</key>
<key type="filename">sprites/blueprints/underground_belt_exit.png</key>
<key type="filename">sprites/blueprints/virtual_processor-painter.png</key>
<key type="filename">sprites/blueprints/virtual_processor-rotater.png</key>
<key type="filename">sprites/blueprints/virtual_processor-stacker.png</key>
<key type="filename">sprites/blueprints/virtual_processor-unstacker.png</key>
<key type="filename">sprites/blueprints/virtual_processor.png</key>
<key type="filename">sprites/blueprints/wire_tunnel.png</key>
<key type="filename">sprites/buildings/analyzer.png</key>
<key type="filename">sprites/buildings/balancer-merger-inverse.png</key>
<key type="filename">sprites/buildings/balancer-merger.png</key>
<key type="filename">sprites/buildings/balancer-splitter-inverse.png</key>
<key type="filename">sprites/buildings/balancer-splitter.png</key>
<key type="filename">sprites/buildings/comparator.png</key>
<key type="filename">sprites/buildings/constant_signal.png</key>
<key type="filename">sprites/buildings/display.png</key>
<key type="filename">sprites/buildings/item_producer.png</key>
<key type="filename">sprites/buildings/lever.png</key>
<key type="filename">sprites/buildings/logic_gate-not.png</key>
<key type="filename">sprites/buildings/logic_gate-or.png</key>
<key type="filename">sprites/buildings/logic_gate-xor.png</key>
<key type="filename">sprites/buildings/logic_gate.png</key>
<key type="filename">sprites/buildings/miner-chainable.png</key>
<key type="filename">sprites/buildings/reader.png</key>
<key type="filename">sprites/buildings/rotater-ccw.png</key>
<key type="filename">sprites/buildings/rotater-rotate180.png</key>
<key type="filename">sprites/buildings/transistor-mirrored.png</key>
<key type="filename">sprites/buildings/transistor.png</key>
<key type="filename">sprites/buildings/underground_belt_entry-tier2.png</key>
<key type="filename">sprites/buildings/underground_belt_entry.png</key>
<key type="filename">sprites/buildings/underground_belt_exit-tier2.png</key>
<key type="filename">sprites/buildings/underground_belt_exit.png</key>
<key type="filename">sprites/buildings/virtual_processor-painter.png</key>
<key type="filename">sprites/buildings/virtual_processor-rotater.png</key>
<key type="filename">sprites/buildings/virtual_processor-stacker.png</key>
<key type="filename">sprites/buildings/virtual_processor-unstacker.png</key>
<key type="filename">sprites/buildings/virtual_processor.png</key>
<key type="filename">sprites/buildings/wire_tunnel.png</key>
<key type="filename">sprites/misc/reader_overlay.png</key>
<key type="filename">sprites/wires/lever_on.png</key>
<key type="filename">sprites/wires/sets/conflict_cross.png</key>
<key type="filename">sprites/wires/sets/conflict_forward.png</key>
<key type="filename">sprites/wires/sets/conflict_split.png</key>
<key type="filename">sprites/wires/sets/conflict_turn.png</key>
<key type="filename">sprites/wires/sets/first_cross.png</key>
<key type="filename">sprites/wires/sets/first_forward.png</key>
<key type="filename">sprites/wires/sets/first_split.png</key>
<key type="filename">sprites/wires/sets/first_turn.png</key>
<key type="filename">sprites/wires/sets/second_cross.png</key>
<key type="filename">sprites/wires/sets/second_forward.png</key>
<key type="filename">sprites/wires/sets/second_split.png</key>
<key type="filename">sprites/wires/sets/second_turn.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>48,48,96,96</rect>
<key>scale9Paddings</key>
<rect>48,48,96,96</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/blueprints/balancer.png</key>
<key type="filename">sprites/blueprints/cutter.png</key>
<key type="filename">sprites/blueprints/filter.png</key>
<key type="filename">sprites/blueprints/mixer.png</key>
<key type="filename">sprites/blueprints/painter-mirrored.png</key>
<key type="filename">sprites/blueprints/painter.png</key>
<key type="filename">sprites/blueprints/stacker.png</key>
<key type="filename">sprites/buildings/balancer.png</key>
<key type="filename">sprites/buildings/filter.png</key>
<key type="filename">sprites/buildings/painter-mirrored.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>96,48,192,96</rect>
<key>scale9Paddings</key>
<rect>96,48,192,96</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/blueprints/cutter-quad.png</key>
<key type="filename">sprites/blueprints/painter-quad.png</key>
<key type="filename">sprites/buildings/cutter-quad.png</key>
<key type="filename">sprites/buildings/painter-quad.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>192,48,384,96</rect>
<key>scale9Paddings</key>
<rect>192,48,384,96</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/blueprints/painter-double.png</key>
<key type="filename">sprites/blueprints/storage.png</key>
<key type="filename">sprites/buildings/painter-double.png</key>
<key type="filename">sprites/buildings/storage.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>96,96,192,192</rect>
<key>scale9Paddings</key>
<rect>96,96,192,192</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/buildings/belt_left.png</key>
<key type="filename">sprites/buildings/belt_right.png</key>
<key type="filename">sprites/buildings/belt_top.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>32,32,63,63</rect>
<key>scale9Paddings</key>
<rect>32,32,63,63</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/buildings/cutter.png</key>
<key type="filename">sprites/buildings/mixer.png</key>
<key type="filename">sprites/buildings/painter.png</key>
<key type="filename">sprites/buildings/stacker.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>64,32,128,64</rect>
<key>scale9Paddings</key>
<rect>64,32,128,64</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/buildings/hub.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>192,192,384,384</rect>
<key>scale9Paddings</key>
<rect>192,192,384,384</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/buildings/miner.png</key>
<key type="filename">sprites/buildings/rotater.png</key>
<key type="filename">sprites/buildings/trash.png</key>
<key type="filename">sprites/misc/processor_disabled.png</key>
<key type="filename">sprites/misc/processor_disconnected.png</key>
<key type="filename">sprites/wires/logical_acceptor.png</key>
<key type="filename">sprites/wires/logical_ejector.png</key>
<key type="filename">sprites/wires/overlay_tile.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>32,32,64,64</rect>
<key>scale9Paddings</key>
<rect>32,32,64,64</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/colors/blue.png</key>
<key type="filename">sprites/colors/cyan.png</key>
<key type="filename">sprites/colors/green.png</key>
<key type="filename">sprites/colors/purple.png</key>
<key type="filename">sprites/colors/red.png</key>
<key type="filename">sprites/colors/uncolored.png</key>
<key type="filename">sprites/colors/white.png</key>
<key type="filename">sprites/colors/yellow.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>18,18,36,36</rect>
<key>scale9Paddings</key>
<rect>18,18,36,36</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/debug/acceptor_slot.png</key>
<key type="filename">sprites/debug/ejector_slot.png</key>
<key type="filename">sprites/misc/hub_direction_indicator.png</key>
<key type="filename">sprites/misc/waypoint.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>8,8,16,16</rect>
<key>scale9Paddings</key>
<rect>8,8,16,16</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/misc/slot_bad_arrow.png</key>
<key type="filename">sprites/misc/slot_good_arrow.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>24,24,48,48</rect>
<key>scale9Paddings</key>
<rect>24,24,48,48</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/misc/storage_overlay.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>44,22,89,43</rect>
<key>scale9Paddings</key>
<rect>44,22,89,43</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/wires/boolean_false.png</key>
<key type="filename">sprites/wires/boolean_true.png</key>
<key type="filename">sprites/wires/network_conflict.png</key>
<key type="filename">sprites/wires/network_empty.png</key>
<key type="filename">sprites/wires/wires_preview.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>16,16,32,32</rect>
<key>scale9Paddings</key>
<rect>16,16,32,32</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/wires/display/blue.png</key>
<key type="filename">sprites/wires/display/cyan.png</key>
<key type="filename">sprites/wires/display/green.png</key>
<key type="filename">sprites/wires/display/purple.png</key>
<key type="filename">sprites/wires/display/red.png</key>
<key type="filename">sprites/wires/display/white.png</key>
<key type="filename">sprites/wires/display/yellow.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>11,11,22,22</rect>
<key>scale9Paddings</key>
<rect>11,11,22,22</rect>
<key>scale9FromFile</key>
<false/>
</struct>
</map>
<key>fileList</key>
<array>
<filename>sprites</filename>
</array>
<key>ignoreFileList</key>
<array/>
<key>replaceList</key>
<array/>
<key>ignoredWarnings</key>
<array/>
<key>commonDivisorX</key>
<uint>1</uint>
<key>commonDivisorY</key>
<uint>1</uint>
<key>packNormalMaps</key>
<false/>
<key>autodetectNormalMaps</key>
<true/>
<key>normalMapFilter</key>
<string></string>
<key>normalMapSuffix</key>
<string></string>
<key>normalMapSheetFileName</key>
<filename></filename>
<key>exporterProperties</key>
<map type="ExporterProperties"/>
</struct>
</data>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -14,6 +14,7 @@
"vetur.format.defaultFormatter.js": "vscode-typescript",
"vetur.format.defaultFormatter.ts": "vscode-typescript",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
"editor.formatOnSave": true,
"files.trimTrailingWhitespace": true
}
}

View File

@ -27,7 +27,7 @@
@include S(border-radius, $globalBorderRadius);
@include DarkThemeOverride {
background-color: rgba(darken($darkModeGameBackground, 15), 0.4);
background-color: rgba(darken($darkModeGameBackground, 15), 0.95);
}
&.secondary {

View File

@ -56,8 +56,9 @@
.helperGif {
@include S(margin-top, 5px);
@include S(width, 150px);
@include S(height, 150px);
background: center center / contain no-repeat;
background: center center / cover no-repeat;
transition: opacity 0.1s ease-out;
}
}

View File

@ -17,13 +17,10 @@
grid-template-rows: 1fr 1fr;
@include S(margin-bottom, 4px);
color: #333438;
// text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2);
&.unpinable {
> canvas {
cursor: pointer;
pointer-events: all;
}
&.removable {
cursor: pointer;
pointer-events: all;
}
> canvas {
@ -31,16 +28,9 @@
@include S(height, 25px);
grid-column: 1 / 2;
grid-row: 1 / 3;
pointer-events: all;
transition: transform 0.1s ease-in-out;
transform-origin: D(2px) center;
will-change: transform;
position: relative;
pointer-events: none;
z-index: 20;
&:hover {
transform: scale(2);
z-index: 21;
}
position: relative;
}
> .amountLabel,

View File

@ -4,9 +4,7 @@
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
overflow: auto;
pointer-events: all;
& {
@ -33,7 +31,6 @@
display: flex;
align-items: center;
flex-direction: column;
max-height: 100vh;
color: #fff;
text-align: center;

View File

@ -73,8 +73,8 @@ ingame_HUD_KeybindingOverlay,
ingame_HUD_Notifications,
ingame_HUD_DebugInfo,
ingame_HUD_EntityDebugger,
ingame_HUD_InteractiveTutorial,
ingame_HUD_TutorialHints,
ingame_HUD_InteractiveTutorial,
ingame_HUD_BuildingsToolbar,
ingame_HUD_wires_toolbar,
ingame_HUD_BlueprintPlacer,

View File

@ -29,6 +29,7 @@ import { MobileWarningState } from "./states/mobile_warning";
import { PreloadState } from "./states/preload";
import { SettingsState } from "./states/settings";
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
import { RestrictionManager } from "./core/restriction_manager";
/**
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
@ -70,6 +71,9 @@ export class Application {
this.inputMgr = new InputDistributor(this);
this.backgroundResourceLoader = new BackgroundResourcesLoader(this);
// Restrictions (Like demo etc)
this.restrictionMgr = new RestrictionManager(this);
// Platform dependent stuff
/** @type {StorageInterface} */

View File

@ -1,4 +1,18 @@
export const CHANGELOG = [
{
version: "1.2.1",
date: "unreleased",
entries: [
"Fixed stacking bug for level 26 which required restarting the game",
"Fix reward notification being too long sometimes (by LeopoldTal)",
"Use locale decimal separator on belt reader display (by LeopoldTal)",
"Vastly improved performance when saving games (by LeopoldTal)",
"Prevent some antivirus programs blocking the opening of external links (by LeopoldTal)",
"Match tutorials to the correct painter variants (by LeopoldTal)",
"Prevent throughput goals containing fractional numbers (by CEbbinghaus)",
"Updated translations and added Hungarian",
],
},
{
version: "1.2.0",
date: "09.10.2020",

View File

@ -1,467 +1,465 @@
import { createLogger } from "../core/logging";
import { Signal } from "../core/signal";
import { fastArrayDelete, fastArrayDeleteValueIfContained } from "./utils";
import { Vector } from "./vector";
import { IS_MOBILE, SUPPORT_TOUCH } from "./config";
import { SOUNDS } from "../platform/sound";
import { GLOBAL_APP } from "./globals";
const logger = createLogger("click_detector");
export const MAX_MOVE_DISTANCE_PX = IS_MOBILE ? 20 : 80;
// For debugging
const registerClickDetectors = G_IS_DEV && true;
if (registerClickDetectors) {
/** @type {Array<ClickDetector>} */
window.activeClickDetectors = [];
}
// Store active click detectors so we can cancel them
/** @type {Array<ClickDetector>} */
const ongoingClickDetectors = [];
// Store when the last touch event was registered, to avoid accepting a touch *and* a click event
export let clickDetectorGlobals = {
lastTouchTime: -1000,
};
/**
* Click detector creation payload typehints
* @typedef {{
* consumeEvents?: boolean,
* preventDefault?: boolean,
* applyCssClass?: string,
* captureTouchmove?: boolean,
* targetOnly?: boolean,
* maxDistance?: number,
* clickSound?: string,
* preventClick?: boolean,
* }} ClickDetectorConstructorArgs
*/
// Detects clicks
export class ClickDetector {
/**
*
* @param {Element} element
* @param {object} param1
* @param {boolean=} param1.consumeEvents Whether to call stopPropagation
* (Useful for nested elements where the parent has a click handler as wel)
* @param {boolean=} param1.preventDefault Whether to call preventDefault (Usually makes the handler faster)
* @param {string=} param1.applyCssClass The css class to add while the element is pressed
* @param {boolean=} param1.captureTouchmove Whether to capture touchmove events as well
* @param {boolean=} param1.targetOnly Whether to also accept clicks on child elements (e.target !== element)
* @param {number=} param1.maxDistance The maximum distance in pixels to accept clicks
* @param {string=} param1.clickSound Sound key to play on touchdown
* @param {boolean=} param1.preventClick Whether to prevent click events
*/
constructor(
element,
{
consumeEvents = false,
preventDefault = true,
applyCssClass = "pressed",
captureTouchmove = false,
targetOnly = false,
maxDistance = MAX_MOVE_DISTANCE_PX,
clickSound = SOUNDS.uiClick,
preventClick = false,
}
) {
assert(element, "No element given!");
this.clickDownPosition = null;
this.consumeEvents = consumeEvents;
this.preventDefault = preventDefault;
this.applyCssClass = applyCssClass;
this.captureTouchmove = captureTouchmove;
this.targetOnly = targetOnly;
this.clickSound = clickSound;
this.maxDistance = maxDistance;
this.preventClick = preventClick;
// Signals
this.click = new Signal();
this.rightClick = new Signal();
this.touchstart = new Signal();
this.touchmove = new Signal();
this.touchend = new Signal();
this.touchcancel = new Signal();
// Simple signals which just receive the touch position
this.touchstartSimple = new Signal();
this.touchmoveSimple = new Signal();
this.touchendSimple = new Signal();
// Store time of touch start
this.clickStartTime = null;
// A click can be cancelled if another detector registers a click
this.cancelled = false;
this.internalBindTo(/** @type {HTMLElement} */ (element));
}
/**
* Cleans up all event listeners of this detector
*/
cleanup() {
if (this.element) {
if (registerClickDetectors) {
const index = window.activeClickDetectors.indexOf(this);
if (index < 0) {
logger.error("Click detector cleanup but is not active");
} else {
window.activeClickDetectors.splice(index, 1);
}
}
const options = this.internalGetEventListenerOptions();
if (SUPPORT_TOUCH) {
this.element.removeEventListener("touchstart", this.handlerTouchStart, options);
this.element.removeEventListener("touchend", this.handlerTouchEnd, options);
this.element.removeEventListener("touchcancel", this.handlerTouchCancel, options);
}
this.element.removeEventListener("mouseup", this.handlerTouchStart, options);
this.element.removeEventListener("mousedown", this.handlerTouchEnd, options);
this.element.removeEventListener("mouseout", this.handlerTouchCancel, options);
if (this.captureTouchmove) {
if (SUPPORT_TOUCH) {
this.element.removeEventListener("touchmove", this.handlerTouchMove, options);
}
this.element.removeEventListener("mousemove", this.handlerTouchMove, options);
}
if (this.preventClick) {
this.element.removeEventListener("click", this.handlerPreventClick, options);
}
this.click.removeAll();
this.touchstart.removeAll();
this.touchmove.removeAll();
this.touchend.removeAll();
this.touchcancel.removeAll();
// TODO: Remove pointer captures
this.element = null;
}
}
// INTERNAL METHODS
/**
*
* @param {Event} event
*/
internalPreventClick(event) {
window.focus();
event.preventDefault();
}
/**
* Internal method to get the options to pass to an event listener
*/
internalGetEventListenerOptions() {
return {
capture: this.consumeEvents,
passive: !this.preventDefault,
};
}
/**
* Binds the click detector to an element
* @param {HTMLElement} element
*/
internalBindTo(element) {
const options = this.internalGetEventListenerOptions();
this.handlerTouchStart = this.internalOnPointerDown.bind(this);
this.handlerTouchEnd = this.internalOnPointerEnd.bind(this);
this.handlerTouchMove = this.internalOnPointerMove.bind(this);
this.handlerTouchCancel = this.internalOnTouchCancel.bind(this);
if (this.preventClick) {
this.handlerPreventClick = this.internalPreventClick.bind(this);
element.addEventListener("click", this.handlerPreventClick, options);
}
if (SUPPORT_TOUCH) {
element.addEventListener("touchstart", this.handlerTouchStart, options);
element.addEventListener("touchend", this.handlerTouchEnd, options);
element.addEventListener("touchcancel", this.handlerTouchCancel, options);
}
element.addEventListener("mousedown", this.handlerTouchStart, options);
element.addEventListener("mouseup", this.handlerTouchEnd, options);
element.addEventListener("mouseout", this.handlerTouchCancel, options);
if (this.captureTouchmove) {
if (SUPPORT_TOUCH) {
element.addEventListener("touchmove", this.handlerTouchMove, options);
}
element.addEventListener("mousemove", this.handlerTouchMove, options);
}
if (registerClickDetectors) {
window.activeClickDetectors.push(this);
}
this.element = element;
}
/**
* Returns if the bound element is currently in the DOM.
*/
internalIsDomElementAttached() {
return this.element && document.documentElement.contains(this.element);
}
/**
* Checks if the given event is relevant for this detector
* @param {TouchEvent|MouseEvent} event
*/
internalEventPreHandler(event, expectedRemainingTouches = 1) {
if (!this.element) {
// Already cleaned up
return false;
}
if (this.targetOnly && event.target !== this.element) {
// Clicked a child element
return false;
}
// Stop any propagation and defaults if configured
if (this.consumeEvents && event.cancelable) {
event.stopPropagation();
}
if (this.preventDefault && event.cancelable) {
event.preventDefault();
}
if (window.TouchEvent && event instanceof TouchEvent) {
clickDetectorGlobals.lastTouchTime = performance.now();
// console.log("Got touches", event.targetTouches.length, "vs", expectedRemainingTouches);
if (event.targetTouches.length !== expectedRemainingTouches) {
return false;
}
}
if (event instanceof MouseEvent) {
if (performance.now() - clickDetectorGlobals.lastTouchTime < 1000.0) {
return false;
}
}
return true;
}
/**
* Extracts the mous position from an event
* @param {TouchEvent|MouseEvent} event
* @returns {Vector} The client space position
*/
static extractPointerPosition(event) {
if (window.TouchEvent && event instanceof TouchEvent) {
if (event.changedTouches.length !== 1) {
logger.warn(
"Got unexpected target touches:",
event.targetTouches.length,
"->",
event.targetTouches
);
return new Vector(0, 0);
}
const touch = event.changedTouches[0];
return new Vector(touch.clientX, touch.clientY);
}
if (event instanceof MouseEvent) {
return new Vector(event.clientX, event.clientY);
}
assertAlways(false, "Got unknown event: " + event);
return new Vector(0, 0);
}
/**
* Cacnels all ongoing events on this detector
*/
cancelOngoingEvents() {
if (this.applyCssClass && this.element) {
this.element.classList.remove(this.applyCssClass);
}
this.clickDownPosition = null;
this.clickStartTime = null;
this.cancelled = true;
fastArrayDeleteValueIfContained(ongoingClickDetectors, this);
}
/**
* Internal pointer down handler
* @param {TouchEvent|MouseEvent} event
*/
internalOnPointerDown(event) {
window.focus();
if (!this.internalEventPreHandler(event, 1)) {
return false;
}
const position = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
if (event instanceof MouseEvent) {
const isRightClick = event.button === 2;
if (isRightClick) {
// Ignore right clicks
this.rightClick.dispatch(position, event);
this.cancelled = true;
this.clickDownPosition = null;
return;
}
}
if (this.clickDownPosition) {
logger.warn("Ignoring double click");
return false;
}
this.cancelled = false;
this.touchstart.dispatch(event);
// Store where the touch started
this.clickDownPosition = position;
this.clickStartTime = performance.now();
this.touchstartSimple.dispatch(this.clickDownPosition.x, this.clickDownPosition.y);
// If we are not currently within a click, register it
if (ongoingClickDetectors.indexOf(this) < 0) {
ongoingClickDetectors.push(this);
} else {
logger.warn("Click detector got pointer down of active pointer twice");
}
// If we should apply any classes, do this now
if (this.applyCssClass) {
this.element.classList.add(this.applyCssClass);
}
// If we should play any sound, do this
if (this.clickSound) {
GLOBAL_APP.sound.playUiSound(this.clickSound);
}
return false;
}
/**
* Internal pointer move handler
* @param {TouchEvent|MouseEvent} event
*/
internalOnPointerMove(event) {
if (!this.internalEventPreHandler(event, 1)) {
return false;
}
this.touchmove.dispatch(event);
const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
this.touchmoveSimple.dispatch(pos.x, pos.y);
return false;
}
/**
* Internal pointer end handler
* @param {TouchEvent|MouseEvent} event
*/
internalOnPointerEnd(event) {
window.focus();
if (!this.internalEventPreHandler(event, 0)) {
return false;
}
if (this.cancelled) {
// warn(this, "Not dispatching touchend on cancelled listener");
return false;
}
if (event instanceof MouseEvent) {
const isRightClick = event.button === 2;
if (isRightClick) {
return;
}
}
const index = ongoingClickDetectors.indexOf(this);
if (index < 0) {
logger.warn("Got pointer end but click detector is not in pressed state");
} else {
fastArrayDelete(ongoingClickDetectors, index);
}
let dispatchClick = false;
let dispatchClickPos = null;
// Check for correct down position, otherwise must have pinched or so
if (this.clickDownPosition) {
const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
const distance = pos.distance(this.clickDownPosition);
if (!IS_MOBILE || distance <= this.maxDistance) {
dispatchClick = true;
dispatchClickPos = pos;
} else {
console.warn("[ClickDetector] Touch does not count as click:", "(was", distance, ")");
}
}
this.clickDownPosition = null;
this.clickStartTime = null;
if (this.applyCssClass) {
this.element.classList.remove(this.applyCssClass);
}
// Dispatch in the end to avoid the element getting invalidated
// Also make sure that the element is still in the dom
if (this.internalIsDomElementAttached()) {
this.touchend.dispatch(event);
this.touchendSimple.dispatch();
if (dispatchClick) {
const detectors = ongoingClickDetectors.slice();
for (let i = 0; i < detectors.length; ++i) {
detectors[i].cancelOngoingEvents();
}
this.click.dispatch(dispatchClickPos, event);
}
}
return false;
}
/**
* Internal touch cancel handler
* @param {TouchEvent|MouseEvent} event
*/
internalOnTouchCancel(event) {
if (!this.internalEventPreHandler(event, 0)) {
return false;
}
if (this.cancelled) {
// warn(this, "Not dispatching touchcancel on cancelled listener");
return false;
}
this.cancelOngoingEvents();
this.touchcancel.dispatch(event);
this.touchendSimple.dispatch(event);
return false;
}
}
import { createLogger } from "../core/logging";
import { Signal } from "../core/signal";
import { fastArrayDelete, fastArrayDeleteValueIfContained } from "./utils";
import { Vector } from "./vector";
import { IS_MOBILE, SUPPORT_TOUCH } from "./config";
import { SOUNDS } from "../platform/sound";
import { GLOBAL_APP } from "./globals";
const logger = createLogger("click_detector");
export const MAX_MOVE_DISTANCE_PX = IS_MOBILE ? 20 : 80;
// For debugging
const registerClickDetectors = G_IS_DEV && true;
if (registerClickDetectors) {
/** @type {Array<ClickDetector>} */
window.activeClickDetectors = [];
}
// Store active click detectors so we can cancel them
/** @type {Array<ClickDetector>} */
const ongoingClickDetectors = [];
// Store when the last touch event was registered, to avoid accepting a touch *and* a click event
export let clickDetectorGlobals = {
lastTouchTime: -1000,
};
/**
* Click detector creation payload typehints
* @typedef {{
* consumeEvents?: boolean,
* preventDefault?: boolean,
* applyCssClass?: string,
* captureTouchmove?: boolean,
* targetOnly?: boolean,
* maxDistance?: number,
* clickSound?: string,
* preventClick?: boolean,
* }} ClickDetectorConstructorArgs
*/
// Detects clicks
export class ClickDetector {
/**
*
* @param {Element} element
* @param {object} param1
* @param {boolean=} param1.consumeEvents Whether to call stopPropagation
* (Useful for nested elements where the parent has a click handler as wel)
* @param {boolean=} param1.preventDefault Whether to call preventDefault (Usually makes the handler faster)
* @param {string=} param1.applyCssClass The css class to add while the element is pressed
* @param {boolean=} param1.captureTouchmove Whether to capture touchmove events as well
* @param {boolean=} param1.targetOnly Whether to also accept clicks on child elements (e.target !== element)
* @param {number=} param1.maxDistance The maximum distance in pixels to accept clicks
* @param {string=} param1.clickSound Sound key to play on touchdown
* @param {boolean=} param1.preventClick Whether to prevent click events
*/
constructor(
element,
{
consumeEvents = false,
preventDefault = true,
applyCssClass = "pressed",
captureTouchmove = false,
targetOnly = false,
maxDistance = MAX_MOVE_DISTANCE_PX,
clickSound = SOUNDS.uiClick,
preventClick = false,
}
) {
assert(element, "No element given!");
this.clickDownPosition = null;
this.consumeEvents = consumeEvents;
this.preventDefault = preventDefault;
this.applyCssClass = applyCssClass;
this.captureTouchmove = captureTouchmove;
this.targetOnly = targetOnly;
this.clickSound = clickSound;
this.maxDistance = maxDistance;
this.preventClick = preventClick;
// Signals
this.click = new Signal();
this.rightClick = new Signal();
this.touchstart = new Signal();
this.touchmove = new Signal();
this.touchend = new Signal();
this.touchcancel = new Signal();
// Simple signals which just receive the touch position
this.touchstartSimple = new Signal();
this.touchmoveSimple = new Signal();
this.touchendSimple = new Signal();
// Store time of touch start
this.clickStartTime = null;
// A click can be cancelled if another detector registers a click
this.cancelled = false;
this.internalBindTo(/** @type {HTMLElement} */ (element));
}
/**
* Cleans up all event listeners of this detector
*/
cleanup() {
if (this.element) {
if (registerClickDetectors) {
const index = window.activeClickDetectors.indexOf(this);
if (index < 0) {
logger.error("Click detector cleanup but is not active");
} else {
window.activeClickDetectors.splice(index, 1);
}
}
const options = this.internalGetEventListenerOptions();
if (SUPPORT_TOUCH) {
this.element.removeEventListener("touchstart", this.handlerTouchStart, options);
this.element.removeEventListener("touchend", this.handlerTouchEnd, options);
this.element.removeEventListener("touchcancel", this.handlerTouchCancel, options);
}
this.element.removeEventListener("mouseup", this.handlerTouchStart, options);
this.element.removeEventListener("mousedown", this.handlerTouchEnd, options);
this.element.removeEventListener("mouseout", this.handlerTouchCancel, options);
if (this.captureTouchmove) {
if (SUPPORT_TOUCH) {
this.element.removeEventListener("touchmove", this.handlerTouchMove, options);
}
this.element.removeEventListener("mousemove", this.handlerTouchMove, options);
}
if (this.preventClick) {
this.element.removeEventListener("click", this.handlerPreventClick, options);
}
this.click.removeAll();
this.touchstart.removeAll();
this.touchmove.removeAll();
this.touchend.removeAll();
this.touchcancel.removeAll();
this.element = null;
}
}
// INTERNAL METHODS
/**
*
* @param {Event} event
*/
internalPreventClick(event) {
window.focus();
event.preventDefault();
}
/**
* Internal method to get the options to pass to an event listener
*/
internalGetEventListenerOptions() {
return {
capture: this.consumeEvents,
passive: !this.preventDefault,
};
}
/**
* Binds the click detector to an element
* @param {HTMLElement} element
*/
internalBindTo(element) {
const options = this.internalGetEventListenerOptions();
this.handlerTouchStart = this.internalOnPointerDown.bind(this);
this.handlerTouchEnd = this.internalOnPointerEnd.bind(this);
this.handlerTouchMove = this.internalOnPointerMove.bind(this);
this.handlerTouchCancel = this.internalOnTouchCancel.bind(this);
if (this.preventClick) {
this.handlerPreventClick = this.internalPreventClick.bind(this);
element.addEventListener("click", this.handlerPreventClick, options);
}
if (SUPPORT_TOUCH) {
element.addEventListener("touchstart", this.handlerTouchStart, options);
element.addEventListener("touchend", this.handlerTouchEnd, options);
element.addEventListener("touchcancel", this.handlerTouchCancel, options);
}
element.addEventListener("mousedown", this.handlerTouchStart, options);
element.addEventListener("mouseup", this.handlerTouchEnd, options);
element.addEventListener("mouseout", this.handlerTouchCancel, options);
if (this.captureTouchmove) {
if (SUPPORT_TOUCH) {
element.addEventListener("touchmove", this.handlerTouchMove, options);
}
element.addEventListener("mousemove", this.handlerTouchMove, options);
}
if (registerClickDetectors) {
window.activeClickDetectors.push(this);
}
this.element = element;
}
/**
* Returns if the bound element is currently in the DOM.
*/
internalIsDomElementAttached() {
return this.element && document.documentElement.contains(this.element);
}
/**
* Checks if the given event is relevant for this detector
* @param {TouchEvent|MouseEvent} event
*/
internalEventPreHandler(event, expectedRemainingTouches = 1) {
if (!this.element) {
// Already cleaned up
return false;
}
if (this.targetOnly && event.target !== this.element) {
// Clicked a child element
return false;
}
// Stop any propagation and defaults if configured
if (this.consumeEvents && event.cancelable) {
event.stopPropagation();
}
if (this.preventDefault && event.cancelable) {
event.preventDefault();
}
if (window.TouchEvent && event instanceof TouchEvent) {
clickDetectorGlobals.lastTouchTime = performance.now();
// console.log("Got touches", event.targetTouches.length, "vs", expectedRemainingTouches);
if (event.targetTouches.length !== expectedRemainingTouches) {
return false;
}
}
if (event instanceof MouseEvent) {
if (performance.now() - clickDetectorGlobals.lastTouchTime < 1000.0) {
return false;
}
}
return true;
}
/**
* Extracts the mous position from an event
* @param {TouchEvent|MouseEvent} event
* @returns {Vector} The client space position
*/
static extractPointerPosition(event) {
if (window.TouchEvent && event instanceof TouchEvent) {
if (event.changedTouches.length !== 1) {
logger.warn(
"Got unexpected target touches:",
event.targetTouches.length,
"->",
event.targetTouches
);
return new Vector(0, 0);
}
const touch = event.changedTouches[0];
return new Vector(touch.clientX, touch.clientY);
}
if (event instanceof MouseEvent) {
return new Vector(event.clientX, event.clientY);
}
assertAlways(false, "Got unknown event: " + event);
return new Vector(0, 0);
}
/**
* Cacnels all ongoing events on this detector
*/
cancelOngoingEvents() {
if (this.applyCssClass && this.element) {
this.element.classList.remove(this.applyCssClass);
}
this.clickDownPosition = null;
this.clickStartTime = null;
this.cancelled = true;
fastArrayDeleteValueIfContained(ongoingClickDetectors, this);
}
/**
* Internal pointer down handler
* @param {TouchEvent|MouseEvent} event
*/
internalOnPointerDown(event) {
window.focus();
if (!this.internalEventPreHandler(event, 1)) {
return false;
}
const position = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
if (event instanceof MouseEvent) {
const isRightClick = event.button === 2;
if (isRightClick) {
// Ignore right clicks
this.rightClick.dispatch(position, event);
this.cancelled = true;
this.clickDownPosition = null;
return;
}
}
if (this.clickDownPosition) {
logger.warn("Ignoring double click");
return false;
}
this.cancelled = false;
this.touchstart.dispatch(event);
// Store where the touch started
this.clickDownPosition = position;
this.clickStartTime = performance.now();
this.touchstartSimple.dispatch(this.clickDownPosition.x, this.clickDownPosition.y);
// If we are not currently within a click, register it
if (ongoingClickDetectors.indexOf(this) < 0) {
ongoingClickDetectors.push(this);
} else {
logger.warn("Click detector got pointer down of active pointer twice");
}
// If we should apply any classes, do this now
if (this.applyCssClass) {
this.element.classList.add(this.applyCssClass);
}
// If we should play any sound, do this
if (this.clickSound) {
GLOBAL_APP.sound.playUiSound(this.clickSound);
}
return false;
}
/**
* Internal pointer move handler
* @param {TouchEvent|MouseEvent} event
*/
internalOnPointerMove(event) {
if (!this.internalEventPreHandler(event, 1)) {
return false;
}
this.touchmove.dispatch(event);
const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
this.touchmoveSimple.dispatch(pos.x, pos.y);
return false;
}
/**
* Internal pointer end handler
* @param {TouchEvent|MouseEvent} event
*/
internalOnPointerEnd(event) {
window.focus();
if (!this.internalEventPreHandler(event, 0)) {
return false;
}
if (this.cancelled) {
// warn(this, "Not dispatching touchend on cancelled listener");
return false;
}
if (event instanceof MouseEvent) {
const isRightClick = event.button === 2;
if (isRightClick) {
return;
}
}
const index = ongoingClickDetectors.indexOf(this);
if (index < 0) {
logger.warn("Got pointer end but click detector is not in pressed state");
} else {
fastArrayDelete(ongoingClickDetectors, index);
}
let dispatchClick = false;
let dispatchClickPos = null;
// Check for correct down position, otherwise must have pinched or so
if (this.clickDownPosition) {
const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
const distance = pos.distance(this.clickDownPosition);
if (!IS_MOBILE || distance <= this.maxDistance) {
dispatchClick = true;
dispatchClickPos = pos;
} else {
console.warn("[ClickDetector] Touch does not count as click:", "(was", distance, ")");
}
}
this.clickDownPosition = null;
this.clickStartTime = null;
if (this.applyCssClass) {
this.element.classList.remove(this.applyCssClass);
}
// Dispatch in the end to avoid the element getting invalidated
// Also make sure that the element is still in the dom
if (this.internalIsDomElementAttached()) {
this.touchend.dispatch(event);
this.touchendSimple.dispatch();
if (dispatchClick) {
const detectors = ongoingClickDetectors.slice();
for (let i = 0; i < detectors.length; ++i) {
detectors[i].cancelOngoingEvents();
}
this.click.dispatch(dispatchClickPos, event);
}
}
return false;
}
/**
* Internal touch cancel handler
* @param {TouchEvent|MouseEvent} event
*/
internalOnTouchCancel(event) {
if (!this.internalEventPreHandler(event, 0)) {
return false;
}
if (this.cancelled) {
// warn(this, "Not dispatching touchcancel on cancelled listener");
return false;
}
this.cancelOngoingEvents();
this.touchcancel.dispatch(event);
this.touchendSimple.dispatch(event);
return false;
}
}

View File

@ -1,5 +1,3 @@
import { queryParamOptions } from "./query_parameters";
export const IS_DEBUG =
G_IS_DEV &&
typeof window !== "undefined" &&
@ -7,13 +5,10 @@ export const IS_DEBUG =
(window.location.host.indexOf("localhost:") >= 0 || window.location.host.indexOf("192.168.0.") >= 0) &&
window.location.search.indexOf("nodebug") < 0;
export const IS_DEMO = queryParamOptions.fullVersion
? false
: (!G_IS_DEV && !G_IS_STANDALONE) ||
(typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0);
export const SUPPORT_TOUCH = false;
export const IS_MAC = navigator.platform.toLowerCase().indexOf("mac") >= 0;
const smoothCanvas = true;
export const THIRDPARTY_URLS = {
@ -23,9 +18,16 @@ export const THIRDPARTY_URLS = {
shapeViewer: "https://viewer.shapez.io",
standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/",
levelTutorialVideos: {
21: "https://www.youtube.com/watch?v=0nUfRLMCcgo&",
25: "https://www.youtube.com/watch?v=7OCV1g40Iew&",
26: "https://www.youtube.com/watch?v=gfm6dS1dCoY",
},
};
export const A_B_TESTING_LINK_TYPE = Math.random() > 0.5 ? "steam_1_pr" : "steam_2_npr";
// export const A_B_TESTING_LINK_TYPE = Math.random() > 0.95 ? "steam_1_pr" : "steam_2_npr";
export const A_B_TESTING_LINK_TYPE = "steam_2_npr";
export const globalConfig = {
// Size of a single tile in Pixels.
@ -64,7 +66,7 @@ export const globalConfig = {
undergroundBeltMaxTilesByTier: [5, 9],
readerAnalyzeIntervalSeconds: G_IS_DEV ? 3 : 10,
readerAnalyzeIntervalSeconds: 10,
buildingSpeeds: {
cutter: 1 / 4,
@ -137,3 +139,8 @@ if (G_IS_DEV && globalConfig.debug.renderForTrailer) {
if (globalConfig.debug.fastGameEnter) {
globalConfig.debug.noArtificialDelays = true;
}
if (G_IS_DEV && globalConfig.debug.noArtificialDelays) {
globalConfig.warmupTimeSecondsFast = 0;
globalConfig.warmupTimeSecondsRegular = 0;
}

View File

@ -1,26 +1,25 @@
import { globalConfig } from "./config";
/**
* @typedef {import("../game/root").GameRoot} GameRoot
* @typedef {import("./rectangle").Rectangle} Rectangle
*/
export class DrawParameters {
constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) {
/** @type {CanvasRenderingContext2D} */
this.context = context;
/** @type {Rectangle} */
this.visibleRect = visibleRect;
/** @type {string} */
this.desiredAtlasScale = desiredAtlasScale;
/** @type {number} */
this.zoomLevel = zoomLevel;
// FIXME: Not really nice
/** @type {GameRoot} */
this.root = root;
}
}
import { globalConfig } from "./config";
/**
* @typedef {import("../game/root").GameRoot} GameRoot
* @typedef {import("./rectangle").Rectangle} Rectangle
*/
export class DrawParameters {
constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) {
/** @type {CanvasRenderingContext2D} */
this.context = context;
/** @type {Rectangle} */
this.visibleRect = visibleRect;
/** @type {string} */
this.desiredAtlasScale = desiredAtlasScale;
/** @type {number} */
this.zoomLevel = zoomLevel;
/** @type {GameRoot} */
this.root = root;
}
}

View File

@ -13,6 +13,17 @@ import { getStringForKeyCode } from "../game/key_action_mapper";
import { createLogger } from "./logging";
import { T } from "../translations";
/*
* ***************************************************
*
* LEGACY CODE WARNING
*
* This is old code from yorg3.io and needs to be refactored
* @TODO
*
* ***************************************************
*/
const kbEnter = 13;
const kbCancel = 27;

View File

@ -2,6 +2,17 @@ import { BaseItem } from "../game/base_item";
import { ClickDetector } from "./click_detector";
import { Signal } from "./signal";
/*
* ***************************************************
*
* LEGACY CODE WARNING
*
* This is old code from yorg3.io and needs to be refactored
* @TODO
*
* ***************************************************
*/
export class FormElement {
constructor(id, label) {
this.id = id;

View File

@ -81,10 +81,6 @@ export class ReadWriteProxy {
return this.writeAsync();
}
getCurrentData() {
return this.currentData;
}
/**
*
* @param {object} obj

View File

@ -0,0 +1,154 @@
/* typehints:start */
import { Application } from "../application";
/* typehints:end */
import { IS_MAC } from "./config";
import { ExplainedResult } from "./explained_result";
import { queryParamOptions } from "./query_parameters";
import { ReadWriteProxy } from "./read_write_proxy";
export class RestrictionManager extends ReadWriteProxy {
/**
* @param {Application} app
*/
constructor(app) {
super(app, "restriction-flags.bin");
this.currentData = this.getDefaultData();
}
// -- RW Proxy Impl
/**
* @param {any} data
*/
verify(data) {
return ExplainedResult.good();
}
/**
*/
getDefaultData() {
return {
version: this.getCurrentVersion(),
savegameV1119Imported: false,
};
}
/**
*/
getCurrentVersion() {
return 1;
}
/**
* @param {any} data
*/
migrate(data) {
return ExplainedResult.good();
}
initialize() {
return this.readAsync().then(() => {
if (this.currentData.savegameV1119Imported) {
console.warn("Levelunlock is granted to current user due to past savegame");
}
});
}
// -- End RW Proxy Impl
/**
* Checks if there are any savegames from the 1.1.19 version
*/
onHasLegacySavegamesChanged(has119Savegames = false) {
if (has119Savegames && !this.currentData.savegameV1119Imported) {
this.currentData.savegameV1119Imported = true;
console.warn("Current user now has access to all levels due to 1119 savegame");
return this.writeAsync();
}
return Promise.resolve();
}
/**
* Returns if the app is currently running as the limited version
* @returns {boolean}
*/
isLimitedVersion() {
if (IS_MAC) {
// On mac, the full version is always active
return false;
}
if (G_IS_STANDALONE) {
// Standalone is never limited
return false;
}
if (queryParamOptions.fullVersion) {
// Full version is activated via flag
return false;
}
if (G_IS_DEV) {
return typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0;
}
return true;
}
/**
* Returns if the app markets the standalone version on steam
* @returns {boolean}
*/
getIsStandaloneMarketingActive() {
return this.isLimitedVersion();
}
/**
* Returns if exporting the base as a screenshot is possible
* @returns {boolean}
*/
getIsExportingScreenshotsPossible() {
return !this.isLimitedVersion();
}
/**
* Returns the maximum number of supported waypoints
* @returns {number}
*/
getMaximumWaypoints() {
return this.isLimitedVersion() ? 2 : 1e20;
}
/**
* Returns if the user has unlimited savegames
* @returns {boolean}
*/
getHasUnlimitedSavegames() {
return !this.isLimitedVersion();
}
/**
* Returns if the app has all settings available
* @returns {boolean}
*/
getHasExtendedSettings() {
return !this.isLimitedVersion();
}
/**
* Returns if all upgrades are available
* @returns {boolean}
*/
getHasExtendedUpgrades() {
return !this.isLimitedVersion() || this.currentData.savegameV1119Imported;
}
/**
* Returns if all levels & freeplay is available
* @returns {boolean}
*/
getHasExtendedLevelsAndFreeplay() {
return !this.isLimitedVersion() || this.currentData.savegameV1119Imported;
}
}

View File

@ -108,17 +108,6 @@ export class RandomNumberGenerator {
assert(max > min, "rng: max <= min");
return Math.floor(this.next() * (max - min) + min);
}
/**
* @param {number} min
* @param {number} max
* @returns {number} Integer in range [min, max]
*/
nextIntRangeInclusive(min, max) {
assert(Number.isFinite(min), "Minimum is no integer");
assert(Number.isFinite(max), "Maximum is no integer");
assert(max > min, "rng: max <= min");
return Math.round(this.next() * (max - min) + min);
}
/**
* @param {number} min

View File

@ -558,7 +558,16 @@ export function formatSeconds(secs) {
}
/**
* Formats a number like 2.5 to "2.5 items / s"
* Formats a number like 2.51 to "2.5"
* @param {number} speed
* @param {string=} separator The decimal separator for numbers like 50.1 (separator='.')
*/
export function round1DigitLocalized(speed, separator = T.global.decimalSeparator) {
return round1Digit(speed).toString().replace(".", separator);
}
/**
* Formats a number like 2.51 to "2.51 items / s"
* @param {number} speed
* @param {boolean=} double
* @param {string=} separator The decimal separator for numbers like 50.1 (separator='.')
@ -681,3 +690,72 @@ export function fillInLinkIntoTranslation(translation, link) {
.replace("<link>", "<a href='" + link + "' target='_blank'>")
.replace("</link>", "</a>");
}
/**
* Generates a file download
* @param {string} filename
* @param {string} text
*/
export function generateFileDownload(filename, text) {
var element = document.createElement("a");
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
/**
* Starts a file chooser
* @param {string} acceptedType
*/
export function startFileChoose(acceptedType = ".bin") {
var input = document.createElement("input");
input.type = "file";
input.accept = acceptedType;
return new Promise(resolve => {
input.onchange = _ => resolve(input.files[0]);
input.click();
});
}
const romanLiterals = [
"0", // NULL
"I",
"II",
"III",
"IV",
"V",
"VI",
"VII",
"VIII",
"IX",
"X",
"XI",
"XII",
"XIII",
"XIV",
"XV",
"XVI",
"XVII",
"XVIII",
"XIX",
"XX",
];
/**
*
* @param {number} number
* @returns {string}
*/
export function getRomanNumber(number) {
number = Math.max(0, Math.round(number));
if (number < romanLiterals.length) {
return romanLiterals[number];
}
return String(number);
}

View File

@ -1111,7 +1111,7 @@ export class BeltPath extends BasicSerializableObject {
isFirstItemProcessed = false;
this.spacingToFirstItem += clampedProgress;
if (remainingVelocity < 0.01) {
if (remainingVelocity < 1e-7) {
break;
}
}

View File

@ -1,13 +1,9 @@
import { globalConfig } from "../core/config";
import { DrawParameters } from "../core/draw_parameters";
import { createLogger } from "../core/logging";
import { findNiceIntegerValue } from "../core/utils";
import { Vector } from "../core/vector";
import { Entity } from "./entity";
import { GameRoot } from "./root";
import { blueprintShape } from "./upgrades";
const logger = createLogger("blueprint");
export class Blueprint {
/**
@ -142,7 +138,7 @@ export class Blueprint {
* @param {GameRoot} root
*/
canAfford(root) {
return root.hubGoals.getShapesStoredByKey(blueprintShape) >= this.getCost();
return root.hubGoals.getShapesStoredByKey(root.gameMode.getBlueprintShapeKey()) >= this.getCost();
}
/**

View File

@ -41,7 +41,7 @@ const variantsCache = new Map();
export function registerBuildingVariant(
code,
meta,
variant = "default" /* FIXME: Circular dependency, actually its defaultBuildingVariant */,
variant = "default" /* @TODO: Circular dependency, actually its defaultBuildingVariant */,
rotationVariant = 0
) {
assert(!gBuildingVariants[code], "Duplicate id: " + code);

View File

@ -7,6 +7,7 @@ import { BeltComponent } from "../components/belt";
import { Entity } from "../entity";
import { MetaBuilding } from "../meta_building";
import { GameRoot } from "../root";
import { THEME } from "../theme";
export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right];
@ -22,7 +23,7 @@ export class MetaBeltBuilding extends MetaBuilding {
}
getSilhouetteColor() {
return "#777";
return THEME.map.chunkOverview.beltColor;
}
getPlacementSound() {

View File

@ -191,7 +191,6 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
) {
tile = tile.addScalars(searchVector.x, searchVector.y);
/* WIRES: FIXME */
const contents = root.map.getTileContent(tile, "regular");
if (contents) {
const undergroundComp = contents.components.UndergroundBelt;

View File

@ -110,12 +110,7 @@ export class MetaVirtualProcessorBuilding extends MetaBuilding {
pinComp.setSlots([
{
pos: new Vector(0, 0),
direction: enumDirection.left,
type: enumPinSlotType.logicalEjector,
},
{
pos: new Vector(0, 0),
direction: enumDirection.right,
direction: enumDirection.top,
type: enumPinSlotType.logicalEjector,
},
{

View File

@ -511,7 +511,11 @@ export class Camera extends BasicSerializableObject {
this.clampZoomLevel();
this.desiredZoom = null;
const mousePosition = this.root.app.mousePosition;
let mousePosition = this.root.app.mousePosition;
if (!this.root.app.settings.getAllSettings().zoomToCursor) {
mousePosition = new Vector(this.root.gameWidth / 2, this.root.gameHeight / 2);
}
if (mousePosition) {
const worldPos = this.root.camera.screenToWorld(mousePosition);
const worldDelta = worldPos.sub(this.center);

View File

@ -26,7 +26,7 @@ export class ItemEjectorComponent extends Component {
static getSchema() {
// The cachedDestSlot, cachedTargetEntity fields are not serialized.
return {
slots: types.array(
slots: types.fixedSizeArray(
types.structured({
item: types.nullable(typeItemSingleton),
progress: types.float,

View File

@ -31,7 +31,7 @@ export class WiredPinsComponent extends Component {
static getSchema() {
return {
slots: types.array(
slots: types.fixedSizeArray(
types.structured({
value: types.nullable(typeItemSingleton),
})

View File

@ -31,6 +31,7 @@ import { KeyActionMapper } from "./key_action_mapper";
import { GameLogic } from "./logic";
import { MapView } from "./map_view";
import { defaultBuildingVariant } from "./meta_building";
import { RegularGameMode } from "./modes/regular";
import { ProductionAnalytics } from "./production_analytics";
import { GameRoot } from "./root";
import { ShapeDefinitionManager } from "./shape_definition_manager";
@ -101,6 +102,9 @@ export class GameCore {
// Needs to come first
root.dynamicTickrate = new DynamicTickrate(root);
// Init game mode
root.gameMode = new RegularGameMode(root);
// Init classes
root.camera = new Camera(root);
root.map = new MapView(root);

View File

@ -182,23 +182,6 @@ export class EntityManager extends BasicSerializableObject {
return this.componentToEntity[componentHandle.getId()] || [];
}
/**
* Return all of a given class. This is SLOW!
* @param {object} entityClass
* @returns {Array<Entity>} entities
*/
getAllOfClass(entityClass) {
// FIXME: Slow
const result = [];
for (let i = 0; i < this.entities.length; ++i) {
const entity = this.entities[i];
if (entity instanceof entityClass) {
result.push(entity);
}
}
return result;
}
/**
* Unregisters all components of an entity from the component to entity mapping
* @param {Entity} entity

71
src/js/game/game_mode.js Normal file
View File

@ -0,0 +1,71 @@
/* typehints:start */
import { enumHubGoalRewards } from "./tutorial_goals";
/* typehints:end */
import { GameRoot } from "./root";
/** @typedef {{
* shape: string,
* amount: number
* }} UpgradeRequirement */
/** @typedef {{
* required: Array<UpgradeRequirement>
* improvement?: number,
* excludePrevious?: boolean
* }} TierRequirement */
/** @typedef {Array<TierRequirement>} UpgradeTiers */
/** @typedef {{
* shape: string,
* required: number,
* reward: enumHubGoalRewards,
* throughputOnly?: boolean
* }} LevelDefinition */
export class GameMode {
/**
*
* @param {GameRoot} root
*/
constructor(root) {
this.root = root;
}
/**
* Should return all available upgrades
* @returns {Object<string, UpgradeTiers>}
*/
getUpgrades() {
abstract;
return null;
}
/**
* Returns the blueprint shape key
* @returns {string}
*/
getBlueprintShapeKey() {
abstract;
return null;
}
/**
* Returns the goals for all levels including their reward
* @returns {Array<LevelDefinition>}
*/
getLevelDefinitions() {
abstract;
return null;
}
/**
* Should return whether free play is available or if the game stops
* after the predefined levels
* @returns {boolean}
*/
getIsFreeplayAvailable() {
return true;
}
}

View File

@ -1,14 +1,13 @@
import { globalConfig, IS_DEMO } from "../core/config";
import { globalConfig } from "../core/config";
import { RandomNumberGenerator } from "../core/rng";
import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils";
import { clamp } from "../core/utils";
import { BasicSerializableObject, types } from "../savegame/serialization";
import { enumColors } from "./colors";
import { enumItemProcessorTypes } from "./components/item_processor";
import { enumAnalyticsDataSource } from "./production_analytics";
import { GameRoot } from "./root";
import { enumSubShape, ShapeDefinition } from "./shape_definition";
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
import { UPGRADES } from "./upgrades";
import { enumHubGoalRewards } from "./tutorial_goals";
export class HubGoals extends BasicSerializableObject {
static getId() {
@ -23,27 +22,36 @@ export class HubGoals extends BasicSerializableObject {
};
}
deserialize(data) {
/**
*
* @param {*} data
* @param {GameRoot} root
*/
deserialize(data, root) {
const errorCode = super.deserialize(data);
if (errorCode) {
return errorCode;
}
if (IS_DEMO) {
this.level = Math.min(this.level, tutorialGoals.length);
const levels = root.gameMode.getLevelDefinitions();
// If freeplay is not available, clamp the level
if (!root.gameMode.getIsFreeplayAvailable()) {
this.level = Math.min(this.level, levels.length);
}
// Compute gained rewards
for (let i = 0; i < this.level - 1; ++i) {
if (i < tutorialGoals.length) {
const reward = tutorialGoals[i].reward;
if (i < levels.length) {
const reward = levels[i].reward;
this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1;
}
}
// Compute upgrade improvements
for (const upgradeId in UPGRADES) {
const tiers = UPGRADES[upgradeId];
const upgrades = this.root.gameMode.getUpgrades();
for (const upgradeId in upgrades) {
const tiers = upgrades[upgradeId];
const level = this.upgradeLevels[upgradeId] || 0;
let totalImprovement = 1;
for (let i = 0; i < level; ++i) {
@ -84,17 +92,16 @@ export class HubGoals extends BasicSerializableObject {
*/
this.upgradeLevels = {};
// Reset levels
for (const key in UPGRADES) {
this.upgradeLevels[key] = 0;
}
/**
* Stores the improvements for all upgrades
* @type {Object<string, number>}
*/
this.upgradeImprovements = {};
for (const key in UPGRADES) {
// Reset levels first
const upgrades = this.root.gameMode.getUpgrades();
for (const key in upgrades) {
this.upgradeLevels[key] = 0;
this.upgradeImprovements[key] = 1;
}
@ -120,7 +127,10 @@ export class HubGoals extends BasicSerializableObject {
* @returns {boolean}
*/
isEndOfDemoReached() {
return IS_DEMO && this.level >= tutorialGoals.length;
return (
!this.root.gameMode.getIsFreeplayAvailable() &&
this.level >= this.root.gameMode.getLevelDefinitions().length
);
}
/**
@ -215,8 +225,9 @@ export class HubGoals extends BasicSerializableObject {
*/
computeNextGoal() {
const storyIndex = this.level - 1;
if (storyIndex < tutorialGoals.length) {
const { shape, required, reward, throughputOnly } = tutorialGoals[storyIndex];
const levels = this.root.gameMode.getLevelDefinitions();
if (storyIndex < levels.length) {
const { shape, required, reward, throughputOnly } = levels[storyIndex];
this.currentGoal = {
/** @type {ShapeDefinition} */
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape),
@ -227,7 +238,8 @@ export class HubGoals extends BasicSerializableObject {
return;
}
const required = Math.min(200, 4 + (this.level - 27) * 0.25);
//Floor Required amount to remove confusion
const required = Math.min(200, Math.floor(4 + (this.level - 27) * 0.25));
this.currentGoal = {
definition: this.computeFreeplayShape(this.level),
required,
@ -254,7 +266,7 @@ export class HubGoals extends BasicSerializableObject {
* Returns whether we are playing in free-play
*/
isFreePlay() {
return this.level >= tutorialGoals.length;
return this.level >= this.root.gameMode.getLevelDefinitions().length;
}
/**
@ -262,7 +274,7 @@ export class HubGoals extends BasicSerializableObject {
* @param {string} upgradeId
*/
canUnlockUpgrade(upgradeId) {
const tiers = UPGRADES[upgradeId];
const tiers = this.root.gameMode.getUpgrades()[upgradeId];
const currentLevel = this.getUpgradeLevel(upgradeId);
if (currentLevel >= tiers.length) {
@ -270,11 +282,6 @@ export class HubGoals extends BasicSerializableObject {
return false;
}
if (IS_DEMO && currentLevel >= 4) {
// DEMO
return false;
}
if (G_IS_DEV && globalConfig.debug.upgradesNoCost) {
return true;
}
@ -296,7 +303,7 @@ export class HubGoals extends BasicSerializableObject {
*/
getAvailableUpgradeCount() {
let count = 0;
for (const upgradeId in UPGRADES) {
for (const upgradeId in this.root.gameMode.getUpgrades()) {
if (this.canUnlockUpgrade(upgradeId)) {
++count;
}
@ -314,7 +321,7 @@ export class HubGoals extends BasicSerializableObject {
return false;
}
const upgradeTiers = UPGRADES[upgradeId];
const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId];
const currentLevel = this.getUpgradeLevel(upgradeId);
const tierData = upgradeTiers[currentLevel];
@ -363,7 +370,7 @@ export class HubGoals extends BasicSerializableObject {
if (allowUncolored) {
universalColors.push(enumColors.uncolored);
}
const index = rng.nextIntRangeInclusive(0, colorWheel.length - 3);
const index = rng.nextIntRange(0, colorWheel.length - 2);
const pickedColors = colorWheel.slice(index, index + 3);
pickedColors.push(rng.choice(universalColors));
return pickedColors;

View File

@ -15,7 +15,7 @@ import { HUDKeybindingOverlay } from "./parts/keybinding_overlay";
import { HUDUnlockNotification } from "./parts/unlock_notification";
import { HUDGameMenu } from "./parts/game_menu";
import { HUDShop } from "./parts/shop";
import { IS_MOBILE, globalConfig, IS_DEMO } from "../../core/config";
import { IS_MOBILE, globalConfig } from "../../core/config";
import { HUDMassSelector } from "./parts/mass_selector";
import { HUDVignetteOverlay } from "./parts/vignette_overlay";
import { HUDStatistics } from "./parts/statistics";
@ -45,9 +45,9 @@ import { HUDLeverToggle } from "./parts/lever_toggle";
import { HUDLayerPreview } from "./parts/layer_preview";
import { HUDMinerHighlight } from "./parts/miner_highlight";
import { HUDBetaOverlay } from "./parts/beta_overlay";
import { HUDPerformanceWarning } from "./parts/performance_warning";
import { HUDStandaloneAdvantages } from "./parts/standalone_advantages";
import { HUDCatMemes } from "./parts/cat_memes";
import { HUDTutorialVideoOffer } from "./parts/tutorial_video_offer";
export class GameHUD {
/**
@ -61,6 +61,18 @@ export class GameHUD {
* Initializes the hud parts
*/
initialize() {
this.signals = {
buildingSelectedForPlacement: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new Signal()),
selectedPlacementBuildingChanged: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new Signal()),
shapePinRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
shapeUnpinRequested: /** @type {TypedSignal<[string]>} */ (new Signal()),
notification: /** @type {TypedSignal<[string, enumNotificationType]>} */ (new Signal()),
buildingsSelectedForCopy: /** @type {TypedSignal<[Array<number>]>} */ (new Signal()),
pasteBlueprintRequested: /** @type {TypedSignal<[]>} */ (new Signal()),
viewShapeDetailsRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
unlockNotificationFinished: /** @type {TypedSignal<[]>} */ (new Signal()),
};
this.parts = {
buildingsToolbar: new HUDBuildingsToolbar(this.root),
wiresToolbar: new HUDWiresToolbar(this.root),
@ -88,7 +100,7 @@ export class GameHUD {
layerPreview: new HUDLayerPreview(this.root),
minerHighlight: new HUDMinerHighlight(this.root),
performanceWarning: new HUDPerformanceWarning(this.root),
tutorialVideoOffer: new HUDTutorialVideoOffer(this.root),
// Typing hints
/* typehints:start */
@ -97,17 +109,6 @@ export class GameHUD {
/* typehints:end */
};
this.signals = {
buildingSelectedForPlacement: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new Signal()),
selectedPlacementBuildingChanged: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new Signal()),
shapePinRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
shapeUnpinRequested: /** @type {TypedSignal<[string]>} */ (new Signal()),
notification: /** @type {TypedSignal<[string, enumNotificationType]>} */ (new Signal()),
buildingsSelectedForCopy: /** @type {TypedSignal<[Array<number>]>} */ (new Signal()),
pasteBlueprintRequested: /** @type {TypedSignal<[]>} */ (new Signal()),
viewShapeDetailsRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
};
if (!IS_MOBILE) {
this.parts.keybindingOverlay = new HUDKeybindingOverlay(this.root);
}
@ -116,7 +117,7 @@ export class GameHUD {
this.parts.entityDebugger = new HUDEntityDebugger(this.root);
}
if (IS_DEMO) {
if (this.root.app.restrictionMgr.getIsStandaloneMarketingActive()) {
this.parts.watermark = new HUDWatermark(this.root);
this.parts.standaloneAdvantages = new HUDStandaloneAdvantages(this.root);
this.parts.catMemes = new HUDCatMemes(this.root);

View File

@ -7,7 +7,7 @@ export class HUDBetaOverlay extends BaseHUDPart {
parent,
"ingame_HUD_BetaOverlay",
[],
"<h2>UNSTABLE BETA VERSION</h2><span>Steam Release: 9th October 2020!</span>"
"<h2>UNSTABLE BETA VERSION</h2><span>Unfinalized & potential buggy content!</span>"
);
}

View File

@ -1,202 +1,203 @@
import { DrawParameters } from "../../../core/draw_parameters";
import { STOP_PROPAGATION } from "../../../core/signal";
import { TrackedState } from "../../../core/tracked_state";
import { makeDiv } from "../../../core/utils";
import { Vector } from "../../../core/vector";
import { T } from "../../../translations";
import { enumMouseButton } from "../../camera";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { blueprintShape } from "../../upgrades";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { Blueprint } from "../../blueprint";
import { SOUNDS } from "../../../platform/sound";
export class HUDBlueprintPlacer extends BaseHUDPart {
createElements(parent) {
const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey(blueprintShape);
const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80);
this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``);
makeDiv(this.costDisplayParent, null, ["label"], T.ingame.blueprintPlacer.cost);
const costContainer = makeDiv(this.costDisplayParent, null, ["costContainer"], "");
this.costDisplayText = makeDiv(costContainer, null, ["costText"], "");
costContainer.appendChild(blueprintCostShapeCanvas);
}
initialize() {
this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this);
/** @type {TypedTrackedState<Blueprint?>} */
this.currentBlueprint = new TrackedState(this.onBlueprintChanged, this);
/** @type {Blueprint?} */
this.lastBlueprintUsed = null;
const keyActionMapper = this.root.keyMapper;
keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this);
keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.abortPlacement, this);
keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this);
keyActionMapper.getBinding(KEYMAPPINGS.massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this);
this.root.camera.downPreHandler.add(this.onMouseDown, this);
this.root.camera.movePreHandler.add(this.onMouseMove, this);
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this);
this.root.signals.editModeChanged.add(this.onEditModeChanged, this);
this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent);
this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this);
}
abortPlacement() {
if (this.currentBlueprint.get()) {
this.currentBlueprint.set(null);
return STOP_PROPAGATION;
}
}
/**
* Called when the layer was changed
* @param {Layer} layer
*/
onEditModeChanged(layer) {
// Check if the layer of the blueprint differs and thus we have to deselect it
const blueprint = this.currentBlueprint.get();
if (blueprint) {
if (blueprint.layer !== layer) {
this.currentBlueprint.set(null);
}
}
}
/**
* Called when the blueprint is now affordable or not
* @param {boolean} canAfford
*/
onCanAffordChanged(canAfford) {
this.costDisplayParent.classList.toggle("canAfford", canAfford);
}
update() {
const currentBlueprint = this.currentBlueprint.get();
this.domAttach.update(currentBlueprint && currentBlueprint.getCost() > 0);
this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root));
}
/**
* Called when the blueprint was changed
* @param {Blueprint} blueprint
*/
onBlueprintChanged(blueprint) {
if (blueprint) {
this.lastBlueprintUsed = blueprint;
this.costDisplayText.innerText = "" + blueprint.getCost();
}
}
/**
* mouse down pre handler
* @param {Vector} pos
* @param {enumMouseButton} button
*/
onMouseDown(pos, button) {
if (button === enumMouseButton.right) {
if (this.currentBlueprint.get()) {
this.abortPlacement();
return STOP_PROPAGATION;
}
}
const blueprint = this.currentBlueprint.get();
if (!blueprint) {
return;
}
if (!blueprint.canAfford(this.root)) {
this.root.soundProxy.playUiError();
return;
}
const worldPos = this.root.camera.screenToWorld(pos);
const tile = worldPos.toTileSpace();
if (blueprint.tryPlace(this.root, tile)) {
const cost = blueprint.getCost();
this.root.hubGoals.takeShapeByKey(blueprintShape, cost);
this.root.soundProxy.playUi(SOUNDS.placeBuilding);
}
}
/**
* Mose move handler
*/
onMouseMove() {
// Prevent movement while blueprint is selected
if (this.currentBlueprint.get()) {
return STOP_PROPAGATION;
}
}
/**
* Called when an array of bulidings was selected
* @param {Array<number>} uids
*/
createBlueprintFromBuildings(uids) {
if (uids.length === 0) {
return;
}
this.currentBlueprint.set(Blueprint.fromUids(this.root, uids));
}
/**
* Attempts to rotate the current blueprint
*/
rotateBlueprint() {
if (this.currentBlueprint.get()) {
if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) {
this.currentBlueprint.get().rotateCcw();
} else {
this.currentBlueprint.get().rotateCw();
}
}
}
/**
* Attempts to paste the last blueprint
*/
pasteBlueprint() {
if (this.lastBlueprintUsed !== null) {
if (this.lastBlueprintUsed.layer !== this.root.currentLayer) {
// Not compatible
this.root.soundProxy.playUiError();
return;
}
this.root.hud.signals.pasteBlueprintRequested.dispatch();
this.currentBlueprint.set(this.lastBlueprintUsed);
} else {
this.root.soundProxy.playUiError();
}
}
/**
*
* @param {DrawParameters} parameters
*/
draw(parameters) {
const blueprint = this.currentBlueprint.get();
if (!blueprint) {
return;
}
const mousePosition = this.root.app.mousePosition;
if (!mousePosition) {
// Not on screen
return;
}
const worldPos = this.root.camera.screenToWorld(mousePosition);
const tile = worldPos.toTileSpace();
blueprint.draw(parameters, tile);
}
}
import { DrawParameters } from "../../../core/draw_parameters";
import { STOP_PROPAGATION } from "../../../core/signal";
import { TrackedState } from "../../../core/tracked_state";
import { makeDiv } from "../../../core/utils";
import { Vector } from "../../../core/vector";
import { SOUNDS } from "../../../platform/sound";
import { T } from "../../../translations";
import { Blueprint } from "../../blueprint";
import { enumMouseButton } from "../../camera";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
export class HUDBlueprintPlacer extends BaseHUDPart {
createElements(parent) {
const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey(
this.root.gameMode.getBlueprintShapeKey()
);
const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80);
this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``);
makeDiv(this.costDisplayParent, null, ["label"], T.ingame.blueprintPlacer.cost);
const costContainer = makeDiv(this.costDisplayParent, null, ["costContainer"], "");
this.costDisplayText = makeDiv(costContainer, null, ["costText"], "");
costContainer.appendChild(blueprintCostShapeCanvas);
}
initialize() {
this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this);
/** @type {TypedTrackedState<Blueprint?>} */
this.currentBlueprint = new TrackedState(this.onBlueprintChanged, this);
/** @type {Blueprint?} */
this.lastBlueprintUsed = null;
const keyActionMapper = this.root.keyMapper;
keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this);
keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.abortPlacement, this);
keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this);
keyActionMapper.getBinding(KEYMAPPINGS.massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this);
this.root.camera.downPreHandler.add(this.onMouseDown, this);
this.root.camera.movePreHandler.add(this.onMouseMove, this);
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this);
this.root.signals.editModeChanged.add(this.onEditModeChanged, this);
this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent);
this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this);
}
abortPlacement() {
if (this.currentBlueprint.get()) {
this.currentBlueprint.set(null);
return STOP_PROPAGATION;
}
}
/**
* Called when the layer was changed
* @param {Layer} layer
*/
onEditModeChanged(layer) {
// Check if the layer of the blueprint differs and thus we have to deselect it
const blueprint = this.currentBlueprint.get();
if (blueprint) {
if (blueprint.layer !== layer) {
this.currentBlueprint.set(null);
}
}
}
/**
* Called when the blueprint is now affordable or not
* @param {boolean} canAfford
*/
onCanAffordChanged(canAfford) {
this.costDisplayParent.classList.toggle("canAfford", canAfford);
}
update() {
const currentBlueprint = this.currentBlueprint.get();
this.domAttach.update(currentBlueprint && currentBlueprint.getCost() > 0);
this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root));
}
/**
* Called when the blueprint was changed
* @param {Blueprint} blueprint
*/
onBlueprintChanged(blueprint) {
if (blueprint) {
this.lastBlueprintUsed = blueprint;
this.costDisplayText.innerText = "" + blueprint.getCost();
}
}
/**
* mouse down pre handler
* @param {Vector} pos
* @param {enumMouseButton} button
*/
onMouseDown(pos, button) {
if (button === enumMouseButton.right) {
if (this.currentBlueprint.get()) {
this.abortPlacement();
return STOP_PROPAGATION;
}
}
const blueprint = this.currentBlueprint.get();
if (!blueprint) {
return;
}
if (!blueprint.canAfford(this.root)) {
this.root.soundProxy.playUiError();
return;
}
const worldPos = this.root.camera.screenToWorld(pos);
const tile = worldPos.toTileSpace();
if (blueprint.tryPlace(this.root, tile)) {
const cost = blueprint.getCost();
this.root.hubGoals.takeShapeByKey(this.root.gameMode.getBlueprintShapeKey(), cost);
this.root.soundProxy.playUi(SOUNDS.placeBuilding);
}
}
/**
* Mose move handler
*/
onMouseMove() {
// Prevent movement while blueprint is selected
if (this.currentBlueprint.get()) {
return STOP_PROPAGATION;
}
}
/**
* Called when an array of bulidings was selected
* @param {Array<number>} uids
*/
createBlueprintFromBuildings(uids) {
if (uids.length === 0) {
return;
}
this.currentBlueprint.set(Blueprint.fromUids(this.root, uids));
}
/**
* Attempts to rotate the current blueprint
*/
rotateBlueprint() {
if (this.currentBlueprint.get()) {
if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) {
this.currentBlueprint.get().rotateCcw();
} else {
this.currentBlueprint.get().rotateCw();
}
}
}
/**
* Attempts to paste the last blueprint
*/
pasteBlueprint() {
if (this.lastBlueprintUsed !== null) {
if (this.lastBlueprintUsed.layer !== this.root.currentLayer) {
// Not compatible
this.root.soundProxy.playUiError();
return;
}
this.root.hud.signals.pasteBlueprintRequested.dispatch();
this.currentBlueprint.set(this.lastBlueprintUsed);
} else {
this.root.soundProxy.playUiError();
}
}
/**
*
* @param {DrawParameters} parameters
*/
draw(parameters) {
const blueprint = this.currentBlueprint.get();
if (!blueprint) {
return;
}
const mousePosition = this.root.app.mousePosition;
if (!mousePosition) {
// Not on screen
return;
}
const worldPos = this.root.camera.screenToWorld(mousePosition);
const tile = worldPos.toTileSpace();
blueprint.draw(parameters, tile);
}
}

View File

@ -6,23 +6,25 @@ import { DynamicDomAttach } from "../dynamic_dom_attach";
import { TrackedState } from "../../../core/tracked_state";
import { cachebust } from "../../../core/cachebust";
import { T } from "../../../translations";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../../components/item_processor";
import { ShapeItem } from "../../items/shape_item";
import { WireComponent } from "../../components/wire";
import { LeverComponent } from "../../components/lever";
// @todo: Make dictionary
const tutorialsByLevel = [
// Level 1
[
// 1.1. place an extractor
{
id: "1_1_extractor",
condition: /** @param {GameRoot} root */ root => {
return root.entityMgr.getAllWithComponent(MinerComponent).length === 0;
},
condition: /** @param {GameRoot} root */ root =>
root.entityMgr.getAllWithComponent(MinerComponent).length === 0,
},
// 1.2. connect to hub
{
id: "1_2_conveyor",
condition: /** @param {GameRoot} root */ root => {
return root.hubGoals.getCurrentGoalDelivered() === 0;
},
condition: /** @param {GameRoot} root */ root => root.hubGoals.getCurrentGoalDelivered() === 0,
},
// 1.3 wait for completion
{
@ -30,6 +32,108 @@ const tutorialsByLevel = [
condition: () => true,
},
],
// Level 2
[
// 2.1 place a cutter
{
id: "2_1_place_cutter",
condition: /** @param {GameRoot} root */ root =>
root.entityMgr
.getAllWithComponent(ItemProcessorComponent)
.filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.cutter).length ===
0,
},
// 2.2 place trash
{
id: "2_2_place_trash",
condition: /** @param {GameRoot} root */ root =>
root.entityMgr
.getAllWithComponent(ItemProcessorComponent)
.filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.trash).length ===
0,
},
// 2.3 place more cutters
{
id: "2_3_more_cutters",
condition: /** @param {GameRoot} root */ root =>
root.entityMgr
.getAllWithComponent(ItemProcessorComponent)
.filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.cutter).length <
3,
},
],
// Level 3
[
// 3.1. rectangles
{
id: "3_1_rectangles",
condition: /** @param {GameRoot} root */ root =>
// 4 miners placed above rectangles and 10 delivered
root.hubGoals.getCurrentGoalDelivered() < 10 ||
root.entityMgr.getAllWithComponent(MinerComponent).filter(entity => {
const tile = entity.components.StaticMapEntity.origin;
const below = root.map.getLowerLayerContentXY(tile.x, tile.y);
if (below && below.getItemType() === "shape") {
const shape = /** @type {ShapeItem} */ (below).definition.getHash();
return shape === "RuRuRuRu";
}
return false;
}).length < 4,
},
],
[], // Level 4
[], // Level 5
[], // Level 6
[], // Level 7
[], // Level 8
[], // Level 9
[], // Level 10
[], // Level 11
[], // Level 12
[], // Level 13
[], // Level 14
[], // Level 15
[], // Level 16
[], // Level 17
[], // Level 18
[], // Level 19
[], // Level 20
// Level 21
[
// 21.1 place quad painter
{
id: "21_1_place_quad_painter",
condition: /** @param {GameRoot} root */ root =>
root.entityMgr
.getAllWithComponent(ItemProcessorComponent)
.filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.painterQuad)
.length === 0,
},
// 21.2 switch to wires layer
{
id: "21_2_switch_to_wires",
condition: /** @param {GameRoot} root */ root =>
root.entityMgr.getAllWithComponent(WireComponent).length < 5,
},
// 21.3 place button
{
id: "21_3_place_button",
condition: /** @param {GameRoot} root */ root =>
root.entityMgr.getAllWithComponent(LeverComponent).length === 0,
},
// 21.4 activate button
{
id: "21_4_press_button",
condition: /** @param {GameRoot} root */ root =>
root.entityMgr.getAllWithComponent(LeverComponent).some(e => !e.components.Lever.toggled),
},
],
];
export class HUDInteractiveTutorial extends BaseHUDPart {

View File

@ -1,16 +0,0 @@
import { T } from "../../../translations";
import { BaseHUDPart } from "../base_hud_part";
export class HUDPerformanceWarning extends BaseHUDPart {
initialize() {
this.warningShown = false;
this.root.signals.entityManuallyPlaced.add(this.checkAfterPlace, this);
}
checkAfterPlace() {
if (!this.warningShown && this.root.entityMgr.entities.length > 10000) {
this.root.hud.parts.dialogs.showInfo(T.dialogs.entityWarning.title, T.dialogs.entityWarning.desc);
this.warningShown = true;
}
}
}

View File

@ -1,12 +1,11 @@
import { ClickDetector } from "../../../core/click_detector";
import { formatBigNumber, makeDiv, arrayDeleteValue } from "../../../core/utils";
import { ShapeDefinition } from "../../shape_definition";
import { BaseHUDPart } from "../base_hud_part";
import { blueprintShape, UPGRADES } from "../../upgrades";
import { enumHubGoalRewards } from "../../tutorial_goals";
import { enumAnalyticsDataSource } from "../../production_analytics";
import { T } from "../../../translations";
import { globalConfig } from "../../../core/config";
import { arrayDeleteValue, formatBigNumber, makeDiv } from "../../../core/utils";
import { T } from "../../../translations";
import { enumAnalyticsDataSource } from "../../production_analytics";
import { ShapeDefinition } from "../../shape_definition";
import { enumHubGoalRewards } from "../../tutorial_goals";
import { BaseHUDPart } from "../base_hud_part";
/**
* Manages the pinned shapes on the left side of the screen
@ -82,7 +81,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
updateShapesAfterUpgrade() {
for (let i = 0; i < this.pinnedShapes.length; ++i) {
const key = this.pinnedShapes[i];
if (key === blueprintShape) {
if (key === this.root.gameMode.getBlueprintShapeKey()) {
// Ignore blueprint shapes
continue;
}
@ -107,13 +106,14 @@ export class HUDPinnedShapes extends BaseHUDPart {
if (key === this.root.hubGoals.currentGoal.definition.getHash()) {
return this.root.hubGoals.currentGoal.required;
}
if (key === blueprintShape) {
if (key === this.root.gameMode.getBlueprintShapeKey()) {
return null;
}
// Check if this shape is required for any upgrade
for (const upgradeId in UPGRADES) {
const upgradeTiers = UPGRADES[upgradeId];
const upgrades = this.root.gameMode.getUpgrades();
for (const upgradeId in upgrades) {
const upgradeTiers = upgrades[upgradeId];
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
const tierHandle = upgradeTiers[currentTier];
@ -138,7 +138,10 @@ export class HUDPinnedShapes extends BaseHUDPart {
* @param {string} key
*/
isShapePinned(key) {
if (key === this.root.hubGoals.currentGoal.definition.getHash() || key === blueprintShape) {
if (
key === this.root.hubGoals.currentGoal.definition.getHash() ||
key === this.root.gameMode.getBlueprintShapeKey()
) {
// This is a "special" shape which is always pinned
return true;
}
@ -178,7 +181,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
// Pin blueprint shape as well
if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
this.internalPinShape({
key: blueprintShape,
key: this.root.gameMode.getBlueprintShapeKey(),
canUnpin: false,
className: "blueprint",
});
@ -214,11 +217,11 @@ export class HUDPinnedShapes extends BaseHUDPart {
let detector = null;
if (canUnpin) {
element.classList.add("unpinable");
element.classList.add("removable");
detector = new ClickDetector(element, {
consumeEvents: true,
preventDefault: true,
targetOnly: true,
targetOnly: false,
});
detector.click.add(() => this.unpinShape(key));
} else {
@ -291,6 +294,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
* @param {string} key
*/
unpinShape(key) {
console.log("unpin", key);
arrayDeleteValue(this.pinnedShapes, key);
this.rerenderFull();
}
@ -306,7 +310,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
return;
}
if (key === blueprintShape) {
if (key === this.root.gameMode.getBlueprintShapeKey()) {
// Can not pin the blueprint shape
return;
}

View File

@ -1,9 +1,7 @@
import { BaseHUDPart } from "../base_hud_part";
import { makeDiv } from "../../../core/utils";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { blueprintShape, UPGRADES } from "../../upgrades";
import { enumNotificationType } from "./notifications";
import { tutorialGoals } from "../../tutorial_goals";
export class HUDSandboxController extends BaseHUDPart {
createElements(parent) {
@ -75,10 +73,11 @@ export class HUDSandboxController extends BaseHUDPart {
}
giveBlueprints() {
if (!this.root.hubGoals.storedShapes[blueprintShape]) {
this.root.hubGoals.storedShapes[blueprintShape] = 0;
const shape = this.root.gameMode.getBlueprintShapeKey();
if (!this.root.hubGoals.storedShapes[shape]) {
this.root.hubGoals.storedShapes[shape] = 0;
}
this.root.hubGoals.storedShapes[blueprintShape] += 1e9;
this.root.hubGoals.storedShapes[shape] += 1e9;
}
maxOutAll() {
@ -89,7 +88,7 @@ export class HUDSandboxController extends BaseHUDPart {
}
modifyUpgrade(id, amount) {
const upgradeTiers = UPGRADES[id];
const upgradeTiers = this.root.gameMode.getUpgrades()[id];
const maxLevel = upgradeTiers.length;
this.root.hubGoals.upgradeLevels[id] = Math.max(
@ -122,9 +121,10 @@ export class HUDSandboxController extends BaseHUDPart {
// Compute gained rewards
hubGoals.gainedRewards = {};
const levels = this.root.gameMode.getLevelDefinitions();
for (let i = 0; i < hubGoals.level - 1; ++i) {
if (i < tutorialGoals.length) {
const reward = tutorialGoals[i].reward;
if (i < levels.length) {
const reward = levels[i].reward;
hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1;
}
}

View File

@ -1,13 +1,13 @@
import { BaseHUDPart } from "../base_hud_part";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { IS_DEMO, globalConfig } from "../../../core/config";
import { T } from "../../../translations";
import { createLogger } from "../../../core/logging";
import { StaticMapEntityComponent } from "../../components/static_map_entity";
import { Vector } from "../../../core/vector";
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { globalConfig } from "../../../core/config";
import { DrawParameters } from "../../../core/draw_parameters";
import { createLogger } from "../../../core/logging";
import { Rectangle } from "../../../core/rectangle";
import { Vector } from "../../../core/vector";
import { T } from "../../../translations";
import { StaticMapEntityComponent } from "../../components/static_map_entity";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { BaseHUDPart } from "../base_hud_part";
const logger = createLogger("screenshot_exporter");
@ -19,7 +19,7 @@ export class HUDScreenshotExporter extends BaseHUDPart {
}
startExport() {
if (IS_DEMO) {
if (!this.root.app.restrictionMgr.getIsExportingScreenshotsPossible()) {
this.root.hud.parts.dialogs.showFeatureRestrictionInfo(T.demo.features.exportingBase);
return;
}
@ -87,7 +87,7 @@ export class HUDScreenshotExporter extends BaseHUDPart {
const parameters = new DrawParameters({
context,
visibleRect,
desiredAtlasScale: chunkScale,
desiredAtlasScale: 0.25,
root: this.root,
zoomLevel: chunkScale,
});

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