1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-13 13:04:03 +00:00

Merge branch 'master' into master

This commit is contained in:
Roland Szabó 2020-08-07 00:06:38 +02:00 committed by GitHub
commit 9a2044dbd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 545 additions and 1152 deletions

View File

@ -14,13 +14,9 @@ import { AdProviderInterface } from "./platform/ad_provider";
import { NoAdProvider } from "./platform/ad_providers/no_ad_provider"; import { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
import { AnalyticsInterface } from "./platform/analytics"; import { AnalyticsInterface } from "./platform/analytics";
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics"; import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
import { NoGameAnalytics } from "./platform/browser/no_game_analytics";
import { SoundImplBrowser } from "./platform/browser/sound"; import { SoundImplBrowser } from "./platform/browser/sound";
import { PlatformWrapperImplBrowser } from "./platform/browser/wrapper"; import { PlatformWrapperImplBrowser } from "./platform/browser/wrapper";
import { PlatformWrapperImplElectron } from "./platform/electron/wrapper"; import { PlatformWrapperImplElectron } from "./platform/electron/wrapper";
import { GameAnalyticsInterface } from "./platform/game_analytics";
import { SoundInterface } from "./platform/sound";
import { StorageInterface } from "./platform/storage";
import { PlatformWrapperInterface } from "./platform/wrapper"; import { PlatformWrapperInterface } from "./platform/wrapper";
import { ApplicationSettings } from "./profile/application_settings"; import { ApplicationSettings } from "./profile/application_settings";
import { SavegameManager } from "./savegame/savegame_manager"; import { SavegameManager } from "./savegame/savegame_manager";
@ -34,6 +30,12 @@ import { PreloadState } from "./states/preload";
import { SettingsState } from "./states/settings"; import { SettingsState } from "./states/settings";
import { ShapezGameAnalytics } from "./platform/browser/game_analytics"; import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
/**
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
* @typedef {import("./platform/sound").SoundInterface} SoundInterface
* @typedef {import("./platform/storage").StorageInterface} StorageInterface
*/
const logger = createLogger("application"); const logger = createLogger("application");
// Set the name of the hidden property and the change event for visibility // Set the name of the hidden property and the change event for visibility

View File

@ -1,20 +1,38 @@
/** /**
* @typedef {{ w: number, h: number }} Size
* @typedef {{ x: number, y: number }} Position
* @typedef {{ * @typedef {{
* frame: { x: number, y: number, w: number, h: number }, * frame: Position & Size,
* rotated: false, * rotated: boolean,
* spriteSourceSize: { x: number, y: number, w: number, h: number }, * spriteSourceSize: Position & Size,
* sourceSize: { w: number, h: number}, * sourceSize: Size,
* trimmed: true * trimmed: boolean
* }} SpriteDefinition * }} SpriteDefinition
*
* @typedef {{
* app: string,
* version: string,
* image: string,
* format: string,
* size: Size,
* scale: string,
* smartupdate: string
* }} AtlasMeta
*
* @typedef {{
* frames: Object.<string, SpriteDefinition>,
* meta: AtlasMeta
* }} SourceData
*/ */
export class AtlasDefinition { export class AtlasDefinition {
constructor(sourceData) { /**
this.sourceFileName = sourceData.meta.image; * @param {SourceData} sourceData
this.meta = sourceData.meta; */
constructor({ frames, meta }) {
/** @type {Object.<string, SpriteDefinition>} */ this.meta = meta;
this.sourceData = sourceData.frames; this.sourceData = frames;
this.sourceFileName = meta.image;
} }
getFullSourcePath() { getFullSourcePath() {
@ -22,6 +40,7 @@ export class AtlasDefinition {
} }
} }
/** @type {AtlasDefinition[]} **/
export const atlasFiles = require export const atlasFiles = require
// @ts-ignore // @ts-ignore
.context("../../../res_built/atlas/", false, /.*\.json/i) .context("../../../res_built/atlas/", false, /.*\.json/i)

View File

@ -115,7 +115,6 @@ export class BackgroundResourcesLoader {
}) })
.then(() => { .then(() => {
logger.log("⏰ Finish load: bare game"); logger.log("⏰ Finish load: bare game");
Loader.createAtlasLinks();
this.bareGameReady = true; this.bareGameReady = true;
initBuildingCodesAfterResourcesLoaded(); initBuildingCodesAfterResourcesLoaded();
this.signalBareGameLoaded.dispatch(); this.signalBareGameLoaded.dispatch();

View File

@ -1,9 +1,9 @@
import { Rectangle } from "./rectangle";
import { globalConfig } from "./config"; import { globalConfig } from "./config";
/* typehints:start */ /**
import { GameRoot } from "../game/root"; * @typedef {import("../game/root").GameRoot} GameRoot
/* typehints:end */ * @typedef {import("./rectangle").Rectangle} Rectangle
*/
export class DrawParameters { export class DrawParameters {
constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) { constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) {

View File

@ -1,18 +1,13 @@
/* typehints:start */ /**
import { AtlasSprite } from "./sprites"; * @typedef {import("./sprites").AtlasSprite} AtlasSprite
import { DrawParameters } from "./draw_parameters"; * @typedef {import("./draw_parameters").DrawParameters} DrawParameters
/* typehints:end */ */
import { Vector } from "./vector";
import { Rectangle } from "./rectangle";
import { createLogger } from "./logging";
const logger = createLogger("draw_utils");
export function initDrawUtils() { export function initDrawUtils() {
CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) { CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) {
if (r < 0.05) {
this.beginPath(); this.beginPath();
if (r < 0.05) {
this.rect(x, y, w, h); this.rect(x, y, w, h);
return; return;
} }
@ -20,25 +15,26 @@ export function initDrawUtils() {
if (w < 2 * r) { if (w < 2 * r) {
r = w / 2; r = w / 2;
} }
if (h < 2 * r) { if (h < 2 * r) {
r = h / 2; r = h / 2;
} }
this.beginPath();
this.moveTo(x + r, y); this.moveTo(x + r, y);
this.arcTo(x + w, y, x + w, y + h, r); this.arcTo(x + w, y, x + w, y + h, r);
this.arcTo(x + w, y + h, x, y + h, r); this.arcTo(x + w, y + h, x, y + h, r);
this.arcTo(x, y + h, x, y, r); this.arcTo(x, y + h, x, y, r);
this.arcTo(x, y, x + w, y, r); this.arcTo(x, y, x + w, y, r);
// this.closePath();
}; };
CanvasRenderingContext2D.prototype.beginCircle = function (x, y, r) { CanvasRenderingContext2D.prototype.beginCircle = function (x, y, r) {
if (r < 0.05) {
this.beginPath(); this.beginPath();
if (r < 0.05) {
this.rect(x, y, 1, 1); this.rect(x, y, 1, 1);
return; return;
} }
this.beginPath();
this.arc(x, y, r, 0, 2.0 * Math.PI); this.arc(x, y, r, 0, 2.0 * Math.PI);
}; };
} }
@ -62,259 +58,3 @@ export function drawRotatedSprite({ parameters, sprite, x, y, angle, size, offse
parameters.context.rotate(-angle); parameters.context.rotate(-angle);
parameters.context.translate(-x, -y); parameters.context.translate(-x, -y);
} }
export function drawLineFast(context, { x1, x2, y1, y2, color = null, lineSize = 1 }) {
const dX = x2 - x1;
const dY = y2 - y1;
const angle = Math.atan2(dY, dX) + 0.0 * Math.PI;
const len = Math.hypot(dX, dY);
context.translate(x1, y1);
context.rotate(angle);
if (color) {
context.fillStyle = color;
}
context.fillRect(0, -lineSize / 2, len, lineSize);
context.rotate(-angle);
context.translate(-x1, -y1);
}
const INSIDE = 0;
const LEFT = 1;
const RIGHT = 2;
const BOTTOM = 4;
const TOP = 8;
// https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
function computeOutCode(x, y, xmin, xmax, ymin, ymax) {
let code = INSIDE;
if (x < xmin)
// to the left of clip window
code |= LEFT;
else if (x > xmax)
// to the right of clip window
code |= RIGHT;
if (y < ymin)
// below the clip window
code |= BOTTOM;
else if (y > ymax)
// above the clip window
code |= TOP;
return code;
}
// CohenSutherland clipping algorithm clips a line from
// P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with
// diagonal from (xmin, ymin) to (xmax, ymax).
/**
*
* @param {CanvasRenderingContext2D} context
*/
export function drawLineFastClipped(context, rect, { x0, y0, x1, y1, color = null, lineSize = 1 }) {
const xmin = rect.x;
const ymin = rect.y;
const xmax = rect.right();
const ymax = rect.bottom();
// compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
let outcode0 = computeOutCode(x0, y0, xmin, xmax, ymin, ymax);
let outcode1 = computeOutCode(x1, y1, xmin, xmax, ymin, ymax);
let accept = false;
// eslint-disable-next-line no-constant-condition
while (true) {
if (!(outcode0 | outcode1)) {
// bitwise OR is 0: both points inside window; trivially accept and exit loop
accept = true;
break;
} else if (outcode0 & outcode1) {
// bitwise AND is not 0: both points share an outside zone (LEFT, RIGHT, TOP,
// or BOTTOM), so both must be outside window; exit loop (accept is false)
break;
} else {
// failed both tests, so calculate the line segment to clip
// from an outside point to an intersection with clip edge
let x, y;
// At least one endpoint is outside the clip rectangle; pick it.
let outcodeOut = outcode0 ? outcode0 : outcode1;
// Now find the intersection point;
// use formulas:
// slope = (y1 - y0) / (x1 - x0)
// x = x0 + (1 / slope) * (ym - y0), where ym is ymin or ymax
// y = y0 + slope * (xm - x0), where xm is xmin or xmax
// No need to worry about divide-by-zero because, in each case, the
// outcode bit being tested guarantees the denominator is non-zero
if (outcodeOut & TOP) {
// point is above the clip window
x = x0 + ((x1 - x0) * (ymax - y0)) / (y1 - y0);
y = ymax;
} else if (outcodeOut & BOTTOM) {
// point is below the clip window
x = x0 + ((x1 - x0) * (ymin - y0)) / (y1 - y0);
y = ymin;
} else if (outcodeOut & RIGHT) {
// point is to the right of clip window
y = y0 + ((y1 - y0) * (xmax - x0)) / (x1 - x0);
x = xmax;
} else if (outcodeOut & LEFT) {
// point is to the left of clip window
y = y0 + ((y1 - y0) * (xmin - x0)) / (x1 - x0);
x = xmin;
}
// Now we move outside point to intersection point to clip
// and get ready for next pass.
if (outcodeOut == outcode0) {
x0 = x;
y0 = y;
outcode0 = computeOutCode(x0, y0, xmin, xmax, ymin, ymax);
} else {
x1 = x;
y1 = y;
outcode1 = computeOutCode(x1, y1, xmin, xmax, ymin, ymax);
}
}
}
if (accept) {
// Following functions are left for implementation by user based on
// their platform (OpenGL/graphics.h etc.)
// DrawRectangle(xmin, ymin, xmax, ymax);
// LineSegment(x0, y0, x1, y1);
drawLineFast(context, {
x1: x0,
y1: y0,
x2: x1,
y2: y1,
color,
lineSize,
});
}
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param {number} h The hue
* @param {number} s The saturation
* @param {number} l The lightness
* @return {Array} The RGB representation
*/
export function hslToRgb(h, s, l) {
let r;
let g;
let b;
if (s === 0) {
r = g = b = l; // achromatic
} else {
// tslint:disable-next-line:no-shadowed-variable
const hue2rgb = function (p, q, t) {
if (t < 0) {
t += 1;
}
if (t > 1) {
t -= 1;
}
if (t < 1 / 6) {
return p + (q - p) * 6 * t;
}
if (t < 1 / 2) {
return q;
}
if (t < 2 / 3) {
return p + (q - p) * (2 / 3 - t) * 6;
}
return p;
};
let q = l < 0.5 ? l * (1 + s) : l + s - l * s;
let p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
export function wrapText(context, text, x, y, maxWidth, lineHeight, stroke = false) {
var words = text.split(" ");
var line = "";
for (var n = 0; n < words.length; n++) {
var testLine = line + words[n] + " ";
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > maxWidth && n > 0) {
if (stroke) {
context.strokeText(line, x, y);
} else {
context.fillText(line, x, y);
}
line = words[n] + " ";
y += lineHeight;
} else {
line = testLine;
}
}
if (stroke) {
context.strokeText(line, x, y);
} else {
context.fillText(line, x, y);
}
}
/**
* Returns a rotated trapez, used for spotlight culling
* @param {number} x
* @param {number} y
* @param {number} w
* @param {number} h
* @param {number} leftHeight
* @param {number} angle
*/
export function rotateTrapezRightFaced(x, y, w, h, leftHeight, angle) {
const halfY = y + h / 2;
const points = [
new Vector(x, halfY - leftHeight / 2),
new Vector(x + w, y),
new Vector(x, halfY + leftHeight / 2),
new Vector(x + w, y + h),
];
return Rectangle.getAroundPointsRotated(points, angle);
}
/**
* Converts values from 0 .. 255 to values like 07, 7f, 5d etc
* @param {number} value
* @returns {string}
*/
export function mapClampedColorValueToHex(value) {
const hex = "0123456789abcdef";
return hex[Math.floor(value / 16)] + hex[value % 16];
}
/**
* Converts rgb to a hex string
* @param {number} r
* @param {number} g
* @param {number} b
* @returns {string}
*/
export function rgbToHex(r, g, b) {
return mapClampedColorValueToHex(r) + mapClampedColorValueToHex(g) + mapClampedColorValueToHex(b);
}

View File

@ -1,19 +1,19 @@
import { SingletonFactory } from "./singleton_factory"; import { SingletonFactory } from "./singleton_factory";
import { Factory } from "./factory"; import { Factory } from "./factory";
/* typehints:start */ /**
import { BaseGameSpeed } from "../game/time/base_game_speed"; * @typedef {import("../game/time/base_game_speed").BaseGameSpeed} BaseGameSpeed
import { Component } from "../game/component"; * @typedef {import("../game/component").Component} Component
import { BaseItem } from "../game/base_item"; * @typedef {import("../game/base_item").BaseItem} BaseItem
import { MetaBuilding } from "../game/meta_building"; * @typedef {import("../game/meta_building").MetaBuilding} MetaBuilding
/* typehints:end */
// These factories are here to remove circular dependencies // These factories are here to remove circular dependencies
/** @type {SingletonFactoryTemplate<MetaBuilding>} */ /** @type {SingletonFactoryTemplate<MetaBuilding>} */
export let gMetaBuildingRegistry = new SingletonFactory(); export let gMetaBuildingRegistry = new SingletonFactory();
/** @type {Object.<string, Array<typeof MetaBuilding>>} */ /** @type {Object.<string, Array<Class<MetaBuilding>>>} */
export let gBuildingsByCategory = null; export let gBuildingsByCategory = null;
/** @type {FactoryTemplate<Component>} */ /** @type {FactoryTemplate<Component>} */
@ -28,7 +28,7 @@ export let gItemRegistry = new Factory("item");
// Helpers // Helpers
/** /**
* @param {Object.<string, Array<typeof MetaBuilding>>} buildings * @param {Object.<string, Array<Class<MetaBuilding>>>} buildings
*/ */
export function initBuildingsByCategory(buildings) { export function initBuildingsByCategory(buildings) {
gBuildingsByCategory = buildings; gBuildingsByCategory = buildings;

View File

@ -1,20 +1,19 @@
/* typehints:start */
import { Application } from "../application";
/* typehints:end */
import { AtlasDefinition } from "./atlas_definitions";
import { makeOffscreenBuffer } from "./buffer_utils"; import { makeOffscreenBuffer } from "./buffer_utils";
import { AtlasSprite, BaseSprite, RegularSprite, SpriteAtlasLink } from "./sprites"; import { AtlasSprite, BaseSprite, RegularSprite, SpriteAtlasLink } from "./sprites";
import { cachebust } from "./cachebust"; import { cachebust } from "./cachebust";
import { createLogger } from "./logging"; import { createLogger } from "./logging";
/**
* @typedef {import("../application").Application} Application
* @typedef {import("./atlas_definitions").AtlasDefinition} AtlasDefinition;
*/
const logger = createLogger("loader"); const logger = createLogger("loader");
const missingSpriteIds = {}; const missingSpriteIds = {};
class LoaderImpl { class LoaderImpl {
constructor() { constructor() {
/** @type {Application} */
this.app = null; this.app = null;
/** @type {Map<string, BaseSprite>} */ /** @type {Map<string, BaseSprite>} */
@ -23,6 +22,9 @@ class LoaderImpl {
this.rawImages = []; this.rawImages = [];
} }
/**
* @param {Application} app
*/
linkAppAfterBoot(app) { linkAppAfterBoot(app) {
this.app = app; this.app = app;
this.makeSpriteNotFoundCanvas(); this.makeSpriteNotFoundCanvas();
@ -58,7 +60,7 @@ class LoaderImpl {
} }
/** /**
* Retursn a regular sprite from the cache * Returns a regular sprite from the cache
* @param {string} key * @param {string} key
* @returns {RegularSprite} * @returns {RegularSprite}
*/ */
@ -155,44 +157,34 @@ class LoaderImpl {
* @param {AtlasDefinition} atlas * @param {AtlasDefinition} atlas
* @param {HTMLImageElement} loadedImage * @param {HTMLImageElement} loadedImage
*/ */
internalParseAtlas(atlas, loadedImage) { internalParseAtlas({ meta: { scale }, sourceData }, loadedImage) {
this.rawImages.push(loadedImage); this.rawImages.push(loadedImage);
for (const spriteKey in atlas.sourceData) { for (const spriteName in sourceData) {
const spriteData = atlas.sourceData[spriteKey]; const { frame, sourceSize, spriteSourceSize } = sourceData[spriteName];
let sprite = /** @type {AtlasSprite} */ (this.sprites.get(spriteKey)); let sprite = /** @type {AtlasSprite} */ (this.sprites.get(spriteName));
if (!sprite) { if (!sprite) {
sprite = new AtlasSprite({ sprite = new AtlasSprite(spriteName);
spriteName: spriteKey, this.sprites.set(spriteName, sprite);
});
this.sprites.set(spriteKey, sprite);
} }
const link = new SpriteAtlasLink({ const link = new SpriteAtlasLink({
packedX: spriteData.frame.x, packedX: frame.x,
packedY: spriteData.frame.y, packedY: frame.y,
packedW: spriteData.frame.w, packedW: frame.w,
packedH: spriteData.frame.h, packedH: frame.h,
packOffsetX: spriteData.spriteSourceSize.x, packOffsetX: spriteSourceSize.x,
packOffsetY: spriteData.spriteSourceSize.y, packOffsetY: spriteSourceSize.y,
atlas: loadedImage, atlas: loadedImage,
w: spriteData.sourceSize.w, w: sourceSize.w,
h: spriteData.sourceSize.h, h: sourceSize.h,
}); });
sprite.linksByResolution[atlas.meta.scale] = link; sprite.linksByResolution[scale] = link;
} }
} }
/**
* Creates the links for the sprites after the atlas has been loaded. Used so we
* don't have to store duplicate sprites.
*/
createAtlasLinks() {
// NOT USED
}
/** /**
* Makes the canvas which shows the question mark, shown when a sprite was not found * Makes the canvas which shows the question mark, shown when a sprite was not found
*/ */
@ -216,14 +208,9 @@ class LoaderImpl {
// @ts-ignore // @ts-ignore
canvas.src = "not-found"; canvas.src = "not-found";
const resolutions = ["0.1", "0.25", "0.5", "0.75", "1"]; const sprite = new AtlasSprite("not-found");
const sprite = new AtlasSprite({ ["0.1", "0.25", "0.5", "0.75", "1"].forEach(resolution => {
spriteName: "not-found", sprite.linksByResolution[resolution] = new SpriteAtlasLink({
});
for (let i = 0; i < resolutions.length; ++i) {
const res = resolutions[i];
const link = new SpriteAtlasLink({
packedX: 0, packedX: 0,
packedY: 0, packedY: 0,
w: dims, w: dims,
@ -234,8 +221,8 @@ class LoaderImpl {
packedH: dims, packedH: dims,
atlas: canvas, atlas: canvas,
}); });
sprite.linksByResolution[res] = link; });
}
this.spriteNotFoundSprite = sprite; this.spriteNotFoundSprite = sprite;
} }
} }

View File

@ -1,5 +1,5 @@
import { globalConfig } from "./config"; import { globalConfig } from "./config";
import { clamp, epsilonCompare, round2Digits } from "./utils"; import { epsilonCompare, round2Digits } from "./utils";
import { Vector } from "./vector"; import { Vector } from "./vector";
export class Rectangle { export class Rectangle {

View File

@ -1,6 +1,6 @@
import { DrawParameters } from "./draw_parameters"; import { DrawParameters } from "./draw_parameters";
import { Rectangle } from "./rectangle"; import { Rectangle } from "./rectangle";
import { epsilonCompare, round3Digits } from "./utils"; import { round3Digits } from "./utils";
const floorSpriteCoordinates = false; const floorSpriteCoordinates = false;
@ -63,10 +63,9 @@ export class SpriteAtlasLink {
export class AtlasSprite extends BaseSprite { export class AtlasSprite extends BaseSprite {
/** /**
* *
* @param {object} param0 * @param {string} spriteName
* @param {string} param0.spriteName
*/ */
constructor({ spriteName = "sprite" }) { constructor(spriteName = "sprite") {
super(); super();
/** @type {Object.<string, SpriteAtlasLink>} */ /** @type {Object.<string, SpriteAtlasLink>} */
this.linksByResolution = {}; this.linksByResolution = {};
@ -197,8 +196,6 @@ export class AtlasSprite extends BaseSprite {
destH = intersection.h; destH = intersection.h;
} }
// assert(epsilonCompare(scaleW, scaleH), "Sprite should be square for cached rendering");
if (floorSpriteCoordinates) { if (floorSpriteCoordinates) {
parameters.context.drawImage( parameters.context.drawImage(
link.atlas, link.atlas,

View File

@ -1,46 +1,7 @@
import { globalConfig, IS_DEBUG } from "./config";
import { Vector } from "./vector";
import { T } from "../translations"; import { T } from "../translations";
// Constants
export const TOP = new Vector(0, -1);
export const RIGHT = new Vector(1, 0);
export const BOTTOM = new Vector(0, 1);
export const LEFT = new Vector(-1, 0);
export const ALL_DIRECTIONS = [TOP, RIGHT, BOTTOM, LEFT];
const bigNumberSuffixTranslationKeys = ["thousands", "millions", "billions", "trillions"]; const bigNumberSuffixTranslationKeys = ["thousands", "millions", "billions", "trillions"];
/**
* Returns the build id
* @returns {string}
*/
export function getBuildId() {
if (G_IS_DEV && IS_DEBUG) {
return "local-dev";
} else if (G_IS_DEV) {
return "dev-" + getPlatformName() + "-" + G_BUILD_COMMIT_HASH;
} else {
return "prod-" + getPlatformName() + "-" + G_BUILD_COMMIT_HASH;
}
}
/**
* Returns the environment id (dev, prod, etc)
* @returns {string}
*/
export function getEnvironmentId() {
if (G_IS_DEV && IS_DEBUG) {
return "local-dev";
} else if (G_IS_DEV) {
return "dev-" + getPlatformName();
} else if (G_IS_RELEASE) {
return "release-" + getPlatformName();
} else {
return "staging-" + getPlatformName();
}
}
/** /**
* Returns if this platform is android * Returns if this platform is android
* @returns {boolean} * @returns {boolean}
@ -66,7 +27,7 @@ export function isIos() {
/** /**
* Returns a platform name * Returns a platform name
* @returns {string} * @returns {"android" | "browser" | "ios" | "standalone" | "unknown"}
*/ */
export function getPlatformName() { export function getPlatformName() {
if (G_IS_STANDALONE) { if (G_IS_STANDALONE) {
@ -96,60 +57,13 @@ export function getIPCRenderer() {
return ipcRenderer; return ipcRenderer;
} }
/**
* Formats a sensitive token by only displaying the first digits of it. Use for
* stuff like savegame keys etc which should not appear in the log.
* @param {string} key
*/
export function formatSensitive(key) {
if (!key) {
return "<null>";
}
key = key || "";
return "[" + key.substr(0, 8) + "...]";
}
/**
* Creates a new 2D array with the given fill method
* @param {number} w Width
* @param {number} h Height
* @param {(function(number, number) : any) | number | boolean | string | null | undefined} filler Either Fill method, which should return the content for each cell, or static content
* @param {string=} context Optional context for memory tracking
* @returns {Array<Array<any>>}
*/
export function make2DArray(w, h, filler, context = null) {
if (typeof filler === "function") {
const tiles = new Array(w);
for (let x = 0; x < w; ++x) {
const row = new Array(h);
for (let y = 0; y < h; ++y) {
row[y] = filler(x, y);
}
tiles[x] = row;
}
return tiles;
} else {
const tiles = new Array(w);
const row = new Array(h);
for (let y = 0; y < h; ++y) {
row[y] = filler;
}
for (let x = 0; x < w; ++x) {
tiles[x] = row.slice();
}
return tiles;
}
}
/** /**
* Makes a new 2D array with undefined contents * Makes a new 2D array with undefined contents
* @param {number} w * @param {number} w
* @param {number} h * @param {number} h
* @param {string=} context
* @returns {Array<Array<any>>} * @returns {Array<Array<any>>}
*/ */
export function make2DUndefinedArray(w, h, context = null) { export function make2DUndefinedArray(w, h) {
const result = new Array(w); const result = new Array(w);
for (let x = 0; x < w; ++x) { for (let x = 0; x < w; ++x) {
result[x] = new Array(h); result[x] = new Array(h);
@ -157,33 +71,6 @@ export function make2DUndefinedArray(w, h, context = null) {
return result; return result;
} }
/**
* Clears a given 2D array with the given fill method
* @param {Array<Array<any>>} array
* @param {number} w Width
* @param {number} h Height
* @param {(function(number, number) : any) | number | boolean | string | null | undefined} filler Either Fill method, which should return the content for each cell, or static content
*/
export function clear2DArray(array, w, h, filler) {
assert(array.length === w, "Array dims mismatch w");
assert(array[0].length === h, "Array dims mismatch h");
if (typeof filler === "function") {
for (let x = 0; x < w; ++x) {
const row = array[x];
for (let y = 0; y < h; ++y) {
row[y] = filler(x, y);
}
}
} else {
for (let x = 0; x < w; ++x) {
const row = array[x];
for (let y = 0; y < h; ++y) {
row[y] = filler;
}
}
}
}
/** /**
* Creates a new map (an empty object without any props) * Creates a new map (an empty object without any props)
*/ */
@ -215,7 +102,9 @@ export function accessNestedPropertyReverse(obj, keys) {
/** /**
* Chooses a random entry of an array * Chooses a random entry of an array
* @param {Array | string} arr * @template T
* @param {T[]} arr
* @returns {T}
*/ */
export function randomChoice(arr) { export function randomChoice(arr) {
return arr[Math.floor(Math.random() * arr.length)]; return arr[Math.floor(Math.random() * arr.length)];
@ -304,23 +193,6 @@ export function arrayDeleteValue(array, value) {
return arrayDelete(array, index); return arrayDelete(array, index);
} }
// Converts a direction into a 0 .. 7 index
/**
* Converts a direction into a index from 0 .. 7, used for miners, zombies etc which have 8 sprites
* @param {Vector} offset direction
* @param {boolean} inverse if inverse, the direction is reversed
* @returns {number} in range [0, 7]
*/
export function angleToSpriteIndex(offset, inverse = false) {
const twoPi = 2.0 * Math.PI;
const factor = inverse ? -1 : 1;
const offs = inverse ? 2.5 : 3.5;
const angle = (factor * Math.atan2(offset.y, offset.x) + offs * Math.PI) % twoPi;
const index = Math.round((angle / twoPi) * 8) % 8;
return index;
}
/** /**
* Compare two floats for epsilon equality * Compare two floats for epsilon equality
* @param {number} a * @param {number} a
@ -331,15 +203,6 @@ export function epsilonCompare(a, b, epsilon = 1e-5) {
return Math.abs(a - b) < epsilon; return Math.abs(a - b) < epsilon;
} }
/**
* Compare a float for epsilon equal to 0
* @param {number} a
* @returns {boolean}
*/
export function epsilonIsZero(a) {
return epsilonCompare(a, 0);
}
/** /**
* Interpolates two numbers * Interpolates two numbers
* @param {number} a * @param {number} a
@ -399,17 +262,6 @@ export function findNiceIntegerValue(num) {
return Math.ceil(findNiceValue(num)); return Math.ceil(findNiceValue(num));
} }
/**
* Smart rounding + fractional handling
* @param {number} n
*/
function roundSmart(n) {
if (n < 100) {
return n.toFixed(1);
}
return Math.round(n);
}
/** /**
* Formats a big number * Formats a big number
* @param {number} num * @param {number} num
@ -477,92 +329,12 @@ export function formatBigNumberFull(num, divider = T.global.thousandsDivider) {
return out.substring(0, out.length - 1); return out.substring(0, out.length - 1);
} }
/**
* Delayes a promise so that it will resolve after a *minimum* amount of time only
* @param {Promise<any>} promise The promise to delay
* @param {number} minTimeMs The time to make it run at least
* @returns {Promise<any>} The delayed promise
*/
export function artificialDelayedPromise(promise, minTimeMs = 500) {
if (G_IS_DEV && globalConfig.debug.noArtificialDelays) {
return promise;
}
const startTime = performance.now();
return promise.then(
result => {
const timeTaken = performance.now() - startTime;
const waitTime = Math.floor(minTimeMs - timeTaken);
if (waitTime > 0) {
return new Promise(resolve => {
setTimeout(() => {
resolve(result);
}, waitTime);
});
} else {
return result;
}
},
error => {
const timeTaken = performance.now() - startTime;
const waitTime = Math.floor(minTimeMs - timeTaken);
if (waitTime > 0) {
// @ts-ignore
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(error);
}, waitTime);
});
} else {
throw error;
}
}
);
}
/**
* Computes a sine-based animation which pulsates from 0 .. 1 .. 0
* @param {number} time Current time in seconds
* @param {number} duration Duration of the full pulse in seconds
* @param {number} seed Seed to offset the animation
*/
export function pulseAnimation(time, duration = 1.0, seed = 0.0) {
return Math.sin((time * Math.PI * 2.0) / duration + seed * 5642.86729349) * 0.5 + 0.5;
}
/**
* Returns the smallest angle between two angles
* @param {number} a
* @param {number} b
* @returns {number} 0 .. 2 PI
*/
export function smallestAngle(a, b) {
return safeMod(a - b + Math.PI, 2.0 * Math.PI) - Math.PI;
}
/**
* Modulo which works for negative numbers
* @param {number} n
* @param {number} m
*/
export function safeMod(n, m) {
return ((n % m) + m) % m;
}
/**
* Wraps an angle between 0 and 2 pi
* @param {number} angle
*/
export function wrapAngle(angle) {
return safeMod(angle, 2.0 * Math.PI);
}
/** /**
* Waits two frames so the ui is updated * Waits two frames so the ui is updated
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
export function waitNextFrame() { export function waitNextFrame() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve) {
window.requestAnimationFrame(function () { window.requestAnimationFrame(function () {
window.requestAnimationFrame(function () { window.requestAnimationFrame(function () {
resolve(); resolve();
@ -617,27 +389,13 @@ export function clamp(v, minimum = 0, maximum = 1) {
return Math.max(minimum, Math.min(maximum, v)); return Math.max(minimum, Math.min(maximum, v));
} }
/**
* Measures how long a function took
* @param {string} name
* @param {function():void} target
*/
export function measure(name, target) {
const now = performance.now();
for (let i = 0; i < 25; ++i) {
target();
}
const dur = (performance.now() - now) / 25.0;
console.warn("->", name, "took", dur.toFixed(2), "ms");
}
/** /**
* Helper method to create a new div element * Helper method to create a new div element
* @param {string=} id * @param {string=} id
* @param {Array<string>=} classes * @param {Array<string>=} classes
* @param {string=} innerHTML * @param {string=} innerHTML
*/ */
export function makeDivElement(id = null, classes = [], innerHTML = "") { function makeDivElement(id = null, classes = [], innerHTML = "") {
const div = document.createElement("div"); const div = document.createElement("div");
if (id) { if (id) {
div.id = id; div.id = id;
@ -662,20 +420,6 @@ export function makeDiv(parent, id = null, classes = [], innerHTML = "") {
return div; return div;
} }
/**
* Helper method to create a new div and place before reference Node
* @param {Element} parent
* @param {Element} referenceNode
* @param {string=} id
* @param {Array<string>=} classes
* @param {string=} innerHTML
*/
export function makeDivBefore(parent, referenceNode, id = null, classes = [], innerHTML = "") {
const div = makeDivElement(id, classes, innerHTML);
parent.insertBefore(div, referenceNode);
return div;
}
/** /**
* Helper method to create a new button element * Helper method to create a new button element
* @param {Array<string>=} classes * @param {Array<string>=} classes
@ -703,19 +447,6 @@ export function makeButton(parent, classes = [], innerHTML = "") {
return element; return element;
} }
/**
* Helper method to create a new button and place before reference Node
* @param {Element} parent
* @param {Element} referenceNode
* @param {Array<string>=} classes
* @param {string=} innerHTML
*/
export function makeButtonBefore(parent, referenceNode, classes = [], innerHTML = "") {
const element = makeButtonElement(classes, innerHTML);
parent.insertBefore(element, referenceNode);
return element;
}
/** /**
* Removes all children of the given element * Removes all children of the given element
* @param {Element} elem * @param {Element} elem
@ -728,20 +459,10 @@ export function removeAllChildren(elem) {
} }
} }
export function smartFadeNumber(current, newOne, minFade = 0.01, maxFade = 0.9) {
const tolerance = Math.min(current, newOne) * 0.5 + 10;
let fade = minFade;
if (Math.abs(current - newOne) < tolerance) {
fade = maxFade;
}
return current * fade + newOne * (1 - fade);
}
/** /**
* Fixes lockstep simulation by converting times like 34.0000000003 to 34.00. * Fixes lockstep simulation by converting times like 34.0000000003 to 34.00.
* We use 3 digits of precision, this allows to store sufficient precision of 1 ms without * We use 3 digits of precision, this allows us to store precision of 1 ms without
* the risk to simulation errors due to resync issues * the risking simulation errors due to resync issues
* @param {number} value * @param {number} value
*/ */
export function quantizeFloat(value) { export function quantizeFloat(value) {
@ -840,37 +561,6 @@ export function isSupportedBrowser() {
} }
} }
/**
* Helper function to create a json schema object
* @param {any} properties
*/
export function schemaObject(properties) {
return {
type: "object",
required: Object.keys(properties).slice(),
additionalProperties: false,
properties,
};
}
/**
* Quickly
* @param {number} x
* @param {number} y
* @param {number} deg
* @returns {Vector}
*/
export function fastRotateMultipleOf90(x, y, deg) {
switch (deg) {
case 0: {
return new Vector(x, y);
}
case 90: {
return new Vector(x, y);
}
}
}
/** /**
* Formats an amount of seconds into something like "5s ago" * Formats an amount of seconds into something like "5s ago"
* @param {number} secs Seconds * @param {number} secs Seconds
@ -928,31 +618,6 @@ export function formatSeconds(secs) {
} }
} }
/**
* Generates a file download
* @param {string} filename
* @param {string} text
*/
export function generateFileDownload(filename, text) {
var element = document.createElement("a");
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
/**
* Capitalizes the first letter
* @param {string} str
*/
export function capitalizeFirstLetter(str) {
return str.substr(0, 1).toUpperCase() + str.substr(1).toLowerCase();
}
/** /**
* Formats a number like 2.5 to "2.5 items / s" * Formats a number like 2.5 to "2.5 items / s"
* @param {number} speed * @param {number} speed

View File

@ -6,7 +6,7 @@ import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
import { GameRoot } from "../root"; import { GameRoot } from "../root";
import { enumHubGoalRewards } from "../tutorial_goals"; import { enumHubGoalRewards } from "../tutorial_goals";
import { T } from "../../translations"; import { T } from "../../translations";
import { round1Digit, round2Digits, formatItemsPerSecond } from "../../core/utils"; import { formatItemsPerSecond } from "../../core/utils";
/** @enum {string} */ /** @enum {string} */
export const enumMinerVariants = { chainable: "chainable" }; export const enumMinerVariants = { chainable: "chainable" };

View File

@ -44,3 +44,9 @@ export class Component extends BasicSerializableObject {
} }
/* dev:end */ /* dev:end */
} }
/**
* TypeScript does not support Abstract Static methods (https://github.com/microsoft/TypeScript/issues/34516)
* One workaround is to declare the type of the component and reference that for static methods
* @typedef {typeof Component} StaticComponent
*/

View File

@ -1,7 +1,6 @@
import { GameRoot } from "./root"; import { GameRoot } from "./root";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { round3Digits } from "../core/utils";
const logger = createLogger("dynamic_tickrate"); const logger = createLogger("dynamic_tickrate");

View File

@ -1,5 +1,4 @@
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { queryParamOptions } from "../core/query_parameters";
import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils"; import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils";
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
import { enumColors } from "./colors"; import { enumColors } from "./colors";
@ -7,7 +6,7 @@ import { enumItemProcessorTypes } from "./components/item_processor";
import { GameRoot, enumLayer } from "./root"; import { GameRoot, enumLayer } from "./root";
import { enumSubShape, ShapeDefinition } from "./shape_definition"; import { enumSubShape, ShapeDefinition } from "./shape_definition";
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals"; import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
import { UPGRADES, blueprintShape } from "./upgrades"; import { UPGRADES } from "./upgrades";
export class HubGoals extends BasicSerializableObject { export class HubGoals extends BasicSerializableObject {
static getId() { static getId() {
@ -328,9 +327,7 @@ export class HubGoals extends BasicSerializableObject {
/** @type {Array<import("./shape_definition").ShapeLayer>} */ /** @type {Array<import("./shape_definition").ShapeLayer>} */
let layers = []; let layers = [];
// @ts-ignore
const randomColor = () => randomChoice(Object.values(enumColors)); const randomColor = () => randomChoice(Object.values(enumColors));
// @ts-ignore
const randomShape = () => randomChoice(Object.values(enumSubShape)); const randomShape = () => randomChoice(Object.values(enumSubShape));
let anyIsMissingTwo = false; let anyIsMissingTwo = false;

View File

@ -1,7 +1,7 @@
import { ClickDetector } from "../../../core/click_detector"; import { ClickDetector } from "../../../core/click_detector";
import { globalConfig, THIRDPARTY_URLS } from "../../../core/config"; import { globalConfig } from "../../../core/config";
import { DrawParameters } from "../../../core/draw_parameters"; import { DrawParameters } from "../../../core/draw_parameters";
import { drawRotatedSprite, rotateTrapezRightFaced } from "../../../core/draw_utils"; import { drawRotatedSprite } from "../../../core/draw_utils";
import { Loader } from "../../../core/loader"; import { Loader } from "../../../core/loader";
import { clamp, makeDiv, removeAllChildren } from "../../../core/utils"; import { clamp, makeDiv, removeAllChildren } from "../../../core/utils";
import { import {
@ -323,7 +323,6 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
// Fade in / out // Fade in / out
parameters.context.lineWidth = 1; parameters.context.lineWidth = 1;
// parameters.context.globalAlpha = 0.3 + pulseAnimation(this.root.time.realtimeNow(), 0.9) * 0.7;
// Determine the bounds and visualize them // Determine the bounds and visualize them
const entityBounds = staticComp.getTileSpaceBounds(); const entityBounds = staticComp.getTileSpaceBounds();

View File

@ -1,10 +1,9 @@
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
import { makeDiv, randomInt } from "../../../core/utils"; import { makeDiv } from "../../../core/utils";
import { SOUNDS } from "../../../platform/sound"; import { SOUNDS } from "../../../platform/sound";
import { enumNotificationType } from "./notifications"; import { enumNotificationType } from "./notifications";
import { T } from "../../../translations"; import { T } from "../../../translations";
import { KEYMAPPINGS } from "../../key_action_mapper"; import { KEYMAPPINGS } from "../../key_action_mapper";
import { IS_DEMO } from "../../../core/config";
import { DynamicDomAttach } from "../dynamic_dom_attach"; import { DynamicDomAttach } from "../dynamic_dom_attach";
export class HUDGameMenu extends BaseHUDPart { export class HUDGameMenu extends BaseHUDPart {

View File

@ -1,5 +1,5 @@
import { ClickDetector } from "../../../core/click_detector"; import { ClickDetector } from "../../../core/click_detector";
import { formatBigNumber, makeDiv, arrayDelete, arrayDeleteValue } from "../../../core/utils"; import { formatBigNumber, makeDiv, arrayDeleteValue } from "../../../core/utils";
import { ShapeDefinition } from "../../shape_definition"; import { ShapeDefinition } from "../../shape_definition";
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
import { blueprintShape, UPGRADES } from "../../upgrades"; import { blueprintShape, UPGRADES } from "../../upgrades";

View File

@ -1,13 +1,11 @@
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
import { makeDiv, formatSeconds, formatBigNumberFull } from "../../../core/utils"; import { makeDiv, formatBigNumberFull } from "../../../core/utils";
import { DynamicDomAttach } from "../dynamic_dom_attach"; import { DynamicDomAttach } from "../dynamic_dom_attach";
import { InputReceiver } from "../../../core/input_receiver"; import { InputReceiver } from "../../../core/input_receiver";
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
import { T } from "../../../translations"; import { T } from "../../../translations";
import { StaticMapEntityComponent } from "../../components/static_map_entity"; import { StaticMapEntityComponent } from "../../components/static_map_entity";
import { ItemProcessorComponent } from "../../components/item_processor";
import { BeltComponent } from "../../components/belt"; import { BeltComponent } from "../../components/belt";
import { IS_DEMO } from "../../../core/config";
export class HUDSettingsMenu extends BaseHUDPart { export class HUDSettingsMenu extends BaseHUDPart {
createElements(parent) { createElements(parent) {
@ -57,16 +55,7 @@ export class HUDSettingsMenu extends BaseHUDPart {
} }
returnToMenu() { returnToMenu() {
// if (IS_DEMO) {
// const { cancel, deleteGame } = this.root.hud.parts.dialogs.showWarning(
// T.dialogs.leaveNotPossibleInDemo.title,
// T.dialogs.leaveNotPossibleInDemo.desc,
// ["cancel:good", "deleteGame:bad"]
// );
// deleteGame.add(() => this.root.gameState.goBackToMenu());
// } else {
this.root.gameState.goBackToMenu(); this.root.gameState.goBackToMenu();
// }
} }
goToSettings() { goToSettings() {
@ -102,7 +91,6 @@ export class HUDSettingsMenu extends BaseHUDPart {
show() { show() {
this.visible = true; this.visible = true;
document.body.classList.add("ingameDialogOpen"); document.body.classList.add("ingameDialogOpen");
// this.background.classList.add("visible");
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60); const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60);

View File

@ -1,5 +1,5 @@
import { InputReceiver } from "../../../core/input_receiver"; import { InputReceiver } from "../../../core/input_receiver";
import { makeButton, makeDiv, removeAllChildren, capitalizeFirstLetter } from "../../../core/utils"; import { makeButton, makeDiv, removeAllChildren } from "../../../core/utils";
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
import { enumAnalyticsDataSource } from "../../production_analytics"; import { enumAnalyticsDataSource } from "../../production_analytics";
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
@ -7,6 +7,14 @@ import { DynamicDomAttach } from "../dynamic_dom_attach";
import { enumDisplayMode, HUDShapeStatisticsHandle } from "./statistics_handle"; import { enumDisplayMode, HUDShapeStatisticsHandle } from "./statistics_handle";
import { T } from "../../../translations"; import { T } from "../../../translations";
/**
* Capitalizes the first letter
* @param {string} str
*/
function capitalizeFirstLetter(str) {
return str.substr(0, 1).toUpperCase() + str.substr(1).toLowerCase();
}
export class HUDStatistics extends BaseHUDPart { export class HUDStatistics extends BaseHUDPart {
createElements(parent) { createElements(parent) {
this.background = makeDiv(parent, "ingame_HUD_Statistics", ["ingameDialog"]); this.background = makeDiv(parent, "ingame_HUD_Statistics", ["ingameDialog"]);

View File

@ -5,7 +5,6 @@ import { Entity } from "./entity";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { BaseItem } from "./base_item"; import { BaseItem } from "./base_item";
import { MapChunkView } from "./map_chunk_view"; import { MapChunkView } from "./map_chunk_view";
import { randomInt } from "../core/utils";
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
const logger = createLogger("map"); const logger = createLogger("map");

View File

@ -28,25 +28,13 @@ export class MapChunk {
this.tileY = y * globalConfig.mapChunkSize; this.tileY = y * globalConfig.mapChunkSize;
/** @type {Array<Array<?Entity>>} */ /** @type {Array<Array<?Entity>>} */
this.contents = make2DUndefinedArray( this.contents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize);
globalConfig.mapChunkSize,
globalConfig.mapChunkSize,
"map-chunk@" + this.x + "|" + this.y
);
/** @type {Array<Array<?Entity>>} */ /** @type {Array<Array<?Entity>>} */
this.wireContents = make2DUndefinedArray( this.wireContents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize);
globalConfig.mapChunkSize,
globalConfig.mapChunkSize,
"map-chunk-wires@" + this.x + "|" + this.y
);
/** @type {Array<Array<?BaseItem>>} */ /** @type {Array<Array<?BaseItem>>} */
this.lowerLayer = make2DUndefinedArray( this.lowerLayer = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize);
globalConfig.mapChunkSize,
globalConfig.mapChunkSize,
"map-chunk-lower@" + this.x + "|" + this.y
);
/** @type {Array<Entity>} */ /** @type {Array<Entity>} */
this.containedEntities = []; this.containedEntities = [];

View File

@ -1,15 +1,6 @@
import { MapChunk } from "./map_chunk"; import { MapChunk } from "./map_chunk";
import { GameRoot } from "./root"; import { GameRoot } from "./root";
import { globalConfig } from "../core/config";
import { DrawParameters } from "../core/draw_parameters"; import { DrawParameters } from "../core/draw_parameters";
import { round1Digit } from "../core/utils";
import { Rectangle } from "../core/rectangle";
import { createLogger } from "../core/logging";
import { smoothenDpi } from "../core/dpi_manager";
import { THEME } from "./theme";
const logger = createLogger("chunk");
const chunkSizePixels = globalConfig.mapChunkSize * globalConfig.tileSize;
export class MapChunkView extends MapChunk { export class MapChunkView extends MapChunk {
/** /**

14
src/js/globals.d.ts vendored
View File

@ -131,22 +131,24 @@ declare interface Math {
degrees(number): number; degrees(number): number;
} }
declare type Class<T = unknown> = new (...args: any[]) => T;
declare interface String { declare interface String {
padStart(size: number, fill?: string): string; padStart(size: number, fill?: string): string;
padEnd(size: number, fill: string): string; padEnd(size: number, fill: string): string;
} }
declare interface FactoryTemplate<T> { declare interface FactoryTemplate<T> {
entries: Array<new (...args: any[]) => T>; entries: Array<Class<T>>;
entryIds: Array<string>; entryIds: Array<string>;
idToEntry: any; idToEntry: any;
getId(): string; getId(): string;
getAllIds(): Array<string>; getAllIds(): Array<string>;
register(entry: new (...args: any[]) => T): void; register(entry: Class<T>): void;
hasId(id: string): boolean; hasId(id: string): boolean;
findById(id: string): new (...args: any[]) => T; findById(id: string): Class<T>;
getEntries(): Array<new (...args: any[]) => T>; getEntries(): Array<Class<T>>;
getNumEntries(): number; getNumEntries(): number;
} }
@ -156,10 +158,10 @@ declare interface SingletonFactoryTemplate<T> {
getId(): string; getId(): string;
getAllIds(): Array<string>; getAllIds(): Array<string>;
register(classHandle: new (...args: any[]) => T): void; register(classHandle: Class<T>): void;
hasId(id: string): boolean; hasId(id: string): boolean;
findById(id: string): T; findById(id: string): T;
findByClass(classHandle: new (...args: any[]) => T): T; findByClass(classHandle: Class<T>): T;
getEntries(): Array<T>; getEntries(): Array<T>;
getNumEntries(): number; getNumEntries(): number;
} }

View File

@ -1,8 +1,6 @@
/* typehints:start */ /**
import { Application } from "../application"; * @typedef {import("../application").Application} Application
import { ShapeDefinition } from "../game/shape_definition"; */
import { Savegame } from "../savegame/savegame";
/* typehints:end */
export class GameAnalyticsInterface { export class GameAnalyticsInterface {
constructor(app) { constructor(app) {

View File

@ -221,7 +221,7 @@ export const allApplicationSettings = [
}), }),
new EnumSetting("refreshRate", { new EnumSetting("refreshRate", {
options: ["60", "100", "120", "144", "165", "250", G_IS_DEV ? "10" : "500"], options: ["60", "75", "100", "120", "144", "165", "250", G_IS_DEV ? "10" : "500"],
valueGetter: rate => rate, valueGetter: rate => rate,
textGetter: rate => rate + " Hz", textGetter: rate => rate + " Hz",
category: enumCategories.advanced, category: enumCategories.advanced,

View File

@ -1,8 +1,3 @@
/* typehints:start */
import { Application } from "../application";
import { GameRoot } from "../game/root";
/* typehints:end */
import { ReadWriteProxy } from "../core/read_write_proxy"; import { ReadWriteProxy } from "../core/read_write_proxy";
import { ExplainedResult } from "../core/explained_result"; import { ExplainedResult } from "../core/explained_result";
import { SavegameSerializer } from "./savegame_serializer"; import { SavegameSerializer } from "./savegame_serializer";
@ -18,20 +13,29 @@ import { SavegameInterface_V1005 } from "./schemas/1005";
const logger = createLogger("savegame"); const logger = createLogger("savegame");
/**
* @typedef {import("../application").Application} Application
* @typedef {import("../game/root").GameRoot} GameRoot
* @typedef {import("./savegame_typedefs").SavegameData} SavegameData
* @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata
* @typedef {import("./savegame_typedefs").SavegameStats} SavegameStats
* @typedef {import("./savegame_typedefs").SerializedGame} SerializedGame
*/
export class Savegame extends ReadWriteProxy { export class Savegame extends ReadWriteProxy {
/** /**
* *
* @param {Application} app * @param {Application} app
* @param {object} param0 * @param {object} param0
* @param {string} param0.internalId * @param {string} param0.internalId
* @param {import("./savegame_manager").SavegameMetadata} param0.metaDataRef Handle to the meta data * @param {SavegameMetadata} param0.metaDataRef Handle to the meta data
*/ */
constructor(app, { internalId, metaDataRef }) { constructor(app, { internalId, metaDataRef }) {
super(app, "savegame-" + internalId + ".bin"); super(app, "savegame-" + internalId + ".bin");
this.internalId = internalId; this.internalId = internalId;
this.metaDataRef = metaDataRef; this.metaDataRef = metaDataRef;
/** @type {import("./savegame_typedefs").SavegameData} */ /** @type {SavegameData} */
this.currentData = this.getDefaultData(); this.currentData = this.getDefaultData();
assert( assert(
@ -65,7 +69,7 @@ export class Savegame extends ReadWriteProxy {
/** /**
* Returns the savegames default data * Returns the savegames default data
* @returns {import("./savegame_typedefs").SavegameData} * @returns {SavegameData}
*/ */
getDefaultData() { getDefaultData() {
return { return {
@ -78,7 +82,7 @@ export class Savegame extends ReadWriteProxy {
/** /**
* Migrates the savegames data * Migrates the savegames data
* @param {import("./savegame_typedefs").SavegameData} data * @param {SavegameData} data
*/ */
migrate(data) { migrate(data) {
if (data.version < 1000) { if (data.version < 1000) {
@ -115,7 +119,7 @@ export class Savegame extends ReadWriteProxy {
/** /**
* Verifies the savegames data * Verifies the savegames data
* @param {import("./savegame_typedefs").SavegameData} data * @param {SavegameData} data
*/ */
verify(data) { verify(data) {
if (!data.dump) { if (!data.dump) {
@ -140,7 +144,7 @@ export class Savegame extends ReadWriteProxy {
} }
/** /**
* Returns the statistics of the savegame * Returns the statistics of the savegame
* @returns {import("./savegame_typedefs").SavegameStats} * @returns {SavegameStats}
*/ */
getStatistics() { getStatistics() {
return this.currentData.stats; return this.currentData.stats;
@ -163,7 +167,7 @@ export class Savegame extends ReadWriteProxy {
/** /**
* Returns the current game dump * Returns the current game dump
* @returns {import("./savegame_typedefs").SerializedGame} * @returns {SerializedGame}
*/ */
getCurrentDump() { getCurrentDump() {
return this.currentData.dump; return this.currentData.dump;

View File

@ -7,31 +7,21 @@ const logger = createLogger("savegame_manager");
const Rusha = require("rusha"); const Rusha = require("rusha");
/**
* @typedef {import("./savegame_typedefs").SavegamesData} SavegamesData
* @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata
*/
/** @enum {string} */ /** @enum {string} */
export const enumLocalSavegameStatus = { export const enumLocalSavegameStatus = {
offline: "offline", offline: "offline",
synced: "synced", synced: "synced",
}; };
/**
* @typedef {{
* lastUpdate: number,
* version: number,
* internalId: string,
* level: number
* }} SavegameMetadata
*
* @typedef {{
* version: number,
* savegames: Array<SavegameMetadata>
* }} SavegamesData
*/
export class SavegameManager extends ReadWriteProxy { export class SavegameManager extends ReadWriteProxy {
constructor(app) { constructor(app) {
super(app, "savegames.bin"); super(app, "savegames.bin");
/** @type {SavegamesData} */
this.currentData = this.getDefaultData(); this.currentData = this.getDefaultData();
} }

View File

@ -1,18 +1,20 @@
/* typehints:start */
import { Component } from "../game/component";
import { GameRoot } from "../game/root";
/* typehints:end */
import { ExplainedResult } from "../core/explained_result"; import { ExplainedResult } from "../core/explained_result";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
// import { BuildingComponent } from "../components/impl/building";
import { gComponentRegistry } from "../core/global_registries"; import { gComponentRegistry } from "../core/global_registries";
import { SerializerInternal } from "./serializer_internal"; import { SerializerInternal } from "./serializer_internal";
/**
* @typedef {import("../game/component").Component} Component
* @typedef {import("../game/component").StaticComponent} StaticComponent
* @typedef {import("../game/entity").Entity} Entity
* @typedef {import("../game/root").GameRoot} GameRoot
* @typedef {import("../savegame/savegame_typedefs").SerializedGame} SerializedGame
*/
const logger = createLogger("savegame_serializer"); const logger = createLogger("savegame_serializer");
/** /**
* Allows to serialize a savegame * Serializes a savegame
*/ */
export class SavegameSerializer { export class SavegameSerializer {
constructor() { constructor() {
@ -26,7 +28,7 @@ export class SavegameSerializer {
* @returns {object} * @returns {object}
*/ */
generateDumpFromGameRoot(root, sanityChecks = true) { generateDumpFromGameRoot(root, sanityChecks = true) {
// Now store generic savegame payload /** @type {SerializedGame} */
const data = { const data = {
camera: root.camera.serialize(), camera: root.camera.serialize(),
time: root.time.serialize(), time: root.time.serialize(),
@ -35,11 +37,10 @@ export class SavegameSerializer {
hubGoals: root.hubGoals.serialize(), hubGoals: root.hubGoals.serialize(),
pinnedShapes: root.hud.parts.pinnedShapes.serialize(), pinnedShapes: root.hud.parts.pinnedShapes.serialize(),
waypoints: root.hud.parts.waypoints.serialize(), waypoints: root.hud.parts.waypoints.serialize(),
entities: this.internal.serializeEntityArray(root.entityMgr.entities),
beltPaths: root.systemMgr.systems.belt.serializePaths(), beltPaths: root.systemMgr.systems.belt.serializePaths(),
}; };
data.entities = this.internal.serializeEntityArray(root.entityMgr.entities);
if (!G_IS_RELEASE) { if (!G_IS_RELEASE) {
if (sanityChecks) { if (sanityChecks) {
// Sanity check // Sanity check
@ -55,7 +56,7 @@ export class SavegameSerializer {
/** /**
* Verifies if there are logical errors in the savegame * Verifies if there are logical errors in the savegame
* @param {object} savegame * @param {SerializedGame} savegame
* @returns {ExplainedResult} * @returns {ExplainedResult}
*/ */
verifyLogicalErrors(savegame) { verifyLogicalErrors(savegame) {
@ -66,11 +67,10 @@ export class SavegameSerializer {
const seenUids = []; const seenUids = [];
// Check for duplicate UIDS // Check for duplicate UIDS
for (const entityListId in savegame.entities) { for (let i = 0; i < savegame.entities.length; ++i) {
for (let i = 0; i < savegame.entities[entityListId].length; ++i) { /** @type {Entity} */
const list = savegame.entities[entityListId][i]; const entity = savegame.entities[i];
for (let k = 0; k < list.length; ++k) {
const entity = list[k];
const uid = entity.uid; const uid = entity.uid;
if (!Number.isInteger(uid)) { if (!Number.isInteger(uid)) {
return ExplainedResult.bad("Entity has invalid uid: " + uid); return ExplainedResult.bad("Entity has invalid uid: " + uid);
@ -82,14 +82,11 @@ export class SavegameSerializer {
// Verify components // Verify components
if (!entity.components) { if (!entity.components) {
return ExplainedResult.bad( return ExplainedResult.bad("Entity is missing key 'components': " + JSON.stringify(entity));
"Entity is missing key 'components': " + JSON.stringify(entity)
);
} }
const components = entity.components; const components = entity.components;
for (const componentId in components) { for (const componentId in components) {
// Verify component data
const componentData = components[componentId];
const componentClass = gComponentRegistry.findById(componentId); const componentClass = gComponentRegistry.findById(componentId);
// Check component id is known // Check component id is known
@ -97,10 +94,13 @@ export class SavegameSerializer {
return ExplainedResult.bad("Unknown component id: " + componentId); return ExplainedResult.bad("Unknown component id: " + componentId);
} }
// Check component data is ok // Verify component data
const componentVerifyError = /** @type {typeof Component} */ (componentClass).verify( const componentData = components[componentId];
const componentVerifyError = /** @type {StaticComponent} */ (componentClass).verify(
componentData componentData
); );
// Check component data is ok
if (componentVerifyError) { if (componentVerifyError) {
return ExplainedResult.bad( return ExplainedResult.bad(
"Component " + componentId + " has invalid data: " + componentVerifyError "Component " + componentId + " has invalid data: " + componentVerifyError
@ -108,15 +108,13 @@ export class SavegameSerializer {
} }
} }
} }
}
}
return ExplainedResult.good(); return ExplainedResult.good();
} }
/** /**
* Tries to load the savegame from a given dump * Tries to load the savegame from a given dump
* @param {import("./savegame_typedefs").SerializedGame} savegame * @param {SerializedGame} savegame
* @param {GameRoot} root * @param {GameRoot} root
* @returns {ExplainedResult} * @returns {ExplainedResult}
*/ */

View File

@ -1,15 +1,8 @@
import { Entity } from "../game/entity";
/**
* @typedef {{
* }} SavegameStats
*/
/** /**
* @typedef {import("../game/entity").Entity} Entity
*
* @typedef {{}} SavegameStats
* *
*/
/**
* @typedef {{ * @typedef {{
* camera: any, * camera: any,
* time: any, * time: any,
@ -21,13 +14,25 @@ import { Entity } from "../game/entity";
* entities: Array<Entity>, * entities: Array<Entity>,
* beltPaths: Array<any> * beltPaths: Array<any>
* }} SerializedGame * }} SerializedGame
*/ *
/**
* @typedef {{ * @typedef {{
* version: number, * version: number,
* dump: SerializedGame, * dump: SerializedGame,
* stats: SavegameStats, * stats: SavegameStats,
* lastUpdate: number, * lastUpdate: number,
* }} SavegameData * }} SavegameData
*
* @typedef {{
* lastUpdate: number,
* version: number,
* internalId: string,
* level: number
* }} SavegameMetadata
*
* @typedef {{
* version: number,
* savegames: Array<SavegameMetadata>
* }} SavegamesData
*/ */
export default {};

View File

@ -43,6 +43,9 @@ export class SavegameInterface_V1001 extends SavegameInterface_V1000 {
for (let i = 0; i < entities.length; ++i) { for (let i = 0; i < entities.length; ++i) {
const entity = entities[i]; const entity = entities[i];
// FIXME - https://github.com/tobspr/shapez.io/issues/514
// Broken in https://github.com/tobspr/shapez.io/commit/bf2eee908fedb84dbbabd359a200c446020a340e
/** @type any **/
const staticComp = entity.components.StaticMapEntity; const staticComp = entity.components.StaticMapEntity;
const beltComp = entity.components.Belt; const beltComp = entity.components.Belt;
if (staticComp) { if (staticComp) {

View File

@ -4,7 +4,7 @@ import { BasicSerializableObject } from "./serialization";
/* typehints:end */ /* typehints:end */
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { round4Digits, schemaObject, accessNestedPropertyReverse } from "../core/utils"; import { round4Digits } from "../core/utils";
export const globalJsonSchemaDefs = {}; export const globalJsonSchemaDefs = {};
/** /**
@ -28,6 +28,19 @@ export function schemaToJsonSchema(schema) {
return jsonSchema; return jsonSchema;
} }
/**
* Helper function to create a json schema object
* @param {any} properties
*/
function schemaObject(properties) {
return {
type: "object",
required: Object.keys(properties).slice(),
additionalProperties: false,
properties,
};
}
/** /**
* Base serialization data type * Base serialization data type
*/ */
@ -75,23 +88,6 @@ export class BaseDataType {
return { return {
$ref: "#/definitions/" + key, $ref: "#/definitions/" + key,
}; };
// return this.getAsJsonSchemaUncached();
// if (!globalJsonSchemaDefs[key]) {
// // schema.$id = key;
// globalJsonSchemaDefs[key] = {
// $id: key,
// definitions: {
// ["d-" + key]: schema
// }
// };
// }
// return {
// $ref: key + "#/definitions/d-" + key
// }
// // return this.getAsJsonSchemaUncached();
} }
/** /**

View File

@ -1,11 +1,10 @@
import { GameState } from "../core/game_state"; import { GameState } from "../core/game_state";
import { cachebust } from "../core/cachebust"; import { cachebust } from "../core/cachebust";
import { globalConfig, IS_DEBUG, IS_DEMO, THIRDPARTY_URLS } from "../core/config"; import { globalConfig, IS_DEMO, THIRDPARTY_URLS } from "../core/config";
import { import {
makeDiv, makeDiv,
makeButtonElement, makeButtonElement,
formatSecondsToTimeAgo, formatSecondsToTimeAgo,
generateFileDownload,
waitNextFrame, waitNextFrame,
isSupportedBrowser, isSupportedBrowser,
makeButton, makeButton,
@ -14,9 +13,29 @@ import {
import { ReadWriteProxy } from "../core/read_write_proxy"; import { ReadWriteProxy } from "../core/read_write_proxy";
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
import { T } from "../translations"; import { T } from "../translations";
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
import { getApplicationSettingById } from "../profile/application_settings"; import { getApplicationSettingById } from "../profile/application_settings";
import { EnumSetting } from "../profile/setting_types";
/**
* @typedef {import("../savegame/savegame_typedefs").SavegameMetadata} SavegameMetadata
* @typedef {import("../profile/setting_types").EnumSetting} EnumSetting
*/
/**
* Generates a file download
* @param {string} filename
* @param {string} text
*/
function generateFileDownload(filename, text) {
var element = document.createElement("a");
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
export class MainMenuState extends GameState { export class MainMenuState extends GameState {
constructor() { constructor() {
@ -128,7 +147,6 @@ export class MainMenuState extends GameState {
const closeLoader = this.dialogs.showLoadingDialog(); const closeLoader = this.dialogs.showLoadingDialog();
const reader = new FileReader(); const reader = new FileReader();
reader.addEventListener("load", event => { reader.addEventListener("load", event => {
// @ts-ignore
const contents = event.target.result; const contents = event.target.result;
let realContent; let realContent;
@ -394,7 +412,7 @@ export class MainMenuState extends GameState {
} }
/** /**
* @param {object} game * @param {SavegameMetadata} game
*/ */
resumeGame(game) { resumeGame(game) {
this.app.analytics.trackUiClick("resume_game"); this.app.analytics.trackUiClick("resume_game");
@ -419,7 +437,7 @@ export class MainMenuState extends GameState {
} }
/** /**
* @param {object} game * @param {SavegameMetadata} game
*/ */
deleteGame(game) { deleteGame(game) {
this.app.analytics.trackUiClick("delete_game"); this.app.analytics.trackUiClick("delete_game");
@ -447,7 +465,7 @@ export class MainMenuState extends GameState {
} }
/** /**
* @param {object} game * @param {SavegameMetadata} game
*/ */
downloadGame(game) { downloadGame(game) {
this.app.analytics.trackUiClick("download_game"); this.app.analytics.trackUiClick("download_game");

View File

@ -1,6 +1,6 @@
import { GameState } from "../core/game_state"; import { GameState } from "../core/game_state";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { findNiceValue, waitNextFrame } from "../core/utils"; import { findNiceValue } from "../core/utils";
import { cachebust } from "../core/cachebust"; import { cachebust } from "../core/cachebust";
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper"; import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
import { T, autoDetectLanguageId, updateApplicationLanguage } from "../translations"; import { T, autoDetectLanguageId, updateApplicationLanguage } from "../translations";
@ -228,12 +228,8 @@ export class PreloadState extends GameState {
this.statusBar.style.width = percentage + "%"; this.statusBar.style.width = percentage + "%";
this.statusBarText.innerText = findNiceValue(percentage) + "%"; this.statusBarText.innerText = findNiceValue(percentage) + "%";
if (G_IS_DEV) {
return Promise.resolve(); return Promise.resolve();
} }
return Promise.resolve();
// return waitNextFrame();
}
showFailMessage(text) { showFailMessage(text) {
logger.error("App init failed:", text); logger.error("App init failed:", text);
@ -279,11 +275,6 @@ export class PreloadState extends GameState {
if (confirm("Are you sure you want to reset the app? This will delete all your savegames")) { if (confirm("Are you sure you want to reset the app? This will delete all your savegames")) {
this.resetApp(); this.resetApp();
} }
// const signals = this.dialogs.showWarning(T.preload.reset_app_warning.title, T.preload.reset_app_warning.desc, [
// "delete:bad:timeout",
// "cancel:good",
// ]);
// signals.delete.add(this.resetApp, this);
} }
resetApp() { resetApp() {

View File

@ -33,13 +33,13 @@ steamPage:
shapez.io est un jeu dans lequel vous devrez construire des usines pour automatiser la création et la combinaison de formes de plus en plus complexes sur une carte infinie. shapez.io est un jeu dans lequel vous devrez construire des usines pour automatiser la création et la combinaison de formes de plus en plus complexes sur une carte infinie.
Lors de la livraison des formes requises vous progresserez et débloquerez des améliorations pour accélerer votre usine. Lors de la livraison des formes requises vous progresserez et débloquerez des améliorations pour accélerer votre usine.
Au vu de l'augmantation des demandes de formes, vous devrez agrandir votre usine pour répondre à la forte demande - Mais n'oubliez pas les ressources, vous drevrez vous étendre au milieu de cette [b]carte infinie[/b] ! Au vu de l'augmentation des demandes de formes, vous devrez agrandir votre usine pour répondre à la forte demande - Mais n'oubliez pas les ressources, vous drevrez vous étendre au milieu de cette [b]carte infinie[/b] !
Bientôt vous devrez mixer les couleurs et peindre vos formes avec - Combinez les ressources de couleurs rouge, verte et bleue pour produire différentes couleurs et peindre les formes avec pour satisfaire la demande. Bientôt vous devrez mixer les couleurs et peindre vos formes avec - Combinez les ressources de couleurs rouge, verte et bleue pour produire différentes couleurs et peindre les formes avec pour satisfaire la demande.
Ce jeu propose 18 niveaux progressifs (qui devraient déjà vous occuper des heures!) mais j'ajoute constamment de nouveau contenus - Il y en a beaucoup de prévus ! Ce jeu propose 18 niveaux progressifs (qui devraient déjà vous occuper quelques heures !) mais j'ajoute constamment de nouveau contenus - Il y en a beaucoup de prévus !
Acheter le jeu vous donne accès à la version complète qui a des fonctionnalitées additionnelles et vous recevrez aussi un accès à des fonctionnalitées fraîchement développées. Acheter le jeu vous donne accès à la version complète qui a des fonctionnalités additionnelles et vous recevrez aussi un accès à des fonctionnalités fraîchement développées.
[b]Avantages de la version complète (standalone)[/b] [b]Avantages de la version complète (standalone)[/b]
@ -59,9 +59,9 @@ steamPage:
[list] [list]
[*] Différentes cartes et challenges (e.g. carte avec obstacles) [*] Différentes cartes et challenges (e.g. carte avec obstacles)
[*] Puzzles (Délivrer la forme requise avec une zone limitée/jeu de batîments) [*] Puzzles (Délivrer la forme requise avec une zone limitée/jeu de bâtiments)
[*] Un mode histoire où les batîments ont un coût [*] Un mode histoire où les bâtiments ont un coût
[*] Générateur de carte configurable (configuration des ressources/formes taille/densitée, graine et plus) [*] Générateur de carte configurable (configuration des ressources/formes/taille/densitée, seed et plus)
[*] Plus de formes [*] Plus de formes
[*] Amélioration des performances (Le jeu tourne déjà plutot bien !) [*] Amélioration des performances (Le jeu tourne déjà plutot bien !)
[*] Et bien plus ! [*] Et bien plus !
@ -127,7 +127,7 @@ global:
control: CTRL control: CTRL
alt: ALT alt: ALT
escape: ESC escape: ESC
shift: SHIFT shift: MAJ
space: ESPACE space: ESPACE
demoBanners: demoBanners:
@ -146,7 +146,7 @@ mainMenu:
# This is shown when using firefox and other browsers which are not supported. # This is shown when using firefox and other browsers which are not supported.
browserWarning: >- browserWarning: >-
Désolé, mais ce jeu est connu pour tourner lentement sur votre navigateur web ! Procurez-vous la version complète ou téléchargez Chrome pour une meilleure expérience. Désolé, ce jeu est connu pour tourner lentement sur votre navigateur web ! Procurez-vous la version complète ou téléchargez Chrome pour une meilleure expérience.
savegameLevel: Niveau <x> savegameLevel: Niveau <x>
savegameLevelUnknown: Niveau inconnu savegameLevelUnknown: Niveau inconnu
@ -234,12 +234,12 @@ dialogs:
massDeleteConfirm: massDeleteConfirm:
title: Confirmation de suppression title: Confirmation de suppression
desc: >- desc: >-
Vous allez supprimer pas mal de bâtiments (<count> pour être exact) ! Êtes vous certains de vouloir faire cela ? Vous allez supprimer pas mal de bâtiments (<count> pour être exact) ! Êtes vous certains de vouloir faire ça ?
massCutConfirm: massCutConfirm:
title: Confirmer la coupure title: Confirmer la coupure
desc: >- desc: >-
Vous vous apprêtez à couper beaucoup de bâtiments (<count> pour être précis) ! Êtes-vous certains de vouloir faire cela ? Vous vous apprêtez à couper beaucoup de bâtiments (<count> pour être précis) ! Êtes-vous certains de vouloir faire ça ?
blueprintsNotUnlocked: blueprintsNotUnlocked:
title: Pas encore débloqué title: Pas encore débloqué
@ -351,10 +351,10 @@ ingame:
description: Affiche le nombre de formes stockées dans votre bâtiment central. description: Affiche le nombre de formes stockées dans votre bâtiment central.
produced: produced:
title: Produit title: Produit
description: Affiche tous les formes que votre usine entière produit, en incluant les formes intermédiaires. description: Affiche tous les formes que votre usine produit, en incluant les formes intermédiaires.
delivered: delivered:
title: Délivré title: Délivré
description: Affiche les formes qui ont été livrées dans votre centre. description: Affiche les formes qui ont été livrées dans votre bâtiment central.
noShapesProduced: Aucune forme n'a été produite jusqu'à présent. noShapesProduced: Aucune forme n'a été produite jusqu'à présent.
# Displays the shapes per minute, e.g. '523 / m' # Displays the shapes per minute, e.g. '523 / m'
@ -398,7 +398,7 @@ ingame:
Connectez l'extracteur avec un <strong>convoyeur</strong> vers votre centre !<br><br>Astuce: <strong>Cliquez et faites glisser</strong> le convoyeur avec votre souris ! Connectez l'extracteur avec un <strong>convoyeur</strong> vers votre centre !<br><br>Astuce: <strong>Cliquez et faites glisser</strong> le convoyeur avec votre souris !
1_3_expand: >- 1_3_expand: >-
Ceci n'est <strong>PAS</strong> un jeu incrémental et inactif ! Construisez plus d'extracteurs et de convoyeurs pour atteindre plus vite votre votre but.<br><br>Astuce: Gardez <strong>SHIFT</strong> enfoncé pour placer plusieurs extracteurs, et utilisez <strong>R</strong> pour les faire pivoter. Ceci n'est <strong>PAS</strong> un jeu incrémental et inactif ! Construisez plus d'extracteurs et de convoyeurs pour atteindre plus vite votre votre but.<br><br>Astuce: Gardez <strong>MAJ</strong> enfoncé pour placer plusieurs extracteurs, et utilisez <strong>R</strong> pour les faire pivoter.
colors: colors:
red: Rouge red: Rouge
@ -487,8 +487,8 @@ buildings:
name: Pivoteur inversé name: Pivoteur inversé
description: Fait pivoter une forme de 90 degrés vers la gauche. description: Fait pivoter une forme de 90 degrés vers la gauche.
fl: fl:
name: Rotate (180) name: Pivoteur (180)
description: Rotates shapes by 180 degrees. description: Fait pivoter les formes de 90 degrés.
stacker: stacker:
default: default:
@ -707,9 +707,9 @@ settings:
Affiche ou non le bouton 'Afficher un indice' dans le coin inférieur gauche. Affiche ou non le bouton 'Afficher un indice' dans le coin inférieur gauche.
language: language:
title: Langage title: Langue
description: >- description: >-
Change le langage. Toutes les traductions sont des contributions des utilisateurs et pourraient être partiellement incomplètes ! Change la langue. Toutes les traductions sont des contributions des utilisateurs et pourraient être partiellement incomplètes !
movementSpeed: movementSpeed:
title: Vitesse de déplacement title: Vitesse de déplacement

View File

@ -155,6 +155,7 @@ mainMenu:
savegameLevel: <x>. szint savegameLevel: <x>. szint
savegameLevelUnknown: Ismeretlen szint savegameLevelUnknown: Ismeretlen szint
dialogs: dialogs:
buttons: buttons:
ok: OK ok: OK
@ -327,7 +328,7 @@ ingame:
# Notifications on the lower right # Notifications on the lower right
notifications: notifications:
newUpgrade: Egy új fejlesztés elérhető! newUpgrade: Egy új fejlesztés elérhető!
gameSaved: A játékállás el lett mentve. gameSaved: A játékállás mentve.
# The "Upgrades" window # The "Upgrades" window
shop: shop:
@ -353,7 +354,7 @@ ingame:
title: Gyártva title: Gyártva
description: Az összes eddig legyártott alakzatod, beleértve a köztes alakzatokat is. description: Az összes eddig legyártott alakzatod, beleértve a köztes alakzatokat is.
delivered: delivered:
title: Kézbesítve title: Beszállítva
description: Az összes alakzat, amely jelenleg kézbesítés alatt van a központba. description: Az összes alakzat, amely jelenleg kézbesítés alatt van a központba.
noShapesProduced: Még nem gyártottál egy alazkatot sem. noShapesProduced: Még nem gyártottál egy alazkatot sem.
@ -364,8 +365,8 @@ ingame:
settingsMenu: settingsMenu:
playtime: Játékidő playtime: Játékidő
buildingsPlaced: Épület buildingsPlaced: Épületek száma:
beltsPlaced: Futószalag beltsPlaced: Futószalagok hossza:
buttons: buttons:
continue: Folytatás continue: Folytatás
@ -406,7 +407,7 @@ ingame:
blue: Kék blue: Kék
yellow: Sárga yellow: Sárga
purple: Lila purple: Lila
cyan: Cián cyan: Világoskék
white: Fehér white: Fehér
uncolored: Színezetlen uncolored: Színezetlen
black: Fekete black: Fekete
@ -434,91 +435,91 @@ shopUpgrades:
buildings: buildings:
belt: belt:
default: default:
name: &belt Conveyor Belt name: &belt Futószalag
description: Transports items, hold and drag to place multiple. description: Elemeket szállít, tartsd nyomva az egérgombot egyszerre több lerakásához.
miner: # Internal name for the Extractor miner: # Internal name for the Extractor
default: default:
name: &miner Extractor name: &miner Bányász
description: Place over a shape or color to extract it. description: Tedd egy alakzatra vagy színre a kibányászásához.
chainable: chainable:
name: Extractor (Chain) name: Bányász (összekapcsolható)
description: Place over a shape or color to extract it. Can be chained. description: Tedd egy alakzatra vagy színre a kibányászásához. Több egymáshoz kapcsolható.
underground_belt: # Internal name for the Tunnel underground_belt: # Internal name for the Tunnel
default: default:
name: &underground_belt Tunnel name: &underground_belt Alagút
description: Allows to tunnel resources under buildings and belts. description: Segítségével futószalagok és épületek alatt átvezethetők az elemek.
tier2: tier2:
name: Tunnel Tier II name: Alagút II
description: Allows to tunnel resources under buildings and belts. description: Segítségével futószalagok és épületek alatt átvezethetők az elemek.
splitter: # Internal name for the Balancer splitter: # Internal name for the Balancer
default: default:
name: &splitter Balancer name: &splitter Elosztó
description: Multifunctional - Evenly distributes all inputs onto all outputs. description: Többfunkciós - Egyenletesen szétosztja a bementeket a kimenetekre.
compact: compact:
name: Merger (compact) name: Egyesítő (kompakt)
description: Merges two conveyor belts into one. description: Két futószalagot egyesít.
compact-inverse: compact-inverse:
name: Merger (compact) name: Egyesítő (kompakt)
description: Merges two conveyor belts into one. description: Két futószalagot egyesít.
cutter: cutter:
default: default:
name: &cutter Cutter name: &cutter Vágó
description: Cuts shapes from top to bottom and outputs both halfs. <strong>If you use only one part, be sure to destroy the other part or it will stall!</strong> description: Függőlegesen félbevágja az alakzatokat. <strong>Ha csak az egyik felet akarod használni, ne felejtsd el a másikat kukába küldeni, különben eldugítja!</strong>
quad: quad:
name: Cutter (Quad) name: Vágó (negyedelő)
description: Cuts shapes into four parts. <strong>If you use only one part, be sure to destroy the other part or it will stall!</strong> description: Négyfelé vágja az alakzatokat. Cuts shapes into four parts. <strong>Ha csak az egyik felet akarod használni, ne felejtsd el a többit a kukába küldeni, különben eldugítja!</strong>
rotater: rotater:
default: default:
name: &rotater Rotate name: &rotater Forgató
description: Rotates shapes clockwise by 90 degrees. description: Elforgatja az alakzatot óramutató irányában 90 fokkal.
ccw: ccw:
name: Rotate (CCW) name: Forgató (fordított)
description: Rotates shapes counter clockwise by 90 degrees. description: Elforgatja az alakzatot óramutatóval ellentétesen 90 fokkal.
fl: fl:
name: Rotate (180) name: Forgató (180)
description: Rotates shapes by 180 degrees. description: Elforgatja az alakzatot 180 fokkal.
stacker: stacker:
default: default:
name: &stacker Stacker name: &stacker Egyesítő
description: Stacks both items. If they can not be merged, the right item is placed above the left item. description: Egyesít két elemet. Ha nem lehet összeilleszteni őket, a jobboldali elem a baloldali tetejére kerül.
mixer: mixer:
default: default:
name: &mixer Color Mixer name: &mixer Színkeverő
description: Mixes two colors using additive blending. description: Összekever két színt összeadó színkeveréssel.
painter: painter:
default: default:
name: &painter Painter name: &painter Festő
description: &painter_desc Colors the whole shape on the left input with the color from the right input. description: &painter_desc Beszínezi az alakzatot a baloldali bemeneten a jobboldali bemeneten érkező színnel.
double: double:
name: Painter (Double) name: Festő (Dupla)
description: Colors the shapes on the left inputs with the color from the top input. description: Beszínezi az alakzatokat a baloldali bemeneteken a fenti bemeneten érkező színnel.
quad: quad:
name: Painter (Quad) name: Festő (Négyszeres)
description: Allows to color each quadrant of the shape with a different color. description: Az alakzat négy negyedét különböző színekkel lehet vele színezni.
mirrored: mirrored:
name: *painter name: *painter
description: *painter_desc description: *painter_desc
trash: trash:
default: default:
name: &trash Trash name: &trash Kuka
description: Accepts inputs from all sides and destroys them. Forever. description: Bármelyik irányból lehet hozzá csatlakozni, és megsemmisíti a beleküldött elemeket. Örökre.
storage: storage:
name: Storage name: Tároló
description: Stores excess items, up to a given capacity. Can be used as an overflow gate. description: Tárolja a fölös elemeket egy bizonyos kapacitásig.
hub: hub:
deliver: Deliver deliver: Deliver
toUnlock: to unlock toUnlock: to unlock

View File

@ -411,7 +411,7 @@ ingame:
cyan: シアン cyan: シアン
white: white:
uncolored: 無色 uncolored: 無色
black: Black black:
shapeViewer: shapeViewer:
title: レイヤー title: レイヤー
empty: empty:
@ -491,8 +491,8 @@ buildings:
name: 回転機 (逆) name: 回転機 (逆)
description: 形を反時計回り方向に90度回転します。 description: 形を反時計回り方向に90度回転します。
fl: fl:
name: Rotate (180) name: 回転機 (180)
description: Rotates shapes by 180 degrees. description: 形を180度回転します。
stacker: stacker:
default: default:
@ -533,20 +533,20 @@ buildings:
advanced_processor: advanced_processor:
default: default:
name: Color Inverter name: Color Inverter
description: Accepts a color or shape and inverts it. description: 入力された色や形の色を反転します。
energy_generator: energy_generator:
deliver: Deliver deliver: Deliver
toGenerateEnergy: For toGenerateEnergy: For
default: default:
name: Energy Generator name: エネルギー発電機
description: Generates energy by consuming shapes. description: 入力された形を使って、エネルギーを発電します。
wire_crossings: wire_crossings:
default: default:
name: Wire Splitter name: ワイヤー分配機
description: Splits a energy wire into two. description: 1つのワイヤーを2つのワイヤーに分配します。
merger: merger:
name: Wire Merger name: ワイヤー合流機
description: Merges two energy wires into one. description: 2つのワイヤーを1つのワイヤーに合流します。
storyRewards: storyRewards:
# Those are the rewards gained from completing the store # Those are the rewards gained from completing the store

View File

@ -23,6 +23,9 @@ steamPage:
# This is the short text appearing on the steam page # This is the short text appearing on the steam page
shortText: shapez.io é um jogo sobre construir fábricas, automatizando a criação e combinação de formas cada vez mais complexas num mapa infinito. shortText: shapez.io é um jogo sobre construir fábricas, automatizando a criação e combinação de formas cada vez mais complexas num mapa infinito.
# This is the text shown above the Discord link
discordLink: Discord Oficial - Converse comigo!
# This is the long description for the steam page - It is contained here so you can help to translate it, and I will regulary update the store page. # This is the long description for the steam page - It is contained here so you can help to translate it, and I will regulary update the store page.
# NOTICE: # NOTICE:
# - Do not translate the first line (This is the gif image at the start of the store) # - Do not translate the first line (This is the gif image at the start of the store)
@ -31,6 +34,7 @@ steamPage:
[img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img] [img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img]
shapez.io é um jogo sobre construir fábricas, automatizando a criação e combinação de formas cada vez mais complexas num mapa infinito. shapez.io é um jogo sobre construir fábricas, automatizando a criação e combinação de formas cada vez mais complexas num mapa infinito.
Após a entrega das formas requisitadas você progredirá no jogo e desbloqueará melhorias para acelerar sua fábrica. Após a entrega das formas requisitadas você progredirá no jogo e desbloqueará melhorias para acelerar sua fábrica.
Conforme sua demanda por formas aumenta, você irá que aumentar sua fábrica para alcançar-la - Mas não se esqueça dos recursos, você precisará expandir pelo [b]mapa infinito[/b]! Conforme sua demanda por formas aumenta, você irá que aumentar sua fábrica para alcançar-la - Mas não se esqueça dos recursos, você precisará expandir pelo [b]mapa infinito[/b]!
@ -82,8 +86,6 @@ steamPage:
[*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]Ajude a traduzir[/url] [*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]Ajude a traduzir[/url]
[/list] [/list]
discordLink: Discord Oficial - Converse comigo!
global: global:
loading: Carregando loading: Carregando
error: Erro error: Erro
@ -134,14 +136,19 @@ demoBanners:
# This is the "advertisement" shown in the main menu and other various places # This is the "advertisement" shown in the main menu and other various places
title: Versão Demo title: Versão Demo
intro: >- intro: >-
Pegue a versão completa para desbloquear todas os recursos Pegue a versão completa para desbloquear todas os recursos!
mainMenu: mainMenu:
play: Jogar play: Jogar
continue: Continuar
newGame: Novo jogo
changelog: Changelog changelog: Changelog
subreddit: Reddit
importSavegame: Importar importSavegame: Importar
openSourceHint: Esse jogo tem código aberto! openSourceHint: Esse jogo tem código aberto!
discordLink: Discord oficial discordLink: Discord oficial
helpTranslate: Ajude a traduzir!
madeBy: Feito por <author-link>
# This is shown when using firefox and other browsers which are not supported. # This is shown when using firefox and other browsers which are not supported.
browserWarning: >- browserWarning: >-
@ -150,12 +157,6 @@ mainMenu:
savegameLevel: Nível <x> savegameLevel: Nível <x>
savegameLevelUnknown: Nível desconhecido savegameLevelUnknown: Nível desconhecido
helpTranslate: Ajude a traduzir!
continue: Continuar
newGame: Novo jogo
madeBy: Feito por <author-link>
subreddit: Reddit
dialogs: dialogs:
buttons: buttons:
ok: OK ok: OK
@ -163,11 +164,11 @@ dialogs:
cancel: Cancelar cancel: Cancelar
later: Voltar later: Voltar
restart: Reiniciar restart: Reiniciar
reset: Reset reset: Resetar
getStandalone: Obter versão completa getStandalone: Obter versão completa
deleteGame: Sei o que estou fazendo deleteGame: Sei o que estou fazendo
viewUpdate: Atualizações viewUpdate: Atualizações
showUpgrades: Ver melhorias showUpgrades: Melhorias
showKeybindings: Controles showKeybindings: Controles
importSavegameError: importSavegameError:
@ -198,7 +199,7 @@ dialogs:
restartRequired: restartRequired:
title: Ação necessária title: Ação necessária
text: >- text: >-
Voce precisa reiniciar o jogo para aplicar as mudanças. Você precisa reiniciar o jogo para aplicar as mudanças.
editKeybinding: editKeybinding:
title: Alterar tecla title: Alterar tecla
@ -236,6 +237,16 @@ dialogs:
desc: >- desc: >-
Você está deletando vários objetos (<count> para ser exato)! Você quer continuar? Você está deletando vários objetos (<count> para ser exato)! Você quer continuar?
massCutConfirm:
title: Confirmar corte
desc: >-
Você está cortando vários objetos (<count> para ser exato)! Você quer continuar?
massCutInsufficientConfirm:
title: Confirmar Corte
desc: >-
You can not afford to paste this area! Are you sure you want to cut it?
blueprintsNotUnlocked: blueprintsNotUnlocked:
title: Não desbloqueado ainda title: Não desbloqueado ainda
desc: >- desc: >-
@ -252,35 +263,24 @@ dialogs:
createMarker: createMarker:
title: Nova Marcação title: Nova Marcação
desc: Dê um nome com significado, também pode adicionar <strong>um pequeno código</strong> de uma forma. (Pode ser gerado <a href="https://viewer.shapez.io" target="_blank">aqui</a>)
titleEdit: Editar Marcador titleEdit: Editar Marcador
desc: Dê um nome com significado, também pode adicionar <strong>um pequeno código</strong> de uma forma. (Pode ser gerado <a href="https://viewer.shapez.io" target="_blank">aqui</a>)
markerDemoLimit: markerDemoLimit:
desc: >- desc: Você só pode criar dois marcadores na versão demo. Adquira a versão completa para marcadores ilimitados!
Você só pode criar dois marcadores na versão demo. Adquira a versão completa para marcadores ilimitados!
massCutConfirm:
title: Confirmar corte
desc: >-
Você está cortando vários objetos (<count> para ser exato)! Você quer continuar?
exportScreenshotWarning: exportScreenshotWarning:
title: Exportar captura de tela title: Exportar captura de tela
desc: >- desc: Você está prestes a exportar uma captura de tela da sua base. Note que isso pode ser bastante lento para uma base grande, e até mesmo pode travar o jogo!
Você está prestes a exportar uma captura de tela da sua base. Note que isso pode ser bastante lento para uma base grande, e até mesmo pode travar o jogo!
massCutInsufficientConfirm:
title: Confirmar Corte
desc: You can not afford to paste this area! Are you sure you want to cut it?
ingame: ingame:
# This is shown in the top left corner and displays useful keybindings in # This is shown in the top left corner and displays useful keybindings in
# every situation # every situation
keybindingsOverlay: keybindingsOverlay:
moveMap: Mover moveMap: Mover
selectBuildings: Selecionar área
stopPlacement: Parar stopPlacement: Parar
rotateBuilding: Rotação rotateBuilding: Rotacionar
placeMultiple: Colocar vários placeMultiple: Colocar vários
reverseOrientation: Inverter orientação reverseOrientation: Inverter orientação
disableAutoOrientation: Desligar orientação automática disableAutoOrientation: Desligar orientação automática
@ -288,17 +288,27 @@ ingame:
placeBuilding: Construir objeto placeBuilding: Construir objeto
createMarker: Criar marcador createMarker: Criar marcador
delete: Destruir delete: Destruir
selectBuildings: Selecionar área
pasteLastBlueprint: Colar último projeto pasteLastBlueprint: Colar último projeto
lockBeltDirection: Ativar Planejador de Esteiras lockBeltDirection: Ativar Planejador de Esteiras
plannerSwitchSide: Flip planner side plannerSwitchSide: Girar Planejador
cutSelection: Cortar cutSelection: Cortar
copySelection: Copiar copySelection: Copiar
clearSelection: Limpar Seleção clearSelection: Limpar Seleção
pipette: Conta-Gotas pipette: Conta-Gotas
switchLayers: Trocar Camadas switchLayers: Trocar Camadas
# Names of the colors, used for the color blind mode
colors:
red: Vermelho
green: Verde
blue: Azul
yellow: Amarelo
purple: Roxo
cyan: Ciano
white: Branco
black: Preto
uncolored: Sem cor
# Everything related to placing buildings (I.e. as soon as you selected a building # Everything related to placing buildings (I.e. as soon as you selected a building
# from the toolbar) # from the toolbar)
buildingPlacement: buildingPlacement:
@ -324,7 +334,7 @@ ingame:
levelCompleteNotification: levelCompleteNotification:
# <level> is replaced by the actual level, so this gets 'Level 03' for example. # <level> is replaced by the actual level, so this gets 'Level 03' for example.
levelTitle: Nível <level> levelTitle: Nível <level>
completed: Completado completed: Concluído
unlockText: Desbloqueado <reward>! unlockText: Desbloqueado <reward>!
buttonNextLevel: Próximo Nível buttonNextLevel: Próximo Nível
@ -336,13 +346,14 @@ ingame:
# The "Upgrades" window # The "Upgrades" window
shop: shop:
title: Melhorias title: Melhorias
buttonUnlock: Comprar buttonUnlock: Melhorar
# Gets replaced to e.g. "Tier IX" # Gets replaced to e.g. "Tier IX"
tier: Nível <x> tier: Nível <x>
# The roman number for each tier # The roman number for each tier
tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X] tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X]
maximumLevel: NÍVEL MÁXIMO (Velocidade x<currentMult>) maximumLevel: NÍVEL MÁXIMO (Velocidade x<currentMult>)
# The "Statistics" window # The "Statistics" window
@ -392,6 +403,12 @@ ingame:
description: Clique com o botão esquerdo do mouse em um marcador para pular, clique com o botão direito do mouse para excluí-lo. <br><br> Pressione <keybinding> para criar um marcador a partir da exibição atual ou <strong>clique com o botão direito do mouse</strong> para criar um marcador no local selecionado. description: Clique com o botão esquerdo do mouse em um marcador para pular, clique com o botão direito do mouse para excluí-lo. <br><br> Pressione <keybinding> para criar um marcador a partir da exibição atual ou <strong>clique com o botão direito do mouse</strong> para criar um marcador no local selecionado.
creationSuccessNotification: Marcador criado. creationSuccessNotification: Marcador criado.
# Shape viewer
shapeViewer:
title: Camadas
empty: Vazio
copyKey: Copiar Chave
# Interactive tutorial # Interactive tutorial
interactiveTutorial: interactiveTutorial:
title: Tutorial title: Tutorial
@ -403,46 +420,38 @@ ingame:
1_3_expand: >- 1_3_expand: >-
Este <strong>NÃO</strong> é um jogo inativo! Construa mais extratores e esteiras para concluir o objetivo mais rapidamente.<br><br>Dica, segure <strong>SHIFT</strong> para colocar vários extratores e use <strong>R</strong> para girá-los. Este <strong>NÃO</strong> é um jogo inativo! Construa mais extratores e esteiras para concluir o objetivo mais rapidamente.<br><br>Dica, segure <strong>SHIFT</strong> para colocar vários extratores e use <strong>R</strong> para girá-los.
colors:
red: Vermelho
green: Verde
blue: Azul
yellow: Amarelo
purple: Roxo
cyan: Ciano
white: Branco
black: Preto
uncolored: Sem cor
shapeViewer:
title: Camadas
empty: Vazio
copyKey: Copiar Chave
# All shop upgrades # All shop upgrades
shopUpgrades: shopUpgrades:
belt: belt:
name: Esteiras, Distribuidores e Túneis name: Esteiras, Distribuidores e Túneis
description: Velocidade x<currentMult> → x<newMult> description: Velocidade x<currentMult> → x<newMult>
miner: miner:
name: Extração name: Extração
description: Velocidade x<currentMult> → x<newMult> description: Velocidade x<currentMult> → x<newMult>
processors: processors:
name: Corte, Rotação & Montagem name: Corte, Rotação e Montagem
description: Velocidade x<currentMult> → x<newMult> description: Velocidade x<currentMult> → x<newMult>
painting: painting:
name: Mistura & Pintura name: Mistura e Pintura
description: Velocidade x<currentMult> → x<newMult> description: Velocidade x<currentMult> → x<newMult>
# Buildings and their name / description # Buildings and their name / description
buildings: buildings:
hub:
deliver: Entregue
toUnlock: para desbloquear
levelShortcut: LVL
belt: belt:
default: default:
name: &belt Esteira Transportadora name: &belt Esteira Transportadora
description: Transporta itens; mantenha pressionado e arraste para colocar vários. description: Transporta itens; mantenha pressionado e arraste para colocar vários.
wire:
default:
name: &wire Fio de Energia
description: Permite transportar energia.
miner: # Internal name for the Extractor miner: # Internal name for the Extractor
default: default:
name: &miner Extrator name: &miner Extrator
@ -482,6 +491,11 @@ buildings:
name: Cortador (Quádruplo) name: Cortador (Quádruplo)
description: Corta as formas em quatro partes. <strong>Se você usar apenas uma parte, não se esqueça de destruir as outras, ou ela irá parar a produção!</strong> description: Corta as formas em quatro partes. <strong>Se você usar apenas uma parte, não se esqueça de destruir as outras, ou ela irá parar a produção!</strong>
advanced_processor:
default:
name: &advanced_processor Inversor de Cor
description: Aceita uma cor ou forma e a inverte.
rotater: rotater:
default: default:
name: &rotater Rotacionador name: &rotater Rotacionador
@ -490,8 +504,8 @@ buildings:
name: Rotacionador (Anti-horário) name: Rotacionador (Anti-horário)
description: Gira as formas no sentido anti-horário em 90 graus. description: Gira as formas no sentido anti-horário em 90 graus.
fl: fl:
name: Rotate (180) name: Rotacionador (180)
description: Rotates shapes by 180 degrees. description: Gira as formas em 180 graus.
stacker: stacker:
default: default:
@ -507,16 +521,18 @@ buildings:
default: default:
name: &painter Pintor name: &painter Pintor
description: &painter_desc Colore a forma inteira na entrada esquerda com a cor da entrada direita. description: &painter_desc Colore a forma inteira na entrada esquerda com a cor da entrada direita.
double:
name: Pintor (Duplo)
description: Colore as duas formas na entrada esquerda com a cor da entrada direita.
quad:
name: Pintor (Quádruplo)
description: Permite colorir cada quadrante da forma com uma cor diferente.
mirrored: mirrored:
name: *painter name: *painter
description: *painter_desc description: *painter_desc
double:
name: Pintor (Duplo)
description: Colore as formas na entrada esquerda com a cor da entrada superior.
quad:
name: Pintor (Quádruplo)
description: Permite colorir cada quadrante da forma com uma cor diferente.
trash: trash:
default: default:
name: &trash Lixo name: &trash Lixo
@ -525,29 +541,22 @@ buildings:
storage: storage:
name: Estoque name: Estoque
description: Armazena itens em excesso, até uma determinada capacidade. Pode ser usado como uma porta de transbordamento. description: Armazena itens em excesso, até uma determinada capacidade. Pode ser usado como uma porta de transbordamento.
hub:
deliver: Entregue
toUnlock: para desbloquear
levelShortcut: LVL
wire:
default:
name: &wire Fio de Energia
description: Permite transportar energia.
advanced_processor:
default:
name: &advanced_processor Inversor de Cor
description: Aceita uma cor ou forma e a inverte.
energy_generator: energy_generator:
deliver: Entregar deliver: Entregar
# This will be shown before the amount, so for example 'For 123 Energy'
toGenerateEnergy: Para toGenerateEnergy: Para
default: default:
name: &energy_generator Gerador de Energia name: &energy_generator Gerador de Energia
description: Consome formas para gerar energia. description: Consome formas para gerar energia.
wire_crossings: wire_crossings:
default: default:
name: &wire_crossings Divisor de Fios name: &wire_crossings Divisor de Fios
description: Divide um fio de energia em dois. description: Divide um fio de energia em dois.
merger: merger:
name: Misturador de Fios name: Misturador de Fios
description: Une dois fios de energia em um. description: Une dois fios de energia em um.
@ -556,7 +565,7 @@ storyRewards:
# Those are the rewards gained from completing the store # Those are the rewards gained from completing the store
reward_cutter_and_trash: reward_cutter_and_trash:
title: Cortando formas title: Cortando formas
desc: Voce desbloqueou <strong>cortador</strong> - corte de formas pela metade <strong>verticalmente</strong> independentemente de sua orientação!<br><br> Certifique-se de se livrar do lixo, ou então <strong> ele irá parar a produção </strong> - Para esse propósito, eu lhe dei uma lixeira, que destrói tudo o que você coloca nela! desc: Você desbloqueou <strong>cortador</strong> - corte de formas pela metade <strong>verticalmente</strong> independentemente de sua orientação!<br><br> Certifique-se de se livrar do lixo, ou então <strong>ele irá parar a produção</strong> - Para esse propósito, eu lhe dei uma lixeira, que destrói tudo o que você coloca nela!
reward_rotater: reward_rotater:
title: Rotação title: Rotação
@ -597,7 +606,8 @@ storyRewards:
reward_splitter_compact: reward_splitter_compact:
title: Distribuidor compacto title: Distribuidor compacto
desc: Você desbloqueou uma variante compacta do <strong>Distribuidor</strong> - ele aceita duas entradas e as une em uma! desc: >-
Você desbloqueou uma variante compacta do <strong>Distribuidor</strong> - ele aceita duas entradas e as une em uma!
reward_cutter_quad: reward_cutter_quad:
title: Cortador quádruplo title: Cortador quádruplo
@ -637,9 +647,9 @@ storyRewards:
settings: settings:
title: opções title: opções
categories: categories:
general: General general: Geral
userInterface: User Interface userInterface: Interface de Usuário
advanced: Advanced advanced: Avançado
versionBadges: versionBadges:
dev: Desenvolvedor dev: Desenvolvedor
@ -653,23 +663,58 @@ settings:
description: >- description: >-
Altera o tamanho da fonte do usuário. A interface ainda será dimensionada com base na resolução do dispositivo, mas essa configuração controla a escala do texto. Altera o tamanho da fonte do usuário. A interface ainda será dimensionada com base na resolução do dispositivo, mas essa configuração controla a escala do texto.
scales: scales:
super_small: Super pequeno super_small: Super Pequeno
small: Pequeno small: Pequeno
regular: Normal regular: Normal
large: Grande large: Grande
huge: Gigante huge: Gigante
autosaveInterval:
title: Intervalo de gravação automática
description: >-
Controla a frequência com que o jogo salva automaticamente. Você também pode desativá-lo totalmente aqui.
intervals:
one_minute: 1 Minuto
two_minutes: 2 Minutos
five_minutes: 5 Minutos
ten_minutes: 10 Minutos
twenty_minutes: 20 Minutos
disabled: Desativado
scrollWheelSensitivity: scrollWheelSensitivity:
title: Sensibilidade do zoom title: Sensibilidade do zoom
description: >- description: >-
Altera a sensibilidade do zoom (roda do mouse ou touchpad). Altera a sensibilidade do zoom (roda do mouse ou touchpad).
sensitivity: sensitivity:
super_slow: Super lento super_slow: Super Lento
slow: Lento slow: Lento
regular: Normal regular: Normal
fast: Rápido fast: Rápido
super_fast: Super Rápido super_fast: Super Rápido
movementSpeed:
title: Velocidade da câmera
description: >-
Altera a velocidade com que a câmera se move com o teclado.
speeds:
super_slow: Super Lento
slow: Lento
regular: Normal
fast: Rápido
super_fast: Super Rápido
extremely_fast: Extremamente Rápido
language:
title: Idioma
description: >-
Altera o idioma. Todas as traduções são contribuições de usuários e podem estar incompletas!
enableColorBlindHelper:
title: Modo daltônico.
description: >-
Permite várias ferramentas que permitem jogar se você é daltônico.
fullscreen: fullscreen:
title: Tela Cheia title: Tela Cheia
description: >- description: >-
@ -678,7 +723,7 @@ settings:
soundsMuted: soundsMuted:
title: Som title: Som
description: >- description: >-
Se ligado o jogo fica mudo Se ligado, o jogo fica mudo.
musicMuted: musicMuted:
title: Música title: Música
@ -689,7 +734,6 @@ settings:
title: Tema title: Tema
description: >- description: >-
Escolha o tema entre (Claro / Escuro). Escolha o tema entre (Claro / Escuro).
themes: themes:
dark: Escuro dark: Escuro
light: Claro light: Claro
@ -709,63 +753,30 @@ settings:
description: >- description: >-
Se ativado, oferece dicas e tutoriais enquanto se joga. Além disso, esconde certos elementos da interface até certo ponto, para facilitar o começo do jogo. Se ativado, oferece dicas e tutoriais enquanto se joga. Além disso, esconde certos elementos da interface até certo ponto, para facilitar o começo do jogo.
language:
title: Idioma
description: >-
Altera o idioma. Todas as traduções são contribuições de usuários e podem estar incompletas!
movementSpeed:
title: Velocidade da câmera
description: Altera a velocidade com que a câmera se move com o teclado.
speeds:
super_slow: Super Lento
slow: Lento
regular: Normal
fast: Rápido
super_fast: Super Rápido
extremely_fast: Extremamente Rápido
enableTunnelSmartplace: enableTunnelSmartplace:
title: Túneis inteligentes title: Túneis inteligentes
description: >- description: >-
Quando colocados, irão remover automaticamente esteiras desnecessárias. Quando colocados, irão remover automaticamente esteiras desnecessárias. Isso também permite arrastar túneis e túneis em excesso serão removidos.
Isso também permite arrastar túneis e túneis em excesso serão removidos.
vignette: vignette:
title: Vignette title: Vinheta
description: >- description: >-
Permite o modo vinheta que escurece os cantos da tela e facilita a Permite o modo vinheta que escurece os cantos da tela e facilita a leitura do texto.
leitura do texto.
autosaveInterval:
title: Intervalo de gravação automática
description: >-
Controla a frequência com que o jogo salva automaticamente.
Você também pode desativá-lo totalmente aqui.
intervals:
one_minute: 1 Minuto
two_minutes: 2 Minutos
five_minutes: 5 Minutos
ten_minutes: 10 Minutos
twenty_minutes: 20 Minutos
disabled: Desativado
compactBuildingInfo:
title: Informações compactas sobre construções
description: >-
Reduz as caixas de informações dos construções, mostrando apenas suas proporções.
Caso contrário, uma descrição e imagem são mostradas.
disableCutDeleteWarnings:
title: Desativar avisos de recorte / exclusão
description: >-
Desative as caixas de diálogo de aviso exibidas ao cortar / excluir
mais de 100 entidades.
enableColorBlindHelper:
title: Modo daltônico.
description: Permite várias ferramentas que permitem jogar se você é daltônico.
rotationByBuilding: rotationByBuilding:
title: Rotação por tipo de construção title: Rotação por tipo de construção
description: >- description: >-
Cada tipo de construção lembra a rotação que você definiu pela última vez individualmente. Cada tipo de construção lembra a rotação que você definiu pela última vez individualmente. Isso pode ser mais confortável se você alternar frequentemente entre a colocação de diferentes tipos de construção.
Isso pode ser mais confortável se você alternar frequentemente entre a colocação de diferentes tipos de construção.
compactBuildingInfo:
title: Informações compactas sobre construções
description: >-
Reduz as caixas de informações dos construções, mostrando apenas suas proporções. Caso contrário, uma descrição e imagem são mostradas.
disableCutDeleteWarnings:
title: Desativar avisos de recorte / exclusão
description: >-
Desative as caixas de diálogo de aviso exibidas ao cortar / excluir mais de 100 entidades.
keybindings: keybindings:
title: Controles title: Controles
@ -790,6 +801,7 @@ keybindings:
mapMoveRight: Mover para direita mapMoveRight: Mover para direita
mapMoveDown: Mover para baixo mapMoveDown: Mover para baixo
mapMoveLeft: Mover para a esquerda mapMoveLeft: Mover para a esquerda
mapMoveFaster: Mover mais rápido
centerMap: Centralizar mapa centerMap: Centralizar mapa
mapZoomIn: Aproximar mapZoomIn: Aproximar
@ -798,12 +810,12 @@ keybindings:
menuOpenShop: Melhorias menuOpenShop: Melhorias
menuOpenStats: Estatísticas menuOpenStats: Estatísticas
menuClose: Fechar Menu
toggleHud: Ocultar Interface toggleHud: Ocultar Interface
toggleFPSInfo: Mostrar FPS e Debug Info toggleFPSInfo: Mostrar FPS e Debug Info
switchLayers: Alternar Camadas switchLayers: Alternar Camadas
exportScreenshot: Exportar Base inteira como Imagem exportScreenshot: Exportar Base Inteira como Imagem
belt: *belt belt: *belt
splitter: *splitter splitter: *splitter
underground_belt: *underground_belt underground_belt: *underground_belt
@ -824,41 +836,33 @@ keybindings:
Modifier: Rotação anti-horária Modifier: Rotação anti-horária
cycleBuildingVariants: Variações cycleBuildingVariants: Variações
confirmMassDelete: Confirmar exclusão em massa confirmMassDelete: Confirmar exclusão em massa
pasteLastBlueprint: Colar último projeto
cycleBuildings: Trocar de construção cycleBuildings: Trocar de construção
lockBeltDirection: Ativar planejador de correia
switchDirectionLockSide: >-
Planejador: Mudar de lado
massSelectStart: Segure e arraste para começar massSelectStart: Segure e arraste para começar
massSelectSelectMultiple: Selecionar mais áreas massSelectSelectMultiple: Selecionar mais áreas
massSelectCopy: Copiar área massSelectCopy: Copiar área
massSelectCut: Cortar área
placementDisableAutoOrientation: Desligar orientação automática placementDisableAutoOrientation: Desligar orientação automática
placeMultiple: Permanecer no modo de construção placeMultiple: Permanecer no modo de construção
placeInverse: Inverter orientação de esteira placeInverse: Inverter orientação de esteira
pasteLastBlueprint: Colar último projeto
massSelectCut: Cortar área
mapMoveFaster: Mover mais rápido
lockBeltDirection: Ativar planejador de correia
switchDirectionLockSide: "Planejador: Mudar de lado"
menuClose: Fechar Menu
about: about:
title: Sobre o jogo title: Sobre o jogo
body: >- body: >-
Esse jogo tem código aberto e é desenvolvido por <a href="https://github.com/tobspr" Esse jogo tem código aberto e é desenvolvido por <a href="https://github.com/tobspr" target="_blank">Tobias Springer</a> (esse sou eu).<br><br>
target="_blank">Tobias Springer</a> (esse sou eu).<br><br>
Se quiser contribuir, confira <a href="<githublink>" Se quiser contribuir, confira <a href="<githublink>" target="_blank">shapez.io no github</a>.<br><br>
target="_blank">shapez.io no github</a>.<br><br>
O jogo não seria possível sem a comunidade incrível do Discord sobre O jogo não seria possível sem a comunidade incrível do Discord sobre os meus jogos - Junte-se à comunidade no <a href="<discordlink>" target="_blank">servidor do Discord</a>!<br><br>
os meus jogos - Junte-se à comunidade no <a href="<discordlink>"
target="_blank">servidor do Discord</a>!<br><br>
A trilha sonora foi feita por <a href="https://soundcloud.com/pettersumelius" A trilha sonora foi feita por <a href="https://soundcloud.com/pettersumelius" target="_blank">Peppsen</a> - Ele é demais.<br><br>
target="_blank">Peppsen</a> - Ele é demais.<br><br>
Finalmente, agradeço muito ao meu melhor amigo Finalmente, agradeço muito ao meu melhor amigo <a href="https://github.com/niklas-dahl" target="_blank">Niklas</a> - Sem nossas sessões de Factorio, esse jogo nunca teria existido.
<a href="https://github.com/niklas-dahl" target="_blank">Niklas</a> - Sem nossas sessões de Factorio,
esse jogo nunca teria existido.
changelog: changelog:
title: Alterações title: Alterações

View File

@ -488,8 +488,8 @@ buildings:
name: Вращатель (Обр.) name: Вращатель (Обр.)
description: Поворачивает фигуры против часовой стрелки на 90 градусов. description: Поворачивает фигуры против часовой стрелки на 90 градусов.
fl: fl:
name: Rotate (180) name: Вращатель (180)
description: Rotates shapes by 180 degrees. description: Вращает фигуры на 180 градусов.
stacker: stacker:
default: default:
@ -635,9 +635,9 @@ storyRewards:
settings: settings:
title: Настройки title: Настройки
categories: categories:
general: General general: Основные
userInterface: User Interface userInterface: Интерфейс
advanced: Advanced advanced: Продвинутые
versionBadges: versionBadges:
dev: Разработчик dev: Разработчик

View File

@ -176,7 +176,7 @@ mainMenu:
# This is shown when using firefox and other browsers which are not supported. # This is shown when using firefox and other browsers which are not supported.
browserWarning: >- browserWarning: >-
很抱歉, 本游戏在当前浏览器上可能运行缓慢! 使用chrome或者获取独立版以得到更好的体验。 很抱歉, 本游戏在当前浏览器上可能运行缓慢! 使用 Chrome 或者获取独立版以得到更好的体验。
savegameLevel: 第<x>关 savegameLevel: 第<x>关
savegameLevelUnknown: savegameLevelUnknown:
@ -592,7 +592,7 @@ storyRewards:
reward_painter: reward_painter:
title: 上色 title: 上色
desc: >- desc: >-
恭喜!你解锁了<strong>上色机</strong>。 开采一些颜色 (就像你开采图形一样) 将其在上色机中与图形结合来将图形上色!<br><br>PS: 如果你患有色盲,可以在设置中启用<strong>色盲模式</strong> 恭喜!你解锁了<strong>上色机</strong>。开采一些颜色 (就像你开采图形一样) 将其在上色机中与图形结合来将图形上色!<br><br>PS如果你患有色盲,可以在设置中启用<strong>色盲模式</strong>
reward_mixer: reward_mixer:
title: 混合颜色 title: 混合颜色
@ -612,7 +612,7 @@ storyRewards:
reward_rotater_ccw: reward_rotater_ccw:
title: 逆时针旋转 title: 逆时针旋转
desc: 恭喜!你解锁了<strong>旋转机</strong>的<strong>逆时针</strong>变体。这个变体可以逆时针旋转图形。选择旋转机然后按“T”键来选取这个变体。 desc: 恭喜!你解锁了<strong>旋转机</strong>的<strong>逆时针</strong>变体。这个变体可以逆时针旋转图形。选择旋转机然后按"T"键来选取这个变体。
reward_miner_chainable: reward_miner_chainable:
title: 链式开采机 title: 链式开采机
@ -654,7 +654,7 @@ storyRewards:
no_reward: no_reward:
title: 下一关 title: 下一关
desc: >- desc: >-
这一关没有奖励,但是下一关有!<br><br> PS: 你生产过的<strong>所有</strong>图形都会被用来<strong>升级建筑</strong>。 这一关没有奖励,但是下一关有!<br><br> PS你生产过的<strong>所有</strong>图形都会被用来<strong>升级建筑</strong>。
no_reward_freeplay: no_reward_freeplay:
title: 下一关 title: 下一关
@ -729,7 +729,7 @@ settings:
refreshRate: refreshRate:
title: 模拟频率、刷新频率 title: 模拟频率、刷新频率
description: >- description: >-
如果你的显示器是144hz的,请在这里更改刷新频率,这样游戏可以正确地根据你的屏幕进行模拟。但是如果你的电脑性能不佳,提高刷新频率可能降低帧数。 如果你的显示器是 144Hz 的,请在这里更改刷新频率,这样游戏可以正确地根据你的屏幕进行模拟。但是如果你的电脑性能不佳,提高刷新频率可能降低帧数。
# description: >- # description: >-
# If you have a 144hz monitor, change the refresh rate here so the game will properly simulate at higher refresh rates. This might actually decrease the FPS if your computer is too slow. # If you have a 144hz monitor, change the refresh rate here so the game will properly simulate at higher refresh rates. This might actually decrease the FPS if your computer is too slow.
@ -799,7 +799,7 @@ settings:
keybindings: keybindings:
title: 按键设置 title: 按键设置
hint: >- hint: >-
提示:使用CTRL、SHIFT、ALT! 这些建在放置建筑时有不同的效果。 提示:使用 CTRL、SHIFT、ALT这些建在放置建筑时有不同的效果。
# hint: >- # hint: >-
# Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different placement options. # Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different placement options.