Initial commit

This commit is contained in:
Tobias Springer 2020-05-09 16:45:23 +02:00
commit 93c6ea683d
304 changed files with 56031 additions and 0 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.wav filter=lfs diff=lfs merge=lfs -text

111
.gitignore vendored Normal file
View File

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

11
.prettierrc.yaml Normal file
View File

@ -0,0 +1,11 @@
# .prettierrc or .prettierrc.yaml
trailingComma: "es5"
tabWidth: 4
semi: true
singleQuote: false
printWidth: 110
useTabs: false
quoteProps: "consistent"
bracketSpacing: true
arrowParens: avoid
endOfLine: "lf"

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode"
}

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# shapez.io
shapez.io source code

44
buildutils.js Normal file
View File

@ -0,0 +1,44 @@
const glob = require("glob");
const execSync = require("child_process").execSync;
const trim = require("trim");
const fs = require("fs");
const path = require("path");
module.exports = {
getRevision: function (useLast = false) {
const commitHash = execSync("git rev-parse --short " + (useLast ? "HEAD^1" : "HEAD")).toString("ascii");
return commitHash.replace(/^\s+|\s+$/g, "");
},
getAllResourceImages() {
return glob
.sync("res/**/*.@(png|svg|jpg)", { cwd: ".." })
.map((f) => f.replace(/^res\//gi, ""))
.filter((f) => {
if (f.indexOf("ui") >= 0) {
// We drop all ui images except for the noinline ones
return f.indexOf("noinline") >= 0;
}
return true;
});
},
getAllAtlasImages() {
return glob.sync("res_built/atlas/*.png", { cwd: ".." }).map((s) => s.replace("res_built/atlas/", "res/"));
},
getAllSounds() {
return glob.sync("res_built/sounds/**/*.mp3", { cwd: ".." }).map((s) => s.replace("res_built/sounds/", "res/sounds/"));
},
getVersion() {
return trim(fs.readFileSync(path.join(__dirname, "version")).toString());
},
/**
* @param {string} url
* @param {string} commitHash
*/
cachebust(url, commitHash) {
return "/v/" + commitHash + "/" + url;
},
};

1
gulp/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.wav filter=lfs diff=lfs merge=lfs -text

1
gulp/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
additional_build_files

48
gulp/babel-es6.config.js Normal file
View File

@ -0,0 +1,48 @@
module.exports = function (api) {
api.cache(true);
const presets = [
[
"@babel/preset-env",
{
targets: "> 5%",
useBuiltIns: "usage",
corejs: 3,
loose: true,
spec: false,
modules: "auto",
// debug: true
},
],
];
const plugins = [
"closure-elimination",
// var is faster than let and const!
[
"@babel/plugin-transform-block-scoping",
{
throwIfClosureRequired: false,
},
],
[
"@babel/plugin-transform-classes",
{
loose: true,
},
],
];
return {
presets,
plugins,
highlightCode: true,
sourceType: "module",
sourceMaps: false,
parserOpts: {},
only: ["../src/js"],
generatorOpts: {
retainLines: false,
compact: true,
minified: true,
comments: true,
},
};
};

41
gulp/babel.config.js Normal file
View File

@ -0,0 +1,41 @@
module.exports = function (api) {
api.cache(true);
const presets = [
[
"@babel/preset-env",
{
targets: "android >= 4.4.4",
useBuiltIns: "usage",
corejs: 3,
loose: true,
spec: false,
modules: "auto",
// debug: true
},
],
];
const plugins = [
"closure-elimination",
[
"@babel/plugin-transform-classes",
{
loose: true,
},
],
];
return {
presets,
plugins,
highlightCode: true,
sourceType: "module",
sourceMaps: false,
parserOpts: {},
only: ["../src/js"],
generatorOpts: {
retainLines: false,
compact: true,
minified: true,
comments: true,
},
};
};

50
gulp/buildutils.js Normal file
View File

@ -0,0 +1,50 @@
const glob = require("glob");
const execSync = require("child_process").execSync;
const trim = require("trim");
const fs = require("fs");
const path = require("path");
module.exports = {
getRevision: function (useLast = false) {
const commitHash = execSync("git rev-parse --short " + (useLast ? "HEAD^1" : "HEAD")).toString(
"ascii"
);
return commitHash.replace(/^\s+|\s+$/g, "");
},
getAllResourceImages() {
return glob
.sync("res/**/*.@(png|svg|jpg)", { cwd: ".." })
.map(f => f.replace(/^res\//gi, ""))
.filter(f => {
if (f.indexOf("ui") >= 0) {
// We drop all ui images except for the noinline ones
return f.indexOf("noinline") >= 0;
}
return true;
});
},
getAllAtlasImages() {
return glob
.sync("res_built/atlas/*.png", { cwd: ".." })
.map(s => s.replace("res_built/atlas/", "res/"));
},
getAllSounds() {
return glob
.sync("res_built/sounds/**/*.mp3", { cwd: ".." })
.map(s => s.replace("res_built/sounds/", "res/sounds/"));
},
getVersion() {
return trim(fs.readFileSync(path.join(__dirname, "..", "version")).toString());
},
/**
* @param {string} url
* @param {string} commitHash
*/
cachebust(url, commitHash) {
return "/v/" + commitHash + "/" + url;
},
};

112
gulp/bundle-loader.js Normal file
View File

@ -0,0 +1,112 @@
/**
* ES6 Bundle Loader
*
* Attempts to load the game code, and if that fails tries with the transpiled
* version. Also handles errors during load.
*/
(function () {
var loadTimeout = null;
var callbackDone = false;
// Catch load errors
function errorHandler(event, source, lineno, colno, error) {
console.error("👀 Init Error:", event, source, lineno, colno, error);
var element = document.createElement("div");
element.style.position = "fixed";
element.style.top = "0";
element.style.right = "0";
element.style.bottom = "0";
element.style.left = "0";
element.style.zIndex = "29999";
element.style.backgroundColor = "#222429";
element.style.display = "flex";
element.style.justifyContent = "center";
element.style.alignItems = "center";
var inner = document.createElement("div");
inner.style.color = "#fff";
inner.style.fontFamily = "GameFont, sans-serif";
inner.style.fontSize = "15px";
inner.style.padding = "30px";
inner.style.textAlign = "center";
element.appendChild(inner);
var heading = document.createElement("h3");
heading.style.color = "#ef5072";
heading.innerText = "Error";
heading.style.marginBottom = "40px";
heading.style.fontSize = "45px";
inner.appendChild(heading);
var content = document.createElement("p");
content.style.color = "#eee";
content.innerText = error || (event && event.message) || event || "Unknown Error";
inner.appendChild(content);
if (source) {
var sourceElement = document.createElement("p");
sourceElement.style.color = "#777";
sourceElement.innerText = sourceElement + ":" + lineno + ":" + colno;
inner.appendChild(sourceElement);
}
document.documentElement.appendChild(element);
}
window.addEventListener("error", errorHandler);
window.addEventListener("unhandledrejection", errorHandler);
function makeJsTag(src, integrity) {
var script = document.createElement("script");
script.src = src;
script.type = "text/javascript";
script.charset = "utf-8";
script.defer = true;
if (integrity) {
script.setAttribute("integrity", integrity);
}
return script;
}
function loadFallbackJs(error) {
console.warn("👀 ES6 Script not supported, loading transpiled code.");
console.warn("👀 Error was:", error);
var scriptTransp = makeJsTag(bundleSrcTranspiled, bundleIntegrityTranspiled);
scriptTransp.addEventListener("error", scriptFail);
scriptTransp.addEventListener("load", onJsLoaded);
document.head.appendChild(scriptTransp);
}
function scriptFail(error) {
console.error("👀 Failed to load bundle!");
console.error("👀 Error was:", error);
throw new Error("Core load failed.");
}
function expectJsParsed() {
if (!callbackDone) {
console.error("👀 Got no core callback");
throw new Error("Core thread failed to respond within time.");
}
}
function onJsLoaded() {
console.log("👀 Core loaded at", Math.floor(performance.now()), "ms");
loadTimeout = setTimeout(expectJsParsed, 15000);
window.removeEventListener("error", errorHandler);
window.removeEventListener("unhandledrejection", errorHandler);
}
window.coreThreadLoadedCb = function () {
console.log("👀 Core responded at", Math.floor(performance.now()), "ms");
clearTimeout(loadTimeout);
loadTimeout = null;
callbackDone = true;
};
var scriptEs6 = makeJsTag(bundleSrc, bundleIntegrity);
scriptEs6.addEventListener("error", loadFallbackJs);
scriptEs6.addEventListener("load", onJsLoaded);
document.head.appendChild(scriptEs6);
})();

96
gulp/convert_atlas.js Normal file
View File

@ -0,0 +1,96 @@
// Converts the atlas description to a JSON file
String.prototype.replaceAll = function (search, replacement) {
var target = this;
return target.split(search).join(replacement);
};
const fs = require("fs");
const path = require("path");
const folder = path.join(__dirname, "res_built", "atlas");
const files = fs.readdirSync(folder);
const metadata = [];
files.forEach(filename => {
if (filename.endsWith(".atlas")) {
// Read content
const content = fs.readFileSync(path.join(folder, filename), "ascii");
const lines = content.replaceAll("\r", "").replaceAll("\t", "").split("\n");
const readLine = () => lines.splice(0, 1)[0];
const readValue = () => readLine().replaceAll(" ", "").split(":")[1];
const readVector = () =>
readValue()
.split(",")
.map(d => parseInt(d, 10));
let maxAtlas = 100;
atlasLoop: while (maxAtlas-- > 0 && lines.length >= 7) {
const result = {
entries: [],
};
// Extract header
const header_fileStart = readLine();
const header_fileName = readLine();
const header_size = readVector();
const header_format = readLine();
const header_filter = readLine();
const header_repeat = readLine();
const baseAtlasName = header_fileName.replace(".png", "");
// Store size
result.size = header_size;
lineLoop: while (lines.length >= 7) {
const entryResult = {};
const nextLine = lines[0];
if (nextLine.length === 0) {
break;
}
const entry_fileName = readLine() + ".png";
const entry_rotate = readValue();
const entry_xy = readVector();
const entry_size = readVector();
const entry_orig = readVector();
const entry_offset = readVector();
const entry_index = readValue();
entryResult.filename = entry_fileName;
entryResult.xy = entry_xy;
entryResult.size = entry_size;
// entryResult.offset = entry_offset;
entryResult.origSize = entry_orig;
let offset = [0, 0];
// GDX Atlas packer uses 1 - y coordinates. This sucks, and we have to convert it
offset[0] = entry_offset[0];
offset[1] = entry_orig[1] - entry_offset[1] - entry_size[1];
entryResult.offset = offset;
result.entries.push(entryResult);
}
console.log("[Atlas]", "'" + baseAtlasName + "'", "has", result.entries.length, "entries");
// fs.writeFileSync(path.join(folder, baseAtlasName + ".gen.json"), JSON.stringify(result));
metadata.push({
filename: baseAtlasName + ".png",
entries: result,
});
}
}
});
fs.writeFileSync(path.join(folder, "meta.gen.json"), JSON.stringify(metadata, null, 4));

153
gulp/cordova.js vendored Normal file
View File

@ -0,0 +1,153 @@
const path = require("path");
const fs = require("fs");
const buildUtils = require("./buildutils");
export function gulptasksCordova($, gulp, buildFolder) {
const cdvRes = path.join("..", "..", "res");
// Cleans up the app assets
// Removes all temporary folders used while optimizing the assets
gulp.task("cleanupAppAssetsBuiltFolder", () => {
return gulp.src(path.join(cdvRes, "built"), { read: false }).pipe($.clean({ force: true }));
});
// Optimizes all built assets
gulp.task("optimizeBuiltAppAssets", () => {
return gulp
.src(path.join(cdvRes, "built", "**", "*.png"))
.pipe($.flatten())
.pipe($.imagemin([$.imagemin.optipng({ optimizationLevel: 1 })]))
.pipe(gulp.dest(path.join(cdvRes, "built")));
});
// Scales the icon resources
gulp.task("scaleIconIos", async () => {
const sizes = [
180,
60,
120,
76,
152,
40,
80,
57,
114,
72,
144,
167,
29,
58,
87,
50,
100,
167,
20,
1024,
24,
48,
55,
172,
196,
];
for (let i = 0; i < sizes.length; ++i) {
const size = sizes[i];
console.log("Scaling icon to", size, "x", size);
const img = await $.jimp.read(path.join(cdvRes, "ios", "icon-prefab.png"));
await img.resize(size, size).write(path.join(cdvRes, "built", "ios", "icon@" + size + ".png"));
}
});
gulp.task("copyOtherIosResources", () => {
return gulp
.src(path.join(cdvRes, "ios", "splash-prefab.png"))
.pipe($.rename("Default@2x~universal~anyany.png"))
.pipe(gulp.dest(path.join(cdvRes, "built", "ios")));
});
gulp.task("prepareIosRes", ["scaleIconIos", "copyOtherIosResources"]);
gulp.task("copyAndroidResources", () => {
return gulp
.src(path.join(cdvRes, "android", "**", "*.*"))
.pipe(gulp.dest(path.join(cdvRes, "built", "android")));
});
gulp.task("prepareAndroidRes", ["copyAndroidResources"]);
gulp.task("prepareCordovaAssets", cb => {
return $.sequence(
"cleanupAppAssetsBuiltFolder",
["prepareIosRes", "prepareAndroidRes"],
"optimizeBuiltAppAssets"
)(cb);
});
// Patches the config.xml by replacing the app id to app_beta
gulp.task("patchConfigXML", () => {
const configUrl = path.join("..", "..", "config.xml");
let configContent = fs.readFileSync(configUrl).toString();
const version = buildUtils.getVersion();
configContent = configContent.replace("%VERSION%", version);
configContent = configContent.replace(' id="io.shapez.app" ', ' id="io.shapez.app_beta" ');
configContent = configContent.replace("<name>Shapez.io</name>", "<name>Shapez.io BETA</name>");
fs.writeFileSync(configUrl, configContent);
});
gulp.task("patchConfigXMLChangeStagingToProd", () => {
const configUrl = path.join("..", "..", "config.xml");
let configContent = fs.readFileSync(configUrl).toString();
configContent = configContent.replace(' id="io.shapez.app_beta" ', ' id="io.shapez.app" ');
configContent = configContent.replace("<name>Shapez.io BETA</name>", "<name>Shapez.io</name>");
fs.writeFileSync(configUrl, configContent);
});
// Triggers a new build on phonegap
gulp.task("triggerPhonegapBuild", () => {
return gulp
.src("src/html/", { dot: false })
.pipe(
$.phonegapBuild({
isRepository: true,
appId: "3339820",
platforms: ["android", "ios"],
user: {
token: process.env.SHAPEZ_CLI_PHONEGAP_KEY,
},
})
)
.pipe(
$.phonegapBuild({
isRepository: true,
appId: "3537816",
platforms: ["android", "ios"],
user: {
token: process.env.SHAPEZ_CLI_PHONEGAP_KEY,
},
})
);
});
// gulp.task("pushToStagingRepo", (cb) => {
// var cmd = spawn('../push-pgb.sh', ['https://TOKEN@github.com/tobspr/shapezapp-cordova-buildslave.git'],
// { stdio: 'inherit', stdout: 'inherit', stderr: 'inherit', shell: true });
// cmd.on('close', function (code) {
// console.log('push staging exited with code ' + code + " / " + cmd.stdout + " / " + cmd.stderr);
// cb(code);
// });
// });
// gulp.task("pushToProdRepo", (cb) => {
// var cmd = spawn('../push-pgb.sh', ['https://TOKEN@github.com/tobspr/shapezapp-cordova-buildslave-release.git'],
// { stdio: 'inherit', stdout: 'inherit', stderr: 'inherit', shell: true });
// cmd.on('close', function (code) {
// console.log('push prod exited with code ' + code + " / " + cmd.stdout + " / " + cmd.stderr);
// cb(code);
// });
// });
}
module.exports = {
gulptasksCordova,
};

101
gulp/css.js Normal file
View File

@ -0,0 +1,101 @@
const path = require("path");
const buildUtils = require("./buildutils");
function gulptasksCSS($, gulp, buildFolder, browserSync) {
// The assets plugin copies the files
const commitHash = buildUtils.getRevision();
const postcssAssetsPlugin = cachebust =>
$.postcssAssets({
loadPaths: [path.join(buildFolder, "res", "ui")],
basePath: buildFolder,
baseUrl: ".",
cachebuster: cachebust
? (filePath, urlPathname) => ({
pathname: buildUtils.cachebust(urlPathname, commitHash),
})
: "",
});
// Postcss configuration
const postcssPlugins = (prod, { cachebust = false }) => {
const plugins = [postcssAssetsPlugin(cachebust)];
if (prod) {
plugins.unshift(
$.postcssUnprefix(),
$.postcssPresetEnv({
browsers: ["> 0.1%"],
})
);
plugins.push(
$.cssMqpacker({
sort: true,
}),
$.cssnano({
preset: [
"advanced",
{
cssDeclarationSorter: false,
discardUnused: true,
mergeIdents: false,
reduceIdents: true,
zindex: true,
},
],
}),
$.postcssRoundSubpixels()
);
}
return plugins;
};
// Performs linting on css
gulp.task("css.lint", () => {
return gulp
.src(["../src/css/**/*.scss"])
.pipe($.sassLint({ configFile: ".sasslint.yml" }))
.pipe($.sassLint.format())
.pipe($.sassLint.failOnError());
});
// Builds the css in dev mode
gulp.task("css.dev", () => {
return gulp
.src(["../src/css/main.scss"])
.pipe($.plumber())
.pipe($.sass.sync().on("error", $.sass.logError))
.pipe($.postcss(postcssPlugins(false, {})))
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
// Builds the css in production mode (=minified)
gulp.task("css.prod", () => {
return (
gulp
.src("../src/css/main.scss", { cwd: __dirname })
.pipe($.plumber())
.pipe($.sass.sync({ outputStyle: "compressed" }).on("error", $.sass.logError))
.pipe($.postcss(postcssPlugins(true, { cachebust: true })))
// .pipe($.cssbeautify())
.pipe(gulp.dest(buildFolder))
);
});
// Builds the css in production mode (=minified), without cachebusting
gulp.task("css.prod-standalone", () => {
return (
gulp
.src("../src/css/main.scss", { cwd: __dirname })
.pipe($.plumber())
.pipe($.sass.sync({ outputStyle: "compressed" }).on("error", $.sass.logError))
.pipe($.postcss(postcssPlugins(true, { cachebust: false })))
// .pipe($.cssbeautify())
.pipe(gulp.dest(buildFolder))
);
});
}
module.exports = {
gulptasksCSS,
};

38
gulp/docs.js Normal file
View File

@ -0,0 +1,38 @@
const path = require("path");
const fs = require("fs");
function gulptasksDocs($, gulp, buildFolder) {
gulp.task("docs.convertJsToTs", () => {
return gulp
.src(path.join("..", "src", "js", "**", "*.js"))
.pipe(
$.rename(path => {
path.extname = ".ts";
})
)
.pipe(gulp.dest(path.join("..", "tsc_temp")));
});
gulp.task("docs.copyTsconfigForHints", () => {
const src = fs.readFileSync(path.join("..", "src", "js", "tsconfig.json")).toString();
const baseConfig = JSON.parse($.stripJsonComments(src));
baseConfig.allowJs = false;
baseConfig.checkJs = false;
baseConfig.declaration = true;
baseConfig.noEmit = false;
baseConfig.strict = false;
baseConfig.strictFunctionTypes = false;
baseConfig.strictBindCallApply = false;
baseConfig.alwaysStrict = false;
baseConfig.composite = true;
baseConfig.outFile = "bundled-ts.js";
fs.writeFileSync(path.join("..", "tsc_temp", "tsconfig.json"), JSON.stringify(baseConfig));
});
gulp.task("main.prepareDocs", $.sequence("docs.convertJsToTs", "docs.copyTsconfigForHints"));
}
module.exports = {
gulptasksDocs,
};

104
gulp/ftp.js Normal file
View File

@ -0,0 +1,104 @@
const path = require("path");
const fs = require("fs");
const buildUtils = require("./buildutils");
function gulptasksFTP($, gulp, buildFolder) {
const commitHash = buildUtils.getRevision();
// Write the "commit.txt" file
gulp.task("ftp.writeVersion", () => {
fs.writeFileSync(
path.join(buildFolder, "version.json"),
JSON.stringify(
{
commit: buildUtils.getRevision(),
appVersion: buildUtils.getVersion(),
buildTime: new Date().getTime(),
},
null,
4
)
);
});
// Copies additional files (like .htaccess) which should be deployed when running
// on the ftp server
// gulp.task("ftp.copyServerFiles", () => {
// return gulp.src(["../ftp_upload/*.*", "../ftp_upload/.*", "../ftp_upload/*"])
// .pipe(gulp.dest(buildFolder));
// });
const gameSrcGlobs = [
path.join(buildFolder, "**/*.*"),
path.join(buildFolder, "**/.*"),
path.join(buildFolder, "**/*"),
path.join(buildFolder, "!**/index.html"),
];
gulp.task("ftp.upload.staging.game", () => {
return gulp
.src(gameSrcGlobs, { base: buildFolder })
.pipe(
$.rename(pth => {
pth.dirname = path.join("v", commitHash, pth.dirname);
})
)
.pipe(
$.sftp({
host: process.env.SHAPEZ_CLI_SERVER_HOST,
user: process.env.SHAPEZ_CLI_STAGING_FTP_USER,
pass: process.env.SHAPEZ_CLI_STAGING_FTP_PW,
})
);
});
gulp.task("ftp.upload.staging.indexHtml", () => {
return gulp.src(path.join(buildFolder, "index.html"), { base: buildFolder }).pipe(
$.sftp({
host: process.env.SHAPEZ_CLI_SERVER_HOST,
user: process.env.SHAPEZ_CLI_STAGING_FTP_USER,
pass: process.env.SHAPEZ_CLI_STAGING_FTP_PW,
})
);
});
gulp.task("ftp.upload.staging", cb => {
$.sequence("ftp.writeVersion", "ftp.upload.staging.game", "ftp.upload.staging.indexHtml")(cb);
});
gulp.task("ftp.upload.prod.game", () => {
return gulp
.src(gameSrcGlobs, { base: buildFolder })
.pipe(
$.rename(pth => {
pth.dirname = path.join("v", commitHash, pth.dirname);
})
)
.pipe(
$.sftp({
host: process.env.SHAPEZ_CLI_SERVER_HOST,
user: process.env.SHAPEZ_CLI_LIVE_FTP_USER,
pass: process.env.SHAPEZ_CLI_LIVE_FTP_PW,
})
);
});
gulp.task("ftp.upload.prod.indexHtml", () => {
return gulp.src(path.join(buildFolder, "index.html"), { base: buildFolder }).pipe(
$.sftp({
host: process.env.SHAPEZ_CLI_SERVER_HOST,
user: process.env.SHAPEZ_CLI_LIVE_FTP_USER,
pass: process.env.SHAPEZ_CLI_LIVE_FTP_PW,
})
);
});
gulp.task("ftp.upload.prod", cb => {
$.sequence("ftp.writeVersion", "ftp.upload.prod.game", "ftp.upload.prod.indexHtml")(cb);
});
}
module.exports = {
gulptasksFTP,
};

274
gulp/gulpfile.js Normal file
View File

@ -0,0 +1,274 @@
/* eslint-disable */
require("colors");
const gulp = require("gulp");
const browserSync = require("browser-sync").create({});
const path = require("path");
const deleteEmpty = require("delete-empty");
const execSync = require("child_process").execSync;
// Load other plugins dynamically
const $ = require("gulp-load-plugins")({
scope: ["devDependencies"],
pattern: "*",
});
// Check environment variables
const envVars = [
"SHAPEZ_CLI_SERVER_HOST",
// "SHAPEZ_CLI_PHONEGAP_KEY",
"SHAPEZ_CLI_STAGING_FTP_USER",
"SHAPEZ_CLI_STAGING_FTP_PW",
"SHAPEZ_CLI_LIVE_FTP_USER",
"SHAPEZ_CLI_LIVE_FTP_PW",
// "SHAPEZ_CLI_TRANSREPORT_FTP_USER",
// "SHAPEZ_CLI_TRANSREPORT_FTP_PW",
];
for (let i = 0; i < envVars.length; ++i) {
if (!process.env[envVars[i]]) {
console.warn("Please set", envVars[i]);
// process.exit(1);
}
}
const baseDir = path.join(__dirname, "..");
const buildFolder = path.join(baseDir, "build");
const imgres = require("./image-resources");
imgres.gulptasksImageResources($, gulp, buildFolder);
const css = require("./css");
css.gulptasksCSS($, gulp, buildFolder, browserSync);
const sounds = require("./sounds");
sounds.gulptasksSounds($, gulp, buildFolder);
const js = require("./js");
js.gulptasksJS($, gulp, buildFolder, browserSync);
const html = require("./html");
html.gulptasksHTML($, gulp, buildFolder, browserSync);
const ftp = require("./ftp");
ftp.gulptasksFTP($, gulp, buildFolder);
const docs = require("./docs");
docs.gulptasksDocs($, gulp, buildFolder);
const standalone = require("./standalone");
standalone.gulptasksStandalone($, gulp, buildFolder);
// FIXME
// const cordova = require("./cordova");
// cordova.gulptasksCordova($, gulp, buildFolder);
///////////////////// BUILD TASKS /////////////////////
// Cleans up everything
gulp.task("utils.cleanup", () => {
return gulp.src(buildFolder, { read: false }).pipe($.clean({ force: true }));
});
// Requires no uncomitted files
gulp.task("utils.requireCleanWorkingTree", cb => {
const output = $.trim(execSync("git status -su").toString("ascii"));
if (output.length > 0) {
console.error("\n\nYou have unstaged changes, please commit everything first!");
process.exit(1);
}
cb();
});
gulp.task("utils.copyAdditionalBuildFiles", cb => {
const additionalFolder = path.join("additional_build_files");
const additionalSrcGlobs = [
path.join(additionalFolder, "**/*.*"),
path.join(additionalFolder, "**/.*"),
path.join(additionalFolder, "**/*"),
];
return gulp.src(additionalSrcGlobs).pipe(gulp.dest(buildFolder));
});
// Starts a webserver on the built directory (useful for testing prod build)
gulp.task("main.webserver", () => {
return gulp.src(buildFolder).pipe(
$.webserver({
livereload: {
enable: true,
},
directoryListing: false,
open: true,
port: 3005,
})
);
});
function serve({ standalone }) {
browserSync.init({
server: buildFolder,
port: 3005,
ghostMode: {
clicks: false,
scroll: false,
location: false,
forms: false,
},
logLevel: "info",
logPrefix: "BS",
online: false,
xip: false,
notify: false,
reloadDebounce: 100,
reloadOnRestart: true,
watchEvents: ["add", "change"],
});
// Watch .scss files, those trigger a css rebuild
gulp.watch(["../src/**/*.scss"], ["css.dev"]);
// Watch .html files, those trigger a html rebuild
gulp.watch("../src/**/*.html", [standalone ? "html.standalone-dev" : "html.dev"]);
// Watch sound files
// gulp.watch(["../res_raw/sounds/**/*.mp3", "../res_raw/sounds/**/*.wav"], ["sounds.dev"]);
gulp.watch(
["../res_raw/sounds/ui/*.mp3", "../res_raw/sounds/ui/*.wav"],
$.sequence("sounds.encodeUi", "sounds.copy")
);
gulp.watch(
["../res_raw/sounds/game/*.mp3", "../res_raw/sounds/game/*.wav"],
$.sequence("sounds.encodeGame", "sounds.copy")
);
gulp.watch(
["../res_raw/sounds/music/*.mp3", "../res_raw/sounds/music/*.wav"],
$.sequence("sounds.encodeMusic", "sounds.copy")
);
// Watch resource files and copy them on change
gulp.watch(imgres.nonImageResourcesGlobs, ["imgres.copyNonImageResources"]);
gulp.watch(imgres.imageResourcesGlobs, ["imgres.copyImageResources"]);
// Watch .atlas files and recompile the atlas on change
gulp.watch("../res_built/atlas/*.json", ["imgres.atlas"]);
// Watch the build folder and reload when anything changed
const extensions = ["html", "js", "png", "jpg", "svg", "mp3", "ico", "woff2"];
gulp.watch(extensions.map(ext => path.join(buildFolder, "**", "*." + ext))).on("change", function (e) {
return gulp.src(e.path).pipe(browserSync.reload({ stream: true }));
});
// Start the webpack watching server (Will never return)
if (standalone) {
$.sequence("js.standalone-dev.watch")(() => true);
} else {
$.sequence("js.dev.watch")(() => true);
}
}
// Live-development
gulp.task("main.serveDev", ["build.dev"], () => serve({ standalone: false }));
gulp.task("main.serveStandalone", ["build.standalone.dev"], () => serve({ standalone: true }));
gulp.task("default", ["main.serveDev"]);
///////////////////// RUNNABLE TASKS /////////////////////
// Pre and postbuild
gulp.task("step.baseResources", cb => $.multiProcess(["sounds.fullbuild", "imgres.allOptimized"], cb, false));
gulp.task("step.deleteEmpty", cb => {
deleteEmpty.sync(buildFolder);
cb();
});
gulp.task("step.postbuild", $.sequence("imgres.cleanupUnusedCssInlineImages", "step.deleteEmpty"));
// Builds everything (dev)
gulp.task("build.dev", cb => {
$.sequence(
"utils.cleanup",
"utils.copyAdditionalBuildFiles",
"imgres.atlas",
"sounds.dev",
"imgres.copyImageResources",
"imgres.copyNonImageResources",
"js.dev",
"css.dev",
"html.dev"
)(cb);
});
// Builds everything (standalone -dev)
gulp.task("build.standalone.dev", cb => {
$.sequence(
"utils.cleanup",
"imgres.atlas",
"sounds.dev",
"imgres.copyImageResources",
"imgres.copyNonImageResources",
"js.standalone-dev",
"css.dev",
"html.standalone-dev"
)(cb);
});
// Builds everything (staging)
gulp.task("step.staging.code", $.sequence("js.staging"));
gulp.task("step.staging.mainbuild", cb =>
$.multiProcess(["utils.copyAdditionalBuildFiles", "step.baseResources", "step.staging.code"], cb, false)
);
gulp.task("step.staging.all", $.sequence("step.staging.mainbuild", "css.prod", "html.staging"));
gulp.task("build.staging", $.sequence("utils.cleanup", "step.staging.all", "step.postbuild"));
// Builds everything (prod)
gulp.task("step.prod.code", $.sequence("js.prod"));
gulp.task("step.prod.mainbuild", cb =>
$.multiProcess(["utils.copyAdditionalBuildFiles", "step.baseResources", "step.prod.code"], cb, false)
);
gulp.task("step.prod.all", $.sequence("step.prod.mainbuild", "css.prod", "html.prod"));
gulp.task("build.prod", $.sequence("utils.cleanup", "step.prod.all", "step.postbuild"));
// Builds everything (standalone-beta)
gulp.task("step.standalone-beta.code", $.sequence("js.standalone-beta"));
gulp.task("step.standalone-beta.mainbuild", cb =>
$.multiProcess(
["utils.copyAdditionalBuildFiles", "step.baseResources", "step.standalone-beta.code"],
cb,
false
)
);
gulp.task(
"step.standalone-beta.all",
$.sequence("step.standalone-beta.mainbuild", "css.prod-standalone", "html.standalone-beta")
);
gulp.task("build.standalone-beta", $.sequence("utils.cleanup", "step.standalone-beta.all", "step.postbuild"));
// Builds everything (standalone-prod)
gulp.task("step.standalone-prod.code", $.sequence("js.standalone-prod"));
gulp.task("step.standalone-prod.mainbuild", cb =>
$.multiProcess(
["utils.copyAdditionalBuildFiles", "step.baseResources", "step.standalone-prod.code"],
cb,
false
)
);
gulp.task(
"step.standalone-prod.all",
$.sequence("step.standalone-prod.mainbuild", "css.prod-standalone", "html.standalone-prod")
);
gulp.task("build.standalone-prod", $.sequence("utils.cleanup", "step.standalone-prod.all", "step.postbuild"));
// Deploying!
gulp.task(
"main.deploy.staging",
$.sequence("utils.requireCleanWorkingTree", "build.staging", "ftp.upload.staging")
);
gulp.task("main.deploy.prod", $.sequence("utils.requireCleanWorkingTree", "build.prod", "ftp.upload.prod"));
gulp.task("main.deploy.all", $.sequence("main.deploy.staging", "main.deploy.prod"));
// gulp.task("main.standalone.beta", $.sequence("build.standalone-beta", "standalone.package.beta"));
gulp.task("main.standalone", $.sequence("build.standalone-prod", "standalone.package.prod"));

360
gulp/html.js Normal file
View File

@ -0,0 +1,360 @@
const buildUtils = require("./buildutils");
const fs = require("fs");
const path = require("path");
const crypto = require("crypto");
function computeIntegrityHash(fullPath, algorithm = "sha256") {
const file = fs.readFileSync(fullPath);
const hash = crypto.createHash(algorithm).update(file).digest("base64");
return algorithm + "-" + hash;
}
function gulptasksHTML($, gulp, buildFolder, browserSync) {
const commitHash = buildUtils.getRevision();
async function buildHtml(
apiUrl,
{
analytics = false,
standalone = false,
app = false,
integrity = true,
enableCachebust = true,
gameAnalyticsKey = null,
gameAnalyticsSecret = null,
}
) {
function cachebust(url) {
if (enableCachebust) {
return buildUtils.cachebust(url, commitHash);
}
return url;
}
const hasLocalFiles = standalone || app;
return gulp
.src("../src/html/" + (standalone ? "index.standalone.html" : "index.html"))
.pipe(
$.dom(function () {
// @ts-ignore
const document = /** @type {Document} */ (this);
// Preconnect to api
const prefetchLink = document.createElement("link");
prefetchLink.rel = "preconnect";
prefetchLink.href = apiUrl;
prefetchLink.setAttribute("crossorigin", "anonymous");
document.head.appendChild(prefetchLink);
// // Append css preload
// const cssPreload = document.createElement("link");
// cssPreload.rel = "preload";
// cssPreload.href = cachebust("main.css");
// cssPreload.setAttribute("as", "style");
// document.head.appendChild(cssPreload);
// document.head.appendChild(prefetchLink);
// // Append js preload
// const jsPreload = document.createElement("link");
// jsPreload.rel = "preload";
// jsPreload.href = cachebust("bundle.js");
// jsPreload.setAttribute("as", "script");
// document.head.appendChild(jsPreload);
// Append css
const css = document.createElement("link");
css.rel = "stylesheet";
css.type = "text/css";
css.media = "none";
css.setAttribute("onload", "this.media='all'");
css.href = cachebust("main.css");
if (integrity) {
css.setAttribute(
"integrity",
computeIntegrityHash(path.join(buildFolder, "main.css"))
);
}
document.head.appendChild(css);
if (analytics) {
// Logrocket
// const logrocketScript = document.createElement("script");
// logrocketScript.src = "https://cdn.lr-ingest.io/LogRocket.min.js";
// logrocketScript.setAttribute("crossorigin", "anonymous");
// document.head.appendChild(logrocketScript);
// const logrocketInit = document.createElement("script");
// logrocketInit.textContent = "window.LogRocket && window.LogRocket.init('TODO: GET LOGROCKET ID');";
// document.head.appendChild(logrocketInit);
}
if (gameAnalyticsKey && gameAnalyticsSecret) {
const gaLoader = document.createElement("script");
gaLoader.textContent = `
window.GameAnalytics=window.GameAnalytics||function(){(GameAnalytics.q=GameAnalytics.q||[]).push(arguments)};
window.ga_comKey = "${gameAnalyticsKey}";
window.ga_comToken = "${gameAnalyticsSecret}";
`;
document.head.appendChild(gaLoader);
const gaScript = document.createElement("script");
gaScript.src = "https://download.gameanalytics.com/js/GameAnalytics-4.0.10.min.js";
gaScript.setAttribute("async", "");
document.head.appendChild(gaScript);
}
if (app) {
// Append cordova link
const cdv = document.createElement("script");
cdv.src = "cordova.js";
cdv.type = "text/javascript";
document.head.appendChild(cdv);
}
// Google analytics
if (analytics) {
const tagManagerScript = document.createElement("script");
tagManagerScript.src = "https://www.googletagmanager.com/gtag/js?id=UA-165342524-1";
tagManagerScript.setAttribute("async", "");
document.head.appendChild(tagManagerScript);
const initScript = document.createElement("script");
initScript.textContent = `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-165342524-1', { anonymize_ip: true });
`;
document.head.appendChild(initScript);
}
// Do not need to preload in app or standalone
if (!hasLocalFiles) {
// Preload images
const images = buildUtils.getAllResourceImages();
// Preload essentials
const preloads = ["fonts/LouisGeorgeCafe.woff2"];
// for (let i = 0; i < images.length; ++i) {
// if (preloads.indexOf(images[i]) < 0) {
// preloads.push(images[i]);
// }
// }
preloads.forEach(src => {
const preloadLink = document.createElement("link");
preloadLink.rel = "preload";
preloadLink.href = cachebust("res/" + src);
if (src.endsWith(".woff2")) {
preloadLink.setAttribute("crossorigin", "anonymous");
preloadLink.setAttribute("as", "font");
} else {
preloadLink.setAttribute("as", "image");
}
document.head.appendChild(preloadLink);
});
// Sound preloads
// const sounds = buildUtils.getAllSounds();
// sounds.forEach((src) => {
// if (src.indexOf("sounds/music/") >= 0) {
// // skip music
// return;
// }
// const preloadLink = document.createElement("link");
// preloadLink.rel = "preload";
// preloadLink.href = cachebust(src);
// // preloadLink.setAttribute("crossorigin", "anonymous");
// preloadLink.setAttribute("as", "fetch");
// document.head.appendChild(preloadLink);
// });
}
const loadingSvg = `background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHN0eWxlPSJtYXJnaW46YXV0bztiYWNrZ3JvdW5kOjAgMCIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgZGlzcGxheT0iYmxvY2siPjxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzM5Mzc0NyIgc3Ryb2tlLXdpZHRoPSIzIiByPSI0MiIgc3Ryb2tlLWRhc2hhcnJheT0iMTk3LjkyMDMzNzE3NjE1Njk4IDY3Ljk3MzQ0NTcyNTM4NTY2IiB0cmFuc2Zvcm09InJvdGF0ZSg0OC4yNjUgNTAgNTApIj48YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iIHR5cGU9InJvdGF0ZSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGR1cj0iNS41NTU1NTU1NTU1NTU1NTVzIiB2YWx1ZXM9IjAgNTAgNTA7MzYwIDUwIDUwIiBrZXlUaW1lcz0iMDsxIi8+PC9jaXJjbGU+PC9zdmc+")`;
const loadingCss = `
@font-face {
font-family: 'GameFont';
font-style: normal;
font-weight: normal;
font-display: swap;
src: url('${cachebust("res/fonts/LouisGeorgeCafe.woff2")}') format('woff2');
}
#ll_fp {
font-family: GameFont;
font-size: 14px;
position: fixed;
z-index: -1;
top: 0;
left: 0;
opacity: 0.05;
}
#ll_p {
display: flex;
position: fixed;
z-index: 99999;
top: 0;
left: 0;
right: 0;
bottom: 0;
justify-content:
center;
align-items: center;
}
#ll_p > div {
position: absolute;
text-align: center;
bottom: 40px;
left: 20px;
right: 20px;
color: #393747;
font-family: 'GameFont', sans-serif;
font-size: 20px;
}
#ll_p > span {
width: 60px;
height: 60px;
display: inline-flex;
background: center center / contain no-repeat;
${loadingSvg};
}
`;
const style = document.createElement("style");
style.setAttribute("type", "text/css");
style.textContent = loadingCss;
document.head.appendChild(style);
// Append loader, but not in standalone (directly include bundle there)
if (standalone) {
const bundleScript = document.createElement("script");
bundleScript.type = "text/javascript";
bundleScript.src = "bundle.js";
if (integrity) {
bundleScript.setAttribute(
"integrity",
computeIntegrityHash(path.join(buildFolder, "bundle.js"))
);
}
document.head.appendChild(bundleScript);
} else {
const loadJs = document.createElement("script");
loadJs.type = "text/javascript";
let scriptContent = "";
scriptContent += `var bundleSrc = '${cachebust("bundle.js")}';\n`;
scriptContent += `var bundleSrcTranspiled = '${cachebust(
"bundle-transpiled.js"
)}';\n`;
if (integrity) {
scriptContent +=
"var bundleIntegrity = '" +
computeIntegrityHash(path.join(buildFolder, "bundle.js")) +
"';\n";
scriptContent +=
"var bundleIntegrityTranspiled = '" +
computeIntegrityHash(path.join(buildFolder, "bundle-transpiled.js")) +
"';\n";
} else {
scriptContent += "var bundleIntegrity = null;\n";
scriptContent += "var bundleIntegrityTranspiled = null;\n";
}
scriptContent += fs.readFileSync("./bundle-loader.js").toString();
loadJs.textContent = scriptContent;
document.head.appendChild(loadJs);
}
const bodyContent = `
<div id="ll_fp">_</div>
<div id="ll_p">
<span></span>
<div>${hasLocalFiles ? "Loading" : "Downloading"} Game Files</div >
</div >
`;
document.body.innerHTML = bodyContent;
})
)
.pipe(
$.htmlmin({
caseSensitive: true,
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true,
collapseWhitespace: true,
preserveLineBreaks: true,
minifyJS: true,
minifyCSS: true,
quoteCharacter: '"',
useShortDoctype: true,
})
)
.pipe($.htmlBeautify())
.pipe($.rename("index.html"))
.pipe(gulp.dest(buildFolder));
}
gulp.task("html.dev", () => {
return buildHtml("http://localhost:5005", {
analytics: false,
integrity: false,
enableCachebust: false,
gameAnalyticsKey: "c8d77921633d5c32a7134e5d5cfcdf12",
// Not an actual "secret" since its built into the JS code
gameAnalyticsSecret: "6d23b40a70199bff0e7a7d8a073543772cf07097",
});
});
gulp.task("html.staging", () => {
return buildHtml("https://api-staging.shapez.io", {
analytics: true,
gameAnalyticsKey: "903fa0dd2d2e23b07e66ea96ddc4c10c",
// Not an actual "secret" since its built into the JS code
gameAnalyticsSecret: "9417fc391d7142b9d73a3861ba6046cafa9df6cb",
});
});
gulp.task("html.prod", () => {
return buildHtml("https://api.shapez.io", {
analytics: true,
gameAnalyticsKey: "16c7f9d352e40c92f6a750fc1a4f0443",
// Not an actual "secret" since its built into the JS code
gameAnalyticsSecret: "4202d7adf154c325ff91731e8be6912e6c0d10e5",
});
});
gulp.task("html.standalone-dev", () => {
return buildHtml("https://localhost:5005", {
analytics: false,
standalone: true,
integrity: false,
enableCachebust: false,
});
});
gulp.task("html.standalone-beta", () => {
return buildHtml("https://api-staging.shapez.io", {
analytics: true,
standalone: true,
enableCachebust: false,
});
});
gulp.task("html.standalone-prod", () => {
return buildHtml("https://api.shapez.io", {
analytics: true,
standalone: true,
enableCachebust: false,
});
});
}
module.exports = {
gulptasksHTML,
};

138
gulp/image-resources.js Normal file
View File

@ -0,0 +1,138 @@
// @ts-ignore
const path = require("path");
// Globs for non-ui resources
const nonImageResourcesGlobs = ["../res/**/*.woff2", "../res/*.ico"];
// Globs for ui resources
const imageResourcesGlobs = ["../res/**/*.png", "../res/**/*.svg", "../res/**/*.jpg"];
function gulptasksImageResources($, gulp, buildFolder) {
// Lossless options
const minifyImagesOptsLossless = () => [
$.imagemin.jpegtran({
progressive: true,
}),
$.imagemin.svgo({}),
$.imagemin.optipng({
optimizationLevel: 3,
}),
];
// Lossy options
const minifyImagesOpts = () => [
$.imageminMozjpeg({
quality: 80,
maxMemory: 1024 * 1024 * 8,
}),
$.imagemin.svgo({}),
$.imageminPngquant({
speed: 1,
strip: true,
quality: [0.65, 0.9],
dithering: false,
verbose: false,
}),
$.imagemin.optipng({
optimizationLevel: 3,
}),
];
// Where the resources folder are
const resourcesDestFolder = path.join(buildFolder, "res");
/**
* Determines if an atlas must use lossless compression
* @param {string} fname
*/
function fileMustBeLossless(fname) {
return fname.indexOf("lossless") >= 0;
}
/////////////// ATLAS /////////////////////
// Copies the atlas to the final destination
gulp.task("imgres.atlas", () => {
return gulp
.src(["../res_built/atlas/*.png"])
.pipe($.cached("imgres.atlas"))
.pipe(gulp.dest(resourcesDestFolder));
});
// Copies the atlas to the final destination after optimizing it (lossy compression)
gulp.task("imgres.atlasOptimized", () => {
return gulp
.src(["../res_built/atlas/*.png"])
.pipe($.cached("imgres.atlasOptimized"))
.pipe(
$.if(
fname => fileMustBeLossless(fname.history[0]),
$.imagemin(minifyImagesOptsLossless()),
$.imagemin(minifyImagesOpts())
)
)
.pipe(gulp.dest(resourcesDestFolder));
});
//////////////////// RESOURCES //////////////////////
// Copies all resources which are no ui resources
gulp.task("imgres.copyNonImageResources", () => {
return gulp
.src(nonImageResourcesGlobs)
.pipe($.cached("imgres.copyNonImageResources"))
.pipe(gulp.dest(resourcesDestFolder));
});
// Copies all ui resources
gulp.task("imgres.copyImageResources", () => {
return gulp
.src(imageResourcesGlobs)
.pipe($.cached("copyImageResources"))
.pipe(gulp.dest(path.join(resourcesDestFolder)));
});
// Copies all ui resources and optimizes them
gulp.task("imgres.copyImageResourcesOptimized", () => {
return gulp
.src(imageResourcesGlobs)
.pipe($.cached("imgres.copyImageResourcesOptimized"))
.pipe(
$.if(
fname => fileMustBeLossless(fname.history[0]),
$.imagemin(minifyImagesOptsLossless()),
$.imagemin(minifyImagesOpts())
)
)
.pipe(gulp.dest(path.join(resourcesDestFolder)));
});
// Copies all resources and optimizes them
gulp.task("imgres.allOptimized", cb =>
$.multiProcess(
["imgres.atlasOptimized", "imgres.copyNonImageResources", "imgres.copyImageResourcesOptimized"],
cb,
false
)
);
// Cleans up unused images which are instead inline into the css
gulp.task("imgres.cleanupUnusedCssInlineImages", () => {
return gulp
.src(
[
path.join(buildFolder, "res", "ui", "**", "*.png"),
path.join(buildFolder, "res", "ui", "**", "*.jpg"),
path.join(buildFolder, "res", "ui", "**", "*.svg"),
],
{ read: false }
)
.pipe($.if(fname => fname.history[0].indexOf("noinline") < 0, $.clean({ force: true })));
});
}
module.exports = {
nonImageResourcesGlobs,
imageResourcesGlobs,
gulptasksImageResources,
};

182
gulp/js.js Normal file
View File

@ -0,0 +1,182 @@
const path = require("path");
function requireUncached(module) {
delete require.cache[require.resolve(module)];
return require(module);
}
function gulptasksJS($, gulp, buildFolder, browserSync) {
gulp.task("js.prettify", () => {
return gulp
.src(path.join(buildFolder, "bundle.js"))
.pipe($.jsbeautifier(require("./jsbeautify.json")))
.pipe(gulp.dest(buildFolder));
});
//// DEV
gulp.task("js.dev.watch", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
watch: true,
})
)
)
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
gulp.task("js.dev", () => {
return gulp
.src("../src/js/main.js")
.pipe($.webpackStream(requireUncached("./webpack.config.js")({})))
.pipe(gulp.dest(buildFolder));
});
//// STAGING
gulp.task("js.staging.transpiled", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: true,
environment: "staging",
apiEndpoint: "https://api-staging.shapez.io/v1",
es6: false,
})
)
)
.pipe($.rename("bundle-transpiled.js"))
.pipe(gulp.dest(buildFolder));
});
gulp.task("js.staging.latest", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: true,
environment: "staging",
apiEndpoint: "https://api-staging.shapez.io/v1",
es6: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task("js.staging", cb => $.multiProcess(["js.staging.transpiled", "js.staging.latest"], cb, false));
//// PROD
gulp.task("js.prod.transpiled", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: false,
environment: "prod",
apiEndpoint: "https://api.shapez.io/v1",
es6: false,
})
)
)
.pipe($.rename("bundle-transpiled.js"))
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
gulp.task("js.prod.latest", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: false,
environment: "prod",
es6: true,
apiEndpoint: "https://api.shapez.io/v1",
})
)
)
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
gulp.task("js.prod", cb => $.multiProcess(["js.prod.transpiled", "js.prod.latest"], cb, false));
//// STANDALONE
gulp.task("js.standalone-dev.watch", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
watch: true,
standalone: true,
})
)
)
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
gulp.task("js.standalone-dev", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
standalone: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task("js.standalone-beta", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: true,
environment: "staging",
apiEndpoint: "https://api-staging.shapez.io/v1",
es6: true,
standalone: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task("js.standalone-prod", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: false,
environment: "prod",
apiEndpoint: "https://api.shapez.io/v1",
es6: true,
standalone: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
// TODO: Tasks for te app
}
module.exports = {
gulptasksJS,
};

6
gulp/jsconfig.json Normal file
View File

@ -0,0 +1,6 @@
{
"compilerOptions": {
"target": "es6",
"checkJs": true
}
}

View File

@ -0,0 +1,11 @@
"use strict";
const lzString = require("lz-string");
module.exports = function (source) {
const compressed = lzString.compressToEncodedURIComponent(source);
const sourcecode = `module.exports = (function() {
return JSON.parse(require("global-compression").decompressX64("${compressed}"));
})()`;
return sourcecode;
};

View File

@ -0,0 +1,21 @@
/*jslint node:true */
"use strict";
const startComment = "typehints:start";
const endComment = "typehints:end";
const regexPattern = new RegExp(
"[\\t ]*\\/\\* ?" + startComment + " ?\\*\\/[\\s\\S]*?\\/\\* ?" + endComment + " ?\\*\\/[\\t ]*\\n?",
"g"
);
function StripBlockLoader(content) {
if (content.indexOf(startComment) >= 0) {
content = content.replace(regexPattern, "");
}
if (this.cacheable) {
this.cacheable(true);
}
return content;
}
module.exports = StripBlockLoader;

115
gulp/package.json Normal file
View File

@ -0,0 +1,115 @@
{
"name": "builder",
"version": "1.0.0",
"description": "builder",
"private": true,
"main": "main.js",
"scripts": {
"gulp": "gulp"
},
"author": "tobspr",
"license": "private",
"dependencies": {
"@babel/core": "^7.9.0",
"@babel/plugin-transform-block-scoping": "^7.4.4",
"@babel/plugin-transform-classes": "^7.5.5",
"@babel/preset-env": "^7.5.4",
"@types/cordova": "^0.0.34",
"@types/filesystem": "^0.0.29",
"@types/node": "^12.7.5",
"ajv": "^6.10.2",
"babel-loader": "^8.1.0",
"browser-sync": "^2.24.6",
"circular-dependency-plugin": "^5.0.2",
"circular-json": "^0.5.9",
"colors": "^1.3.3",
"core-js": "3",
"crypto": "^1.0.1",
"cssnano-preset-advanced": "^4.0.7",
"delete-empty": "^3.0.0",
"email-validator": "^2.0.4",
"eslint": "^5.9.0",
"fastdom": "^1.0.9",
"flatted": "^2.0.1",
"fs-extra": "^8.1.0",
"howler": "^2.1.2",
"html-loader": "^0.5.5",
"ignore-loader": "^0.1.2",
"lz-string": "^1.4.4",
"markdown-loader": "^5.1.0",
"node-sri": "^1.1.1",
"obfuscator-loader": "^1.1.2",
"phonegap-plugin-mobile-accessibility": "^1.0.5",
"promise-polyfill": "^8.1.0",
"query-string": "^6.8.1",
"rusha": "^0.8.13",
"serialize-error": "^3.0.0",
"sloc": "^0.2.1",
"strictdom": "^1.0.1",
"string-replace-webpack-plugin": "^0.1.3",
"terser-webpack-plugin": "^1.1.0",
"uglify-template-string-loader": "^1.1.0",
"unused-files-webpack-plugin": "^3.4.0",
"webpack": "^4.31.0",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.0",
"webpack-deep-scope-plugin": "^1.6.0",
"webpack-plugin-replace": "^1.1.1",
"webpack-strip-block": "^0.2.0",
"whatwg-fetch": "^3.0.0",
"worker-loader": "^2.0.0"
},
"devDependencies": {
"autoprefixer": "^9.4.3",
"babel-plugin-closure-elimination": "^1.3.0",
"babel-plugin-console-source": "^2.0.2",
"babel-plugin-danger-remove-unused-import": "^1.1.2",
"css-mqpacker": "^7.0.0",
"cssnano": "^4.1.10",
"electron-packager": "^14.0.6",
"faster.js": "^1.1.0",
"glob": "^7.1.3",
"gulp": "^3.9.1",
"gulp-cache": "^1.1.3",
"gulp-cached": "^1.1.1",
"gulp-clean": "^0.4.0",
"gulp-cssbeautify": "^1.0.1",
"gulp-csslint": "^1.0.1",
"gulp-dom": "^1.0.0",
"gulp-flatten": "^0.4.0",
"gulp-fluent-ffmpeg": "^1.0.2",
"gulp-html-beautify": "^1.0.1",
"gulp-htmlmin": "^5.0.1",
"gulp-if": "^2.0.2",
"gulp-imagemin": "^5.0.3",
"gulp-javascript-obfuscator": "^1.1.5",
"gulp-jsbeautifier": "^3.0.0",
"gulp-load-plugins": "^1.5.0",
"gulp-multi-process": "^1.3.1",
"gulp-phonegap-build": "^0.1.5",
"gulp-plumber": "^1.2.1",
"gulp-pngquant": "^1.0.12",
"gulp-postcss": "^8.0.0",
"gulp-rename": "^1.4.0",
"gulp-sass": "^4.0.1",
"gulp-sass-lint": "^1.4.0",
"gulp-sequence": "^1.0.0",
"gulp-sftp": "^0.1.5",
"gulp-terser": "^1.2.0",
"gulp-webserver": "^0.9.1",
"imagemin-mozjpeg": "^8.0.0",
"imagemin-pngquant": "^8.0.0",
"jimp": "^0.6.1",
"js-yaml": "^3.13.1",
"onesky-fetch": "^0.0.7",
"postcss-assets": "^5.0.0",
"postcss-preset-env": "^6.5.0",
"postcss-round-subpixels": "^1.2.0",
"postcss-unprefix": "^2.1.3",
"sass-unused": "^0.3.0",
"speed-measure-webpack-plugin": "^1.3.1",
"strip-json-comments": "^3.0.1",
"trim": "^0.0.1",
"webpack-stream": "^5.1.0"
}
}

107
gulp/sounds.js Normal file
View File

@ -0,0 +1,107 @@
const path = require("path");
function gulptasksSounds($, gulp, buildFolder) {
// Gather some basic infos
const soundsDir = path.join("..", "res_raw", "sounds");
const builtSoundsDir = path.join("..", "res_built", "sounds");
gulp.task("sounds.clear", () => {
return gulp.src(builtSoundsDir).pipe($.clean({ force: true }));
});
const filters = ["loudnorm", "volume=0.2"];
const fileCache = new $.cache.Cache({
cacheDirName: "shapezio-precompiled-sounds",
});
// Encodes the game music
gulp.task("sounds.encodeMusic", () => {
return gulp
.src([path.join(soundsDir, "music", "**", "*.wav"), path.join(soundsDir, "music", "**", "*.mp3")])
.pipe(
$.cache(
$.fluentFfmpeg("mp3", function (cmd) {
return cmd
.audioBitrate(48)
.audioChannels(1)
.audioFrequency(22050)
.audioCodec("libmp3lame");
// .audioFilters(["volume=0.25"])
}),
{
name: "music",
fileCache,
}
)
)
.pipe(gulp.dest(path.join(builtSoundsDir, "music")));
});
// Encodes the ui sounds
gulp.task("sounds.encodeUi", () => {
return gulp
.src([path.join(soundsDir, "ui", "**", "*.wav"), path.join(soundsDir, "ui", "**", "*.mp3")])
.pipe(
$.cache(
$.fluentFfmpeg("mp3", function (cmd) {
return cmd
.audioBitrate(128)
.audioChannels(1)
.audioFrequency(22050)
.audioCodec("libmp3lame")
.audioFilters(filters);
})
),
{
name: "uisounds",
fileCache,
}
)
.pipe(gulp.dest(path.join(builtSoundsDir, "ui")));
});
// Encodes the game sounds
gulp.task("sounds.encodeGame", () => {
return gulp
.src([path.join(soundsDir, "game", "**", "*.wav"), path.join(soundsDir, "game", "**", "*.mp3")])
.pipe(
$.cache(
$.fluentFfmpeg("mp3", function (cmd) {
return cmd
.audioBitrate(128)
.audioChannels(1)
.audioFrequency(22050)
.audioCodec("libmp3lame")
.audioFilters(filters);
}),
{
nane: "gamesounds",
fileCache,
}
)
)
.pipe(gulp.dest(path.join(builtSoundsDir, "game")));
});
gulp.task("sounds.copy", () => {
return gulp
.src(path.join(builtSoundsDir, "**", "*.mp3"))
.pipe($.cached("sounds.copy"))
.pipe(gulp.dest(path.join(buildFolder, "res", "sounds")));
});
gulp.task("sounds.buildall", cb =>
$.multiProcess(["sounds.encodeMusic", "sounds.encodeUi", "sounds.encodeGame"], cb, true)
);
gulp.task("sounds.fullbuild", cb => $.sequence("sounds.clear", "sounds.buildall", "sounds.copy")(cb));
gulp.task("sounds.dev", cb => {
return $.sequence("sounds.buildall", "sounds.copy")(cb);
});
}
module.exports = {
gulptasksSounds,
};

215
gulp/standalone.js Normal file
View File

@ -0,0 +1,215 @@
const packager = require("electron-packager");
const path = require("path");
const buildutils = require("./buildutils");
const fs = require("fs");
const fse = require("fs-extra");
const execSync = require("child_process").execSync;
function gulptasksStandalone($, gulp, buildFolder) {
const electronBaseDir = path.join("../electron");
const tempDestDir = path.join("..", "tmp_standalone_files");
const tempDestBuildDir = path.join(tempDestDir, "built");
gulp.task("standalone.prepare.cleanup", () => {
return gulp.src(tempDestDir, { read: false }).pipe($.clean({ force: true }));
});
gulp.task("standalone.prepare.copyPrefab", () => {
// const requiredFiles = $.glob.sync("../electron/");
const requiredFiles = [
path.join(electronBaseDir, "lib", "**", "*.node"),
path.join(electronBaseDir, "node_modules", "**", "*.*"),
path.join(electronBaseDir, "node_modules", "**", ".*"),
path.join(electronBaseDir, "node_modules", "**", "*"),
path.join(electronBaseDir, "favicon*"),
];
return gulp.src(requiredFiles, { base: electronBaseDir }).pipe(gulp.dest(tempDestBuildDir));
});
gulp.task("standalone.prepare.writePackageJson", () => {
fs.writeFileSync(
path.join(tempDestBuildDir, "package.json"),
JSON.stringify(
{
devDependencies: {
electron: "6.0.10",
},
},
null,
4
)
);
});
gulp.task("standalone.prepare.minifyCode", () => {
return gulp
.src(path.join(electronBaseDir, "*.js"))
.pipe(
$.terser({
ecma: 6,
parse: {},
module: false,
toplevel: true,
keep_classnames: false,
keep_fnames: false,
safari10: false,
compress: {
arguments: false, // breaks
drop_console: false,
// keep_fargs: false,
keep_infinity: true,
passes: 2,
module: false,
toplevel: true,
unsafe_math: true,
unsafe_arrows: false,
warnings: true,
},
mangle: {
eval: true,
keep_classnames: false,
keep_fnames: false,
module: false,
toplevel: true,
safari10: false,
},
output: {
comments: false,
ascii_only: true,
beautify: false,
braces: false,
ecma: 6,
},
})
)
.pipe(gulp.dest(tempDestBuildDir));
});
gulp.task("standalone.prepare.copyGamefiles", () => {
return gulp.src("../../www/**/*.*", { base: "../../www" }).pipe(gulp.dest(tempDestBuildDir));
});
gulp.task("standalone.killRunningInstances", () => {
try {
execSync("taskkill /F /IM shapezio.exe");
} catch (ex) {
console.warn("Failed to kill running instances, maybe none are up.");
}
});
gulp.task(
"standalone.prepare",
$.sequence(
"standalone.killRunningInstances",
"standalone.prepare.cleanup",
"standalone.prepare.copyPrefab",
"standalone.prepare.writePackageJson",
"standalone.prepare.minifyCode",
"standalone.prepare.copyGamefiles"
)
);
/**
*
* @param {'win32'|'linux'|'darwin'} platform
* @param {'x64'|'ia32'} arch
* @param {function():void} cb
* @param {boolean=} isRelease
*/
function packageStandalone(platform, arch, cb, isRelease = false) {
const libDirName = (platform === "win32" ? "win" : platform) + (arch === "x64" ? "64" : "32");
const libDir = path.join(electronBaseDir, "lib", libDirName);
if (!fs.existsSync(libDir)) {
console.error("FATAL ERROR: LIB DIR does not exist:", libDir);
cb();
return;
}
packager({
dir: tempDestBuildDir,
appCopyright: "Tobias Springer IT Solutions",
appVersion: buildutils.getVersion(),
buildVersion: "1.0.0",
arch,
platform,
asar: true,
executableName: "shapezio",
icon: path.join(electronBaseDir, "favicon"),
name: "Shapez.io Standalone",
out: tempDestDir,
overwrite: true,
appBundleId: "io.shapez.standalone",
appCategoryType: "public.app-category.games",
}).then(
appPaths => {
console.log("Packages created:", appPaths);
appPaths.forEach(appPath => {
if (!fs.existsSync(appPath)) {
console.error("Bad app path gotten:", appPath);
return;
}
console.log("Copying lib files to", appPath);
const libFiles = $.glob.sync(path.join("**", "*.+(dylib|so|dll|lib)"), { cwd: libDir });
libFiles.forEach(f => {
console.log(" -> Copying", f);
fs.copyFileSync(path.join(libDir, f), path.join(appPath, f));
});
const playablePath = appPath + "_PLAYABLE";
fse.copySync(appPath, playablePath);
fs.writeFileSync(path.join(playablePath, "steam_appid.txt"), "1134480");
fs.writeFileSync(
path.join(playablePath, "play.bat"),
"start shapezio --dev --disable-direct-composition --in-process-gpu\r\n"
);
fs.writeFileSync(
path.join(playablePath, "play_local.bat"),
"start shapezio --local --dev --disable-direct-composition --in-process-gpu\r\n"
);
});
cb();
},
err => {
console.error("Packaging error:", err);
cb();
}
);
}
// gulp.task("standalone.package.beta.win64", (cb) => packageStandalone("win32", "x64", cb));
// gulp.task("standalone.package.beta.win32", (cb) => packageStandalone("win32", "ia32", cb));
// gulp.task("standalone.package.beta.linux64", (cb) => packageStandalone("linux", "x64", cb));
// gulp.task("standalone.package.beta.linux32", (cb) => packageStandalone("linux", "ia32", cb));
// gulp.task("standalone.package.beta.darwin64", (cb) => packageStandalone("darwin", "x64", cb));
// gulp.task("standalone.package.beta", $.sequence("standalone.prepare", [
// "standalone.package.beta.win64",
// "standalone.package.beta.win32",
// "standalone.package.beta.linux64",
// "standalone.package.beta.linux32",
// "standalone.package.beta.darwin64"
// ]));
gulp.task("standalone.package.prod.win64", cb => packageStandalone("win32", "x64", cb, true));
gulp.task("standalone.package.prod.win32", cb => packageStandalone("win32", "ia32", cb, true));
gulp.task("standalone.package.prod.linux64", cb => packageStandalone("linux", "x64", cb, true));
gulp.task("standalone.package.prod.linux32", cb => packageStandalone("linux", "ia32", cb, true));
gulp.task("standalone.package.prod.darwin64", cb => packageStandalone("darwin", "x64", cb, true));
gulp.task(
"standalone.package.prod",
$.sequence("standalone.prepare", [
"standalone.package.prod.win64",
// "standalone.package.prod.win32",
// "standalone.package.prod.linux64",
// "standalone.package.prod.linux32",
// "standalone.package.prod.darwin64"
])
);
}
module.exports = { gulptasksStandalone };

59
gulp/tsconfig.json Normal file
View File

@ -0,0 +1,59 @@
{
"compilerOptions": {
/* Basic Options */
"target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
// "lib": [], /* Specify library files to be included in the compilation. */
"allowJs": true /* Allow javascript files to be compiled. */,
"checkJs": true /* Report errors in .js files. */,
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./typedefs_gen", /* Concatenate and emit output to single file. */
// "outDir": "./typedefs_gen", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "incremental": true, /* Enable incremental compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true /* Do not emit outputs. */,
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
// "strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
"strictFunctionTypes": true /* Enable strict checking of function types. */,
"strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */,
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
"noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */,
"alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
/* Module Resolution Options */
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
"resolveJsonModule": true
},
"exclude": ["backend/shared/gameserver_base_impl"]
}

116
gulp/webpack.config.js Normal file
View File

@ -0,0 +1,116 @@
const path = require("path");
const webpack = require("webpack");
const utils = require("./buildutils");
const lzString = require("lz-string");
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const CircularDependencyPlugin = require("circular-dependency-plugin");
module.exports = ({ watch = false, standalone = false }) => {
return {
mode: "development",
devtool: "cheap-source-map",
entry: {
"bundle.js": [path.resolve(__dirname, "../src/js/main.js")],
},
watch,
node: {
fs: "empty",
},
resolve: {
alias: {
"global-compression": path.resolve(__dirname, "..", "src", "js", "core", "lzstring.js"),
},
},
context: path.resolve(__dirname, ".."),
plugins: [
new webpack.DefinePlugin({
assert: "window.assert",
assertAlways: "window.assert",
abstract:
"window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));",
G_HAVE_ASSERT: "true",
G_APP_ENVIRONMENT: JSON.stringify("dev"),
G_API_ENDPOINT: JSON.stringify(
lzString.compressToEncodedURIComponent("http://localhost:5005/v1")
),
G_TRACKING_ENDPOINT: JSON.stringify(
lzString.compressToEncodedURIComponent("http://localhost:10005/v1")
),
G_IS_DEV: "true",
G_IS_PROD: "false",
G_IS_RELEASE: "false",
G_IS_MOBILE_APP: "false",
G_IS_BROWSER: "true",
G_IS_STANDALONE: standalone ? "true" : "false",
G_BUILD_TIME: "" + new Date().getTime(),
G_BUILD_COMMIT_HASH: JSON.stringify(utils.getRevision()),
G_BUILD_VERSION: JSON.stringify(utils.getVersion()),
G_ALL_UI_IMAGES: JSON.stringify(utils.getAllResourceImages()),
}),
new CircularDependencyPlugin({
// exclude detection of files based on a RegExp
exclude: /node_modules/,
// add errors to webpack instead of warnings
failOnError: true,
// allow import cycles that include an asyncronous import,
// e.g. via import(/* webpackMode: "weak" */ './file.js')
allowAsyncCycles: false,
// set the current working directory for displaying module paths
cwd: path.join(__dirname, "..", "src", "js"),
}),
// new BundleAnalyzerPlugin()
],
module: {
rules: [
{
test: /\.json$/,
enforce: "pre",
use: ["./gulp/loader.compressjson"],
type: "javascript/auto",
},
{ test: /\.(png|jpe?g|svg)$/, loader: "ignore-loader" },
{
test: /\.md$/,
use: [
{
loader: "html-loader",
},
"markdown-loader",
],
},
{
test: /\.js$/,
enforce: "pre",
exclude: /node_modules/,
use: [
{
loader: "webpack-strip-block",
options: {
start: "typehints:start",
end: "typehints:end",
},
},
],
},
{
test: /\.worker\.js$/,
use: {
loader: "worker-loader",
options: {
fallback: false,
inline: true,
},
},
},
],
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "..", "build"),
},
};
};

View File

@ -0,0 +1,258 @@
const path = require("path");
const webpack = require("webpack");
const utils = require("./buildutils");
const lzString = require("lz-string");
const TerserPlugin = require("terser-webpack-plugin");
const StringReplacePlugin = require("string-replace-webpack-plugin");
// const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const UnusedFilesPlugin = require("unused-files-webpack-plugin").UnusedFilesWebpackPlugin;
// const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
module.exports = ({
enableAssert = false,
apiEndpoint,
environment,
es6 = false,
standalone = false,
isBrowser = true,
mobileApp = false,
}) => {
const globalDefs = {
assert: enableAssert ? "window.assert" : "false && window.assert",
assertAlways: "window.assert",
abstract: "window.assert(false, 'abstract method called');",
G_IS_DEV: "false",
G_IS_PROD: "true",
G_IS_RELEASE: environment === "prod" ? "true" : "false",
G_IS_STANDALONE: standalone ? "true" : "false",
G_IS_BROWSER: isBrowser ? "true" : "false",
G_IS_MOBILE_APP: mobileApp ? "true" : "false",
G_API_ENDPOINT: JSON.stringify(lzString.compressToEncodedURIComponent(apiEndpoint)),
G_TRACKING_ENDPOINT: JSON.stringify(
lzString.compressToEncodedURIComponent("https://tracking.shapez.io/v1")
),
G_APP_ENVIRONMENT: JSON.stringify(environment),
G_HAVE_ASSERT: enableAssert ? "true" : "false",
G_BUILD_TIME: "" + new Date().getTime(),
G_BUILD_COMMIT_HASH: JSON.stringify(utils.getRevision()),
G_BUILD_VERSION: JSON.stringify(utils.getVersion()),
G_ALL_UI_IMAGES: JSON.stringify(utils.getAllResourceImages()),
};
return {
mode: "production",
devtool: false,
entry: {
"bundle.js": [path.resolve(__dirname, "..", "src", "js", "main.js")],
},
node: {
fs: "empty",
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "..", "build"),
},
context: path.resolve(__dirname, ".."),
stats: {
// Examine all modules
maxModules: Infinity,
// Display bailout reasons
optimizationBailout: true,
},
resolve: {
alias: {
"global-compression": path.resolve(__dirname, "..", "src", "js", "core", "lzstring.js"),
},
},
optimization: {
minimize: true,
// namedModules: true,
noEmitOnErrors: true,
removeAvailableModules: true,
removeEmptyChunks: true,
mergeDuplicateChunks: true,
flagIncludedChunks: true,
occurrenceOrder: true,
providedExports: true,
usedExports: true,
concatenateModules: true,
sideEffects: true,
minimizer: [
new TerserPlugin({
parallel: true,
sourceMap: false,
cache: false,
terserOptions: {
ecma: es6 ? 6 : 5,
parse: {},
module: true,
toplevel: true,
keep_classnames: false,
keep_fnames: false,
keep_fargs: false,
safari10: true,
compress: {
arguments: false, // breaks
drop_console: false,
global_defs: globalDefs,
keep_fargs: false,
keep_infinity: true,
passes: 2,
module: true,
pure_funcs: [
"Math.round",
"Math.ceil",
"Math.floor",
"Math.sqrt",
"Math.hypot",
"Math.abs",
"Math.max",
"Math.min",
"Math.sin",
"Math.cos",
"Math.tan",
"Math.sign",
"Math.pow",
"Math.atan2",
"Math_round",
"Math_ceil",
"Math_floor",
"Math_sqrt",
"Math_hypot",
"Math_abs",
"Math_max",
"Math_min",
"Math_sin",
"Math_cos",
"Math_tan",
"Math_sign",
"Math_pow",
"Math_atan2",
],
toplevel: true,
unsafe_math: true,
unsafe_arrows: false,
warnings: true,
},
mangle: {
eval: true,
keep_classnames: false,
keep_fnames: false,
module: true,
toplevel: true,
safari10: true,
},
output: {
comments: false,
ascii_only: true,
beautify: false,
braces: false,
ecma: es6 ? 6 : 5,
preamble:
"/* Shapez.io Codebase - Copyright 2020 Tobias Springer - " +
utils.getVersion() +
" @ " +
utils.getRevision() +
" */",
},
},
}),
],
},
performance: {
maxEntrypointSize: 5120000,
maxAssetSize: 5120000,
},
plugins: [
new webpack.DefinePlugin(globalDefs),
new UnusedFilesPlugin({
failOnUnused: false,
cwd: path.join(__dirname, "..", "src", "js"),
patterns: ["../src/js/**/*.js"],
}),
// new ReplaceCompressBlocks()
// new webpack.optimize.ModuleConcatenationPlugin()
// new WebpackDeepScopeAnalysisPlugin()
// new BundleAnalyzerPlugin()
],
module: {
rules: [
{
test: /\.json$/,
enforce: "pre",
use: ["./gulp/loader.compressjson"],
type: "javascript/auto",
},
{ test: /\.(png|jpe?g|svg)$/, loader: "ignore-loader" },
{
test: /\.js$/,
enforce: "pre",
exclude: /node_modules/,
use: [
{
loader: "webpack-strip-block",
options: {
start: "typehints:start",
end: "typehints:end",
},
},
{
loader: "webpack-strip-block",
options: {
start: "dev:start",
end: "dev:end",
},
},
],
},
{
test: /\.js$/,
use: [
// "thread-loader",
{
loader: "babel-loader?cacheDirectory",
options: {
configFile: require.resolve(
es6 ? "./babel-es6.config.js" : "./babel.config.js"
),
},
},
"uglify-template-string-loader", // Finally found this plugin
StringReplacePlugin.replace({
replacements: [
{ pattern: /globalConfig\.tileSize/g, replacement: () => "32" },
{ pattern: /globalConfig\.halfTileSize/g, replacement: () => "16" },
{
pattern: /globalConfig\.beltSpeedItemsPerSecond/g,
replacement: () => "1.0",
},
{ pattern: /globalConfig\.itemSpacingOnBelts/g, replacement: () => "0.8" },
{ pattern: /globalConfig\.debug/g, replacement: () => "''" },
],
}),
],
},
{
test: /\.worker\.js$/,
use: {
loader: "worker-loader",
options: {
fallback: false,
inline: true,
},
},
},
{
test: /\.md$/,
use: ["html-loader", "markdown-loader"],
},
],
},
};
};

14195
gulp/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

7
jsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
"compilerOptions": {
"target": "es6",
"checkJs": true,
}
}

84
package.json Normal file
View File

@ -0,0 +1,84 @@
{
"name": "shapez.io",
"version": "1.0.0",
"main": "index.js",
"repository": "https://github.com/tobspr/shapez.io",
"author": "Tobias Springer <tobias.springer1@gmail.com>",
"license": "MIT",
"private": true,
"scripts": {
"dev": "./gulp/gulp main.serveDev",
"prettier-all": "prettier --write src/**/*.* && prettier --write gulp/**/*.*"
},
"dependencies": {
"@babel/core": "^7.5.4",
"@babel/plugin-transform-block-scoping": "^7.4.4",
"@babel/plugin-transform-classes": "^7.5.5",
"@babel/preset-env": "^7.5.4",
"@types/cordova": "^0.0.34",
"@types/filesystem": "^0.0.29",
"ajv": "^6.10.2",
"babel-loader": "^8.0.4",
"browser-sync": "^2.24.6",
"circular-dependency-plugin": "^5.0.2",
"circular-json": "^0.5.9",
"clipboard-copy": "^3.1.0",
"colors": "^1.3.3",
"core-js": "3",
"cssnano-preset-advanced": "^4.0.7",
"email-validator": "^2.0.4",
"eslint": "^5.9.0",
"fastdom": "^1.0.8",
"flatted": "^2.0.1",
"howler": "^2.1.2",
"html-loader": "^0.5.5",
"ignore-loader": "^0.1.2",
"lz-string": "^1.4.4",
"markdown-loader": "^4.0.0",
"obfuscator-loader": "^1.1.2",
"phonegap-plugin-mobile-accessibility": "^1.0.5",
"promise-polyfill": "^8.1.0",
"query-string": "^6.8.1",
"rusha": "^0.8.13",
"serialize-error": "^3.0.0",
"sloc": "^0.2.1",
"strictdom": "^1.0.1",
"string-replace-webpack-plugin": "^0.1.3",
"terser-webpack-plugin": "^1.1.0",
"uglify-template-string-loader": "^1.1.0",
"unused-files-webpack-plugin": "^3.4.0",
"webpack": "^4.31.0",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.0",
"webpack-deep-scope-plugin": "^1.6.0",
"webpack-plugin-replace": "^1.1.1",
"webpack-strip-block": "^0.2.0",
"whatwg-fetch": "^3.0.0",
"worker-loader": "^2.0.0"
},
"devDependencies": {
"autoprefixer": "^9.4.3",
"babel-plugin-closure-elimination": "^1.3.0",
"babel-plugin-console-source": "^2.0.2",
"babel-plugin-danger-remove-unused-import": "^1.1.2",
"css-mqpacker": "^7.0.0",
"cssnano": "^4.1.10",
"faster.js": "^1.1.0",
"glob": "^7.1.3",
"imagemin-mozjpeg": "^8.0.0",
"imagemin-pngquant": "^8.0.0",
"jimp": "^0.6.1",
"js-yaml": "^3.13.1",
"onesky-fetch": "^0.0.7",
"postcss-assets": "^5.0.0",
"postcss-preset-env": "^6.5.0",
"postcss-round-subpixels": "^1.2.0",
"postcss-unprefix": "^2.1.3",
"prettier": "^2.0.4",
"sass-unused": "^0.3.0",
"speed-measure-webpack-plugin": "^1.3.1",
"strip-json-comments": "^3.0.1",
"trim": "^0.0.1",
"webpack-stream": "^5.1.0"
}
}

Binary file not shown.

BIN
res/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
res/ui/icons/close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
res/ui/icons/mouse_left.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
res/ui/icons/shop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 B

BIN
res/ui/icons/statistics.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

6
res/ui/loading.svg Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
<circle cx="50" cy="50" fill="none" stroke="#393747" stroke-width="3" r="42" stroke-dasharray="197.92033717615698 67.97344572538566" transform="rotate(48.2654 50 50)">
<animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" dur="5.555555555555555s" values="0 50 50;360 50 50" keyTimes="0;1"></animateTransform>
</circle>
<!-- [ldio] generated by https://loading.io/ --></svg>

After

Width:  |  Height:  |  Size: 688 B

BIN
res/ui/locked_building.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
res/ui/menu_bg.noinline.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

BIN
res/ui/sad_ghost.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
res/ui/upgrades/belt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
res/ui/upgrades/miner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

417
res_raw/atlas.tps Normal file
View File

@ -0,0 +1,417 @@
<?xml version="1.0" encoding="UTF-8"?>
<data version="1.0">
<struct type="Settings">
<key>fileFormatVersion</key>
<int>4</int>
<key>texturePackerVersion</key>
<string>5.3.0</string>
<key>autoSDSettings</key>
<array>
<struct type="AutoSDSettings">
<key>scale</key>
<double>1</double>
<key>extension</key>
<string>_100</string>
<key>spriteFilter</key>
<string></string>
<key>acceptFractionalValues</key>
<false/>
<key>maxTextureSize</key>
<QSize>
<key>width</key>
<int>-1</int>
<key>height</key>
<int>-1</int>
</QSize>
</struct>
<struct type="AutoSDSettings">
<key>scale</key>
<double>0.75</double>
<key>extension</key>
<string>_75</string>
<key>spriteFilter</key>
<string></string>
<key>acceptFractionalValues</key>
<false/>
<key>maxTextureSize</key>
<QSize>
<key>width</key>
<int>-1</int>
<key>height</key>
<int>-1</int>
</QSize>
</struct>
<struct type="AutoSDSettings">
<key>scale</key>
<double>0.5</double>
<key>extension</key>
<string>_50</string>
<key>spriteFilter</key>
<string></string>
<key>acceptFractionalValues</key>
<false/>
<key>maxTextureSize</key>
<QSize>
<key>width</key>
<int>-1</int>
<key>height</key>
<int>-1</int>
</QSize>
</struct>
<struct type="AutoSDSettings">
<key>scale</key>
<double>0.25</double>
<key>extension</key>
<string>_25</string>
<key>spriteFilter</key>
<string></string>
<key>acceptFractionalValues</key>
<false/>
<key>maxTextureSize</key>
<QSize>
<key>width</key>
<int>-1</int>
<key>height</key>
<int>-1</int>
</QSize>
</struct>
<struct type="AutoSDSettings">
<key>scale</key>
<double>0.1</double>
<key>extension</key>
<string>_10</string>
<key>spriteFilter</key>
<string></string>
<key>acceptFractionalValues</key>
<false/>
<key>maxTextureSize</key>
<QSize>
<key>width</key>
<int>-1</int>
<key>height</key>
<int>-1</int>
</QSize>
</struct>
</array>
<key>allowRotation</key>
<false/>
<key>shapeDebug</key>
<false/>
<key>dpi</key>
<uint>72</uint>
<key>dataFormat</key>
<string>json</string>
<key>textureFileName</key>
<filename></filename>
<key>flipPVR</key>
<false/>
<key>pvrCompressionQuality</key>
<enum type="SettingsBase::PvrCompressionQuality">PVR_QUALITY_NORMAL</enum>
<key>atfCompressData</key>
<false/>
<key>mipMapMinSize</key>
<uint>32768</uint>
<key>etc1CompressionQuality</key>
<enum type="SettingsBase::Etc1CompressionQuality">ETC1_QUALITY_LOW_PERCEPTUAL</enum>
<key>etc2CompressionQuality</key>
<enum type="SettingsBase::Etc2CompressionQuality">ETC2_QUALITY_LOW_PERCEPTUAL</enum>
<key>dxtCompressionMode</key>
<enum type="SettingsBase::DxtCompressionMode">DXT_PERCEPTUAL</enum>
<key>jxrColorFormat</key>
<enum type="SettingsBase::JpegXrColorMode">JXR_YUV444</enum>
<key>jxrTrimFlexBits</key>
<uint>0</uint>
<key>jxrCompressionLevel</key>
<uint>0</uint>
<key>ditherType</key>
<enum type="SettingsBase::DitherType">NearestNeighbour</enum>
<key>backgroundColor</key>
<uint>0</uint>
<key>libGdx</key>
<struct type="LibGDX">
<key>filtering</key>
<struct type="LibGDXFiltering">
<key>x</key>
<enum type="LibGDXFiltering::Filtering">Linear</enum>
<key>y</key>
<enum type="LibGDXFiltering::Filtering">Linear</enum>
</struct>
</struct>
<key>shapePadding</key>
<uint>0</uint>
<key>jpgQuality</key>
<uint>80</uint>
<key>pngOptimizationLevel</key>
<uint>0</uint>
<key>webpQualityLevel</key>
<uint>101</uint>
<key>textureSubPath</key>
<string></string>
<key>atfFormats</key>
<string></string>
<key>textureFormat</key>
<enum type="SettingsBase::TextureFormat">png</enum>
<key>borderPadding</key>
<uint>1</uint>
<key>maxTextureSize</key>
<QSize>
<key>width</key>
<int>4096</int>
<key>height</key>
<int>4096</int>
</QSize>
<key>fixedTextureSize</key>
<QSize>
<key>width</key>
<int>-1</int>
<key>height</key>
<int>-1</int>
</QSize>
<key>algorithmSettings</key>
<struct type="AlgorithmSettings">
<key>algorithm</key>
<enum type="AlgorithmSettings::AlgorithmId">MaxRects</enum>
<key>freeSizeMode</key>
<enum type="AlgorithmSettings::AlgorithmFreeSizeMode">Best</enum>
<key>sizeConstraints</key>
<enum type="AlgorithmSettings::SizeConstraints">AnySize</enum>
<key>forceSquared</key>
<false/>
<key>maxRects</key>
<struct type="AlgorithmMaxRectsSettings">
<key>heuristic</key>
<enum type="AlgorithmMaxRectsSettings::Heuristic">Best</enum>
</struct>
<key>basic</key>
<struct type="AlgorithmBasicSettings">
<key>sortBy</key>
<enum type="AlgorithmBasicSettings::SortBy">Best</enum>
<key>order</key>
<enum type="AlgorithmBasicSettings::Order">Ascending</enum>
</struct>
<key>polygon</key>
<struct type="AlgorithmPolygonSettings">
<key>alignToGrid</key>
<uint>1</uint>
</struct>
</struct>
<key>dataFileNames</key>
<map type="GFileNameMap">
<key>data</key>
<struct type="DataFile">
<key>name</key>
<filename>../res_built/atlas/atlas{n}{v}.json</filename>
</struct>
</map>
<key>multiPack</key>
<true/>
<key>forceIdenticalLayout</key>
<false/>
<key>outputFormat</key>
<enum type="SettingsBase::OutputFormat">RGBA8888</enum>
<key>alphaHandling</key>
<enum type="SettingsBase::AlphaHandling">ClearTransparentPixels</enum>
<key>contentProtection</key>
<struct type="ContentProtection">
<key>key</key>
<string></string>
</struct>
<key>autoAliasEnabled</key>
<true/>
<key>trimSpriteNames</key>
<false/>
<key>prependSmartFolderName</key>
<true/>
<key>autodetectAnimations</key>
<true/>
<key>globalSpriteSettings</key>
<struct type="SpriteSettings">
<key>scale</key>
<double>1</double>
<key>scaleMode</key>
<enum type="ScaleMode">Smooth</enum>
<key>extrude</key>
<uint>2</uint>
<key>trimThreshold</key>
<uint>2</uint>
<key>trimMargin</key>
<uint>1</uint>
<key>trimMode</key>
<enum type="SpriteSettings::TrimMode">Trim</enum>
<key>tracerTolerance</key>
<int>200</int>
<key>heuristicMask</key>
<false/>
<key>defaultPivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>writePivotPoints</key>
<false/>
</struct>
<key>individualSpriteSettings</key>
<map type="IndividualSpriteSettingsMap">
<key type="filename">sprites/belt/forward_0.png</key>
<key type="filename">sprites/belt/forward_1.png</key>
<key type="filename">sprites/belt/forward_2.png</key>
<key type="filename">sprites/buildings/miner.png</key>
<key type="filename">sprites/buildings/rotater.png</key>
<key type="filename">sprites/buildings/trash.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>32,32,64,64</rect>
<key>scale9Paddings</key>
<rect>32,32,64,64</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/belt/forward_3.png</key>
<key type="filename">sprites/belt/forward_4.png</key>
<key type="filename">sprites/belt/forward_5.png</key>
<key type="filename">sprites/belt/left_0.png</key>
<key type="filename">sprites/belt/left_1.png</key>
<key type="filename">sprites/belt/left_2.png</key>
<key type="filename">sprites/belt/left_3.png</key>
<key type="filename">sprites/belt/left_4.png</key>
<key type="filename">sprites/belt/left_5.png</key>
<key type="filename">sprites/belt/right_0.png</key>
<key type="filename">sprites/belt/right_1.png</key>
<key type="filename">sprites/belt/right_2.png</key>
<key type="filename">sprites/belt/right_3.png</key>
<key type="filename">sprites/belt/right_4.png</key>
<key type="filename">sprites/belt/right_5.png</key>
<key type="filename">sprites/buildings/belt_left.png</key>
<key type="filename">sprites/buildings/belt_right.png</key>
<key type="filename">sprites/buildings/belt_top.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>32,32,63,63</rect>
<key>scale9Paddings</key>
<rect>32,32,63,63</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/buildings/cutter.png</key>
<key type="filename">sprites/buildings/mixer.png</key>
<key type="filename">sprites/buildings/painter.png</key>
<key type="filename">sprites/buildings/splitter.png</key>
<key type="filename">sprites/buildings/stacker.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>64,32,128,64</rect>
<key>scale9Paddings</key>
<rect>64,32,128,64</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/buildings/hub.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>192,192,384,384</rect>
<key>scale9Paddings</key>
<rect>192,192,384,384</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/buildings/underground_belt_entry.png</key>
<key type="filename">sprites/buildings/underground_belt_exit.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>48,48,96,96</rect>
<key>scale9Paddings</key>
<rect>48,48,96,96</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/debug/acceptor_slot.png</key>
<key type="filename">sprites/debug/ejector_slot.png</key>
<key type="filename">sprites/map_overview/belt_forward.png</key>
<key type="filename">sprites/map_overview/belt_left.png</key>
<key type="filename">sprites/map_overview/belt_right.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>8,8,16,16</rect>
<key>scale9Paddings</key>
<rect>8,8,16,16</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/misc/slot_bad_arrow.png</key>
<key type="filename">sprites/misc/slot_good_arrow.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>24,24,48,48</rect>
<key>scale9Paddings</key>
<rect>24,24,48,48</rect>
<key>scale9FromFile</key>
<false/>
</struct>
</map>
<key>fileList</key>
<array>
<filename>sprites</filename>
</array>
<key>ignoreFileList</key>
<array/>
<key>replaceList</key>
<array/>
<key>ignoredWarnings</key>
<array/>
<key>commonDivisorX</key>
<uint>1</uint>
<key>commonDivisorY</key>
<uint>1</uint>
<key>packNormalMaps</key>
<false/>
<key>autodetectNormalMaps</key>
<true/>
<key>normalMapFilter</key>
<string></string>
<key>normalMapSuffix</key>
<string></string>
<key>normalMapSheetFileName</key>
<filename></filename>
<key>exporterProperties</key>
<map type="ExporterProperties"/>
</struct>
</data>

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:811a57acb5a16af8079744e048c3809799211f1d7c6e79eaa5247ed852b5d4eb
size 23990546

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6b4cbf863591aba38d1729fb1cbaa792323f045722279f0deca2492acaaf19b5
size 123844300

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2b164ef5d1942b30480c202583266707cd52443cdd6fccc5304d941a4aa93c97
size 17420

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:59eea912cf8d9c6668ec89b3fbfc3cb8c49221aad43449c62d9e30486550cb2f
size 169258

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f4c56728534d030fca2d75f297027a2774d6f765f5be84a27a3705569f878f2
size 20158

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:88c66825955990b1b93197c2b943d010b3190b242991db7e4c10f6db8a688bb7
size 59222

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:250247888bb15badf626dbcb6c76daddac5bb592075d4b6fcccc4930dc3a1465
size 16924

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1c119251f4a7511f5d95c4b9eb208062ebb59eaafe535a8b352fcf7fbb5b409c
size 22178

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

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