diff --git a/src/js/application.js b/src/js/application.js index bf70239d..e5e22b60 100644 --- a/src/js/application.js +++ b/src/js/application.js @@ -14,13 +14,9 @@ import { AdProviderInterface } from "./platform/ad_provider"; import { NoAdProvider } from "./platform/ad_providers/no_ad_provider"; import { AnalyticsInterface } from "./platform/analytics"; import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics"; -import { NoGameAnalytics } from "./platform/browser/no_game_analytics"; import { SoundImplBrowser } from "./platform/browser/sound"; import { PlatformWrapperImplBrowser } from "./platform/browser/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 { ApplicationSettings } from "./profile/application_settings"; import { SavegameManager } from "./savegame/savegame_manager"; @@ -34,6 +30,12 @@ import { PreloadState } from "./states/preload"; import { SettingsState } from "./states/settings"; 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"); // Set the name of the hidden property and the change event for visibility diff --git a/src/js/core/atlas_definitions.js b/src/js/core/atlas_definitions.js index 42cd2bce..38d36b59 100644 --- a/src/js/core/atlas_definitions.js +++ b/src/js/core/atlas_definitions.js @@ -1,20 +1,38 @@ /** + * @typedef {{ w: number, h: number }} Size + * @typedef {{ x: number, y: number }} Position * @typedef {{ - * frame: { x: number, y: number, w: number, h: number }, - * rotated: false, - * spriteSourceSize: { x: number, y: number, w: number, h: number }, - * sourceSize: { w: number, h: number}, - * trimmed: true + * frame: Position & Size, + * rotated: boolean, + * spriteSourceSize: Position & Size, + * sourceSize: Size, + * trimmed: boolean * }} SpriteDefinition + * + * @typedef {{ + * app: string, + * version: string, + * image: string, + * format: string, + * size: Size, + * scale: string, + * smartupdate: string + * }} AtlasMeta + * + * @typedef {{ + * frames: Object., + * meta: AtlasMeta + * }} SourceData */ export class AtlasDefinition { - constructor(sourceData) { - this.sourceFileName = sourceData.meta.image; - this.meta = sourceData.meta; - - /** @type {Object.} */ - this.sourceData = sourceData.frames; + /** + * @param {SourceData} sourceData + */ + constructor({ frames, meta }) { + this.meta = meta; + this.sourceData = frames; + this.sourceFileName = meta.image; } getFullSourcePath() { @@ -22,6 +40,7 @@ export class AtlasDefinition { } } +/** @type {AtlasDefinition[]} **/ export const atlasFiles = require // @ts-ignore .context("../../../res_built/atlas/", false, /.*\.json/i) diff --git a/src/js/core/background_resources_loader.js b/src/js/core/background_resources_loader.js index 213e5869..b3a7671b 100644 --- a/src/js/core/background_resources_loader.js +++ b/src/js/core/background_resources_loader.js @@ -115,7 +115,6 @@ export class BackgroundResourcesLoader { }) .then(() => { logger.log("⏰ Finish load: bare game"); - Loader.createAtlasLinks(); this.bareGameReady = true; initBuildingCodesAfterResourcesLoaded(); this.signalBareGameLoaded.dispatch(); diff --git a/src/js/core/draw_parameters.js b/src/js/core/draw_parameters.js index dcdf6d13..45620a9e 100644 --- a/src/js/core/draw_parameters.js +++ b/src/js/core/draw_parameters.js @@ -1,9 +1,9 @@ -import { Rectangle } from "./rectangle"; import { globalConfig } from "./config"; -/* typehints:start */ -import { GameRoot } from "../game/root"; -/* typehints:end */ +/** + * @typedef {import("../game/root").GameRoot} GameRoot + * @typedef {import("./rectangle").Rectangle} Rectangle + */ export class DrawParameters { constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) { diff --git a/src/js/core/draw_utils.js b/src/js/core/draw_utils.js index 1b37b929..ea5b70c2 100644 --- a/src/js/core/draw_utils.js +++ b/src/js/core/draw_utils.js @@ -1,18 +1,13 @@ -/* typehints:start */ -import { AtlasSprite } from "./sprites"; -import { DrawParameters } from "./draw_parameters"; -/* typehints:end */ - -import { Vector } from "./vector"; -import { Rectangle } from "./rectangle"; -import { createLogger } from "./logging"; - -const logger = createLogger("draw_utils"); +/** + * @typedef {import("./sprites").AtlasSprite} AtlasSprite + * @typedef {import("./draw_parameters").DrawParameters} DrawParameters + */ export function initDrawUtils() { CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) { + this.beginPath(); + if (r < 0.05) { - this.beginPath(); this.rect(x, y, w, h); return; } @@ -20,25 +15,26 @@ export function initDrawUtils() { if (w < 2 * r) { r = w / 2; } + if (h < 2 * r) { r = h / 2; } - this.beginPath(); + this.moveTo(x + r, y); this.arcTo(x + w, y, x + w, 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, x + w, y, r); - // this.closePath(); }; CanvasRenderingContext2D.prototype.beginCircle = function (x, y, r) { + this.beginPath(); + if (r < 0.05) { - this.beginPath(); this.rect(x, y, 1, 1); return; } - this.beginPath(); + 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.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; -} - -// Cohen–Sutherland 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); -} diff --git a/src/js/core/global_registries.js b/src/js/core/global_registries.js index 321732e0..ad45850c 100644 --- a/src/js/core/global_registries.js +++ b/src/js/core/global_registries.js @@ -1,19 +1,19 @@ import { SingletonFactory } from "./singleton_factory"; import { Factory } from "./factory"; -/* typehints:start */ -import { BaseGameSpeed } from "../game/time/base_game_speed"; -import { Component } from "../game/component"; -import { BaseItem } from "../game/base_item"; -import { MetaBuilding } from "../game/meta_building"; -/* typehints:end */ +/** + * @typedef {import("../game/time/base_game_speed").BaseGameSpeed} BaseGameSpeed + * @typedef {import("../game/component").Component} Component + * @typedef {import("../game/base_item").BaseItem} BaseItem + * @typedef {import("../game/meta_building").MetaBuilding} MetaBuilding + // These factories are here to remove circular dependencies /** @type {SingletonFactoryTemplate} */ export let gMetaBuildingRegistry = new SingletonFactory(); -/** @type {Object.>} */ +/** @type {Object.>>} */ export let gBuildingsByCategory = null; /** @type {FactoryTemplate} */ @@ -28,7 +28,7 @@ export let gItemRegistry = new Factory("item"); // Helpers /** - * @param {Object.>} buildings + * @param {Object.>>} buildings */ export function initBuildingsByCategory(buildings) { gBuildingsByCategory = buildings; diff --git a/src/js/core/loader.js b/src/js/core/loader.js index 8888ecbf..d7f544e3 100644 --- a/src/js/core/loader.js +++ b/src/js/core/loader.js @@ -1,20 +1,19 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ - -import { AtlasDefinition } from "./atlas_definitions"; import { makeOffscreenBuffer } from "./buffer_utils"; import { AtlasSprite, BaseSprite, RegularSprite, SpriteAtlasLink } from "./sprites"; import { cachebust } from "./cachebust"; import { createLogger } from "./logging"; +/** + * @typedef {import("../application").Application} Application + * @typedef {import("./atlas_definitions").AtlasDefinition} AtlasDefinition; + */ + const logger = createLogger("loader"); const missingSpriteIds = {}; class LoaderImpl { constructor() { - /** @type {Application} */ this.app = null; /** @type {Map} */ @@ -23,6 +22,9 @@ class LoaderImpl { this.rawImages = []; } + /** + * @param {Application} app + */ linkAppAfterBoot(app) { this.app = app; 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 * @returns {RegularSprite} */ @@ -155,44 +157,34 @@ class LoaderImpl { * @param {AtlasDefinition} atlas * @param {HTMLImageElement} loadedImage */ - internalParseAtlas(atlas, loadedImage) { + internalParseAtlas({ meta: { scale }, sourceData }, loadedImage) { this.rawImages.push(loadedImage); - for (const spriteKey in atlas.sourceData) { - const spriteData = atlas.sourceData[spriteKey]; + for (const spriteName in sourceData) { + const { frame, sourceSize, spriteSourceSize } = sourceData[spriteName]; - let sprite = /** @type {AtlasSprite} */ (this.sprites.get(spriteKey)); + let sprite = /** @type {AtlasSprite} */ (this.sprites.get(spriteName)); if (!sprite) { - sprite = new AtlasSprite({ - spriteName: spriteKey, - }); - this.sprites.set(spriteKey, sprite); + sprite = new AtlasSprite(spriteName); + this.sprites.set(spriteName, sprite); } const link = new SpriteAtlasLink({ - packedX: spriteData.frame.x, - packedY: spriteData.frame.y, - packedW: spriteData.frame.w, - packedH: spriteData.frame.h, - packOffsetX: spriteData.spriteSourceSize.x, - packOffsetY: spriteData.spriteSourceSize.y, + packedX: frame.x, + packedY: frame.y, + packedW: frame.w, + packedH: frame.h, + packOffsetX: spriteSourceSize.x, + packOffsetY: spriteSourceSize.y, atlas: loadedImage, - w: spriteData.sourceSize.w, - h: spriteData.sourceSize.h, + w: sourceSize.w, + 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 */ @@ -216,14 +208,9 @@ class LoaderImpl { // @ts-ignore canvas.src = "not-found"; - const resolutions = ["0.1", "0.25", "0.5", "0.75", "1"]; - const sprite = new AtlasSprite({ - spriteName: "not-found", - }); - - for (let i = 0; i < resolutions.length; ++i) { - const res = resolutions[i]; - const link = new SpriteAtlasLink({ + const sprite = new AtlasSprite("not-found"); + ["0.1", "0.25", "0.5", "0.75", "1"].forEach(resolution => { + sprite.linksByResolution[resolution] = new SpriteAtlasLink({ packedX: 0, packedY: 0, w: dims, @@ -234,8 +221,8 @@ class LoaderImpl { packedH: dims, atlas: canvas, }); - sprite.linksByResolution[res] = link; - } + }); + this.spriteNotFoundSprite = sprite; } } diff --git a/src/js/core/rectangle.js b/src/js/core/rectangle.js index 75279e58..6b4315aa 100644 --- a/src/js/core/rectangle.js +++ b/src/js/core/rectangle.js @@ -1,5 +1,5 @@ import { globalConfig } from "./config"; -import { clamp, epsilonCompare, round2Digits } from "./utils"; +import { epsilonCompare, round2Digits } from "./utils"; import { Vector } from "./vector"; export class Rectangle { diff --git a/src/js/core/sprites.js b/src/js/core/sprites.js index 8427f2ef..6cf495cf 100644 --- a/src/js/core/sprites.js +++ b/src/js/core/sprites.js @@ -1,6 +1,6 @@ import { DrawParameters } from "./draw_parameters"; import { Rectangle } from "./rectangle"; -import { epsilonCompare, round3Digits } from "./utils"; +import { round3Digits } from "./utils"; const floorSpriteCoordinates = false; @@ -63,10 +63,9 @@ export class SpriteAtlasLink { export class AtlasSprite extends BaseSprite { /** * - * @param {object} param0 - * @param {string} param0.spriteName + * @param {string} spriteName */ - constructor({ spriteName = "sprite" }) { + constructor(spriteName = "sprite") { super(); /** @type {Object.} */ this.linksByResolution = {}; @@ -197,8 +196,6 @@ export class AtlasSprite extends BaseSprite { destH = intersection.h; } - // assert(epsilonCompare(scaleW, scaleH), "Sprite should be square for cached rendering"); - if (floorSpriteCoordinates) { parameters.context.drawImage( link.atlas, diff --git a/src/js/core/utils.js b/src/js/core/utils.js index cb00e465..0aa97992 100644 --- a/src/js/core/utils.js +++ b/src/js/core/utils.js @@ -1,46 +1,7 @@ -import { globalConfig, IS_DEBUG } from "./config"; -import { Vector } from "./vector"; 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"]; -/** - * 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 {boolean} @@ -66,7 +27,7 @@ export function isIos() { /** * Returns a platform name - * @returns {string} + * @returns {"android" | "browser" | "ios" | "standalone" | "unknown"} */ export function getPlatformName() { if (G_IS_STANDALONE) { @@ -96,60 +57,13 @@ export function getIPCRenderer() { 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 ""; - } - 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>} - */ -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 * @param {number} w * @param {number} h - * @param {string=} context * @returns {Array>} */ -export function make2DUndefinedArray(w, h, context = null) { +export function make2DUndefinedArray(w, h) { const result = new Array(w); for (let x = 0; x < w; ++x) { result[x] = new Array(h); @@ -157,33 +71,6 @@ export function make2DUndefinedArray(w, h, context = null) { return result; } -/** - * Clears a given 2D array with the given fill method - * @param {Array>} 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) */ @@ -215,7 +102,9 @@ export function accessNestedPropertyReverse(obj, keys) { /** * Chooses a random entry of an array - * @param {Array | string} arr + * @template T + * @param {T[]} arr + * @returns {T} */ export function randomChoice(arr) { return arr[Math.floor(Math.random() * arr.length)]; @@ -304,23 +193,6 @@ export function arrayDeleteValue(array, value) { 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 * @param {number} a @@ -331,15 +203,6 @@ export function epsilonCompare(a, b, epsilon = 1e-5) { 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 * @param {number} a @@ -399,17 +262,6 @@ export function findNiceIntegerValue(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 * @param {number} num @@ -477,92 +329,12 @@ export function formatBigNumberFull(num, divider = T.global.thousandsDivider) { return out.substring(0, out.length - 1); } -/** - * Delayes a promise so that it will resolve after a *minimum* amount of time only - * @param {Promise} promise The promise to delay - * @param {number} minTimeMs The time to make it run at least - * @returns {Promise} 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 * @returns {Promise} */ export function waitNextFrame() { - return new Promise(function (resolve, reject) { + return new Promise(function (resolve) { window.requestAnimationFrame(function () { window.requestAnimationFrame(function () { resolve(); @@ -617,27 +389,13 @@ export function clamp(v, minimum = 0, maximum = 1) { 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 * @param {string=} id * @param {Array=} classes * @param {string=} innerHTML */ -export function makeDivElement(id = null, classes = [], innerHTML = "") { +function makeDivElement(id = null, classes = [], innerHTML = "") { const div = document.createElement("div"); if (id) { div.id = id; @@ -662,20 +420,6 @@ export function makeDiv(parent, id = null, classes = [], innerHTML = "") { 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=} 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 * @param {Array=} classes @@ -703,19 +447,6 @@ export function makeButton(parent, classes = [], innerHTML = "") { return element; } -/** - * Helper method to create a new button and place before reference Node - * @param {Element} parent - * @param {Element} referenceNode - * @param {Array=} 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 * @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. - * We use 3 digits of precision, this allows to store sufficient precision of 1 ms without - * the risk to simulation errors due to resync issues + * We use 3 digits of precision, this allows us to store precision of 1 ms without + * the risking simulation errors due to resync issues * @param {number} 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" * @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" * @param {number} speed diff --git a/src/js/game/buildings/miner.js b/src/js/game/buildings/miner.js index ed87bc85..17fccf46 100644 --- a/src/js/game/buildings/miner.js +++ b/src/js/game/buildings/miner.js @@ -6,7 +6,7 @@ import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; import { GameRoot } from "../root"; import { enumHubGoalRewards } from "../tutorial_goals"; import { T } from "../../translations"; -import { round1Digit, round2Digits, formatItemsPerSecond } from "../../core/utils"; +import { formatItemsPerSecond } from "../../core/utils"; /** @enum {string} */ export const enumMinerVariants = { chainable: "chainable" }; diff --git a/src/js/game/component.js b/src/js/game/component.js index 1d44d60f..7d30faff 100644 --- a/src/js/game/component.js +++ b/src/js/game/component.js @@ -44,3 +44,9 @@ export class Component extends BasicSerializableObject { } /* 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 + */ diff --git a/src/js/game/dynamic_tickrate.js b/src/js/game/dynamic_tickrate.js index f289e2c1..a5033acf 100644 --- a/src/js/game/dynamic_tickrate.js +++ b/src/js/game/dynamic_tickrate.js @@ -1,7 +1,6 @@ import { GameRoot } from "./root"; import { createLogger } from "../core/logging"; import { globalConfig } from "../core/config"; -import { round3Digits } from "../core/utils"; const logger = createLogger("dynamic_tickrate"); diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index 36f8f107..cd319059 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -1,5 +1,4 @@ import { globalConfig } from "../core/config"; -import { queryParamOptions } from "../core/query_parameters"; import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils"; import { BasicSerializableObject, types } from "../savegame/serialization"; import { enumColors } from "./colors"; @@ -7,7 +6,7 @@ import { enumItemProcessorTypes } from "./components/item_processor"; import { GameRoot, enumLayer } from "./root"; import { enumSubShape, ShapeDefinition } from "./shape_definition"; import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals"; -import { UPGRADES, blueprintShape } from "./upgrades"; +import { UPGRADES } from "./upgrades"; export class HubGoals extends BasicSerializableObject { static getId() { @@ -328,9 +327,7 @@ export class HubGoals extends BasicSerializableObject { /** @type {Array} */ let layers = []; - // @ts-ignore const randomColor = () => randomChoice(Object.values(enumColors)); - // @ts-ignore const randomShape = () => randomChoice(Object.values(enumSubShape)); let anyIsMissingTwo = false; diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index d5770d0a..ce6e116b 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -1,7 +1,7 @@ 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 { drawRotatedSprite, rotateTrapezRightFaced } from "../../../core/draw_utils"; +import { drawRotatedSprite } from "../../../core/draw_utils"; import { Loader } from "../../../core/loader"; import { clamp, makeDiv, removeAllChildren } from "../../../core/utils"; import { @@ -323,7 +323,6 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { // Fade in / out parameters.context.lineWidth = 1; - // parameters.context.globalAlpha = 0.3 + pulseAnimation(this.root.time.realtimeNow(), 0.9) * 0.7; // Determine the bounds and visualize them const entityBounds = staticComp.getTileSpaceBounds(); diff --git a/src/js/game/hud/parts/game_menu.js b/src/js/game/hud/parts/game_menu.js index 64285624..2f820f7a 100644 --- a/src/js/game/hud/parts/game_menu.js +++ b/src/js/game/hud/parts/game_menu.js @@ -1,10 +1,9 @@ import { BaseHUDPart } from "../base_hud_part"; -import { makeDiv, randomInt } from "../../../core/utils"; +import { makeDiv } from "../../../core/utils"; import { SOUNDS } from "../../../platform/sound"; import { enumNotificationType } from "./notifications"; import { T } from "../../../translations"; import { KEYMAPPINGS } from "../../key_action_mapper"; -import { IS_DEMO } from "../../../core/config"; import { DynamicDomAttach } from "../dynamic_dom_attach"; export class HUDGameMenu extends BaseHUDPart { diff --git a/src/js/game/hud/parts/pinned_shapes.js b/src/js/game/hud/parts/pinned_shapes.js index bda49f1e..2f7dd11e 100644 --- a/src/js/game/hud/parts/pinned_shapes.js +++ b/src/js/game/hud/parts/pinned_shapes.js @@ -1,5 +1,5 @@ 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 { BaseHUDPart } from "../base_hud_part"; import { blueprintShape, UPGRADES } from "../../upgrades"; diff --git a/src/js/game/hud/parts/settings_menu.js b/src/js/game/hud/parts/settings_menu.js index 6034ab9d..391fde01 100644 --- a/src/js/game/hud/parts/settings_menu.js +++ b/src/js/game/hud/parts/settings_menu.js @@ -1,13 +1,11 @@ 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 { InputReceiver } from "../../../core/input_receiver"; import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; import { T } from "../../../translations"; import { StaticMapEntityComponent } from "../../components/static_map_entity"; -import { ItemProcessorComponent } from "../../components/item_processor"; import { BeltComponent } from "../../components/belt"; -import { IS_DEMO } from "../../../core/config"; export class HUDSettingsMenu extends BaseHUDPart { createElements(parent) { @@ -57,16 +55,7 @@ export class HUDSettingsMenu extends BaseHUDPart { } 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(); - // } } goToSettings() { @@ -102,7 +91,6 @@ export class HUDSettingsMenu extends BaseHUDPart { show() { this.visible = true; document.body.classList.add("ingameDialogOpen"); - // this.background.classList.add("visible"); this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60); diff --git a/src/js/game/hud/parts/statistics.js b/src/js/game/hud/parts/statistics.js index e1a747a2..e15af4fb 100644 --- a/src/js/game/hud/parts/statistics.js +++ b/src/js/game/hud/parts/statistics.js @@ -1,5 +1,5 @@ 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 { enumAnalyticsDataSource } from "../../production_analytics"; import { BaseHUDPart } from "../base_hud_part"; @@ -7,6 +7,14 @@ import { DynamicDomAttach } from "../dynamic_dom_attach"; import { enumDisplayMode, HUDShapeStatisticsHandle } from "./statistics_handle"; 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 { createElements(parent) { this.background = makeDiv(parent, "ingame_HUD_Statistics", ["ingameDialog"]); diff --git a/src/js/game/map.js b/src/js/game/map.js index 3f8a16f8..b0992627 100644 --- a/src/js/game/map.js +++ b/src/js/game/map.js @@ -5,7 +5,6 @@ import { Entity } from "./entity"; import { createLogger } from "../core/logging"; import { BaseItem } from "./base_item"; import { MapChunkView } from "./map_chunk_view"; -import { randomInt } from "../core/utils"; import { BasicSerializableObject, types } from "../savegame/serialization"; const logger = createLogger("map"); diff --git a/src/js/game/map_chunk.js b/src/js/game/map_chunk.js index dd9ba81d..84b9e47a 100644 --- a/src/js/game/map_chunk.js +++ b/src/js/game/map_chunk.js @@ -28,25 +28,13 @@ export class MapChunk { this.tileY = y * globalConfig.mapChunkSize; /** @type {Array>} */ - this.contents = make2DUndefinedArray( - globalConfig.mapChunkSize, - globalConfig.mapChunkSize, - "map-chunk@" + this.x + "|" + this.y - ); + this.contents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize); /** @type {Array>} */ - this.wireContents = make2DUndefinedArray( - globalConfig.mapChunkSize, - globalConfig.mapChunkSize, - "map-chunk-wires@" + this.x + "|" + this.y - ); + this.wireContents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize); /** @type {Array>} */ - this.lowerLayer = make2DUndefinedArray( - globalConfig.mapChunkSize, - globalConfig.mapChunkSize, - "map-chunk-lower@" + this.x + "|" + this.y - ); + this.lowerLayer = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize); /** @type {Array} */ this.containedEntities = []; diff --git a/src/js/game/map_chunk_view.js b/src/js/game/map_chunk_view.js index c3f09b39..491939f4 100644 --- a/src/js/game/map_chunk_view.js +++ b/src/js/game/map_chunk_view.js @@ -1,15 +1,6 @@ import { MapChunk } from "./map_chunk"; import { GameRoot } from "./root"; -import { globalConfig } from "../core/config"; 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 { /** diff --git a/src/js/globals.d.ts b/src/js/globals.d.ts index cc6118ca..3f842e7e 100644 --- a/src/js/globals.d.ts +++ b/src/js/globals.d.ts @@ -131,22 +131,24 @@ declare interface Math { degrees(number): number; } +declare type Class = new (...args: any[]) => T; + declare interface String { padStart(size: number, fill?: string): string; padEnd(size: number, fill: string): string; } declare interface FactoryTemplate { - entries: Array T>; + entries: Array>; entryIds: Array; idToEntry: any; getId(): string; getAllIds(): Array; - register(entry: new (...args: any[]) => T): void; + register(entry: Class): void; hasId(id: string): boolean; - findById(id: string): new (...args: any[]) => T; - getEntries(): Array T>; + findById(id: string): Class; + getEntries(): Array>; getNumEntries(): number; } @@ -156,10 +158,10 @@ declare interface SingletonFactoryTemplate { getId(): string; getAllIds(): Array; - register(classHandle: new (...args: any[]) => T): void; + register(classHandle: Class): void; hasId(id: string): boolean; findById(id: string): T; - findByClass(classHandle: new (...args: any[]) => T): T; + findByClass(classHandle: Class): T; getEntries(): Array; getNumEntries(): number; } diff --git a/src/js/platform/game_analytics.js b/src/js/platform/game_analytics.js index ea6aa293..765b2d67 100644 --- a/src/js/platform/game_analytics.js +++ b/src/js/platform/game_analytics.js @@ -1,8 +1,6 @@ -/* typehints:start */ -import { Application } from "../application"; -import { ShapeDefinition } from "../game/shape_definition"; -import { Savegame } from "../savegame/savegame"; -/* typehints:end */ +/** + * @typedef {import("../application").Application} Application + */ export class GameAnalyticsInterface { constructor(app) { diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index 50c34e0e..2167c77c 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -221,7 +221,7 @@ export const allApplicationSettings = [ }), 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, textGetter: rate => rate + " Hz", category: enumCategories.advanced, diff --git a/src/js/savegame/savegame.js b/src/js/savegame/savegame.js index 1db813d7..2a7102a9 100644 --- a/src/js/savegame/savegame.js +++ b/src/js/savegame/savegame.js @@ -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 { ExplainedResult } from "../core/explained_result"; import { SavegameSerializer } from "./savegame_serializer"; @@ -18,20 +13,29 @@ import { SavegameInterface_V1005 } from "./schemas/1005"; 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 { /** * * @param {Application} app * @param {object} param0 * @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 }) { super(app, "savegame-" + internalId + ".bin"); this.internalId = internalId; this.metaDataRef = metaDataRef; - /** @type {import("./savegame_typedefs").SavegameData} */ + /** @type {SavegameData} */ this.currentData = this.getDefaultData(); assert( @@ -65,7 +69,7 @@ export class Savegame extends ReadWriteProxy { /** * Returns the savegames default data - * @returns {import("./savegame_typedefs").SavegameData} + * @returns {SavegameData} */ getDefaultData() { return { @@ -78,7 +82,7 @@ export class Savegame extends ReadWriteProxy { /** * Migrates the savegames data - * @param {import("./savegame_typedefs").SavegameData} data + * @param {SavegameData} data */ migrate(data) { if (data.version < 1000) { @@ -115,7 +119,7 @@ export class Savegame extends ReadWriteProxy { /** * Verifies the savegames data - * @param {import("./savegame_typedefs").SavegameData} data + * @param {SavegameData} data */ verify(data) { if (!data.dump) { @@ -140,7 +144,7 @@ export class Savegame extends ReadWriteProxy { } /** * Returns the statistics of the savegame - * @returns {import("./savegame_typedefs").SavegameStats} + * @returns {SavegameStats} */ getStatistics() { return this.currentData.stats; @@ -163,7 +167,7 @@ export class Savegame extends ReadWriteProxy { /** * Returns the current game dump - * @returns {import("./savegame_typedefs").SerializedGame} + * @returns {SerializedGame} */ getCurrentDump() { return this.currentData.dump; diff --git a/src/js/savegame/savegame_manager.js b/src/js/savegame/savegame_manager.js index e3052806..42e56734 100644 --- a/src/js/savegame/savegame_manager.js +++ b/src/js/savegame/savegame_manager.js @@ -7,31 +7,21 @@ const logger = createLogger("savegame_manager"); const Rusha = require("rusha"); +/** + * @typedef {import("./savegame_typedefs").SavegamesData} SavegamesData + * @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata + */ + /** @enum {string} */ export const enumLocalSavegameStatus = { offline: "offline", synced: "synced", }; -/** - * @typedef {{ - * lastUpdate: number, - * version: number, - * internalId: string, - * level: number - * }} SavegameMetadata - * - * @typedef {{ - * version: number, - * savegames: Array - * }} SavegamesData - */ - export class SavegameManager extends ReadWriteProxy { constructor(app) { super(app, "savegames.bin"); - /** @type {SavegamesData} */ this.currentData = this.getDefaultData(); } diff --git a/src/js/savegame/savegame_serializer.js b/src/js/savegame/savegame_serializer.js index eff802a0..92db738b 100644 --- a/src/js/savegame/savegame_serializer.js +++ b/src/js/savegame/savegame_serializer.js @@ -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 { createLogger } from "../core/logging"; -// import { BuildingComponent } from "../components/impl/building"; import { gComponentRegistry } from "../core/global_registries"; 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"); /** - * Allows to serialize a savegame + * Serializes a savegame */ export class SavegameSerializer { constructor() { @@ -26,7 +28,7 @@ export class SavegameSerializer { * @returns {object} */ generateDumpFromGameRoot(root, sanityChecks = true) { - // Now store generic savegame payload + /** @type {SerializedGame} */ const data = { camera: root.camera.serialize(), time: root.time.serialize(), @@ -35,11 +37,10 @@ export class SavegameSerializer { hubGoals: root.hubGoals.serialize(), pinnedShapes: root.hud.parts.pinnedShapes.serialize(), waypoints: root.hud.parts.waypoints.serialize(), + entities: this.internal.serializeEntityArray(root.entityMgr.entities), beltPaths: root.systemMgr.systems.belt.serializePaths(), }; - data.entities = this.internal.serializeEntityArray(root.entityMgr.entities); - if (!G_IS_RELEASE) { if (sanityChecks) { // Sanity check @@ -55,7 +56,7 @@ export class SavegameSerializer { /** * Verifies if there are logical errors in the savegame - * @param {object} savegame + * @param {SerializedGame} savegame * @returns {ExplainedResult} */ verifyLogicalErrors(savegame) { @@ -66,47 +67,44 @@ export class SavegameSerializer { const seenUids = []; // Check for duplicate UIDS - for (const entityListId in savegame.entities) { - for (let i = 0; i < savegame.entities[entityListId].length; ++i) { - const list = savegame.entities[entityListId][i]; - for (let k = 0; k < list.length; ++k) { - const entity = list[k]; - const uid = entity.uid; - if (!Number.isInteger(uid)) { - return ExplainedResult.bad("Entity has invalid uid: " + uid); - } - if (seenUids.indexOf(uid) >= 0) { - return ExplainedResult.bad("Duplicate uid " + uid); - } - seenUids.push(uid); + for (let i = 0; i < savegame.entities.length; ++i) { + /** @type {Entity} */ + const entity = savegame.entities[i]; - // Verify components - if (!entity.components) { - return ExplainedResult.bad( - "Entity is missing key 'components': " + JSON.stringify(entity) - ); - } - const components = entity.components; - for (const componentId in components) { - // Verify component data - const componentData = components[componentId]; - const componentClass = gComponentRegistry.findById(componentId); + const uid = entity.uid; + if (!Number.isInteger(uid)) { + return ExplainedResult.bad("Entity has invalid uid: " + uid); + } + if (seenUids.indexOf(uid) >= 0) { + return ExplainedResult.bad("Duplicate uid " + uid); + } + seenUids.push(uid); - // Check component id is known - if (!componentClass) { - return ExplainedResult.bad("Unknown component id: " + componentId); - } + // Verify components + if (!entity.components) { + return ExplainedResult.bad("Entity is missing key 'components': " + JSON.stringify(entity)); + } - // Check component data is ok - const componentVerifyError = /** @type {typeof Component} */ (componentClass).verify( - componentData - ); - if (componentVerifyError) { - return ExplainedResult.bad( - "Component " + componentId + " has invalid data: " + componentVerifyError - ); - } - } + const components = entity.components; + for (const componentId in components) { + const componentClass = gComponentRegistry.findById(componentId); + + // Check component id is known + if (!componentClass) { + return ExplainedResult.bad("Unknown component id: " + componentId); + } + + // Verify component data + const componentData = components[componentId]; + const componentVerifyError = /** @type {StaticComponent} */ (componentClass).verify( + componentData + ); + + // Check component data is ok + if (componentVerifyError) { + return ExplainedResult.bad( + "Component " + componentId + " has invalid data: " + componentVerifyError + ); } } } @@ -116,7 +114,7 @@ export class SavegameSerializer { /** * Tries to load the savegame from a given dump - * @param {import("./savegame_typedefs").SerializedGame} savegame + * @param {SerializedGame} savegame * @param {GameRoot} root * @returns {ExplainedResult} */ diff --git a/src/js/savegame/savegame_typedefs.js b/src/js/savegame/savegame_typedefs.js index 642865cd..f5bb08c2 100644 --- a/src/js/savegame/savegame_typedefs.js +++ b/src/js/savegame/savegame_typedefs.js @@ -1,15 +1,8 @@ -import { Entity } from "../game/entity"; - -/** - * @typedef {{ - * }} SavegameStats - */ - /** + * @typedef {import("../game/entity").Entity} Entity + * + * @typedef {{}} SavegameStats * - */ - -/** * @typedef {{ * camera: any, * time: any, @@ -21,13 +14,25 @@ import { Entity } from "../game/entity"; * entities: Array, * beltPaths: Array * }} SerializedGame - */ - -/** + * * @typedef {{ * version: number, * dump: SerializedGame, * stats: SavegameStats, * lastUpdate: number, * }} SavegameData + * + * @typedef {{ + * lastUpdate: number, + * version: number, + * internalId: string, + * level: number + * }} SavegameMetadata + * + * @typedef {{ + * version: number, + * savegames: Array + * }} SavegamesData */ + +export default {}; diff --git a/src/js/savegame/schemas/1001.js b/src/js/savegame/schemas/1001.js index 3af5eebe..e07c25cc 100644 --- a/src/js/savegame/schemas/1001.js +++ b/src/js/savegame/schemas/1001.js @@ -43,6 +43,9 @@ export class SavegameInterface_V1001 extends SavegameInterface_V1000 { for (let i = 0; i < entities.length; ++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 beltComp = entity.components.Belt; if (staticComp) { diff --git a/src/js/savegame/serialization_data_types.js b/src/js/savegame/serialization_data_types.js index 86b177c1..0f9b4542 100644 --- a/src/js/savegame/serialization_data_types.js +++ b/src/js/savegame/serialization_data_types.js @@ -4,7 +4,7 @@ import { BasicSerializableObject } from "./serialization"; /* typehints:end */ import { Vector } from "../core/vector"; -import { round4Digits, schemaObject, accessNestedPropertyReverse } from "../core/utils"; +import { round4Digits } from "../core/utils"; export const globalJsonSchemaDefs = {}; /** @@ -28,6 +28,19 @@ export function schemaToJsonSchema(schema) { 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 */ @@ -75,23 +88,6 @@ export class BaseDataType { return { $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(); } /** diff --git a/src/js/states/main_menu.js b/src/js/states/main_menu.js index 927f02f6..3d39e826 100644 --- a/src/js/states/main_menu.js +++ b/src/js/states/main_menu.js @@ -1,11 +1,10 @@ import { GameState } from "../core/game_state"; 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 { makeDiv, makeButtonElement, formatSecondsToTimeAgo, - generateFileDownload, waitNextFrame, isSupportedBrowser, makeButton, @@ -14,9 +13,29 @@ import { import { ReadWriteProxy } from "../core/read_write_proxy"; import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; import { T } from "../translations"; -import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper"; 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 { constructor() { @@ -128,7 +147,6 @@ export class MainMenuState extends GameState { const closeLoader = this.dialogs.showLoadingDialog(); const reader = new FileReader(); reader.addEventListener("load", event => { - // @ts-ignore const contents = event.target.result; let realContent; @@ -394,7 +412,7 @@ export class MainMenuState extends GameState { } /** - * @param {object} game + * @param {SavegameMetadata} game */ resumeGame(game) { this.app.analytics.trackUiClick("resume_game"); @@ -419,7 +437,7 @@ export class MainMenuState extends GameState { } /** - * @param {object} game + * @param {SavegameMetadata} game */ deleteGame(game) { this.app.analytics.trackUiClick("delete_game"); @@ -447,7 +465,7 @@ export class MainMenuState extends GameState { } /** - * @param {object} game + * @param {SavegameMetadata} game */ downloadGame(game) { this.app.analytics.trackUiClick("download_game"); diff --git a/src/js/states/preload.js b/src/js/states/preload.js index f1551893..0f47e8d6 100644 --- a/src/js/states/preload.js +++ b/src/js/states/preload.js @@ -1,6 +1,6 @@ import { GameState } from "../core/game_state"; import { createLogger } from "../core/logging"; -import { findNiceValue, waitNextFrame } from "../core/utils"; +import { findNiceValue } from "../core/utils"; import { cachebust } from "../core/cachebust"; import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper"; import { T, autoDetectLanguageId, updateApplicationLanguage } from "../translations"; @@ -228,11 +228,7 @@ export class PreloadState extends GameState { this.statusBar.style.width = percentage + "%"; this.statusBarText.innerText = findNiceValue(percentage) + "%"; - if (G_IS_DEV) { - return Promise.resolve(); - } return Promise.resolve(); - // return waitNextFrame(); } showFailMessage(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")) { 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() { diff --git a/translations/base-fr.yaml b/translations/base-fr.yaml index 34ce2944..4b195673 100644 --- a/translations/base-fr.yaml +++ b/translations/base-fr.yaml @@ -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. 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. - 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] @@ -59,11 +59,11 @@ steamPage: [list] [*] 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) - [*] Un mode histoire où les batîments ont un coût - [*] Générateur de carte configurable (configuration des ressources/formes taille/densitée, graine et plus) + [*] Puzzles (Délivrer la forme requise avec une zone limitée/jeu de bâtiments) + [*] Un mode histoire où les bâtiments ont un coût + [*] Générateur de carte configurable (configuration des ressources/formes/taille/densitée, seed et plus) [*] 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 ! [/list] @@ -127,7 +127,7 @@ global: control: CTRL alt: ALT escape: ESC - shift: SHIFT + shift: MAJ space: ESPACE demoBanners: @@ -140,13 +140,13 @@ mainMenu: play: Jouer changelog: Historique importSavegame: Importer - openSourceHint: Ce jeu est open source! + openSourceHint: Ce jeu est open source ! discordLink: Serveur Discord officiel - helpTranslate: Contribuez à la traduction! + helpTranslate: Contribuez à la traduction ! # This is shown when using firefox and other browsers which are not supported. 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 savegameLevelUnknown: Niveau inconnu @@ -234,12 +234,12 @@ dialogs: massDeleteConfirm: title: Confirmation de suppression desc: >- - Vous allez supprimer pas mal de bâtiments ( pour être exact) ! Êtes vous certains de vouloir faire cela ? + Vous allez supprimer pas mal de bâtiments ( pour être exact) ! Êtes vous certains de vouloir faire ça ? massCutConfirm: title: Confirmer la coupure desc: >- - Vous vous apprêtez à couper beaucoup de bâtiments ( pour être précis) ! Êtes-vous certains de vouloir faire cela ? + Vous vous apprêtez à couper beaucoup de bâtiments ( pour être précis) ! Êtes-vous certains de vouloir faire ça ? blueprintsNotUnlocked: title: Pas encore débloqué @@ -351,10 +351,10 @@ ingame: description: Affiche le nombre de formes stockées dans votre bâtiment central. produced: 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: 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. # Displays the shapes per minute, e.g. '523 / m' @@ -398,7 +398,7 @@ ingame: Connectez l'extracteur avec un convoyeur vers votre centre !

Astuce: Cliquez et faites glisser le convoyeur avec votre souris ! 1_3_expand: >- - Ceci n'est PAS un jeu incrémental et inactif ! Construisez plus d'extracteurs et de convoyeurs pour atteindre plus vite votre votre but.

Astuce: Gardez SHIFT enfoncé pour placer plusieurs extracteurs, et utilisez R pour les faire pivoter. + Ceci n'est PAS un jeu incrémental et inactif ! Construisez plus d'extracteurs et de convoyeurs pour atteindre plus vite votre votre but.

Astuce: Gardez MAJ enfoncé pour placer plusieurs extracteurs, et utilisez R pour les faire pivoter. colors: red: Rouge @@ -487,8 +487,8 @@ buildings: name: Pivoteur inversé description: Fait pivoter une forme de 90 degrés vers la gauche. fl: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: Pivoteur (180) + description: Fait pivoter les formes de 90 degrés. stacker: default: @@ -707,9 +707,9 @@ settings: Affiche ou non le bouton 'Afficher un indice' dans le coin inférieur gauche. language: - title: Langage + title: Langue 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: title: Vitesse de déplacement diff --git a/translations/base-hu.yaml b/translations/base-hu.yaml index ce22403c..83d3fb08 100644 --- a/translations/base-hu.yaml +++ b/translations/base-hu.yaml @@ -155,6 +155,7 @@ mainMenu: savegameLevel: . szint savegameLevelUnknown: Ismeretlen szint + dialogs: buttons: ok: OK @@ -327,7 +328,7 @@ ingame: # Notifications on the lower right notifications: 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 shop: @@ -353,7 +354,7 @@ ingame: title: Gyártva description: Az összes eddig legyártott alakzatod, beleértve a köztes alakzatokat is. delivered: - title: Kézbesítve + title: Beszállítva 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. @@ -364,8 +365,8 @@ ingame: settingsMenu: playtime: Játékidő - buildingsPlaced: Épület - beltsPlaced: Futószalag + buildingsPlaced: Épületek száma: + beltsPlaced: Futószalagok hossza: buttons: continue: Folytatás @@ -406,7 +407,7 @@ ingame: blue: Kék yellow: Sárga purple: Lila - cyan: Cián + cyan: Világoskék white: Fehér uncolored: Színezetlen black: Fekete @@ -434,91 +435,91 @@ shopUpgrades: buildings: belt: default: - name: &belt Conveyor Belt - description: Transports items, hold and drag to place multiple. + name: &belt Futószalag + description: Elemeket szállít, tartsd nyomva az egérgombot egyszerre több lerakásához. miner: # Internal name for the Extractor default: - name: &miner Extractor - description: Place over a shape or color to extract it. + name: &miner Bányász + description: Tedd egy alakzatra vagy színre a kibányászásához. chainable: - name: Extractor (Chain) - description: Place over a shape or color to extract it. Can be chained. + name: Bányász (összekapcsolható) + 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 default: - name: &underground_belt Tunnel - description: Allows to tunnel resources under buildings and belts. + name: &underground_belt Alagút + description: Segítségével futószalagok és épületek alatt átvezethetők az elemek. tier2: - name: Tunnel Tier II - description: Allows to tunnel resources under buildings and belts. + name: Alagút II + description: Segítségével futószalagok és épületek alatt átvezethetők az elemek. splitter: # Internal name for the Balancer default: - name: &splitter Balancer - description: Multifunctional - Evenly distributes all inputs onto all outputs. + name: &splitter Elosztó + description: Többfunkciós - Egyenletesen szétosztja a bementeket a kimenetekre. compact: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: Egyesítő (kompakt) + description: Két futószalagot egyesít. compact-inverse: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: Egyesítő (kompakt) + description: Két futószalagot egyesít. cutter: default: - name: &cutter Cutter - description: Cuts shapes from top to bottom and outputs both halfs. If you use only one part, be sure to destroy the other part or it will stall! + name: &cutter Vágó + description: Függőlegesen félbevágja az alakzatokat. Ha csak az egyik felet akarod használni, ne felejtsd el a másikat kukába küldeni, különben eldugítja! quad: - name: Cutter (Quad) - description: Cuts shapes into four parts. If you use only one part, be sure to destroy the other part or it will stall! + name: Vágó (negyedelő) + description: Négyfelé vágja az alakzatokat. Cuts shapes into four parts. Ha csak az egyik felet akarod használni, ne felejtsd el a többit a kukába küldeni, különben eldugítja! rotater: default: - name: &rotater Rotate - description: Rotates shapes clockwise by 90 degrees. + name: &rotater Forgató + description: Elforgatja az alakzatot óramutató irányában 90 fokkal. ccw: - name: Rotate (CCW) - description: Rotates shapes counter clockwise by 90 degrees. + name: Forgató (fordított) + description: Elforgatja az alakzatot óramutatóval ellentétesen 90 fokkal. fl: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: Forgató (180) + description: Elforgatja az alakzatot 180 fokkal. stacker: default: - name: &stacker Stacker - description: Stacks both items. If they can not be merged, the right item is placed above the left item. + name: &stacker Egyesítő + description: Egyesít két elemet. Ha nem lehet összeilleszteni őket, a jobboldali elem a baloldali tetejére kerül. mixer: default: - name: &mixer Color Mixer - description: Mixes two colors using additive blending. + name: &mixer Színkeverő + description: Összekever két színt összeadó színkeveréssel. painter: default: - name: &painter Painter - description: &painter_desc Colors the whole shape on the left input with the color from the right input. + name: &painter Festő + description: &painter_desc Beszínezi az alakzatot a baloldali bemeneten a jobboldali bemeneten érkező színnel. double: - name: Painter (Double) - description: Colors the shapes on the left inputs with the color from the top input. + name: Festő (Dupla) + description: Beszínezi az alakzatokat a baloldali bemeneteken a fenti bemeneten érkező színnel. quad: - name: Painter (Quad) - description: Allows to color each quadrant of the shape with a different color. + name: Festő (Négyszeres) + description: Az alakzat négy negyedét különböző színekkel lehet vele színezni. mirrored: name: *painter description: *painter_desc trash: default: - name: &trash Trash - description: Accepts inputs from all sides and destroys them. Forever. + name: &trash Kuka + description: Bármelyik irányból lehet hozzá csatlakozni, és megsemmisíti a beleküldött elemeket. Örökre. storage: - name: Storage - description: Stores excess items, up to a given capacity. Can be used as an overflow gate. + name: Tároló + description: Tárolja a fölös elemeket egy bizonyos kapacitásig. hub: deliver: Deliver toUnlock: to unlock diff --git a/translations/base-ja.yaml b/translations/base-ja.yaml index edfc7219..fe362132 100644 --- a/translations/base-ja.yaml +++ b/translations/base-ja.yaml @@ -411,7 +411,7 @@ ingame: cyan: シアン white: 白 uncolored: 無色 - black: Black + black: 黒 shapeViewer: title: レイヤー empty: 空 @@ -491,8 +491,8 @@ buildings: name: 回転機 (逆) description: 形を反時計回り方向に90度回転します。 fl: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: 回転機 (180) + description: 形を180度回転します。 stacker: default: @@ -533,20 +533,20 @@ buildings: advanced_processor: default: name: Color Inverter - description: Accepts a color or shape and inverts it. + description: 入力された色や形の色を反転します。 energy_generator: deliver: Deliver toGenerateEnergy: For default: - name: Energy Generator - description: Generates energy by consuming shapes. + name: エネルギー発電機 + description: 入力された形を使って、エネルギーを発電します。 wire_crossings: default: - name: Wire Splitter - description: Splits a energy wire into two. + name: ワイヤー分配機 + description: 1つのワイヤーを2つのワイヤーに分配します。 merger: - name: Wire Merger - description: Merges two energy wires into one. + name: ワイヤー合流機 + description: 2つのワイヤーを1つのワイヤーに合流します。 storyRewards: # Those are the rewards gained from completing the store diff --git a/translations/base-pt-BR.yaml b/translations/base-pt-BR.yaml index 14af1915..731bd008 100644 --- a/translations/base-pt-BR.yaml +++ b/translations/base-pt-BR.yaml @@ -23,6 +23,9 @@ steamPage: # 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. + # 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. # NOTICE: # - 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] 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. 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] [/list] - discordLink: Discord Oficial - Converse comigo! - global: loading: Carregando error: Erro @@ -134,14 +136,19 @@ demoBanners: # This is the "advertisement" shown in the main menu and other various places title: Versão Demo intro: >- - Pegue a versão completa para desbloquear todas os recursos + Pegue a versão completa para desbloquear todas os recursos! mainMenu: play: Jogar + continue: Continuar + newGame: Novo jogo changelog: Changelog + subreddit: Reddit importSavegame: Importar openSourceHint: Esse jogo tem código aberto! discordLink: Discord oficial + helpTranslate: Ajude a traduzir! + madeBy: Feito por # This is shown when using firefox and other browsers which are not supported. browserWarning: >- @@ -150,12 +157,6 @@ mainMenu: savegameLevel: Nível savegameLevelUnknown: Nível desconhecido - helpTranslate: Ajude a traduzir! - continue: Continuar - newGame: Novo jogo - madeBy: Feito por - subreddit: Reddit - dialogs: buttons: ok: OK @@ -163,11 +164,11 @@ dialogs: cancel: Cancelar later: Voltar restart: Reiniciar - reset: Reset + reset: Resetar getStandalone: Obter versão completa deleteGame: Sei o que estou fazendo viewUpdate: Atualizações - showUpgrades: Ver melhorias + showUpgrades: Melhorias showKeybindings: Controles importSavegameError: @@ -198,7 +199,7 @@ dialogs: restartRequired: title: Ação necessária text: >- - Voce precisa reiniciar o jogo para aplicar as mudanças. + Você precisa reiniciar o jogo para aplicar as mudanças. editKeybinding: title: Alterar tecla @@ -228,7 +229,7 @@ dialogs: upgradesIntroduction: title: Desbloquear melhorias desc: >- - Todas as formas que você produz podem ser usadas para desbloquear melhorias - Não destrua suas antigas fábricas!! + Todas as formas que você produz podem ser usadas para desbloquear melhorias - Não destrua suas antigas fábricas!! O guia de melhorias pode ser encontrado no canto superior direito da tela. massDeleteConfirm: @@ -236,6 +237,16 @@ dialogs: desc: >- Você está deletando vários objetos ( para ser exato)! Você quer continuar? + massCutConfirm: + title: Confirmar corte + desc: >- + Você está cortando vários objetos ( 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: title: Não desbloqueado ainda desc: >- @@ -245,42 +256,31 @@ dialogs: title: Teclas úteis desc: >- Este jogo possui muitas combinações de teclas que facilitam a construção de grandes fábricas - Aqui estão algumas, certifique-se de verificar as combinações de teclas !

+ Aqui estão algumas, certifique-se de verificar as combinações de teclas!

CTRL + Arrastar: Seleciona área para copiar/deletar.
SHIFT: Mantenha pressionado para colocar várias construções.
ALT: Inverte as posições.
createMarker: title: Nova Marcação - desc: Dê um nome com significado, também pode adicionar um pequeno código de uma forma. (Pode ser gerado aqui) titleEdit: Editar Marcador + desc: Dê um nome com significado, também pode adicionar um pequeno código de uma forma. (Pode ser gerado aqui) markerDemoLimit: - desc: >- - 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 ( para ser exato)! Você quer continuar? + desc: Você só pode criar dois marcadores na versão demo. Adquira a versão completa para marcadores ilimitados! exportScreenshotWarning: title: Exportar captura de tela - 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! - - massCutInsufficientConfirm: - title: Confirmar Corte - desc: You can not afford to paste this area! Are you sure you want to cut it? + 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! ingame: # This is shown in the top left corner and displays useful keybindings in # every situation keybindingsOverlay: moveMap: Mover - + selectBuildings: Selecionar área stopPlacement: Parar - rotateBuilding: Rotação + rotateBuilding: Rotacionar placeMultiple: Colocar vários reverseOrientation: Inverter orientação disableAutoOrientation: Desligar orientação automática @@ -288,17 +288,27 @@ ingame: placeBuilding: Construir objeto createMarker: Criar marcador delete: Destruir - selectBuildings: Selecionar área pasteLastBlueprint: Colar último projeto - lockBeltDirection: Ativar Planejador de Esteiras - plannerSwitchSide: Flip planner side + plannerSwitchSide: Girar Planejador cutSelection: Cortar copySelection: Copiar clearSelection: Limpar Seleção pipette: Conta-Gotas 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 # from the toolbar) buildingPlacement: @@ -324,7 +334,7 @@ ingame: levelCompleteNotification: # is replaced by the actual level, so this gets 'Level 03' for example. levelTitle: Nível - completed: Completado + completed: Concluído unlockText: Desbloqueado ! buttonNextLevel: Próximo Nível @@ -336,13 +346,14 @@ ingame: # The "Upgrades" window shop: title: Melhorias - buttonUnlock: Comprar + buttonUnlock: Melhorar # Gets replaced to e.g. "Tier IX" tier: Nível # The roman number for each tier tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X] + maximumLevel: NÍVEL MÁXIMO (Velocidade x) # The "Statistics" window @@ -389,60 +400,58 @@ ingame: waypoints: waypoints: Marcadores hub: HUB - 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.

Pressione para criar um marcador a partir da exibição atual ou clique com o botão direito do mouse 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.

Pressione para criar um marcador a partir da exibição atual ou clique com o botão direito do mouse para criar um marcador no local selecionado. creationSuccessNotification: Marcador criado. - # Interactive tutorial - interactiveTutorial: - title: Tutorial - hints: - 1_1_extractor: Coloque um extrator em cima de uma fonte de círculo para extraí-lo! - 1_2_conveyor: >- - Conecte o extrator com uma esteira transportadora até a sua base!

Dica, clique e arraste a esteira com o mouse! - - 1_3_expand: >- - Este NÃO é um jogo inativo! Construa mais extratores e esteiras para concluir o objetivo mais rapidamente.

Dica, segure SHIFT para colocar vários extratores e use R para girá-los. - - colors: - red: Vermelho - green: Verde - blue: Azul - yellow: Amarelo - purple: Roxo - cyan: Ciano - white: Branco - black: Preto - uncolored: Sem cor + # Shape viewer shapeViewer: title: Camadas empty: Vazio copyKey: Copiar Chave + # Interactive tutorial + interactiveTutorial: + title: Tutorial + hints: + 1_1_extractor: Coloque um extrator em cima de uma fonte de círculo para extraí-lo! + 1_2_conveyor: >- + Conecte o extrator com uma esteira transportadora até a sua base!

Dica, clique e arraste a esteira com o mouse! + + 1_3_expand: >- + Este NÃO é um jogo inativo! Construa mais extratores e esteiras para concluir o objetivo mais rapidamente.

Dica, segure SHIFT para colocar vários extratores e use R para girá-los. + # All shop upgrades shopUpgrades: belt: name: Esteiras, Distribuidores e Túneis description: Velocidade x → x - miner: name: Extração description: Velocidade x → x - processors: - name: Corte, Rotação & Montagem + name: Corte, Rotação e Montagem description: Velocidade x → x - painting: - name: Mistura & Pintura + name: Mistura e Pintura description: Velocidade x → x # Buildings and their name / description buildings: + hub: + deliver: Entregue + toUnlock: para desbloquear + levelShortcut: LVL + belt: default: name: &belt Esteira Transportadora 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 default: name: &miner Extrator @@ -477,10 +486,15 @@ buildings: cutter: default: name: &cutter Cortador - description: Corta as formas verticalmente e produz as duas metades. Se você usar apenas uma parte, não se esqueça de destruir a outra parte, ou ela irá parar a produção! + description: Corta as formas verticalmente e produz as duas metades. Se você usar apenas uma parte, não se esqueça de destruir a outra parte, ou ela irá parar a produção! quad: name: Cortador (Quádruplo) - description: Corta as formas em quatro partes. Se você usar apenas uma parte, não se esqueça de destruir as outras, ou ela irá parar a produção! + description: Corta as formas em quatro partes. Se você usar apenas uma parte, não se esqueça de destruir as outras, ou ela irá parar a produção! + + advanced_processor: + default: + name: &advanced_processor Inversor de Cor + description: Aceita uma cor ou forma e a inverte. rotater: default: @@ -490,8 +504,8 @@ buildings: name: Rotacionador (Anti-horário) description: Gira as formas no sentido anti-horário em 90 graus. fl: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: Rotacionador (180) + description: Gira as formas em 180 graus. stacker: default: @@ -507,16 +521,18 @@ buildings: default: name: &painter Pintor 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: name: *painter 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: default: name: &trash Lixo @@ -525,29 +541,22 @@ buildings: storage: name: Estoque 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: deliver: Entregar + + # This will be shown before the amount, so for example 'For 123 Energy' toGenerateEnergy: Para + default: name: &energy_generator Gerador de Energia description: Consome formas para gerar energia. + wire_crossings: default: name: &wire_crossings Divisor de Fios description: Divide um fio de energia em dois. + merger: name: Misturador de Fios description: Une dois fios de energia em um. @@ -556,11 +565,11 @@ storyRewards: # Those are the rewards gained from completing the store reward_cutter_and_trash: title: Cortando formas - desc: Voce desbloqueou cortador - corte de formas pela metade verticalmente independentemente de sua orientação!

Certifique-se de se livrar do lixo, ou então ele irá parar a produção - Para esse propósito, eu lhe dei uma lixeira, que destrói tudo o que você coloca nela! + desc: Você desbloqueou cortador - corte de formas pela metade verticalmente independentemente de sua orientação!

Certifique-se de se livrar do lixo, ou então ele irá parar a produção - Para esse propósito, eu lhe dei uma lixeira, que destrói tudo o que você coloca nela! reward_rotater: title: Rotação - desc: O rotacionador foi desbloqueado! Gira as formas no sentido horário em 90 graus. + desc: O rotacionador foi desbloqueado! Gira as formas no sentido horário em 90 graus. reward_painter: title: Pintura @@ -569,11 +578,11 @@ storyRewards: reward_mixer: title: Misturando cores - desc: O misturador foi desbloqueado - combine duas cores usando mistura aditiva com esta construção! + desc: O misturador foi desbloqueado - combine duas cores usando mistura aditiva com esta construção! reward_stacker: title: Empilhador - desc: Agora você pode combinar formas com o empilhador! Ambas as entradas são combinadas e, se puderem ser colocadas próximas uma da outra, serão fundidas . Caso contrário, a entrada direita é empilhada em cima da entrada esquerda! + desc: Agora você pode combinar formas com o empilhador! Ambas as entradas são combinadas e, se puderem ser colocadas próximas uma da outra, serão fundidas. Caso contrário, a entrada direita é empilhada em cima da entrada esquerda! reward_splitter: title: Distribuidor @@ -597,7 +606,8 @@ storyRewards: reward_splitter_compact: title: Distribuidor compacto - desc: Você desbloqueou uma variante compacta do Distribuidor - ele aceita duas entradas e as une em uma! + desc: >- + Você desbloqueou uma variante compacta do Distribuidor - ele aceita duas entradas e as une em uma! reward_cutter_quad: title: Cortador quádruplo @@ -605,7 +615,7 @@ storyRewards: reward_painter_double: title: Pintura dupla - desc: Você desbloqueou uma variante do pintor - funciona como o pintor regular, mas processa duas formas ao mesmo tempo , consumindo apenas uma cor em vez de duas! + desc: Você desbloqueou uma variante do pintor - funciona como o pintor regular, mas processa duas formas ao mesmo tempo, consumindo apenas uma cor em vez de duas! reward_painter_quad: title: Pintura quádrupla @@ -637,9 +647,9 @@ storyRewards: settings: title: opções categories: - general: General - userInterface: User Interface - advanced: Advanced + general: Geral + userInterface: Interface de Usuário + advanced: Avançado versionBadges: dev: Desenvolvedor @@ -653,23 +663,58 @@ settings: 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. scales: - super_small: Super pequeno + super_small: Super Pequeno small: Pequeno regular: Normal large: Grande 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: title: Sensibilidade do zoom description: >- Altera a sensibilidade do zoom (roda do mouse ou touchpad). sensitivity: - super_slow: Super lento + super_slow: Super Lento slow: Lento regular: Normal fast: 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: title: Tela Cheia description: >- @@ -678,7 +723,7 @@ settings: soundsMuted: title: Som description: >- - Se ligado o jogo fica mudo + Se ligado, o jogo fica mudo. musicMuted: title: Música @@ -689,7 +734,6 @@ settings: title: Tema description: >- Escolha o tema entre (Claro / Escuro). - themes: dark: Escuro light: Claro @@ -709,63 +753,30 @@ settings: 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. - 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: title: Túneis inteligentes description: >- - Quando colocados, irão remover automaticamente esteiras desnecessárias. - Isso também permite arrastar túneis e túneis em excesso serão removidos. + Quando colocados, irão remover automaticamente esteiras desnecessárias. Isso também permite arrastar túneis e túneis em excesso serão removidos. + vignette: - title: Vignette + title: Vinheta description: >- - Permite o modo vinheta que escurece os cantos da tela e facilita a - leitura do texto. + Permite o modo vinheta que escurece os cantos da tela e facilita a 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: title: Rotação por tipo de construção description: >- - 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. + 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. + + 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: title: Controles @@ -790,6 +801,7 @@ keybindings: mapMoveRight: Mover para direita mapMoveDown: Mover para baixo mapMoveLeft: Mover para a esquerda + mapMoveFaster: Mover mais rápido centerMap: Centralizar mapa mapZoomIn: Aproximar @@ -798,12 +810,12 @@ keybindings: menuOpenShop: Melhorias menuOpenStats: Estatísticas + menuClose: Fechar Menu toggleHud: Ocultar Interface toggleFPSInfo: Mostrar FPS e Debug Info switchLayers: Alternar Camadas - exportScreenshot: Exportar Base inteira como Imagem - + exportScreenshot: Exportar Base Inteira como Imagem belt: *belt splitter: *splitter underground_belt: *underground_belt @@ -824,41 +836,33 @@ keybindings: Modifier: Rotação anti-horária cycleBuildingVariants: Variações confirmMassDelete: Confirmar exclusão em massa + pasteLastBlueprint: Colar último projeto cycleBuildings: Trocar de construção + lockBeltDirection: Ativar planejador de correia + switchDirectionLockSide: >- + Planejador: Mudar de lado massSelectStart: Segure e arraste para começar massSelectSelectMultiple: Selecionar mais áreas massSelectCopy: Copiar área + massSelectCut: Cortar área placementDisableAutoOrientation: Desligar orientação automática placeMultiple: Permanecer no modo de construção 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: title: Sobre o jogo body: >- - Esse jogo tem código aberto e é desenvolvido por Tobias Springer (esse sou eu).

+ Esse jogo tem código aberto e é desenvolvido por Tobias Springer (esse sou eu).

- Se quiser contribuir, confira shapez.io no github.

+ Se quiser contribuir, confira shapez.io no github.

- O jogo não seria possível sem a comunidade incrível do Discord sobre - os meus jogos - Junte-se à comunidade no servidor do Discord!

+ O jogo não seria possível sem a comunidade incrível do Discord sobre os meus jogos - Junte-se à comunidade no servidor do Discord!

- A trilha sonora foi feita por Peppsen - Ele é demais.

+ A trilha sonora foi feita por Peppsen - Ele é demais.

- Finalmente, agradeço muito ao meu melhor amigo - Niklas - Sem nossas sessões de Factorio, - esse jogo nunca teria existido. + Finalmente, agradeço muito ao meu melhor amigo Niklas - Sem nossas sessões de Factorio, esse jogo nunca teria existido. changelog: title: Alterações diff --git a/translations/base-ru.yaml b/translations/base-ru.yaml index 61f1ebf3..90b9af40 100644 --- a/translations/base-ru.yaml +++ b/translations/base-ru.yaml @@ -488,8 +488,8 @@ buildings: name: Вращатель (Обр.) description: Поворачивает фигуры против часовой стрелки на 90 градусов. fl: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: Вращатель (180) + description: Вращает фигуры на 180 градусов. stacker: default: @@ -635,9 +635,9 @@ storyRewards: settings: title: Настройки categories: - general: General - userInterface: User Interface - advanced: Advanced + general: Основные + userInterface: Интерфейс + advanced: Продвинутые versionBadges: dev: Разработчик diff --git a/translations/base-zh-CN.yaml b/translations/base-zh-CN.yaml index 3aad1244..225619e0 100644 --- a/translations/base-zh-CN.yaml +++ b/translations/base-zh-CN.yaml @@ -176,7 +176,7 @@ mainMenu: # This is shown when using firefox and other browsers which are not supported. browserWarning: >- - 很抱歉, 本游戏在当前浏览器上可能运行缓慢! 使用chrome或者获取独立版以得到更好的体验。 + 很抱歉, 本游戏在当前浏览器上可能运行缓慢! 使用 Chrome 或者获取独立版以得到更好的体验。 savegameLevel: 第关 savegameLevelUnknown: @@ -232,7 +232,7 @@ dialogs: editKeybinding: title: 更改按键设置 - desc: 请按下你想要使用的按键,或者按下ESC键来取消设置。 + desc: 请按下你想要使用的按键,或者按下 ESC 键来取消设置。 resetKeybindingsConfirmation: title: 重置所有按键 @@ -244,11 +244,11 @@ dialogs: featureRestriction: title: 演示版 - desc: 你尝试使用了 功能。该功能在演示版中不可用。请考虑购买独立版以获得更好的体验。 + desc: 你尝试使用了功能。该功能在演示版中不可用。请考虑购买独立版以获得更好的体验。 oneSavegameLimit: title: 存档数量限制 - desc: 演示版中只能保存一份存档。 请删除旧存档或者获取独立版! + desc: 演示版中只能保存一份存档。请删除旧存档或者获取独立版! updateSummary: title: 更新啦! @@ -314,7 +314,7 @@ ingame: placeMultiple: 放置多个 reverseOrientation: 反向放置 disableAutoOrientation: 关闭自动定向 - toggleHud: 开关HUD + toggleHud: 开关 HUD placeBuilding: 放置建筑 createMarker: 创建地图标记 delete: 销毁 @@ -332,7 +332,7 @@ ingame: buildingPlacement: # Buildings can have different variants which are unlocked at later levels, # and this is the hint shown when there are multiple variants available. - cycleBuildingVariants: 按键以选择建筑变体. + cycleBuildingVariants: 按 键以选择建筑变体. # Shows the hotkey in the ui, e.g. "Hotkey: Q" hotkeyLabel: >- @@ -391,7 +391,7 @@ ingame: noShapesProduced: 你还没有生产任何图形。 # Displays the shapes per minute, e.g. '523 / m' - shapesPerMinute: 个/分钟 + shapesPerMinute: 个 / 分钟 # Settings menu, when you press "ESC" settingsMenu: @@ -419,7 +419,7 @@ ingame: waypoints: waypoints: 地图标记 hub: 基地 - description: 左键跳转到地图标记,右键删除地图标记。

在当前地点创建地图标记,或者在选定位置上右键创建地图标记. + description: 左键跳转到地图标记,右键删除地图标记。

在当前地点创建地图标记,或者在选定位置上右键创建地图标记. creationSuccessNotification: 成功创建地图标记。 # Interactive tutorial @@ -432,7 +432,7 @@ ingame: 1_3_expand: >- 这不是一个挂机游戏!建造更多的开采机和传送带来更快地完成目标。

- 提示:按住SHIFT键来放置多个开采机,用R键旋转它们。 + 提示:按住 SHIFT 键来放置多个开采机,用 R 键旋转它们。 colors: red: 红色 @@ -505,10 +505,10 @@ buildings: cutter: default: name: &cutter 切割机 - description: 将图形从上到下切开并输出。 如果你只需要其中一半,记得把另一半销毁掉,否则切割机会停止工作! + description: 将图形从上到下切开并输出。如果你只需要其中一半,记得把另一半销毁掉,否则切割机会停止工作! quad: name: 切割机(四向) - description: 将输入的图形切成四块。 如果你只需要其中一块,记得把其他的销毁掉,否则切割机会停止工作! + description: 将输入的图形切成四块。如果你只需要其中一块,记得把其他的销毁掉,否则切割机会停止工作! rotater: default: @@ -592,7 +592,7 @@ storyRewards: reward_painter: title: 上色 desc: >- - 恭喜!你解锁了上色机。 开采一些颜色 (就像你开采图形一样) 将其在上色机中与图形结合来将图形上色!

PS: 如果你患有色盲,可以在设置中启用色盲模式! + 恭喜!你解锁了上色机。开采一些颜色 (就像你开采图形一样) 将其在上色机中与图形结合来将图形上色!

PS:如果你患有色盲,可以在设置中启用色盲模式! reward_mixer: title: 混合颜色 @@ -612,7 +612,7 @@ storyRewards: reward_rotater_ccw: title: 逆时针旋转 - desc: 恭喜!你解锁了旋转机逆时针变体。这个变体可以逆时针旋转图形。选择旋转机然后按“T”键来选取这个变体。 + desc: 恭喜!你解锁了旋转机逆时针变体。这个变体可以逆时针旋转图形。选择旋转机然后按"T"键来选取这个变体。 reward_miner_chainable: title: 链式开采机 @@ -648,13 +648,13 @@ storyRewards: reward_blueprints: title: 蓝图 - desc: 你现在可以复制粘贴你的工厂的一部分了!按住CTRL键并拖动鼠标来选择一块区域,然后按C键复制。

粘贴并不是免费的,你需要使用蓝图图形来粘贴你的蓝图。蓝图图形是你刚刚交付的图形。 + desc: 你现在可以复制粘贴你的工厂的一部分了!按住 CTRL 键并拖动鼠标来选择一块区域,然后按C键复制。

粘贴并不是免费的,你需要使用蓝图图形来粘贴你的蓝图。蓝图图形是你刚刚交付的图形。 # Special reward, which is shown when there is no reward actually no_reward: title: 下一关 desc: >- - 这一关没有奖励,但是下一关有!

PS: 你生产过的所有图形都会被用来升级建筑。 + 这一关没有奖励,但是下一关有!

PS:你生产过的所有图形都会被用来升级建筑。 no_reward_freeplay: title: 下一关 @@ -729,21 +729,21 @@ settings: refreshRate: title: 模拟频率、刷新频率 description: >- - 如果你的显示器是144hz的,请在这里更改刷新频率,这样游戏可以正确地根据你的屏幕进行模拟。但是如果你的电脑性能不佳,提高刷新频率可能降低帧数。 + 如果你的显示器是 144Hz 的,请在这里更改刷新频率,这样游戏可以正确地根据你的屏幕进行模拟。但是如果你的电脑性能不佳,提高刷新频率可能降低帧数。 # 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. alwaysMultiplace: title: 多重放置 description: >- - 开启这个选项之后放下建筑将不会取消建筑选择。等同于一直按下SHIFT键。 + 开启这个选项之后放下建筑将不会取消建筑选择。等同于一直按下 SHIFT 键。 # description: >- # If enabled, all buildings will stay selected after placement until you cancel it. This is equivalent to holding SHIFT permanently. offerHints: title: 提示与教程 description: >- - 是否显示提示、教程以及一些其他的帮助理解游戏的UI元素。 + 是否显示提示、教程以及一些其他的帮助理解游戏的 UI 元素。 # description: >- # Whether to offer hints and tutorials while playing. Also hides certain UI elements onto a given level to make it easier to get into the game. @@ -799,7 +799,7 @@ settings: keybindings: title: 按键设置 hint: >- - 提示:使用CTRL、SHIFT、ALT! 这些建在放置建筑时有不同的效果。 + 提示:使用 CTRL、SHIFT、ALT!这些建在放置建筑时有不同的效果。 # hint: >- # Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different placement options. @@ -881,20 +881,20 @@ about: title: 关于游戏 # title: About this Game body: >- - 本游戏由Tobias Springer(我)开发,并且已经开源。

- 如果你想参与开发,请查看shapez.io on github

- 这个游戏的开发少不了热情的Discord社区。请加入我们的Discord 服务器

- 本游戏的音乐由Peppsen制作——他是个很棒的伙伴。

+ 本游戏的音乐由 Peppsen 制作——他是个很棒的伙伴。

- 最后,我想感谢我最好的朋友Niklas——如果没有与他的异星工厂(factorio)的游戏体验,shapez.io将不会存在。 + 最后,我想感谢我最好的朋友 Niklas ——如果没有与他的异星工厂(factorio)的游戏体验,shapez.io将不会存在。 changelog: title: 版本日志