mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
merged from upstream
This commit is contained in:
commit
28fef86abe
8
.editorconfig
Executable file
8
.editorconfig
Executable 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
|
||||||
4
.gitattributes
vendored
4
.gitattributes
vendored
@ -1,4 +0,0 @@
|
|||||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.webm filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.psd filter=lfs diff=lfs merge=lfs -text
|
|
||||||
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@ -35,19 +35,23 @@ jobs:
|
|||||||
cd gulp/
|
cd gulp/
|
||||||
yarn
|
yarn
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: |
|
run: |
|
||||||
yarn lint
|
yarn lint
|
||||||
|
|
||||||
- name: YAML Lint
|
|
||||||
uses: ibiqlik/action-yamllint@v1.0.0
|
|
||||||
with:
|
|
||||||
file_or_dir: translations/*.yaml
|
|
||||||
|
|
||||||
- name: TSLint
|
- name: TSLint
|
||||||
run: |
|
run: |
|
||||||
cd gulp
|
cd gulp
|
||||||
yarn gulp translations.fullBuild
|
yarn gulp translations.fullBuild
|
||||||
cd ..
|
cd ..
|
||||||
yarn tslint
|
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
66
.gitignore
vendored
@ -15,34 +15,11 @@ pids
|
|||||||
*.seed
|
*.seed
|
||||||
*.pid.lock
|
*.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)
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
build/Release
|
build/Release
|
||||||
|
|
||||||
# Dependency directories
|
# Dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# TypeScript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# TypeScript cache
|
# TypeScript cache
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
@ -53,18 +30,9 @@ typings/
|
|||||||
# Optional eslint cache
|
# Optional eslint cache
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
|
||||||
# Microbundle cache
|
|
||||||
.rpt2_cache/
|
|
||||||
.rts2_cache_cjs/
|
|
||||||
.rts2_cache_es/
|
|
||||||
.rts2_cache_umd/
|
|
||||||
|
|
||||||
# Optional REPL history
|
# Optional REPL history
|
||||||
.node_repl_history
|
.node_repl_history
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
# Yarn Integrity file
|
||||||
.yarn-integrity
|
.yarn-integrity
|
||||||
|
|
||||||
@ -72,41 +40,11 @@ typings/
|
|||||||
.env
|
.env
|
||||||
.env.test
|
.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
|
# Buildfiles
|
||||||
build
|
build
|
||||||
|
res_built
|
||||||
|
|
||||||
|
gulp/runnable-texturepacker.jar
|
||||||
tmp_standalone_files
|
tmp_standalone_files
|
||||||
|
|
||||||
# Local config
|
# Local config
|
||||||
|
|||||||
@ -4,3 +4,4 @@ rules:
|
|||||||
line-length:
|
line-length:
|
||||||
level: warning
|
level: warning
|
||||||
max: 200
|
max: 200
|
||||||
|
document-start: disable
|
||||||
|
|||||||
31
Dockerfile
Normal file
31
Dockerfile
Normal 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"]
|
||||||
@ -22,15 +22,14 @@ Your goal is to produce shapes by cutting, rotating, merging and painting parts
|
|||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
- Make sure git `git lfs` extension is on your path
|
|
||||||
- Run `git lfs pull` to download sound assets
|
|
||||||
- Make sure `ffmpeg` is on your path
|
- Make sure `ffmpeg` is on your path
|
||||||
- Install Node.js and Yarn
|
- Install Node.js and Yarn
|
||||||
|
- Install Java (required for textures)
|
||||||
- Run `yarn` in the root folder
|
- Run `yarn` in the root folder
|
||||||
- Cd into `gulp` folder
|
- Cd into `gulp` folder
|
||||||
- Run `yarn` and then `yarn gulp` - it should now open in your browser
|
- 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 `config.js`.
|
**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).
|
||||||
|
|
||||||
## Helping translate
|
## Helping translate
|
||||||
|
|
||||||
@ -116,8 +115,8 @@ This is a quick checklist, if a new building is added this points should be fulf
|
|||||||
|
|
||||||
### Assets
|
### 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">
|
<img src="https://i.imgur.com/W25Fkl0.png" alt="shapez.io Screenshot">
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
The artwork can be found here:
|
|
||||||
|
|
||||||
https://github.com/tobspr/shapez.io-artwork
|
|
||||||
@ -10,7 +10,7 @@
|
|||||||
"start": "electron --disable-direct-composition --in-process-gpu ."
|
"start": "electron --disable-direct-composition --in-process-gpu ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^6.1.12"
|
"electron": "10.1.3"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
|||||||
1087
electron/yarn.lock
1087
electron/yarn.lock
File diff suppressed because it is too large
Load Diff
1
gulp/.gitattributes
vendored
1
gulp/.gitattributes
vendored
@ -1 +0,0 @@
|
|||||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
|
||||||
1
gulp/.gitignore
vendored
1
gulp/.gitignore
vendored
@ -1,2 +1 @@
|
|||||||
additional_build_files
|
additional_build_files
|
||||||
steampipe
|
|
||||||
|
|||||||
127
gulp/atlas2json.js
Normal file
127
gulp/atlas2json.js
Normal 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 };
|
||||||
@ -25,6 +25,14 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getTag() {
|
||||||
|
try {
|
||||||
|
return execSync("git describe --tag --exact-match").toString("ascii");
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Current git HEAD is not a version tag');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getVersion() {
|
getVersion() {
|
||||||
return trim(fs.readFileSync(path.join(__dirname, "..", "version")).toString());
|
return trim(fs.readFileSync(path.join(__dirname, "..", "version")).toString());
|
||||||
},
|
},
|
||||||
|
|||||||
12
gulp/entitlements.plist
Normal file
12
gulp/entitlements.plist
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.debugger</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@ -8,23 +8,6 @@ const path = require("path");
|
|||||||
const deleteEmpty = require("delete-empty");
|
const deleteEmpty = require("delete-empty");
|
||||||
const execSync = require("child_process").execSync;
|
const execSync = require("child_process").execSync;
|
||||||
|
|
||||||
const lfsOutput = execSync("git lfs install", { encoding: "utf-8" });
|
|
||||||
if (!lfsOutput.toLowerCase().includes("git lfs initialized")) {
|
|
||||||
console.error(`
|
|
||||||
Git LFS is not installed, unable to build.
|
|
||||||
|
|
||||||
To install Git LFS on Linux:
|
|
||||||
- Arch:
|
|
||||||
sudo pacman -S git-lfs
|
|
||||||
- Debian/Ubuntu:
|
|
||||||
sudo apt install git-lfs
|
|
||||||
|
|
||||||
For other systems, see:
|
|
||||||
https://github.com/git-lfs/git-lfs/wiki/Installation
|
|
||||||
`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load other plugins dynamically
|
// Load other plugins dynamically
|
||||||
const $ = require("gulp-load-plugins")({
|
const $ = require("gulp-load-plugins")({
|
||||||
scope: ["devDependencies"],
|
scope: ["devDependencies"],
|
||||||
@ -42,6 +25,10 @@ const envVars = [
|
|||||||
"SHAPEZ_CLI_STAGING_FTP_PW",
|
"SHAPEZ_CLI_STAGING_FTP_PW",
|
||||||
"SHAPEZ_CLI_LIVE_FTP_USER",
|
"SHAPEZ_CLI_LIVE_FTP_USER",
|
||||||
"SHAPEZ_CLI_LIVE_FTP_PW",
|
"SHAPEZ_CLI_LIVE_FTP_PW",
|
||||||
|
"SHAPEZ_CLI_APPLE_ID",
|
||||||
|
"SHAPEZ_CLI_APPLE_CERT_NAME",
|
||||||
|
"SHAPEZ_CLI_GITHUB_USER",
|
||||||
|
"SHAPEZ_CLI_GITHUB_TOKEN",
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 0; i < envVars.length; ++i) {
|
for (let i = 0; i < envVars.length; ++i) {
|
||||||
@ -78,13 +65,12 @@ docs.gulptasksDocs($, gulp, buildFolder);
|
|||||||
const standalone = require("./standalone");
|
const standalone = require("./standalone");
|
||||||
standalone.gulptasksStandalone($, gulp, buildFolder);
|
standalone.gulptasksStandalone($, gulp, buildFolder);
|
||||||
|
|
||||||
|
const releaseUploader = require("./release-uploader");
|
||||||
|
releaseUploader.gulptasksReleaseUploader($, gulp, buildFolder);
|
||||||
|
|
||||||
const translations = require("./translations");
|
const translations = require("./translations");
|
||||||
translations.gulptasksTranslations($, gulp, buildFolder);
|
translations.gulptasksTranslations($, gulp, buildFolder);
|
||||||
|
|
||||||
// FIXME
|
|
||||||
// const cordova = require("./cordova");
|
|
||||||
// cordova.gulptasksCordova($, gulp, buildFolder);
|
|
||||||
|
|
||||||
///////////////////// BUILD TASKS /////////////////////
|
///////////////////// BUILD TASKS /////////////////////
|
||||||
|
|
||||||
// Cleans up everything
|
// Cleans up everything
|
||||||
@ -96,8 +82,16 @@ gulp.task("utils.cleanBuildTempFolder", () => {
|
|||||||
.src(path.join(__dirname, "..", "src", "js", "built-temp"), { read: false, allowEmpty: true })
|
.src(path.join(__dirname, "..", "src", "js", "built-temp"), { read: false, allowEmpty: true })
|
||||||
.pipe($.clean({ force: 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
|
// Requires no uncomitted files
|
||||||
gulp.task("utils.requireCleanWorkingTree", cb => {
|
gulp.task("utils.requireCleanWorkingTree", cb => {
|
||||||
@ -184,10 +178,12 @@ function serve({ standalone }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Watch resource files and copy them on change
|
// 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.nonImageResourcesGlobs, gulp.series("imgres.copyNonImageResources"));
|
||||||
gulp.watch(imgres.imageResourcesGlobs, gulp.series("imgres.copyImageResources"));
|
gulp.watch(imgres.imageResourcesGlobs, gulp.series("imgres.copyImageResources"));
|
||||||
|
|
||||||
// Watch .atlas files and recompile the atlas on change
|
// 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"));
|
gulp.watch("../res_built/atlas/*.json", gulp.series("imgres.atlas"));
|
||||||
|
|
||||||
// Watch the build folder and reload when anything changed
|
// Watch the build folder and reload when anything changed
|
||||||
@ -225,6 +221,8 @@ gulp.task(
|
|||||||
gulp.series(
|
gulp.series(
|
||||||
"utils.cleanup",
|
"utils.cleanup",
|
||||||
"utils.copyAdditionalBuildFiles",
|
"utils.copyAdditionalBuildFiles",
|
||||||
|
"imgres.buildAtlas",
|
||||||
|
"imgres.atlasToJson",
|
||||||
"imgres.atlas",
|
"imgres.atlas",
|
||||||
"sounds.dev",
|
"sounds.dev",
|
||||||
"imgres.copyImageResources",
|
"imgres.copyImageResources",
|
||||||
@ -240,12 +238,13 @@ gulp.task(
|
|||||||
"build.standalone.dev",
|
"build.standalone.dev",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
"utils.cleanup",
|
"utils.cleanup",
|
||||||
|
"imgres.buildAtlas",
|
||||||
|
"imgres.atlasToJson",
|
||||||
"imgres.atlas",
|
"imgres.atlas",
|
||||||
"sounds.dev",
|
"sounds.dev",
|
||||||
"imgres.copyImageResources",
|
"imgres.copyImageResources",
|
||||||
"imgres.copyNonImageResources",
|
"imgres.copyNonImageResources",
|
||||||
"translations.fullBuild",
|
"translations.fullBuild",
|
||||||
"js.standalone-dev",
|
|
||||||
"css.dev",
|
"css.dev",
|
||||||
"html.standalone-dev"
|
"html.standalone-dev"
|
||||||
)
|
)
|
||||||
@ -299,6 +298,17 @@ gulp.task(
|
|||||||
gulp.series("utils.cleanup", "step.standalone-prod.all", "step.postbuild")
|
gulp.series("utils.cleanup", "step.standalone-prod.all", "step.postbuild")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// OS X build and release upload
|
||||||
|
gulp.task(
|
||||||
|
"build.darwin64-prod",
|
||||||
|
gulp.series(
|
||||||
|
"build.standalone-prod",
|
||||||
|
"standalone.prepare",
|
||||||
|
"standalone.package.prod.darwin64",
|
||||||
|
"standalone.uploadRelease.darwin64"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Deploying!
|
// Deploying!
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"main.deploy.alpha",
|
"main.deploy.alpha",
|
||||||
|
|||||||
26
gulp/html.js
26
gulp/html.js
@ -54,19 +54,19 @@ function gulptasksHTML($, gulp, buildFolder) {
|
|||||||
document.head.appendChild(css);
|
document.head.appendChild(css);
|
||||||
|
|
||||||
// Append async css
|
// Append async css
|
||||||
const asyncCss = document.createElement("link");
|
// const asyncCss = document.createElement("link");
|
||||||
asyncCss.rel = "stylesheet";
|
// asyncCss.rel = "stylesheet";
|
||||||
asyncCss.type = "text/css";
|
// asyncCss.type = "text/css";
|
||||||
asyncCss.media = "none";
|
// asyncCss.media = "none";
|
||||||
asyncCss.setAttribute("onload", "this.media='all'");
|
// asyncCss.setAttribute("onload", "this.media='all'");
|
||||||
asyncCss.href = cachebust("async-resources.css");
|
// asyncCss.href = cachebust("async-resources.css");
|
||||||
if (integrity) {
|
// if (integrity) {
|
||||||
asyncCss.setAttribute(
|
// asyncCss.setAttribute(
|
||||||
"integrity",
|
// "integrity",
|
||||||
computeIntegrityHash(path.join(buildFolder, "async-resources.css"))
|
// computeIntegrityHash(path.join(buildFolder, "async-resources.css"))
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
document.head.appendChild(asyncCss);
|
// document.head.appendChild(asyncCss);
|
||||||
|
|
||||||
if (app) {
|
if (app) {
|
||||||
// Append cordova link
|
// Append cordova link
|
||||||
|
|||||||
@ -1,5 +1,15 @@
|
|||||||
|
const { existsSync } = require("fs");
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const path = require("path");
|
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
|
// Globs for non-ui resources
|
||||||
const nonImageResourcesGlobs = ["../res/**/*.woff2", "../res/*.ico", "../res/**/*.webm"];
|
const nonImageResourcesGlobs = ["../res/**/*.woff2", "../res/*.ico", "../res/**/*.webm"];
|
||||||
@ -7,6 +17,9 @@ const nonImageResourcesGlobs = ["../res/**/*.woff2", "../res/*.ico", "../res/**/
|
|||||||
// Globs for ui resources
|
// Globs for ui resources
|
||||||
const imageResourcesGlobs = ["../res/**/*.png", "../res/**/*.svg", "../res/**/*.jpg", "../res/**/*.gif"];
|
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) {
|
function gulptasksImageResources($, gulp, buildFolder) {
|
||||||
// Lossless options
|
// Lossless options
|
||||||
const minifyImagesOptsLossless = () => [
|
const minifyImagesOptsLossless = () => [
|
||||||
@ -59,6 +72,54 @@ function gulptasksImageResources($, gulp, buildFolder) {
|
|||||||
|
|
||||||
/////////////// ATLAS /////////////////////
|
/////////////// 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
|
// Copies the atlas to the final destination
|
||||||
gulp.task("imgres.atlas", () => {
|
gulp.task("imgres.atlas", () => {
|
||||||
return gulp.src(["../res_built/atlas/*.png"]).pipe(gulp.dest(resourcesDestFolder));
|
return gulp.src(["../res_built/atlas/*.png"]).pipe(gulp.dest(resourcesDestFolder));
|
||||||
@ -112,6 +173,8 @@ function gulptasksImageResources($, gulp, buildFolder) {
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"imgres.allOptimized",
|
"imgres.allOptimized",
|
||||||
gulp.parallel(
|
gulp.parallel(
|
||||||
|
"imgres.buildAtlas",
|
||||||
|
"imgres.atlasToJson",
|
||||||
"imgres.atlasOptimized",
|
"imgres.atlasOptimized",
|
||||||
"imgres.copyNonImageResources",
|
"imgres.copyNonImageResources",
|
||||||
"imgres.copyImageResourcesOptimized"
|
"imgres.copyImageResourcesOptimized"
|
||||||
@ -135,6 +198,7 @@ function gulptasksImageResources($, gulp, buildFolder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
rawImageResourcesGlobs,
|
||||||
nonImageResourcesGlobs,
|
nonImageResourcesGlobs,
|
||||||
imageResourcesGlobs,
|
imageResourcesGlobs,
|
||||||
gulptasksImageResources,
|
gulptasksImageResources,
|
||||||
|
|||||||
@ -47,6 +47,7 @@
|
|||||||
"serialize-error": "^3.0.0",
|
"serialize-error": "^3.0.0",
|
||||||
"strictdom": "^1.0.1",
|
"strictdom": "^1.0.1",
|
||||||
"string-replace-webpack-plugin": "^0.1.3",
|
"string-replace-webpack-plugin": "^0.1.3",
|
||||||
|
"strip-indent": "^3.0.0",
|
||||||
"terser-webpack-plugin": "^1.1.0",
|
"terser-webpack-plugin": "^1.1.0",
|
||||||
"through2": "^3.0.1",
|
"through2": "^3.0.1",
|
||||||
"uglify-template-string-loader": "^1.1.0",
|
"uglify-template-string-loader": "^1.1.0",
|
||||||
@ -66,7 +67,6 @@
|
|||||||
"babel-plugin-danger-remove-unused-import": "^1.1.2",
|
"babel-plugin-danger-remove-unused-import": "^1.1.2",
|
||||||
"css-mqpacker": "^7.0.0",
|
"css-mqpacker": "^7.0.0",
|
||||||
"cssnano": "^4.1.10",
|
"cssnano": "^4.1.10",
|
||||||
"postcss-critical-split": "^2.5.3",
|
|
||||||
"electron-packager": "^14.0.6",
|
"electron-packager": "^14.0.6",
|
||||||
"faster.js": "^1.1.0",
|
"faster.js": "^1.1.0",
|
||||||
"glob": "^7.1.3",
|
"glob": "^7.1.3",
|
||||||
@ -99,6 +99,7 @@
|
|||||||
"jimp": "^0.6.1",
|
"jimp": "^0.6.1",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"postcss-assets": "^5.0.0",
|
"postcss-assets": "^5.0.0",
|
||||||
|
"postcss-critical-split": "^2.5.3",
|
||||||
"postcss-preset-env": "^6.5.0",
|
"postcss-preset-env": "^6.5.0",
|
||||||
"postcss-round-subpixels": "^1.2.0",
|
"postcss-round-subpixels": "^1.2.0",
|
||||||
"postcss-unprefix": "^2.1.3",
|
"postcss-unprefix": "^2.1.3",
|
||||||
|
|||||||
66
gulp/release-uploader.js
Normal file
66
gulp/release-uploader.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
|
const execSync = require("child_process").execSync;
|
||||||
|
const { Octokit } = require("@octokit/rest");
|
||||||
|
const buildutils = require("./buildutils");
|
||||||
|
|
||||||
|
function gulptasksReleaseUploader($, gulp, buildFolder) {
|
||||||
|
const standaloneDir = path.join(__dirname, "..", "tmp_standalone_files");
|
||||||
|
const darwinApp = path.join(standaloneDir, "shapez.io-standalone-darwin-x64", "shapez.io-standalone.app");
|
||||||
|
const dmgName = "shapez.io-standalone.dmg";
|
||||||
|
const dmgPath = path.join(standaloneDir, "shapez.io-standalone-darwin-x64", dmgName);
|
||||||
|
|
||||||
|
gulp.task("standalone.uploadRelease.darwin64.cleanup", () => {
|
||||||
|
return gulp.src(dmgPath, { read: false, allowEmpty: true }).pipe($.clean({ force: true }));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("standalone.uploadRelease.darwin64.compress", cb => {
|
||||||
|
console.log("Packaging disk image", dmgPath);
|
||||||
|
execSync(`hdiutil create -format UDBZ -srcfolder ${darwinApp} ${dmgPath}`);
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("standalone.uploadRelease.darwin64.upload", async cb => {
|
||||||
|
const currentTag = buildutils.getTag();
|
||||||
|
|
||||||
|
const octokit = new Octokit({
|
||||||
|
auth: process.env.SHAPEZ_CLI_GITHUB_TOKEN
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdRelease = await octokit.request("POST /repos/{owner}/{repo}/releases", {
|
||||||
|
owner: process.env.SHAPEZ_CLI_GITHUB_USER,
|
||||||
|
repo: "shapez.io",
|
||||||
|
tag_name: currentTag,
|
||||||
|
name: currentTag,
|
||||||
|
draft: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: { id, upload_url } } = createdRelease;
|
||||||
|
console.log(`Created release ${id} for tag ${currentTag}`);
|
||||||
|
|
||||||
|
const dmgContents = fs.readFileSync(dmgPath);
|
||||||
|
const dmgSize = fs.statSync(dmgPath).size;
|
||||||
|
console.log("Uploading", dmgContents.length / 1024 / 1024, "MB to", upload_url);
|
||||||
|
|
||||||
|
await octokit.request({
|
||||||
|
method: "POST",
|
||||||
|
url: upload_url,
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/x-apple-diskimage"
|
||||||
|
},
|
||||||
|
name: dmgName,
|
||||||
|
data: dmgContents
|
||||||
|
});
|
||||||
|
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("standalone.uploadRelease.darwin64",
|
||||||
|
gulp.series(
|
||||||
|
"standalone.uploadRelease.darwin64.cleanup",
|
||||||
|
"standalone.uploadRelease.darwin64.compress",
|
||||||
|
"standalone.uploadRelease.darwin64.upload"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { gulptasksReleaseUploader };
|
||||||
@ -1,8 +1,10 @@
|
|||||||
|
require("colors");
|
||||||
const packager = require("electron-packager");
|
const packager = require("electron-packager");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const { getVersion } = require("./buildutils");
|
const { getVersion } = require("./buildutils");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const fse = require("fs-extra");
|
const fse = require("fs-extra");
|
||||||
|
const buildutils = require("./buildutils");
|
||||||
const execSync = require("child_process").execSync;
|
const execSync = require("child_process").execSync;
|
||||||
|
|
||||||
function gulptasksStandalone($, gulp) {
|
function gulptasksStandalone($, gulp) {
|
||||||
@ -46,6 +48,20 @@ function gulptasksStandalone($, gulp) {
|
|||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task("standalone.prepareVDF", cb => {
|
||||||
|
const hash = buildutils.getRevision();
|
||||||
|
|
||||||
|
const steampipeDir = path.join(__dirname, "steampipe", "scripts");
|
||||||
|
const templateContents = fs
|
||||||
|
.readFileSync(path.join(steampipeDir, "app.vdf.template"), { encoding: "utf-8" })
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
const convertedContents = templateContents.replace("$DESC$", "Commit " + hash);
|
||||||
|
fs.writeFileSync(path.join(steampipeDir, "app.vdf"), convertedContents);
|
||||||
|
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task("standalone.prepare.minifyCode", () => {
|
gulp.task("standalone.prepare.minifyCode", () => {
|
||||||
return gulp.src(path.join(electronBaseDir, "*.js")).pipe(gulp.dest(tempDestBuildDir));
|
return gulp.src(path.join(electronBaseDir, "*.js")).pipe(gulp.dest(tempDestBuildDir));
|
||||||
});
|
});
|
||||||
@ -80,8 +96,9 @@ function gulptasksStandalone($, gulp) {
|
|||||||
* @param {'win32'|'linux'|'darwin'} platform
|
* @param {'win32'|'linux'|'darwin'} platform
|
||||||
* @param {'x64'|'ia32'} arch
|
* @param {'x64'|'ia32'} arch
|
||||||
* @param {function():void} cb
|
* @param {function():void} cb
|
||||||
|
* @param {boolean=} isRelease
|
||||||
*/
|
*/
|
||||||
function packageStandalone(platform, arch, cb) {
|
function packageStandalone(platform, arch, cb, isRelease = true) {
|
||||||
const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml"));
|
const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml"));
|
||||||
|
|
||||||
packager({
|
packager({
|
||||||
@ -99,6 +116,21 @@ function gulptasksStandalone($, gulp) {
|
|||||||
overwrite: true,
|
overwrite: true,
|
||||||
appBundleId: "io.shapez.standalone",
|
appBundleId: "io.shapez.standalone",
|
||||||
appCategoryType: "public.app-category.games",
|
appCategoryType: "public.app-category.games",
|
||||||
|
...(isRelease &&
|
||||||
|
platform === "darwin" && {
|
||||||
|
osxSign: {
|
||||||
|
"identity": process.env.SHAPEZ_CLI_APPLE_CERT_NAME,
|
||||||
|
"hardened-runtime": true,
|
||||||
|
"hardenedRuntime": true,
|
||||||
|
"entitlements": "entitlements.plist",
|
||||||
|
"entitlements-inherit": "entitlements.plist",
|
||||||
|
"signature-flags": "library",
|
||||||
|
},
|
||||||
|
osxNotarize: {
|
||||||
|
appleId: process.env.SHAPEZ_CLI_APPLE_ID,
|
||||||
|
appleIdPassword: "@keychain:SHAPEZ_CLI_APPLE_ID",
|
||||||
|
},
|
||||||
|
}),
|
||||||
}).then(
|
}).then(
|
||||||
appPaths => {
|
appPaths => {
|
||||||
console.log("Packages created:", appPaths);
|
console.log("Packages created:", appPaths);
|
||||||
@ -123,7 +155,15 @@ function gulptasksStandalone($, gulp) {
|
|||||||
fs.chmodSync(path.join(appPath, "play.sh"), 0o775);
|
fs.chmodSync(path.join(appPath, "play.sh"), 0o775);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platform === "darwin") {
|
if (process.platform === "win32" && platform === "darwin") {
|
||||||
|
console.warn(
|
||||||
|
"Cross-building for macOS on Windows: dereferencing symlinks.\n".red +
|
||||||
|
"This will nearly double app size and make code signature invalid. Sorry!\n"
|
||||||
|
.red.bold +
|
||||||
|
"For more information, see " +
|
||||||
|
"https://github.com/electron/electron-packager/issues/71".underline
|
||||||
|
);
|
||||||
|
|
||||||
// Clear up framework folders
|
// Clear up framework folders
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(appPath, "play.sh"),
|
path.join(appPath, "play.sh"),
|
||||||
@ -175,6 +215,9 @@ function gulptasksStandalone($, gulp) {
|
|||||||
gulp.task("standalone.package.prod.linux64", cb => packageStandalone("linux", "x64", cb));
|
gulp.task("standalone.package.prod.linux64", cb => packageStandalone("linux", "x64", cb));
|
||||||
gulp.task("standalone.package.prod.linux32", cb => packageStandalone("linux", "ia32", cb));
|
gulp.task("standalone.package.prod.linux32", cb => packageStandalone("linux", "ia32", cb));
|
||||||
gulp.task("standalone.package.prod.darwin64", cb => packageStandalone("darwin", "x64", cb));
|
gulp.task("standalone.package.prod.darwin64", cb => packageStandalone("darwin", "x64", cb));
|
||||||
|
gulp.task("standalone.package.prod.darwin64.unsigned", cb =>
|
||||||
|
packageStandalone("darwin", "x64", cb, false)
|
||||||
|
);
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"standalone.package.prod",
|
"standalone.package.prod",
|
||||||
|
|||||||
2
gulp/steampipe/.gitignore
vendored
Normal file
2
gulp/steampipe/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
steamtemp
|
||||||
|
app.vdf
|
||||||
15
gulp/steampipe/scripts/app.vdf.template
Normal file
15
gulp/steampipe/scripts/app.vdf.template
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
"appbuild"
|
||||||
|
{
|
||||||
|
"appid" "1318690"
|
||||||
|
"desc" "$DESC$"
|
||||||
|
"buildoutput" "C:\work\shapez\shapez.io\gulp\steampipe\steamtemp"
|
||||||
|
"contentroot" ""
|
||||||
|
"setlive" ""
|
||||||
|
"preview" "0"
|
||||||
|
"local" ""
|
||||||
|
"depots"
|
||||||
|
{
|
||||||
|
"1318691" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\windows.vdf"
|
||||||
|
"1318692" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\linux.vdf"
|
||||||
|
}
|
||||||
|
}
|
||||||
12
gulp/steampipe/scripts/linux.vdf
Normal file
12
gulp/steampipe/scripts/linux.vdf
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
"DepotBuildConfig"
|
||||||
|
{
|
||||||
|
"DepotID" "1318692"
|
||||||
|
"contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files\shapez.io-standalone-linux-x64"
|
||||||
|
"FileMapping"
|
||||||
|
{
|
||||||
|
"LocalPath" "*"
|
||||||
|
"DepotPath" "."
|
||||||
|
"recursive" "1"
|
||||||
|
}
|
||||||
|
"FileExclusion" "*.pdb"
|
||||||
|
}
|
||||||
12
gulp/steampipe/scripts/windows.vdf
Normal file
12
gulp/steampipe/scripts/windows.vdf
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
"DepotBuildConfig"
|
||||||
|
{
|
||||||
|
"DepotID" "1318691"
|
||||||
|
"contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files\shapez.io-standalone-win32-x64"
|
||||||
|
"FileMapping"
|
||||||
|
{
|
||||||
|
"LocalPath" "*"
|
||||||
|
"DepotPath" "."
|
||||||
|
"recursive" "1"
|
||||||
|
}
|
||||||
|
"FileExclusion" "*.pdb"
|
||||||
|
}
|
||||||
4
gulp/steampipe/upload.bat
Normal file
4
gulp/steampipe/upload.bat
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
cmd /c gulp standalone.prepareVDF
|
||||||
|
steamcmd +login %STEAM_UPLOAD_SHAPEZ_ID% %STEAM_UPLOAD_SHAPEZ_USER% +run_app_build %cd%/scripts/app.vdf +quit
|
||||||
|
start https://partner.steamgames.com/apps/builds/1318690
|
||||||
@ -1,6 +1,9 @@
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
const yaml = require("gulp-yaml");
|
const gulpYaml = require("gulp-yaml");
|
||||||
|
const YAML = require("yaml");
|
||||||
|
const stripIndent = require("strip-indent");
|
||||||
|
const trim = require("trim");
|
||||||
|
|
||||||
const translationsSourceDir = path.join(__dirname, "..", "translations");
|
const translationsSourceDir = path.join(__dirname, "..", "translations");
|
||||||
const translationsJsonDir = path.join(__dirname, "..", "src", "js", "built-temp");
|
const translationsJsonDir = path.join(__dirname, "..", "src", "js", "built-temp");
|
||||||
@ -10,11 +13,75 @@ function gulptasksTranslations($, gulp) {
|
|||||||
return gulp
|
return gulp
|
||||||
.src(path.join(translationsSourceDir, "*.yaml"))
|
.src(path.join(translationsSourceDir, "*.yaml"))
|
||||||
.pipe($.plumber())
|
.pipe($.plumber())
|
||||||
.pipe(yaml({ space: 2, safe: true }))
|
.pipe(gulpYaml({ space: 2, safe: true }))
|
||||||
.pipe(gulp.dest(translationsJsonDir));
|
.pipe(gulp.dest(translationsJsonDir));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("translations.fullBuild", gulp.series("translations.convertToJson"));
|
gulp.task("translations.fullBuild", gulp.series("translations.convertToJson"));
|
||||||
|
|
||||||
|
gulp.task("translations.prepareSteamPage", cb => {
|
||||||
|
const files = fs.readdirSync(translationsSourceDir);
|
||||||
|
|
||||||
|
files
|
||||||
|
.filter(name => name.endsWith(".yaml"))
|
||||||
|
.forEach(fname => {
|
||||||
|
const languageName = fname.replace(".yaml", "");
|
||||||
|
const abspath = path.join(translationsSourceDir, fname);
|
||||||
|
|
||||||
|
const destpath = path.join(translationsSourceDir, "tmp", languageName + "-store.txt");
|
||||||
|
|
||||||
|
const contents = fs.readFileSync(abspath, { encoding: "utf-8" });
|
||||||
|
const data = YAML.parse(contents);
|
||||||
|
|
||||||
|
const storePage = data.steamPage;
|
||||||
|
|
||||||
|
const content = `
|
||||||
|
[img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img]
|
||||||
|
|
||||||
|
${storePage.intro.replace(/\n/gi, "\n\n")}
|
||||||
|
|
||||||
|
[h2]${storePage.title_advantages}[/h2]
|
||||||
|
|
||||||
|
[list]
|
||||||
|
${storePage.advantages
|
||||||
|
.map(x => "[*] " + x.replace(/<b>/, "[b]").replace(/<\/b>/, "[/b]"))
|
||||||
|
.join("\n")}
|
||||||
|
[/list]
|
||||||
|
|
||||||
|
[h2]${storePage.title_future}[/h2]
|
||||||
|
|
||||||
|
[list]
|
||||||
|
${storePage.planned
|
||||||
|
.map(x => "[*] " + x.replace(/<b>/, "[b]").replace(/<\/b>/, "[/b]"))
|
||||||
|
.join("\n")}
|
||||||
|
[/list]
|
||||||
|
|
||||||
|
[h2]${storePage.title_open_source}[/h2]
|
||||||
|
|
||||||
|
${storePage.text_open_source.replace(/\n/gi, "\n\n")}
|
||||||
|
|
||||||
|
[h2]${storePage.title_links}[/h2]
|
||||||
|
|
||||||
|
[list]
|
||||||
|
[*] [url=https://discord.com/invite/HN7EVzV]${storePage.links.discord}[/url]
|
||||||
|
[*] [url=https://trello.com/b/ISQncpJP/shapezio]${storePage.links.roadmap}[/url]
|
||||||
|
[*] [url=https://www.reddit.com/r/shapezio]${storePage.links.subreddit}[/url]
|
||||||
|
[*] [url=https://github.com/tobspr/shapez.io]${storePage.links.source_code}[/url]
|
||||||
|
[*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]${
|
||||||
|
storePage.links.translate
|
||||||
|
}[/url]
|
||||||
|
[/list]
|
||||||
|
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
fs.writeFileSync(destpath, trim(content.replace(/(\n[ \t\r]*)/gi, "\n")), {
|
||||||
|
encoding: "utf-8",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
cb();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@ -8198,6 +8198,11 @@ min-document@^2.19.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
dom-walk "^0.1.0"
|
dom-walk "^0.1.0"
|
||||||
|
|
||||||
|
min-indent@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
|
||||||
|
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
|
||||||
|
|
||||||
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
|
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
||||||
@ -11945,6 +11950,13 @@ strip-indent@^1.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
get-stdin "^4.0.1"
|
get-stdin "^4.0.1"
|
||||||
|
|
||||||
|
strip-indent@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
|
||||||
|
integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
|
||||||
|
dependencies:
|
||||||
|
min-indent "^1.0.0"
|
||||||
|
|
||||||
strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
|
strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||||
|
|||||||
@ -71,6 +71,7 @@
|
|||||||
"yawn-yaml": "^1.5.0"
|
"yawn-yaml": "^1.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@octokit/rest": "^18.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "3.0.1",
|
"@typescript-eslint/eslint-plugin": "3.0.1",
|
||||||
"@typescript-eslint/parser": "3.0.1",
|
"@typescript-eslint/parser": "3.0.1",
|
||||||
"autoprefixer": "^9.4.3",
|
"autoprefixer": "^9.4.3",
|
||||||
|
|||||||
@ -1,8 +1,24 @@
|
|||||||
#ingame_HUD_BetaOverlay {
|
#ingame_HUD_BetaOverlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@include S(top, 10px);
|
@include S(top, 10px);
|
||||||
@include S(right, 15px);
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
color: $colorRedBright;
|
color: $colorRedBright;
|
||||||
@include Heading;
|
@include Heading;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
@include PlainText;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #555;
|
||||||
|
@include SuperSmallText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#ingame_HUD_BlueprintPlacer {
|
#ingame_HUD_BlueprintPlacer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@include S(top, 50px);
|
@include S(top, 70px);
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
color: #333;
|
color: #333;
|
||||||
@ -13,6 +13,7 @@
|
|||||||
@include S(width, 120px);
|
@include S(width, 120px);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@include S(border-radius, $globalBorderRadius);
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
@include PlainText;
|
@include PlainText;
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
@include S(border-radius, $globalBorderRadius);
|
@include S(border-radius, $globalBorderRadius);
|
||||||
|
|
||||||
@include DarkThemeOverride {
|
@include DarkThemeOverride {
|
||||||
background-color: rgba(darken($darkModeGameBackground, 15), 0.4);
|
background-color: rgba(darken($darkModeGameBackground, 15), 0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.secondary {
|
&.secondary {
|
||||||
|
|||||||
23
src/css/ingame_hud/cat_memes.scss
Normal file
23
src/css/ingame_hud/cat_memes.scss
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#ingame_HUD_CatMemes {
|
||||||
|
position: absolute;
|
||||||
|
@include S(width, 150px);
|
||||||
|
@include S(height, 150px);
|
||||||
|
background: transparent center center / contain no-repeat;
|
||||||
|
|
||||||
|
right: 0;
|
||||||
|
@include S(bottom, 150px);
|
||||||
|
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("res/ui/memes/cat1.png") !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include InlineAnimation(0.5s ease-in-out) {
|
||||||
|
0% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -178,6 +178,29 @@
|
|||||||
display: list-item;
|
display: list-item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ingameItemChooser {
|
||||||
|
@include S(margin, 10px, 0);
|
||||||
|
display: grid;
|
||||||
|
@include S(grid-column-gap, 3px);
|
||||||
|
@include S(grid-row-gap, 5px);
|
||||||
|
grid-template-columns: repeat(10, 1fr);
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
pointer-events: all;
|
||||||
|
@include S(width, 25px);
|
||||||
|
@include S(height, 25px);
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
@include IncreasedClickArea(3px);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .buttons {
|
> .buttons {
|
||||||
@ -220,7 +243,7 @@
|
|||||||
content: " ";
|
content: " ";
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: rgba(#fff, 0.6);
|
background: rgba(#fff, 0.6);
|
||||||
@include InlineAnimation(5s linear) {
|
@include InlineAnimation(3s linear) {
|
||||||
0% {
|
0% {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,13 +17,10 @@
|
|||||||
grid-template-rows: 1fr 1fr;
|
grid-template-rows: 1fr 1fr;
|
||||||
@include S(margin-bottom, 4px);
|
@include S(margin-bottom, 4px);
|
||||||
color: #333438;
|
color: #333438;
|
||||||
// text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2);
|
|
||||||
|
|
||||||
&.unpinable {
|
&.removable {
|
||||||
> canvas {
|
cursor: pointer;
|
||||||
cursor: pointer;
|
pointer-events: all;
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> canvas {
|
> canvas {
|
||||||
@ -31,16 +28,9 @@
|
|||||||
@include S(height, 25px);
|
@include S(height, 25px);
|
||||||
grid-column: 1 / 2;
|
grid-column: 1 / 2;
|
||||||
grid-row: 1 / 3;
|
grid-row: 1 / 3;
|
||||||
pointer-events: all;
|
pointer-events: none;
|
||||||
transition: transform 0.1s ease-in-out;
|
|
||||||
transform-origin: D(2px) center;
|
|
||||||
will-change: transform;
|
|
||||||
position: relative;
|
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
&:hover {
|
position: relative;
|
||||||
transform: scale(2);
|
|
||||||
z-index: 21;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> .amountLabel,
|
> .amountLabel,
|
||||||
|
|||||||
169
src/css/ingame_hud/standalone_advantages.scss
Normal file
169
src/css/ingame_hud/standalone_advantages.scss
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#ingame_HUD_StandaloneAdvantages {
|
||||||
|
.content {
|
||||||
|
@include S(width, 440px);
|
||||||
|
@include S(min-height, 300px);
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
@include PlainText;
|
||||||
|
}
|
||||||
|
|
||||||
|
.points {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
@include S(grid-column-gap, 10px);
|
||||||
|
@include S(grid-row-gap, 20px);
|
||||||
|
@include S(margin, 10px, 0, 20px);
|
||||||
|
grid-template-rows: #{D(40px)};
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lowerBar {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
transition: opacity 0.12s ease-in-out;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.otherCloseButton {
|
||||||
|
@include SuperSmallText;
|
||||||
|
@include S(margin-right, 30px);
|
||||||
|
color: #aaa;
|
||||||
|
@include S(margin, 0);
|
||||||
|
@include IncreasedClickArea(0px);
|
||||||
|
@include S(margin-top, 15px);
|
||||||
|
|
||||||
|
@include InlineAnimation(5s ease-in-out) {
|
||||||
|
0% {
|
||||||
|
opacity: 0.05;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.05;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.steamLinkButton {
|
||||||
|
@include IncreasedClickArea(5px);
|
||||||
|
@include S(margin, 0);
|
||||||
|
@include S(width, 180px);
|
||||||
|
@include S(height, 40px);
|
||||||
|
background: #171a23 center center / contain no-repeat;
|
||||||
|
|
||||||
|
@include S(border-radius, $globalBorderRadius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.point {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: #{D(55px)} auto;
|
||||||
|
grid-template-rows: 1fr 1fr;
|
||||||
|
|
||||||
|
> strong {
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
grid-row: 1 / 2;
|
||||||
|
@include PlainText;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
> p {
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
grid-row: 2 / 3;
|
||||||
|
@include SuperSmallText;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
background: transparent #{D(10px)} center / #{D(30px)} no-repeat;
|
||||||
|
|
||||||
|
&.levels {
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("res/ui/icons/advantage_new_levels.png");
|
||||||
|
}
|
||||||
|
> strong {
|
||||||
|
color: #f13555;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.upgrades {
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("res/ui/icons/advantage_upgrades.png");
|
||||||
|
}
|
||||||
|
> strong {
|
||||||
|
color: #8a00ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.buildings {
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("res/ui/icons/advantage_buildings.png");
|
||||||
|
}
|
||||||
|
> strong {
|
||||||
|
color: #3fce8b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.wires {
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("res/ui/icons/advantage_wires.png");
|
||||||
|
}
|
||||||
|
> strong {
|
||||||
|
color: #ef2fdb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.markers {
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("res/ui/icons/advantage_markers.png");
|
||||||
|
}
|
||||||
|
> strong {
|
||||||
|
color: #4294ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.savegames {
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("res/ui/icons/advantage_savegames.png");
|
||||||
|
}
|
||||||
|
> strong {
|
||||||
|
color: #ff9500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.darkmode {
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("res/ui/icons/advantage_dark_mode.png");
|
||||||
|
}
|
||||||
|
> strong {
|
||||||
|
color: #292c32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.support {
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("res/ui/icons/advantage_support.png");
|
||||||
|
}
|
||||||
|
> strong {
|
||||||
|
color: #e72d2d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,22 +1,85 @@
|
|||||||
#ingame_HUD_Watermark {
|
#ingame_HUD_Watermark {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
& {
|
|
||||||
/* @load-async */
|
|
||||||
background: uiResource("get_on_steam.png") center center / contain no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include S(width, 110px);
|
@include S(border-radius, $globalBorderRadius);
|
||||||
@include S(height, 40px);
|
@include S(top, 70px);
|
||||||
@include S(top, 10px);
|
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@include S(left, 160px);
|
left: 50%;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
background: rgba(207, 65, 65, 0.8);
|
||||||
|
color: #fff;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
@include PlainText;
|
||||||
|
@include S(padding, 10px);
|
||||||
|
|
||||||
transition: all 0.12s ease-in;
|
|
||||||
transition-property: opacity, transform;
|
|
||||||
transform: skewX(-0.5deg);
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: skewX(-1deg) scale(1.02);
|
transform: translateX(-50%) scale(1.02) !important;
|
||||||
opacity: 0.9;
|
}
|
||||||
|
|
||||||
|
> strong {
|
||||||
|
@include PlainText;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
> p {
|
||||||
|
@include SuperSmallText;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
&.visible {
|
||||||
|
@include InlineAnimation(0.5s ease-in-out) {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.visible) {
|
||||||
|
@include InlineAnimation(0.5s ease-in-out) {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ingame_HUD_WatermarkClicker {
|
||||||
|
@include S(top, 55px);
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) !important;
|
||||||
|
@include SuperSmallText;
|
||||||
|
color: $colorBlueBright;
|
||||||
|
text-transform: uppercase;
|
||||||
|
pointer-events: all;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
@include S(margin-left, 4px);
|
||||||
|
content: "";
|
||||||
|
@include S(width, 10px);
|
||||||
|
@include S(height, 10px);
|
||||||
|
display: inline-flex;
|
||||||
|
background: center center / contain no-repeat;
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("res/ui/icons/demo_steam_link_indicator.png");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,6 +52,8 @@
|
|||||||
@import "ingame_hud/color_blind_helper";
|
@import "ingame_hud/color_blind_helper";
|
||||||
@import "ingame_hud/shape_viewer";
|
@import "ingame_hud/shape_viewer";
|
||||||
@import "ingame_hud/sandbox_controller";
|
@import "ingame_hud/sandbox_controller";
|
||||||
|
@import "ingame_hud/standalone_advantages";
|
||||||
|
@import "ingame_hud/cat_memes";
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
$elements:
|
$elements:
|
||||||
@ -71,12 +73,13 @@ ingame_HUD_KeybindingOverlay,
|
|||||||
ingame_HUD_Notifications,
|
ingame_HUD_Notifications,
|
||||||
ingame_HUD_DebugInfo,
|
ingame_HUD_DebugInfo,
|
||||||
ingame_HUD_EntityDebugger,
|
ingame_HUD_EntityDebugger,
|
||||||
ingame_HUD_InteractiveTutorial,
|
|
||||||
ingame_HUD_TutorialHints,
|
ingame_HUD_TutorialHints,
|
||||||
ingame_HUD_buildings_toolbar,
|
ingame_HUD_InteractiveTutorial,
|
||||||
|
ingame_HUD_BuildingsToolbar,
|
||||||
ingame_HUD_wires_toolbar,
|
ingame_HUD_wires_toolbar,
|
||||||
ingame_HUD_BlueprintPlacer,
|
ingame_HUD_BlueprintPlacer,
|
||||||
ingame_HUD_Waypoints_Hint,
|
ingame_HUD_Waypoints_Hint,
|
||||||
|
ingame_HUD_WatermarkClicker,
|
||||||
ingame_HUD_Watermark,
|
ingame_HUD_Watermark,
|
||||||
ingame_HUD_ColorBlindBelowTileHelper,
|
ingame_HUD_ColorBlindBelowTileHelper,
|
||||||
ingame_HUD_SandboxController,
|
ingame_HUD_SandboxController,
|
||||||
@ -88,9 +91,11 @@ ingame_HUD_BetaOverlay,
|
|||||||
ingame_HUD_Shop,
|
ingame_HUD_Shop,
|
||||||
ingame_HUD_Statistics,
|
ingame_HUD_Statistics,
|
||||||
ingame_HUD_ShapeViewer,
|
ingame_HUD_ShapeViewer,
|
||||||
|
ingame_HUD_StandaloneAdvantages,
|
||||||
ingame_HUD_UnlockNotification,
|
ingame_HUD_UnlockNotification,
|
||||||
ingame_HUD_SettingsMenu,
|
ingame_HUD_SettingsMenu,
|
||||||
ingame_HUD_ModalDialogs;
|
ingame_HUD_ModalDialogs,
|
||||||
|
ingame_HUD_CatMemes;
|
||||||
|
|
||||||
$zindex: 100;
|
$zindex: 100;
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
$buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, trash, underground_belt, wire,
|
$buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, trash, underground_belt, wire,
|
||||||
constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor, reader, storage,
|
constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor, reader, storage,
|
||||||
transistor, analyzer, comparator;
|
transistor, analyzer, comparator, item_producer;
|
||||||
|
|
||||||
@each $building in $buildings {
|
@each $building in $buildings {
|
||||||
[data-icon="building_icons/#{$building}.png"] {
|
[data-icon="building_icons/#{$building}.png"] {
|
||||||
@ -12,8 +12,8 @@ $buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, tra
|
|||||||
$buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2, miner, miner-chainable,
|
$buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2, miner, miner-chainable,
|
||||||
cutter, cutter-quad, rotater, rotater-ccw, stacker, mixer, painter-double, painter-quad, trash, storage,
|
cutter, cutter-quad, rotater, rotater-ccw, stacker, mixer, painter-double, painter-quad, trash, storage,
|
||||||
reader, rotater-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not,
|
reader, rotater-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not,
|
||||||
logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker,
|
logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker, item_producer,
|
||||||
virtual_processor-stacker, virtual_processor-painter, wire-second, painter, painter-mirrored;
|
virtual_processor-stacker, virtual_processor-painter, wire-second, painter, painter-mirrored, comparator;
|
||||||
@each $building in $buildingsAndVariants {
|
@each $building in $buildingsAndVariants {
|
||||||
[data-icon="building_tutorials/#{$building}.png"] {
|
[data-icon="building_tutorials/#{$building}.png"] {
|
||||||
/* @load-async */
|
/* @load-async */
|
||||||
@ -75,3 +75,17 @@ $languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW
|
|||||||
background-image: uiResource("languages/#{$language}.svg") !important;
|
background-image: uiResource("languages/#{$language}.svg") !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
PRICE
|
||||||
|
*/
|
||||||
|
|
||||||
|
.steam_1_pr {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("get_on_steam_with_price.png") !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.steam_2_npr {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("get_on_steam.png") !important;
|
||||||
|
}
|
||||||
|
|||||||
@ -133,10 +133,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
@include S(height, 40px);
|
@include S(height, 40px);
|
||||||
@include S(width, 180px);
|
@include S(width, 180px);
|
||||||
& {
|
background: #171a23 center center / contain no-repeat;
|
||||||
/* @load-async */
|
|
||||||
background: #171a23 uiResource("get_on_steam.png") center center / contain no-repeat;
|
|
||||||
}
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: block;
|
display: block;
|
||||||
text-indent: -999em;
|
text-indent: -999em;
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import { MobileWarningState } from "./states/mobile_warning";
|
|||||||
import { PreloadState } from "./states/preload";
|
import { PreloadState } from "./states/preload";
|
||||||
import { SettingsState } from "./states/settings";
|
import { SettingsState } from "./states/settings";
|
||||||
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
||||||
|
import { RestrictionManager } from "./core/restriction_manager";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
|
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
|
||||||
@ -70,6 +71,9 @@ export class Application {
|
|||||||
this.inputMgr = new InputDistributor(this);
|
this.inputMgr = new InputDistributor(this);
|
||||||
this.backgroundResourceLoader = new BackgroundResourcesLoader(this);
|
this.backgroundResourceLoader = new BackgroundResourcesLoader(this);
|
||||||
|
|
||||||
|
// Restrictions (Like demo etc)
|
||||||
|
this.restrictionMgr = new RestrictionManager(this);
|
||||||
|
|
||||||
// Platform dependent stuff
|
// Platform dependent stuff
|
||||||
|
|
||||||
/** @type {StorageInterface} */
|
/** @type {StorageInterface} */
|
||||||
|
|||||||
@ -1,51 +1,9 @@
|
|||||||
export const CHANGELOG = [
|
export const CHANGELOG = [
|
||||||
{
|
{
|
||||||
version: "1.2.0",
|
version: "1.2.0",
|
||||||
date: "unreleased",
|
date: "09.10.2020",
|
||||||
entries: [
|
entries: [
|
||||||
"WIRES",
|
"⚠️⚠️This update is HUGE, view the full changelog <a href='https://shapez.io/wires/' target='_blank'>here</a>! ⚠️⚠️",
|
||||||
"Reworked menu UI design (by dengr1605)",
|
|
||||||
"Allow holding ALT in belt planner to reverse direction (by jakobhellermann)",
|
|
||||||
"Clear cursor when trying to pipette the same building twice (by hexy)",
|
|
||||||
"Fixed level 18 stacker bug: If you experienced it already, you know it, if not, I don't want to spoiler (by hexy)",
|
|
||||||
"Added keybinding to close menus (by isaisstillalive / Sandwichs-del)",
|
|
||||||
"Fix rare crash regarding the buildings toolbar (by isaisstillalive)",
|
|
||||||
"Fixed some phrases (by EnderDoom77)",
|
|
||||||
"Zoom towards mouse cursor (by Dimava)",
|
|
||||||
"Added multiple settings to optimize the performance",
|
|
||||||
"Updated the soundtrack again, it is now 40 minutes in total!",
|
|
||||||
"Added a button to the statistics dialog to disable the sorting (by squeek502)",
|
|
||||||
"Tier 2 tunnels are now 9 tiles wide, so the gap between is 8 tiles (double the tier 1 range)",
|
|
||||||
"Updated and added new translations (Thanks to all contributors!)",
|
|
||||||
"Show connected chained miners on hover",
|
|
||||||
"Added setting to be able to delete buildings while placing (inspired by hexy)",
|
|
||||||
"You can now adjust the sound and music volumes! (inspired by Yoshie2000)",
|
|
||||||
"Some hud elements now have reduced opacity when hovering, so you can see through (inspired by mvb005)",
|
|
||||||
"Mark pinned shapes in statistics dialog and show them first (inspired by davidburhans)",
|
|
||||||
"Added setting to show chunk borders",
|
|
||||||
"Quad painters have been reworked! They now are integrated with the wires, and only paint the shape when the value is 1 (inspired by dengr1605)",
|
|
||||||
"There are now compact 1x1 balancers available to be unlocked!",
|
|
||||||
"Replaced level completion sound to be less distracting",
|
|
||||||
"Allow editing waypoints (by isaisstillalive)",
|
|
||||||
"Show confirmation when cutting area which is too expensive to get pasted again (by isaisstillalive)",
|
|
||||||
"Show mouse and camera tile on debug overlay (F4) (by dengr)",
|
|
||||||
"Fix belt planner placing the belt when a dialog opens in the meantime",
|
|
||||||
"Added confirmation when deleting a savegame",
|
|
||||||
"Make chained mainer the default and only option after unlocking it",
|
|
||||||
"Fixed tunnels entrances connecting to exits sometimes when they shouldn't",
|
|
||||||
"You can now pan the map with your mouse by moving the cursor to the edges of the screen!",
|
|
||||||
"Added setting to auto select the extractor when pipetting a resource patch (by Exund)",
|
|
||||||
"You can now change the unit (seconds / minutes / hours) in the statistics dialog",
|
|
||||||
"The initial belt planner direction is now based on the cursor movement (by MizardX)",
|
|
||||||
"Fix preferred variant not getting saved when clicking on the hud (by Danacus)",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
version: "1.1.19",
|
|
||||||
date: "02.07.2020",
|
|
||||||
entries: [
|
|
||||||
"There are now notifications every 15 minutes in the demo version to buy the full version (For further details and the reason, check the #surveys channel in the Discord)",
|
|
||||||
"I'm still working on the wires update, I hope to release it mid july!",
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { Signal } from "./signal";
|
|||||||
import { SOUNDS, MUSIC } from "../platform/sound";
|
import { SOUNDS, MUSIC } from "../platform/sound";
|
||||||
import { AtlasDefinition, atlasFiles } from "./atlas_definitions";
|
import { AtlasDefinition, atlasFiles } from "./atlas_definitions";
|
||||||
import { initBuildingCodesAfterResourcesLoaded } from "../game/meta_building_registry";
|
import { initBuildingCodesAfterResourcesLoaded } from "../game/meta_building_registry";
|
||||||
|
import { cachebust } from "./cachebust";
|
||||||
|
|
||||||
const logger = createLogger("background_loader");
|
const logger = createLogger("background_loader");
|
||||||
|
|
||||||
@ -110,6 +111,7 @@ export class BackgroundResourcesLoader {
|
|||||||
essentialBareGameSounds,
|
essentialBareGameSounds,
|
||||||
essentialBareGameAtlases
|
essentialBareGameAtlases
|
||||||
)
|
)
|
||||||
|
.then(() => this.internalPreloadCss("async-resources.scss"))
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.warn("⏰ Failed to load essentials for bare game:", err);
|
logger.warn("⏰ Failed to load essentials for bare game:", err);
|
||||||
})
|
})
|
||||||
@ -136,6 +138,21 @@ export class BackgroundResourcesLoader {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internalPreloadCss(name) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const link = document.createElement("link");
|
||||||
|
|
||||||
|
link.onload = resolve;
|
||||||
|
link.onerror = reject;
|
||||||
|
|
||||||
|
link.setAttribute("rel", "stylesheet");
|
||||||
|
link.setAttribute("media", "all");
|
||||||
|
link.setAttribute("type", "text/css");
|
||||||
|
link.setAttribute("href", cachebust("async-resources.css"));
|
||||||
|
document.head.appendChild(link);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Array<string>} sprites
|
* @param {Array<string>} sprites
|
||||||
* @param {Array<string>} sounds
|
* @param {Array<string>} sounds
|
||||||
|
|||||||
@ -146,8 +146,6 @@ export class ClickDetector {
|
|||||||
this.touchend.removeAll();
|
this.touchend.removeAll();
|
||||||
this.touchcancel.removeAll();
|
this.touchcancel.removeAll();
|
||||||
|
|
||||||
// TODO: Remove pointer captures
|
|
||||||
|
|
||||||
this.element = null;
|
this.element = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import { queryParamOptions } from "./query_parameters";
|
|
||||||
|
|
||||||
export const IS_DEBUG =
|
export const IS_DEBUG =
|
||||||
G_IS_DEV &&
|
G_IS_DEV &&
|
||||||
typeof window !== "undefined" &&
|
typeof window !== "undefined" &&
|
||||||
@ -7,23 +5,23 @@ export const IS_DEBUG =
|
|||||||
(window.location.host.indexOf("localhost:") >= 0 || window.location.host.indexOf("192.168.0.") >= 0) &&
|
(window.location.host.indexOf("localhost:") >= 0 || window.location.host.indexOf("192.168.0.") >= 0) &&
|
||||||
window.location.search.indexOf("nodebug") < 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 SUPPORT_TOUCH = false;
|
||||||
|
|
||||||
|
export const IS_MAC = navigator.platform.toLowerCase().indexOf("mac") >= 0;
|
||||||
|
|
||||||
const smoothCanvas = true;
|
const smoothCanvas = true;
|
||||||
|
|
||||||
export const THIRDPARTY_URLS = {
|
export const THIRDPARTY_URLS = {
|
||||||
discord: "https://discord.gg/HN7EVzV",
|
discord: "https://discord.gg/HN7EVzV",
|
||||||
github: "https://github.com/tobspr/shapez.io",
|
github: "https://github.com/tobspr/shapez.io",
|
||||||
reddit: "https://www.reddit.com/r/shapezio",
|
reddit: "https://www.reddit.com/r/shapezio",
|
||||||
|
shapeViewer: "https://viewer.shapez.io",
|
||||||
|
|
||||||
standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/",
|
standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const A_B_TESTING_LINK_TYPE = Math.random() > 0.5 ? "steam_1_pr" : "steam_2_npr";
|
||||||
|
|
||||||
export const globalConfig = {
|
export const globalConfig = {
|
||||||
// Size of a single tile in Pixels.
|
// Size of a single tile in Pixels.
|
||||||
// NOTICE: Update webpack.production.config too!
|
// NOTICE: Update webpack.production.config too!
|
||||||
@ -61,7 +59,7 @@ export const globalConfig = {
|
|||||||
|
|
||||||
undergroundBeltMaxTilesByTier: [5, 9],
|
undergroundBeltMaxTilesByTier: [5, 9],
|
||||||
|
|
||||||
readerAnalyzeIntervalSeconds: G_IS_DEV ? 3 : 10,
|
readerAnalyzeIntervalSeconds: 10,
|
||||||
|
|
||||||
buildingSpeeds: {
|
buildingSpeeds: {
|
||||||
cutter: 1 / 4,
|
cutter: 1 / 4,
|
||||||
@ -134,3 +132,8 @@ if (G_IS_DEV && globalConfig.debug.renderForTrailer) {
|
|||||||
if (globalConfig.debug.fastGameEnter) {
|
if (globalConfig.debug.fastGameEnter) {
|
||||||
globalConfig.debug.noArtificialDelays = true;
|
globalConfig.debug.noArtificialDelays = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (G_IS_DEV && globalConfig.debug.noArtificialDelays) {
|
||||||
|
globalConfig.warmupTimeSecondsFast = 0;
|
||||||
|
globalConfig.warmupTimeSecondsRegular = 0;
|
||||||
|
}
|
||||||
|
|||||||
@ -19,7 +19,6 @@ export class DrawParameters {
|
|||||||
/** @type {number} */
|
/** @type {number} */
|
||||||
this.zoomLevel = zoomLevel;
|
this.zoomLevel = zoomLevel;
|
||||||
|
|
||||||
// FIXME: Not really nice
|
|
||||||
/** @type {GameRoot} */
|
/** @type {GameRoot} */
|
||||||
this.root = root;
|
this.root = root;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,17 @@ import { getStringForKeyCode } from "../game/key_action_mapper";
|
|||||||
import { createLogger } from "./logging";
|
import { createLogger } from "./logging";
|
||||||
import { T } from "../translations";
|
import { T } from "../translations";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ***************************************************
|
||||||
|
*
|
||||||
|
* LEGACY CODE WARNING
|
||||||
|
*
|
||||||
|
* This is old code from yorg3.io and needs to be refactored
|
||||||
|
* @TODO
|
||||||
|
*
|
||||||
|
* ***************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
const kbEnter = 13;
|
const kbEnter = 13;
|
||||||
const kbCancel = 27;
|
const kbCancel = 27;
|
||||||
|
|
||||||
@ -60,6 +71,8 @@ export class Dialog {
|
|||||||
this.buttonSignals[buttonId] = new Signal();
|
this.buttonSignals[buttonId] = new Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.valueChosen = new Signal();
|
||||||
|
|
||||||
this.timeouts = [];
|
this.timeouts = [];
|
||||||
this.clickDetectors = [];
|
this.clickDetectors = [];
|
||||||
|
|
||||||
@ -164,7 +177,7 @@ export class Dialog {
|
|||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
button.classList.remove("timedButton");
|
button.classList.remove("timedButton");
|
||||||
arrayDeleteValue(this.timeouts, timeout);
|
arrayDeleteValue(this.timeouts, timeout);
|
||||||
}, 5000);
|
}, 3000);
|
||||||
this.timeouts.push(timeout);
|
this.timeouts.push(timeout);
|
||||||
}
|
}
|
||||||
if (isEnter || isEscape) {
|
if (isEnter || isEscape) {
|
||||||
@ -431,10 +444,12 @@ export class DialogWithForm extends Dialog {
|
|||||||
for (let i = 0; i < this.formElements.length; ++i) {
|
for (let i = 0; i < this.formElements.length; ++i) {
|
||||||
const elem = this.formElements[i];
|
const elem = this.formElements[i];
|
||||||
elem.bindEvents(div, this.clickDetectors);
|
elem.bindEvents(div, this.clickDetectors);
|
||||||
|
elem.valueChosen.add(this.closeRequested.dispatch, this.closeRequested);
|
||||||
|
elem.valueChosen.add(this.valueChosen.dispatch, this.valueChosen);
|
||||||
}
|
}
|
||||||
|
|
||||||
waitNextFrame().then(() => {
|
waitNextFrame().then(() => {
|
||||||
this.formElements[0].focus();
|
this.formElements[this.formElements.length - 1].focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
|
|||||||
@ -1,9 +1,24 @@
|
|||||||
|
import { BaseItem } from "../game/base_item";
|
||||||
import { ClickDetector } from "./click_detector";
|
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 {
|
export class FormElement {
|
||||||
constructor(id, label) {
|
constructor(id, label) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.label = label;
|
this.label = label;
|
||||||
|
|
||||||
|
this.valueChosen = new Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
getHtml() {
|
getHtml() {
|
||||||
@ -148,3 +163,70 @@ export class FormElementCheckbox extends FormElement {
|
|||||||
|
|
||||||
focus(parent) {}
|
focus(parent) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FormElementItemChooser extends FormElement {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {object} param0
|
||||||
|
* @param {string} param0.id
|
||||||
|
* @param {string=} param0.label
|
||||||
|
* @param {Array<BaseItem>} param0.items
|
||||||
|
*/
|
||||||
|
constructor({ id, label, items = [] }) {
|
||||||
|
super(id, label);
|
||||||
|
this.items = items;
|
||||||
|
this.element = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {BaseItem}
|
||||||
|
*/
|
||||||
|
this.chosenItem = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHtml() {
|
||||||
|
let classes = [];
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="formElement">
|
||||||
|
${this.label ? `<label>${this.label}</label>` : ""}
|
||||||
|
<div class="ingameItemChooser input" data-formId="${this.id}"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {HTMLElement} parent
|
||||||
|
* @param {Array<ClickDetector>} clickTrackers
|
||||||
|
*/
|
||||||
|
bindEvents(parent, clickTrackers) {
|
||||||
|
this.element = this.getFormElement(parent);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.items.length; ++i) {
|
||||||
|
const item = this.items[i];
|
||||||
|
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
canvas.width = 128;
|
||||||
|
canvas.height = 128;
|
||||||
|
const context = canvas.getContext("2d");
|
||||||
|
item.drawFullSizeOnCanvas(context, 128);
|
||||||
|
this.element.appendChild(canvas);
|
||||||
|
|
||||||
|
const detector = new ClickDetector(canvas, {});
|
||||||
|
clickTrackers.push(detector);
|
||||||
|
detector.click.add(() => {
|
||||||
|
this.chosenItem = item;
|
||||||
|
this.valueChosen.dispatch(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {}
|
||||||
|
}
|
||||||
|
|||||||
@ -81,10 +81,6 @@ export class ReadWriteProxy {
|
|||||||
return this.writeAsync();
|
return this.writeAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentData() {
|
|
||||||
return this.currentData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {object} obj
|
* @param {object} obj
|
||||||
@ -173,7 +169,7 @@ export class ReadWriteProxy {
|
|||||||
// Check for errors during read
|
// Check for errors during read
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (err === FILE_NOT_FOUND) {
|
if (err === FILE_NOT_FOUND) {
|
||||||
logger.log("File not found, using default data");
|
logger.error("File not found, using default data");
|
||||||
|
|
||||||
// File not found or unreadable, assume default file
|
// File not found or unreadable, assume default file
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
|
|||||||
154
src/js/core/restriction_manager.js
Normal file
154
src/js/core/restriction_manager.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -108,17 +108,6 @@ export class RandomNumberGenerator {
|
|||||||
assert(max > min, "rng: max <= min");
|
assert(max > min, "rng: max <= min");
|
||||||
return Math.floor(this.next() * (max - min) + 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
|
* @param {number} min
|
||||||
|
|||||||
@ -708,3 +708,83 @@ export function dirInterval(key, frames, object, premessage, ...args) {
|
|||||||
}
|
}
|
||||||
logIntervals[key] = interval;
|
logIntervals[key] = interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills in a <link> tag
|
||||||
|
* @param {string} translation
|
||||||
|
* @param {string} link
|
||||||
|
*/
|
||||||
|
export function fillInLinkIntoTranslation(translation, link) {
|
||||||
|
return translation
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|||||||
@ -28,6 +28,15 @@ export class BaseItem extends BasicSerializableObject {
|
|||||||
return "shape";
|
return "shape";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string id of the item
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getAsCopyableKey() {
|
||||||
|
abstract;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if the item equals the other itme
|
* Returns if the item equals the other itme
|
||||||
* @param {BaseItem} other
|
* @param {BaseItem} other
|
||||||
@ -51,6 +60,15 @@ export class BaseItem extends BasicSerializableObject {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the item to a canvas
|
||||||
|
* @param {CanvasRenderingContext2D} context
|
||||||
|
* @param {number} size
|
||||||
|
*/
|
||||||
|
drawFullSizeOnCanvas(context, size) {
|
||||||
|
abstract;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the item at the given position
|
* Draws the item at the given position
|
||||||
* @param {number} x
|
* @param {number} x
|
||||||
|
|||||||
@ -1112,7 +1112,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
|
|
||||||
isFirstItemProcessed = false;
|
isFirstItemProcessed = false;
|
||||||
this.spacingToFirstItem += clampedProgress;
|
this.spacingToFirstItem += clampedProgress;
|
||||||
if (remainingVelocity < 0.01) {
|
if (remainingVelocity < 1e-7) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,9 @@
|
|||||||
|
import { globalConfig } from "../core/config";
|
||||||
import { DrawParameters } from "../core/draw_parameters";
|
import { DrawParameters } from "../core/draw_parameters";
|
||||||
import { Loader } from "../core/loader";
|
import { findNiceIntegerValue } from "../core/utils";
|
||||||
import { createLogger } from "../core/logging";
|
|
||||||
import { Vector } from "../core/vector";
|
import { Vector } from "../core/vector";
|
||||||
import { Entity } from "./entity";
|
import { Entity } from "./entity";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { findNiceIntegerValue } from "../core/utils";
|
|
||||||
import { blueprintShape } from "./upgrades";
|
|
||||||
import { globalConfig } from "../core/config";
|
|
||||||
|
|
||||||
const logger = createLogger("blueprint");
|
|
||||||
|
|
||||||
export class Blueprint {
|
export class Blueprint {
|
||||||
/**
|
/**
|
||||||
@ -174,7 +169,7 @@ export class Blueprint {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
canAfford(root) {
|
canAfford(root) {
|
||||||
return root.hubGoals.getShapesStoredByKey(blueprintShape) >= this.getCost();
|
return root.hubGoals.getShapesStoredByKey(root.gameMode.getBlueprintShapeKey()) >= this.getCost();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -41,7 +41,7 @@ const variantsCache = new Map();
|
|||||||
export function registerBuildingVariant(
|
export function registerBuildingVariant(
|
||||||
code,
|
code,
|
||||||
meta,
|
meta,
|
||||||
variant = "default" /* FIXME: Circular dependency, actually its defaultBuildingVariant */,
|
variant = "default" /* @TODO: Circular dependency, actually its defaultBuildingVariant */,
|
||||||
rotationVariant = 0
|
rotationVariant = 0
|
||||||
) {
|
) {
|
||||||
assert(!gBuildingVariants[code], "Duplicate id: " + code);
|
assert(!gBuildingVariants[code], "Duplicate id: " + code);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
|||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { MetaBuilding } from "../meta_building";
|
import { MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
|
|
||||||
const overlayMatrix = generateMatrixRotations([1, 1, 0, 1, 1, 1, 0, 1, 0]);
|
const overlayMatrix = generateMatrixRotations([1, 1, 0, 1, 1, 1, 0, 1, 0]);
|
||||||
|
|
||||||
@ -21,8 +22,7 @@ export class MetaAnalyzerBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
// @todo
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_virtual_processing);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {"wires"} **/
|
/** @returns {"wires"} **/
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { BeltComponent } from "../components/belt";
|
|||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { MetaBuilding } from "../meta_building";
|
import { MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
|
import { THEME } from "../theme";
|
||||||
|
|
||||||
export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right];
|
export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right];
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ export class MetaBeltBuilding extends MetaBuilding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSilhouetteColor() {
|
getSilhouetteColor() {
|
||||||
return "#777";
|
return THEME.map.chunkOverview.beltColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlacementSound() {
|
getPlacementSound() {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
|||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { MetaBuilding } from "../meta_building";
|
import { MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
|
|
||||||
export class MetaComparatorBuilding extends MetaBuilding {
|
export class MetaComparatorBuilding extends MetaBuilding {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -18,8 +19,7 @@ export class MetaComparatorBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
// @todo
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_virtual_processing);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {"wires"} **/
|
/** @returns {"wires"} **/
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export class MetaFilterBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_filters_and_levers);
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDimensions() {
|
getDimensions() {
|
||||||
|
|||||||
44
src/js/game/buildings/item_producer.js
Normal file
44
src/js/game/buildings/item_producer.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { enumDirection, Vector } from "../../core/vector";
|
||||||
|
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||||
|
import { ItemProducerComponent } from "../components/item_producer";
|
||||||
|
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
||||||
|
import { Entity } from "../entity";
|
||||||
|
import { MetaBuilding } from "../meta_building";
|
||||||
|
|
||||||
|
export class MetaItemProducerBuilding extends MetaBuilding {
|
||||||
|
constructor() {
|
||||||
|
super("item_producer");
|
||||||
|
}
|
||||||
|
|
||||||
|
getSilhouetteColor() {
|
||||||
|
return "#b37dcd";
|
||||||
|
}
|
||||||
|
|
||||||
|
getShowWiresLayerPreview() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the entity at the given location
|
||||||
|
* @param {Entity} entity
|
||||||
|
*/
|
||||||
|
setupEntityComponents(entity) {
|
||||||
|
entity.addComponent(
|
||||||
|
new ItemEjectorComponent({
|
||||||
|
slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
entity.addComponent(
|
||||||
|
new WiredPinsComponent({
|
||||||
|
slots: [
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
type: enumPinSlotType.logicalAcceptor,
|
||||||
|
direction: enumDirection.bottom,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
entity.addComponent(new ItemProducerComponent());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,7 +20,7 @@ export class MetaLeverBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_filters_and_levers);
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDimensions() {
|
getDimensions() {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
|||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate";
|
import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate";
|
||||||
import { generateMatrixRotations } from "../../core/utils";
|
import { generateMatrixRotations } from "../../core/utils";
|
||||||
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumLogicGateVariants = {
|
export const enumLogicGateVariants = {
|
||||||
@ -48,8 +49,7 @@ export class MetaLogicGateBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
// @todo
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_logic_gates);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {"wires"} **/
|
/** @returns {"wires"} **/
|
||||||
|
|||||||
@ -71,7 +71,7 @@ export class MetaPainterBuilding extends MetaBuilding {
|
|||||||
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_painter_double)) {
|
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_painter_double)) {
|
||||||
variants.push(enumPainterVariants.double);
|
variants.push(enumPainterVariants.double);
|
||||||
}
|
}
|
||||||
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_painter_quad)) {
|
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers)) {
|
||||||
variants.push(enumPainterVariants.quad);
|
variants.push(enumPainterVariants.quad);
|
||||||
}
|
}
|
||||||
return variants;
|
return variants;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
|||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
|
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumTransistorVariants = {
|
export const enumTransistorVariants = {
|
||||||
@ -29,8 +30,7 @@ export class MetaTransistorBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
// @todo
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_logic_gates);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {"wires"} **/
|
/** @returns {"wires"} **/
|
||||||
|
|||||||
@ -191,7 +191,6 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
|
|||||||
) {
|
) {
|
||||||
tile = tile.addScalars(searchVector.x, searchVector.y);
|
tile = tile.addScalars(searchVector.x, searchVector.y);
|
||||||
|
|
||||||
/* WIRES: FIXME */
|
|
||||||
const contents = root.map.getTileContent(tile, "regular");
|
const contents = root.map.getTileContent(tile, "regular");
|
||||||
if (contents) {
|
if (contents) {
|
||||||
const undergroundComp = contents.components.UndergroundBelt;
|
const undergroundComp = contents.components.UndergroundBelt;
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins";
|
|||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
|
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
import { MetaCutterBuilding } from "./cutter";
|
import { MetaCutterBuilding } from "./cutter";
|
||||||
import { MetaPainterBuilding } from "./painter";
|
import { MetaPainterBuilding } from "./painter";
|
||||||
import { MetaRotaterBuilding } from "./rotater";
|
import { MetaRotaterBuilding } from "./rotater";
|
||||||
@ -47,8 +48,7 @@ export class MetaVirtualProcessorBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
// @todo
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_virtual_processing);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {"wires"} **/
|
/** @returns {"wires"} **/
|
||||||
|
|||||||
@ -82,7 +82,7 @@ export class MetaWireBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_filters_and_levers);
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export class MetaWireTunnelBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_filters_and_levers);
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,6 +53,6 @@ export class MetaWireTunnelBuilding extends MetaBuilding {
|
|||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
*/
|
*/
|
||||||
setupEntityComponents(entity) {
|
setupEntityComponents(entity) {
|
||||||
entity.addComponent(new WireTunnelComponent({}));
|
entity.addComponent(new WireTunnelComponent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -353,7 +353,7 @@ export class Camera extends BasicSerializableObject {
|
|||||||
.add(() => (this.desiredZoom = this.zoomLevel * 1.2));
|
.add(() => (this.desiredZoom = this.zoomLevel * 1.2));
|
||||||
mapper
|
mapper
|
||||||
.getBinding(KEYMAPPINGS.navigation.mapZoomOut)
|
.getBinding(KEYMAPPINGS.navigation.mapZoomOut)
|
||||||
.add(() => (this.desiredZoom = this.zoomLevel * 0.8));
|
.add(() => (this.desiredZoom = this.zoomLevel / 1.2));
|
||||||
|
|
||||||
mapper.getBinding(KEYMAPPINGS.navigation.centerMap).add(() => this.centerOnMap());
|
mapper.getBinding(KEYMAPPINGS.navigation.centerMap).add(() => this.centerOnMap());
|
||||||
}
|
}
|
||||||
@ -502,16 +502,20 @@ export class Camera extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
const prevZoom = this.zoomLevel;
|
const prevZoom = this.zoomLevel;
|
||||||
|
|
||||||
const delta = Math.sign(event.deltaY) * -0.15 * this.root.app.settings.getScrollWheelSensitivity();
|
const scale = 1 + 0.15 * this.root.app.settings.getScrollWheelSensitivity();
|
||||||
assert(Number.isFinite(delta), "Got invalid delta in mouse wheel event: " + event.deltaY);
|
assert(Number.isFinite(scale), "Got invalid scale in mouse wheel event: " + event.deltaY);
|
||||||
assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *before* wheel: " + this.zoomLevel);
|
assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *before* wheel: " + this.zoomLevel);
|
||||||
this.zoomLevel *= 1 + delta;
|
this.zoomLevel *= event.deltaY < 0 ? scale : 1 / scale;
|
||||||
assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *after* wheel: " + this.zoomLevel);
|
assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *after* wheel: " + this.zoomLevel);
|
||||||
|
|
||||||
this.clampZoomLevel();
|
this.clampZoomLevel();
|
||||||
this.desiredZoom = null;
|
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) {
|
if (mousePosition) {
|
||||||
const worldPos = this.root.camera.screenToWorld(mousePosition);
|
const worldPos = this.root.camera.screenToWorld(mousePosition);
|
||||||
const worldDelta = worldPos.sub(this.center);
|
const worldDelta = worldPos.sub(this.center);
|
||||||
@ -939,6 +943,7 @@ export class Camera extends BasicSerializableObject {
|
|||||||
this.zoomLevel = this.zoomLevel * fade + this.desiredZoom * (1 - fade);
|
this.zoomLevel = this.zoomLevel * fade + this.desiredZoom * (1 - fade);
|
||||||
assert(Number.isFinite(this.zoomLevel), "Zoom level is NaN after fade: " + this.zoomLevel);
|
assert(Number.isFinite(this.zoomLevel), "Zoom level is NaN after fade: " + this.zoomLevel);
|
||||||
} else {
|
} else {
|
||||||
|
this.zoomLevel = this.desiredZoom;
|
||||||
this.desiredZoom = null;
|
this.desiredZoom = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { WireTunnelComponent } from "./components/wire_tunnel";
|
|||||||
import { DisplayComponent } from "./components/display";
|
import { DisplayComponent } from "./components/display";
|
||||||
import { BeltReaderComponent } from "./components/belt_reader";
|
import { BeltReaderComponent } from "./components/belt_reader";
|
||||||
import { FilterComponent } from "./components/filter";
|
import { FilterComponent } from "./components/filter";
|
||||||
|
import { ItemProducerComponent } from "./components/item_producer";
|
||||||
|
|
||||||
export function initComponentRegistry() {
|
export function initComponentRegistry() {
|
||||||
gComponentRegistry.register(StaticMapEntityComponent);
|
gComponentRegistry.register(StaticMapEntityComponent);
|
||||||
@ -39,6 +40,7 @@ export function initComponentRegistry() {
|
|||||||
gComponentRegistry.register(DisplayComponent);
|
gComponentRegistry.register(DisplayComponent);
|
||||||
gComponentRegistry.register(BeltReaderComponent);
|
gComponentRegistry.register(BeltReaderComponent);
|
||||||
gComponentRegistry.register(FilterComponent);
|
gComponentRegistry.register(FilterComponent);
|
||||||
|
gComponentRegistry.register(ItemProducerComponent);
|
||||||
|
|
||||||
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS
|
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class ItemEjectorComponent extends Component {
|
|||||||
static getSchema() {
|
static getSchema() {
|
||||||
// The cachedDestSlot, cachedTargetEntity fields are not serialized.
|
// The cachedDestSlot, cachedTargetEntity fields are not serialized.
|
||||||
return {
|
return {
|
||||||
slots: types.array(
|
slots: types.fixedSizeArray(
|
||||||
types.structured({
|
types.structured({
|
||||||
item: types.nullable(typeItemSingleton),
|
item: types.nullable(typeItemSingleton),
|
||||||
progress: types.float,
|
progress: types.float,
|
||||||
|
|||||||
7
src/js/game/components/item_producer.js
Normal file
7
src/js/game/components/item_producer.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Component } from "../component";
|
||||||
|
|
||||||
|
export class ItemProducerComponent extends Component {
|
||||||
|
static getId() {
|
||||||
|
return "ItemProducer";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -31,7 +31,7 @@ export class WiredPinsComponent extends Component {
|
|||||||
|
|
||||||
static getSchema() {
|
static getSchema() {
|
||||||
return {
|
return {
|
||||||
slots: types.array(
|
slots: types.fixedSizeArray(
|
||||||
types.structured({
|
types.structured({
|
||||||
value: types.nullable(typeItemSingleton),
|
value: types.nullable(typeItemSingleton),
|
||||||
})
|
})
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import { KeyActionMapper } from "./key_action_mapper";
|
|||||||
import { GameLogic } from "./logic";
|
import { GameLogic } from "./logic";
|
||||||
import { MapView } from "./map_view";
|
import { MapView } from "./map_view";
|
||||||
import { defaultBuildingVariant } from "./meta_building";
|
import { defaultBuildingVariant } from "./meta_building";
|
||||||
|
import { RegularGameMode } from "./modes/regular";
|
||||||
import { ProductionAnalytics } from "./production_analytics";
|
import { ProductionAnalytics } from "./production_analytics";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { ShapeDefinitionManager } from "./shape_definition_manager";
|
import { ShapeDefinitionManager } from "./shape_definition_manager";
|
||||||
@ -101,6 +102,9 @@ export class GameCore {
|
|||||||
// Needs to come first
|
// Needs to come first
|
||||||
root.dynamicTickrate = new DynamicTickrate(root);
|
root.dynamicTickrate = new DynamicTickrate(root);
|
||||||
|
|
||||||
|
// Init game mode
|
||||||
|
root.gameMode = new RegularGameMode(root);
|
||||||
|
|
||||||
// Init classes
|
// Init classes
|
||||||
root.camera = new Camera(root);
|
root.camera = new Camera(root);
|
||||||
root.map = new MapView(root);
|
root.map = new MapView(root);
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { WireTunnelComponent } from "./components/wire_tunnel";
|
|||||||
import { DisplayComponent } from "./components/display";
|
import { DisplayComponent } from "./components/display";
|
||||||
import { BeltReaderComponent } from "./components/belt_reader";
|
import { BeltReaderComponent } from "./components/belt_reader";
|
||||||
import { FilterComponent } from "./components/filter";
|
import { FilterComponent } from "./components/filter";
|
||||||
|
import { ItemProducerComponent } from "./components/item_producer";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,6 +86,9 @@ export class EntityComponentStorage {
|
|||||||
/** @type {FilterComponent} */
|
/** @type {FilterComponent} */
|
||||||
this.Filter;
|
this.Filter;
|
||||||
|
|
||||||
|
/** @type {ItemProducerComponent} */
|
||||||
|
this.ItemProducer;
|
||||||
|
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -188,26 +188,6 @@ export class EntityManager extends BasicSerializableObject {
|
|||||||
else return [...set.values()];
|
else return [...set.values()];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated lol
|
|
||||||
// /**
|
|
||||||
// * Return all of a given class. This is SLOW!
|
|
||||||
// * @param {object} entityClass
|
|
||||||
// * @returns {Array<Entity>} entities
|
|
||||||
// */
|
|
||||||
// getAllOfClass(entityClass) {
|
|
||||||
// // FIXME: Slow
|
|
||||||
// // Fine! I will!
|
|
||||||
// const result = [];
|
|
||||||
// const entities = [...this.entities.values()];
|
|
||||||
// for (let i = entities.length; i >= 0; --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
|
* Unregisters all components of an entity from the component to entity mapping
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
|
|||||||
71
src/js/game/game_mode.js
Normal file
71
src/js/game/game_mode.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -23,6 +23,7 @@ import { DisplaySystem } from "./systems/display";
|
|||||||
import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays";
|
import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays";
|
||||||
import { BeltReaderSystem } from "./systems/belt_reader";
|
import { BeltReaderSystem } from "./systems/belt_reader";
|
||||||
import { FilterSystem } from "./systems/filter";
|
import { FilterSystem } from "./systems/filter";
|
||||||
|
import { ItemProducerSystem } from "./systems/item_producer";
|
||||||
|
|
||||||
const logger = createLogger("game_system_manager");
|
const logger = createLogger("game_system_manager");
|
||||||
|
|
||||||
@ -96,6 +97,9 @@ export class GameSystemManager {
|
|||||||
/** @type {FilterSystem} */
|
/** @type {FilterSystem} */
|
||||||
filter: null,
|
filter: null,
|
||||||
|
|
||||||
|
/** @type {ItemProducerSystem} */
|
||||||
|
itemProducer: null,
|
||||||
|
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
};
|
};
|
||||||
this.systemUpdateOrder = [];
|
this.systemUpdateOrder = [];
|
||||||
@ -130,6 +134,8 @@ export class GameSystemManager {
|
|||||||
|
|
||||||
add("filter", FilterSystem);
|
add("filter", FilterSystem);
|
||||||
|
|
||||||
|
add("itemProducer", ItemProducerSystem);
|
||||||
|
|
||||||
add("itemEjector", ItemEjectorSystem);
|
add("itemEjector", ItemEjectorSystem);
|
||||||
|
|
||||||
add("mapResources", MapResourcesSystem);
|
add("mapResources", MapResourcesSystem);
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils";
|
import { RandomNumberGenerator } from "../core/rng";
|
||||||
|
import { clamp } from "../core/utils";
|
||||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||||
import { enumColors } from "./colors";
|
import { enumColors } from "./colors";
|
||||||
import { enumItemProcessorTypes } from "./components/item_processor";
|
import { enumItemProcessorTypes } from "./components/item_processor";
|
||||||
|
import { enumAnalyticsDataSource } from "./production_analytics";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
||||||
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
|
import { enumHubGoalRewards } from "./tutorial_goals";
|
||||||
import { UPGRADES } from "./upgrades";
|
|
||||||
|
|
||||||
export class HubGoals extends BasicSerializableObject {
|
export class HubGoals extends BasicSerializableObject {
|
||||||
static getId() {
|
static getId() {
|
||||||
@ -18,32 +19,39 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
level: types.uint,
|
level: types.uint,
|
||||||
storedShapes: types.keyValueMap(types.uint),
|
storedShapes: types.keyValueMap(types.uint),
|
||||||
upgradeLevels: types.keyValueMap(types.uint),
|
upgradeLevels: types.keyValueMap(types.uint),
|
||||||
|
|
||||||
currentGoal: types.structured({
|
|
||||||
definition: types.knownType(ShapeDefinition),
|
|
||||||
required: types.uint,
|
|
||||||
reward: types.nullable(types.enum(enumHubGoalRewards)),
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialize(data) {
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} data
|
||||||
|
* @param {GameRoot} root
|
||||||
|
*/
|
||||||
|
deserialize(data, root) {
|
||||||
const errorCode = super.deserialize(data);
|
const errorCode = super.deserialize(data);
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// Compute gained rewards
|
||||||
for (let i = 0; i < this.level - 1; ++i) {
|
for (let i = 0; i < this.level - 1; ++i) {
|
||||||
if (i < tutorialGoals.length) {
|
if (i < levels.length) {
|
||||||
const reward = tutorialGoals[i].reward;
|
const reward = levels[i].reward;
|
||||||
this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1;
|
this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute upgrade improvements
|
// Compute upgrade improvements
|
||||||
for (const upgradeId in UPGRADES) {
|
const upgrades = this.root.gameMode.getUpgrades();
|
||||||
const tiers = UPGRADES[upgradeId];
|
for (const upgradeId in upgrades) {
|
||||||
|
const tiers = upgrades[upgradeId];
|
||||||
const level = this.upgradeLevels[upgradeId] || 0;
|
const level = this.upgradeLevels[upgradeId] || 0;
|
||||||
let totalImprovement = 1;
|
let totalImprovement = 1;
|
||||||
for (let i = 0; i < level; ++i) {
|
for (let i = 0; i < level; ++i) {
|
||||||
@ -53,15 +61,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute current goal
|
// Compute current goal
|
||||||
const goal = tutorialGoals[this.level - 1];
|
this.computeNextGoal();
|
||||||
if (goal) {
|
|
||||||
this.currentGoal = {
|
|
||||||
/** @type {ShapeDefinition} */
|
|
||||||
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(goal.shape),
|
|
||||||
required: goal.required,
|
|
||||||
reward: goal.reward,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,11 +97,15 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
* @type {Object<string, number>}
|
* @type {Object<string, number>}
|
||||||
*/
|
*/
|
||||||
this.upgradeImprovements = {};
|
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;
|
this.upgradeImprovements[key] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.createNextGoal();
|
this.computeNextGoal();
|
||||||
|
|
||||||
// Allow quickly switching goals in dev mode
|
// Allow quickly switching goals in dev mode
|
||||||
if (G_IS_DEV) {
|
if (G_IS_DEV) {
|
||||||
@ -109,13 +113,26 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
if (ev.key === "b") {
|
if (ev.key === "b") {
|
||||||
// root is not guaranteed to exist within ~0.5s after loading in
|
// root is not guaranteed to exist within ~0.5s after loading in
|
||||||
if (this.root && this.root.app && this.root.app.gameAnalytics) {
|
if (this.root && this.root.app && this.root.app.gameAnalytics) {
|
||||||
this.onGoalCompleted();
|
if (!this.isEndOfDemoReached()) {
|
||||||
|
this.onGoalCompleted();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the end of the demo is reached
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isEndOfDemoReached() {
|
||||||
|
return (
|
||||||
|
!this.root.gameMode.getIsFreeplayAvailable() &&
|
||||||
|
this.level >= this.root.gameMode.getLevelDefinitions().length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns how much of the current shape is stored
|
* Returns how much of the current shape is stored
|
||||||
* @param {ShapeDefinition} definition
|
* @param {ShapeDefinition} definition
|
||||||
@ -150,6 +167,15 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
* Returns how much of the current goal was already delivered
|
* Returns how much of the current goal was already delivered
|
||||||
*/
|
*/
|
||||||
getCurrentGoalDelivered() {
|
getCurrentGoalDelivered() {
|
||||||
|
if (this.currentGoal.throughputOnly) {
|
||||||
|
return (
|
||||||
|
this.root.productionAnalytics.getCurrentShapeRate(
|
||||||
|
enumAnalyticsDataSource.delivered,
|
||||||
|
this.currentGoal.definition
|
||||||
|
) / globalConfig.analyticsSliceDurationSeconds
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return this.getShapesStored(this.currentGoal.definition);
|
return this.getShapesStored(this.currentGoal.definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,36 +210,40 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
this.root.signals.shapeDelivered.dispatch(definition);
|
this.root.signals.shapeDelivered.dispatch(definition);
|
||||||
|
|
||||||
// Check if we have enough for the next level
|
// Check if we have enough for the next level
|
||||||
const targetHash = this.currentGoal.definition.getHash();
|
|
||||||
if (
|
if (
|
||||||
this.storedShapes[targetHash] >= this.currentGoal.required ||
|
this.getCurrentGoalDelivered() >= this.currentGoal.required ||
|
||||||
(G_IS_DEV && globalConfig.debug.rewardsInstant)
|
(G_IS_DEV && globalConfig.debug.rewardsInstant)
|
||||||
) {
|
) {
|
||||||
this.onGoalCompleted();
|
if (!this.isEndOfDemoReached()) {
|
||||||
|
this.onGoalCompleted();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the next goal
|
* Creates the next goal
|
||||||
*/
|
*/
|
||||||
createNextGoal() {
|
computeNextGoal() {
|
||||||
const storyIndex = this.level - 1;
|
const storyIndex = this.level - 1;
|
||||||
if (storyIndex < tutorialGoals.length) {
|
const levels = this.root.gameMode.getLevelDefinitions();
|
||||||
const { shape, required, reward } = tutorialGoals[storyIndex];
|
if (storyIndex < levels.length) {
|
||||||
|
const { shape, required, reward, throughputOnly } = levels[storyIndex];
|
||||||
this.currentGoal = {
|
this.currentGoal = {
|
||||||
/** @type {ShapeDefinition} */
|
/** @type {ShapeDefinition} */
|
||||||
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape),
|
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape),
|
||||||
required,
|
required,
|
||||||
reward,
|
reward,
|
||||||
|
throughputOnly,
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const required = Math.min(200, 4 + (this.level - 27) * 0.25);
|
||||||
this.currentGoal = {
|
this.currentGoal = {
|
||||||
/** @type {ShapeDefinition} */
|
definition: this.computeFreeplayShape(this.level),
|
||||||
definition: this.createRandomShape(),
|
required,
|
||||||
required: findNiceIntegerValue(1000 + Math.pow(this.level * 2000, 0.8)),
|
|
||||||
reward: enumHubGoalRewards.no_reward_freeplay,
|
reward: enumHubGoalRewards.no_reward_freeplay,
|
||||||
|
throughputOnly: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +256,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
|
|
||||||
this.root.app.gameAnalytics.handleLevelCompleted(this.level);
|
this.root.app.gameAnalytics.handleLevelCompleted(this.level);
|
||||||
++this.level;
|
++this.level;
|
||||||
this.createNextGoal();
|
this.computeNextGoal();
|
||||||
|
|
||||||
this.root.signals.storyGoalCompleted.dispatch(this.level - 1, reward);
|
this.root.signals.storyGoalCompleted.dispatch(this.level - 1, reward);
|
||||||
}
|
}
|
||||||
@ -235,7 +265,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
* Returns whether we are playing in free-play
|
* Returns whether we are playing in free-play
|
||||||
*/
|
*/
|
||||||
isFreePlay() {
|
isFreePlay() {
|
||||||
return this.level >= tutorialGoals.length;
|
return this.level >= this.root.gameMode.getLevelDefinitions().length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -243,7 +273,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
* @param {string} upgradeId
|
* @param {string} upgradeId
|
||||||
*/
|
*/
|
||||||
canUnlockUpgrade(upgradeId) {
|
canUnlockUpgrade(upgradeId) {
|
||||||
const tiers = UPGRADES[upgradeId];
|
const tiers = this.root.gameMode.getUpgrades()[upgradeId];
|
||||||
const currentLevel = this.getUpgradeLevel(upgradeId);
|
const currentLevel = this.getUpgradeLevel(upgradeId);
|
||||||
|
|
||||||
if (currentLevel >= tiers.length) {
|
if (currentLevel >= tiers.length) {
|
||||||
@ -272,7 +302,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
*/
|
*/
|
||||||
getAvailableUpgradeCount() {
|
getAvailableUpgradeCount() {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (const upgradeId in UPGRADES) {
|
for (const upgradeId in this.root.gameMode.getUpgrades()) {
|
||||||
if (this.canUnlockUpgrade(upgradeId)) {
|
if (this.canUnlockUpgrade(upgradeId)) {
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
@ -290,7 +320,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradeTiers = UPGRADES[upgradeId];
|
const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId];
|
||||||
const currentLevel = this.getUpgradeLevel(upgradeId);
|
const currentLevel = this.getUpgradeLevel(upgradeId);
|
||||||
|
|
||||||
const tierData = upgradeTiers[currentLevel];
|
const tierData = upgradeTiers[currentLevel];
|
||||||
@ -320,15 +350,85 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Picks random colors which are close to each other
|
||||||
|
* @param {RandomNumberGenerator} rng
|
||||||
|
*/
|
||||||
|
generateRandomColorSet(rng, allowUncolored = false) {
|
||||||
|
const colorWheel = [
|
||||||
|
enumColors.red,
|
||||||
|
enumColors.yellow,
|
||||||
|
enumColors.green,
|
||||||
|
enumColors.cyan,
|
||||||
|
enumColors.blue,
|
||||||
|
enumColors.purple,
|
||||||
|
enumColors.red,
|
||||||
|
enumColors.yellow,
|
||||||
|
];
|
||||||
|
|
||||||
|
const universalColors = [enumColors.white];
|
||||||
|
if (allowUncolored) {
|
||||||
|
universalColors.push(enumColors.uncolored);
|
||||||
|
}
|
||||||
|
const index = rng.nextIntRange(0, colorWheel.length - 2);
|
||||||
|
const pickedColors = colorWheel.slice(index, index + 3);
|
||||||
|
pickedColors.push(rng.choice(universalColors));
|
||||||
|
return pickedColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a (seeded) random shape
|
||||||
|
* @param {number} level
|
||||||
* @returns {ShapeDefinition}
|
* @returns {ShapeDefinition}
|
||||||
*/
|
*/
|
||||||
createRandomShape() {
|
computeFreeplayShape(level) {
|
||||||
const layerCount = clamp(this.level / 25, 2, 4);
|
const layerCount = clamp(this.level / 25, 2, 4);
|
||||||
|
|
||||||
/** @type {Array<import("./shape_definition").ShapeLayer>} */
|
/** @type {Array<import("./shape_definition").ShapeLayer>} */
|
||||||
let layers = [];
|
let layers = [];
|
||||||
|
|
||||||
const randomColor = () => randomChoice(Object.values(enumColors));
|
const rng = new RandomNumberGenerator(this.root.map.seed + "/" + level);
|
||||||
const randomShape = () => randomChoice(Object.values(enumSubShape));
|
|
||||||
|
const colors = this.generateRandomColorSet(rng, level > 35);
|
||||||
|
|
||||||
|
let pickedSymmetry = null; // pairs of quadrants that must be the same
|
||||||
|
let availableShapes = [enumSubShape.rect, enumSubShape.circle, enumSubShape.star];
|
||||||
|
if (rng.next() < 0.5) {
|
||||||
|
pickedSymmetry = [
|
||||||
|
// radial symmetry
|
||||||
|
[0, 2],
|
||||||
|
[1, 3],
|
||||||
|
];
|
||||||
|
availableShapes.push(enumSubShape.windmill); // windmill looks good only in radial symmetry
|
||||||
|
} else {
|
||||||
|
const symmetries = [
|
||||||
|
[
|
||||||
|
// horizontal axis
|
||||||
|
[0, 3],
|
||||||
|
[1, 2],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// vertical axis
|
||||||
|
[0, 1],
|
||||||
|
[2, 3],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// diagonal axis
|
||||||
|
[0, 2],
|
||||||
|
[1],
|
||||||
|
[3],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// other diagonal axis
|
||||||
|
[1, 3],
|
||||||
|
[0],
|
||||||
|
[2],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
pickedSymmetry = rng.choice(symmetries);
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomColor = () => rng.choice(colors);
|
||||||
|
const randomShape = () => rng.choice(Object.values(enumSubShape));
|
||||||
|
|
||||||
let anyIsMissingTwo = false;
|
let anyIsMissingTwo = false;
|
||||||
|
|
||||||
@ -336,23 +436,24 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
/** @type {import("./shape_definition").ShapeLayer} */
|
/** @type {import("./shape_definition").ShapeLayer} */
|
||||||
const layer = [null, null, null, null];
|
const layer = [null, null, null, null];
|
||||||
|
|
||||||
for (let quad = 0; quad < 4; ++quad) {
|
for (let j = 0; j < pickedSymmetry.length; ++j) {
|
||||||
layer[quad] = {
|
const group = pickedSymmetry[j];
|
||||||
subShape: randomShape(),
|
const shape = randomShape();
|
||||||
color: randomColor(),
|
const color = randomColor();
|
||||||
};
|
for (let k = 0; k < group.length; ++k) {
|
||||||
}
|
const quad = group[k];
|
||||||
|
layer[quad] = {
|
||||||
// Sometimes shapes are missing
|
subShape: shape,
|
||||||
if (Math.random() > 0.85) {
|
color,
|
||||||
layer[randomInt(0, 3)] = null;
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sometimes they actually are missing *two* ones!
|
// Sometimes they actually are missing *two* ones!
|
||||||
// Make sure at max only one layer is missing it though, otherwise we could
|
// Make sure at max only one layer is missing it though, otherwise we could
|
||||||
// create an uncreateable shape
|
// create an uncreateable shape
|
||||||
if (Math.random() > 0.95 && !anyIsMissingTwo) {
|
if (level > 75 && rng.next() > 0.95 && !anyIsMissingTwo) {
|
||||||
layer[randomInt(0, 3)] = null;
|
layer[rng.nextIntRange(0, 4)] = null;
|
||||||
anyIsMissingTwo = true;
|
anyIsMissingTwo = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { HUDKeybindingOverlay } from "./parts/keybinding_overlay";
|
|||||||
import { HUDUnlockNotification } from "./parts/unlock_notification";
|
import { HUDUnlockNotification } from "./parts/unlock_notification";
|
||||||
import { HUDGameMenu } from "./parts/game_menu";
|
import { HUDGameMenu } from "./parts/game_menu";
|
||||||
import { HUDShop } from "./parts/shop";
|
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 { HUDMassSelector } from "./parts/mass_selector";
|
||||||
import { HUDVignetteOverlay } from "./parts/vignette_overlay";
|
import { HUDVignetteOverlay } from "./parts/vignette_overlay";
|
||||||
import { HUDStatistics } from "./parts/statistics";
|
import { HUDStatistics } from "./parts/statistics";
|
||||||
@ -45,6 +45,9 @@ import { HUDLeverToggle } from "./parts/lever_toggle";
|
|||||||
import { HUDLayerPreview } from "./parts/layer_preview";
|
import { HUDLayerPreview } from "./parts/layer_preview";
|
||||||
import { HUDMinerHighlight } from "./parts/miner_highlight";
|
import { HUDMinerHighlight } from "./parts/miner_highlight";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
|
import { HUDBetaOverlay } from "./parts/beta_overlay";
|
||||||
|
import { HUDStandaloneAdvantages } from "./parts/standalone_advantages";
|
||||||
|
import { HUDCatMemes } from "./parts/cat_memes";
|
||||||
|
|
||||||
export class GameHUD {
|
export class GameHUD {
|
||||||
/**
|
/**
|
||||||
@ -76,7 +79,6 @@ export class GameHUD {
|
|||||||
pinnedShapes: new HUDPinnedShapes(this.root),
|
pinnedShapes: new HUDPinnedShapes(this.root),
|
||||||
notifications: new HUDNotifications(this.root),
|
notifications: new HUDNotifications(this.root),
|
||||||
settingsMenu: new HUDSettingsMenu(this.root),
|
settingsMenu: new HUDSettingsMenu(this.root),
|
||||||
// betaOverlay: new HUDBetaOverlay(this.root),
|
|
||||||
debugInfo: new HUDDebugInfo(this.root),
|
debugInfo: new HUDDebugInfo(this.root),
|
||||||
dialogs: new HUDModalDialogs(this.root),
|
dialogs: new HUDModalDialogs(this.root),
|
||||||
screenshotExporter: new HUDScreenshotExporter(this.root),
|
screenshotExporter: new HUDScreenshotExporter(this.root),
|
||||||
@ -113,8 +115,10 @@ export class GameHUD {
|
|||||||
this.parts.entityDebugger = new HUDEntityDebugger(this.root);
|
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.watermark = new HUDWatermark(this.root);
|
||||||
|
this.parts.standaloneAdvantages = new HUDStandaloneAdvantages(this.root);
|
||||||
|
this.parts.catMemes = new HUDCatMemes(this.root);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.renderChanges) {
|
if (G_IS_DEV && globalConfig.debug.renderChanges) {
|
||||||
@ -138,6 +142,10 @@ export class GameHUD {
|
|||||||
this.parts.sandboxController = new HUDSandboxController(this.root);
|
this.parts.sandboxController = new HUDSandboxController(this.root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!G_IS_RELEASE && !G_IS_DEV) {
|
||||||
|
this.parts.betaOverlay = new HUDBetaOverlay(this.root);
|
||||||
|
}
|
||||||
|
|
||||||
const frag = document.createDocumentFragment();
|
const frag = document.createDocumentFragment();
|
||||||
for (const key in this.parts) {
|
for (const key in this.parts) {
|
||||||
this.parts[key].createElements(frag);
|
this.parts[key].createElements(frag);
|
||||||
|
|||||||
@ -3,7 +3,12 @@ import { makeDiv } from "../../../core/utils";
|
|||||||
|
|
||||||
export class HUDBetaOverlay extends BaseHUDPart {
|
export class HUDBetaOverlay extends BaseHUDPart {
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
this.element = makeDiv(parent, "ingame_HUD_BetaOverlay", [], "CLOSED BETA");
|
this.element = makeDiv(
|
||||||
|
parent,
|
||||||
|
"ingame_HUD_BetaOverlay",
|
||||||
|
[],
|
||||||
|
"<h2>UNSTABLE BETA VERSION</h2><span>Unfinalized & potential buggy content!</span>"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {}
|
initialize() {}
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { Vector } from "../../../core/vector";
|
|||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { enumMouseButton } from "../../camera";
|
import { enumMouseButton } from "../../camera";
|
||||||
import { KEYMAPPINGS } from "../../key_action_mapper";
|
import { KEYMAPPINGS } from "../../key_action_mapper";
|
||||||
import { blueprintShape } from "../../upgrades";
|
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
import { Blueprint } from "../../blueprint";
|
import { Blueprint } from "../../blueprint";
|
||||||
@ -15,7 +14,9 @@ import { Entity } from "../../entity";
|
|||||||
|
|
||||||
export class HUDBlueprintPlacer extends BaseHUDPart {
|
export class HUDBlueprintPlacer extends BaseHUDPart {
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey(blueprintShape);
|
const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey(
|
||||||
|
this.root.gameMode.getBlueprintShapeKey()
|
||||||
|
);
|
||||||
const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80);
|
const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80);
|
||||||
|
|
||||||
this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``);
|
this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``);
|
||||||
@ -124,7 +125,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
|
|||||||
const tile = worldPos.toTileSpace();
|
const tile = worldPos.toTileSpace();
|
||||||
if (blueprint.tryPlace(this.root, tile)) {
|
if (blueprint.tryPlace(this.root, tile)) {
|
||||||
const cost = blueprint.getCost();
|
const cost = blueprint.getCost();
|
||||||
this.root.hubGoals.takeShapeByKey(blueprintShape, cost);
|
this.root.hubGoals.takeShapeByKey(this.root.gameMode.getBlueprintShapeKey(), cost);
|
||||||
this.root.soundProxy.playUi(SOUNDS.placeBuilding);
|
this.root.soundProxy.playUi(SOUNDS.placeBuilding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -334,7 +334,11 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
|
|||||||
const tileBelow = this.root.map.getLowerLayerContentXY(tile.x, tile.y);
|
const tileBelow = this.root.map.getLowerLayerContentXY(tile.x, tile.y);
|
||||||
|
|
||||||
// Check if there's a shape or color item below, if so select the miner
|
// Check if there's a shape or color item below, if so select the miner
|
||||||
if (tileBelow && this.root.app.settings.getAllSettings().pickMinerOnPatch) {
|
if (
|
||||||
|
tileBelow &&
|
||||||
|
this.root.app.settings.getAllSettings().pickMinerOnPatch &&
|
||||||
|
this.root.currentLayer === "regular"
|
||||||
|
) {
|
||||||
this.currentMetaBuilding.set(gMetaBuildingRegistry.findByClass(MetaMinerBuilding));
|
this.currentMetaBuilding.set(gMetaBuildingRegistry.findByClass(MetaMinerBuilding));
|
||||||
|
|
||||||
// Select chained miner if available, since that's always desired once unlocked
|
// Select chained miner if available, since that's always desired once unlocked
|
||||||
|
|||||||
@ -14,6 +14,8 @@ import { MetaTrashBuilding } from "../../buildings/trash";
|
|||||||
import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt";
|
import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt";
|
||||||
import { HUDBaseToolbar } from "./base_toolbar";
|
import { HUDBaseToolbar } from "./base_toolbar";
|
||||||
import { MetaStorageBuilding } from "../../buildings/storage";
|
import { MetaStorageBuilding } from "../../buildings/storage";
|
||||||
|
import { MetaItemProducerBuilding } from "../../buildings/item_producer";
|
||||||
|
import { queryParamOptions } from "../../../core/query_parameters";
|
||||||
|
|
||||||
export class HUDBuildingsToolbar extends HUDBaseToolbar {
|
export class HUDBuildingsToolbar extends HUDBaseToolbar {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
@ -29,6 +31,7 @@ export class HUDBuildingsToolbar extends HUDBaseToolbar {
|
|||||||
MetaMixerBuilding,
|
MetaMixerBuilding,
|
||||||
MetaPainterBuilding,
|
MetaPainterBuilding,
|
||||||
MetaTrashBuilding,
|
MetaTrashBuilding,
|
||||||
|
...(queryParamOptions.sandboxMode || G_IS_DEV ? [MetaItemProducerBuilding] : []),
|
||||||
],
|
],
|
||||||
secondaryBuildings: [
|
secondaryBuildings: [
|
||||||
MetaStorageBuilding,
|
MetaStorageBuilding,
|
||||||
@ -39,7 +42,7 @@ export class HUDBuildingsToolbar extends HUDBaseToolbar {
|
|||||||
],
|
],
|
||||||
visibilityCondition: () =>
|
visibilityCondition: () =>
|
||||||
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "regular",
|
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "regular",
|
||||||
htmlElementId: "ingame_HUD_buildings_toolbar",
|
htmlElementId: "ingame_HUD_BuildingsToolbar",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/js/game/hud/parts/cat_memes.js
Normal file
21
src/js/game/hud/parts/cat_memes.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { makeDiv } from "../../../core/utils";
|
||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
|
|
||||||
|
const memeShowIntervalSeconds = 70 * 60;
|
||||||
|
const memeShowDuration = 5;
|
||||||
|
|
||||||
|
export class HUDCatMemes extends BaseHUDPart {
|
||||||
|
createElements(parent) {
|
||||||
|
this.element = makeDiv(parent, "ingame_HUD_CatMemes");
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.domAttach = new DynamicDomAttach(this.root, this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const now = this.root.time.realtimeNow();
|
||||||
|
this.domAttach.update(now % memeShowIntervalSeconds > memeShowIntervalSeconds - memeShowDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,23 +6,25 @@ import { DynamicDomAttach } from "../dynamic_dom_attach";
|
|||||||
import { TrackedState } from "../../../core/tracked_state";
|
import { TrackedState } from "../../../core/tracked_state";
|
||||||
import { cachebust } from "../../../core/cachebust";
|
import { cachebust } from "../../../core/cachebust";
|
||||||
import { T } from "../../../translations";
|
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 = [
|
const tutorialsByLevel = [
|
||||||
// Level 1
|
// Level 1
|
||||||
[
|
[
|
||||||
// 1.1. place an extractor
|
// 1.1. place an extractor
|
||||||
{
|
{
|
||||||
id: "1_1_extractor",
|
id: "1_1_extractor",
|
||||||
condition: /** @param {GameRoot} root */ root => {
|
condition: /** @param {GameRoot} root */ root =>
|
||||||
return root.entityMgr.getAllWithComponent(MinerComponent).length === 0;
|
root.entityMgr.getAllWithComponent(MinerComponent).length === 0,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
// 1.2. connect to hub
|
// 1.2. connect to hub
|
||||||
{
|
{
|
||||||
id: "1_2_conveyor",
|
id: "1_2_conveyor",
|
||||||
condition: /** @param {GameRoot} root */ root => {
|
condition: /** @param {GameRoot} root */ root => root.hubGoals.getCurrentGoalDelivered() === 0,
|
||||||
return root.hubGoals.getCurrentGoalDelivered() === 0;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
// 1.3 wait for completion
|
// 1.3 wait for completion
|
||||||
{
|
{
|
||||||
@ -30,6 +32,108 @@ const tutorialsByLevel = [
|
|||||||
condition: () => true,
|
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 {
|
export class HUDInteractiveTutorial extends BaseHUDPart {
|
||||||
|
|||||||
@ -259,7 +259,7 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
|
|||||||
label: T.ingame.keybindingsOverlay.switchLayers,
|
label: T.ingame.keybindingsOverlay.switchLayers,
|
||||||
keys: [k.ingame.switchLayers],
|
keys: [k.ingame.switchLayers],
|
||||||
condition: () =>
|
condition: () =>
|
||||||
this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_filters_and_levers),
|
this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -122,7 +122,7 @@ export class HUDModalDialogs extends BaseHUDPart {
|
|||||||
|
|
||||||
dialog.buttonSignals.getStandalone.add(() => {
|
dialog.buttonSignals.getStandalone.add(() => {
|
||||||
this.app.analytics.trackUiClick("demo_dialog_click");
|
this.app.analytics.trackUiClick("demo_dialog_click");
|
||||||
window.open(THIRDPARTY_URLS.standaloneStorePage);
|
window.open(THIRDPARTY_URLS.standaloneStorePage + "?ref=ddc");
|
||||||
});
|
});
|
||||||
|
|
||||||
return dialog.buttonSignals;
|
return dialog.buttonSignals;
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
import { ClickDetector } from "../../../core/click_detector";
|
import { ClickDetector } from "../../../core/click_detector";
|
||||||
import { formatBigNumber, makeDiv, arrayDeleteValue } from "../../../core/utils";
|
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 { ShapeDefinition } from "../../shape_definition";
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
|
||||||
import { blueprintShape, UPGRADES } from "../../upgrades";
|
|
||||||
import { enumHubGoalRewards } from "../../tutorial_goals";
|
import { enumHubGoalRewards } from "../../tutorial_goals";
|
||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the pinned shapes on the left side of the screen
|
* Manages the pinned shapes on the left side of the screen
|
||||||
@ -22,11 +24,13 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
* convenient. Also allows for cleaning up handles.
|
* convenient. Also allows for cleaning up handles.
|
||||||
* @type {Array<{
|
* @type {Array<{
|
||||||
* key: string,
|
* key: string,
|
||||||
|
* definition: ShapeDefinition,
|
||||||
* amountLabel: HTMLElement,
|
* amountLabel: HTMLElement,
|
||||||
* lastRenderedValue: string,
|
* lastRenderedValue: string,
|
||||||
* element: HTMLElement,
|
* element: HTMLElement,
|
||||||
* detector?: ClickDetector,
|
* detector?: ClickDetector,
|
||||||
* infoDetector?: ClickDetector
|
* infoDetector?: ClickDetector,
|
||||||
|
* throughputOnly?: boolean
|
||||||
* }>}
|
* }>}
|
||||||
*/
|
*/
|
||||||
this.handles = [];
|
this.handles = [];
|
||||||
@ -77,7 +81,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
updateShapesAfterUpgrade() {
|
updateShapesAfterUpgrade() {
|
||||||
for (let i = 0; i < this.pinnedShapes.length; ++i) {
|
for (let i = 0; i < this.pinnedShapes.length; ++i) {
|
||||||
const key = this.pinnedShapes[i];
|
const key = this.pinnedShapes[i];
|
||||||
if (key === blueprintShape) {
|
if (key === this.root.gameMode.getBlueprintShapeKey()) {
|
||||||
// Ignore blueprint shapes
|
// Ignore blueprint shapes
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -102,13 +106,14 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
if (key === this.root.hubGoals.currentGoal.definition.getHash()) {
|
if (key === this.root.hubGoals.currentGoal.definition.getHash()) {
|
||||||
return this.root.hubGoals.currentGoal.required;
|
return this.root.hubGoals.currentGoal.required;
|
||||||
}
|
}
|
||||||
if (key === blueprintShape) {
|
if (key === this.root.gameMode.getBlueprintShapeKey()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this shape is required for any upgrade
|
// Check if this shape is required for any upgrade
|
||||||
for (const upgradeId in UPGRADES) {
|
const upgrades = this.root.gameMode.getUpgrades();
|
||||||
const upgradeTiers = UPGRADES[upgradeId];
|
for (const upgradeId in upgrades) {
|
||||||
|
const upgradeTiers = upgrades[upgradeId];
|
||||||
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
|
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
|
||||||
const tierHandle = upgradeTiers[currentTier];
|
const tierHandle = upgradeTiers[currentTier];
|
||||||
|
|
||||||
@ -133,7 +138,10 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
* @param {string} key
|
* @param {string} key
|
||||||
*/
|
*/
|
||||||
isShapePinned(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
|
// This is a "special" shape which is always pinned
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -163,29 +171,40 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
this.handles = [];
|
this.handles = [];
|
||||||
|
|
||||||
// Pin story goal
|
// Pin story goal
|
||||||
this.internalPinShape(currentKey, false, "goal");
|
this.internalPinShape({
|
||||||
|
key: currentKey,
|
||||||
|
canUnpin: false,
|
||||||
|
className: "goal",
|
||||||
|
throughputOnly: currentGoal.throughputOnly,
|
||||||
|
});
|
||||||
|
|
||||||
// Pin blueprint shape as well
|
// Pin blueprint shape as well
|
||||||
if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
|
if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
|
||||||
this.internalPinShape(blueprintShape, false, "blueprint");
|
this.internalPinShape({
|
||||||
|
key: this.root.gameMode.getBlueprintShapeKey(),
|
||||||
|
canUnpin: false,
|
||||||
|
className: "blueprint",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pin manually pinned shapes
|
// Pin manually pinned shapes
|
||||||
for (let i = 0; i < this.pinnedShapes.length; ++i) {
|
for (let i = 0; i < this.pinnedShapes.length; ++i) {
|
||||||
const key = this.pinnedShapes[i];
|
const key = this.pinnedShapes[i];
|
||||||
if (key !== currentKey) {
|
if (key !== currentKey) {
|
||||||
this.internalPinShape(key);
|
this.internalPinShape({ key });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pins a new shape
|
* Pins a new shape
|
||||||
* @param {string} key
|
* @param {object} param0
|
||||||
* @param {boolean} canUnpin
|
* @param {string} param0.key
|
||||||
* @param {string=} className
|
* @param {boolean=} param0.canUnpin
|
||||||
|
* @param {string=} param0.className
|
||||||
|
* @param {boolean=} param0.throughputOnly
|
||||||
*/
|
*/
|
||||||
internalPinShape(key, canUnpin = true, className = null) {
|
internalPinShape({ key, canUnpin = true, className = null, throughputOnly = false }) {
|
||||||
const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key);
|
const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key);
|
||||||
|
|
||||||
const element = makeDiv(this.element, null, ["shape"]);
|
const element = makeDiv(this.element, null, ["shape"]);
|
||||||
@ -198,11 +217,11 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
|
|
||||||
let detector = null;
|
let detector = null;
|
||||||
if (canUnpin) {
|
if (canUnpin) {
|
||||||
element.classList.add("unpinable");
|
element.classList.add("removable");
|
||||||
detector = new ClickDetector(element, {
|
detector = new ClickDetector(element, {
|
||||||
consumeEvents: true,
|
consumeEvents: true,
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
targetOnly: true,
|
targetOnly: false,
|
||||||
});
|
});
|
||||||
detector.click.add(() => this.unpinShape(key));
|
detector.click.add(() => this.unpinShape(key));
|
||||||
} else {
|
} else {
|
||||||
@ -229,11 +248,13 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
|
|
||||||
this.handles.push({
|
this.handles.push({
|
||||||
key,
|
key,
|
||||||
|
definition,
|
||||||
element,
|
element,
|
||||||
amountLabel,
|
amountLabel,
|
||||||
lastRenderedValue: "",
|
lastRenderedValue: "",
|
||||||
detector,
|
detector,
|
||||||
infoDetector,
|
infoDetector,
|
||||||
|
throughputOnly,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,8 +265,21 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
for (let i = 0; i < this.handles.length; ++i) {
|
for (let i = 0; i < this.handles.length; ++i) {
|
||||||
const handle = this.handles[i];
|
const handle = this.handles[i];
|
||||||
|
|
||||||
const currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key);
|
let currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key);
|
||||||
const currentValueFormatted = formatBigNumber(currentValue);
|
let currentValueFormatted = formatBigNumber(currentValue);
|
||||||
|
|
||||||
|
if (handle.throughputOnly) {
|
||||||
|
currentValue =
|
||||||
|
this.root.productionAnalytics.getCurrentShapeRate(
|
||||||
|
enumAnalyticsDataSource.delivered,
|
||||||
|
handle.definition
|
||||||
|
) / globalConfig.analyticsSliceDurationSeconds;
|
||||||
|
currentValueFormatted = T.ingame.statistics.shapesDisplayUnits.second.replace(
|
||||||
|
"<shapes>",
|
||||||
|
String(currentValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (currentValueFormatted !== handle.lastRenderedValue) {
|
if (currentValueFormatted !== handle.lastRenderedValue) {
|
||||||
handle.lastRenderedValue = currentValueFormatted;
|
handle.lastRenderedValue = currentValueFormatted;
|
||||||
handle.amountLabel.innerText = currentValueFormatted;
|
handle.amountLabel.innerText = currentValueFormatted;
|
||||||
@ -260,6 +294,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
* @param {string} key
|
* @param {string} key
|
||||||
*/
|
*/
|
||||||
unpinShape(key) {
|
unpinShape(key) {
|
||||||
|
console.log("unpin", key);
|
||||||
arrayDeleteValue(this.pinnedShapes, key);
|
arrayDeleteValue(this.pinnedShapes, key);
|
||||||
this.rerenderFull();
|
this.rerenderFull();
|
||||||
}
|
}
|
||||||
@ -275,7 +310,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === blueprintShape) {
|
if (key === this.root.gameMode.getBlueprintShapeKey()) {
|
||||||
// Can not pin the blueprint shape
|
// Can not pin the blueprint shape
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import { BaseHUDPart } from "../base_hud_part";
|
|
||||||
import { makeDiv } from "../../../core/utils";
|
import { makeDiv } from "../../../core/utils";
|
||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
import { blueprintShape, UPGRADES } from "../../upgrades";
|
|
||||||
import { enumNotificationType } from "./notifications";
|
import { enumNotificationType } from "./notifications";
|
||||||
import { tutorialGoals } from "../../tutorial_goals";
|
|
||||||
|
|
||||||
export class HUDSandboxController extends BaseHUDPart {
|
export class HUDSandboxController extends BaseHUDPart {
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
@ -75,10 +73,11 @@ export class HUDSandboxController extends BaseHUDPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
giveBlueprints() {
|
giveBlueprints() {
|
||||||
if (!this.root.hubGoals.storedShapes[blueprintShape]) {
|
const shape = this.root.gameMode.getBlueprintShapeKey();
|
||||||
this.root.hubGoals.storedShapes[blueprintShape] = 0;
|
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() {
|
maxOutAll() {
|
||||||
@ -89,7 +88,7 @@ export class HUDSandboxController extends BaseHUDPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
modifyUpgrade(id, amount) {
|
modifyUpgrade(id, amount) {
|
||||||
const upgradeTiers = UPGRADES[id];
|
const upgradeTiers = this.root.gameMode.getUpgrades()[id];
|
||||||
const maxLevel = upgradeTiers.length;
|
const maxLevel = upgradeTiers.length;
|
||||||
|
|
||||||
this.root.hubGoals.upgradeLevels[id] = Math.max(
|
this.root.hubGoals.upgradeLevels[id] = Math.max(
|
||||||
@ -113,7 +112,7 @@ export class HUDSandboxController extends BaseHUDPart {
|
|||||||
modifyLevel(amount) {
|
modifyLevel(amount) {
|
||||||
const hubGoals = this.root.hubGoals;
|
const hubGoals = this.root.hubGoals;
|
||||||
hubGoals.level = Math.max(1, hubGoals.level + amount);
|
hubGoals.level = Math.max(1, hubGoals.level + amount);
|
||||||
hubGoals.createNextGoal();
|
hubGoals.computeNextGoal();
|
||||||
|
|
||||||
// Clear all shapes of this level
|
// Clear all shapes of this level
|
||||||
hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0;
|
hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0;
|
||||||
@ -122,9 +121,10 @@ export class HUDSandboxController extends BaseHUDPart {
|
|||||||
|
|
||||||
// Compute gained rewards
|
// Compute gained rewards
|
||||||
hubGoals.gainedRewards = {};
|
hubGoals.gainedRewards = {};
|
||||||
|
const levels = this.root.gameMode.getLevelDefinitions();
|
||||||
for (let i = 0; i < hubGoals.level - 1; ++i) {
|
for (let i = 0; i < hubGoals.level - 1; ++i) {
|
||||||
if (i < tutorialGoals.length) {
|
if (i < levels.length) {
|
||||||
const reward = tutorialGoals[i].reward;
|
const reward = levels[i].reward;
|
||||||
hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1;
|
hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 { makeOffscreenBuffer } from "../../../core/buffer_utils";
|
||||||
|
import { globalConfig } from "../../../core/config";
|
||||||
import { DrawParameters } from "../../../core/draw_parameters";
|
import { DrawParameters } from "../../../core/draw_parameters";
|
||||||
|
import { createLogger } from "../../../core/logging";
|
||||||
import { Rectangle } from "../../../core/rectangle";
|
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");
|
const logger = createLogger("screenshot_exporter");
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ export class HUDScreenshotExporter extends BaseHUDPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
startExport() {
|
startExport() {
|
||||||
if (IS_DEMO) {
|
if (!this.root.app.restrictionMgr.getIsExportingScreenshotsPossible()) {
|
||||||
this.root.hud.parts.dialogs.showFeatureRestrictionInfo(T.demo.features.exportingBase);
|
this.root.hud.parts.dialogs.showFeatureRestrictionInfo(T.demo.features.exportingBase);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ export class HUDScreenshotExporter extends BaseHUDPart {
|
|||||||
const parameters = new DrawParameters({
|
const parameters = new DrawParameters({
|
||||||
context,
|
context,
|
||||||
visibleRect,
|
visibleRect,
|
||||||
desiredAtlasScale: chunkScale,
|
desiredAtlasScale: 0.25,
|
||||||
root: this.root,
|
root: this.root,
|
||||||
zoomLevel: chunkScale,
|
zoomLevel: chunkScale,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export class HUDSettingsMenu extends BaseHUDPart {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 0; i < buttons.length; ++i) {
|
for (let i = 0; i < buttons.length; ++i) {
|
||||||
const { title, action, id } = buttons[i];
|
const { action, id } = buttons[i];
|
||||||
|
|
||||||
const element = document.createElement("button");
|
const element = document.createElement("button");
|
||||||
element.classList.add("styledButton");
|
element.classList.add("styledButton");
|
||||||
@ -88,13 +88,8 @@ export class HUDSettingsMenu extends BaseHUDPart {
|
|||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
document.body.classList.remove("ingameDialogOpen");
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
document.body.classList.add("ingameDialogOpen");
|
|
||||||
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
||||||
|
|
||||||
const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60);
|
const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60);
|
||||||
@ -120,7 +115,6 @@ export class HUDSettingsMenu extends BaseHUDPart {
|
|||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
document.body.classList.remove("ingameDialogOpen");
|
|
||||||
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,7 +67,6 @@ export class HUDShapeViewer extends BaseHUDPart {
|
|||||||
*/
|
*/
|
||||||
close() {
|
close() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
document.body.classList.remove("ingameDialogOpen");
|
|
||||||
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
@ -78,7 +77,6 @@ export class HUDShapeViewer extends BaseHUDPart {
|
|||||||
*/
|
*/
|
||||||
renderForShape(definition) {
|
renderForShape(definition) {
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
document.body.classList.add("ingameDialogOpen");
|
|
||||||
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
||||||
|
|
||||||
removeAllChildren(this.renderArea);
|
removeAllChildren(this.renderArea);
|
||||||
@ -124,13 +122,6 @@ export class HUDShapeViewer extends BaseHUDPart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleans up everything
|
|
||||||
*/
|
|
||||||
cleanup() {
|
|
||||||
document.body.classList.remove("ingameDialogOpen");
|
|
||||||
}
|
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
this.domAttach.update(this.visible);
|
this.domAttach.update(this.visible);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import { ClickDetector } from "../../../core/click_detector";
|
import { ClickDetector } from "../../../core/click_detector";
|
||||||
import { InputReceiver } from "../../../core/input_receiver";
|
import { InputReceiver } from "../../../core/input_receiver";
|
||||||
import { formatBigNumber, makeDiv } from "../../../core/utils";
|
import { formatBigNumber, getRomanNumber, makeDiv } from "../../../core/utils";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
|
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
|
||||||
import { UPGRADES } from "../../upgrades";
|
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ export class HUDShop extends BaseHUDPart {
|
|||||||
this.upgradeToElements = {};
|
this.upgradeToElements = {};
|
||||||
|
|
||||||
// Upgrades
|
// Upgrades
|
||||||
for (const upgradeId in UPGRADES) {
|
for (const upgradeId in this.root.gameMode.getUpgrades()) {
|
||||||
const handle = {};
|
const handle = {};
|
||||||
handle.requireIndexToElement = [];
|
handle.requireIndexToElement = [];
|
||||||
|
|
||||||
@ -59,7 +58,7 @@ export class HUDShop extends BaseHUDPart {
|
|||||||
rerenderFull() {
|
rerenderFull() {
|
||||||
for (const upgradeId in this.upgradeToElements) {
|
for (const upgradeId in this.upgradeToElements) {
|
||||||
const handle = this.upgradeToElements[upgradeId];
|
const handle = this.upgradeToElements[upgradeId];
|
||||||
const upgradeTiers = UPGRADES[upgradeId];
|
const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId];
|
||||||
|
|
||||||
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
|
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
|
||||||
const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId];
|
const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId];
|
||||||
@ -68,7 +67,7 @@ export class HUDShop extends BaseHUDPart {
|
|||||||
// Set tier
|
// Set tier
|
||||||
handle.elemTierLabel.innerText = T.ingame.shop.tier.replace(
|
handle.elemTierLabel.innerText = T.ingame.shop.tier.replace(
|
||||||
"<x>",
|
"<x>",
|
||||||
"" + T.ingame.shop.tierLabels[currentTier]
|
getRomanNumber(currentTier + 1)
|
||||||
);
|
);
|
||||||
|
|
||||||
handle.elemTierLabel.setAttribute("data-tier", currentTier);
|
handle.elemTierLabel.setAttribute("data-tier", currentTier);
|
||||||
@ -90,17 +89,15 @@ export class HUDShop extends BaseHUDPart {
|
|||||||
// Max level
|
// Max level
|
||||||
handle.elemDescription.innerText = T.ingame.shop.maximumLevel.replace(
|
handle.elemDescription.innerText = T.ingame.shop.maximumLevel.replace(
|
||||||
"<currentMult>",
|
"<currentMult>",
|
||||||
currentTierMultiplier.toString()
|
formatBigNumber(currentTierMultiplier)
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set description
|
// Set description
|
||||||
handle.elemDescription.innerText = T.shopUpgrades[upgradeId].description
|
handle.elemDescription.innerText = T.shopUpgrades[upgradeId].description
|
||||||
.replace("<currentMult>", currentTierMultiplier.toString())
|
.replace("<currentMult>", formatBigNumber(currentTierMultiplier))
|
||||||
.replace("<newMult>", (currentTierMultiplier + tierHandle.improvement).toString())
|
.replace("<newMult>", formatBigNumber(currentTierMultiplier + tierHandle.improvement));
|
||||||
// Backwards compatibility
|
|
||||||
.replace("<gain>", (tierHandle.improvement * 100.0).toString());
|
|
||||||
|
|
||||||
tierHandle.required.forEach(({ shape, amount }) => {
|
tierHandle.required.forEach(({ shape, amount }) => {
|
||||||
const container = makeDiv(handle.elemRequirements, null, ["requirement"]);
|
const container = makeDiv(handle.elemRequirements, null, ["requirement"]);
|
||||||
@ -207,8 +204,6 @@ export class HUDShop extends BaseHUDPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
document.body.classList.remove("ingameDialogOpen");
|
|
||||||
|
|
||||||
// Cleanup detectors
|
// Cleanup detectors
|
||||||
for (const upgradeId in this.upgradeToElements) {
|
for (const upgradeId in this.upgradeToElements) {
|
||||||
const handle = this.upgradeToElements[upgradeId];
|
const handle = this.upgradeToElements[upgradeId];
|
||||||
@ -224,15 +219,12 @@ export class HUDShop extends BaseHUDPart {
|
|||||||
|
|
||||||
show() {
|
show() {
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
document.body.classList.add("ingameDialogOpen");
|
|
||||||
// this.background.classList.add("visible");
|
|
||||||
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
||||||
this.rerenderFull();
|
this.rerenderFull();
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
document.body.classList.remove("ingameDialogOpen");
|
|
||||||
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|||||||
85
src/js/game/hud/parts/standalone_advantages.js
Normal file
85
src/js/game/hud/parts/standalone_advantages.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { A_B_TESTING_LINK_TYPE, THIRDPARTY_URLS } from "../../../core/config";
|
||||||
|
import { InputReceiver } from "../../../core/input_receiver";
|
||||||
|
import { makeDiv } from "../../../core/utils";
|
||||||
|
import { T } from "../../../translations";
|
||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
|
|
||||||
|
const showIntervalSeconds = 30 * 60;
|
||||||
|
|
||||||
|
export class HUDStandaloneAdvantages extends BaseHUDPart {
|
||||||
|
createElements(parent) {
|
||||||
|
this.background = makeDiv(parent, "ingame_HUD_StandaloneAdvantages", ["ingameDialog"]);
|
||||||
|
|
||||||
|
// DIALOG Inner / Wrapper
|
||||||
|
this.dialogInner = makeDiv(this.background, null, ["dialogInner"]);
|
||||||
|
this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.standaloneAdvantages.title);
|
||||||
|
this.contentDiv = makeDiv(
|
||||||
|
this.dialogInner,
|
||||||
|
null,
|
||||||
|
["content"],
|
||||||
|
`
|
||||||
|
<div class="points">
|
||||||
|
${Object.entries(T.ingame.standaloneAdvantages.points)
|
||||||
|
.map(
|
||||||
|
([key, trans]) => `
|
||||||
|
<div class="point ${key}">
|
||||||
|
<strong>${trans.title}</strong>
|
||||||
|
<p>${trans.desc}</p>
|
||||||
|
</div>`
|
||||||
|
)
|
||||||
|
.join("")}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lowerBar">
|
||||||
|
<button class="steamLinkButton ${A_B_TESTING_LINK_TYPE}"></button>
|
||||||
|
<button class="otherCloseButton">${T.ingame.standaloneAdvantages.no_thanks}</button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
this.trackClicks(this.contentDiv.querySelector("button.steamLinkButton"), () => {
|
||||||
|
this.root.app.analytics.trackUiClick("standalone_advantage_visit_steam");
|
||||||
|
this.root.app.platformWrapper.openExternalLink(
|
||||||
|
THIRDPARTY_URLS.standaloneStorePage + "?ref=savs&prc=" + A_B_TESTING_LINK_TYPE
|
||||||
|
);
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
this.trackClicks(this.contentDiv.querySelector("button.otherCloseButton"), () => {
|
||||||
|
this.root.app.analytics.trackUiClick("standalone_advantage_no_thanks");
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.domAttach = new DynamicDomAttach(this.root, this.background, {
|
||||||
|
attachClass: "visible",
|
||||||
|
});
|
||||||
|
|
||||||
|
this.inputReciever = new InputReceiver("standalone-advantages");
|
||||||
|
this.close();
|
||||||
|
|
||||||
|
this.lastShown = this.root.gameIsFresh ? this.root.time.now() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.lastShown = this.root.time.now();
|
||||||
|
this.visible = true;
|
||||||
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.visible = false;
|
||||||
|
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
if (!this.visible && this.root.time.now() - this.lastShown > showIntervalSeconds) {
|
||||||
|
this.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.domAttach.update(this.visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -151,17 +151,12 @@ export class HUDStatistics extends BaseHUDPart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
document.body.classList.remove("ingameDialogOpen");
|
|
||||||
}
|
|
||||||
|
|
||||||
isBlockingOverlay() {
|
isBlockingOverlay() {
|
||||||
return this.visible;
|
return this.visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
document.body.classList.add("ingameDialogOpen");
|
|
||||||
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
||||||
this.rerenderFull();
|
this.rerenderFull();
|
||||||
this.update();
|
this.update();
|
||||||
@ -169,7 +164,6 @@ export class HUDStatistics extends BaseHUDPart {
|
|||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
document.body.classList.remove("ingameDialogOpen");
|
|
||||||
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,7 +70,6 @@ export class HUDPartTutorialHints extends BaseHUDPart {
|
|||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.enlarged = false;
|
this.enlarged = false;
|
||||||
document.body.classList.remove("ingameDialogOpen");
|
|
||||||
this.element.classList.remove("enlarged", "noBlur");
|
this.element.classList.remove("enlarged", "noBlur");
|
||||||
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
||||||
this.update();
|
this.update();
|
||||||
@ -79,8 +78,6 @@ export class HUDPartTutorialHints extends BaseHUDPart {
|
|||||||
show() {
|
show() {
|
||||||
this.root.app.analytics.trackUiClick("tutorial_hint_show");
|
this.root.app.analytics.trackUiClick("tutorial_hint_show");
|
||||||
this.root.app.analytics.trackUiClick("tutorial_hint_show_lvl_" + this.root.hubGoals.level);
|
this.root.app.analytics.trackUiClick("tutorial_hint_show_lvl_" + this.root.hubGoals.level);
|
||||||
|
|
||||||
document.body.classList.add("ingameDialogOpen");
|
|
||||||
this.element.classList.add("enlarged", "noBlur");
|
this.element.classList.add("enlarged", "noBlur");
|
||||||
this.enlarged = true;
|
this.enlarged = true;
|
||||||
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
import { globalConfig } from "../../../core/config";
|
import { globalConfig } from "../../../core/config";
|
||||||
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
||||||
|
import { InputReceiver } from "../../../core/input_receiver";
|
||||||
import { makeDiv } from "../../../core/utils";
|
import { makeDiv } from "../../../core/utils";
|
||||||
import { SOUNDS } from "../../../platform/sound";
|
import { SOUNDS } from "../../../platform/sound";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { defaultBuildingVariant } from "../../meta_building";
|
import { defaultBuildingVariant } from "../../meta_building";
|
||||||
import { enumHubGoalRewards } from "../../tutorial_goals";
|
import { enumHubGoalRewards } from "../../tutorial_goals";
|
||||||
|
import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings";
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings";
|
import { enumNotificationType } from "./notifications";
|
||||||
import { InputReceiver } from "../../../core/input_receiver";
|
|
||||||
|
|
||||||
export class HUDUnlockNotification extends BaseHUDPart {
|
export class HUDUnlockNotification extends BaseHUDPart {
|
||||||
initialize() {
|
initialize() {
|
||||||
@ -50,6 +51,18 @@ export class HUDUnlockNotification extends BaseHUDPart {
|
|||||||
* @param {enumHubGoalRewards} reward
|
* @param {enumHubGoalRewards} reward
|
||||||
*/
|
*/
|
||||||
showForLevel(level, reward) {
|
showForLevel(level, reward) {
|
||||||
|
this.root.soundProxy.playUi(SOUNDS.levelComplete);
|
||||||
|
|
||||||
|
const levels = this.root.gameMode.getLevelDefinitions();
|
||||||
|
// Don't use getIsFreeplay() because we want the freeplay level up to show
|
||||||
|
if (level > levels.length) {
|
||||||
|
this.root.hud.signals.notification.dispatch(
|
||||||
|
T.ingame.notifications.freeplayLevelComplete.replace("<level>", String(level)),
|
||||||
|
enumNotificationType.success
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
||||||
this.elemTitle.innerText = T.ingame.levelCompleteNotification.levelTitle.replace(
|
this.elemTitle.innerText = T.ingame.levelCompleteNotification.levelTitle.replace(
|
||||||
"<level>",
|
"<level>",
|
||||||
@ -83,7 +96,6 @@ export class HUDUnlockNotification extends BaseHUDPart {
|
|||||||
|
|
||||||
this.elemContents.innerHTML = html;
|
this.elemContents.innerHTML = html;
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.root.soundProxy.playUi(SOUNDS.levelComplete);
|
|
||||||
|
|
||||||
if (this.buttonShowTimeout) {
|
if (this.buttonShowTimeout) {
|
||||||
clearTimeout(this.buttonShowTimeout);
|
clearTimeout(this.buttonShowTimeout);
|
||||||
|
|||||||
@ -1,44 +1,72 @@
|
|||||||
import { BaseHUDPart } from "../base_hud_part";
|
|
||||||
import { DrawParameters } from "../../../core/draw_parameters";
|
|
||||||
import { makeDiv } from "../../../core/utils";
|
|
||||||
import { THIRDPARTY_URLS } from "../../../core/config";
|
import { THIRDPARTY_URLS } from "../../../core/config";
|
||||||
|
import { makeDiv } from "../../../core/utils";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
|
|
||||||
|
const watermarkShowIntervalSeconds = G_IS_DEV ? 120 : 7 * 60;
|
||||||
|
const watermarkShowDuration = 5;
|
||||||
|
|
||||||
export class HUDWatermark extends BaseHUDPart {
|
export class HUDWatermark extends BaseHUDPart {
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
this.element = makeDiv(parent, "ingame_HUD_Watermark");
|
this.element = makeDiv(
|
||||||
|
parent,
|
||||||
|
"ingame_HUD_Watermark",
|
||||||
|
[],
|
||||||
|
`
|
||||||
|
<strong>${T.ingame.watermark.title}</strong>
|
||||||
|
<p>${T.ingame.watermark.desc}</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
this.linkElement = makeDiv(
|
||||||
|
parent,
|
||||||
|
"ingame_HUD_WatermarkClicker",
|
||||||
|
[],
|
||||||
|
T.ingame.watermark.get_on_steam
|
||||||
|
);
|
||||||
|
this.trackClicks(this.linkElement, () => {
|
||||||
|
this.root.app.analytics.trackUiClick("watermark_click_2_direct");
|
||||||
|
this.root.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.standaloneStorePage + "?ref=wtmd");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.trackClicks(this.element, this.onWatermarkClick);
|
this.trackClicks(this.element, this.onWatermarkClick);
|
||||||
|
|
||||||
|
this.domAttach = new DynamicDomAttach(this.root, this.element, {
|
||||||
|
attachClass: "visible",
|
||||||
|
timeToKeepSeconds: 0.5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.domAttach.update(
|
||||||
|
this.root.time.realtimeNow() % watermarkShowIntervalSeconds < watermarkShowDuration
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onWatermarkClick() {
|
onWatermarkClick() {
|
||||||
this.root.app.analytics.trackUiClick("watermark_click_2");
|
this.root.app.analytics.trackUiClick("watermark_click_2_new");
|
||||||
this.root.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.standaloneStorePage);
|
this.root.hud.parts.standaloneAdvantages.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {DrawParameters} parameters
|
* @param {import("../../../core/draw_utils").DrawParameters} parameters
|
||||||
*/
|
*/
|
||||||
drawOverlays(parameters) {
|
drawOverlays(parameters) {
|
||||||
const w = this.root.gameWidth;
|
const w = this.root.gameWidth;
|
||||||
const x = 280 * this.root.app.getEffectiveUiScale();
|
|
||||||
|
|
||||||
parameters.context.fillStyle = "#f77";
|
parameters.context.fillStyle = "rgba(230, 230, 230, 0.9)";
|
||||||
parameters.context.font = "bold " + this.root.app.getEffectiveUiScale() * 17 + "px GameFont";
|
parameters.context.font = "bold " + this.root.app.getEffectiveUiScale() * 40 + "px GameFont";
|
||||||
// parameters.context.textAlign = "center";
|
parameters.context.textAlign = "center";
|
||||||
parameters.context.fillText(
|
parameters.context.fillText(
|
||||||
T.demoBanners.title.toUpperCase(),
|
T.demoBanners.title.toUpperCase(),
|
||||||
x,
|
w / 2,
|
||||||
this.root.app.getEffectiveUiScale() * 27
|
this.root.app.getEffectiveUiScale() * 50
|
||||||
);
|
);
|
||||||
|
|
||||||
parameters.context.font = "bold " + this.root.app.getEffectiveUiScale() * 12 + "px GameFont";
|
parameters.context.textAlign = "left";
|
||||||
// parameters.context.textAlign = "center";
|
|
||||||
parameters.context.fillText(T.demoBanners.intro, x, this.root.app.getEffectiveUiScale() * 45);
|
|
||||||
|
|
||||||
// parameters.context.textAlign = "left";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user