diff --git a/src/css/common.scss b/src/css/common.scss index c768b117..e3d8087e 100644 --- a/src/css/common.scss +++ b/src/css/common.scss @@ -1,5 +1,4 @@ // Common classes and style - * { margin: 0; padding: 0; @@ -14,7 +13,6 @@ body { overflow: hidden; font-family: $mainFont; font-synthesis: none; - position: fixed; top: 0; left: 0; @@ -26,19 +24,15 @@ html { position: fixed; // scroll-behavior: smooth; background: $mainBgColor; - // Disable zooming and thus -ms-touch-action: pan-x, pan-y; touch-action: pan-x, pan-y; -ms-content-zooming: none; - top: 0; left: 0; bottom: 0; right: 0; - background: #dee1ea; - @include DarkThemeOverride { background: $darkModeGameBackground; } @@ -50,7 +44,6 @@ body { -moz-user-select: none; -ms-user-select: none; background: inherit !important; - text-transform: none; white-space: normal; word-break: normal; @@ -66,19 +59,17 @@ body { scrollbar-width: 6px; -webkit-font-smoothing: antialiased; // -webkit-overflow-scrolling: touch; /* stop scrolling immediately */ - -webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */ - -webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */ - + -webkit-touch-callout: none; + /* prevent callout to copy image, etc when tap to hold */ + -webkit-text-size-adjust: none; + /* prevent webkit from resizing text to fit */ // Internet explorer scrollbar-face-color: #888; scrollbar-track-color: rgba(255, 255, 255, 0.1); - // Firefox scrollbar-color: #cdd0d4 rgba(#000, 0.05); - overflow: hidden; @include Text; - &.externalAdOpen { &::before { text-transform: uppercase; @@ -97,7 +88,6 @@ body { justify-content: center; align-items: center; color: #fff; - @include InlineAnimation(1s ease-in-out infinite) { 50% { transform: scale(1.05); @@ -105,9 +95,7 @@ body { } } } - // For recording the bg video - // filter: blur(5px); // &::after { // position: fixed; @@ -122,20 +110,24 @@ body { } img { - -webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */ + -webkit-touch-callout: none; + /* prevent callout to copy image, etc when tap to hold */ } i { font-style: normal; } + b, strong { font-weight: normal; } + u, a { text-decoration: none; } + input, textarea, select { @@ -152,7 +144,6 @@ button { cursor: pointer; position: relative; @include TextShadow3D; - &.prefab_BuyButtonWithResources { display: flex; box-sizing: border-box; @@ -163,27 +154,23 @@ button { justify-content: center; align-items: center; @include S(width, 85px); - &.tooExpensive { color: $colorRedBright; background-color: #555; cursor: default; } - .cost_entry { display: flex; flex-grow: 1; justify-content: center; align-items: center; } - b { display: flex; flex-grow: 1; justify-content: center; align-items: center; } - &.tooExpensive { cursor: default !important; background-color: #565859 !important; @@ -214,7 +201,6 @@ button { // color: $accentColorDark; letter-spacing: 0.05em !important; // box-shadow: 0 #{D(1px)} #{D(2px)} 0 rgba(0, 10, 20, 0.2); - .keybinding { @include S(bottom, -2.5px); @include S(right, -2px); @@ -226,10 +212,13 @@ button { } ::selection { - background: $colorGreenBright; /* WebKit/Blink Browsers */ + background: $colorGreenBright; + /* WebKit/Blink Browsers */ } + ::-moz-selection { - background: $colorGreenBright; /* Gecko Browsers */ + background: $colorGreenBright; + /* Gecko Browsers */ } input[type="text"], @@ -244,35 +233,27 @@ input[type="email"] { background: lighten($mainBgColor, 8); color: #eee; text-align: left; - user-select: text !important; pointer-events: all !important; - @include Text; @include IncreasedClickArea(15px); @include S(border-radius, $globalBorderRadius); - &::placeholder { color: #fff; opacity: 0.4; } - transition: background-color 0.1s ease-in-out !important; @include TextShadow3D(#fff); @include BoxShadow3D(lighten($mainBgColor, 30)); - &:focus { @include BoxShadow3D(lighten($mainBgColor, 35)); } - &.errored { @include BoxShadow3D(mix(lighten($mainBgColor, 30), #f77, 25%)); - &:focus { @include BoxShadow3D(mix(lighten($mainBgColor, 50), #f77, 25%)); } } - &.input-token { @include SuperHeading; text-align: center; @@ -324,7 +305,6 @@ canvas { // &.unsmoothed { // } letter-spacing: 0 !important; - transform: translateZ(0); backface-visibility: hidden; -webkit-backface-visibility: hidden; @@ -404,13 +384,11 @@ canvas { align-items: center; justify-content: center; text-transform: uppercase; - @include Text; @include TextShadow3D; opacity: 1; z-index: 20; color: #393747; - &::after { content: " "; background: uiResource("loading.svg") center center / contain no-repeat; @@ -421,7 +399,6 @@ canvas { display: inline-block; vertical-align: middle; } - @include DarkThemeOverride { color: #fff; } @@ -443,7 +420,6 @@ canvas { .prefab_FeatureComingSoon { position: relative; - &::after { @include S(top, -5px); @include S(left, -5px); @@ -461,9 +437,7 @@ canvas { @include PlainText; text-transform: uppercase; } - opacity: 0.6; - > * { opacity: 0.5 !important; } @@ -488,14 +462,12 @@ canvas { align-items: center; justify-content: center; flex-direction: column; - .loadingImage { background: uiResource("loading.svg") center center / #{D(60px)} no-repeat; width: 100%; display: flex; flex-grow: 1; } - .loadingStatus { position: absolute; @include S(left, 20px); @@ -503,12 +475,10 @@ canvas { @include S(bottom, 30px); @include Text; @include TextShadow3D(#aaa); - display: flex; flex-direction: column; justify-content: center; align-items: center; - > .bar { display: none; @include S(margin-top, 15px); @@ -517,7 +487,6 @@ canvas { position: relative; @include TextShadow3D(#fff); height: 2px; - .inner { position: absolute !important; top: 0; @@ -526,9 +495,7 @@ canvas { z-index: 1; @include BoxShadow3D($themeColor, $size: 1px); @include S(border-radius, $globalBorderRadius); - transform-origin: 0% 50%; - @include InlineAnimation(1.3s ease-in-out infinite) { 0% { background-color: darken($themeColor, 5); @@ -544,7 +511,6 @@ canvas { } } } - .status { display: none; position: relative; @@ -579,11 +545,9 @@ canvas { &.loading { opacity: 0.2; } - &:hover { background-color: darken($bgColor, 5); } - .knob { @include S(width, 20px); @include S(height, 17px); @@ -594,20 +558,51 @@ canvas { @include BorderRadius(20px); @include BoxShadow3D(#fff, $size: 1px); } - &.checked { background-color: $themeColor; @include BoxShadow3D($themeColor, $size: 2px); .knob { @include S(margin-left, 15px); } - &:hover { background-color: lighten($themeColor, 15); } } } +.range { + display: flex; + align-items: center; + justify-content: center; +} + +.range-input { + cursor: pointer; + background-color: transparent; + width: 100px; + height: 10px; + transform: translate(7px, 2px); + &::-webkit-slider-runnable-track { + background-color: darken($mainBgColor, 3); + color: darken($mainBgColor, 3); + height: 16px; + border-radius: 8px; + } + &::-webkit-slider-thumb { + appearance: none; + -webkit-appearance: none; + box-shadow: inset 0 0 0 10px $themeColor; + background-color: transparent; + width: 20px; + height: 20px; + border-radius: 50%; + transition: 0.3s; + } + &:hover::-webkit-slider-thumb { + box-shadow: inset 0 0 0 10px lighten($themeColor, 15); + } +} + .keybinding { background: #fff; text-transform: uppercase; @@ -622,7 +617,6 @@ canvas { text-shadow: none !important; // font-family: Arial, sans-serif !important; } - font-weight: bold; color: $accentColorDark; text-align: center; @@ -638,7 +632,6 @@ canvas { @include S(height, 12px); overflow: hidden; border: #{D(0px)} solid $accentColorDark; - .keybinding_space { @include S(font-size, 17px); @include S(line-height, 11px); @@ -651,7 +644,6 @@ canvas { .xpaystation-widget-lightbox-overlay { background: rgba($mainBgColor, 0.94); } - &, iframe { pointer-events: all; @@ -692,9 +684,7 @@ iframe { * { pointer-events: all; } - background: rgba($mainBgColor, 0.94) !important; - .cpmsvideoclosebanner { font-family: GameFont !important; font-size: 16px !important; diff --git a/src/js/game/systems/logic_gate.js b/src/js/game/systems/logic_gate.js index 1b04bf9a..3bfc20cd 100644 --- a/src/js/game/systems/logic_gate.js +++ b/src/js/game/systems/logic_gate.js @@ -117,7 +117,7 @@ export class LogicGateSystem extends GameSystemWithFilter { */ compute_XOR(parameters) { assert(parameters.length === 2, "bad parameter count for XOR"); - return isTruthyItem(parameters[0]) ^ isTruthyItem(parameters[1]) + return isTruthyItem(parameters[0]) !== isTruthyItem(parameters[1]) ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON; } diff --git a/src/js/platform/browser/sound.js b/src/js/platform/browser/sound.js index 508dcf8c..e8a70158 100644 --- a/src/js/platform/browser/sound.js +++ b/src/js/platform/browser/sound.js @@ -146,9 +146,10 @@ class MusicInstance extends MusicInstanceInterface { return this.playing; } - play() { + play(volume) { if (this.howl) { this.playing = true; + this.howl.volume(volume); if (this.instance) { this.howl.play(this.instance); } else { @@ -157,6 +158,12 @@ class MusicInstance extends MusicInstanceInterface { } } + setVolume(volume) { + if (this.howl) { + this.howl.volume(volume); + } + } + deinitialize() { if (this.howl) { this.howl.unload(); diff --git a/src/js/platform/sound.js b/src/js/platform/sound.js index 0d509bee..f578ff68 100644 --- a/src/js/platform/sound.js +++ b/src/js/platform/sound.js @@ -61,7 +61,11 @@ export class MusicInstanceInterface { abstract; } - play() { + play(volume) { + abstract; + } + + setVolume(volume) { abstract; } @@ -101,6 +105,9 @@ export class SoundInterface { this.musicMuted = false; this.soundsMuted = false; + + this.musicVolume = 1.0; + this.soundVolume = 1.0; } /** @@ -122,6 +129,8 @@ export class SoundInterface { this.musicMuted = this.app.settings.getAllSettings().musicMuted; this.soundsMuted = this.app.settings.getAllSettings().soundsMuted; + this.musicVolume = this.app.settings.getAllSettings().musicVolume; + this.soundVolume = this.app.settings.getAllSettings().soundVolume; if (G_IS_DEV && globalConfig.debug.disableMusic) { this.musicMuted = true; @@ -189,7 +198,7 @@ export class SoundInterface { } } else { if (this.currentMusic) { - this.currentMusic.play(); + this.currentMusic.play(this.musicVolume); } } } @@ -202,6 +211,41 @@ export class SoundInterface { this.soundsMuted = muted; } + /** + * Returns the music volume + * @returns {number} + */ + getMusicVolume() { + return this.musicVolume; + } + + /** + * Returns the sound volume + * @returns {number} + */ + getSoundVolume() { + return this.soundVolume; + } + + /** + * Sets the music volume + * @param {number} volume + */ + setMusicVolume(volume) { + this.musicVolume = clamp(volume, 0, 1); + if (this.currentMusic) { + this.currentMusic.setVolume(this.musicVolume); + } + } + + /** + * Sets the sound volume + * @param {number} volume + */ + setSoundVolume(volume) { + this.soundVolume = clamp(volume, 0, 1); + } + /** * Focus change handler, called by the pap * @param {boolean} pageIsVisible @@ -211,7 +255,7 @@ export class SoundInterface { if (this.currentMusic) { if (pageIsVisible) { if (!this.currentMusic.isPlaying() && !this.musicMuted) { - this.currentMusic.play(); + this.currentMusic.play(this.musicVolume); } } else { this.currentMusic.stop(); @@ -230,7 +274,7 @@ export class SoundInterface { logger.warn("Sound", key, "not found, probably not loaded yet"); return; } - this.sounds[key].play(1.0); + this.sounds[key].play(this.soundVolume); } /** @@ -253,9 +297,9 @@ export class SoundInterface { return; } - let volume = 1.0; + let volume = this.soundVolume; if (!root.camera.isWorldPointOnScreen(worldPosition)) { - volume = 0.2; + volume = this.soundVolume / 5; // In the old implementation this value was fixed to 0.2 => 20% of 1.0 } volume *= clamp(root.camera.zoomLevel / 3); this.sounds[key].play(clamp(volume)); @@ -277,7 +321,7 @@ export class SoundInterface { this.currentMusic = music; if (music && this.pageIsVisible && !this.musicMuted) { logger.log("Starting", this.currentMusic.key); - music.play(); + music.play(this.musicVolume); } } } diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index 0e7b76ce..c9e8c1e0 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -1,623 +1,644 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ - -import { ReadWriteProxy } from "../core/read_write_proxy"; -import { BoolSetting, EnumSetting, BaseSetting } from "./setting_types"; -import { createLogger } from "../core/logging"; -import { ExplainedResult } from "../core/explained_result"; -import { THEMES, THEME, applyGameTheme } from "../game/theme"; -import { IS_DEMO } from "../core/config"; -import { T } from "../translations"; -import { LANGUAGES } from "../languages"; - -const logger = createLogger("application_settings"); - -/** - * @enum {string} - */ -export const enumCategories = { - general: "general", - userInterface: "userInterface", - performance: "performance", - advanced: "advanced", -}; - -export const uiScales = [ - { - id: "super_small", - size: 0.6, - }, - { - id: "small", - size: 0.8, - }, - { - id: "regular", - size: 1, - }, - { - id: "large", - size: 1.05, - }, - { - id: "huge", - size: 1.1, - }, -]; - -export const scrollWheelSensitivities = [ - { - id: "super_slow", - scale: 0.25, - }, - { - id: "slow", - scale: 0.5, - }, - { - id: "regular", - scale: 1, - }, - { - id: "fast", - scale: 2, - }, - { - id: "super_fast", - scale: 4, - }, -]; - -export const movementSpeeds = [ - { - id: "super_slow", - multiplier: 0.25, - }, - { - id: "slow", - multiplier: 0.5, - }, - { - id: "regular", - multiplier: 1, - }, - { - id: "fast", - multiplier: 2, - }, - { - id: "super_fast", - multiplier: 4, - }, - { - id: "extremely_fast", - multiplier: 8, - }, -]; - -export const autosaveIntervals = [ - { - id: "one_minute", - seconds: 60, - }, - { - id: "two_minutes", - seconds: 120, - }, - { - id: "five_minutes", - seconds: 5 * 60, - }, - { - id: "ten_minutes", - seconds: 10 * 60, - }, - { - id: "twenty_minutes", - seconds: 20 * 60, - }, - { - id: "disabled", - seconds: null, - }, -]; - -const refreshRateOptions = ["30", "60", "120", "180", "240"]; - -if (G_IS_DEV) { - refreshRateOptions.unshift("10"); - refreshRateOptions.unshift("5"); - refreshRateOptions.push("1000"); - refreshRateOptions.push("2000"); - refreshRateOptions.push("5000"); - refreshRateOptions.push("10000"); -} - -/** @type {Array} */ -export const allApplicationSettings = [ - new EnumSetting("language", { - options: Object.keys(LANGUAGES), - valueGetter: key => key, - textGetter: key => LANGUAGES[key].name, - category: enumCategories.general, - restartRequired: true, - changeCb: (app, id) => null, - magicValue: "auto-detect", - }), - - new EnumSetting("uiScale", { - options: uiScales.sort((a, b) => a.size - b.size), - valueGetter: scale => scale.id, - textGetter: scale => T.settings.labels.uiScale.scales[scale.id], - category: enumCategories.userInterface, - restartRequired: false, - changeCb: - /** - * @param {Application} app - */ - (app, id) => app.updateAfterUiScaleChanged(), - }), - - new BoolSetting( - "soundsMuted", - enumCategories.general, - /** - * @param {Application} app - */ - (app, value) => app.sound.setSoundsMuted(value) - ), - new BoolSetting( - "musicMuted", - enumCategories.general, - /** - * @param {Application} app - */ - (app, value) => app.sound.setMusicMuted(value) - ), - - new BoolSetting( - "fullscreen", - enumCategories.general, - /** - * @param {Application} app - */ - (app, value) => { - if (app.platformWrapper.getSupportsFullscreen()) { - app.platformWrapper.setFullscreen(value); - } - }, - !IS_DEMO - ), - - new BoolSetting( - "enableColorBlindHelper", - enumCategories.general, - /** - * @param {Application} app - */ - (app, value) => null - ), - - new BoolSetting("offerHints", enumCategories.userInterface, (app, value) => {}), - - new EnumSetting("theme", { - options: Object.keys(THEMES), - valueGetter: theme => theme, - textGetter: theme => T.settings.labels.theme.themes[theme], - category: enumCategories.userInterface, - restartRequired: false, - changeCb: - /** - * @param {Application} app - */ - (app, id) => { - applyGameTheme(id); - document.documentElement.setAttribute("data-theme", id); - }, - enabled: !IS_DEMO, - }), - - new EnumSetting("autosaveInterval", { - options: autosaveIntervals, - valueGetter: interval => interval.id, - textGetter: interval => T.settings.labels.autosaveInterval.intervals[interval.id], - category: enumCategories.advanced, - restartRequired: false, - changeCb: - /** - * @param {Application} app - */ - (app, id) => null, - }), - - new EnumSetting("scrollWheelSensitivity", { - options: scrollWheelSensitivities.sort((a, b) => a.scale - b.scale), - valueGetter: scale => scale.id, - textGetter: scale => T.settings.labels.scrollWheelSensitivity.sensitivity[scale.id], - category: enumCategories.advanced, - restartRequired: false, - changeCb: - /** - * @param {Application} app - */ - (app, id) => app.updateAfterUiScaleChanged(), - }), - - new EnumSetting("movementSpeed", { - options: movementSpeeds.sort((a, b) => a.multiplier - b.multiplier), - valueGetter: multiplier => multiplier.id, - textGetter: multiplier => T.settings.labels.movementSpeed.speeds[multiplier.id], - category: enumCategories.advanced, - restartRequired: false, - changeCb: (app, id) => {}, - }), - - new BoolSetting("alwaysMultiplace", enumCategories.advanced, (app, value) => {}), - new BoolSetting("clearCursorOnDeleteWhilePlacing", enumCategories.advanced, (app, value) => {}), - new BoolSetting("enableTunnelSmartplace", enumCategories.advanced, (app, value) => {}), - new BoolSetting("vignette", enumCategories.userInterface, (app, value) => {}), - new BoolSetting("compactBuildingInfo", enumCategories.userInterface, (app, value) => {}), - new BoolSetting("disableCutDeleteWarnings", enumCategories.advanced, (app, value) => {}), - new BoolSetting("rotationByBuilding", enumCategories.advanced, (app, value) => {}), - new BoolSetting("displayChunkBorders", enumCategories.advanced, (app, value) => {}), - - new EnumSetting("refreshRate", { - options: refreshRateOptions, - valueGetter: rate => rate, - textGetter: rate => rate + " Hz", - category: enumCategories.performance, - restartRequired: false, - changeCb: (app, id) => {}, - enabled: !IS_DEMO, - }), - - new BoolSetting("lowQualityMapResources", enumCategories.performance, (app, value) => {}), - new BoolSetting("disableTileGrid", enumCategories.performance, (app, value) => {}), - new BoolSetting("lowQualityTextures", enumCategories.performance, (app, value) => {}), -]; - -export function getApplicationSettingById(id) { - return allApplicationSettings.find(setting => setting.id === id); -} - -class SettingsStorage { - constructor() { - this.uiScale = "regular"; - this.fullscreen = G_IS_STANDALONE; - - this.soundsMuted = false; - this.musicMuted = false; - this.theme = "light"; - this.refreshRate = "60"; - this.scrollWheelSensitivity = "regular"; - this.movementSpeed = "regular"; - this.language = "auto-detect"; - this.autosaveInterval = "two_minutes"; - - this.alwaysMultiplace = false; - this.offerHints = true; - this.enableTunnelSmartplace = true; - this.vignette = true; - this.compactBuildingInfo = false; - this.disableCutDeleteWarnings = false; - this.rotationByBuilding = true; - this.clearCursorOnDeleteWhilePlacing = true; - this.displayChunkBorders = false; - - this.enableColorBlindHelper = false; - - this.lowQualityMapResources = false; - this.disableTileGrid = false; - this.lowQualityTextures = false; - - /** - * @type {Object.} - */ - this.keybindingOverrides = {}; - } -} - -export class ApplicationSettings extends ReadWriteProxy { - constructor(app) { - super(app, "app_settings.bin"); - } - - initialize() { - // Read and directly write latest data back - return this.readAsync() - .then(() => { - // Apply default setting callbacks - const settings = this.getAllSettings(); - for (let i = 0; i < allApplicationSettings.length; ++i) { - const handle = allApplicationSettings[i]; - handle.apply(this.app, settings[handle.id]); - } - }) - - .then(() => this.writeAsync()); - } - - save() { - return this.writeAsync(); - } - - // Getters - - /** - * @returns {SettingsStorage} - */ - getAllSettings() { - return this.getCurrentData().settings; - } - - /** - * @param {string} key - */ - getSetting(key) { - assert(this.getAllSettings().hasOwnProperty(key), "Setting not known: " + key); - return this.getAllSettings()[key]; - } - - getInterfaceScaleId() { - if (!this.currentData) { - // Not initialized yet - return "regular"; - } - return this.getAllSettings().uiScale; - } - - getDesiredFps() { - return parseInt(this.getAllSettings().refreshRate); - } - - getInterfaceScaleValue() { - const id = this.getInterfaceScaleId(); - for (let i = 0; i < uiScales.length; ++i) { - if (uiScales[i].id === id) { - return uiScales[i].size; - } - } - logger.error("Unknown ui scale id:", id); - return 1; - } - - getScrollWheelSensitivity() { - const id = this.getAllSettings().scrollWheelSensitivity; - for (let i = 0; i < scrollWheelSensitivities.length; ++i) { - if (scrollWheelSensitivities[i].id === id) { - return scrollWheelSensitivities[i].scale; - } - } - logger.error("Unknown scroll wheel sensitivity id:", id); - return 1; - } - - getMovementSpeed() { - const id = this.getAllSettings().movementSpeed; - for (let i = 0; i < movementSpeeds.length; ++i) { - if (movementSpeeds[i].id === id) { - return movementSpeeds[i].multiplier; - } - } - logger.error("Unknown movement speed id:", id); - return 1; - } - - getAutosaveIntervalSeconds() { - const id = this.getAllSettings().autosaveInterval; - for (let i = 0; i < autosaveIntervals.length; ++i) { - if (autosaveIntervals[i].id === id) { - return autosaveIntervals[i].seconds; - } - } - logger.error("Unknown autosave interval id:", id); - return 120; - } - - getIsFullScreen() { - return this.getAllSettings().fullscreen; - } - - getKeybindingOverrides() { - return this.getAllSettings().keybindingOverrides; - } - - getLanguage() { - return this.getAllSettings().language; - } - - // Setters - - updateLanguage(id) { - assert(LANGUAGES[id], "Language not known: " + id); - return this.updateSetting("language", id); - } - - /** - * @param {string} key - * @param {string|boolean} value - */ - updateSetting(key, value) { - for (let i = 0; i < allApplicationSettings.length; ++i) { - const setting = allApplicationSettings[i]; - if (setting.id === key) { - if (!setting.validate(value)) { - assertAlways(false, "Bad setting value: " + key); - } - this.getAllSettings()[key] = value; - if (setting.changeCb) { - setting.changeCb(this.app, value); - } - return this.writeAsync(); - } - } - assertAlways(false, "Unknown setting: " + key); - } - - /** - * Sets a new keybinding override - * @param {string} keybindingId - * @param {number} keyCode - */ - updateKeybindingOverride(keybindingId, keyCode) { - assert(Number.isInteger(keyCode), "Not a valid key code: " + keyCode); - this.getAllSettings().keybindingOverrides[keybindingId] = keyCode; - return this.writeAsync(); - } - - /** - * Resets a given keybinding override - * @param {string} id - */ - resetKeybindingOverride(id) { - delete this.getAllSettings().keybindingOverrides[id]; - return this.writeAsync(); - } - /** - * Resets all keybinding overrides - */ - resetKeybindingOverrides() { - this.getAllSettings().keybindingOverrides = {}; - return this.writeAsync(); - } - - // RW Proxy impl - verify(data) { - if (!data.settings) { - return ExplainedResult.bad("missing key 'settings'"); - } - if (typeof data.settings !== "object") { - return ExplainedResult.bad("Bad settings object"); - } - - const settings = data.settings; - for (let i = 0; i < allApplicationSettings.length; ++i) { - const setting = allApplicationSettings[i]; - const storedValue = settings[setting.id]; - if (!setting.validate(storedValue)) { - return ExplainedResult.bad("Bad setting value for " + setting.id + ": " + storedValue); - } - } - return ExplainedResult.good(); - } - - getDefaultData() { - return { - version: this.getCurrentVersion(), - settings: new SettingsStorage(), - }; - } - - getCurrentVersion() { - return 24; - } - - /** @param {{settings: SettingsStorage, version: number}} data */ - migrate(data) { - // Simply reset before - if (data.version < 5) { - data.settings = new SettingsStorage(); - data.version = this.getCurrentVersion(); - return ExplainedResult.good(); - } - - if (data.version < 6) { - data.settings.alwaysMultiplace = false; - data.version = 6; - } - - if (data.version < 7) { - data.settings.offerHints = true; - data.version = 7; - } - - if (data.version < 8) { - data.settings.scrollWheelSensitivity = "regular"; - data.version = 8; - } - - if (data.version < 9) { - data.settings.language = "auto-detect"; - data.version = 9; - } - - if (data.version < 10) { - data.settings.movementSpeed = "regular"; - data.version = 10; - } - - if (data.version < 11) { - data.settings.enableTunnelSmartplace = true; - data.version = 11; - } - - if (data.version < 12) { - data.settings.vignette = true; - data.version = 12; - } - - if (data.version < 13) { - data.settings.compactBuildingInfo = false; - data.version = 13; - } - - if (data.version < 14) { - data.settings.disableCutDeleteWarnings = false; - data.version = 14; - } - - if (data.version < 15) { - data.settings.autosaveInterval = "two_minutes"; - data.version = 15; - } - - if (data.version < 16) { - // RE-ENABLE this setting, it already existed - data.settings.enableTunnelSmartplace = true; - data.version = 16; - } - - if (data.version < 17) { - data.settings.enableColorBlindHelper = false; - data.version = 17; - } - - if (data.version < 18) { - data.settings.rotationByBuilding = true; - data.version = 18; - } - - if (data.version < 19) { - data.settings.lowQualityMapResources = false; - data.version = 19; - } - - if (data.version < 20) { - data.settings.disableTileGrid = false; - data.version = 20; - } - - if (data.version < 21) { - data.settings.lowQualityTextures = false; - data.version = 21; - } - - if (data.version < 22) { - data.settings.clearCursorOnDeleteWhilePlacing = true; - data.version = 22; - } - - if (data.version < 23) { - data.settings.displayChunkBorders = false; - data.version = 23; - } - - if (data.version < 24) { - data.settings.refreshRate = "60"; - data.version = 24; - } - - return ExplainedResult.good(); - } -} +/* typehints:start */ +import { Application } from "../application"; +/* typehints:end */ + +import { ReadWriteProxy } from "../core/read_write_proxy"; +import { BoolSetting, EnumSetting, RangeSetting, BaseSetting } from "./setting_types"; +import { createLogger } from "../core/logging"; +import { ExplainedResult } from "../core/explained_result"; +import { THEMES, THEME, applyGameTheme } from "../game/theme"; +import { IS_DEMO } from "../core/config"; +import { T } from "../translations"; +import { LANGUAGES } from "../languages"; + +const logger = createLogger("application_settings"); + +/** + * @enum {string} + */ +export const enumCategories = { + general: "general", + userInterface: "userInterface", + performance: "performance", + advanced: "advanced", +}; + +export const uiScales = [ + { + id: "super_small", + size: 0.6, + }, + { + id: "small", + size: 0.8, + }, + { + id: "regular", + size: 1, + }, + { + id: "large", + size: 1.05, + }, + { + id: "huge", + size: 1.1, + }, +]; + +export const scrollWheelSensitivities = [ + { + id: "super_slow", + scale: 0.25, + }, + { + id: "slow", + scale: 0.5, + }, + { + id: "regular", + scale: 1, + }, + { + id: "fast", + scale: 2, + }, + { + id: "super_fast", + scale: 4, + }, +]; + +export const movementSpeeds = [ + { + id: "super_slow", + multiplier: 0.25, + }, + { + id: "slow", + multiplier: 0.5, + }, + { + id: "regular", + multiplier: 1, + }, + { + id: "fast", + multiplier: 2, + }, + { + id: "super_fast", + multiplier: 4, + }, + { + id: "extremely_fast", + multiplier: 8, + }, +]; + +export const autosaveIntervals = [ + { + id: "one_minute", + seconds: 60, + }, + { + id: "two_minutes", + seconds: 120, + }, + { + id: "five_minutes", + seconds: 5 * 60, + }, + { + id: "ten_minutes", + seconds: 10 * 60, + }, + { + id: "twenty_minutes", + seconds: 20 * 60, + }, + { + id: "disabled", + seconds: null, + }, +]; + +const refreshRateOptions = ["30", "60", "120", "180", "240"]; + +if (G_IS_DEV) { + refreshRateOptions.unshift("10"); + refreshRateOptions.unshift("5"); + refreshRateOptions.push("1000"); + refreshRateOptions.push("2000"); + refreshRateOptions.push("5000"); + refreshRateOptions.push("10000"); +} + +/** @type {Array} */ +export const allApplicationSettings = [ + new EnumSetting("language", { + options: Object.keys(LANGUAGES), + valueGetter: key => key, + textGetter: key => LANGUAGES[key].name, + category: enumCategories.general, + restartRequired: true, + changeCb: (app, id) => null, + magicValue: "auto-detect", + }), + + new EnumSetting("uiScale", { + options: uiScales.sort((a, b) => a.size - b.size), + valueGetter: scale => scale.id, + textGetter: scale => T.settings.labels.uiScale.scales[scale.id], + category: enumCategories.userInterface, + restartRequired: false, + changeCb: + /** + * @param {Application} app + */ + (app, id) => app.updateAfterUiScaleChanged(), + }), + + new BoolSetting( + "soundsMuted", + enumCategories.general, + /** + * @param {Application} app + */ + (app, value) => app.sound.setSoundsMuted(value) + ), + new RangeSetting( + "soundVolume", + enumCategories.general, + /** + * @param {Application} app + */ + (app, value) => app.sound.setSoundVolume(value / 100.0) + ), + new BoolSetting( + "musicMuted", + enumCategories.general, + /** + * @param {Application} app + */ + (app, value) => app.sound.setMusicMuted(value) + ), + new RangeSetting( + "musicVolume", + enumCategories.general, + /** + * @param {Application} app + */ + (app, value) => app.sound.setMusicVolume(value / 100.0) + ), + + new BoolSetting( + "fullscreen", + enumCategories.general, + /** + * @param {Application} app + */ + (app, value) => { + if (app.platformWrapper.getSupportsFullscreen()) { + app.platformWrapper.setFullscreen(value); + } + }, + !IS_DEMO + ), + + new BoolSetting( + "enableColorBlindHelper", + enumCategories.general, + /** + * @param {Application} app + */ + (app, value) => null + ), + + new BoolSetting("offerHints", enumCategories.userInterface, (app, value) => {}), + + new EnumSetting("theme", { + options: Object.keys(THEMES), + valueGetter: theme => theme, + textGetter: theme => T.settings.labels.theme.themes[theme], + category: enumCategories.userInterface, + restartRequired: false, + changeCb: + /** + * @param {Application} app + */ + (app, id) => { + applyGameTheme(id); + document.documentElement.setAttribute("data-theme", id); + }, + enabled: !IS_DEMO, + }), + + new EnumSetting("autosaveInterval", { + options: autosaveIntervals, + valueGetter: interval => interval.id, + textGetter: interval => T.settings.labels.autosaveInterval.intervals[interval.id], + category: enumCategories.advanced, + restartRequired: false, + changeCb: + /** + * @param {Application} app + */ + (app, id) => null, + }), + + new EnumSetting("scrollWheelSensitivity", { + options: scrollWheelSensitivities.sort((a, b) => a.scale - b.scale), + valueGetter: scale => scale.id, + textGetter: scale => T.settings.labels.scrollWheelSensitivity.sensitivity[scale.id], + category: enumCategories.advanced, + restartRequired: false, + changeCb: + /** + * @param {Application} app + */ + (app, id) => app.updateAfterUiScaleChanged(), + }), + + new EnumSetting("movementSpeed", { + options: movementSpeeds.sort((a, b) => a.multiplier - b.multiplier), + valueGetter: multiplier => multiplier.id, + textGetter: multiplier => T.settings.labels.movementSpeed.speeds[multiplier.id], + category: enumCategories.advanced, + restartRequired: false, + changeCb: (app, id) => {}, + }), + + new BoolSetting("alwaysMultiplace", enumCategories.advanced, (app, value) => {}), + new BoolSetting("clearCursorOnDeleteWhilePlacing", enumCategories.advanced, (app, value) => {}), + new BoolSetting("enableTunnelSmartplace", enumCategories.advanced, (app, value) => {}), + new BoolSetting("vignette", enumCategories.userInterface, (app, value) => {}), + new BoolSetting("compactBuildingInfo", enumCategories.userInterface, (app, value) => {}), + new BoolSetting("disableCutDeleteWarnings", enumCategories.advanced, (app, value) => {}), + new BoolSetting("rotationByBuilding", enumCategories.advanced, (app, value) => {}), + new BoolSetting("displayChunkBorders", enumCategories.advanced, (app, value) => {}), + + new EnumSetting("refreshRate", { + options: refreshRateOptions, + valueGetter: rate => rate, + textGetter: rate => rate + " Hz", + category: enumCategories.performance, + restartRequired: false, + changeCb: (app, id) => {}, + enabled: !IS_DEMO, + }), + + new BoolSetting("lowQualityMapResources", enumCategories.performance, (app, value) => {}), + new BoolSetting("disableTileGrid", enumCategories.performance, (app, value) => {}), + new BoolSetting("lowQualityTextures", enumCategories.performance, (app, value) => {}), +]; + +export function getApplicationSettingById(id) { + return allApplicationSettings.find(setting => setting.id === id); +} + +class SettingsStorage { + constructor() { + this.uiScale = "regular"; + this.fullscreen = G_IS_STANDALONE; + + this.soundsMuted = false; + this.musicMuted = false; + this.soundVolume = 1.0; + this.musicVolume = 1.0; + + this.theme = "light"; + this.refreshRate = "60"; + this.scrollWheelSensitivity = "regular"; + this.movementSpeed = "regular"; + this.language = "auto-detect"; + this.autosaveInterval = "two_minutes"; + + this.alwaysMultiplace = false; + this.offerHints = true; + this.enableTunnelSmartplace = true; + this.vignette = true; + this.compactBuildingInfo = false; + this.disableCutDeleteWarnings = false; + this.rotationByBuilding = true; + this.clearCursorOnDeleteWhilePlacing = true; + this.displayChunkBorders = false; + + this.enableColorBlindHelper = false; + + this.lowQualityMapResources = false; + this.disableTileGrid = false; + this.lowQualityTextures = false; + + /** + * @type {Object.} + */ + this.keybindingOverrides = {}; + } +} + +export class ApplicationSettings extends ReadWriteProxy { + constructor(app) { + super(app, "app_settings.bin"); + } + + initialize() { + // Read and directly write latest data back + return this.readAsync() + .then(() => { + // Apply default setting callbacks + const settings = this.getAllSettings(); + for (let i = 0; i < allApplicationSettings.length; ++i) { + const handle = allApplicationSettings[i]; + handle.apply(this.app, settings[handle.id]); + } + }) + + .then(() => this.writeAsync()); + } + + save() { + return this.writeAsync(); + } + + // Getters + + /** + * @returns {SettingsStorage} + */ + getAllSettings() { + return this.getCurrentData().settings; + } + + /** + * @param {string} key + */ + getSetting(key) { + assert(this.getAllSettings().hasOwnProperty(key), "Setting not known: " + key); + return this.getAllSettings()[key]; + } + + getInterfaceScaleId() { + if (!this.currentData) { + // Not initialized yet + return "regular"; + } + return this.getAllSettings().uiScale; + } + + getDesiredFps() { + return parseInt(this.getAllSettings().refreshRate); + } + + getInterfaceScaleValue() { + const id = this.getInterfaceScaleId(); + for (let i = 0; i < uiScales.length; ++i) { + if (uiScales[i].id === id) { + return uiScales[i].size; + } + } + logger.error("Unknown ui scale id:", id); + return 1; + } + + getScrollWheelSensitivity() { + const id = this.getAllSettings().scrollWheelSensitivity; + for (let i = 0; i < scrollWheelSensitivities.length; ++i) { + if (scrollWheelSensitivities[i].id === id) { + return scrollWheelSensitivities[i].scale; + } + } + logger.error("Unknown scroll wheel sensitivity id:", id); + return 1; + } + + getMovementSpeed() { + const id = this.getAllSettings().movementSpeed; + for (let i = 0; i < movementSpeeds.length; ++i) { + if (movementSpeeds[i].id === id) { + return movementSpeeds[i].multiplier; + } + } + logger.error("Unknown movement speed id:", id); + return 1; + } + + getAutosaveIntervalSeconds() { + const id = this.getAllSettings().autosaveInterval; + for (let i = 0; i < autosaveIntervals.length; ++i) { + if (autosaveIntervals[i].id === id) { + return autosaveIntervals[i].seconds; + } + } + logger.error("Unknown autosave interval id:", id); + return 120; + } + + getIsFullScreen() { + return this.getAllSettings().fullscreen; + } + + getKeybindingOverrides() { + return this.getAllSettings().keybindingOverrides; + } + + getLanguage() { + return this.getAllSettings().language; + } + + // Setters + + updateLanguage(id) { + assert(LANGUAGES[id], "Language not known: " + id); + return this.updateSetting("language", id); + } + + /** + * @param {string} key + * @param {string|boolean|number} value + */ + updateSetting(key, value) { + for (let i = 0; i < allApplicationSettings.length; ++i) { + const setting = allApplicationSettings[i]; + if (setting.id === key) { + if (!setting.validate(value)) { + assertAlways(false, "Bad setting value: " + key); + } + this.getAllSettings()[key] = value; + if (setting.changeCb) { + setting.changeCb(this.app, value); + } + return this.writeAsync(); + } + } + assertAlways(false, "Unknown setting: " + key); + } + + /** + * Sets a new keybinding override + * @param {string} keybindingId + * @param {number} keyCode + */ + updateKeybindingOverride(keybindingId, keyCode) { + assert(Number.isInteger(keyCode), "Not a valid key code: " + keyCode); + this.getAllSettings().keybindingOverrides[keybindingId] = keyCode; + return this.writeAsync(); + } + + /** + * Resets a given keybinding override + * @param {string} id + */ + resetKeybindingOverride(id) { + delete this.getAllSettings().keybindingOverrides[id]; + return this.writeAsync(); + } + /** + * Resets all keybinding overrides + */ + resetKeybindingOverrides() { + this.getAllSettings().keybindingOverrides = {}; + return this.writeAsync(); + } + + // RW Proxy impl + verify(data) { + if (!data.settings) { + return ExplainedResult.bad("missing key 'settings'"); + } + if (typeof data.settings !== "object") { + return ExplainedResult.bad("Bad settings object"); + } + + const settings = data.settings; + for (let i = 0; i < allApplicationSettings.length; ++i) { + const setting = allApplicationSettings[i]; + const storedValue = settings[setting.id]; + if (!setting.validate(storedValue)) { + return ExplainedResult.bad("Bad setting value for " + setting.id + ": " + storedValue); + } + } + return ExplainedResult.good(); + } + + getDefaultData() { + return { + version: this.getCurrentVersion(), + settings: new SettingsStorage(), + }; + } + + getCurrentVersion() { + return 24; + } + + /** @param {{settings: SettingsStorage, version: number}} data */ + migrate(data) { + // Simply reset before + if (data.version < 5) { + data.settings = new SettingsStorage(); + data.version = this.getCurrentVersion(); + return ExplainedResult.good(); + } + + if (data.version < 6) { + data.settings.alwaysMultiplace = false; + data.version = 6; + } + + if (data.version < 7) { + data.settings.offerHints = true; + data.version = 7; + } + + if (data.version < 8) { + data.settings.scrollWheelSensitivity = "regular"; + data.version = 8; + } + + if (data.version < 9) { + data.settings.language = "auto-detect"; + data.version = 9; + } + + if (data.version < 10) { + data.settings.movementSpeed = "regular"; + data.version = 10; + } + + if (data.version < 11) { + data.settings.enableTunnelSmartplace = true; + data.version = 11; + } + + if (data.version < 12) { + data.settings.vignette = true; + data.version = 12; + } + + if (data.version < 13) { + data.settings.compactBuildingInfo = false; + data.version = 13; + } + + if (data.version < 14) { + data.settings.disableCutDeleteWarnings = false; + data.version = 14; + } + + if (data.version < 15) { + data.settings.autosaveInterval = "two_minutes"; + data.version = 15; + } + + if (data.version < 16) { + // RE-ENABLE this setting, it already existed + data.settings.enableTunnelSmartplace = true; + data.version = 16; + } + + if (data.version < 17) { + data.settings.enableColorBlindHelper = false; + data.version = 17; + } + + if (data.version < 18) { + data.settings.rotationByBuilding = true; + data.version = 18; + } + + if (data.version < 19) { + data.settings.lowQualityMapResources = false; + data.version = 19; + } + + if (data.version < 20) { + data.settings.disableTileGrid = false; + data.version = 20; + } + + if (data.version < 21) { + data.settings.lowQualityTextures = false; + data.version = 21; + } + + if (data.version < 22) { + data.settings.clearCursorOnDeleteWhilePlacing = true; + data.version = 22; + } + + if (data.version < 23) { + data.settings.displayChunkBorders = false; + data.version = 23; + } + + if (data.version < 24) { + data.settings.musicVolume = 1.0; + data.settings.soundVolume = 1.0; + data.settings.refreshRate = "60"; + data.version = 24; + } + + return ExplainedResult.good(); + } +} diff --git a/src/js/profile/setting_types.js b/src/js/profile/setting_types.js index 9e361f66..3b884c58 100644 --- a/src/js/profile/setting_types.js +++ b/src/js/profile/setting_types.js @@ -220,3 +220,78 @@ export class BoolSetting extends BaseSetting { return typeof value === "boolean"; } } + +export class RangeSetting extends BaseSetting { + constructor( + id, + category, + changeCb = null, + enabled = true, + defaultValue = 100, + minValue = 0, + maxValue = 100, + stepSize = 1 + ) { + super(id, category, changeCb, enabled); + + this.defaultValue = defaultValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.stepSize = stepSize; + } + + getHtml() { + return ` +
+ ${this.enabled ? "" : `${T.demo.settingNotAvailable}`} + +
+ +
+ + +
+
+
+ ${T.settings.labels[this.id].description} +
+
`; + } + + bind(app, element, dialogs) { + this.app = app; + this.element = element; + this.dialogs = dialogs; + + this.element.querySelector(".range-input").addEventListener("input", () => { + this.modify(); + }); + } + + syncValueToElement() { + const value = this.app.settings.getSetting(this.id); + /** @type {HTMLInputElement} */ + const rangeInput = this.element.querySelector(".range-input"), + rangeLabel = this.element.querySelector(".range-label"); + rangeInput.value = value; + rangeLabel.innerHTML = value; + } + + modify() { + /** @type {HTMLInputElement} */ + const rangeInput = this.element.querySelector(".range-input"); + const newValue = Number(rangeInput.value); + this.app.settings.updateSetting(this.id, newValue); + this.syncValueToElement(); + + if (this.changeCb) { + this.changeCb(this.app, newValue); + } + } + + validate(value) { + return typeof value === "number"; + } +} diff --git a/translations/base-de.yaml b/translations/base-de.yaml index 3c39e5fc..da8e1008 100644 --- a/translations/base-de.yaml +++ b/translations/base-de.yaml @@ -728,6 +728,16 @@ settings: description: >- Bei der Aktivierung wird die Musik stummgeschaltet. + soundVolume: + title: Geräuschlautstärke + description: >- + Ändert die Lautstärke von Geräuschen. + + musicVolume: + title: Musiklautstärke + description: >- + Ändert die Lautstärke der Musik. + theme: title: Farbmodus description: >- diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 8ef3e080..d6acd7ea 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -1,983 +1,993 @@ -# -# GAME TRANSLATIONS -# -# Contributing: -# -# If you want to contribute, please make a pull request on this respository -# and I will have a look. -# -# Placeholders: -# -# Do *not* replace placeholders! Placeholders have a special syntax like -# `Hotkey: `. They are encapsulated within angle brackets. The correct -# translation for this one in German for example would be: `Taste: ` (notice -# how the placeholder stayed '' and was not replaced!) -# -# Adding a new language: -# -# If you want to add a new language, ask me in the Discord and I will setup -# the basic structure so the game also detects it. -# - ---- -steamPage: - # This is the short text appearing on the steam page - shortText: shapez.io is a game about building factories to automate the creation and processing of increasingly complex shapes across an infinitely expanding map. - - # This is the text shown above the Discord link - discordLink: Official Discord - Chat with me! - - # 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) - # - Please keep the markup (Stuff like [b], [list] etc) in the same format - longText: >- - [img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img] - - shapez.io is a game about building factories to automate the creation and processing of increasingly complex shapes across an infinitely expanding map. - - Upon delivering the requested shapes you'll progress within the game and unlock upgrades to speed up your factory. - - As the demand for shapes increases, you'll have to scale up your factory to meet the demand - Don't forget about resources though, you'll have to expand across the [b]infinite map[/b]! - - Soon you'll have to mix colors and paint your shapes with them - Combine red, green and blue color resources to produce different colors and paint shapes with them to satisfy the demand. - - This game features 18 progressive levels (Which should already keep you busy for hours!) but I'm constantly adding new content - There's a lot planned! - - Purchasing the game gives you access to the standalone version which has additional features, and you'll also receive access to newly developed features. - - [b]Standalone Advantages[/b] - - [list] - [*] Dark Mode - [*] Unlimited Waypoints - [*] Unlimited Savegames - [*] Additional settings - [*] Coming soon: Wires & Energy! Aiming for (roughly) end of July 2020. - [*] Coming soon: More Levels - [*] Allows me to further develop shapez.io ❤️ - [/list] - - [b]Future Updates[/b] - - I am updating the game often and trying to push an update at least once every week! - - [list] - [*] Different maps and challenges (e.g. maps with obstacles) - [*] Puzzles (Deliver the requested shape with a restricted area / set of buildings) - [*] A story mode where buildings have a cost - [*] Configurable map generator (Configure resource/shape size/density, seed and more) - [*] Additional types of shapes - [*] Performance improvements (The game already runs pretty well!) - [*] And much more! - [/list] - - [b]This game is open source![/b] - - Anybody can contribute, I'm actively involved in the community and attempt to review all suggestions and take feedback into consideration where possible. - Be sure to check out my trello board for the full roadmap! - - [b]Links[/b] - - [list] - [*] [url=https://discord.com/invite/HN7EVzV]Official Discord[/url] - [*] [url=https://trello.com/b/ISQncpJP/shapezio]Roadmap[/url] - [*] [url=https://www.reddit.com/r/shapezio]Subreddit[/url] - [*] [url=https://github.com/tobspr/shapez.io]Source code (GitHub)[/url] - [*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]Help translate[/url] - [/list] - -global: - loading: Loading - error: Error - - # How big numbers are rendered, e.g. "10,000" - thousandsDivider: "," - - # What symbol to use to seperate the integer part from the fractional part of a number, e.g. "0.4" - decimalSeparator: "." - - # The suffix for large numbers, e.g. 1.3k, 400.2M, etc. - suffix: - thousands: k - millions: M - billions: B - trillions: T - - # Shown for infinitely big numbers - infinite: inf - - time: - # Used for formatting past time dates - oneSecondAgo: one second ago - xSecondsAgo: seconds ago - oneMinuteAgo: one minute ago - xMinutesAgo: minutes ago - oneHourAgo: one hour ago - xHoursAgo: hours ago - oneDayAgo: one day ago - xDaysAgo: days ago - - # Short formats for times, e.g. '5h 23m' - secondsShort: s - minutesAndSecondsShort: m s - hoursAndMinutesShort: h m - - xMinutes: minutes - - keys: - tab: TAB - control: CTRL - alt: ALT - escape: ESC - shift: SHIFT - space: SPACE - -demoBanners: - # This is the "advertisement" shown in the main menu and other various places - title: Demo Version - intro: >- - Get the standalone to unlock all features! - -mainMenu: - play: Play - continue: Continue - newGame: New Game - changelog: Changelog - subreddit: Reddit - importSavegame: Import - openSourceHint: This game is open source! - discordLink: Official Discord Server - helpTranslate: Help translate! - madeBy: Made by - - # This is shown when using firefox and other browsers which are not supported. - browserWarning: >- - Sorry, but the game is known to run slow on your browser! Get the standalone version or download Google Chrome for the full experience. - - savegameLevel: Level - savegameLevelUnknown: Unknown Level - savegameUnnamed: Unnamed - -dialogs: - buttons: - ok: OK - delete: Delete - cancel: Cancel - later: Later - restart: Restart - reset: Reset - getStandalone: Get Standalone - deleteGame: I know what I am doing - viewUpdate: View Update - showUpgrades: Show Upgrades - showKeybindings: Show Keybindings - - importSavegameError: - title: Import Error - text: >- - Failed to import your savegame: - - importSavegameSuccess: - title: Savegame Imported - text: >- - Your savegame has been successfully imported. - - gameLoadFailure: - title: Game is broken - text: >- - Failed to load your savegame: - - confirmSavegameDelete: - title: Confirm deletion - text: >- - Are you sure you want to delete the game? - - savegameDeletionError: - title: Failed to delete - text: >- - Failed to delete the savegame: - - restartRequired: - title: Restart required - text: >- - You need to restart the game to apply the settings. - - editKeybinding: - title: Change Keybinding - desc: Press the key or mouse button you want to assign, or escape to cancel. - - resetKeybindingsConfirmation: - title: Reset keybindings - desc: This will reset all keybindings to their default values. Please confirm. - - keybindingsResetOk: - title: Keybindings reset - desc: The keybindings have been reset to their respective defaults! - - featureRestriction: - title: Demo Version - desc: You tried to access a feature () which is not available in the demo. Consider getting the standalone version for the full experience! - - oneSavegameLimit: - title: Limited savegames - desc: You can only have one savegame at a time in the demo version. Please remove the existing one or get the standalone version! - - updateSummary: - title: New update! - desc: >- - Here are the changes since you last played: - - upgradesIntroduction: - title: Unlock Upgrades - desc: >- - All shapes you produce can be used to unlock upgrades - Don't destroy your old factories! - The upgrades tab can be found on the top right corner of the screen. - - massDeleteConfirm: - title: Confirm delete - desc: >- - You are deleting a lot of buildings ( to be exact)! Are you sure you want to do this? - - massCutConfirm: - title: Confirm cut - desc: >- - You are cutting a lot of buildings ( to be exact)! Are you sure you want to do this? - - massCutInsufficientConfirm: - title: Confirm cut - desc: >- - You can not afford to paste this area! Are you sure you want to cut it? - - blueprintsNotUnlocked: - title: Not unlocked yet - desc: >- - Complete level 12 to unlock Blueprints! - - keybindingsIntroduction: - title: Useful keybindings - desc: >- - This game has a lot of keybindings which make it easier to build big factories. - Here are a few, but be sure to check out the keybindings!

- CTRL + Drag: Select an area.
- SHIFT: Hold to place multiple of one building.
- ALT: Invert orientation of placed belts.
- - createMarker: - title: New Marker - titleEdit: Edit Marker - desc: Give it a meaningful name, you can also include a short key of a shape (Which you can generate here) - - markerDemoLimit: - desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers! - - exportScreenshotWarning: - title: Export screenshot - desc: You requested to export your base as a screenshot. Please note that this can be quite slow for a big base and even crash your game! - - renameSavegame: - title: Rename Savegame - desc: You can rename your savegame here. - -ingame: - # This is shown in the top left corner and displays useful keybindings in - # every situation - keybindingsOverlay: - moveMap: Move - selectBuildings: Select area - stopPlacement: Stop placement - rotateBuilding: Rotate building - placeMultiple: Place multiple - reverseOrientation: Reverse orientation - disableAutoOrientation: Disable auto-orientation - toggleHud: Toggle HUD - placeBuilding: Place building - createMarker: Create marker - delete: Delete - pasteLastBlueprint: Paste last blueprint - lockBeltDirection: Enable belt planner - plannerSwitchSide: Flip planner side - cutSelection: Cut - copySelection: Copy - clearSelection: Clear selection - pipette: Pipette - switchLayers: Switch layers - - # Names of the colors, used for the color blind mode - colors: - red: Red - green: Green - blue: Blue - yellow: Yellow - purple: Magenta - cyan: Cyan - white: White - black: Black - uncolored: Gray - - # Everything related to placing buildings (I.e. as soon as you selected a building - # from the toolbar) - 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: Press to cycle variants. - - # Shows the hotkey in the ui, e.g. "Hotkey: Q" - hotkeyLabel: >- - Hotkey: - - infoTexts: - speed: Speed - range: Range - storage: Storage - oneItemPerSecond: 1 item / second - itemsPerSecond: items / s - itemsPerSecondDouble: (x2) - - tiles: tiles - - # The notification when completing a level - levelCompleteNotification: - # is replaced by the actual level, so this gets 'Level 03' for example. - levelTitle: Level - completed: Completed - unlockText: Unlocked ! - buttonNextLevel: Next Level - - # Notifications on the lower right - notifications: - newUpgrade: A new upgrade is available! - gameSaved: Your game has been saved. - - # The "Upgrades" window - shop: - title: Upgrades - buttonUnlock: Upgrade - - # Gets replaced to e.g. "Tier IX" - tier: Tier - - # The roman number for each tier - tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X] - - maximumLevel: MAXIMUM LEVEL (Speed x) - - # The "Statistics" window - statistics: - title: Statistics - dataSources: - stored: - title: Stored - description: Displaying amount of stored shapes in your central building. - produced: - title: Produced - description: Displaying all shapes your whole factory produces, including intermediate products. - delivered: - title: Delivered - description: Displaying shapes which are delivered to your central building. - noShapesProduced: No shapes have been produced so far. - - # Displays the shapes per second, e.g. '523 / s' - shapesPerSecond: / s - - # Settings menu, when you press "ESC" - settingsMenu: - playtime: Playtime - - buildingsPlaced: Buildings - beltsPlaced: Belts - - buttons: - continue: Continue - settings: Settings - menu: Return to menu - - # Bottom left tutorial hints - tutorialHints: - title: Need help? - showHint: Show hint - hideHint: Close - - # When placing a blueprint - blueprintPlacer: - cost: Cost - - # Map markers - waypoints: - waypoints: Markers - hub: HUB - description: Left-click a marker to jump to it, right-click to delete it.

Press to create a marker from the current view, or right-click to create a marker at the selected location. - creationSuccessNotification: Marker has been created. - - # Shape viewer - shapeViewer: - title: Layers - empty: Empty - copyKey: Copy Key - - # Interactive tutorial - interactiveTutorial: - title: Tutorial - hints: - 1_1_extractor: Place an extractor on top of a circle shape to extract it! - 1_2_conveyor: >- - Connect the extractor with a conveyor belt to your hub!

Tip: Click and drag the belt with your mouse! - - 1_3_expand: >- - This is NOT an idle game! Build more extractors and belts to finish the goal quicker.

Tip: Hold SHIFT to place multiple extractors, and use R to rotate them. - -# All shop upgrades -shopUpgrades: - belt: - name: Belts, Distributor & Tunnels - description: Speed x → x - miner: - name: Extraction - description: Speed x → x - processors: - name: Cutting, Rotating & Stacking - description: Speed x → x - painting: - name: Mixing & Painting - description: Speed x → x - -# Buildings and their name / description -buildings: - hub: - deliver: Deliver - toUnlock: to unlock - levelShortcut: LVL - - belt: - default: - name: &belt Conveyor Belt - description: Transports items, hold and drag to place multiple. - - # Internal name for the Extractor - miner: - default: - name: &miner Extractor - description: Place over a shape or color to extract it. - - chainable: - name: Extractor (Chain) - description: Place over a shape or color to extract it. Can be chained. - - # Internal name for the Tunnel - underground_belt: - default: - name: &underground_belt Tunnel - description: Allows you to tunnel resources under buildings and belts. - - tier2: - name: Tunnel Tier II - description: Allows you to tunnel resources under buildings and belts. - - # Internal name for the Balancer - splitter: - default: - name: &splitter Balancer - description: Multifunctional - Evenly distributes all inputs onto all outputs. - - compact: - name: Merger (compact) - description: Merges two conveyor belts into one. - - compact-inverse: - name: Merger (compact) - description: Merges two conveyor belts into one. - - compact-merge: - name: Splitter (compact) - description: Splits one conveyor belt into two. - - compact-merge-inverse: - name: Splitter (compact) - description: Splits one conveyor belt into two. - - cutter: - default: - name: &cutter Cutter - description: Cuts shapes from top to bottom and outputs both halves. If you use only one part, be sure to destroy the other part or it will stall! - quad: - name: Cutter (Quad) - description: Cuts shapes into four parts. If you use only one part, be sure to destroy the other parts or it will stall! - - rotater: - default: - name: &rotater Rotate - description: Rotates shapes clockwise by 90 degrees. - ccw: - name: Rotate (CCW) - description: Rotates shapes counter-clockwise by 90 degrees. - fl: - name: Rotate (180) - description: Rotates shapes by 180 degrees. - - stacker: - default: - name: &stacker Stacker - description: Stacks both items. If they can not be merged, the right item is placed above the left item. - - mixer: - default: - name: &mixer Color Mixer - description: Mixes two colors using additive blending. - - painter: - default: - name: &painter Painter - description: &painter_desc Colors the whole shape on the left input with the color from the top input. - - mirrored: - name: *painter - description: *painter_desc - - double: - name: Painter (Double) - description: Colors the shapes on the left inputs with the color from the top input. - - quad: - name: Painter (Quad) - description: Allows you to color each quadrant of the shape with a different color. - - trash: - default: - name: &trash Trash - description: Accepts inputs from all sides and destroys them. Forever. - - storage: - name: Storage - description: Stores excess items, up to a given capacity. Can be used as an overflow gate. - - wire: - default: - name: &wire Wire - description: &wire_desc Allows to connect logical components and can transfer items, colors or boolean signals. - - wire_tunnel: - default: - name: &wire_tunnel Wire Tunnel - description: Allows to cross two wires without connecting them. - - coating: - name: Wire Insulation - description: Allows to pass through signals without connecting to other wires on the sides. - - constant_signal: - default: - name: &constant_signal Constant Signal - description: Emits a constant signal (shape, color or boolean). - - lever: - default: - name: &lever Switch - description: Can be toggled to emit 1 / 0 - - logic_gate: - default: - name: &logic_gate AND Gate - description: Emits a truthy boolean signal if both inputs are truthy. - not: - name: NOT - description: Inverts the given signal. - xor: - name: XOR - description: Emits a truthy signal if one of the inputs is truthy, but not both. - or: - name: OR - description: Emits a truthy signal if one of the inputs is truthy. - - transistor: - name: Gate - description: Only forwards the bottom input if the left input is true. - - filter: - default: - name: &filter Filter - # TEMP - description: Only leaves through items who match exactly the provided shape / color. If you put in a boolean 1, it leaves everything through, if you put in a 0 it will leave nothing through. - - display: - default: - name: &display Display - # TEMP - description: Can be connected on the wires layer to show a color or shape. When inputting a boolean item, the display will be white if the value is 1. - - reader: - default: - name: &reader Belt Reader - # TEMP - description: Allows to read the current item from a belt, as well as measuring the throughput. - - virtual_processor: - default: - name: &virtual_processor Virtual Cutter - description: Virtually cuts the shape input from top to bottom and returns both halfs. - - analyzer: - name: Shape Analyzer - description: Analyzes the top right quadrant of the lowest layer of the shape and returns its shape and color - - rotater: - name: Virtual Rotater - description: Virtually rotates the shape by 90 degrees clockwise. - - unstacker: - name: Virtual Unstacker - description: Returns the topmost layer to the right, and the remaining ones on the left. - - shapecompare: - name: Compare - description: Returns true if both items are exactly equal. Can compare shapes, items and booleans. - -storyRewards: - # Those are the rewards gained from completing the store - reward_cutter_and_trash: - title: Cutting Shapes - desc: You just unlocked the cutter - it cuts shapes in half from top to bottom regardless of its orientation!

Be sure to get rid of the waste, or otherwise it will stall - For this purpose I have given you the trash can, which destroys everything you put into it! - - reward_rotater: - title: Rotating - desc: The rotater has been unlocked! It rotates shapes clockwise by 90 degrees. - - reward_painter: - title: Painting - desc: >- - The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are colorblind, there is a colorblind mode in the settings! - - reward_mixer: - title: Color Mixing - desc: The mixer has been unlocked - Combine two colors using additive blending with this building! - - reward_stacker: - title: Combiner - desc: You can now combine shapes with the combiner! Both inputs are combined, and if they can be put next to each other, they will be fused. If not, the right input is stacked on top of the left input! - - reward_splitter: - title: Splitter/Merger - desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging items onto multiple belts!

- - reward_tunnel: - title: Tunnel - desc: The tunnel has been unlocked - You can now tunnel items through belts and buildings with it! - - reward_rotater_ccw: - title: CCW Rotating - desc: You have unlocked a variant of the rotater - It allows you to rotate shapes counter-clockwise! To build it, select the rotater and press 'T' to cycle through its variants! - - reward_miner_chainable: - title: Chaining Extractor - desc: You have unlocked the chaining extractor! It can forward its resources to other extractors so you can more efficiently extract resources! - - reward_underground_belt_tier_2: - title: Tunnel Tier II - desc: You have unlocked a new variant of the tunnel - It has a bigger range, and you can also mix-n-match those tunnels now! - - reward_splitter_compact: - title: Compact Balancer - desc: >- - You have unlocked a compact variant of the balancer - It accepts two inputs and merges them into one belt! - - reward_cutter_quad: - title: Quad Cutting - desc: You have unlocked a variant of the cutter - It allows you to cut shapes in four parts instead of just two! - - reward_painter_double: - title: Double Painting - desc: You have unlocked a variant of the painter - It works as the regular painter but processes two shapes at once consuming just one color instead of two! - - reward_painter_quad: - title: Quad Painting - desc: You have unlocked a variant of the painter - It allows you to paint each part of the shape individually! - - reward_storage: - title: Storage Buffer - desc: You have unlocked a variant of the trash - It allows you to store items up to a given capacity! - - reward_freeplay: - title: Freeplay - desc: You did it! You unlocked the free-play mode! This means that shapes are now randomly generated! (No worries, more content is planned for the standalone!) - - reward_blueprints: - title: Blueprints - desc: You can now copy and paste parts of your factory! Select an area (Hold CTRL, then drag with your mouse), and press 'C' to copy it.

Pasting it is not free, you need to produce blueprint shapes to afford it! (Those you just delivered). - - # Special reward, which is shown when there is no reward actually - no_reward: - title: Next level - desc: >- - This level gave you no reward, but the next one will!

PS: Better not destroy your existing factory - You'll need all those shapes later to unlock upgrades! - - no_reward_freeplay: - title: Next level - desc: >- - Congratulations! By the way, more content is planned for the standalone! - -settings: - title: Settings - categories: - general: General - userInterface: User Interface - advanced: Advanced - performance: Performance - - versionBadges: - dev: Development - staging: Staging - prod: Production - buildDate: Built - - labels: - uiScale: - title: Interface scale - description: >- - Changes the size of the user interface. The interface will still scale based on your device's resolution, but this setting controls the amount of scaling. - scales: - super_small: Super small - small: Small - regular: Regular - large: Large - huge: Huge - - autosaveInterval: - title: Autosave Interval - description: >- - Controls how often the game saves automatically. You can also disable it entirely here. - - intervals: - one_minute: 1 Minute - two_minutes: 2 Minutes - five_minutes: 5 Minutes - ten_minutes: 10 Minutes - twenty_minutes: 20 Minutes - disabled: Disabled - - scrollWheelSensitivity: - title: Zoom sensitivity - description: >- - Changes how sensitive the zoom is (Either mouse wheel or trackpad). - sensitivity: - super_slow: Super slow - slow: Slow - regular: Regular - fast: Fast - super_fast: Super fast - - movementSpeed: - title: Movement speed - description: >- - Changes how fast the view moves when using the keyboard. - speeds: - super_slow: Super slow - slow: Slow - regular: Regular - fast: Fast - super_fast: Super Fast - extremely_fast: Extremely Fast - - language: - title: Language - description: >- - Change the language. All translations are user-contributed and might be incomplete! - - enableColorBlindHelper: - title: Color Blind Mode - description: >- - Enables various tools which allow you to play the game if you are color blind. - - fullscreen: - title: Fullscreen - description: >- - It is recommended to play the game in fullscreen to get the best experience. Only available in the standalone. - - soundsMuted: - title: Mute Sounds - description: >- - If enabled, mutes all sound effects. - - musicMuted: - title: Mute Music - description: >- - If enabled, mutes all music. - - theme: - title: Game theme - description: >- - Choose the game theme (light / dark). - themes: - dark: Dark - light: Light - - refreshRate: - title: Tick Rate - description: >- - The game will automatically adjust the tickrate to be between this target tickrate and half of it. For example, with a tickrate of 60hz, the game will try to stay at 60hz, and if your computer can't handle it it will go down until it eventually reaches 30hz. - - alwaysMultiplace: - title: Multiplace - description: >- - If enabled, all buildings will stay selected after placement until you cancel it. This is equivalent to holding SHIFT permanently. - - offerHints: - title: Hints & Tutorials - description: >- - Whether to offer hints and tutorials while playing. Also hides certain UI elements up to a given level to make it easier to get into the game. - - enableTunnelSmartplace: - title: Smart Tunnels - description: >- - When enabled, placing tunnels will automatically remove unnecessary belts. This also enables you to drag tunnels and excess tunnels will get removed. - - vignette: - title: Vignette - description: >- - Enables the vignette, which darkens the screen corners and makes text easier to read. - - rotationByBuilding: - title: Rotation by building type - description: >- - Each building type remembers the rotation you last set it to individually. This may be more comfortable if you frequently switch between placing different building types. - - compactBuildingInfo: - title: Compact Building Infos - description: >- - Shortens info boxes for buildings by only showing their ratios. Otherwise a description and image is shown. - - disableCutDeleteWarnings: - title: Disable Cut/Delete Warnings - description: >- - Disables the warning dialogs brought up when cutting/deleting more than 100 entities. - - lowQualityMapResources: - title: Low Quality Map Resources - description: >- - Simplifies the rendering of resources on the map when zoomed in to improve performance. - It even looks cleaner, so be sure to try it out! - - disableTileGrid: - title: Disable Grid - description: >- - Disabling the tile grid can help with the performance. This also makes the game look cleaner! - - clearCursorOnDeleteWhilePlacing: - title: Clear Cursor on Right Click - description: >- - Enabled by default, clears the cursor whenever you right click while you have a building selected for placement. If disabled, you can delete buildings by right-clicking while placing a building. - - lowQualityTextures: - title: Low quality textures (Ugly) - description: >- - Uses low quality textures to save performance. This will make the game look very ugly! - - displayChunkBorders: - title: Display Chunk Borders - description: >- - The game is divided into chunks of 16x16 tiles, if this setting is enabled the borders of each chunk are displayed. - -keybindings: - title: Keybindings - hint: >- - Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different placement options. - - resetKeybindings: Reset Keybindings - - categoryLabels: - general: Application - ingame: Game - navigation: Navigating - placement: Placement - massSelect: Mass Select - buildings: Building Shortcuts - placementModifiers: Placement Modifiers - - mappings: - confirm: Confirm - back: Back - mapMoveUp: Move Up - mapMoveRight: Move Right - mapMoveDown: Move Down - mapMoveLeft: Move Left - mapMoveFaster: Move Faster - centerMap: Center Map - - mapZoomIn: Zoom in - mapZoomOut: Zoom out - createMarker: Create Marker - - menuOpenShop: Upgrades - menuOpenStats: Statistics - menuClose: Close Menu - - toggleHud: Toggle HUD - toggleFPSInfo: Toggle FPS and Debug Info - switchLayers: Switch layers - exportScreenshot: Export whole Base as Image - - # --- Do not translate the values in this section - belt: *belt - splitter: *splitter - underground_belt: *underground_belt - miner: *miner - cutter: *cutter - rotater: *rotater - stacker: *stacker - mixer: *mixer - painter: *painter - trash: *trash - wire: *wire - constant_signal: *constant_signal - logic_gate: *logic_gate - lever: *lever - filter: *filter - wire_tunnel: *wire_tunnel - display: *display - reader: *reader - # --- - - pipette: Pipette - rotateWhilePlacing: Rotate - rotateInverseModifier: >- - Modifier: Rotate CCW instead - cycleBuildingVariants: Cycle Variants - confirmMassDelete: Delete area - pasteLastBlueprint: Paste last blueprint - cycleBuildings: Cycle Buildings - lockBeltDirection: Enable belt planner - switchDirectionLockSide: >- - Planner: Switch side - - massSelectStart: Hold and drag to start - massSelectSelectMultiple: Select multiple areas - massSelectCopy: Copy area - massSelectCut: Cut area - - placementDisableAutoOrientation: Disable automatic orientation - placeMultiple: Stay in placement mode - placeInverse: Invert automatic belt orientation - -about: - title: About this Game - body: >- - This game is open source and developed by Tobias Springer (this is me).

- - If you want to contribute, check out shapez.io on GitHub.

- - This game wouldn't have been possible without the great Discord community around my games - You should really join the Discord server!

- - The soundtrack was made by Peppsen - He's awesome.

- - Finally, huge thanks to my best friend Niklas - Without our Factorio sessions, this game would never have existed. - -changelog: - title: Changelog - -demo: - features: - restoringGames: Restoring savegames - importingGames: Importing savegames - oneGameLimit: Limited to one savegame - customizeKeybindings: Customizing Keybindings - exportingBase: Exporting whole Base as Image - - settingNotAvailable: Not available in the demo. +# +# GAME TRANSLATIONS +# +# Contributing: +# +# If you want to contribute, please make a pull request on this respository +# and I will have a look. +# +# Placeholders: +# +# Do *not* replace placeholders! Placeholders have a special syntax like +# `Hotkey: `. They are encapsulated within angle brackets. The correct +# translation for this one in German for example would be: `Taste: ` (notice +# how the placeholder stayed '' and was not replaced!) +# +# Adding a new language: +# +# If you want to add a new language, ask me in the Discord and I will setup +# the basic structure so the game also detects it. +# + +--- +steamPage: + # This is the short text appearing on the steam page + shortText: shapez.io is a game about building factories to automate the creation and processing of increasingly complex shapes across an infinitely expanding map. + + # This is the text shown above the Discord link + discordLink: Official Discord - Chat with me! + + # 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) + # - Please keep the markup (Stuff like [b], [list] etc) in the same format + longText: >- + [img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img] + + shapez.io is a game about building factories to automate the creation and processing of increasingly complex shapes across an infinitely expanding map. + + Upon delivering the requested shapes you'll progress within the game and unlock upgrades to speed up your factory. + + As the demand for shapes increases, you'll have to scale up your factory to meet the demand - Don't forget about resources though, you'll have to expand across the [b]infinite map[/b]! + + Soon you'll have to mix colors and paint your shapes with them - Combine red, green and blue color resources to produce different colors and paint shapes with them to satisfy the demand. + + This game features 18 progressive levels (Which should already keep you busy for hours!) but I'm constantly adding new content - There's a lot planned! + + Purchasing the game gives you access to the standalone version which has additional features, and you'll also receive access to newly developed features. + + [b]Standalone Advantages[/b] + + [list] + [*] Dark Mode + [*] Unlimited Waypoints + [*] Unlimited Savegames + [*] Additional settings + [*] Coming soon: Wires & Energy! Aiming for (roughly) end of July 2020. + [*] Coming soon: More Levels + [*] Allows me to further develop shapez.io ❤️ + [/list] + + [b]Future Updates[/b] + + I am updating the game often and trying to push an update at least once every week! + + [list] + [*] Different maps and challenges (e.g. maps with obstacles) + [*] Puzzles (Deliver the requested shape with a restricted area / set of buildings) + [*] A story mode where buildings have a cost + [*] Configurable map generator (Configure resource/shape size/density, seed and more) + [*] Additional types of shapes + [*] Performance improvements (The game already runs pretty well!) + [*] And much more! + [/list] + + [b]This game is open source![/b] + + Anybody can contribute, I'm actively involved in the community and attempt to review all suggestions and take feedback into consideration where possible. + Be sure to check out my trello board for the full roadmap! + + [b]Links[/b] + + [list] + [*] [url=https://discord.com/invite/HN7EVzV]Official Discord[/url] + [*] [url=https://trello.com/b/ISQncpJP/shapezio]Roadmap[/url] + [*] [url=https://www.reddit.com/r/shapezio]Subreddit[/url] + [*] [url=https://github.com/tobspr/shapez.io]Source code (GitHub)[/url] + [*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]Help translate[/url] + [/list] + +global: + loading: Loading + error: Error + + # How big numbers are rendered, e.g. "10,000" + thousandsDivider: "," + + # What symbol to use to seperate the integer part from the fractional part of a number, e.g. "0.4" + decimalSeparator: "." + + # The suffix for large numbers, e.g. 1.3k, 400.2M, etc. + suffix: + thousands: k + millions: M + billions: B + trillions: T + + # Shown for infinitely big numbers + infinite: inf + + time: + # Used for formatting past time dates + oneSecondAgo: one second ago + xSecondsAgo: seconds ago + oneMinuteAgo: one minute ago + xMinutesAgo: minutes ago + oneHourAgo: one hour ago + xHoursAgo: hours ago + oneDayAgo: one day ago + xDaysAgo: days ago + + # Short formats for times, e.g. '5h 23m' + secondsShort: s + minutesAndSecondsShort: m s + hoursAndMinutesShort: h m + + xMinutes: minutes + + keys: + tab: TAB + control: CTRL + alt: ALT + escape: ESC + shift: SHIFT + space: SPACE + +demoBanners: + # This is the "advertisement" shown in the main menu and other various places + title: Demo Version + intro: >- + Get the standalone to unlock all features! + +mainMenu: + play: Play + continue: Continue + newGame: New Game + changelog: Changelog + subreddit: Reddit + importSavegame: Import + openSourceHint: This game is open source! + discordLink: Official Discord Server + helpTranslate: Help translate! + madeBy: Made by + + # This is shown when using firefox and other browsers which are not supported. + browserWarning: >- + Sorry, but the game is known to run slow on your browser! Get the standalone version or download Google Chrome for the full experience. + + savegameLevel: Level + savegameLevelUnknown: Unknown Level + savegameUnnamed: Unnamed + +dialogs: + buttons: + ok: OK + delete: Delete + cancel: Cancel + later: Later + restart: Restart + reset: Reset + getStandalone: Get Standalone + deleteGame: I know what I am doing + viewUpdate: View Update + showUpgrades: Show Upgrades + showKeybindings: Show Keybindings + + importSavegameError: + title: Import Error + text: >- + Failed to import your savegame: + + importSavegameSuccess: + title: Savegame Imported + text: >- + Your savegame has been successfully imported. + + gameLoadFailure: + title: Game is broken + text: >- + Failed to load your savegame: + + confirmSavegameDelete: + title: Confirm deletion + text: >- + Are you sure you want to delete the game? + + savegameDeletionError: + title: Failed to delete + text: >- + Failed to delete the savegame: + + restartRequired: + title: Restart required + text: >- + You need to restart the game to apply the settings. + + editKeybinding: + title: Change Keybinding + desc: Press the key or mouse button you want to assign, or escape to cancel. + + resetKeybindingsConfirmation: + title: Reset keybindings + desc: This will reset all keybindings to their default values. Please confirm. + + keybindingsResetOk: + title: Keybindings reset + desc: The keybindings have been reset to their respective defaults! + + featureRestriction: + title: Demo Version + desc: You tried to access a feature () which is not available in the demo. Consider getting the standalone version for the full experience! + + oneSavegameLimit: + title: Limited savegames + desc: You can only have one savegame at a time in the demo version. Please remove the existing one or get the standalone version! + + updateSummary: + title: New update! + desc: >- + Here are the changes since you last played: + + upgradesIntroduction: + title: Unlock Upgrades + desc: >- + All shapes you produce can be used to unlock upgrades - Don't destroy your old factories! + The upgrades tab can be found on the top right corner of the screen. + + massDeleteConfirm: + title: Confirm delete + desc: >- + You are deleting a lot of buildings ( to be exact)! Are you sure you want to do this? + + massCutConfirm: + title: Confirm cut + desc: >- + You are cutting a lot of buildings ( to be exact)! Are you sure you want to do this? + + massCutInsufficientConfirm: + title: Confirm cut + desc: >- + You can not afford to paste this area! Are you sure you want to cut it? + + blueprintsNotUnlocked: + title: Not unlocked yet + desc: >- + Complete level 12 to unlock Blueprints! + + keybindingsIntroduction: + title: Useful keybindings + desc: >- + This game has a lot of keybindings which make it easier to build big factories. + Here are a few, but be sure to check out the keybindings!

+ CTRL + Drag: Select an area.
+ SHIFT: Hold to place multiple of one building.
+ ALT: Invert orientation of placed belts.
+ + createMarker: + title: New Marker + titleEdit: Edit Marker + desc: Give it a meaningful name, you can also include a short key of a shape (Which you can generate here) + + markerDemoLimit: + desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers! + + exportScreenshotWarning: + title: Export screenshot + desc: You requested to export your base as a screenshot. Please note that this can be quite slow for a big base and even crash your game! + + renameSavegame: + title: Rename Savegame + desc: You can rename your savegame here. + +ingame: + # This is shown in the top left corner and displays useful keybindings in + # every situation + keybindingsOverlay: + moveMap: Move + selectBuildings: Select area + stopPlacement: Stop placement + rotateBuilding: Rotate building + placeMultiple: Place multiple + reverseOrientation: Reverse orientation + disableAutoOrientation: Disable auto-orientation + toggleHud: Toggle HUD + placeBuilding: Place building + createMarker: Create marker + delete: Delete + pasteLastBlueprint: Paste last blueprint + lockBeltDirection: Enable belt planner + plannerSwitchSide: Flip planner side + cutSelection: Cut + copySelection: Copy + clearSelection: Clear selection + pipette: Pipette + switchLayers: Switch layers + + # Names of the colors, used for the color blind mode + colors: + red: Red + green: Green + blue: Blue + yellow: Yellow + purple: Magenta + cyan: Cyan + white: White + black: Black + uncolored: Gray + + # Everything related to placing buildings (I.e. as soon as you selected a building + # from the toolbar) + 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: Press to cycle variants. + + # Shows the hotkey in the ui, e.g. "Hotkey: Q" + hotkeyLabel: >- + Hotkey: + + infoTexts: + speed: Speed + range: Range + storage: Storage + oneItemPerSecond: 1 item / second + itemsPerSecond: items / s + itemsPerSecondDouble: (x2) + + tiles: tiles + + # The notification when completing a level + levelCompleteNotification: + # is replaced by the actual level, so this gets 'Level 03' for example. + levelTitle: Level + completed: Completed + unlockText: Unlocked ! + buttonNextLevel: Next Level + + # Notifications on the lower right + notifications: + newUpgrade: A new upgrade is available! + gameSaved: Your game has been saved. + + # The "Upgrades" window + shop: + title: Upgrades + buttonUnlock: Upgrade + + # Gets replaced to e.g. "Tier IX" + tier: Tier + + # The roman number for each tier + tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X] + + maximumLevel: MAXIMUM LEVEL (Speed x) + + # The "Statistics" window + statistics: + title: Statistics + dataSources: + stored: + title: Stored + description: Displaying amount of stored shapes in your central building. + produced: + title: Produced + description: Displaying all shapes your whole factory produces, including intermediate products. + delivered: + title: Delivered + description: Displaying shapes which are delivered to your central building. + noShapesProduced: No shapes have been produced so far. + + # Displays the shapes per second, e.g. '523 / s' + shapesPerSecond: / s + + # Settings menu, when you press "ESC" + settingsMenu: + playtime: Playtime + + buildingsPlaced: Buildings + beltsPlaced: Belts + + buttons: + continue: Continue + settings: Settings + menu: Return to menu + + # Bottom left tutorial hints + tutorialHints: + title: Need help? + showHint: Show hint + hideHint: Close + + # When placing a blueprint + blueprintPlacer: + cost: Cost + + # Map markers + waypoints: + waypoints: Markers + hub: HUB + description: Left-click a marker to jump to it, right-click to delete it.

Press to create a marker from the current view, or right-click to create a marker at the selected location. + creationSuccessNotification: Marker has been created. + + # Shape viewer + shapeViewer: + title: Layers + empty: Empty + copyKey: Copy Key + + # Interactive tutorial + interactiveTutorial: + title: Tutorial + hints: + 1_1_extractor: Place an extractor on top of a circle shape to extract it! + 1_2_conveyor: >- + Connect the extractor with a conveyor belt to your hub!

Tip: Click and drag the belt with your mouse! + + 1_3_expand: >- + This is NOT an idle game! Build more extractors and belts to finish the goal quicker.

Tip: Hold SHIFT to place multiple extractors, and use R to rotate them. + +# All shop upgrades +shopUpgrades: + belt: + name: Belts, Distributor & Tunnels + description: Speed x → x + miner: + name: Extraction + description: Speed x → x + processors: + name: Cutting, Rotating & Stacking + description: Speed x → x + painting: + name: Mixing & Painting + description: Speed x → x + +# Buildings and their name / description +buildings: + hub: + deliver: Deliver + toUnlock: to unlock + levelShortcut: LVL + + belt: + default: + name: &belt Conveyor Belt + description: Transports items, hold and drag to place multiple. + + # Internal name for the Extractor + miner: + default: + name: &miner Extractor + description: Place over a shape or color to extract it. + + chainable: + name: Extractor (Chain) + description: Place over a shape or color to extract it. Can be chained. + + # Internal name for the Tunnel + underground_belt: + default: + name: &underground_belt Tunnel + description: Allows you to tunnel resources under buildings and belts. + + tier2: + name: Tunnel Tier II + description: Allows you to tunnel resources under buildings and belts. + + # Internal name for the Balancer + splitter: + default: + name: &splitter Balancer + description: Multifunctional - Evenly distributes all inputs onto all outputs. + + compact: + name: Merger (compact) + description: Merges two conveyor belts into one. + + compact-inverse: + name: Merger (compact) + description: Merges two conveyor belts into one. + + compact-merge: + name: Splitter (compact) + description: Splits one conveyor belt into two. + + compact-merge-inverse: + name: Splitter (compact) + description: Splits one conveyor belt into two. + + cutter: + default: + name: &cutter Cutter + description: Cuts shapes from top to bottom and outputs both halves. If you use only one part, be sure to destroy the other part or it will stall! + quad: + name: Cutter (Quad) + description: Cuts shapes into four parts. If you use only one part, be sure to destroy the other parts or it will stall! + + rotater: + default: + name: &rotater Rotate + description: Rotates shapes clockwise by 90 degrees. + ccw: + name: Rotate (CCW) + description: Rotates shapes counter-clockwise by 90 degrees. + fl: + name: Rotate (180) + description: Rotates shapes by 180 degrees. + + stacker: + default: + name: &stacker Stacker + description: Stacks both items. If they can not be merged, the right item is placed above the left item. + + mixer: + default: + name: &mixer Color Mixer + description: Mixes two colors using additive blending. + + painter: + default: + name: &painter Painter + description: &painter_desc Colors the whole shape on the left input with the color from the top input. + + mirrored: + name: *painter + description: *painter_desc + + double: + name: Painter (Double) + description: Colors the shapes on the left inputs with the color from the top input. + + quad: + name: Painter (Quad) + description: Allows you to color each quadrant of the shape with a different color. + + trash: + default: + name: &trash Trash + description: Accepts inputs from all sides and destroys them. Forever. + + storage: + name: Storage + description: Stores excess items, up to a given capacity. Can be used as an overflow gate. + + wire: + default: + name: &wire Wire + description: &wire_desc Allows to connect logical components and can transfer items, colors or boolean signals. + + wire_tunnel: + default: + name: &wire_tunnel Wire Tunnel + description: Allows to cross two wires without connecting them. + + coating: + name: Wire Insulation + description: Allows to pass through signals without connecting to other wires on the sides. + + constant_signal: + default: + name: &constant_signal Constant Signal + description: Emits a constant signal (shape, color or boolean). + + lever: + default: + name: &lever Switch + description: Can be toggled to emit 1 / 0 + + logic_gate: + default: + name: &logic_gate AND Gate + description: Emits a truthy boolean signal if both inputs are truthy. + not: + name: NOT + description: Inverts the given signal. + xor: + name: XOR + description: Emits a truthy signal if one of the inputs is truthy, but not both. + or: + name: OR + description: Emits a truthy signal if one of the inputs is truthy. + + transistor: + name: Gate + description: Only forwards the bottom input if the left input is true. + + filter: + default: + name: &filter Filter + # TEMP + description: Only leaves through items who match exactly the provided shape / color. If you put in a boolean 1, it leaves everything through, if you put in a 0 it will leave nothing through. + + display: + default: + name: &display Display + # TEMP + description: Can be connected on the wires layer to show a color or shape. When inputting a boolean item, the display will be white if the value is 1. + + reader: + default: + name: &reader Belt Reader + # TEMP + description: Allows to read the current item from a belt, as well as measuring the throughput. + + virtual_processor: + default: + name: &virtual_processor Virtual Cutter + description: Virtually cuts the shape input from top to bottom and returns both halfs. + + analyzer: + name: Shape Analyzer + description: Analyzes the top right quadrant of the lowest layer of the shape and returns its shape and color + + rotater: + name: Virtual Rotater + description: Virtually rotates the shape by 90 degrees clockwise. + + unstacker: + name: Virtual Unstacker + description: Returns the topmost layer to the right, and the remaining ones on the left. + + shapecompare: + name: Compare + description: Returns true if both items are exactly equal. Can compare shapes, items and booleans. + +storyRewards: + # Those are the rewards gained from completing the store + reward_cutter_and_trash: + title: Cutting Shapes + desc: You just unlocked the cutter - it cuts shapes in half from top to bottom regardless of its orientation!

Be sure to get rid of the waste, or otherwise it will stall - For this purpose I have given you the trash can, which destroys everything you put into it! + + reward_rotater: + title: Rotating + desc: The rotater has been unlocked! It rotates shapes clockwise by 90 degrees. + + reward_painter: + title: Painting + desc: >- + The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are colorblind, there is a colorblind mode in the settings! + + reward_mixer: + title: Color Mixing + desc: The mixer has been unlocked - Combine two colors using additive blending with this building! + + reward_stacker: + title: Combiner + desc: You can now combine shapes with the combiner! Both inputs are combined, and if they can be put next to each other, they will be fused. If not, the right input is stacked on top of the left input! + + reward_splitter: + title: Splitter/Merger + desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging items onto multiple belts!

+ + reward_tunnel: + title: Tunnel + desc: The tunnel has been unlocked - You can now tunnel items through belts and buildings with it! + + reward_rotater_ccw: + title: CCW Rotating + desc: You have unlocked a variant of the rotater - It allows you to rotate shapes counter-clockwise! To build it, select the rotater and press 'T' to cycle through its variants! + + reward_miner_chainable: + title: Chaining Extractor + desc: You have unlocked the chaining extractor! It can forward its resources to other extractors so you can more efficiently extract resources! + + reward_underground_belt_tier_2: + title: Tunnel Tier II + desc: You have unlocked a new variant of the tunnel - It has a bigger range, and you can also mix-n-match those tunnels now! + + reward_splitter_compact: + title: Compact Balancer + desc: >- + You have unlocked a compact variant of the balancer - It accepts two inputs and merges them into one belt! + + reward_cutter_quad: + title: Quad Cutting + desc: You have unlocked a variant of the cutter - It allows you to cut shapes in four parts instead of just two! + + reward_painter_double: + title: Double Painting + desc: You have unlocked a variant of the painter - It works as the regular painter but processes two shapes at once consuming just one color instead of two! + + reward_painter_quad: + title: Quad Painting + desc: You have unlocked a variant of the painter - It allows you to paint each part of the shape individually! + + reward_storage: + title: Storage Buffer + desc: You have unlocked a variant of the trash - It allows you to store items up to a given capacity! + + reward_freeplay: + title: Freeplay + desc: You did it! You unlocked the free-play mode! This means that shapes are now randomly generated! (No worries, more content is planned for the standalone!) + + reward_blueprints: + title: Blueprints + desc: You can now copy and paste parts of your factory! Select an area (Hold CTRL, then drag with your mouse), and press 'C' to copy it.

Pasting it is not free, you need to produce blueprint shapes to afford it! (Those you just delivered). + + # Special reward, which is shown when there is no reward actually + no_reward: + title: Next level + desc: >- + This level gave you no reward, but the next one will!

PS: Better not destroy your existing factory - You'll need all those shapes later to unlock upgrades! + + no_reward_freeplay: + title: Next level + desc: >- + Congratulations! By the way, more content is planned for the standalone! + +settings: + title: Settings + categories: + general: General + userInterface: User Interface + advanced: Advanced + performance: Performance + + versionBadges: + dev: Development + staging: Staging + prod: Production + buildDate: Built + + labels: + uiScale: + title: Interface scale + description: >- + Changes the size of the user interface. The interface will still scale based on your device's resolution, but this setting controls the amount of scaling. + scales: + super_small: Super small + small: Small + regular: Regular + large: Large + huge: Huge + + autosaveInterval: + title: Autosave Interval + description: >- + Controls how often the game saves automatically. You can also disable it entirely here. + + intervals: + one_minute: 1 Minute + two_minutes: 2 Minutes + five_minutes: 5 Minutes + ten_minutes: 10 Minutes + twenty_minutes: 20 Minutes + disabled: Disabled + + scrollWheelSensitivity: + title: Zoom sensitivity + description: >- + Changes how sensitive the zoom is (Either mouse wheel or trackpad). + sensitivity: + super_slow: Super slow + slow: Slow + regular: Regular + fast: Fast + super_fast: Super fast + + movementSpeed: + title: Movement speed + description: >- + Changes how fast the view moves when using the keyboard. + speeds: + super_slow: Super slow + slow: Slow + regular: Regular + fast: Fast + super_fast: Super Fast + extremely_fast: Extremely Fast + + language: + title: Language + description: >- + Change the language. All translations are user-contributed and might be incomplete! + + enableColorBlindHelper: + title: Color Blind Mode + description: >- + Enables various tools which allow you to play the game if you are color blind. + + fullscreen: + title: Fullscreen + description: >- + It is recommended to play the game in fullscreen to get the best experience. Only available in the standalone. + + soundsMuted: + title: Mute Sounds + description: >- + If enabled, mutes all sound effects. + + musicMuted: + title: Mute Music + description: >- + If enabled, mutes all music. + + soundVolume: + title: Sound Volume + description: >- + Set the volume for sound effects + + musicVolume: + title: Music Volume + description: >- + Set the volume for music + + theme: + title: Game theme + description: >- + Choose the game theme (light / dark). + themes: + dark: Dark + light: Light + + refreshRate: + title: Tick Rate + description: >- + The game will automatically adjust the tickrate to be between this target tickrate and half of it. For example, with a tickrate of 60hz, the game will try to stay at 60hz, and if your computer can't handle it it will go down until it eventually reaches 30hz. + + alwaysMultiplace: + title: Multiplace + description: >- + If enabled, all buildings will stay selected after placement until you cancel it. This is equivalent to holding SHIFT permanently. + + offerHints: + title: Hints & Tutorials + description: >- + Whether to offer hints and tutorials while playing. Also hides certain UI elements up to a given level to make it easier to get into the game. + + enableTunnelSmartplace: + title: Smart Tunnels + description: >- + When enabled, placing tunnels will automatically remove unnecessary belts. This also enables you to drag tunnels and excess tunnels will get removed. + + vignette: + title: Vignette + description: >- + Enables the vignette, which darkens the screen corners and makes text easier to read. + + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. This may be more comfortable if you frequently switch between placing different building types. + + compactBuildingInfo: + title: Compact Building Infos + description: >- + Shortens info boxes for buildings by only showing their ratios. Otherwise a description and image is shown. + + disableCutDeleteWarnings: + title: Disable Cut/Delete Warnings + description: >- + Disables the warning dialogs brought up when cutting/deleting more than 100 entities. + + lowQualityMapResources: + title: Low Quality Map Resources + description: >- + Simplifies the rendering of resources on the map when zoomed in to improve performance. + It even looks cleaner, so be sure to try it out! + + disableTileGrid: + title: Disable Grid + description: >- + Disabling the tile grid can help with the performance. This also makes the game look cleaner! + + clearCursorOnDeleteWhilePlacing: + title: Clear Cursor on Right Click + description: >- + Enabled by default, clears the cursor whenever you right click while you have a building selected for placement. If disabled, you can delete buildings by right-clicking while placing a building. + + lowQualityTextures: + title: Low quality textures (Ugly) + description: >- + Uses low quality textures to save performance. This will make the game look very ugly! + + displayChunkBorders: + title: Display Chunk Borders + description: >- + The game is divided into chunks of 16x16 tiles, if this setting is enabled the borders of each chunk are displayed. + +keybindings: + title: Keybindings + hint: >- + Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different placement options. + + resetKeybindings: Reset Keybindings + + categoryLabels: + general: Application + ingame: Game + navigation: Navigating + placement: Placement + massSelect: Mass Select + buildings: Building Shortcuts + placementModifiers: Placement Modifiers + + mappings: + confirm: Confirm + back: Back + mapMoveUp: Move Up + mapMoveRight: Move Right + mapMoveDown: Move Down + mapMoveLeft: Move Left + mapMoveFaster: Move Faster + centerMap: Center Map + + mapZoomIn: Zoom in + mapZoomOut: Zoom out + createMarker: Create Marker + + menuOpenShop: Upgrades + menuOpenStats: Statistics + menuClose: Close Menu + + toggleHud: Toggle HUD + toggleFPSInfo: Toggle FPS and Debug Info + switchLayers: Switch layers + exportScreenshot: Export whole Base as Image + + # --- Do not translate the values in this section + belt: *belt + splitter: *splitter + underground_belt: *underground_belt + miner: *miner + cutter: *cutter + rotater: *rotater + stacker: *stacker + mixer: *mixer + painter: *painter + trash: *trash + wire: *wire + constant_signal: *constant_signal + logic_gate: *logic_gate + lever: *lever + filter: *filter + wire_tunnel: *wire_tunnel + display: *display + reader: *reader + # --- + + pipette: Pipette + rotateWhilePlacing: Rotate + rotateInverseModifier: >- + Modifier: Rotate CCW instead + cycleBuildingVariants: Cycle Variants + confirmMassDelete: Delete area + pasteLastBlueprint: Paste last blueprint + cycleBuildings: Cycle Buildings + lockBeltDirection: Enable belt planner + switchDirectionLockSide: >- + Planner: Switch side + + massSelectStart: Hold and drag to start + massSelectSelectMultiple: Select multiple areas + massSelectCopy: Copy area + massSelectCut: Cut area + + placementDisableAutoOrientation: Disable automatic orientation + placeMultiple: Stay in placement mode + placeInverse: Invert automatic belt orientation + +about: + title: About this Game + body: >- + This game is open source and developed by Tobias Springer (this is me).

+ + If you want to contribute, check out shapez.io on GitHub.

+ + This game wouldn't have been possible without the great Discord community around my games - You should really join the Discord server!

+ + The soundtrack was made by Peppsen - He's awesome.

+ + Finally, huge thanks to my best friend Niklas - Without our Factorio sessions, this game would never have existed. + +changelog: + title: Changelog + +demo: + features: + restoringGames: Restoring savegames + importingGames: Importing savegames + oneGameLimit: Limited to one savegame + customizeKeybindings: Customizing Keybindings + exportingBase: Exporting whole Base as Image + + settingNotAvailable: Not available in the demo. diff --git a/translations/base-tr.yaml b/translations/base-tr.yaml index eff8521f..26b95e35 100644 --- a/translations/base-tr.yaml +++ b/translations/base-tr.yaml @@ -72,7 +72,7 @@ steamPage: Bu oyuna herkes katkıda bulunabilir! Aktif olarak toplulukğa katkıda bulunuyorum ve bütün önerileri gözden geçirmeye çalışıyorum. Yol planıma Trello'dan bakmayı unutmayın! - + [b]Links[/b] @@ -274,7 +274,6 @@ dialogs: title: Kesmeyi onayla desc: Seçili yapıları yapıştırmak için yeterli kaynağınız yok! Kesmek istediğinize emin misiniz? - ingame: # This is shown in the top left corner and displays useful keybindings in # every situation @@ -618,7 +617,7 @@ storyRewards: title: Özgür Mod desc: Başardın! Özgür mod açıldı! Merkeze istenilen şekiller artık rastgele oluşturulacak! (Merak etme, yeni içerikler planlanıyor!) - reward_blueprints: # 'Taslaklar' yerine 'planlar' da kullanılabilir. + reward_blueprints: # 'Taslaklar' yerine 'planlar' da kullanılabilir. title: Taslaklar desc: Fabrikanın bölümlerini artık kopyalayıp yapıştırabilirsin! Bir alan seç (CTRL tuşuna bas ve fareyi sol-tık tuşuna basarak sürükle), ve kopyalamak için 'C' tuşuna bas.

Kopyaladığın taslağı bedel karşılığı yapıştırabilmek için taslak şekilleri üretmelisin! (Az önce teslim ettiğin şekiller). @@ -640,7 +639,7 @@ settings: userInterface: Kullanıcı Arayüzü advanced: Gelİşmİş - versionBadges: # Development, Staging, Production + versionBadges: # Development, Staging, Production dev: Geliştirme staging: Yükseltme prod: Üretim