1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-13 13:04:03 +00:00
This commit is contained in:
Git-Nivrak 2020-10-08 10:04:25 +03:00
commit 52baa337ca
105 changed files with 4083 additions and 3838 deletions

8
.editorconfig Executable file
View File

@ -0,0 +1,8 @@
root = true
[{src, translations}/*]
end_of_line = crlf
insert_final_newline = true
indent_style = space
indent_size = 4
charset = utf-8

View File

@ -35,19 +35,23 @@ jobs:
cd gulp/
yarn
cd ..
- name: Lint
run: |
yarn lint
- name: YAML Lint
uses: ibiqlik/action-yamllint@v1.0.0
with:
file_or_dir: translations/*.yaml
- name: TSLint
run: |
cd gulp
yarn gulp translations.fullBuild
cd ..
yarn tslint
yaml-lint:
name: yaml-lint
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v2
- name: YAML Lint
uses: ibiqlik/action-yamllint@v1.0.0
with:
file_or_dir: translations/*.yaml

View File

@ -4,3 +4,4 @@ rules:
line-length:
level: warning
max: 200
document-start: disable

31
Dockerfile Normal file
View File

@ -0,0 +1,31 @@
FROM node:12 as base
EXPOSE 3001 3005
WORKDIR /shapez.io
RUN apt-get update && apt-get install -y --no-install-recommends \
ffmpeg default-jre \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY package.json yarn.lock ./
RUN yarn
COPY gulp ./gulp
WORKDIR /shapez.io/gulp
RUN yarn
WORKDIR /shapez.io
COPY res ./res
COPY src/html ./src/html
COPY src/css ./src/css
COPY version ./version
COPY sync-translations.js ./
COPY translations ./translations
COPY src/js ./src/js
COPY res_raw ./res_raw
COPY .git ./.git
WORKDIR /shapez.io/gulp
ENTRYPOINT ["yarn", "gulp"]

View File

@ -86,8 +86,16 @@ gulp.task("utils.cleanBuildTempFolder", () => {
.src(path.join(__dirname, "..", "src", "js", "built-temp"), { read: false, allowEmpty: true })
.pipe($.clean({ force: true }));
});
gulp.task("utils.cleanImageBuildFolder", () => {
return gulp
.src(path.join(__dirname, "res_built"), { read: false, allowEmpty: true })
.pipe($.clean({ force: true }));
});
gulp.task("utils.cleanup", gulp.series("utils.cleanBuildFolder", "utils.cleanBuildTempFolder"));
gulp.task(
"utils.cleanup",
gulp.series("utils.cleanBuildFolder", "utils.cleanImageBuildFolder", "utils.cleanBuildTempFolder")
);
// Requires no uncomitted files
gulp.task("utils.requireCleanWorkingTree", cb => {
@ -234,12 +242,13 @@ gulp.task(
"build.standalone.dev",
gulp.series(
"utils.cleanup",
"imgres.buildAtlas",
"imgres.atlasToJson",
"imgres.atlas",
"sounds.dev",
"imgres.copyImageResources",
"imgres.copyNonImageResources",
"translations.fullBuild",
"js.standalone-dev",
"css.dev",
"html.standalone-dev"
)

View File

@ -173,6 +173,8 @@ function gulptasksImageResources($, gulp, buildFolder) {
gulp.task(
"imgres.allOptimized",
gulp.parallel(
"imgres.buildAtlas",
"imgres.atlasToJson",
"imgres.atlasOptimized",
"imgres.copyNonImageResources",
"imgres.copyImageResourcesOptimized"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -17,13 +17,10 @@
grid-template-rows: 1fr 1fr;
@include S(margin-bottom, 4px);
color: #333438;
// text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2);
&.unpinable {
> canvas {
cursor: pointer;
pointer-events: all;
}
&.removable {
cursor: pointer;
pointer-events: all;
}
> canvas {
@ -31,16 +28,9 @@
@include S(height, 25px);
grid-column: 1 / 2;
grid-row: 1 / 3;
pointer-events: all;
transition: transform 0.1s ease-in-out;
transform-origin: D(2px) center;
will-change: transform;
position: relative;
pointer-events: none;
z-index: 20;
&:hover {
transform: scale(2);
z-index: 21;
}
position: relative;
}
> .amountLabel,

View File

@ -29,6 +29,7 @@ import { MobileWarningState } from "./states/mobile_warning";
import { PreloadState } from "./states/preload";
import { SettingsState } from "./states/settings";
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
import { RestrictionManager } from "./core/restriction_manager";
/**
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
@ -70,6 +71,9 @@ export class Application {
this.inputMgr = new InputDistributor(this);
this.backgroundResourceLoader = new BackgroundResourcesLoader(this);
// Restrictions (Like demo etc)
this.restrictionMgr = new RestrictionManager(this);
// Platform dependent stuff
/** @type {StorageInterface} */

View File

@ -1,5 +1,3 @@
import { queryParamOptions } from "./query_parameters";
export const IS_DEBUG =
G_IS_DEV &&
typeof window !== "undefined" &&
@ -7,13 +5,10 @@ export const IS_DEBUG =
(window.location.host.indexOf("localhost:") >= 0 || window.location.host.indexOf("192.168.0.") >= 0) &&
window.location.search.indexOf("nodebug") < 0;
export const IS_DEMO = queryParamOptions.fullVersion
? false
: (!G_IS_DEV && !G_IS_STANDALONE) ||
(typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0);
export const SUPPORT_TOUCH = false;
export const IS_MAC = navigator.platform.toLowerCase().indexOf("mac") >= 0;
const smoothCanvas = true;
export const THIRDPARTY_URLS = {
@ -64,7 +59,7 @@ export const globalConfig = {
undergroundBeltMaxTilesByTier: [5, 9],
readerAnalyzeIntervalSeconds: G_IS_DEV ? 3 : 10,
readerAnalyzeIntervalSeconds: 10,
buildingSpeeds: {
cutter: 1 / 4,

View File

@ -81,10 +81,6 @@ export class ReadWriteProxy {
return this.writeAsync();
}
getCurrentData() {
return this.currentData;
}
/**
*
* @param {object} obj

View File

@ -0,0 +1,155 @@
/* typehints:start */
import { Application } from "../application";
/* typehints:end */
import { IS_MAC } from "./config";
import { ExplainedResult } from "./explained_result";
import { queryParamOptions } from "./query_parameters";
import { ReadWriteProxy } from "./read_write_proxy";
export class RestrictionManager extends ReadWriteProxy {
/**
* @param {Application} app
*/
constructor(app) {
super(app, "restriction-flags.bin");
this.currentData = this.getDefaultData();
}
// -- RW Proxy Impl
/**
* @param {any} data
*/
verify(data) {
return ExplainedResult.good();
}
/**
*/
getDefaultData() {
return {
version: this.getCurrentVersion(),
savegameV1119Imported: false,
};
}
/**
*/
getCurrentVersion() {
return 1;
}
/**
* @param {any} data
*/
migrate(data) {
// Todo
return ExplainedResult.good();
}
initialize() {
return this.readAsync().then(() => {
if (this.currentData.savegameV1119Imported) {
console.warn("Levelunlock is granted to current user due to past savegame");
}
});
}
// -- End RW Proxy Impl
/**
* Checks if there are any savegames from the 1.1.19 version
*/
onHasLegacySavegamesChanged(has119Savegames = false) {
if (has119Savegames && !this.currentData.savegameV1119Imported) {
this.currentData.savegameV1119Imported = true;
console.warn("Current user now has access to all levels due to 1119 savegame");
return this.writeAsync();
}
return Promise.resolve();
}
/**
* Returns if the app is currently running as the limited version
* @returns {boolean}
*/
isLimitedVersion() {
if (IS_MAC) {
// On mac, the full version is always active
return false;
}
if (G_IS_STANDALONE) {
// Standalone is never limited
return false;
}
if (queryParamOptions.fullVersion) {
// Full version is activated via flag
return false;
}
if (G_IS_DEV) {
return typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0;
}
return true;
}
/**
* Returns if the app markets the standalone version on steam
* @returns {boolean}
*/
getIsStandaloneMarketingActive() {
return this.isLimitedVersion();
}
/**
* Returns if exporting the base as a screenshot is possible
* @returns {boolean}
*/
getIsExportingScreenshotsPossible() {
return !this.isLimitedVersion();
}
/**
* Returns the maximum number of supported waypoints
* @returns {number}
*/
getMaximumWaypoints() {
return this.isLimitedVersion() ? 2 : 1e20;
}
/**
* Returns if the user has unlimited savegames
* @returns {boolean}
*/
getHasUnlimitedSavegames() {
return !this.isLimitedVersion();
}
/**
* Returns if the app has all settings available
* @returns {boolean}
*/
getHasExtendedSettings() {
return !this.isLimitedVersion();
}
/**
* Returns if all upgrades are available
* @returns {boolean}
*/
getHasExtendedUpgrades() {
return !this.isLimitedVersion() || this.currentData.savegameV1119Imported;
}
/**
* Returns if all levels & freeplay is available
* @returns {boolean}
*/
getHasExtendedLevelsAndFreeplay() {
return !this.isLimitedVersion() || this.currentData.savegameV1119Imported;
}
}

View File

@ -108,17 +108,6 @@ export class RandomNumberGenerator {
assert(max > min, "rng: max <= min");
return Math.floor(this.next() * (max - min) + min);
}
/**
* @param {number} min
* @param {number} max
* @returns {number} Integer in range [min, max]
*/
nextIntRangeInclusive(min, max) {
assert(Number.isFinite(min), "Minimum is no integer");
assert(Number.isFinite(max), "Maximum is no integer");
assert(max > min, "rng: max <= min");
return Math.round(this.next() * (max - min) + min);
}
/**
* @param {number} min

View File

@ -681,3 +681,72 @@ export function fillInLinkIntoTranslation(translation, link) {
.replace("<link>", "<a href='" + link + "' target='_blank'>")
.replace("</link>", "</a>");
}
/**
* Generates a file download
* @param {string} filename
* @param {string} text
*/
export function generateFileDownload(filename, text) {
var element = document.createElement("a");
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
/**
* Starts a file chooser
* @param {string} acceptedType
*/
export function startFileChoose(acceptedType = ".bin") {
var input = document.createElement("input");
input.type = "file";
input.accept = acceptedType;
return new Promise(resolve => {
input.onchange = _ => resolve(input.files[0]);
input.click();
});
}
const romanLiterals = [
"0", // NULL
"I",
"II",
"III",
"IV",
"V",
"VI",
"VII",
"VIII",
"IX",
"X",
"XI",
"XII",
"XIII",
"XIV",
"XV",
"XVI",
"XVII",
"XVIII",
"XIX",
"XX",
];
/**
*
* @param {number} number
* @returns {string}
*/
export function getRomanNumber(number) {
number = Math.max(0, Math.round(number));
if (number < romanLiterals.length) {
return romanLiterals[number];
}
return String(number);
}

View File

@ -1111,7 +1111,7 @@ export class BeltPath extends BasicSerializableObject {
isFirstItemProcessed = false;
this.spacingToFirstItem += clampedProgress;
if (remainingVelocity < 0.01) {
if (remainingVelocity < 1e-7) {
break;
}
}

View File

@ -1,13 +1,9 @@
import { globalConfig } from "../core/config";
import { DrawParameters } from "../core/draw_parameters";
import { createLogger } from "../core/logging";
import { findNiceIntegerValue } from "../core/utils";
import { Vector } from "../core/vector";
import { Entity } from "./entity";
import { GameRoot } from "./root";
import { blueprintShape } from "./upgrades";
const logger = createLogger("blueprint");
export class Blueprint {
/**
@ -142,7 +138,7 @@ export class Blueprint {
* @param {GameRoot} root
*/
canAfford(root) {
return root.hubGoals.getShapesStoredByKey(blueprintShape) >= this.getCost();
return root.hubGoals.getShapesStoredByKey(root.gameMode.getBlueprintShapeKey()) >= this.getCost();
}
/**

View File

@ -110,12 +110,7 @@ export class MetaVirtualProcessorBuilding extends MetaBuilding {
pinComp.setSlots([
{
pos: new Vector(0, 0),
direction: enumDirection.left,
type: enumPinSlotType.logicalEjector,
},
{
pos: new Vector(0, 0),
direction: enumDirection.right,
direction: enumDirection.top,
type: enumPinSlotType.logicalEjector,
},
{

View File

@ -511,7 +511,11 @@ export class Camera extends BasicSerializableObject {
this.clampZoomLevel();
this.desiredZoom = null;
const mousePosition = this.root.app.mousePosition;
let mousePosition = this.root.app.mousePosition;
if (!this.root.app.settings.getAllSettings().zoomToCursor) {
mousePosition = new Vector(this.root.gameWidth / 2, this.root.gameHeight / 2);
}
if (mousePosition) {
const worldPos = this.root.camera.screenToWorld(mousePosition);
const worldDelta = worldPos.sub(this.center);

View File

@ -31,6 +31,7 @@ import { KeyActionMapper } from "./key_action_mapper";
import { GameLogic } from "./logic";
import { MapView } from "./map_view";
import { defaultBuildingVariant } from "./meta_building";
import { RegularGameMode } from "./modes/regular";
import { ProductionAnalytics } from "./production_analytics";
import { GameRoot } from "./root";
import { ShapeDefinitionManager } from "./shape_definition_manager";
@ -101,6 +102,9 @@ export class GameCore {
// Needs to come first
root.dynamicTickrate = new DynamicTickrate(root);
// Init game mode
root.gameMode = new RegularGameMode(root);
// Init classes
root.camera = new Camera(root);
root.map = new MapView(root);

71
src/js/game/game_mode.js Normal file
View File

@ -0,0 +1,71 @@
/* typehints:start */
import { enumHubGoalRewards } from "./tutorial_goals";
/* typehints:end */
import { GameRoot } from "./root";
/** @typedef {{
* shape: string,
* amount: number
* }} UpgradeRequirement */
/** @typedef {{
* required: Array<UpgradeRequirement>
* improvement?: number,
* excludePrevious?: boolean
* }} TierRequirement */
/** @typedef {Array<TierRequirement>} UpgradeTiers */
/** @typedef {{
* shape: string,
* required: number,
* reward: enumHubGoalRewards,
* throughputOnly?: boolean
* }} LevelDefinition */
export class GameMode {
/**
*
* @param {GameRoot} root
*/
constructor(root) {
this.root = root;
}
/**
* Should return all available upgrades
* @returns {Object<string, UpgradeTiers>}
*/
getUpgrades() {
abstract;
return null;
}
/**
* Returns the blueprint shape key
* @returns {string}
*/
getBlueprintShapeKey() {
abstract;
return null;
}
/**
* Returns the goals for all levels including their reward
* @returns {Array<LevelDefinition>}
*/
getLevelDefinitions() {
abstract;
return null;
}
/**
* Should return whether free play is available or if the game stops
* after the predefined levels
* @returns {boolean}
*/
getIsFreeplayAvailable() {
return true;
}
}

View File

@ -1,14 +1,13 @@
import { globalConfig, IS_DEMO } from "../core/config";
import { globalConfig } from "../core/config";
import { RandomNumberGenerator } from "../core/rng";
import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils";
import { clamp } from "../core/utils";
import { BasicSerializableObject, types } from "../savegame/serialization";
import { enumColors } from "./colors";
import { enumItemProcessorTypes } from "./components/item_processor";
import { enumAnalyticsDataSource } from "./production_analytics";
import { GameRoot } from "./root";
import { enumSubShape, ShapeDefinition } from "./shape_definition";
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
import { UPGRADES } from "./upgrades";
import { enumHubGoalRewards } from "./tutorial_goals";
export class HubGoals extends BasicSerializableObject {
static getId() {
@ -23,27 +22,36 @@ export class HubGoals extends BasicSerializableObject {
};
}
deserialize(data) {
/**
*
* @param {*} data
* @param {GameRoot} root
*/
deserialize(data, root) {
const errorCode = super.deserialize(data);
if (errorCode) {
return errorCode;
}
if (IS_DEMO) {
this.level = Math.min(this.level, tutorialGoals.length);
const levels = root.gameMode.getLevelDefinitions();
// If freeplay is not available, clamp the level
if (!root.gameMode.getIsFreeplayAvailable()) {
this.level = Math.min(this.level, levels.length);
}
// Compute gained rewards
for (let i = 0; i < this.level - 1; ++i) {
if (i < tutorialGoals.length) {
const reward = tutorialGoals[i].reward;
if (i < levels.length) {
const reward = levels[i].reward;
this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1;
}
}
// Compute upgrade improvements
for (const upgradeId in UPGRADES) {
const tiers = UPGRADES[upgradeId];
const upgrades = this.root.gameMode.getUpgrades();
for (const upgradeId in upgrades) {
const tiers = upgrades[upgradeId];
const level = this.upgradeLevels[upgradeId] || 0;
let totalImprovement = 1;
for (let i = 0; i < level; ++i) {
@ -84,17 +92,16 @@ export class HubGoals extends BasicSerializableObject {
*/
this.upgradeLevels = {};
// Reset levels
for (const key in UPGRADES) {
this.upgradeLevels[key] = 0;
}
/**
* Stores the improvements for all upgrades
* @type {Object<string, number>}
*/
this.upgradeImprovements = {};
for (const key in UPGRADES) {
// Reset levels first
const upgrades = this.root.gameMode.getUpgrades();
for (const key in upgrades) {
this.upgradeLevels[key] = 0;
this.upgradeImprovements[key] = 1;
}
@ -120,7 +127,10 @@ export class HubGoals extends BasicSerializableObject {
* @returns {boolean}
*/
isEndOfDemoReached() {
return IS_DEMO && this.level >= tutorialGoals.length;
return (
!this.root.gameMode.getIsFreeplayAvailable() &&
this.level >= this.root.gameMode.getLevelDefinitions().length
);
}
/**
@ -215,8 +225,9 @@ export class HubGoals extends BasicSerializableObject {
*/
computeNextGoal() {
const storyIndex = this.level - 1;
if (storyIndex < tutorialGoals.length) {
const { shape, required, reward, throughputOnly } = tutorialGoals[storyIndex];
const levels = this.root.gameMode.getLevelDefinitions();
if (storyIndex < levels.length) {
const { shape, required, reward, throughputOnly } = levels[storyIndex];
this.currentGoal = {
/** @type {ShapeDefinition} */
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape),
@ -254,7 +265,7 @@ export class HubGoals extends BasicSerializableObject {
* Returns whether we are playing in free-play
*/
isFreePlay() {
return this.level >= tutorialGoals.length;
return this.level >= this.root.gameMode.getLevelDefinitions().length;
}
/**
@ -262,7 +273,7 @@ export class HubGoals extends BasicSerializableObject {
* @param {string} upgradeId
*/
canUnlockUpgrade(upgradeId) {
const tiers = UPGRADES[upgradeId];
const tiers = this.root.gameMode.getUpgrades()[upgradeId];
const currentLevel = this.getUpgradeLevel(upgradeId);
if (currentLevel >= tiers.length) {
@ -270,11 +281,6 @@ export class HubGoals extends BasicSerializableObject {
return false;
}
if (IS_DEMO && currentLevel >= 4) {
// DEMO
return false;
}
if (G_IS_DEV && globalConfig.debug.upgradesNoCost) {
return true;
}
@ -296,7 +302,7 @@ export class HubGoals extends BasicSerializableObject {
*/
getAvailableUpgradeCount() {
let count = 0;
for (const upgradeId in UPGRADES) {
for (const upgradeId in this.root.gameMode.getUpgrades()) {
if (this.canUnlockUpgrade(upgradeId)) {
++count;
}
@ -314,7 +320,7 @@ export class HubGoals extends BasicSerializableObject {
return false;
}
const upgradeTiers = UPGRADES[upgradeId];
const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId];
const currentLevel = this.getUpgradeLevel(upgradeId);
const tierData = upgradeTiers[currentLevel];
@ -363,7 +369,7 @@ export class HubGoals extends BasicSerializableObject {
if (allowUncolored) {
universalColors.push(enumColors.uncolored);
}
const index = rng.nextIntRangeInclusive(0, colorWheel.length - 3);
const index = rng.nextIntRange(0, colorWheel.length - 2);
const pickedColors = colorWheel.slice(index, index + 3);
pickedColors.push(rng.choice(universalColors));
return pickedColors;

View File

@ -15,7 +15,7 @@ import { HUDKeybindingOverlay } from "./parts/keybinding_overlay";
import { HUDUnlockNotification } from "./parts/unlock_notification";
import { HUDGameMenu } from "./parts/game_menu";
import { HUDShop } from "./parts/shop";
import { IS_MOBILE, globalConfig, IS_DEMO } from "../../core/config";
import { IS_MOBILE, globalConfig } from "../../core/config";
import { HUDMassSelector } from "./parts/mass_selector";
import { HUDVignetteOverlay } from "./parts/vignette_overlay";
import { HUDStatistics } from "./parts/statistics";
@ -46,7 +46,6 @@ import { HUDEditConstantSignal } from "./parts/edit_constant_signal";
import { HUDLayerPreview } from "./parts/layer_preview";
import { HUDMinerHighlight } from "./parts/miner_highlight";
import { HUDBetaOverlay } from "./parts/beta_overlay";
import { HUDPerformanceWarning } from "./parts/performance_warning";
import { HUDStandaloneAdvantages } from "./parts/standalone_advantages";
import { HUDCatMemes } from "./parts/cat_memes";
@ -90,7 +89,6 @@ export class GameHUD {
layerPreview: new HUDLayerPreview(this.root),
minerHighlight: new HUDMinerHighlight(this.root),
performanceWarning: new HUDPerformanceWarning(this.root),
// Typing hints
/* typehints:start */
@ -118,7 +116,7 @@ export class GameHUD {
this.parts.entityDebugger = new HUDEntityDebugger(this.root);
}
if (IS_DEMO) {
if (this.root.app.restrictionMgr.getIsStandaloneMarketingActive()) {
this.parts.watermark = new HUDWatermark(this.root);
this.parts.standaloneAdvantages = new HUDStandaloneAdvantages(this.root);
this.parts.catMemes = new HUDCatMemes(this.root);

View File

@ -7,7 +7,7 @@ export class HUDBetaOverlay extends BaseHUDPart {
parent,
"ingame_HUD_BetaOverlay",
[],
"<h2>UNSTABLE BETA VERSION</h2><span>Steam Release: 9th October 2020!</span>"
"<h2>UNSTABLE BETA VERSION</h2><span>Unfinalized & potential buggy content!</span>"
);
}

View File

@ -1,202 +1,203 @@
import { DrawParameters } from "../../../core/draw_parameters";
import { STOP_PROPAGATION } from "../../../core/signal";
import { TrackedState } from "../../../core/tracked_state";
import { makeDiv } from "../../../core/utils";
import { Vector } from "../../../core/vector";
import { T } from "../../../translations";
import { enumMouseButton } from "../../camera";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { blueprintShape } from "../../upgrades";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { Blueprint } from "../../blueprint";
import { SOUNDS } from "../../../platform/sound";
export class HUDBlueprintPlacer extends BaseHUDPart {
createElements(parent) {
const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey(blueprintShape);
const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80);
this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``);
makeDiv(this.costDisplayParent, null, ["label"], T.ingame.blueprintPlacer.cost);
const costContainer = makeDiv(this.costDisplayParent, null, ["costContainer"], "");
this.costDisplayText = makeDiv(costContainer, null, ["costText"], "");
costContainer.appendChild(blueprintCostShapeCanvas);
}
initialize() {
this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this);
/** @type {TypedTrackedState<Blueprint?>} */
this.currentBlueprint = new TrackedState(this.onBlueprintChanged, this);
/** @type {Blueprint?} */
this.lastBlueprintUsed = null;
const keyActionMapper = this.root.keyMapper;
keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this);
keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.abortPlacement, this);
keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this);
keyActionMapper.getBinding(KEYMAPPINGS.massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this);
this.root.camera.downPreHandler.add(this.onMouseDown, this);
this.root.camera.movePreHandler.add(this.onMouseMove, this);
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this);
this.root.signals.editModeChanged.add(this.onEditModeChanged, this);
this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent);
this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this);
}
abortPlacement() {
if (this.currentBlueprint.get()) {
this.currentBlueprint.set(null);
return STOP_PROPAGATION;
}
}
/**
* Called when the layer was changed
* @param {Layer} layer
*/
onEditModeChanged(layer) {
// Check if the layer of the blueprint differs and thus we have to deselect it
const blueprint = this.currentBlueprint.get();
if (blueprint) {
if (blueprint.layer !== layer) {
this.currentBlueprint.set(null);
}
}
}
/**
* Called when the blueprint is now affordable or not
* @param {boolean} canAfford
*/
onCanAffordChanged(canAfford) {
this.costDisplayParent.classList.toggle("canAfford", canAfford);
}
update() {
const currentBlueprint = this.currentBlueprint.get();
this.domAttach.update(currentBlueprint && currentBlueprint.getCost() > 0);
this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root));
}
/**
* Called when the blueprint was changed
* @param {Blueprint} blueprint
*/
onBlueprintChanged(blueprint) {
if (blueprint) {
this.lastBlueprintUsed = blueprint;
this.costDisplayText.innerText = "" + blueprint.getCost();
}
}
/**
* mouse down pre handler
* @param {Vector} pos
* @param {enumMouseButton} button
*/
onMouseDown(pos, button) {
if (button === enumMouseButton.right) {
if (this.currentBlueprint.get()) {
this.abortPlacement();
return STOP_PROPAGATION;
}
}
const blueprint = this.currentBlueprint.get();
if (!blueprint) {
return;
}
if (!blueprint.canAfford(this.root)) {
this.root.soundProxy.playUiError();
return;
}
const worldPos = this.root.camera.screenToWorld(pos);
const tile = worldPos.toTileSpace();
if (blueprint.tryPlace(this.root, tile)) {
const cost = blueprint.getCost();
this.root.hubGoals.takeShapeByKey(blueprintShape, cost);
this.root.soundProxy.playUi(SOUNDS.placeBuilding);
}
}
/**
* Mose move handler
*/
onMouseMove() {
// Prevent movement while blueprint is selected
if (this.currentBlueprint.get()) {
return STOP_PROPAGATION;
}
}
/**
* Called when an array of bulidings was selected
* @param {Array<number>} uids
*/
createBlueprintFromBuildings(uids) {
if (uids.length === 0) {
return;
}
this.currentBlueprint.set(Blueprint.fromUids(this.root, uids));
}
/**
* Attempts to rotate the current blueprint
*/
rotateBlueprint() {
if (this.currentBlueprint.get()) {
if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) {
this.currentBlueprint.get().rotateCcw();
} else {
this.currentBlueprint.get().rotateCw();
}
}
}
/**
* Attempts to paste the last blueprint
*/
pasteBlueprint() {
if (this.lastBlueprintUsed !== null) {
if (this.lastBlueprintUsed.layer !== this.root.currentLayer) {
// Not compatible
this.root.soundProxy.playUiError();
return;
}
this.root.hud.signals.pasteBlueprintRequested.dispatch();
this.currentBlueprint.set(this.lastBlueprintUsed);
} else {
this.root.soundProxy.playUiError();
}
}
/**
*
* @param {DrawParameters} parameters
*/
draw(parameters) {
const blueprint = this.currentBlueprint.get();
if (!blueprint) {
return;
}
const mousePosition = this.root.app.mousePosition;
if (!mousePosition) {
// Not on screen
return;
}
const worldPos = this.root.camera.screenToWorld(mousePosition);
const tile = worldPos.toTileSpace();
blueprint.draw(parameters, tile);
}
}
import { DrawParameters } from "../../../core/draw_parameters";
import { STOP_PROPAGATION } from "../../../core/signal";
import { TrackedState } from "../../../core/tracked_state";
import { makeDiv } from "../../../core/utils";
import { Vector } from "../../../core/vector";
import { SOUNDS } from "../../../platform/sound";
import { T } from "../../../translations";
import { Blueprint } from "../../blueprint";
import { enumMouseButton } from "../../camera";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
export class HUDBlueprintPlacer extends BaseHUDPart {
createElements(parent) {
const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey(
this.root.gameMode.getBlueprintShapeKey()
);
const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80);
this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``);
makeDiv(this.costDisplayParent, null, ["label"], T.ingame.blueprintPlacer.cost);
const costContainer = makeDiv(this.costDisplayParent, null, ["costContainer"], "");
this.costDisplayText = makeDiv(costContainer, null, ["costText"], "");
costContainer.appendChild(blueprintCostShapeCanvas);
}
initialize() {
this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this);
/** @type {TypedTrackedState<Blueprint?>} */
this.currentBlueprint = new TrackedState(this.onBlueprintChanged, this);
/** @type {Blueprint?} */
this.lastBlueprintUsed = null;
const keyActionMapper = this.root.keyMapper;
keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this);
keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.abortPlacement, this);
keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this);
keyActionMapper.getBinding(KEYMAPPINGS.massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this);
this.root.camera.downPreHandler.add(this.onMouseDown, this);
this.root.camera.movePreHandler.add(this.onMouseMove, this);
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this);
this.root.signals.editModeChanged.add(this.onEditModeChanged, this);
this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent);
this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this);
}
abortPlacement() {
if (this.currentBlueprint.get()) {
this.currentBlueprint.set(null);
return STOP_PROPAGATION;
}
}
/**
* Called when the layer was changed
* @param {Layer} layer
*/
onEditModeChanged(layer) {
// Check if the layer of the blueprint differs and thus we have to deselect it
const blueprint = this.currentBlueprint.get();
if (blueprint) {
if (blueprint.layer !== layer) {
this.currentBlueprint.set(null);
}
}
}
/**
* Called when the blueprint is now affordable or not
* @param {boolean} canAfford
*/
onCanAffordChanged(canAfford) {
this.costDisplayParent.classList.toggle("canAfford", canAfford);
}
update() {
const currentBlueprint = this.currentBlueprint.get();
this.domAttach.update(currentBlueprint && currentBlueprint.getCost() > 0);
this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root));
}
/**
* Called when the blueprint was changed
* @param {Blueprint} blueprint
*/
onBlueprintChanged(blueprint) {
if (blueprint) {
this.lastBlueprintUsed = blueprint;
this.costDisplayText.innerText = "" + blueprint.getCost();
}
}
/**
* mouse down pre handler
* @param {Vector} pos
* @param {enumMouseButton} button
*/
onMouseDown(pos, button) {
if (button === enumMouseButton.right) {
if (this.currentBlueprint.get()) {
this.abortPlacement();
return STOP_PROPAGATION;
}
}
const blueprint = this.currentBlueprint.get();
if (!blueprint) {
return;
}
if (!blueprint.canAfford(this.root)) {
this.root.soundProxy.playUiError();
return;
}
const worldPos = this.root.camera.screenToWorld(pos);
const tile = worldPos.toTileSpace();
if (blueprint.tryPlace(this.root, tile)) {
const cost = blueprint.getCost();
this.root.hubGoals.takeShapeByKey(this.root.gameMode.getBlueprintShapeKey(), cost);
this.root.soundProxy.playUi(SOUNDS.placeBuilding);
}
}
/**
* Mose move handler
*/
onMouseMove() {
// Prevent movement while blueprint is selected
if (this.currentBlueprint.get()) {
return STOP_PROPAGATION;
}
}
/**
* Called when an array of bulidings was selected
* @param {Array<number>} uids
*/
createBlueprintFromBuildings(uids) {
if (uids.length === 0) {
return;
}
this.currentBlueprint.set(Blueprint.fromUids(this.root, uids));
}
/**
* Attempts to rotate the current blueprint
*/
rotateBlueprint() {
if (this.currentBlueprint.get()) {
if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) {
this.currentBlueprint.get().rotateCcw();
} else {
this.currentBlueprint.get().rotateCw();
}
}
}
/**
* Attempts to paste the last blueprint
*/
pasteBlueprint() {
if (this.lastBlueprintUsed !== null) {
if (this.lastBlueprintUsed.layer !== this.root.currentLayer) {
// Not compatible
this.root.soundProxy.playUiError();
return;
}
this.root.hud.signals.pasteBlueprintRequested.dispatch();
this.currentBlueprint.set(this.lastBlueprintUsed);
} else {
this.root.soundProxy.playUiError();
}
}
/**
*
* @param {DrawParameters} parameters
*/
draw(parameters) {
const blueprint = this.currentBlueprint.get();
if (!blueprint) {
return;
}
const mousePosition = this.root.app.mousePosition;
if (!mousePosition) {
// Not on screen
return;
}
const worldPos = this.root.camera.screenToWorld(mousePosition);
const tile = worldPos.toTileSpace();
blueprint.draw(parameters, tile);
}
}

View File

@ -1,16 +0,0 @@
import { T } from "../../../translations";
import { BaseHUDPart } from "../base_hud_part";
export class HUDPerformanceWarning extends BaseHUDPart {
initialize() {
this.warningShown = false;
this.root.signals.entityManuallyPlaced.add(this.checkAfterPlace, this);
}
checkAfterPlace() {
if (!this.warningShown && this.root.entityMgr.entities.length > 10000) {
this.root.hud.parts.dialogs.showInfo(T.dialogs.entityWarning.title, T.dialogs.entityWarning.desc);
this.warningShown = true;
}
}
}

View File

@ -1,12 +1,11 @@
import { ClickDetector } from "../../../core/click_detector";
import { formatBigNumber, makeDiv, arrayDeleteValue } from "../../../core/utils";
import { ShapeDefinition } from "../../shape_definition";
import { BaseHUDPart } from "../base_hud_part";
import { blueprintShape, UPGRADES } from "../../upgrades";
import { enumHubGoalRewards } from "../../tutorial_goals";
import { enumAnalyticsDataSource } from "../../production_analytics";
import { T } from "../../../translations";
import { globalConfig } from "../../../core/config";
import { arrayDeleteValue, formatBigNumber, makeDiv } from "../../../core/utils";
import { T } from "../../../translations";
import { enumAnalyticsDataSource } from "../../production_analytics";
import { ShapeDefinition } from "../../shape_definition";
import { enumHubGoalRewards } from "../../tutorial_goals";
import { BaseHUDPart } from "../base_hud_part";
/**
* Manages the pinned shapes on the left side of the screen
@ -82,7 +81,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
updateShapesAfterUpgrade() {
for (let i = 0; i < this.pinnedShapes.length; ++i) {
const key = this.pinnedShapes[i];
if (key === blueprintShape) {
if (key === this.root.gameMode.getBlueprintShapeKey()) {
// Ignore blueprint shapes
continue;
}
@ -107,13 +106,14 @@ export class HUDPinnedShapes extends BaseHUDPart {
if (key === this.root.hubGoals.currentGoal.definition.getHash()) {
return this.root.hubGoals.currentGoal.required;
}
if (key === blueprintShape) {
if (key === this.root.gameMode.getBlueprintShapeKey()) {
return null;
}
// Check if this shape is required for any upgrade
for (const upgradeId in UPGRADES) {
const upgradeTiers = UPGRADES[upgradeId];
const upgrades = this.root.gameMode.getUpgrades();
for (const upgradeId in upgrades) {
const upgradeTiers = upgrades[upgradeId];
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
const tierHandle = upgradeTiers[currentTier];
@ -138,7 +138,10 @@ export class HUDPinnedShapes extends BaseHUDPart {
* @param {string} key
*/
isShapePinned(key) {
if (key === this.root.hubGoals.currentGoal.definition.getHash() || key === blueprintShape) {
if (
key === this.root.hubGoals.currentGoal.definition.getHash() ||
key === this.root.gameMode.getBlueprintShapeKey()
) {
// This is a "special" shape which is always pinned
return true;
}
@ -178,7 +181,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
// Pin blueprint shape as well
if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
this.internalPinShape({
key: blueprintShape,
key: this.root.gameMode.getBlueprintShapeKey(),
canUnpin: false,
className: "blueprint",
});
@ -214,11 +217,11 @@ export class HUDPinnedShapes extends BaseHUDPart {
let detector = null;
if (canUnpin) {
element.classList.add("unpinable");
element.classList.add("removable");
detector = new ClickDetector(element, {
consumeEvents: true,
preventDefault: true,
targetOnly: true,
targetOnly: false,
});
detector.click.add(() => this.unpinShape(key));
} else {
@ -291,6 +294,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
* @param {string} key
*/
unpinShape(key) {
console.log("unpin", key);
arrayDeleteValue(this.pinnedShapes, key);
this.rerenderFull();
}
@ -306,7 +310,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
return;
}
if (key === blueprintShape) {
if (key === this.root.gameMode.getBlueprintShapeKey()) {
// Can not pin the blueprint shape
return;
}

View File

@ -1,9 +1,7 @@
import { BaseHUDPart } from "../base_hud_part";
import { makeDiv } from "../../../core/utils";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { blueprintShape, UPGRADES } from "../../upgrades";
import { enumNotificationType } from "./notifications";
import { tutorialGoals } from "../../tutorial_goals";
export class HUDSandboxController extends BaseHUDPart {
createElements(parent) {
@ -75,10 +73,11 @@ export class HUDSandboxController extends BaseHUDPart {
}
giveBlueprints() {
if (!this.root.hubGoals.storedShapes[blueprintShape]) {
this.root.hubGoals.storedShapes[blueprintShape] = 0;
const shape = this.root.gameMode.getBlueprintShapeKey();
if (!this.root.hubGoals.storedShapes[shape]) {
this.root.hubGoals.storedShapes[shape] = 0;
}
this.root.hubGoals.storedShapes[blueprintShape] += 1e9;
this.root.hubGoals.storedShapes[shape] += 1e9;
}
maxOutAll() {
@ -89,7 +88,7 @@ export class HUDSandboxController extends BaseHUDPart {
}
modifyUpgrade(id, amount) {
const upgradeTiers = UPGRADES[id];
const upgradeTiers = this.root.gameMode.getUpgrades()[id];
const maxLevel = upgradeTiers.length;
this.root.hubGoals.upgradeLevels[id] = Math.max(
@ -122,9 +121,10 @@ export class HUDSandboxController extends BaseHUDPart {
// Compute gained rewards
hubGoals.gainedRewards = {};
const levels = this.root.gameMode.getLevelDefinitions();
for (let i = 0; i < hubGoals.level - 1; ++i) {
if (i < tutorialGoals.length) {
const reward = tutorialGoals[i].reward;
if (i < levels.length) {
const reward = levels[i].reward;
hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1;
}
}

View File

@ -1,13 +1,13 @@
import { BaseHUDPart } from "../base_hud_part";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { IS_DEMO, globalConfig } from "../../../core/config";
import { T } from "../../../translations";
import { createLogger } from "../../../core/logging";
import { StaticMapEntityComponent } from "../../components/static_map_entity";
import { Vector } from "../../../core/vector";
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { globalConfig } from "../../../core/config";
import { DrawParameters } from "../../../core/draw_parameters";
import { createLogger } from "../../../core/logging";
import { Rectangle } from "../../../core/rectangle";
import { Vector } from "../../../core/vector";
import { T } from "../../../translations";
import { StaticMapEntityComponent } from "../../components/static_map_entity";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { BaseHUDPart } from "../base_hud_part";
const logger = createLogger("screenshot_exporter");
@ -19,7 +19,7 @@ export class HUDScreenshotExporter extends BaseHUDPart {
}
startExport() {
if (IS_DEMO) {
if (!this.root.app.restrictionMgr.getIsExportingScreenshotsPossible()) {
this.root.hud.parts.dialogs.showFeatureRestrictionInfo(T.demo.features.exportingBase);
return;
}
@ -87,7 +87,7 @@ export class HUDScreenshotExporter extends BaseHUDPart {
const parameters = new DrawParameters({
context,
visibleRect,
desiredAtlasScale: chunkScale,
desiredAtlasScale: 0.25,
root: this.root,
zoomLevel: chunkScale,
});

View File

@ -1,9 +1,8 @@
import { ClickDetector } from "../../../core/click_detector";
import { InputReceiver } from "../../../core/input_receiver";
import { formatBigNumber, makeDiv } from "../../../core/utils";
import { formatBigNumber, getRomanNumber, makeDiv } from "../../../core/utils";
import { T } from "../../../translations";
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
import { UPGRADES } from "../../upgrades";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
@ -21,7 +20,7 @@ export class HUDShop extends BaseHUDPart {
this.upgradeToElements = {};
// Upgrades
for (const upgradeId in UPGRADES) {
for (const upgradeId in this.root.gameMode.getUpgrades()) {
const handle = {};
handle.requireIndexToElement = [];
@ -59,7 +58,7 @@ export class HUDShop extends BaseHUDPart {
rerenderFull() {
for (const upgradeId in this.upgradeToElements) {
const handle = this.upgradeToElements[upgradeId];
const upgradeTiers = UPGRADES[upgradeId];
const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId];
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId];
@ -68,7 +67,7 @@ export class HUDShop extends BaseHUDPart {
// Set tier
handle.elemTierLabel.innerText = T.ingame.shop.tier.replace(
"<x>",
"" + T.ingame.shop.tierLabels[currentTier]
getRomanNumber(currentTier + 1)
);
handle.elemTierLabel.setAttribute("data-tier", currentTier);

View File

@ -1,14 +1,14 @@
import { globalConfig } from "../../../core/config";
import { gMetaBuildingRegistry } from "../../../core/global_registries";
import { InputReceiver } from "../../../core/input_receiver";
import { makeDiv } from "../../../core/utils";
import { SOUNDS } from "../../../platform/sound";
import { T } from "../../../translations";
import { defaultBuildingVariant } from "../../meta_building";
import { enumHubGoalRewards, tutorialGoals } from "../../tutorial_goals";
import { enumHubGoalRewards } from "../../tutorial_goals";
import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings";
import { InputReceiver } from "../../../core/input_receiver";
import { enumNotificationType } from "./notifications";
export class HUDUnlockNotification extends BaseHUDPart {
@ -53,7 +53,9 @@ export class HUDUnlockNotification extends BaseHUDPart {
showForLevel(level, reward) {
this.root.soundProxy.playUi(SOUNDS.levelComplete);
if (level > tutorialGoals.length) {
const levels = this.root.gameMode.getLevelDefinitions();
// Don't use getIsFreeplay() because we want the freeplay level up to show
if (level > levels.length) {
this.root.hud.signals.notification.dispatch(
T.ingame.notifications.freeplayLevelComplete.replace("<level>", String(level)),
enumNotificationType.success

View File

@ -1,5 +1,5 @@
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { globalConfig, IS_DEMO, THIRDPARTY_URLS } from "../../../core/config";
import { globalConfig, THIRDPARTY_URLS } from "../../../core/config";
import { DrawParameters } from "../../../core/draw_parameters";
import { Loader } from "../../../core/loader";
import { DialogWithForm } from "../../../core/modal_dialog_elements";
@ -302,7 +302,7 @@ export class HUDWaypoints extends BaseHUDPart {
// Show info that you can have only N markers in the demo,
// actually show this *after* entering the name so you want the
// standalone even more (I'm evil :P)
if (IS_DEMO && this.waypoints.length > 2) {
if (this.waypoints.length > this.root.app.restrictionMgr.getMaximumWaypoints()) {
this.root.hud.parts.dialogs.showFeatureRestrictionInfo(
"",
T.dialogs.markerDemoLimit.desc

View File

@ -248,6 +248,8 @@ export function getStringForKeyCode(code) {
return ",";
case 189:
return "-";
case 190:
return ".";
case 191:
return "/";
case 219:
@ -260,7 +262,9 @@ export function getStringForKeyCode(code) {
return "'";
}
return String.fromCharCode(code);
return (48 <= code && code <= 57) || (65 <= code && code <= 90)
? String.fromCharCode(code)
: "[" + code + "]";
}
export class Keybinding {

View File

@ -0,0 +1,480 @@
import { findNiceIntegerValue } from "../../core/utils";
import { GameMode } from "../game_mode";
import { ShapeDefinition } from "../shape_definition";
import { enumHubGoalRewards } from "../tutorial_goals";
const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
const finalGameShape = "RuCw--Cw:----Ru--";
const preparementShape = "CpRpCp--:SwSwSwSw";
const blueprintShape = "CbCbCbRb:CwCwCwCw";
// Tiers need % of the previous tier as requirement too
const tierGrowth = 2.5;
/**
* Generates all upgrades
* @returns {Object<string, import("../game_mode").UpgradeTiers>} */
function generateUpgrades(limitedVersion = false) {
const fixedImprovements = [0.5, 0.5, 1, 1, 2, 1, 1];
const numEndgameUpgrades = limitedVersion ? 0 : 1000 - fixedImprovements.length - 1;
function generateInfiniteUnlocks() {
return new Array(numEndgameUpgrades).fill(null).map((_, i) => ({
required: [
{ shape: preparementShape, amount: 30000 + i * 10000 },
{ shape: finalGameShape, amount: 20000 + i * 5000 },
{ shape: rocketShape, amount: 20000 + i * 5000 },
],
excludePrevious: true,
}));
}
// Fill in endgame upgrades
for (let i = 0; i < numEndgameUpgrades; ++i) {
if (i < 20) {
fixedImprovements.push(0.1);
} else if (i < 50) {
fixedImprovements.push(0.05);
} else if (i < 100) {
fixedImprovements.push(0.025);
} else {
fixedImprovements.push(0.0125);
}
}
const upgrades = {
belt: [
{
required: [{ shape: "CuCuCuCu", amount: 60 }],
},
{
required: [{ shape: "--CuCu--", amount: 500 }],
},
{
required: [{ shape: "CpCpCpCp", amount: 1000 }],
},
{
required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 6000 }],
},
{
required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 25000 }],
},
{
required: [{ shape: preparementShape, amount: 25000 }],
excludePrevious: true,
},
{
required: [
{ shape: preparementShape, amount: 25000 },
{ shape: finalGameShape, amount: 50000 },
],
excludePrevious: true,
},
...generateInfiniteUnlocks(),
],
miner: [
{
required: [{ shape: "RuRuRuRu", amount: 300 }],
},
{
required: [{ shape: "Cu------", amount: 800 }],
},
{
required: [{ shape: "ScScScSc", amount: 3500 }],
},
{
required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 23000 }],
},
{
required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 50000 }],
},
{
required: [{ shape: preparementShape, amount: 25000 }],
excludePrevious: true,
},
{
required: [
{ shape: preparementShape, amount: 25000 },
{ shape: finalGameShape, amount: 50000 },
],
excludePrevious: true,
},
...generateInfiniteUnlocks(),
],
processors: [
{
required: [{ shape: "SuSuSuSu", amount: 500 }],
},
{
required: [{ shape: "RuRu----", amount: 600 }],
},
{
required: [{ shape: "CgScScCg", amount: 3500 }],
},
{
required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 25000 }],
},
{
required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 50000 }],
},
{
required: [{ shape: preparementShape, amount: 25000 }],
excludePrevious: true,
},
{
required: [
{ shape: preparementShape, amount: 25000 },
{ shape: finalGameShape, amount: 50000 },
],
excludePrevious: true,
},
...generateInfiniteUnlocks(),
],
painting: [
{
required: [{ shape: "RbRb----", amount: 600 }],
},
{
required: [{ shape: "WrWrWrWr", amount: 3800 }],
},
{
required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 6500 }],
},
{
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 25000 }],
},
{
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 50000 }],
},
{
required: [{ shape: preparementShape, amount: 25000 }],
excludePrevious: true,
},
{
required: [
{ shape: preparementShape, amount: 25000 },
{ shape: finalGameShape, amount: 50000 },
],
excludePrevious: true,
},
...generateInfiniteUnlocks(),
],
};
// Automatically generate tier levels
for (const upgradeId in upgrades) {
const upgradeTiers = upgrades[upgradeId];
let currentTierRequirements = [];
for (let i = 0; i < upgradeTiers.length; ++i) {
const tierHandle = upgradeTiers[i];
tierHandle.improvement = fixedImprovements[i];
const originalRequired = tierHandle.required.slice();
for (let k = currentTierRequirements.length - 1; k >= 0; --k) {
const oldTierRequirement = currentTierRequirements[k];
if (!tierHandle.excludePrevious) {
tierHandle.required.unshift({
shape: oldTierRequirement.shape,
amount: oldTierRequirement.amount,
});
}
}
currentTierRequirements.push(
...originalRequired.map(req => ({
amount: req.amount,
shape: req.shape,
}))
);
currentTierRequirements.forEach(tier => {
tier.amount = findNiceIntegerValue(tier.amount * tierGrowth);
});
}
}
// VALIDATE
if (G_IS_DEV) {
for (const upgradeId in upgrades) {
upgrades[upgradeId].forEach(tier => {
tier.required.forEach(({ shape }) => {
try {
ShapeDefinition.fromShortKey(shape);
} catch (ex) {
throw new Error("Invalid upgrade goal: '" + ex + "' for shape" + shape);
}
});
});
}
}
return upgrades;
}
/**
* Generates the level definitions
* @param {boolean} limitedVersion
*/
export function generateLevelDefinitions(limitedVersion = false) {
const levelDefinitions = [
// 1
// Circle
{
shape: "CuCuCuCu", // belts t1
required: 30,
reward: enumHubGoalRewards.reward_cutter_and_trash,
},
// 2
// Cutter
{
shape: "----CuCu", //
required: 40,
reward: enumHubGoalRewards.no_reward,
},
// 3
// Rectangle
{
shape: "RuRuRuRu", // miners t1
required: 70,
reward: enumHubGoalRewards.reward_balancer,
},
// 4
{
shape: "RuRu----", // processors t2
required: 70,
reward: enumHubGoalRewards.reward_rotater,
},
// 5
// Rotater
{
shape: "Cu----Cu", // belts t2
required: 170,
reward: enumHubGoalRewards.reward_tunnel,
},
// 6
{
shape: "Cu------", // miners t2
required: 270,
reward: enumHubGoalRewards.reward_painter,
},
// 7
// Painter
{
shape: "CrCrCrCr", // unused
required: 300,
reward: enumHubGoalRewards.reward_rotater_ccw,
},
// 8
{
shape: "RbRb----", // painter t2
required: 480,
reward: enumHubGoalRewards.reward_mixer,
},
// 9
// Mixing (purple)
{
shape: "CpCpCpCp", // belts t3
required: 600,
reward: enumHubGoalRewards.reward_merger,
},
// 10
// STACKER: Star shape + cyan
{
shape: "ScScScSc", // miners t3
required: 800,
reward: enumHubGoalRewards.reward_stacker,
},
// 11
// Chainable miner
{
shape: "CgScScCg", // processors t3
required: 1000,
reward: enumHubGoalRewards.reward_miner_chainable,
},
// 12
// Blueprints
{
shape: "CbCbCbRb:CwCwCwCw",
required: 1000,
reward: enumHubGoalRewards.reward_blueprints,
},
// 13
// Tunnel Tier 2
{
shape: "RpRpRpRp:CwCwCwCw", // painting t3
required: 3800,
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
},
// DEMO STOPS HERE
...(limitedVersion
? [
{
shape: "RpRpRpRp:CwCwCwCw",
required: 0,
reward: enumHubGoalRewards.reward_demo_end,
},
]
: [
// 14
// Belt reader
{
shape: "--Cg----:--Cr----", // unused
required: 16, // Per second!
reward: enumHubGoalRewards.reward_belt_reader,
throughputOnly: true,
},
// 15
// Storage
{
shape: "SrSrSrSr:CyCyCyCy", // unused
required: 10000,
reward: enumHubGoalRewards.reward_storage,
},
// 16
// Quad Cutter
{
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
required: 6000,
reward: enumHubGoalRewards.reward_cutter_quad,
},
// 17
// Double painter
{
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
required: 20000,
reward: enumHubGoalRewards.reward_painter_double,
},
// 18
// Rotater (180deg)
{
shape: "Sg----Sg:CgCgCgCg:--CyCy--", // unused
required: 20000,
reward: enumHubGoalRewards.reward_rotater_180,
},
// 19
// Compact splitter
{
shape: "CpRpCp--:SwSwSwSw",
required: 25000,
reward: enumHubGoalRewards.reward_splitter,
},
// 20
// WIRES
{
shape: finalGameShape,
required: 25000,
reward: enumHubGoalRewards.reward_wires_painter_and_levers,
},
// 21
// Filter
{
shape: "CrCwCrCw:CwCrCwCr:CrCwCrCw:CwCrCwCr",
required: 25000,
reward: enumHubGoalRewards.reward_filter,
},
// 22
// Constant signal
{
shape: "Cg----Cr:Cw----Cw:Sy------:Cy----Cy",
required: 25000,
reward: enumHubGoalRewards.reward_constant_signal,
},
// 23
// Display
{
shape: "CcSyCcSy:SyCcSyCc:CcSyCcSy",
required: 25000,
reward: enumHubGoalRewards.reward_display,
},
// 24 Logic gates
{
shape: "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy",
required: 25000,
reward: enumHubGoalRewards.reward_logic_gates,
},
// 25 Virtual Processing
{
shape: "Rg--Rg--:CwRwCwRw:--Rg--Rg",
required: 25000,
reward: enumHubGoalRewards.reward_virtual_processing,
},
// 26 Freeplay
{
shape: "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw",
required: 50000,
reward: enumHubGoalRewards.reward_freeplay,
},
]),
];
if (G_IS_DEV) {
levelDefinitions.forEach(({ shape }) => {
try {
ShapeDefinition.fromShortKey(shape);
} catch (ex) {
throw new Error("Invalid tutorial goal: '" + ex + "' for shape" + shape);
}
});
}
return levelDefinitions;
}
const fullVersionUpgrades = generateUpgrades(false);
const demoVersionUpgrades = generateUpgrades(true);
const fullVersionLevels = generateLevelDefinitions(false);
const demoVersionLevels = generateLevelDefinitions(true);
export class RegularGameMode extends GameMode {
constructor(root) {
super(root);
}
getUpgrades() {
return this.root.app.restrictionMgr.getHasExtendedUpgrades()
? fullVersionUpgrades
: demoVersionUpgrades;
}
getIsFreeplayAvailable() {
return this.root.app.restrictionMgr.getHasExtendedLevelsAndFreeplay();
}
getBlueprintShapeKey() {
return blueprintShape;
}
getLevelDefinitions() {
return this.root.app.restrictionMgr.getHasExtendedLevelsAndFreeplay()
? fullVersionLevels
: demoVersionLevels;
}
}

View File

@ -1,221 +1,225 @@
/* eslint-disable no-unused-vars */
import { Signal } from "../core/signal";
import { RandomNumberGenerator } from "../core/rng";
import { createLogger } from "../core/logging";
// Type hints
/* typehints:start */
import { GameTime } from "./time/game_time";
import { EntityManager } from "./entity_manager";
import { GameSystemManager } from "./game_system_manager";
import { GameHUD } from "./hud/hud";
import { MapView } from "./map_view";
import { Camera } from "./camera";
import { InGameState } from "../states/ingame";
import { AutomaticSave } from "./automatic_save";
import { Application } from "../application";
import { SoundProxy } from "./sound_proxy";
import { Savegame } from "../savegame/savegame";
import { GameLogic } from "./logic";
import { ShapeDefinitionManager } from "./shape_definition_manager";
import { HubGoals } from "./hub_goals";
import { BufferMaintainer } from "../core/buffer_maintainer";
import { ProductionAnalytics } from "./production_analytics";
import { Entity } from "./entity";
import { ShapeDefinition } from "./shape_definition";
import { BaseItem } from "./base_item";
import { DynamicTickrate } from "./dynamic_tickrate";
import { KeyActionMapper } from "./key_action_mapper";
import { Vector } from "../core/vector";
/* typehints:end */
const logger = createLogger("game/root");
/** @type {Array<Layer>} */
export const layers = ["regular", "wires"];
/**
* The game root is basically the whole game state at a given point,
* combining all important classes. We don't have globals, but this
* class is passed to almost all game classes.
*/
export class GameRoot {
/**
* Constructs a new game root
* @param {Application} app
*/
constructor(app) {
this.app = app;
/** @type {Savegame} */
this.savegame = null;
/** @type {InGameState} */
this.gameState = null;
/** @type {KeyActionMapper} */
this.keyMapper = null;
// Store game dimensions
this.gameWidth = 500;
this.gameHeight = 500;
// Stores whether the current session is a fresh game (true), or was continued (false)
/** @type {boolean} */
this.gameIsFresh = true;
// Stores whether the logic is already initialized
/** @type {boolean} */
this.logicInitialized = false;
// Stores whether the game is already initialized, that is, all systems etc have been created
/** @type {boolean} */
this.gameInitialized = false;
/**
* Whether a bulk operation is running
*/
this.bulkOperationRunning = false;
//////// Other properties ///////
/** @type {Camera} */
this.camera = null;
/** @type {HTMLCanvasElement} */
this.canvas = null;
/** @type {CanvasRenderingContext2D} */
this.context = null;
/** @type {MapView} */
this.map = null;
/** @type {GameLogic} */
this.logic = null;
/** @type {EntityManager} */
this.entityMgr = null;
/** @type {GameHUD} */
this.hud = null;
/** @type {GameSystemManager} */
this.systemMgr = null;
/** @type {GameTime} */
this.time = null;
/** @type {HubGoals} */
this.hubGoals = null;
/** @type {BufferMaintainer} */
this.buffers = null;
/** @type {AutomaticSave} */
this.automaticSave = null;
/** @type {SoundProxy} */
this.soundProxy = null;
/** @type {ShapeDefinitionManager} */
this.shapeDefinitionMgr = null;
/** @type {ProductionAnalytics} */
this.productionAnalytics = null;
/** @type {DynamicTickrate} */
this.dynamicTickrate = null;
/** @type {Layer} */
this.currentLayer = "regular";
this.signals = {
// Entities
entityManuallyPlaced: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityAdded: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityChanged: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityGotNewComponent: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityComponentRemoved: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityQueuedForDestroy: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityDestroyed: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
// Global
resized: /** @type {TypedSignal<[number, number]>} */ (new Signal()),
readyToRender: /** @type {TypedSignal<[]>} */ (new Signal()),
aboutToDestruct: /** @type {TypedSignal<[]>} */ new Signal(),
// Game Hooks
gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved
gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored
gameFrameStarted: /** @type {TypedSignal<[]>} */ (new Signal()), // New frame
storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()),
upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()),
// Called right after game is initialized
postLoadHook: /** @type {TypedSignal<[]>} */ (new Signal()),
shapeDelivered: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()),
bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()),
editModeChanged: /** @type {TypedSignal<[Layer]>} */ (new Signal()),
// Called to check if an entity can be placed, second parameter is an additional offset.
// Use to introduce additional placement checks
prePlacementCheck: /** @type {TypedSignal<[Entity, Vector]>} */ (new Signal()),
// Called before actually placing an entity, use to perform additional logic
// for freeing space before actually placing.
freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
};
// RNG's
/** @type {Object.<string, Object.<string, RandomNumberGenerator>>} */
this.rngs = {};
// Work queue
this.queue = {
requireRedraw: false,
};
}
/**
* Destructs the game root
*/
destruct() {
logger.log("destructing root");
this.signals.aboutToDestruct.dispatch();
this.reset();
}
/**
* Resets the whole root and removes all properties
*/
reset() {
if (this.signals) {
// Destruct all signals
for (let i = 0; i < this.signals.length; ++i) {
this.signals[i].removeAll();
}
}
if (this.hud) {
this.hud.cleanup();
}
if (this.camera) {
this.camera.cleanup();
}
// Finally free all properties
for (let prop in this) {
if (this.hasOwnProperty(prop)) {
delete this[prop];
}
}
}
}
/* eslint-disable no-unused-vars */
import { Signal } from "../core/signal";
import { RandomNumberGenerator } from "../core/rng";
import { createLogger } from "../core/logging";
// Type hints
/* typehints:start */
import { GameTime } from "./time/game_time";
import { EntityManager } from "./entity_manager";
import { GameSystemManager } from "./game_system_manager";
import { GameHUD } from "./hud/hud";
import { MapView } from "./map_view";
import { Camera } from "./camera";
import { InGameState } from "../states/ingame";
import { AutomaticSave } from "./automatic_save";
import { Application } from "../application";
import { SoundProxy } from "./sound_proxy";
import { Savegame } from "../savegame/savegame";
import { GameLogic } from "./logic";
import { ShapeDefinitionManager } from "./shape_definition_manager";
import { HubGoals } from "./hub_goals";
import { BufferMaintainer } from "../core/buffer_maintainer";
import { ProductionAnalytics } from "./production_analytics";
import { Entity } from "./entity";
import { ShapeDefinition } from "./shape_definition";
import { BaseItem } from "./base_item";
import { DynamicTickrate } from "./dynamic_tickrate";
import { KeyActionMapper } from "./key_action_mapper";
import { Vector } from "../core/vector";
import { GameMode } from "./game_mode";
/* typehints:end */
const logger = createLogger("game/root");
/** @type {Array<Layer>} */
export const layers = ["regular", "wires"];
/**
* The game root is basically the whole game state at a given point,
* combining all important classes. We don't have globals, but this
* class is passed to almost all game classes.
*/
export class GameRoot {
/**
* Constructs a new game root
* @param {Application} app
*/
constructor(app) {
this.app = app;
/** @type {Savegame} */
this.savegame = null;
/** @type {InGameState} */
this.gameState = null;
/** @type {KeyActionMapper} */
this.keyMapper = null;
// Store game dimensions
this.gameWidth = 500;
this.gameHeight = 500;
// Stores whether the current session is a fresh game (true), or was continued (false)
/** @type {boolean} */
this.gameIsFresh = true;
// Stores whether the logic is already initialized
/** @type {boolean} */
this.logicInitialized = false;
// Stores whether the game is already initialized, that is, all systems etc have been created
/** @type {boolean} */
this.gameInitialized = false;
/**
* Whether a bulk operation is running
*/
this.bulkOperationRunning = false;
//////// Other properties ///////
/** @type {Camera} */
this.camera = null;
/** @type {HTMLCanvasElement} */
this.canvas = null;
/** @type {CanvasRenderingContext2D} */
this.context = null;
/** @type {MapView} */
this.map = null;
/** @type {GameLogic} */
this.logic = null;
/** @type {EntityManager} */
this.entityMgr = null;
/** @type {GameHUD} */
this.hud = null;
/** @type {GameSystemManager} */
this.systemMgr = null;
/** @type {GameTime} */
this.time = null;
/** @type {HubGoals} */
this.hubGoals = null;
/** @type {BufferMaintainer} */
this.buffers = null;
/** @type {AutomaticSave} */
this.automaticSave = null;
/** @type {SoundProxy} */
this.soundProxy = null;
/** @type {ShapeDefinitionManager} */
this.shapeDefinitionMgr = null;
/** @type {ProductionAnalytics} */
this.productionAnalytics = null;
/** @type {DynamicTickrate} */
this.dynamicTickrate = null;
/** @type {Layer} */
this.currentLayer = "regular";
/** @type {GameMode} */
this.gameMode = null;
this.signals = {
// Entities
entityManuallyPlaced: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityAdded: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityChanged: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityGotNewComponent: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityComponentRemoved: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityQueuedForDestroy: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
entityDestroyed: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
// Global
resized: /** @type {TypedSignal<[number, number]>} */ (new Signal()),
readyToRender: /** @type {TypedSignal<[]>} */ (new Signal()),
aboutToDestruct: /** @type {TypedSignal<[]>} */ new Signal(),
// Game Hooks
gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved
gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored
gameFrameStarted: /** @type {TypedSignal<[]>} */ (new Signal()), // New frame
storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()),
upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()),
// Called right after game is initialized
postLoadHook: /** @type {TypedSignal<[]>} */ (new Signal()),
shapeDelivered: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()),
bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()),
editModeChanged: /** @type {TypedSignal<[Layer]>} */ (new Signal()),
// Called to check if an entity can be placed, second parameter is an additional offset.
// Use to introduce additional placement checks
prePlacementCheck: /** @type {TypedSignal<[Entity, Vector]>} */ (new Signal()),
// Called before actually placing an entity, use to perform additional logic
// for freeing space before actually placing.
freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
};
// RNG's
/** @type {Object.<string, Object.<string, RandomNumberGenerator>>} */
this.rngs = {};
// Work queue
this.queue = {
requireRedraw: false,
};
}
/**
* Destructs the game root
*/
destruct() {
logger.log("destructing root");
this.signals.aboutToDestruct.dispatch();
this.reset();
}
/**
* Resets the whole root and removes all properties
*/
reset() {
if (this.signals) {
// Destruct all signals
for (let i = 0; i < this.signals.length; ++i) {
this.signals[i].removeAll();
}
}
if (this.hud) {
this.hud.cleanup();
}
if (this.camera) {
this.camera.cleanup();
}
// Finally free all properties
for (let prop in this) {
if (this.hasOwnProperty(prop)) {
delete this[prop];
}
}
}
}

View File

@ -12,7 +12,6 @@ import { GameSystemWithFilter } from "../game_system_with_filter";
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
import { ShapeDefinition } from "../shape_definition";
import { blueprintShape } from "../upgrades";
export class ConstantSignalSystem extends GameSystemWithFilter {
constructor(root) {
@ -64,7 +63,9 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
this.root.hubGoals.currentGoal.definition
),
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(blueprintShape),
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(
this.root.gameMode.getBlueprintShapeKey()
),
...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key =>
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)
),

View File

@ -1,4 +1,4 @@
import { globalConfig, IS_DEMO } from "../../core/config";
import { globalConfig } from "../../core/config";
import { smoothenDpi } from "../../core/dpi_manager";
import { DrawParameters } from "../../core/draw_parameters";
import { drawSpriteClipped } from "../../core/draw_utils";

View File

@ -154,22 +154,18 @@ export class LogicGateSystem extends GameSystemWithFilter {
/**
* @param {Array<BaseItem|null>} parameters
* @returns {[BaseItem, BaseItem]}
* @returns {BaseItem}
*/
compute_ROTATE(parameters) {
const item = parameters[0];
if (!item || item.getItemType() !== "shape") {
// Not a shape
return [null, null];
return null;
}
const definition = /** @type {ShapeItem} */ (item).definition;
const rotatedDefinitionCCW = this.root.shapeDefinitionMgr.shapeActionRotateCCW(definition);
const rotatedDefinitionCW = this.root.shapeDefinitionMgr.shapeActionRotateCW(definition);
return [
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinitionCCW),
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinitionCW),
];
return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinitionCW);
}
/**

View File

@ -1,7 +1,3 @@
import { IS_DEMO } from "../core/config";
import { ShapeDefinition } from "./shape_definition";
import { finalGameShape } from "./upgrades";
/**
* Don't forget to also update tutorial_goals_mappings.js as well as the translations!
* @enum {string}
@ -40,229 +36,3 @@ export const enumHubGoalRewards = {
no_reward: "no_reward",
no_reward_freeplay: "no_reward_freeplay",
};
export const tutorialGoals = [
// 1
// Circle
{
shape: "CuCuCuCu", // belts t1
required: 30,
reward: enumHubGoalRewards.reward_cutter_and_trash,
},
// 2
// Cutter
{
shape: "----CuCu", //
required: 40,
reward: enumHubGoalRewards.no_reward,
},
// 3
// Rectangle
{
shape: "RuRuRuRu", // miners t1
required: 70,
reward: enumHubGoalRewards.reward_balancer,
},
// 4
{
shape: "RuRu----", // processors t2
required: 70,
reward: enumHubGoalRewards.reward_rotater,
},
// 5
// Rotater
{
shape: "Cu----Cu", // belts t2
required: 170,
reward: enumHubGoalRewards.reward_tunnel,
},
// 6
{
shape: "Cu------", // miners t2
required: 270,
reward: enumHubGoalRewards.reward_painter,
},
// 7
// Painter
{
shape: "CrCrCrCr", // unused
required: 300,
reward: enumHubGoalRewards.reward_rotater_ccw,
},
// 8
{
shape: "RbRb----", // painter t2
required: 480,
reward: enumHubGoalRewards.reward_mixer,
},
// 9
// Mixing (purple)
{
shape: "CpCpCpCp", // belts t3
required: 600,
reward: enumHubGoalRewards.reward_merger,
},
// 10
// STACKER: Star shape + cyan
{
shape: "ScScScSc", // miners t3
required: 800,
reward: enumHubGoalRewards.reward_stacker,
},
// 11
// Chainable miner
{
shape: "CgScScCg", // processors t3
required: 1000,
reward: enumHubGoalRewards.reward_miner_chainable,
},
// 12
// Blueprints
{
shape: "CbCbCbRb:CwCwCwCw",
required: 1000,
reward: enumHubGoalRewards.reward_blueprints,
},
// 13
// Tunnel Tier 2
{
shape: "RpRpRpRp:CwCwCwCw", // painting t3
required: 3800,
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
},
// DEMO STOPS HERE
...(IS_DEMO
? [
{
shape: "RpRpRpRp:CwCwCwCw",
required: 0,
reward: enumHubGoalRewards.reward_demo_end,
},
]
: [
// 14
// Belt reader
{
shape: "--Cg----:--Cr----", // unused
required: 16, // Per second!
reward: enumHubGoalRewards.reward_belt_reader,
throughputOnly: true,
},
// 15
// Storage
{
shape: "SrSrSrSr:CyCyCyCy", // unused
required: 10000,
reward: enumHubGoalRewards.reward_storage,
},
// 16
// Quad Cutter
{
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
required: 6000,
reward: enumHubGoalRewards.reward_cutter_quad,
},
// 17
// Double painter
{
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
required: 20000,
reward: enumHubGoalRewards.reward_painter_double,
},
// 18
// Rotater (180deg)
{
shape: "Sg----Sg:CgCgCgCg:--CyCy--", // unused
required: 20000,
reward: enumHubGoalRewards.reward_rotater_180,
},
// 19
// Compact splitter
{
shape: "CpRpCp--:SwSwSwSw",
required: 25000,
reward: enumHubGoalRewards.reward_splitter,
},
// 20
// WIRES
{
shape: finalGameShape,
required: 25000,
reward: enumHubGoalRewards.reward_wires_painter_and_levers,
},
// 21
// Filter
{
shape: "CrCwCrCw:CwCrCwCr:CrCwCrCw:CwCrCwCr",
required: 25000,
reward: enumHubGoalRewards.reward_filter,
},
// 22
// Constant signal
{
shape: "Cg----Cr:Cw----Cw:Sy------:Cy----Cy",
required: 25000,
reward: enumHubGoalRewards.reward_constant_signal,
},
// 23
// Display
{
shape: "CcSyCcSy:SyCcSyCc:CcSyCcSy",
required: 25000,
reward: enumHubGoalRewards.reward_display,
},
// 24 Logic gates
{
shape: "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy",
required: 25000,
reward: enumHubGoalRewards.reward_logic_gates,
},
// 25 Virtual Processing
{
shape: "Rg--Rg--:CwRwCwRw:--Rg--Rg",
required: 25000,
reward: enumHubGoalRewards.reward_virtual_processing,
},
// 26 Freeplay
{
shape: "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw",
required: 50000,
reward: enumHubGoalRewards.reward_freeplay,
},
]),
];
if (G_IS_DEV) {
tutorialGoals.forEach(({ shape }) => {
try {
ShapeDefinition.fromShortKey(shape);
} catch (ex) {
throw new Error("Invalid tutorial goal: '" + ex + "' for shape" + shape);
}
});
}

View File

@ -1,212 +0,0 @@
import { IS_DEMO } from "../core/config";
import { findNiceIntegerValue } from "../core/utils";
import { ShapeDefinition } from "./shape_definition";
export const preparementShape = "CpRpCp--:SwSwSwSw";
export const finalGameShape = "RuCw--Cw:----Ru--";
export const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
export const blueprintShape = "CbCbCbRb:CwCwCwCw";
const fixedImprovements = [0.5, 0.5, 1, 1, 2, 1, 1];
const numEndgameUpgrades = !IS_DEMO ? 20 - fixedImprovements.length - 1 : 0;
function generateEndgameUpgrades() {
return new Array(numEndgameUpgrades).fill(null).map((_, i) => ({
required: [
{ shape: preparementShape, amount: 30000 + i * 10000 },
{ shape: finalGameShape, amount: 20000 + i * 5000 },
{ shape: rocketShape, amount: 20000 + i * 5000 },
],
excludePrevious: true,
}));
}
for (let i = 0; i < numEndgameUpgrades; ++i) {
fixedImprovements.push(0.1);
}
/** @typedef {{
* shape: string,
* amount: number
* }} UpgradeRequirement */
/** @typedef {{
* required: Array<UpgradeRequirement>
* improvement?: number,
* excludePrevious?: boolean
* }} TierRequirement */
/** @typedef {Array<TierRequirement>} UpgradeTiers */
/** @type {Object<string, UpgradeTiers>} */
export const UPGRADES = {
belt: [
{
required: [{ shape: "CuCuCuCu", amount: 60 }],
},
{
required: [{ shape: "--CuCu--", amount: 500 }],
},
{
required: [{ shape: "CpCpCpCp", amount: 1000 }],
},
{
required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 6000 }],
},
{
required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 25000 }],
},
{
required: [{ shape: preparementShape, amount: 25000 }],
excludePrevious: true,
},
{
required: [
{ shape: preparementShape, amount: 25000 },
{ shape: finalGameShape, amount: 50000 },
],
excludePrevious: true,
},
...generateEndgameUpgrades(),
],
miner: [
{
required: [{ shape: "RuRuRuRu", amount: 300 }],
},
{
required: [{ shape: "Cu------", amount: 800 }],
},
{
required: [{ shape: "ScScScSc", amount: 3500 }],
},
{
required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 23000 }],
},
{
required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 50000 }],
},
{
required: [{ shape: preparementShape, amount: 25000 }],
excludePrevious: true,
},
{
required: [
{ shape: preparementShape, amount: 25000 },
{ shape: finalGameShape, amount: 50000 },
],
excludePrevious: true,
},
...generateEndgameUpgrades(),
],
processors: [
{
required: [{ shape: "SuSuSuSu", amount: 500 }],
},
{
required: [{ shape: "RuRu----", amount: 600 }],
},
{
required: [{ shape: "CgScScCg", amount: 3500 }],
},
{
required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 25000 }],
},
{
required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 50000 }],
},
{
required: [{ shape: preparementShape, amount: 25000 }],
excludePrevious: true,
},
{
required: [
{ shape: preparementShape, amount: 25000 },
{ shape: finalGameShape, amount: 50000 },
],
excludePrevious: true,
},
...generateEndgameUpgrades(),
],
painting: [
{
required: [{ shape: "RbRb----", amount: 600 }],
},
{
required: [{ shape: "WrWrWrWr", amount: 3800 }],
},
{
required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 6500 }],
},
{
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 25000 }],
},
{
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 50000 }],
},
{
required: [{ shape: preparementShape, amount: 25000 }],
excludePrevious: true,
},
{
required: [
{ shape: preparementShape, amount: 25000 },
{ shape: finalGameShape, amount: 50000 },
],
excludePrevious: true,
},
...generateEndgameUpgrades(),
],
};
// Tiers need % of the previous tier as requirement too
const tierGrowth = 2.5;
// Automatically generate tier levels
for (const upgradeId in UPGRADES) {
const upgradeTiers = UPGRADES[upgradeId];
let currentTierRequirements = [];
for (let i = 0; i < upgradeTiers.length; ++i) {
const tierHandle = upgradeTiers[i];
tierHandle.improvement = fixedImprovements[i];
const originalRequired = tierHandle.required.slice();
for (let k = currentTierRequirements.length - 1; k >= 0; --k) {
const oldTierRequirement = currentTierRequirements[k];
if (!tierHandle.excludePrevious) {
tierHandle.required.unshift({
shape: oldTierRequirement.shape,
amount: oldTierRequirement.amount,
});
}
}
currentTierRequirements.push(
...originalRequired.map(req => ({
amount: req.amount,
shape: req.shape,
}))
);
currentTierRequirements.forEach(tier => {
tier.amount = findNiceIntegerValue(tier.amount * tierGrowth);
});
}
}
// VALIDATE
if (G_IS_DEV) {
for (const upgradeId in UPGRADES) {
UPGRADES[upgradeId].forEach(tier => {
tier.required.forEach(({ shape }) => {
try {
ShapeDefinition.fromShortKey(shape);
} catch (ex) {
throw new Error("Invalid upgrade goal: '" + ex + "' for shape" + shape);
}
});
});
}
}

View File

@ -1,14 +1,12 @@
import { globalConfig } from "../../core/config";
import { createLogger } from "../../core/logging";
import { queryParamOptions } from "../../core/query_parameters";
import { BeltComponent } from "../../game/components/belt";
import { StaticMapEntityComponent } from "../../game/components/static_map_entity";
import { GameRoot } from "../../game/root";
import { InGameState } from "../../states/ingame";
import { GameAnalyticsInterface } from "../game_analytics";
import { FILE_NOT_FOUND } from "../storage";
import { blueprintShape, UPGRADES } from "../../game/upgrades";
import { tutorialGoals } from "../../game/tutorial_goals";
import { BeltComponent } from "../../game/components/belt";
import { StaticMapEntityComponent } from "../../game/components/static_map_entity";
import { queryParamOptions } from "../../core/query_parameters";
const logger = createLogger("game_analytics");
@ -190,23 +188,26 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
/**
* Returns true if the shape is interesting
* @param {GameRoot} root
* @param {string} key
*/
isInterestingShape(key) {
if (key === blueprintShape) {
isInterestingShape(root, key) {
if (key === root.gameMode.getBlueprintShapeKey()) {
return true;
}
// Check if its a story goal
for (let i = 0; i < tutorialGoals.length; ++i) {
if (key === tutorialGoals[i].shape) {
const levels = root.gameMode.getLevelDefinitions();
for (let i = 0; i < levels.length; ++i) {
if (key === levels[i].shape) {
return true;
}
}
// Check if its required to unlock an upgrade
for (const upgradeKey in UPGRADES) {
const upgradeTiers = UPGRADES[upgradeKey];
const upgrades = root.gameMode.getUpgrades();
for (const upgradeKey in upgrades) {
const upgradeTiers = upgrades[upgradeKey];
for (let i = 0; i < upgradeTiers.length; ++i) {
const tier = upgradeTiers[i];
const required = tier.required;
@ -226,7 +227,9 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
* @param {GameRoot} root
*/
generateGameDump(root) {
const shapeIds = Object.keys(root.hubGoals.storedShapes).filter(this.isInterestingShape.bind(this));
const shapeIds = Object.keys(root.hubGoals.storedShapes).filter(key =>
this.isInterestingShape(root, key)
);
let shapes = {};
for (let i = 0; i < shapeIds.length; ++i) {
shapes[shapeIds[i]] = root.hubGoals.storedShapes[shapeIds[i]];

View File

@ -1,214 +1,202 @@
import { globalConfig, IS_DEMO, IS_MOBILE } from "../../core/config";
import { createLogger } from "../../core/logging";
import { queryParamOptions } from "../../core/query_parameters";
import { clamp } from "../../core/utils";
import { GamedistributionAdProvider } from "../ad_providers/gamedistribution";
import { NoAdProvider } from "../ad_providers/no_ad_provider";
import { PlatformWrapperInterface } from "../wrapper";
import { StorageImplBrowser } from "./storage";
import { StorageImplBrowserIndexedDB } from "./storage_indexed_db";
const logger = createLogger("platform/browser");
export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
initialize() {
this.recaptchaTokenCallback = null;
this.embedProvider = {
id: "shapezio-website",
adProvider: NoAdProvider,
iframed: false,
externalLinks: true,
iogLink: true,
unlimitedSavegames: IS_DEMO ? false : true,
showDemoBadge: IS_DEMO,
};
if (!G_IS_STANDALONE && queryParamOptions.embedProvider) {
const providerId = queryParamOptions.embedProvider;
this.embedProvider.iframed = true;
this.embedProvider.iogLink = false;
switch (providerId) {
case "armorgames": {
this.embedProvider.id = "armorgames";
break;
}
case "iogames.space": {
this.embedProvider.id = "iogames.space";
this.embedProvider.iogLink = true;
this.embedProvider.unlimitedSavegames = true;
this.embedProvider.showDemoBadge = false;
break;
}
case "miniclip": {
this.embedProvider.id = "miniclip";
break;
}
case "gamedistribution": {
this.embedProvider.id = "gamedistribution";
this.embedProvider.externalLinks = false;
this.embedProvider.adProvider = GamedistributionAdProvider;
break;
}
case "kongregate": {
this.embedProvider.id = "kongregate";
break;
}
case "crazygames": {
this.embedProvider.id = "crazygames";
break;
}
default: {
logger.error("Got unsupported embed provider:", providerId);
}
}
}
logger.log("Embed provider:", this.embedProvider.id);
return this.detectStorageImplementation()
.then(() => this.initializeAdProvider())
.then(() => super.initialize());
}
detectStorageImplementation() {
return new Promise(resolve => {
logger.log("Detecting storage");
if (!window.indexedDB) {
logger.log("Indexed DB not supported");
this.app.storage = new StorageImplBrowser(this.app);
resolve();
return;
}
// Try accessing the indexedb
let request;
try {
request = window.indexedDB.open("indexeddb_feature_detection", 1);
} catch (ex) {
logger.warn("Error while opening indexed db:", ex);
this.app.storage = new StorageImplBrowser(this.app);
resolve();
return;
}
request.onerror = err => {
logger.log("Indexed DB can *not* be accessed: ", err);
logger.log("Using fallback to local storage");
this.app.storage = new StorageImplBrowser(this.app);
resolve();
};
request.onsuccess = () => {
logger.log("Indexed DB *can* be accessed");
this.app.storage = new StorageImplBrowserIndexedDB(this.app);
resolve();
};
});
}
getHasUnlimitedSavegames() {
return this.embedProvider.unlimitedSavegames;
}
getShowDemoBadges() {
return this.embedProvider.showDemoBadge;
}
getId() {
return "browser@" + this.embedProvider.id;
}
getUiScale() {
if (IS_MOBILE) {
return 1;
}
const avgDims = Math.min(this.app.screenWidth, this.app.screenHeight);
return clamp((avgDims / 1000.0) * 1.9, 0.1, 10);
}
getSupportsRestart() {
return true;
}
getTouchPanStrength() {
return IS_MOBILE ? 1 : 0.5;
}
openExternalLink(url, force = false) {
logger.log("Opening external:", url);
if (force || this.embedProvider.externalLinks) {
window.open(url);
} else {
// Do nothing
alert(
"This platform does not allow opening external links. You can play on https://shapez.io directly to open them.\n\nClicked Link: " +
url
);
}
}
performRestart() {
logger.log("Performing restart");
window.location.reload(true);
}
/**
* Detects if there is an adblocker installed
* @returns {Promise<boolean>}
*/
detectAdblock() {
return Promise.race([
new Promise(resolve => {
// If the request wasn't blocked within a very short period of time, this means
// the adblocker is not active and the request was actually made -> ignore it then
setTimeout(() => resolve(false), 30);
}),
new Promise(resolve => {
fetch("https://googleads.g.doubleclick.net/pagead/id", {
method: "HEAD",
mode: "no-cors",
})
.then(res => {
resolve(false);
})
.catch(err => {
resolve(true);
});
}),
]);
}
initializeAdProvider() {
if (G_IS_DEV && !globalConfig.debug.testAds) {
logger.log("Ads disabled in local environment");
return Promise.resolve();
}
// First, detect adblocker
return this.detectAdblock().then(hasAdblocker => {
if (hasAdblocker) {
logger.log("Adblock detected");
return;
}
const adProvider = this.embedProvider.adProvider;
this.app.adProvider = new adProvider(this.app);
return this.app.adProvider.initialize().catch(err => {
logger.error("Failed to initialize ad provider, disabling ads:", err);
this.app.adProvider = new NoAdProvider(this.app);
});
});
}
exitApp() {
// Can not exit app
}
}
import { globalConfig, IS_MOBILE } from "../../core/config";
import { createLogger } from "../../core/logging";
import { queryParamOptions } from "../../core/query_parameters";
import { clamp } from "../../core/utils";
import { GamedistributionAdProvider } from "../ad_providers/gamedistribution";
import { NoAdProvider } from "../ad_providers/no_ad_provider";
import { PlatformWrapperInterface } from "../wrapper";
import { StorageImplBrowser } from "./storage";
import { StorageImplBrowserIndexedDB } from "./storage_indexed_db";
const logger = createLogger("platform/browser");
export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
initialize() {
this.recaptchaTokenCallback = null;
this.embedProvider = {
id: "shapezio-website",
adProvider: NoAdProvider,
iframed: false,
externalLinks: true,
iogLink: true,
};
if (!G_IS_STANDALONE && queryParamOptions.embedProvider) {
const providerId = queryParamOptions.embedProvider;
this.embedProvider.iframed = true;
this.embedProvider.iogLink = false;
switch (providerId) {
case "armorgames": {
this.embedProvider.id = "armorgames";
break;
}
case "iogames.space": {
this.embedProvider.id = "iogames.space";
this.embedProvider.iogLink = true;
break;
}
case "miniclip": {
this.embedProvider.id = "miniclip";
break;
}
case "gamedistribution": {
this.embedProvider.id = "gamedistribution";
this.embedProvider.externalLinks = false;
this.embedProvider.adProvider = GamedistributionAdProvider;
break;
}
case "kongregate": {
this.embedProvider.id = "kongregate";
break;
}
case "crazygames": {
this.embedProvider.id = "crazygames";
break;
}
default: {
logger.error("Got unsupported embed provider:", providerId);
}
}
}
logger.log("Embed provider:", this.embedProvider.id);
return this.detectStorageImplementation()
.then(() => this.initializeAdProvider())
.then(() => super.initialize());
}
detectStorageImplementation() {
return new Promise(resolve => {
logger.log("Detecting storage");
if (!window.indexedDB) {
logger.log("Indexed DB not supported");
this.app.storage = new StorageImplBrowser(this.app);
resolve();
return;
}
// Try accessing the indexedb
let request;
try {
request = window.indexedDB.open("indexeddb_feature_detection", 1);
} catch (ex) {
logger.warn("Error while opening indexed db:", ex);
this.app.storage = new StorageImplBrowser(this.app);
resolve();
return;
}
request.onerror = err => {
logger.log("Indexed DB can *not* be accessed: ", err);
logger.log("Using fallback to local storage");
this.app.storage = new StorageImplBrowser(this.app);
resolve();
};
request.onsuccess = () => {
logger.log("Indexed DB *can* be accessed");
this.app.storage = new StorageImplBrowserIndexedDB(this.app);
resolve();
};
});
}
getId() {
return "browser@" + this.embedProvider.id;
}
getUiScale() {
if (IS_MOBILE) {
return 1;
}
const avgDims = Math.min(this.app.screenWidth, this.app.screenHeight);
return clamp((avgDims / 1000.0) * 1.9, 0.1, 10);
}
getSupportsRestart() {
return true;
}
getTouchPanStrength() {
return IS_MOBILE ? 1 : 0.5;
}
openExternalLink(url, force = false) {
logger.log("Opening external:", url);
if (force || this.embedProvider.externalLinks) {
window.open(url);
} else {
// Do nothing
alert(
"This platform does not allow opening external links. You can play on https://shapez.io directly to open them.\n\nClicked Link: " +
url
);
}
}
performRestart() {
logger.log("Performing restart");
window.location.reload(true);
}
/**
* Detects if there is an adblocker installed
* @returns {Promise<boolean>}
*/
detectAdblock() {
return Promise.race([
new Promise(resolve => {
// If the request wasn't blocked within a very short period of time, this means
// the adblocker is not active and the request was actually made -> ignore it then
setTimeout(() => resolve(false), 30);
}),
new Promise(resolve => {
fetch("https://googleads.g.doubleclick.net/pagead/id", {
method: "HEAD",
mode: "no-cors",
})
.then(res => {
resolve(false);
})
.catch(err => {
resolve(true);
});
}),
]);
}
initializeAdProvider() {
if (G_IS_DEV && !globalConfig.debug.testAds) {
logger.log("Ads disabled in local environment");
return Promise.resolve();
}
// First, detect adblocker
return this.detectAdblock().then(hasAdblocker => {
if (hasAdblocker) {
logger.log("Adblock detected");
return;
}
const adProvider = this.embedProvider.adProvider;
this.app.adProvider = new adProvider(this.app);
return this.app.adProvider.initialize().catch(err => {
logger.error("Failed to initialize ad provider, disabling ads:", err);
this.app.adProvider = new NoAdProvider(this.app);
});
});
}
exitApp() {
// Can not exit app
}
}

View File

@ -1,65 +1,57 @@
import { PlatformWrapperImplBrowser } from "../browser/wrapper";
import { getIPCRenderer } from "../../core/utils";
import { createLogger } from "../../core/logging";
import { StorageImplElectron } from "./storage";
import { PlatformWrapperInterface } from "../wrapper";
const logger = createLogger("electron-wrapper");
export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
initialize() {
this.app.storage = new StorageImplElectron(this);
return PlatformWrapperInterface.prototype.initialize.call(this);
}
getId() {
return "electron";
}
getSupportsRestart() {
return true;
}
openExternalLink(url) {
logger.log(this, "Opening external:", url);
window.open(url, "about:blank");
}
getSupportsAds() {
return false;
}
getHasUnlimitedSavegames() {
return true;
}
getShowDemoBadges() {
return false;
}
performRestart() {
logger.log(this, "Performing restart");
window.location.reload(true);
}
initializeAdProvider() {
return Promise.resolve();
}
getSupportsFullscreen() {
return true;
}
setFullscreen(flag) {
getIPCRenderer().send("set-fullscreen", flag);
}
getSupportsAppExit() {
return true;
}
exitApp() {
logger.log(this, "Sending app exit signal");
getIPCRenderer().send("exit-app");
}
}
import { PlatformWrapperImplBrowser } from "../browser/wrapper";
import { getIPCRenderer } from "../../core/utils";
import { createLogger } from "../../core/logging";
import { StorageImplElectron } from "./storage";
import { PlatformWrapperInterface } from "../wrapper";
const logger = createLogger("electron-wrapper");
export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
initialize() {
this.app.storage = new StorageImplElectron(this);
return PlatformWrapperInterface.prototype.initialize.call(this);
}
getId() {
return "electron";
}
getSupportsRestart() {
return true;
}
openExternalLink(url) {
logger.log(this, "Opening external:", url);
window.open(url, "about:blank");
}
getSupportsAds() {
return false;
}
performRestart() {
logger.log(this, "Performing restart");
window.location.reload(true);
}
initializeAdProvider() {
return Promise.resolve();
}
getSupportsFullscreen() {
return true;
}
setFullscreen(flag) {
getIPCRenderer().send("set-fullscreen", flag);
}
getSupportsAppExit() {
return true;
}
exitApp() {
logger.log(this, "Sending app exit signal");
getIPCRenderer().send("exit-app");
}
}

View File

@ -6,7 +6,7 @@ import { GameRoot } from "../game/root";
import { newEmptyMap, clamp } from "../core/utils";
import { createLogger } from "../core/logging";
import { globalConfig, IS_DEMO } from "../core/config";
import { globalConfig } from "../core/config";
const logger = createLogger("sound");
@ -29,7 +29,9 @@ export const SOUNDS = {
};
export const MUSIC = {
theme: IS_DEMO ? "theme-short" : "theme-full",
// The theme always depends on the standalone only, even if running the full
// version in the browser
theme: G_IS_STANDALONE ? "theme-full" : "theme-short",
menu: "menu",
};

View File

@ -1,142 +1,131 @@
/* typehints:start */
import { Application } from "../application";
/* typehints:end */
import { IS_MOBILE } from "../core/config";
export class PlatformWrapperInterface {
constructor(app) {
/** @type {Application} */
this.app = app;
}
/** @returns {string} */
getId() {
abstract;
return "unknown-platform";
}
/**
* Returns the UI scale, called on every resize
* @returns {number} */
getUiScale() {
return 1;
}
/** @returns {boolean} */
getSupportsRestart() {
abstract;
return false;
}
/**
* Whether the user has unlimited savegames
*/
getHasUnlimitedSavegames() {
return true;
}
getShowDemoBadges() {
return false;
}
/**
* Returns the strength of touch pans with the mouse
*/
getTouchPanStrength() {
return 1;
}
/** @returns {Promise<void>} */
initialize() {
document.documentElement.classList.add("p-" + this.getId());
return Promise.resolve();
}
/**
* Should initialize the apps ad provider in case supported
* @returns {Promise<void>}
*/
initializeAdProvider() {
return Promise.resolve();
}
/**
* Should return the minimum supported zoom level
* @returns {number}
*/
getMinimumZoom() {
return 0.1 * this.getScreenScale();
}
/**
* Should return the maximum supported zoom level
* @returns {number}
*/
getMaximumZoom() {
return 3.5 * this.getScreenScale();
}
getScreenScale() {
return Math.min(window.innerWidth, window.innerHeight) / 1024.0;
}
/**
* Should return if this platform supports ads at all
*/
getSupportsAds() {
return false;
}
/**
* Attempt to open an external url
* @param {string} url
* @param {boolean=} force Whether to always open the url even if not allowed
*/
openExternalLink(url, force = false) {
abstract;
}
/**
* Attempt to restart the app
*/
performRestart() {
abstract;
}
/**
* Returns whether this platform supports a toggleable fullscreen
*/
getSupportsFullscreen() {
return false;
}
/**
* Should set the apps fullscreen state to the desired state
* @param {boolean} flag
*/
setFullscreen(flag) {
abstract;
}
/**
* Returns whether this platform supports quitting the app
*/
getSupportsAppExit() {
return false;
}
/**
* Attempts to quit the app
*/
exitApp() {
abstract;
}
/**
* Whether this platform supports a keyboard
*/
getSupportsKeyboard() {
return !IS_MOBILE;
}
}
/* typehints:start */
import { Application } from "../application";
/* typehints:end */
import { IS_MOBILE } from "../core/config";
export class PlatformWrapperInterface {
constructor(app) {
/** @type {Application} */
this.app = app;
}
/** @returns {string} */
getId() {
abstract;
return "unknown-platform";
}
/**
* Returns the UI scale, called on every resize
* @returns {number} */
getUiScale() {
return 1;
}
/** @returns {boolean} */
getSupportsRestart() {
abstract;
return false;
}
/**
* Returns the strength of touch pans with the mouse
*/
getTouchPanStrength() {
return 1;
}
/** @returns {Promise<void>} */
initialize() {
document.documentElement.classList.add("p-" + this.getId());
return Promise.resolve();
}
/**
* Should initialize the apps ad provider in case supported
* @returns {Promise<void>}
*/
initializeAdProvider() {
return Promise.resolve();
}
/**
* Should return the minimum supported zoom level
* @returns {number}
*/
getMinimumZoom() {
return 0.1 * this.getScreenScale();
}
/**
* Should return the maximum supported zoom level
* @returns {number}
*/
getMaximumZoom() {
return 3.5 * this.getScreenScale();
}
getScreenScale() {
return Math.min(window.innerWidth, window.innerHeight) / 1024.0;
}
/**
* Should return if this platform supports ads at all
*/
getSupportsAds() {
return false;
}
/**
* Attempt to open an external url
* @param {string} url
* @param {boolean=} force Whether to always open the url even if not allowed
*/
openExternalLink(url, force = false) {
abstract;
}
/**
* Attempt to restart the app
*/
performRestart() {
abstract;
}
/**
* Returns whether this platform supports a toggleable fullscreen
*/
getSupportsFullscreen() {
return false;
}
/**
* Should set the apps fullscreen state to the desired state
* @param {boolean} flag
*/
setFullscreen(flag) {
abstract;
}
/**
* Returns whether this platform supports quitting the app
*/
getSupportsAppExit() {
return false;
}
/**
* Attempts to quit the app
*/
exitApp() {
abstract;
}
/**
* Whether this platform supports a keyboard
*/
getSupportsKeyboard() {
return !IS_MOBILE;
}
}

View File

@ -6,8 +6,7 @@ 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 { THEMES, applyGameTheme } from "../game/theme";
import { T } from "../translations";
import { LANGUAGES } from "../languages";
@ -187,7 +186,9 @@ export const allApplicationSettings = [
app.platformWrapper.setFullscreen(value);
}
},
!IS_DEMO
/**
* @param {Application} app
*/ app => app.restrictionMgr.getHasExtendedSettings()
),
new BoolSetting(
@ -215,7 +216,9 @@ export const allApplicationSettings = [
applyGameTheme(id);
document.documentElement.setAttribute("data-theme", id);
},
enabled: !IS_DEMO,
enabledCb: /**
* @param {Application} app
*/ app => app.restrictionMgr.getHasExtendedSettings(),
}),
new EnumSetting("autosaveInterval", {
@ -255,6 +258,7 @@ export const allApplicationSettings = [
new BoolSetting("enableMousePan", enumCategories.advanced, (app, value) => {}),
new BoolSetting("alwaysMultiplace", enumCategories.advanced, (app, value) => {}),
new BoolSetting("zoomToCursor", 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) => {}),
@ -271,7 +275,9 @@ export const allApplicationSettings = [
category: enumCategories.performance,
restartRequired: false,
changeCb: (app, id) => {},
enabled: !IS_DEMO,
enabledCb: /**
* @param {Application} app
*/ app => app.restrictionMgr.getHasExtendedSettings(),
}),
new BoolSetting("lowQualityMapResources", enumCategories.performance, (app, value) => {}),
@ -317,6 +323,7 @@ class SettingsStorage {
this.disableTileGrid = false;
this.lowQualityTextures = false;
this.simplifiedBelts = false;
this.zoomToCursor = true;
/**
* @type {Object.<string, number>}
@ -355,7 +362,7 @@ export class ApplicationSettings extends ReadWriteProxy {
* @returns {SettingsStorage}
*/
getAllSettings() {
return this.getCurrentData().settings;
return this.currentData.settings;
}
/**
@ -527,7 +534,7 @@ export class ApplicationSettings extends ReadWriteProxy {
}
getCurrentVersion() {
return 28;
return 29;
}
/** @param {{settings: SettingsStorage, version: number}} data */
@ -660,6 +667,11 @@ export class ApplicationSettings extends ReadWriteProxy {
data.version = 28;
}
if (data.version < 29) {
data.settings.zoomToCursor = true;
data.version = 29;
}
return ExplainedResult.good();
}
}

View File

@ -13,13 +13,13 @@ export class BaseSetting {
* @param {string} id
* @param {string} categoryId
* @param {function(Application, any):void} changeCb
* @param {boolean} enabled
* @param {function(Application) : boolean=} enabledCb
*/
constructor(id, categoryId, changeCb, enabled) {
constructor(id, categoryId, changeCb, enabledCb = null) {
this.id = id;
this.categoryId = categoryId;
this.changeCb = changeCb;
this.enabled = enabled;
this.enabledCb = enabledCb;
/** @type {Application} */
this.app = null;
@ -39,6 +39,7 @@ export class BaseSetting {
}
/**
* Binds all parameters
* @param {Application} app
* @param {HTMLElement} element
* @param {any} dialogs
@ -49,19 +50,37 @@ export class BaseSetting {
this.dialogs = dialogs;
}
getHtml() {
/**
* Returns the HTML for this setting
* @param {Application} app
*/
getHtml(app) {
abstract;
return "";
}
/**
* Returns whether this setting is enabled and available
* @param {Application} app
*/
getIsAvailable(app) {
return this.enabledCb ? this.enabledCb(app) : true;
}
syncValueToElement() {
abstract;
}
/**
* Attempts to modify the setting
*/
modify() {
abstract;
}
/**
* Shows the dialog that a restart is required
*/
showRestartRequiredDialog() {
const { restart } = this.dialogs.showInfo(
T.dialogs.restartRequired.title,
@ -74,6 +93,7 @@ export class BaseSetting {
}
/**
* Validates the set value
* @param {any} value
* @returns {boolean}
*/
@ -96,10 +116,10 @@ export class EnumSetting extends BaseSetting {
iconPrefix = null,
changeCb = null,
magicValue = null,
enabled = true,
enabledCb = null,
}
) {
super(id, category, changeCb, enabled);
super(id, category, changeCb, enabledCb);
this.options = options;
this.valueGetter = valueGetter;
@ -110,10 +130,14 @@ export class EnumSetting extends BaseSetting {
this.magicValue = magicValue;
}
getHtml() {
/**
* @param {Application} app
*/
getHtml(app) {
const available = this.getIsAvailable(app);
return `
<div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}">
${this.enabled ? "" : `<span class="standaloneOnlyHint">${T.demo.settingNotAvailable}</span>`}
<div class="setting cardbox ${available ? "enabled" : "disabled"}">
${available ? "" : `<span class="standaloneOnlyHint">${T.demo.settingNotAvailable}</span>`}
<div class="row">
<label>${T.settings.labels[this.id].title}</label>
<div class="value enum" data-setting="${this.id}"></div>
@ -180,14 +204,18 @@ export class EnumSetting extends BaseSetting {
}
export class BoolSetting extends BaseSetting {
constructor(id, category, changeCb = null, enabled = true) {
super(id, category, changeCb, enabled);
constructor(id, category, changeCb = null, enabledCb = null) {
super(id, category, changeCb, enabledCb);
}
getHtml() {
/**
* @param {Application} app
*/
getHtml(app) {
const available = this.getIsAvailable(app);
return `
<div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}">
${this.enabled ? "" : `<span class="standaloneOnlyHint">${T.demo.settingNotAvailable}</span>`}
<div class="setting cardbox ${available ? "enabled" : "disabled"}">
${available ? "" : `<span class="standaloneOnlyHint">${T.demo.settingNotAvailable}</span>`}
<div class="row">
<label>${T.settings.labels[this.id].title}</label>
@ -226,13 +254,13 @@ export class RangeSetting extends BaseSetting {
id,
category,
changeCb = null,
enabled = true,
defaultValue = 1.0,
minValue = 0,
maxValue = 1.0,
stepSize = 0.0001
stepSize = 0.0001,
enabledCb = null
) {
super(id, category, changeCb, enabled);
super(id, category, changeCb, enabledCb);
this.defaultValue = defaultValue;
this.minValue = minValue;
@ -240,10 +268,14 @@ export class RangeSetting extends BaseSetting {
this.stepSize = stepSize;
}
getHtml() {
/**
* @param {Application} app
*/
getHtml(app) {
const available = this.getIsAvailable(app);
return `
<div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}">
${this.enabled ? "" : `<span class="standaloneOnlyHint">${T.demo.settingNotAvailable}</span>`}
<div class="setting cardbox ${available ? "enabled" : "disabled"}">
${available ? "" : `<span class="standaloneOnlyHint">${T.demo.settingNotAvailable}</span>`}
<div class="row">
<label>${T.settings.labels[this.id].title}</label>

View File

@ -40,13 +40,6 @@ export class SavegameManager extends ReadWriteProxy {
return 1002;
}
/**
* @returns {SavegamesData}
*/
getCurrentData() {
return super.getCurrentData();
}
verify(data) {
// TODO / FIXME!!!!
return ExplainedResult.good();
@ -96,6 +89,14 @@ export class SavegameManager extends ReadWriteProxy {
return new Savegame(this.app, { internalId, metaDataRef: metadata });
}
/**
* Returns if this manager has any savegame of a 1.1.19 version, which
* enables all levels
*/
getHasAnyLegacySavegames() {
return this.currentData.savegames.some(savegame => savegame.version === 1005 || savegame.level > 14);
}
/**
* Deletes a savegame
* @param {SavegameMetadata} game
@ -149,7 +150,9 @@ export class SavegameManager extends ReadWriteProxy {
});
this.currentData.savegames.push(metaData);
this.sortSavegames();
// Notice: This is async and happening in the background
this.updateAfterSavegamesChanged();
return new Savegame(this.app, {
internalId: id,
@ -157,8 +160,16 @@ export class SavegameManager extends ReadWriteProxy {
});
}
/**
* Attempts to import a savegame
* @param {object} data
*/
importSavegame(data) {
const savegame = this.createNewSavegame();
// Track legacy savegames
const isOldSavegame = data.version < 1006;
const migrationResult = savegame.migrate(data);
if (migrationResult.isBad()) {
return Promise.reject("Failed to migrate: " + migrationResult.reason);
@ -170,7 +181,19 @@ export class SavegameManager extends ReadWriteProxy {
return Promise.reject("Verification failed: " + verification.result);
}
return savegame.writeSavegameAndMetadata().then(() => this.sortSavegames());
return savegame
.writeSavegameAndMetadata()
.then(() => this.updateAfterSavegamesChanged())
.then(() => this.app.restrictionMgr.onHasLegacySavegamesChanged(isOldSavegame));
}
/**
* Hook after the savegames got changed
*/
updateAfterSavegamesChanged() {
return this.sortSavegames()
.then(() => this.writeAsync())
.then(() => this.app.restrictionMgr.onHasLegacySavegamesChanged(this.getHasAnyLegacySavegames()));
}
/**
@ -219,7 +242,7 @@ export class SavegameManager extends ReadWriteProxy {
if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) {
return Promise.resolve();
}
return this.sortSavegames().then(() => this.writeAsync());
return this.updateAfterSavegamesChanged();
});
}
}

View File

@ -130,7 +130,7 @@ export class SavegameSerializer {
errorReason = errorReason || root.time.deserialize(savegame.time);
errorReason = errorReason || root.camera.deserialize(savegame.camera);
errorReason = errorReason || root.map.deserialize(savegame.map);
errorReason = errorReason || root.hubGoals.deserialize(savegame.hubGoals);
errorReason = errorReason || root.hubGoals.deserialize(savegame.hubGoals, root);
errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes);
errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints);
errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);

View File

@ -19,7 +19,6 @@ import { getCodeFromBuildingData } from "../../game/building_codes.js";
import { StaticMapEntityComponent } from "../../game/components/static_map_entity.js";
import { Entity } from "../../game/entity.js";
import { defaultBuildingVariant, MetaBuilding } from "../../game/meta_building.js";
import { finalGameShape } from "../../game/upgrades.js";
import { SavegameInterface_V1005 } from "./1005.js";
const schema = require("./1006.json");
@ -152,7 +151,8 @@ export class SavegameInterface_V1006 extends SavegameInterface_V1005 {
stored[shapeKey] = rebalance(stored[shapeKey]);
}
stored[finalGameShape] = 0;
// Reset final game shape
stored["RuCw--Cw:----Ru--"] = 0;
// Reduce goals
if (dump.hubGoals.currentGoal) {

View File

@ -1,179 +1,173 @@
import { TextualGameState } from "../core/textual_game_state";
import { SOUNDS } from "../platform/sound";
import { T } from "../translations";
import { KEYMAPPINGS, getStringForKeyCode } from "../game/key_action_mapper";
import { Dialog } from "../core/modal_dialog_elements";
import { IS_DEMO } from "../core/config";
export class KeybindingsState extends TextualGameState {
constructor() {
super("KeybindingsState");
}
getStateHeaderTitle() {
return T.keybindings.title;
}
getMainContentHTML() {
return `
<div class="topEntries">
<span class="hint">${T.keybindings.hint}</span>
<button class="styledButton resetBindings">${T.keybindings.resetKeybindings}</button>
</div>
<div class="keybindings">
</div>
`;
}
onEnter() {
const keybindingsElem = this.htmlElement.querySelector(".keybindings");
this.trackClicks(this.htmlElement.querySelector(".resetBindings"), this.resetBindings);
for (const category in KEYMAPPINGS) {
const categoryDiv = document.createElement("div");
categoryDiv.classList.add("category");
keybindingsElem.appendChild(categoryDiv);
const labelDiv = document.createElement("strong");
labelDiv.innerText = T.keybindings.categoryLabels[category];
labelDiv.classList.add("categoryLabel");
categoryDiv.appendChild(labelDiv);
for (const keybindingId in KEYMAPPINGS[category]) {
const mapped = KEYMAPPINGS[category][keybindingId];
const elem = document.createElement("div");
elem.classList.add("entry");
elem.setAttribute("data-keybinding", keybindingId);
categoryDiv.appendChild(elem);
const title = document.createElement("span");
title.classList.add("title");
title.innerText = T.keybindings.mappings[keybindingId];
elem.appendChild(title);
const mappingDiv = document.createElement("span");
mappingDiv.classList.add("mapping");
elem.appendChild(mappingDiv);
const editBtn = document.createElement("button");
editBtn.classList.add("styledButton", "editKeybinding");
const resetBtn = document.createElement("button");
resetBtn.classList.add("styledButton", "resetKeybinding");
if (mapped.builtin) {
editBtn.classList.add("disabled");
resetBtn.classList.add("disabled");
} else {
this.trackClicks(editBtn, () => this.editKeybinding(keybindingId));
this.trackClicks(resetBtn, () => this.resetKeybinding(keybindingId));
}
elem.appendChild(editBtn);
elem.appendChild(resetBtn);
}
}
this.updateKeybindings();
}
editKeybinding(id) {
// if (IS_DEMO) {
// this.dialogs.showFeatureRestrictionInfo(T.demo.features.customizeKeybindings);
// return;
// }
const dialog = new Dialog({
app: this.app,
title: T.dialogs.editKeybinding.title,
contentHTML: T.dialogs.editKeybinding.desc,
buttons: ["cancel:good"],
type: "info",
});
dialog.inputReciever.keydown.add(({ keyCode, shift, alt, event }) => {
if (keyCode === 27) {
this.dialogs.closeDialog(dialog);
return;
}
if (event) {
event.preventDefault();
}
if (event.target && event.target.tagName === "BUTTON" && keyCode === 1) {
return;
}
if (
// Enter
keyCode === 13
) {
// Ignore builtins
return;
}
this.app.settings.updateKeybindingOverride(id, keyCode);
this.dialogs.closeDialog(dialog);
this.updateKeybindings();
});
dialog.inputReciever.backButton.add(() => {});
this.dialogs.internalShowDialog(dialog);
this.app.sound.playUiSound(SOUNDS.dialogOk);
}
updateKeybindings() {
const overrides = this.app.settings.getKeybindingOverrides();
for (const category in KEYMAPPINGS) {
for (const keybindingId in KEYMAPPINGS[category]) {
const mapped = KEYMAPPINGS[category][keybindingId];
const container = this.htmlElement.querySelector("[data-keybinding='" + keybindingId + "']");
assert(container, "Container for keybinding not found: " + keybindingId);
let keyCode = mapped.keyCode;
if (overrides[keybindingId]) {
keyCode = overrides[keybindingId];
}
const mappingDiv = container.querySelector(".mapping");
mappingDiv.innerHTML = getStringForKeyCode(keyCode);
mappingDiv.classList.toggle("changed", !!overrides[keybindingId]);
const resetBtn = container.querySelector("button.resetKeybinding");
resetBtn.classList.toggle("disabled", mapped.builtin || !overrides[keybindingId]);
}
}
}
resetKeybinding(id) {
this.app.settings.resetKeybindingOverride(id);
this.updateKeybindings();
}
resetBindings() {
const { reset } = this.dialogs.showWarning(
T.dialogs.resetKeybindingsConfirmation.title,
T.dialogs.resetKeybindingsConfirmation.desc,
["cancel:good", "reset:bad"]
);
reset.add(() => {
this.app.settings.resetKeybindingOverrides();
this.updateKeybindings();
this.dialogs.showInfo(T.dialogs.keybindingsResetOk.title, T.dialogs.keybindingsResetOk.desc);
});
}
getDefaultPreviousState() {
return "SettingsState";
}
}
import { Dialog } from "../core/modal_dialog_elements";
import { TextualGameState } from "../core/textual_game_state";
import { getStringForKeyCode, KEYMAPPINGS } from "../game/key_action_mapper";
import { SOUNDS } from "../platform/sound";
import { T } from "../translations";
export class KeybindingsState extends TextualGameState {
constructor() {
super("KeybindingsState");
}
getStateHeaderTitle() {
return T.keybindings.title;
}
getMainContentHTML() {
return `
<div class="topEntries">
<span class="hint">${T.keybindings.hint}</span>
<button class="styledButton resetBindings">${T.keybindings.resetKeybindings}</button>
</div>
<div class="keybindings">
</div>
`;
}
onEnter() {
const keybindingsElem = this.htmlElement.querySelector(".keybindings");
this.trackClicks(this.htmlElement.querySelector(".resetBindings"), this.resetBindings);
for (const category in KEYMAPPINGS) {
const categoryDiv = document.createElement("div");
categoryDiv.classList.add("category");
keybindingsElem.appendChild(categoryDiv);
const labelDiv = document.createElement("strong");
labelDiv.innerText = T.keybindings.categoryLabels[category];
labelDiv.classList.add("categoryLabel");
categoryDiv.appendChild(labelDiv);
for (const keybindingId in KEYMAPPINGS[category]) {
const mapped = KEYMAPPINGS[category][keybindingId];
const elem = document.createElement("div");
elem.classList.add("entry");
elem.setAttribute("data-keybinding", keybindingId);
categoryDiv.appendChild(elem);
const title = document.createElement("span");
title.classList.add("title");
title.innerText = T.keybindings.mappings[keybindingId];
elem.appendChild(title);
const mappingDiv = document.createElement("span");
mappingDiv.classList.add("mapping");
elem.appendChild(mappingDiv);
const editBtn = document.createElement("button");
editBtn.classList.add("styledButton", "editKeybinding");
const resetBtn = document.createElement("button");
resetBtn.classList.add("styledButton", "resetKeybinding");
if (mapped.builtin) {
editBtn.classList.add("disabled");
resetBtn.classList.add("disabled");
} else {
this.trackClicks(editBtn, () => this.editKeybinding(keybindingId));
this.trackClicks(resetBtn, () => this.resetKeybinding(keybindingId));
}
elem.appendChild(editBtn);
elem.appendChild(resetBtn);
}
}
this.updateKeybindings();
}
editKeybinding(id) {
const dialog = new Dialog({
app: this.app,
title: T.dialogs.editKeybinding.title,
contentHTML: T.dialogs.editKeybinding.desc,
buttons: ["cancel:good"],
type: "info",
});
dialog.inputReciever.keydown.add(({ keyCode, shift, alt, event }) => {
if (keyCode === 27) {
this.dialogs.closeDialog(dialog);
return;
}
if (event) {
event.preventDefault();
}
if (event.target && event.target.tagName === "BUTTON" && keyCode === 1) {
return;
}
if (
// Enter
keyCode === 13
) {
// Ignore builtins
return;
}
this.app.settings.updateKeybindingOverride(id, keyCode);
this.dialogs.closeDialog(dialog);
this.updateKeybindings();
});
dialog.inputReciever.backButton.add(() => {});
this.dialogs.internalShowDialog(dialog);
this.app.sound.playUiSound(SOUNDS.dialogOk);
}
updateKeybindings() {
const overrides = this.app.settings.getKeybindingOverrides();
for (const category in KEYMAPPINGS) {
for (const keybindingId in KEYMAPPINGS[category]) {
const mapped = KEYMAPPINGS[category][keybindingId];
const container = this.htmlElement.querySelector("[data-keybinding='" + keybindingId + "']");
assert(container, "Container for keybinding not found: " + keybindingId);
let keyCode = mapped.keyCode;
if (overrides[keybindingId]) {
keyCode = overrides[keybindingId];
}
const mappingDiv = container.querySelector(".mapping");
mappingDiv.innerHTML = getStringForKeyCode(keyCode);
mappingDiv.classList.toggle("changed", !!overrides[keybindingId]);
const resetBtn = container.querySelector("button.resetKeybinding");
resetBtn.classList.toggle("disabled", mapped.builtin || !overrides[keybindingId]);
}
}
}
resetKeybinding(id) {
this.app.settings.resetKeybindingOverride(id);
this.updateKeybindings();
}
resetBindings() {
const { reset } = this.dialogs.showWarning(
T.dialogs.resetKeybindingsConfirmation.title,
T.dialogs.resetKeybindingsConfirmation.desc,
["cancel:good", "reset:bad"]
);
reset.add(() => {
this.app.settings.resetKeybindingOverrides();
this.updateKeybindings();
this.dialogs.showInfo(T.dialogs.keybindingsResetOk.title, T.dialogs.keybindingsResetOk.desc);
});
}
getDefaultPreviousState() {
return "SettingsState";
}
}

View File

@ -1,21 +1,23 @@
import { GameState } from "../core/game_state";
import { cachebust } from "../core/cachebust";
import { A_B_TESTING_LINK_TYPE, globalConfig, IS_DEMO, THIRDPARTY_URLS } from "../core/config";
import { A_B_TESTING_LINK_TYPE, globalConfig, THIRDPARTY_URLS } from "../core/config";
import { GameState } from "../core/game_state";
import { DialogWithForm } from "../core/modal_dialog_elements";
import { FormElementInput } from "../core/modal_dialog_forms";
import { ReadWriteProxy } from "../core/read_write_proxy";
import {
makeDiv,
makeButtonElement,
formatSecondsToTimeAgo,
waitNextFrame,
generateFileDownload,
isSupportedBrowser,
makeButton,
makeButtonElement,
makeDiv,
removeAllChildren,
startFileChoose,
waitNextFrame,
} from "../core/utils";
import { ReadWriteProxy } from "../core/read_write_proxy";
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
import { T } from "../translations";
import { getApplicationSettingById } from "../profile/application_settings";
import { FormElementInput } from "../core/modal_dialog_forms";
import { DialogWithForm } from "../core/modal_dialog_elements";
import { T } from "../translations";
const trim = require("trim");
@ -24,23 +26,6 @@ const trim = require("trim");
* @typedef {import("../profile/setting_types").EnumSetting} EnumSetting
*/
/**
* Generates a file download
* @param {string} filename
* @param {string} text
*/
function generateFileDownload(filename, text) {
var element = document.createElement("a");
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
export class MainMenuState extends GameState {
constructor() {
super("MainMenuState");
@ -49,18 +34,16 @@ export class MainMenuState extends GameState {
getInnerHTML() {
const bannerHtml = `
<h3>${T.demoBanners.title}</h3>
<p>${T.demoBanners.intro}</p>
<a href="#" class="steamLink ${A_B_TESTING_LINK_TYPE}" target="_blank">Get the shapez.io standalone!</a>
`;
return `
const showDemoBadges = this.app.restrictionMgr.getIsStandaloneMarketingActive();
return `
<div class="topButtons">
<button class="languageChoose" data-languageicon="${this.app.settings.getLanguage()}"></button>
<button class="settingsButton"></button>
${
G_IS_STANDALONE || G_IS_DEV
? `
@ -74,17 +57,14 @@ export class MainMenuState extends GameState {
<source src="${cachebust("res/bg_render.webm")}" type="video/webm">
</video>
<div class="logo">
<img src="${cachebust("res/logo.png")}" alt="shapez.io Logo">
<span class="updateLabel">Wires update!</span>
</div>
<div class="mainWrapper ${IS_DEMO ? "demo" : "noDemo"}">
<div class="mainWrapper ${showDemoBadges ? "demo" : "noDemo"}">
<div class="sideContainer">
${IS_DEMO ? `<div class="standaloneBanner">${bannerHtml}</div>` : ""}
${showDemoBadges ? `<div class="standaloneBanner">${bannerHtml}</div>` : ""}
</div>
<div class="mainContainer">
@ -95,12 +75,9 @@ export class MainMenuState extends GameState {
}
<div class="buttons"></div>
</div>
</div>
<div class="footer">
<a class="githubLink boxLink" target="_blank">
${T.mainMenu.openSourceHint}
<span class="thirdpartyLogo githubLogo"></span>
@ -123,32 +100,29 @@ export class MainMenuState extends GameState {
"<author-link>",
'<a class="producerLink" target="_blank">Tobias Springer</a>'
)}</div>
</div>
`;
}
/**
* Asks the user to import a savegame
*/
requestImportSavegame() {
if (
IS_DEMO &&
this.app.savegameMgr.getSavegamesMetaData().length > 0 &&
!this.app.platformWrapper.getHasUnlimitedSavegames()
!this.app.restrictionMgr.getHasUnlimitedSavegames()
) {
this.app.analytics.trackUiClick("importgame_slot_limit_show");
this.showSavegameSlotLimit();
return;
}
var input = document.createElement("input");
input.type = "file";
input.accept = ".bin";
input.onchange = e => {
const file = input.files[0];
// Create a 'fake' file-input to accept savegames
startFileChoose(".bin").then(file => {
if (file) {
const closeLoader = this.dialogs.showLoadingDialog();
waitNextFrame().then(() => {
this.app.analytics.trackUiClick("import_savegame");
const closeLoader = this.dialogs.showLoadingDialog();
const reader = new FileReader();
reader.addEventListener("load", event => {
const contents = event.target.result;
@ -194,8 +168,7 @@ export class MainMenuState extends GameState {
reader.readAsText(file, "utf-8");
});
}
};
input.click();
});
}
onBackButton() {
@ -557,9 +530,8 @@ export class MainMenuState extends GameState {
onPlayButtonClicked() {
if (
IS_DEMO &&
this.app.savegameMgr.getSavegamesMetaData().length > 0 &&
!this.app.platformWrapper.getHasUnlimitedSavegames()
!this.app.restrictionMgr.getHasUnlimitedSavegames()
) {
this.app.analytics.trackUiClick("startgame_slot_limit_show");
this.showSavegameSlotLimit();

View File

@ -145,6 +145,11 @@ export class PreloadState extends GameState {
this.app.backgroundResourceLoader.startLoading();
})
.then(() => this.setStatus("Initializing restrictions"))
.then(() => {
return this.app.restrictionMgr.initialize();
})
.then(() => this.setStatus("Initializing savegame"))
.then(() => {
return this.app.savegameMgr.initialize().catch(err => {

View File

@ -1,169 +1,169 @@
import { TextualGameState } from "../core/textual_game_state";
import { formatSecondsToTimeAgo } from "../core/utils";
import { allApplicationSettings, enumCategories } from "../profile/application_settings";
import { T } from "../translations";
export class SettingsState extends TextualGameState {
constructor() {
super("SettingsState");
}
getStateHeaderTitle() {
return T.settings.title;
}
getMainContentHTML() {
return `
<div class="sidebar">
${this.getCategoryButtonsHtml()}
${
this.app.platformWrapper.getSupportsKeyboard()
? `
<button class="styledButton categoryButton editKeybindings">
${T.keybindings.title}
</button>`
: ""
}
<div class="other">
<button class="styledButton about">${T.about.title}</button>
<div class="versionbar">
<div class="buildVersion">${T.global.loading} ...</div>
</div>
</div>
</div>
<div class="categoryContainer">
${this.getSettingsHtml()}
</div>
`;
}
getCategoryButtonsHtml() {
return Object.keys(enumCategories)
.map(key => enumCategories[key])
.map(
category =>
`
<button class="styledButton categoryButton" data-category-btn="${category}">
${T.settings.categories[category]}
</button>
`
)
.join("");
}
getSettingsHtml() {
const categoriesHTML = {};
Object.keys(enumCategories).forEach(key => {
const catName = enumCategories[key];
categoriesHTML[catName] = `<div class="category" data-category="${catName}">`;
});
for (let i = 0; i < allApplicationSettings.length; ++i) {
const setting = allApplicationSettings[i];
categoriesHTML[setting.categoryId] += setting.getHtml();
}
return Object.keys(categoriesHTML)
.map(k => categoriesHTML[k] + "</div>")
.join("");
}
renderBuildText() {
const labelVersion = this.htmlElement.querySelector(".buildVersion");
const lastBuildMs = new Date().getTime() - G_BUILD_TIME;
const lastBuildText = formatSecondsToTimeAgo(lastBuildMs / 1000.0);
const version = T.settings.versionBadges[G_APP_ENVIRONMENT];
labelVersion.innerHTML = `
<span class='version'>
${G_BUILD_VERSION} @ ${version} @ ${G_BUILD_COMMIT_HASH}
</span>
<span class='buildTime'>
${T.settings.buildDate.replace("<at-date>", lastBuildText)}<br />
</span>`;
}
onEnter(payload) {
this.renderBuildText();
this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, {
preventDefault: false,
});
const keybindingsButton = this.htmlElement.querySelector(".editKeybindings");
if (keybindingsButton) {
this.trackClicks(keybindingsButton, this.onKeybindingsClicked, { preventDefault: false });
}
this.initSettings();
this.initCategoryButtons();
this.htmlElement.querySelector(".category").classList.add("active");
this.htmlElement.querySelector(".categoryButton").classList.add("active");
}
setActiveCategory(category) {
const previousCategory = this.htmlElement.querySelector(".category.active");
const previousCategoryButton = this.htmlElement.querySelector(".categoryButton.active");
if (previousCategory.getAttribute("data-category") == category) {
return;
}
previousCategory.classList.remove("active");
previousCategoryButton.classList.remove("active");
const newCategory = this.htmlElement.querySelector("[data-category='" + category + "']");
const newCategoryButton = this.htmlElement.querySelector("[data-category-btn='" + category + "']");
newCategory.classList.add("active");
newCategoryButton.classList.add("active");
}
initSettings() {
allApplicationSettings.forEach(setting => {
/** @type {HTMLElement} */
const element = this.htmlElement.querySelector("[data-setting='" + setting.id + "']");
setting.bind(this.app, element, this.dialogs);
setting.syncValueToElement();
this.trackClicks(
element,
() => {
setting.modify();
},
{ preventDefault: false }
);
});
}
initCategoryButtons() {
Object.keys(enumCategories).forEach(key => {
const category = enumCategories[key];
const button = this.htmlElement.querySelector("[data-category-btn='" + category + "']");
this.trackClicks(
button,
() => {
this.setActiveCategory(category);
},
{ preventDefault: false }
);
});
}
onAboutClicked() {
this.moveToStateAddGoBack("AboutState");
}
onKeybindingsClicked() {
this.moveToStateAddGoBack("KeybindingsState");
}
}
import { TextualGameState } from "../core/textual_game_state";
import { formatSecondsToTimeAgo } from "../core/utils";
import { allApplicationSettings, enumCategories } from "../profile/application_settings";
import { T } from "../translations";
export class SettingsState extends TextualGameState {
constructor() {
super("SettingsState");
}
getStateHeaderTitle() {
return T.settings.title;
}
getMainContentHTML() {
return `
<div class="sidebar">
${this.getCategoryButtonsHtml()}
${
this.app.platformWrapper.getSupportsKeyboard()
? `
<button class="styledButton categoryButton editKeybindings">
${T.keybindings.title}
</button>`
: ""
}
<div class="other">
<button class="styledButton about">${T.about.title}</button>
<div class="versionbar">
<div class="buildVersion">${T.global.loading} ...</div>
</div>
</div>
</div>
<div class="categoryContainer">
${this.getSettingsHtml()}
</div>
`;
}
getCategoryButtonsHtml() {
return Object.keys(enumCategories)
.map(key => enumCategories[key])
.map(
category =>
`
<button class="styledButton categoryButton" data-category-btn="${category}">
${T.settings.categories[category]}
</button>
`
)
.join("");
}
getSettingsHtml() {
const categoriesHTML = {};
Object.keys(enumCategories).forEach(key => {
const catName = enumCategories[key];
categoriesHTML[catName] = `<div class="category" data-category="${catName}">`;
});
for (let i = 0; i < allApplicationSettings.length; ++i) {
const setting = allApplicationSettings[i];
categoriesHTML[setting.categoryId] += setting.getHtml(this.app);
}
return Object.keys(categoriesHTML)
.map(k => categoriesHTML[k] + "</div>")
.join("");
}
renderBuildText() {
const labelVersion = this.htmlElement.querySelector(".buildVersion");
const lastBuildMs = new Date().getTime() - G_BUILD_TIME;
const lastBuildText = formatSecondsToTimeAgo(lastBuildMs / 1000.0);
const version = T.settings.versionBadges[G_APP_ENVIRONMENT];
labelVersion.innerHTML = `
<span class='version'>
${G_BUILD_VERSION} @ ${version} @ ${G_BUILD_COMMIT_HASH}
</span>
<span class='buildTime'>
${T.settings.buildDate.replace("<at-date>", lastBuildText)}<br />
</span>`;
}
onEnter(payload) {
this.renderBuildText();
this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, {
preventDefault: false,
});
const keybindingsButton = this.htmlElement.querySelector(".editKeybindings");
if (keybindingsButton) {
this.trackClicks(keybindingsButton, this.onKeybindingsClicked, { preventDefault: false });
}
this.initSettings();
this.initCategoryButtons();
this.htmlElement.querySelector(".category").classList.add("active");
this.htmlElement.querySelector(".categoryButton").classList.add("active");
}
setActiveCategory(category) {
const previousCategory = this.htmlElement.querySelector(".category.active");
const previousCategoryButton = this.htmlElement.querySelector(".categoryButton.active");
if (previousCategory.getAttribute("data-category") == category) {
return;
}
previousCategory.classList.remove("active");
previousCategoryButton.classList.remove("active");
const newCategory = this.htmlElement.querySelector("[data-category='" + category + "']");
const newCategoryButton = this.htmlElement.querySelector("[data-category-btn='" + category + "']");
newCategory.classList.add("active");
newCategoryButton.classList.add("active");
}
initSettings() {
allApplicationSettings.forEach(setting => {
/** @type {HTMLElement} */
const element = this.htmlElement.querySelector("[data-setting='" + setting.id + "']");
setting.bind(this.app, element, this.dialogs);
setting.syncValueToElement();
this.trackClicks(
element,
() => {
setting.modify();
},
{ preventDefault: false }
);
});
}
initCategoryButtons() {
Object.keys(enumCategories).forEach(key => {
const category = enumCategories[key];
const button = this.htmlElement.querySelector("[data-category-btn='" + category + "']");
this.trackClicks(
button,
() => {
this.setActiveCategory(category);
},
{ preventDefault: false }
);
});
}
onAboutClicked() {
this.moveToStateAddGoBack("AboutState");
}
onKeybindingsClicked() {
this.moveToStateAddGoBack("KeybindingsState");
}
}

View File

@ -33,6 +33,7 @@ The base language is English and can be found [here](base-en.yaml).
- [Ukrainian](base-uk.yaml)
- [Indonesian](base-ind.yaml)
- [Serbian](base-sr.yaml)
- [Czech](base-cz.yaml)
(If you want to translate into a new language, see below!)

View File

@ -1,20 +1,17 @@
steamPage:
shortText: shapez.io is a game about building factories to automate the creation
and processing of increasingly complex shapes across an infinitely
expanding map.
shortText: لعبة شيبز (أشكال) هي لعبة تدور حول بناء مصانع وتوصيلها حتى تقوم بشكل
.آلي بصناعة أشكال مختلفة تزداد تعقيدا في خريطة لانهائية.
discordLinkShort: Official Discord
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
لعبة شيبز (أشكال) هي لعبة مريحة تقوم فيها ببناء مصانع ووتشغيلها آليا لصناعة أشكال هندسية.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.
مع التقدم في المستوى، تزداد الأشكال تعقيداً، فيتوجب عليك التوسع في الخريطة اللانهائية، وذلك ليس كافياً للتقدم في مستوى اللعبة
حيث عليك صناعة المزيد بأضعاف مضاعفة لتلبية الطلب، الشيء الوحيد الذي يمكنه مساعدتك هو التوسع.
And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling!
بينما في البداية تقوم بصناعة أشكال مختلفة، تتطلب منك المراحل المتقدمة تلوين هذه الأشكال، حيث يتوجب عليك استخراج وخلط الألوان.
While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors!
Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later!
title_advantages: Standalone Advantages
عند شراءك اللعبة على ستيم (Steam) تحصل على الإصدار الكامل للعبة، ولكن يمكن أيضاً لعبة نسخة تجريبية على موقع shapez.io ثم يمكنك القرار لاحقا
title_advantages: ميزات نسخة الحاسوب
advantages:
- <b>12 New Level</b> for a total of 26 levels
- <b>18 New Buildings</b> for a fully automated factory!

View File

@ -5,7 +5,7 @@ steamPage:
discordLinkShort: Official Discord
intro: >-
Shapez.io es un joc relaxant en el qual has de construir fàbriques per a
la producció automàtica de formes geomètriques.
la producció automàtica de formes geomètriques.
A mesura que el nivell augmenta, les formes esdevenen més complexes, i has d'explorar el mapa infinit.
@ -94,7 +94,7 @@ mainMenu:
helpTranslate: Ajuda a traduir-lo!
madeBy: Creat per <author-link>
browserWarning: >-
Disculpa, però el joc funcionarà lent al teu navegador! Aconsegueix el joc complet o descarrega't chrome per una millor experiència.
savegameLevel: Nivell <x>
savegameLevelUnknown: Nivell desconegut

View File

@ -1,10 +1,10 @@
steamPage:
shortText: shapez.io je hra o stavbě továren pro automatizaci výroby a
kombinování čím dál složitějších tvarů na nekonečné mapě.
discordLinkShort: Official Discord
discordLinkShort: Oficiální Discord
intro: >-
Shapez.io je relaxační hra, ve které musíte stavět továrny pro
automatizaci výroby geometrických tvarů.
automatizaci výroby geometrických tvarů.
Jak se zvyšuje úroveň, tvary se stávají stále složitějšími a vy se musíte rozšířit po nekonečné mapě.
@ -13,38 +13,38 @@ steamPage:
Zatímco tvary zpracováváte pouze na začátku, musíte je později obarvit - k tomu musíte těžit a míchat barvy!
Koupením hry na platformě Steam vám dá přístup k plné verzi hry, ale také můžete hrát demo verzi na shapez.io a potom se můžete rozhodnou jestli hru koupíte!
title_advantages: Samostatné výhody
title_advantages: Výhody samostatné verze hry
advantages:
- <b>12 Nových úrovní</b> celkem 26 úrovní
- <b>18 Nových budov</b> pro plně automatizovanou továrnu!
- <b>20 vylepšení</b> pro mnoho hodin zábavy!
- <b>Wires Update</b> pro zcela nové rozměry!
- <b>Dark Mode</b>!
- Neomezené Savegames
- Neomezený počet uložených her
- Neomezené značky
- Podpořte mě! ❤️
title_future: Plánovaný kontent
planned:
- Blueprintová knihovna (Samostatně exkluzivní)
- Blueprintová knihovna
- Steam Achievements
- Puzzle Mode
- Minnimapa
- Mody
- Sandbox mode
- Puzzle Mód
- Minimapa
- Módy
- Sandbox Mód
- ... a o hodně víc!
title_open_source: Tato hra je open source!
title_links: Odkazy
links:
discord: Officiální Discord
discord: Oficiální Discord
roadmap: Roadmap
subreddit: Subreddit
source_code: Source code (GitHub)
translate: Pomozte přeložit hru!
text_open_source: |-
Kdokoli může přispět, aktivně se zapojit do komunity,
pokusit se zkontrolovat všechny návrhy a vzít v úvahu zpětnou vazbu
Kdokoli může přispět, jsem aktivně zapojený do komunity,
pokouším se zkontrolovat všechny návrhy a vzít v úvahu zpětnou vazbu všude,
kde je to možné.
Nezapomeňte se podívat na můj trello board, kde najdete kompletní plán!
global:
loading: Načítám
@ -105,8 +105,8 @@ dialogs:
later: Později
restart: Restart
reset: Reset
getStandalone: Získejte Plnou verzi
deleteGame: Vím co dělám
getStandalone: Získejte plnou verzi
deleteGame: Vím, co dělám
viewUpdate: Zobrazit aktualizaci
showUpgrades: Zobrazit vylepšení
showKeybindings: Zobrazit klávesové zkratky
@ -345,7 +345,7 @@ ingame:
desc: Kliknutím sem zobrazíte výhody Steam verze!
get_on_steam: Získejte na steamu
standaloneAdvantages:
title: Získejte plnou verzy!
title: Získejte plnou verzi!
no_thanks: Ne, děkuji!
points:
levels:
@ -399,10 +399,10 @@ buildings:
miner:
default:
name: Extraktor
description: Umístěte na náleziště tvaru nebo barvy pro zahájení těžby.
description: Umístěte na naleziště tvaru nebo barvy pro zahájení těžby.
chainable:
name: Extraktor (Navazující)
description: Umístěte na náleziště tvaru nebo barvy pro zahájení těžby. Lze
description: Umístěte na naleziště tvaru nebo barvy pro zahájení těžby. Lze
zapojit po skupinách.
underground_belt:
default:
@ -470,7 +470,7 @@ buildings:
balancer:
default:
name: Vyvažovač
description: Multifunkční - Rozděluje vstupy do výstupy.
description: Multifunkční - Rovnoměrně rozděluje vstupy na výstupech.
merger:
name: Spojovač (kompaktní)
description: Spojí dva pásy do jednoho.
@ -535,7 +535,7 @@ buildings:
reader:
default:
name: Čtečka pásů
description: Umožňuje měřit průměrnou propustnost pásu. Výstup čte poslední
description: Umožňuje změřit průměrnou propustnost pásu. Výstup čte poslední
položku ve vrstvě kabelů.
analyzer:
default:
@ -633,7 +633,7 @@ storyRewards:
barvy!
reward_storage:
title: Sklad
desc: Právě jste odemkli <strong>sklad</strong> - Umožnuje skladovat přebytečné věci
desc: Právě jste odemkli <strong>sklad</strong> - Umožnuje skladovat přebytečné věci
až do naplnění kapacity!<br><br> Dává prioritu levému
výstupu, takže ho také můžete použít jako <strong>průtokovou bránu</strong>!
reward_freeplay:
@ -732,7 +732,7 @@ settings:
general: Obecné
userInterface: Uživatelské rozhraní
advanced: Rozšířené
performance: Performance
performance: Výkon
versionBadges:
dev: Vývojová verze
staging: Testovací verze
@ -742,7 +742,7 @@ settings:
uiScale:
title: Škálování UI
description: Změní velikost uživatelského rozhraní. Rozhraní se bude stále
přizpůsobovoat rozlišení vaší obrazovky, toto nastavení pouze
přizpůsobovat rozlišení vaší obrazovky, toto nastavení pouze
mění jeho škálu.
scales:
super_small: Velmi malé
@ -751,7 +751,7 @@ settings:
large: Velké
huge: Obrovské
scrollWheelSensitivity:
title: Citlivost přibížení
title: Citlivost přiblížení
description: Změní citlivost přiblížení (kolečkem myši nebo trackpadem).
sensitivity:
super_slow: Hodně pomalé
@ -801,8 +801,8 @@ settings:
slow: Pomalá
regular: Normální
fast: Rychlá
super_fast: Hodně Rychlá
extremely_fast: Extrémně Rychlá
super_fast: Hodně rychlá
extremely_fast: Extrémně rychlá
enableTunnelSmartplace:
title: Chytré tunely
description: Pokládání tunelů po zapnutí bude samo odstraňovat nepotřebné pásy.
@ -829,7 +829,7 @@ settings:
koeficient. V opačném případě zobrazí popis a obrázek.
disableCutDeleteWarnings:
title: Zakázat upozornění o vyjmutí nebo odstranění
description: Deaktivujte varovná dialogová okna vyvolaná při vymutí/mazání více
description: Deaktivujte varovná dialogová okna vyvolaná při vyjmutí/mazání více
než 100 entit.
enableColorBlindHelper:
title: Režim pro barvoslepé
@ -965,7 +965,7 @@ about:
Pokud se chceš na hře podílet, podívej se na <a href="<githublink>" target="_blank">shapez.io na githubu</a>.<br><br>
Tato hra by nebyla ani možná bez skvělé Discord komunity okolo Tobiasových her - Vážně by ses měl přijít mrknout na <a href="<discordlink>" target="_blank">Discord server</a>!<br><br>
Tato hra by nebyla možná ani bez skvělé Discord komunity okolo Tobiasových her - Vážně by ses měl přijít mrknout na <a href="<discordlink>" target="_blank">Discord server</a>!<br><br>
Soundtrack udělal <a href="https://soundcloud.com/pettersumelius" target="_blank">Peppsen</a> - Je úžasnej.<br><br>

View File

@ -5,7 +5,7 @@ steamPage:
discordLinkShort: Official Discord
intro: >-
Shapez.io er et afslapet spil hvor du skal bygge fabrikker for at
automatisere productionen af geometriske figurer.
automatisere productionen af geometriske figurer.
Jo længer du når, jo mere kompliseret bliver figurene, og du bliver nød til at spræde dig ud på den grænseløse spilleflade.

View File

@ -1,40 +1,39 @@
---
steamPage:
shortText: In shapez.io nutzt du die vorhandenen Ressourcen, um mit deinen
Maschinen durch Kombination immer komplexere Formen zu erschaffen.
discordLinkShort: Offizieller Discord
intro: >-
Du magst Automatisierungsspiele? Dann bist du hier genau richtig!
shapez.io ist ein ruhiges Spiel, in dem du Fabriken zur automatisierten Produktion von geometrischen Formen bauen musst. Mit steigendem Level werden die Formen immer komplexer, und du musst dich auf der unendlich großen Karte ausbreiten.
Und als ob das noch nicht genug wäre, musst du auch exponentiell mehr produzieren, um die Anforderungen zu erfüllen - Da hilft nur skalieren! Während du am Anfang nur Formen verarbeitest, musst du diese später einfärben - Dafür musst du Farben extrahieren und mischen!
Der Kauf des Spiels auf Steam gibt dir Zugriff auf die Vollversion, du kannst aber auch zuerst eine Demo auf shapez.io spielen und dich später entscheiden!
shapez.io ist ein ruhiges Spiel, in dem du Fabriken zur automatisierten Produktion von geometrischen Formen bauen musst.
Mit steigendem Level werden die Formen immer komplexer, und du musst dich auf der unendlich großen Karte ausbreiten.
Das ist noch nicht alles, denn du musst exponentiell mehr produzieren, um die Anforderungen zu erfüllen - Da hilft nur skalieren!
Während du am Anfang nur Formen verarbeitest, musst du diese später auch einfärben - Dafür musst du Farben extrahieren und mischen!
Der Kauf des Spiels auf Steam gibt dir Zugriff auf die Vollversion, aber du kannst auch zuerst die Demo auf shapez.io spielen und dich später entscheiden!
title_advantages: Vorteile der Vollversion
advantages:
- <b>12 Neue Level</b> für insgesamt 26 Level
- <b>18 Neue Gebäude</b> für eine komplett automatisierte Fabrik!
- <b>20 Upgrade Stufen</b> für viele Stunden Spielspaß
- <b>Wires Update</b> für eine komplett neue Dimension!
- <b>Dark Mode</b>!
- <b>20 Upgrade-Stufen</b> für viele Stunden Spielspaß
- <b>Wires-Update</b> für eine komplett neue Dimension!
- <b>Dark-Mode</b>!
- Unbegrenzte Speicherstände
- Unbegrenzte Wegpunkte
- Unterstütze mich! ❤️
discordLinkShort: Offizieller Discord
title_future: Geplante Inhalte
planned:
- Blaupausen-Bibliothek
- Steam Errungenschaften
- Errungenschaften auf Steam
- Puzzel-Modus
- Minimap
- Mod Unterstützung
- Sandkasten - Modus
- Modunterstützung
- Sandkastenmodus
- ... und noch viel mehr!
title_open_source: Dieses Spiel ist Quelloffen!
title_open_source: Dieses Spiel ist quelloffen!
text_open_source: >-
Jeder kann etwas zum Spiel beitragen! Ich engagiere mich aktiv in der
Community und versuche alle Vorschläge zu berücksichtigen.
Die vollständige Roadmap findet ihr in meinem Trello-Board!
Die vollständige Roadmap findet ihr auf meinem Trello-Board!
title_links: Links
links:
discord: Offizieller Discord
@ -45,7 +44,7 @@ steamPage:
global:
loading: Laden
error: Fehler
thousandsDivider: .
thousandsDivider: "."
decimalSeparator: ","
suffix:
thousands: k
@ -74,16 +73,16 @@ global:
shift: UMSCH
space: LEER
demoBanners:
title: Demo Version
title: Demo-Version
intro: Kauf die Vollversion für alle Features!
mainMenu:
play: Spielen
changelog: Änderungsprotokoll
continue: Fortsetzen
newGame: Neues Spiel
changelog: Änderungsprotokoll
subreddit: Reddit
importSavegame: Importieren
openSourceHint: Dieses Spiel ist Open Source!
openSourceHint: Dieses Spiel ist quelloffen!
discordLink: Offizieller Discord Server
helpTranslate: Hilf beim Übersetzen!
madeBy: Ein Spiel von <author-link>
@ -110,7 +109,7 @@ dialogs:
title: Importfehler
text: "Fehler beim Importieren deines Speicherstands:"
importSavegameSuccess:
title: Speicherstand Importieren
title: Speicherstand importiert
text: Dein Speicherstand wurde erfolgreich importiert.
gameLoadFailure:
title: Der Speicherstand ist kaputt
@ -121,13 +120,13 @@ dialogs:
'<savegameName>' auf Level <savegameLevel><br><br>Das kann nicht rückgängig gemacht werden!
savegameDeletionError:
title: Löschen fehlgeschlagen
text: "Das Löschen des Spiels ist fehlgeschlagen:"
text: "Das Löschen des Speicherstands ist fehlgeschlagen:"
restartRequired:
title: Neustart nötig
text: Du musst das Spiel neu starten, um die Einstellungen anzuwenden.
editKeybinding:
title: Tastenbelegung ändern
desc: Drücke die (Maus-)Taste, die du belegen möchtest, oder ESC um abzubrechen.
desc: Drücke die (Maus-)Taste, die du belegen möchtest, oder ESC zum Abbrechen.
resetKeybindingsConfirmation:
title: Tastenbelegung zurücksetzen
desc: Dies wird alle deine Tastenbelegungen auf den Standard zurücksetzen. Bist
@ -138,16 +137,16 @@ dialogs:
featureRestriction:
title: Demo-Version
desc: Du hast ein Feature benutzt (<feature>), welches nicht in der Demo
enthalten ist. Erwerbe die Vollversion auf Steam für das volle Erlebnis!
enthalten ist. Erwerbe die Vollversion für das volle Erlebnis!
oneSavegameLimit:
title: Begrenzte Spielstände
desc: Du kannst in der Demo nur einen Spielstand haben. Bitte lösche den
existierenden Spielstand oder hole dir die Vollversion!
title: Begrenzte Speicherstände
desc: Du kannst in der Demo nur einen Speicherstand haben. Bitte lösche den
existierenden oder hole dir die Vollversion!
updateSummary:
title: Neues Update!
desc: "Hier sind die Änderungen, seitdem du das letzte Mal gespielt hast:"
upgradesIntroduction:
title: Upgrades Freischalten
title: Upgrades freischalten
desc: >-
Viele deiner Formen können noch benutzt werden, um Upgrades freizuschalten
- <strong>Zerstöre deine alten Fabriken nicht!</strong> Den
@ -179,26 +178,26 @@ dialogs:
<code class='keybinding'>ALT</code>: Invertiere die Platzierungsrichtung der Förderbänder.<br>
createMarker:
title: Neuer Marker
desc: Vergib einen vernünftigen namen, du kannst auch den <strong>Kurz-Code</strong> einer Form eingeben (Welchen du <link>hier</link>) generieren kannst.
titleEdit: Marker bearbeiten
desc: Gib ihm einen griffigen Namen. Du kannst auch den <strong>Kurz-Code</strong> einer Form eingeben (Welchen du <link>hier</link> generieren kannst).
editSignal:
title: Signal setzen
descItems: "Wähle ein vordefiniertes Item:"
descShortKey: ... oder gib den <strong>Kurz-Code</strong> einer Form an (Welchen du <link>hier</link> generieren kannst).
markerDemoLimit:
desc: Du kannst nur 2 Marker in der Demo benutzen. Hol dir die Vollversion, um
desc: Du kannst nur 2 Marker in der Demo benutzen. Hole dir die Vollversion, um
unendlich viele Marker zu erstellen!
exportScreenshotWarning:
title: Bildschirmfoto exportieren
desc: Hier kannst du ein Bildschirmfoto von deiner ganzen Fabrik erstellen. Für
extrem große Fabriken kann das jedoch sehr lange dauern und ggf. zum
Spielabsturz führen!
editSignal:
title: Signal Setzen
descItems: "Wähle ein vordefiniertes item:"
descShortKey: ... oder gib den <strong>Kurz-Code</strong> einer Form an (Welchen du <link>hier</link> generieren kannst)
renameSavegame:
title: Speicherstand umbenennen
desc: Hier kannst du deinen Speicherstand umbenennen.
entityWarning:
title: Leistungswarnung
desc: Du hast eine Menge Gebäude platziert, das hier ist nur ein freundlicher Hinweis dass das Spiel nicht mit unendlich vielen Gebäuden umgehen kann!
desc: Du hast eine Menge Gebäude platziert. Das hier ist nur ein freundlicher Hinweis, dass das Spiel nicht mit unendlich vielen Gebäuden umgehen kann. Halte deine Fabriken kompakt!
ingame:
keybindingsOverlay:
moveMap: Bewegen
@ -229,9 +228,9 @@ ingame:
cyan: Cyan
white: Weiß
black: Schwarz
uncolored: Farblos
uncolored: Grau
buildingPlacement:
cycleBuildingVariants: Presse <key> zum Wechseln
cycleBuildingVariants: Drücke <key> zum Wechseln
hotkeyLabel: "Taste: <key>"
infoTexts:
speed: Geschw.
@ -284,11 +283,10 @@ ingame:
description: Alle im Hub gelagerten Formen.
produced:
title: Produziert
description: Alle Formen, die in deiner Fabrik hergestellt werden, einschließlich Zwischenprodukte.
description: Alle in deiner Fabrik hergestellten Formen inkl. Zwischenprodukte.
delivered:
title: Geliefert
description: Formen, die an den Hub geliefert werden.
description: An den Hub gelieferte Formen.
noShapesProduced: Es werden noch keine Formen produziert oder geliefert.
shapesDisplayUnits:
second: <shapes> / s
@ -298,7 +296,6 @@ ingame:
playtime: Spielzeit
buildingsPlaced: Gebäude
beltsPlaced: Förderbänder
tutorialHints:
title: Brauchst du Hilfe?
showHint: Hinweis
@ -306,7 +303,7 @@ ingame:
blueprintPlacer:
cost: Kosten
waypoints:
waypoints: Markierungen
waypoints: Marker
hub: Hub
description: Linksklick auf einen Marker, um dort hinzugelangen. Rechtsklick, um
ihn zu löschen.<br><br>Drücke <keybinding>, um einen Marker aus
@ -318,29 +315,29 @@ ingame:
empty: Leer
copyKey: Schlüssel kopieren
interactiveTutorial:
title: Tutorial
title: Einführung
hints:
1_1_extractor: Platziere einen <strong>Extrahierer</strong> auf der
<strong>Kreisform</strong>, um sie zu extrahieren!
1_2_conveyor: "Verbinde den Extrahierer mit einem <strong>Förderband</strong>
1_2_conveyor: "Verbinde den Extrahierer mit einem <strong>Fließband</strong>
und schließe ihn am Hub an!<br><br>Tipp: <strong>Drücke und
ziehe</strong> das Förderband mit der Maus!"
ziehe</strong> das Fließband mit der Maus!"
1_3_expand: "Dies ist <strong>KEIN</strong> Idle-Game! Baue mehr Extrahierer und
Förderbänder, um das Ziel schneller zu erreichen.<br><br>Tipp:
Halte <strong>UMSCH</strong>, um mehrere Gebäude zu platzieren
und nutze <strong>R</strong>, um sie zu rotieren."
connectedMiners:
one_miner: 1 Extrahierer
one_miner: Ein Extrahierer
n_miners: <amount> Extrahierer
limited_items: Begrenzt auf <max_throughput>
watermark:
title: Demo Version
title: Demo-Version
desc: Klicke hier, um die Vorteile der Vollversion zu sehen!
get_on_steam: Zur Vollversion
standaloneAdvantages:
title: Vorteile der Vollversion
no_thanks: Nein, Dank!
no_thanks: Nein, danke!
points:
levels:
title: 12 Neue Level
@ -352,20 +349,20 @@ ingame:
title: ∞ Speicherstände
desc: So viele dein Herz begehrt!
upgrades:
title: 20 Upgrade Stufen
title: 20 Upgrade-Stufen
desc: Diese Demo hat nur 5!
markers:
title: ∞ Marker
desc: Verliere dich nie in deiner Fabrik!
desc: Verliere nie den Überblick!
wires:
title: Wires
desc: Eine ganz neue Dimension!
darkmode:
title: Dark Mode
title: Dark-Mode
desc: Werde nicht mehr geblendet!
support:
title: Unterstütze Mich
desc: Ich verwende meine Freizeit!
desc: Ich entwickle in meiner Freizeit!
shopUpgrades:
belt:
name: Förderbänder, Verteiler & Tunnel
@ -382,12 +379,12 @@ shopUpgrades:
buildings:
hub:
deliver: Liefere
toUnlock: "Für die Belohnung:"
toUnlock: "und schalte frei:"
levelShortcut: LVL
endOfDemo: Ende der Demo
belt:
default:
name: Förderband
name: Fließband
description: Transportiert Items. Halte und ziehe, um mehrere zu platzieren.
miner:
default:
@ -406,13 +403,29 @@ buildings:
name: Tunnel Stufe II
description: Erlaubt dir, Formen und Farbe unter Gebäuden und Förderbändern
durchzuleiten. Höhere Reichweite.
balancer:
default:
name: Verteiler
description: Multifunktional - Verteilt alle Eingänge gleichmäßig auf die Ausgänge.
merger:
name: Kombinierer (kompakt)
description: Kombiniert zwei Fließbänder zu einem.
merger-inverse:
name: Kombinierer (kompakt)
description: Kombiniert zwei Fließbänder zu einem.
splitter:
name: Aufteiler (kompakt)
description: Teilt ein Fließband in zwei.
splitter-inverse:
name: Aufteiler (kompakt)
description: Teilt ein Fließband in zwei.
cutter:
default:
name: Schneider
description: Zerschneidet Formen von oben nach unten. <strong>Benutze oder
zerstöre beide Hälften, sonst verstopft die Maschine!</strong>
quad:
name: Schneider (Vierfach)
name: Schneider (vierfach)
description: Zerschneidet Formen in vier Teile. <strong>Benutze oder zerstöre
alle Viertel, sonst verstopft die Maschine!</strong>
rotater:
@ -444,11 +457,11 @@ buildings:
description: Färbt die ganze Form aus dem linken Eingang mit der Farbe aus dem
oberen Eingang.
double:
name: Färber (2-fach)
name: Färber (zweifach)
description: Färbt beide Formen aus dem linken Eingang mit der Farbe aus dem
oberen Eingang.
quad:
name: Färber (4-fach)
name: Färber (vierfach)
description: Erlaubt dir, jeden Quadranten der Form individuell zu färben. Nur
Quadranten mit einem <strong>wahren Signal</strong> auf der Wires-Ebene
werden angemalt!
@ -457,49 +470,33 @@ buildings:
name: Mülleimer
description: Akzeptiert Formen und Farben aus jeder Richtung und zerstört sie.
Für immer ...
wire:
default:
name: Signalkabel
description: Erlaubt dir Signale zu transportieren.
second:
name: Signalkabel
description: Überträgt Signale, die Gegenstände, Farben oder Wahrheitswerte (1 oder 0) sein können. Unterschiedlich farbige Kabel verbinden sich nicht miteinander.
balancer:
default:
name: Verteiler
description: Multifunktional - Verteilt alle Eingänge gleichmäßig auf alle Ausgänge.
merger:
name: Kombinierer (kompakt)
description: Kombiniert zwei Fließbänder in eins.
merger-inverse:
name: Kombinierer (kompakt)
description: Kombiniert zwei Fließbänder in eins.
splitter:
name: Verteiler (kompakt)
description: Teilt ein Fließband in zwei.
splitter-inverse:
name: Verteiler (kompakt)
description: Teilt ein Fließband in zwei.
storage:
default:
name: Speicher
description:
Speichert überschüssige Gegenstände, bis zu einer bestimmten Kapazität. Priorisiert den linken
Ausgang und kann als Überlauftor verwendet werden.
wire:
default:
name: Signalkabel
description: Erlaubt den Transport von Signalen. Das sind Items, Farben oder Wahrheitswerte (1 oder 0). Unterschiedlich gefärbte Kabel verbinden sich nicht.
second:
name: Signalkabel
description: Erlaubt den Transport von Signalen. Das sind Items, Farben oder Wahrheitswerte (1 oder 0). Unterschiedlich gefärbte Kabel verbinden sich nicht.
wire_tunnel:
default:
name: Signal-Kreuzung
description: Erlaubt es, zwei Kabel zu kreuzen, ohne sie zu verbinden.
name: Kabelkreuzung
description: Erschafft eine isolierte Kreuzung zweier Kabel.
constant_signal:
default:
name: Konstantes Signal
description: Sendet ein konstantes Signal, das entweder eine Form, eine Farbe oder
Wahrheitswert (1 / 0) sein kann.
name: Signalgeber
description: Sendet ein konstantes Signal. Du wählst zwischen Formen, Farben oder
Wahrheitswerten (1 oder 0).
lever:
default:
name: Schalter
description:
Kann umgeschaltet werden, um einen Wahrheitswert (1 / 0) auf der Wires-Ebene auszusenden,
Sendet einen Wahrheitswert (1 oder 0) auf der Wires-Ebene abhängig von seiner Stellung,
welcher dann z.B. zur Steuerung eines Filters verwendet werden kann.
logic_gate:
default:
@ -516,7 +513,7 @@ buildings:
(wahr bedeutet Form, Farbe oder "1").
or:
name: ODER Gatter
description: Gibt eine "1" aus, wenn eine der Eingäge wahr ist (wahr bedeutet Form, Farbe oder "1").
description: Gibt eine "1" aus, wenn einer der Eingänge wahr ist (wahr bedeutet Form, Farbe oder "1").
transistor:
default:
name: Transistor
@ -533,12 +530,12 @@ buildings:
restlichen nach rechts. Kann auch mit Wahrheitswerten gesteuert werden.
display:
default:
name: Display
description: Verbinde ein Signal, um es auf dem Display anzuzeigen - Es kann eine Form sein,
Farbe oder Wahrheitswert.
name: Anzeige
description: Verbinde ein Signal, um es auf der Anzeige darzustellen. Es kann eine Form,
Farbe oder ein Wahrheitswert sein.
reader:
default:
name: Fließband Leser
name: Fließbandkontrolle
description:
Ermöglicht es, den durchschnittlichen Durchsatz des Fließbandes zu messen. Gibt den letzten
Gegenstand auf der Wires-Ebene aus (sobald freigeschaltet).
@ -550,7 +547,7 @@ buildings:
comparator:
default:
name: Vergleich
description: Gibt eine "1" zurück, wenn beide Signale genau gleich sind. Kann Formen, Gegenstände und Wahrheitswerte vergleichen.
description: Gibt eine "1" zurück, wenn beide Signale genau gleich sind. Kann Formen, Farben und Wahrheitswerte vergleichen.
virtual_processor:
default:
name: Virtueller Schneider
@ -559,7 +556,7 @@ buildings:
name: Virtueller Rotierer
description: Dreht die Form virtuell, sowohl im als auch gegen den Uhrzeigersinn.
unstacker:
name: Virtueller Unstapler
name: Virtueller Entstapler
description: Extrahiert virtuell die oberste Ebene nach rechts und die
die restlichen Ebenen nach links.
stacker:
@ -568,11 +565,10 @@ buildings:
painter:
name: Virtueller Färber
description: Färbt virtuell die Form vom unteren Eingang mit der Farbe aus dem rechten Eingang.
item_producer:
default:
name: Item-Produzent
description: Nur im Sandbox-Modus verfügbar, gibt das Signal aus der Wires-Ebene auf der regulären Schicht aus.
description: Nur im Sandkastenmodus verfügbar. Gibt das Signal aus der Wires-Ebene auf der regulären Ebene aus.
storyRewards:
reward_cutter_and_trash:
title: Formen zerschneiden
@ -602,10 +598,10 @@ storyRewards:
sie nebeneinander, werden sie <strong>verschmolzen</strong>.
Anderenfalls wird die rechte auf die linke Form
<strong>gestapelt</strong>.
reward_splitter:
title: Verteiler/Kombinierer
desc: Du hast eine <strong>Splitter</strong> Variante des
<strong>Verteilers</strong> freigeschaltet - Er teilt ein Fließband auf zwei auf!
reward_balancer:
title: Verteiler
desc: Der multifunktionale <strong>Verteiler</strong> wurde freigeschaltet! Er kann
benutzt werden, um größere Fabriken zu bauen, indem Items auf Fließbänder aufgeteilt oder zusammengelegt werden!
reward_tunnel:
title: Tunnel
desc: Der <strong>Tunnel</strong> wurde freigeschaltet! Du kannst Items nun
@ -627,68 +623,59 @@ storyRewards:
desc: Du hast eine neue Variante des <strong>Tunnels</strong> freigeschaltet!
Dieser hat eine <strong>höhere Reichweite</strong> und du kannst
beide Tunnel miteinander mischen.
reward_merger:
title: Kompakter Kombinierer
desc: Du hast eine <strong>kompakte Variante</strong> des <strong>Verteilers</strong>
freigeschaltet! Der Kombinierer vereint zwei Eingäge zu einem Ausgang.
reward_splitter:
title: Kompakter Aufteiler
desc: Du hast eine <strong>kompakte Variante</strong> des <strong>Verteilers</strong>
freigeschaltet! Der Aufteiler spaltet einen Eingang in zwei Aufgänge auf.
reward_belt_reader:
title: Fließbandkontrolle
desc: Du hast nun die <strong>Fließbandkontrolle</strong> freigeschaltet! Damit kannst du dir
den Durchsatz eines Fließbandes anzeigen lassen.<br><br>Wenn du Stromkabel freischaltest,
wird er um eine sehr nützliche Funktion ergänzt!
reward_cutter_quad:
title: Schneider (4-fach)
desc: Du hast eine neue Variante des <strong>Schneiders</strong> freigeschaltet!
Damit kannst du Formen in alle <strong>vier Teile</strong>
zerschneiden.
Damit kannst du Formen in alle <strong>vier Teile</strong> zerschneiden.
reward_painter_double:
title: Färber (2-fach)
desc: Du hast eine neue Variante des <strong>Färbers</strong> freigeschaltet!
Hiermit kannst du <strong>zwei Formen auf einmal</strong> färben und
verbrauchst nur eine Farbe.
reward_storage:
title: Speicher
desc: Du hast das <strong>Speicher</strong> Gebäude freigeschaltet - Es erlaubt dir
title: Lager
desc: Du hast das <strong>Lager</strong> freigeschaltet! Es erlaubt dir,
Gegenstände bis zu einer bestimmten Kapazität zu speichern!<br><br>
Es priorisiert den linken Ausgang, also kannst du es auch als <strong>Überlauftor</strong> benutzen!
reward_freeplay:
title: Freies Spiel
desc: >-
Du hast es geschafft! Du hast den <strong>Freispiel-Modus</strong> freigeschaltet! Das bedeutet,
dass die Formen jetzt <strong>zufällig</strong> erzeugt werden!<br><br>
Da der Hub ab jetzt einen <strong>Durchsatz</strong> benötigt, empfehle ich dringend eine Maschine zu bauen,
die automatisch die gewünschte Form liefert!<br><br>
Der HUB gibt die gewünschte Form auf der Wires-Ebene aus, also ist alles was du tun musst, sie zu analysieren und
automatisch deine Fabrik basierend darauf zu konfigurieren.
reward_blueprints:
title: Blaupause
title: Blaupausen
desc: Jetzt kannst du Teile deiner Fabrik <strong>kopieren und
einfügen</strong>! Wähle ein Areal aus (Halte STRG und ziehe mit
deiner Maus) und drücke 'C', um zu kopieren.<br><br>Einfügen ist
<strong>nicht kostenlos</strong>, du musst
<strong>nicht kostenlos</strong>! Du musst
<strong>Blaupausenformen</strong> produzieren, um die Kopierkosten
zu decken (Welche du gerade produziert hast)!
no_reward:
title: Nächstes Level
desc: "Dieses Level hat dir keine Belohnung gegeben, aber im Nächsten gibt es
eine! <br><br> PS: Denke daran, deine alten Fabriken nicht zu
zerstören - Du wirst sie später <strong>alle</strong> noch brauchen,
um <strong>Upgrades freizuschalten</strong>!"
no_reward_freeplay:
title: Nächstes Level
desc: Du hast das nächste Level freigeschalten!
reward_balancer:
title: Verteiler
desc: Der multifunktionale <strong>Verteiler</strong> wurde freigeschaltet - Er kann
benutzt werden, um größere Fabriken zu bauen, indem Gegenstände auf mehrere Fließbänder aufgeteilt und zusammengelegt werden!
reward_merger:
title: Kompakter Verteiler
desc: >-
Du hast eine <strong>kompakte Variante</strong> des <strong>Verteilers</strong> freigeschalten - Sie verteilt zwei Fließbänder auf eins!
reward_belt_reader:
title: Fließband Leser
desc: You have now unlocked the <strong>belt reader</strong>! It allows you to
measure the throughput of a belt.<br><br>And wait until you unlock
wires - then it gets really useful!
reward_rotater_180:
title: Rotater (180 degrees)
desc: You just unlocked the 180 degrees <strong>rotater</strong>! - It allows
you to rotate a shape by 180 degrees (Surprise! :D)
title: Rotierer (180°)
desc: Du hast eine weitere Variante des <strong>Rotierers</strong> freigeschaltet! Mit ihm
kannst du Formen um 180° drehen (Überraschung! :D).
reward_wires_painter_and_levers:
title: Wires & Quad Painter
desc: "You just unlocked the <strong>Wires Layer</strong>: It is a separate
layer on top of the regular layer and introduces a lot of new
mechanics!<br><br> For the beginning I unlocked you the <strong>Quad
Painter</strong> - Connect the slots you would like to paint with on
the wires layer!<br><br> To switch to the wires layer, press
<strong>E</strong>."
reward_filter:
title: Item Filter
desc: You unlocked the <strong>Item Filter</strong>! It will route items either
to the top or the right output depending on whether they match the
signal from the wires layer or not.<br><br> You can also pass in a
boolean signal (1 / 0) to entirely activate or disable it.
reward_display:
title: Display
desc: "You have unlocked the <strong>Display</strong> - Connect a signal on the
@ -718,35 +705,39 @@ storyRewards:
shape requested by the HUB (I recommend to try it!).<br><br> - Build
something cool with wires.<br><br> - Continue to play
regulary.<br><br> Whatever you choose, remember to have fun!
reward_wires_painter_and_levers:
title: Wires & Quad Painter
desc: "You just unlocked the <strong>Wires Layer</strong>: It is a separate
layer on top of the regular layer and introduces a lot of new
mechanics!<br><br> For the beginning I unlocked you the <strong>Quad
Painter</strong> - Connect the slots you would like to paint with on
the wires layer!<br><br> To switch to the wires layer, press
<strong>E</strong>."
reward_filter:
title: Item Filter
desc: You unlocked the <strong>Item Filter</strong>! It will route items either
to the top or the right output depending on whether they match the
signal from the wires layer or not.<br><br> You can also pass in a
boolean signal (1 / 0) to entirely activate or disable it.
no_reward:
title: Nächstes Level
desc: "Dieses Level hat dir keine Belohnung gegeben, aber im Nächsten gibt es
eine! <br><br> PS: Denke daran, deine alten Fabriken nicht zu
zerstören - Du wirst sie später <strong>alle</strong> noch brauchen,
um <strong>Upgrades freizuschalten</strong>!"
no_reward_freeplay:
title: Nächstes Level
desc: Du hast das nächste Level freigeschaltet!
reward_freeplay:
title: Freies Spiel
desc: Du hast es geschafft! Du hast den <strong>Freispiel-Modus</strong> freigeschaltet! Das bedeutet,
dass die abzuliefernden Formen jetzt <strong>zufällig</strong> erzeugt werden!<br><br>
Da der Hub ab jetzt einen bestimmten <strong>Durchsatz</strong> benötigt, empfehle ich dringend, eine Maschine zu bauen,
die automatisch die gewünschte Form liefert!<br><br>
Der Hub gibt die gewünschte Form auf der Wires-Ebene aus. Also musst du sie nur analysieren und
basierend darauf automatisch deine Fabrik konfigurieren.
reward_demo_end:
title: End of Demo
desc: You have reached the end of the demo version!
title: Ende der Demo
desc: Du bist am Ende der Demo angekommen!
settings:
title: Einstellungen
categories:
general: Allgemein
userInterface: Benutzeroberfläche
advanced: Erweitert
performance: Performance
performance: Leistung
versionBadges:
dev: Entwicklung
staging: Beta
prod: Produktion
buildDate: Gebaut am <at-date>
rangeSliderPercentage: <amount> %
labels:
uiScale:
title: HUD Größe
@ -862,46 +853,44 @@ settings:
description: Deaktiviert die Warnung, welche beim Löschen und Ausschneiden von
mehr als 100 Feldern angezeigt wird.
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!
title: Minimalistische Ressourcen
description: Vereinfacht die Darstellung der Ressourcen auf der hereingezoomten Karte
zur Verbesserung der Leistung. Die Darstellung ist übersichtlicher, also probiere
es ruhig aus!
disableTileGrid:
title: Disable Grid
description: Disabling the tile grid can help with the performance. This also
makes the game look cleaner!
title: Gitter deaktivieren
description: Das Deaktivieren des Gitters kann deine Leistung verbessern. Außerdem vereinfacht
es die Darstellung!
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.
title: Abwählen mit Rechtsklick
description: Standardmäßig eingeschaltet, wählt es das aktuelle, zur Platzierung ausgewählte Gebäude
ab, wenn du die rechte Masutaste drückst. Wenn du es abschaltest, kannst du mit der rechten
Maustaste Gebäude löschen, während du im Platzierungsmodus bist.
lowQualityTextures:
title: Low quality textures (Ugly)
description: Uses low quality textures to save performance. This will make the
game look very ugly!
title: Niedrige Texturqualität (Unschön)
description: Das Spiel verwendet eine niedrigere Auflösung bei den Texturen.
Allerdings leidet die Grafik des Spiels sehr darunter!
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.
title: Chunk-Ränder anzeigen
description: Das Spiel ist in Blöcke (Chunks) aus je 16x16 Feldern aufgeteilt. Diese Einstellung
lässt dich die Grenzen zwischen den Chunks anzeigen.
pickMinerOnPatch:
title: Pick miner on resource patch
description: Enabled by default, selects the miner if you use the pipette when
hovering a resource patch.
title: Automatisch Extrahierer auswählen
description: Standardmäßig eingeschaltet, wählst du automatisch den Extrahierer, wenn du mit
der Pipette auf einen Ressourcenfleck zeigst
simplifiedBelts:
title: Simplified Belts (Ugly)
description: Does not render belt items except when hovering the belt to save
performance. I do not recommend to play with this setting if you
do not absolutely need the performance.
title: Minimalistische Förderbänder (Unschön)
description: Zur Verbesserung der Leistung werden die Items auf Förderbändern nur angezeigt,
wenn du deine Maus darüber bewegst. Hier leidet sowohl die Grafik, also auch dein
Spielerlebnis. Benutze die Funktion nur, wenn du auf die Leistung wirklich angewiesen bist!
enableMousePan:
title: Enable Mouse Pan
description: Allows to move the map by moving the cursor to the edges of the
screen. The speed depends on the Movement Speed setting.
rangeSliderPercentage: <amount> %
title: Scrollen am Bildschirmrand
description: Damit kannst du dich über die Karte bewegen, indem du deinen Mauszeiger am
Bildschirmrand platzierst. Die Geschwindigkeit stimmt dabei mit den Tasten überein.
keybindings:
title: Tastenbelegung
hint: "Tipp: Benutze STRG, UMSCH and ALT! Sie aktivieren verschiedene
Platzierungsoptionen."
hint: "Tipp: Benutze STRG, UMSCH and ALT! Sie aktivieren verschiedene Platzierungsoptionen."
resetKeybindings: Tastenbelegung zurücksetzen
categoryLabels:
general: Anwendung
@ -922,7 +911,7 @@ keybindings:
centerMap: Karte zentrieren
mapZoomIn: Reinzoomen
mapZoomOut: Rauszoomen
createMarker: Markierung erstellen
createMarker: Marker erstellen
menuOpenShop: Upgrades
menuOpenStats: Statistiken
menuClose: Menü schließen
@ -930,61 +919,59 @@ keybindings:
toggleFPSInfo: FPS und Debug-Info an/aus
switchLayers: Ebenen wechseln
exportScreenshot: Ganze Fabrik als Foto exportieren
belt: Förderband
belt: Fließband
balancer: Verteiler
underground_belt: Tunnel
miner: Extrahierer
cutter: Schneider
rotater: Rotierer (-90°)
rotater: Rotierer (90°)
stacker: Stapler
mixer: Farbmischer
painter: Färber
trash: Mülleimer
storage: Lager
wire: Stromkabel
constant_signal: Signalgeber
logic_gate: Logikgatter
lever: Schalter (regulär)
filter: Filter
wire_tunnel: Kabelkreuzung
display: Anzeige
reader: Fließbandkontrolle
virtual_processor: Virtueller Schneider
transistor: Transistor
analyzer: Formanalyse
comparator: Vergleich
item_producer: Item-Produzent (Sandkastenmodus)
pipette: Pipette
rotateWhilePlacing: Rotieren
rotateInverseModifier: "Modifikator: stattdessen gegen den UZS rotieren"
cycleBuildingVariants: Nächste Variante auswählen
confirmMassDelete: Massenlöschung bestätigen
confirmMassDelete: Löschen bestätigen
pasteLastBlueprint: Letzte Blaupause einfügen
cycleBuildings: Nächstes Gebäude auswählen
lockBeltDirection: Bandplaner aktivieren
switchDirectionLockSide: "Bandplaner: Seite wechseln"
copyWireValue: "Kabel: Wert unter Mauszeiger kopieren"
massSelectStart: Halten und ziehen zum Beginnen
massSelectSelectMultiple: Mehrere Areale markieren
massSelectCopy: Areal kopieren
massSelectCut: Areal ausschneiden
placementDisableAutoOrientation: Automatische Orientierung deaktivieren
placeMultiple: Im Platziermodus bleiben
placeInverse: Automatische Förderbandorientierung invertieren
wire: Stromkabel
balancer: Balancer
storage: Storage
constant_signal: Constant Signal
logic_gate: Logic Gate
lever: Switch (regular)
filter: Filter
wire_tunnel: Wire Crossing
display: Display
reader: Belt Reader
virtual_processor: Virtual Cutter
transistor: Transistor
analyzer: Shape Analyzer
comparator: Compare
item_producer: Item Producer (Sandbox)
copyWireValue: "Wires: Copy value below cursor"
placeInverse: Automatische Fließbandorientierung invertieren
about:
title: Über dieses Spiel
body: >-
Dieses Spiel hat einen offenen Quellcode (Open Source) und wurde von <a
Dieses Spiel ist quelloffen (Open Source) und wurde von <a
href="https://github.com/tobspr" target="_blank">Tobias Springer</a>
(das bin ich!) entwickelt.<br><br>
Wenn du etwas zum Spiel beitragen möchtest, dann schaue dir <a href="<githublink>" target="_blank">shapez.io auf GitHub</a> an.<br><br>
Das Spiel wurde erst durch die großartige Discord-Community um meine Spiele möglich gemacht. Komm doch einfach mal auf dem <a href="<discordlink>" target="_blank">Discord-Server</a> vorbei!<br><br>
Das Spiel wurde erst durch die großartige Discord-Community um meine Spiele möglich gemacht.
Komm doch einfach mal auf dem <a href="<discordlink>" target="_blank">Discord-Server</a> vorbei!<br><br>
Der Soundtrack wurde von <a href="https://soundcloud.com/pettersumelius" target="_blank">Peppsen</a> komponiert! Klasse Typ.<br><br>
Abschließend möchte ich meinem Kumpel <a href="https://github.com/niklas-dahl" target="_blank">Niklas</a> danken! Ohne unsere etlichen gemeinsamen Stunden in Factorio wäre dieses Projekt nie zustande gekommen.
Abschließend möchte ich meinem Kumpel <a href="https://github.com/niklas-dahl" target="_blank">Niklas</a> danken!
Ohne unsere etlichen gemeinsamen Stunden in Factorio wäre dieses Projekt nie zustande gekommen.
changelog:
title: Änderungen
demo:
@ -996,75 +983,59 @@ demo:
exportingBase: Ganze Fabrik als Foto exportieren
settingNotAvailable: Nicht verfügbar in der Demo.
tips:
- Der Hub akzeptiert jede Art von Form, nicht nur die aktuelle!
- Stelle sicher, dass deine Fabriken modular sind - es zahlt sich aus!
- Baue nicht zu nah am Hub, sonst wird es ein riesiges Chaos geben!
- Wenn das Stapeln nicht funktioniert, versuche die Eingänge zu wechseln.
- Du kannst mittels <b>R</b> die Richtung des Bandplaners umkehren.
- Halte <b>STRG</b> um die Förderbänder ohne automatische Orientierung zu
platzieren.
- Die Ratios bleiben gleich, solange die die Upgrades auf der selben Stufen
sind.
- Der Hub akzeptiert alle Formen, nicht nur die aktuell geforderten!
- Stelle sicher, dass deine Fabriken modular sind. Es zahlt sich irgendwann aus!
- Baue nicht zu nah am Hub, sonst entsteht ein riesiges Chaos!
- Wenn der Stapler nicht die richtige Form ausspuckt, wechsle doch mal die Eingänge.
- Du kannst mit <b>R</b> die Richtung des Bandplaners umkehren.
- Halte <b>STRG</b>, um die Förderbänder ohne automatische Orientierung zu platzieren.
- Die Verhältnisse der Maschinen bleiben gleich, wenn du die Upgrades gleichmäßig kaufst.
- Serielle Ausführung ist effizienter als parallele.
- Du wirst später im Spiel mehr Varianten von Gebäuden freischalten!
- Du kanst <b>T</b> drücken, um auf andere Varianten des Gebäude zu wechseln.
- Für viele Gebäude wirst du im Spielverlauf neue Varianten freischalten!
- Du kanst <b>T</b> drücken, um auf andere Varianten des Gebäudes zu wechseln.
- Symmetrie ist der Schlüssel!
- Du kannst verschiedene Arten von Tunneln miteinander verweben.
- Versuche kompakte Fabriken zu bauen - es zahlt sich aus!
- Der Färber hat eine spiegelverkehrte Variante, die du mittels <b>T</b>
auswählen kannst.
- Versuche kompakte Fabriken zu bauen. Es zahlt sich aus!
- Der Färber hat eine spiegelverkehrte Variante, die du mit <b>T</b> auswählen kannst.
- Das richtige Verhältnis der Gebäude maximiert die Effizienz.
- Auf dem maximalen Level genügen 5 Extrahierer für ein einzelnes Förderband.
- Auf der gleichen Upgrade-Stufe genügen 5 Extrahierer für ein ganzes Fließband.
- Vergiss die Tunnel nicht!
- Du musst die Items für maximale Effizienz nicht gleichmässig aufteilen.
- Das Halten von <b>UMSCH</b> aktiviert den Bandplaner, der dir das
Platzieren langer Linien vereinfacht.
- Schneider schneiden immer vertikal, egal deren Orientierung.
- Um Weiss zu erhalten, mixe alle Farben zusammen.
- Der Speicher priorisiert den linken Ausgang.
- Investiere Zeit, um wiederholbare Designs zu erstellen - es lohnt sich!
- Das Halten von <b>STRG</b> ermöglicht dir mehrere Gebäude zu platzieren.
- Du kanst <b>ALT</b> gedrückt halten, um die Richtung der Förderbänder
umzukehren.
- Effizienz ist der Schlüssel!
- Formflecken, die weiter vom Hub entfernt sind, sind komplexer.
- Gebäude haben eine limitierte Geschwindigkeit, teile sie auf für maximale
Effizienz.
- Benutze Verteiler um deine Effizienz zu maximieren.
- Organisation ist wichtig. Versuch das Kreuzen von Förderbändern zu
minimieren.
- Plane im Voraus, oder es gibt ein riesigen Chaos!
- Lösche deine alten Fabriken nicht! Du benötigst sie um Upgrades
freizuschalten.
- Versuch Level 20 alleine zu meistern, bevor du nach Hilfe suchst!
- Mach es dir nicht zu kompliziert, versuch es einfach zu halten und du
wirst weit vorankommen.
- Vielleicht musst du Fabriken später im Spiel wiederverwenden. Plane deine
Fabriken so, dass sie wiederverwendbar sind.
- Manchmal kannst du die gewünschte Form auf der Karte finden, ohne sie mit
Staplern zu erstellen.
- Für maximale Effizienz musst du die Items nicht gleichmässig aufteilen.
- Das Halten von <b>UMSCH</b> aktiviert den Bandplaner, der lange Förderbänder ganz einfach platziert.
- Schneider teilen die Form immer vertikal, unabhängig von der Orientierung.
- Weiß erhälst du aus der Kombination aller 3 Grundfarben.
- Das Lager gibt Items immer zuerst am linken Ausgang ab.
- Es lohnt sich, Zeit in den Bau von wiederverwendbaren Designs zu stecken!
- Das Halten von <b>STRG</b> ermöglicht dir, mehrere Gebäude zu platzieren.
- Du kanst <b>ALT</b> gedrückt halten, um die Richtung der Förderbänder umzukehren.
- Effizienz ist entscheidend!
- Abbaubare Formen werden komplexer, je weiter sie vom Hub entfernt sind.
- Gebäude haben eine limitierte Geschwindigkeit. Teile die Last zwischen mehreren auf.
- Benutze Aufteiler, um deine Effizienz zu maximieren.
- Organisation ist wichtig! Verheddere dich nicht in einem Gewirr aus Förderbändern.
- Plane vorher und lasse dir Platz für Reserven, oder es gibt ein riesiges Chaos!
- Lösche deine alten Fabriken nicht! Du benötigst sie um Upgrades freizuschalten.
- Versuche Level 20 alleine zu meistern, bevor du nach Hilfe suchst!
- Mache es dir nicht zu kompliziert! Auch mit einfachen Konzepten kommst du hier sehr weit.
- Manche Fabriken musst du später wiederverwenden. Also baue sie so, damit du genau das kannst.
- Manchmal kannst du die gewünschte Form auf der Karte finden, ohne sie herstellen zu müssen.
- Vollständige Windmühlen werden nicht natürlich generiert.
- Färbe deine Formen vor dem Schneiden für maximale Effizienz.
- Mit Modulen ist der Raum nur eine Wahrnehmung; eine Sorge für die
sterblichen Menschen.
- Mache eine separate Blaupausenfabrik. Sie sind wichtig für Module.
- Schau dir den Farbmischer genauer an, und deine Fragen werden beantwortet.
- Benutze <b>STRG</b> + rechter Mausklick, um einen Bereich zu selektieren.
- Färbe deine Formen vor dem Schneiden! Das geht viel schneller.
- Mit Modulen wird Platz nur noch zum Begriff; eine Sorge für Sterbliche.
- Stelle deinen Nachschub an Blaupausen sicher. Ohne sie sind Module nutzlos.
- Schau dir den Farbmischer genauer an und du wirst deine Antwort finden.
- Benutze <b>STRG</b> + Rechtsklick, um einen Bereich zu selektieren.
- Zu nahe am Hub zu bauen, kann späteren Projekten im Weg stehen.
- Das Pin-Symbol neben jeder Form in der Upgrade-Liste heftet sie an den
Bildschirm.
- Die Reißzwecke neben Formen in der Upgrade-Liste lässt sie dich am Bildschirm anheften.
- Mische alle drei Grundfarben, um Weiß zu erhalten!
- Du hast eine unendlich grosse Karte, nutze den Platz, expandiere!
- Du hast eine unendlich grosse Karte, nutze den Platz und expandiere!
- Probier auch mal Factorio! Es ist mein Lieblingsspiel.
- Der Vierfachschneider schneidet im Uhrzeigersinn von oben rechts beginnend!
- Du kannst deine Speicherstände im Hauptmenü herunterladen!
- Diese Spiel hat viele nützliche Tastenbelegungen! Schau sie dir in den
Einstellungen an.
- Dieses Spiel hat viele Einstellungen, schau sie dir einmal an!
- Die Markierung des Hubs hat einen kleinen Kompass, der die Richtung
anzeigt!
- Um die Förderbänder zu leeren, schneide den Bereich aus und füge ihn in
der gleichen Position wieder ein.
- Drücke F4 um deine FPS und Tick Rate anzuzeigen.
- Drücke doppelt F4 um die Kachel des Zeigers und der Kamera anzuzeigen.
- Du kannst die angehefteten Formen auf der linken Seite ablösen.
- Diese Spiel hat viele nützliche Tastenbelegungen! Schau sie dir in den Einstellungen an.
- Dieses Spiel hat eine Menge Einstellungen, schaue sie dir einmal an!
- Die Richtung zu deinem Hub ist oben rechts mit einer kleinen Kompassnadel markiert!
- Um alle Förderbänder zu leeren, schneide den Bereich aus und füge ihn auf der selben Position wieder ein.
- Drücke F4 um deine FPS und Tickrate anzuzeigen.
- Drücke doppelt F4 um den Standort des Mauszeigers und der Kamera zu bestimmen.
- Du kannst die angehefteten Formen am linken Rand wieder entfernen.

View File

@ -5,7 +5,7 @@ steamPage:
discordLinkShort: Official Discord
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
automated production of geometric shapes.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.

View File

@ -272,10 +272,6 @@ dialogs:
title: Rename Savegame
desc: You can rename your savegame here.
entityWarning:
title: Performance Warning
desc: You have placed a lot of buildings, this is just a friendly reminder that the game can not handle an endless number of buildings - try to keep your factories compact!
ingame:
# This is shown in the top left corner and displays useful keybindings in
# every situation
@ -355,10 +351,6 @@ ingame:
# Gets replaced to e.g. "Tier IX"
tier: Tier <x>
# The roman number for each tier
tierLabels:
[I, II, III, IV, V, VI, VII, VIII, IX, X, XI, XII, XIII, XIV, XV, XVI, XVII, XVIII, XIX, XX]
maximumLevel: MAXIMUM LEVEL (Speed x<currentMult>)
# The "Statistics" window
@ -1033,6 +1025,11 @@ settings:
description: >-
Allows panning the map by moving the cursor to the edges of the screen. The scroll speed depends on the Movement Speed setting.
zoomToCursor:
title: Zoom towards Cursor
description: >-
If activated the zoom will happen in the direction of your mouse position, otherwise in the middle of the screen.
keybindings:
title: Keybindings
hint: >-

View File

@ -2,29 +2,29 @@ steamPage:
shortText: shapez.io es un juego sobre construir fábricas para automatizar la
creación y combinación de figuras cada vez más complejas en un mapa
infinito.
discordLinkShort: Official Discord
discordLinkShort: Discord oficial
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
Shapez.io es un juego tranquilo en el que tienes que construir fábricas para la
producción automatizada de formas geométricas.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.
A medida que el nivel aumenta, las formas se vuelven más y más complejas, y tienes que extenderte en un mapa infinito.
And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling!
Y por si fuera poco, también tienes que producir exponencialmente más para satisfacer las demandas - ¡lo único que ayuda es escalar!
While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors!
Mientras que sólo procesas formas al principio, tienes que colorearlas después - ¡para ello tienes que extraer y mezclar los colores!
Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later!
title_advantages: Standalone Advantages
Comprando el juego en Steam tienes acceso a la versión completa, ¡pero también puedes jugar una demo en shapez.io primero y decidir después!
title_advantages: Ventajas del juego
advantages:
- <b>12 New Level</b> for a total of 26 levels
- <b>18 New Buildings</b> for a fully automated factory!
- <b>12 nuevos niveles</b> de un total de 26 niveles
- <b>18 nuevos edificios</b> ¡para una fábrica totalmente automatizada!
- <b>20 Upgrade Tiers</b> for many hours of fun!
- <b>Wires Update</b> for an entirely new dimension!
- <b>Dark Mode</b>!
- Unlimited Savegames
- Unlimited Markers
- <b>Modo oscuro</b>!
- Partidad guardadas ilimitadas
- Marcadores ilimitados
- Support me! ❤️
title_future: Planned Content
title_future: Contenido futuro
planned:
- Blueprint Library (Standalone Exclusive)
- Steam Achievements

View File

@ -5,7 +5,7 @@ steamPage:
discordLinkShort: Official Discord
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
automated production of geometric shapes.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.

View File

@ -4,7 +4,7 @@ steamPage:
discordLinkShort: Official Discord
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
automated production of geometric shapes.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.

View File

@ -5,7 +5,7 @@ steamPage:
discordLinkShort: Official Discord
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
automated production of geometric shapes.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.

View File

@ -4,17 +4,17 @@ steamPage:
yang meluas tanpa batas.
discordLinkShort: Official Discord
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
Shapez.io adalah game santai dimana anda harus membuat pabrik untuk
mengotomatiskan produksi bentuk-bentuk geometris.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.
Semakin meningkatnya level, bentuk-bentuknya menjadi lebih kompleks, dan anda perlu meluas di peta yang tak terbatas.
And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling!
Dan jita itu tidak cukup, anda juga perlu untuk memproduksi secara ekxponensial untuk memenuhkan kebutuhan - hal yang membantu hanyalah memperbesar pabrik!
While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors!
Walaupun anda hanya memproses bentuk di awal, anda perlu mewarnainya untuk nanti - untuk ini, anda perlu untuk mengekstrak dan mencampur warna!
Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later!
title_advantages: Standalone Advantages
Membeli game ini di Steam memberikan anda akses ke versi lengkap, namun anda juga dapat mencoba demo dan memutuskan nanti!
title_advantages: Keuntungan versi penuh
advantages:
- <b>12 New Level</b> for a total of 26 levels
- <b>18 New Buildings</b> for a fully automated factory!
@ -411,12 +411,12 @@ buildings:
beberapa.
wire:
default:
name: Kawat Energi
description: Memungkinkan anda untuk mengangkut energi.
name: Kabel
description: Memungkinkan anda untuk mengangkut Energi.
second:
name: Wire
description: Transfers signals, which can be items, colors or booleans (1 / 0).
Different colored wires do not connect.
name: Kabel
description: Mentransfer sinyal, dapat berupa bentuk, warna, atau boolean (1 / 0).
Kabel dengan warna berbeda tidak akan menyambung.
miner:
default:
name: Ekstraktor
@ -455,13 +455,13 @@ buildings:
name: Pemutar (Berlawanan Arah Jarum Jam)
description: Memutar bentuk berlawanan arah jarum jam sebesar 90 derajat.
rotate180:
name: Rotate (180)
description: Rotates shapes by 180 degrees.
name: Pemutar (180)
description: Memutar bentuk searah jarum jam sebesar 180 derajat.
stacker:
default:
name: Penyusun
description: Menggabungkan kedua artikel. Apabila mereka tidak dapat
digabungkan, artikel kanan akan diletakkan diatas artikel kiri.
name: Penumpuk
description: Menumpukkan kedua bentuk. Apabila mereka tidak dapat
digabungkan, bentuk kanan akan diletakkan diatas bentuk kiri.
mixer:
default:
name: Pencampur Warna
@ -489,123 +489,120 @@ buildings:
description: Menerima input dari semua sisi dan menghancurkannya. Selamanya.
balancer:
default:
name: Balancer
description: Multifunctional - Evenly distributes all inputs onto all outputs.
name: Pengimbang
description: Multifungsional - Mendistribusikan seluruh input secara merata ke seluruh output.
merger:
name: Merger (compact)
description: Merges two conveyor belts into one.
name: Penggabung (Kompak)
description: Menggabungkan dua sabuk konveyor menjadi satu.
merger-inverse:
name: Merger (compact)
description: Merges two conveyor belts into one.
name: Penggabung (Kompak)
description: Menggabungkan dua sabuk konveyor menjadi satu.
splitter:
name: Splitter (compact)
description: Splits one conveyor belt into two.
name: Pemisah (Kompak)
description: Memisahkan satu sabuk konveyor menjadi dua.
splitter-inverse:
name: Splitter (compact)
description: Splits one conveyor belt into two.
name: Pemisah (Kompak)
description: Memisahkan satu sabuk konveyor menjadi dua.
storage:
default:
name: Storage
description: Stores excess items, up to a given capacity. Prioritizes the left
output and can be used as an overflow gate.
name: Tempat Penyimpanan
description: Menyumpan bentuk yang berlebuhan, hingga kapasitas yang tertentu. Memprioritaskan output dari kiri
wire_tunnel:
default:
name: Wire Crossing
description: Allows to cross two wires without connecting them.
name: Penyebrangan Kabel
description: Memungkinkan untuk menyebrangkan 2 kabel tanpa menyambungkannya.
constant_signal:
default:
name: Constant Signal
description: Emits a constant signal, which can be either a shape, color or
boolean (1 / 0).
name: Sinyal Konstan
description: Mengeluarkan sinyal yang konstan, dapat berupa bentuk, warna atau boolean (1 / 0).
lever:
default:
name: Switch
description: Can be toggled to emit a boolean signal (1 / 0) on the wires layer,
which can then be used to control for example an item filter.
name: Saklar
description: Dapat diubah untuk mengeluarkan sinyal boolean (1 / 0) pada lapisan kabel,
yang bisa digunakan untuk mengontrol seperti penyaring.
logic_gate:
default:
name: AND Gate
description: Emits a boolean "1" if both inputs are truthy. (Truthy means shape,
color or boolean "1")
name: Gerbang AND
description: Mengeluarkan boolean "1" jika kedua input adalah benar. (Benar berarti sebuah bentuk,
warna atau boolean "1")
not:
name: NOT Gate
description: Emits a boolean "1" if the input is not truthy. (Truthy means
shape, color or boolean "1")
name: Gerbang NOT
description: Mengeluarkan boolean "1" jika input adalah tidak benar. (Benar berarti sebuah bentuk,
warna atau boolean "1")
xor:
name: XOR Gate
description: Emits a boolean "1" if one of the inputs is truthy, but not both.
(Truthy means shape, color or boolean "1")
name: Gerbang XOR
description: Mengeluarkan boolean "1" jika kedua input adalah benar, namun bukan keduanya.
(Benar berarti sebuah bentuk, warna atau boolean "1")
or:
name: OR Gate
description: Emits a boolean "1" if one of the inputs is truthy. (Truthy means
shape, color or boolean "1")
name: Gerbang OR
description: Mengeluarkan boolean "1" jika satu input adalah benar. (Benar berarti sebuah bentuk,
warna atau boolean "1")
transistor:
default:
name: Transistor
description: Forwards the bottom input if the side input is truthy (a shape,
color or "1").
description: Melanjutkan sinyal dari input bawah jika input samping adalah benar (sebuah bentuk,
warna atau boolean "1")
mirrored:
name: Transistor
description: Forwards the bottom input if the side input is truthy (a shape,
color or "1").
description: Melanjutkan sinyal dari input bawah jika input samping adalah benar (sebuah bentuk,
warna atau boolean "1")
filter:
default:
name: Filter
description: Connect a signal to route all matching items to the top and the
remaining to the right. Can be controlled with boolean signals
too.
description: Hubungkan sebuah sinyal untuk merutekan semua benda yang cocok ke atas dan
sisanya ke kanan. Dapat juga dikontrol dengan sinyal boolean
display:
default:
name: Display
description: Connect a signal to show it on the display - It can be a shape,
color or boolean.
name: Layar
description: Hubungkan dengan sebuah sinyal untuk ditunjukkan pada layar - Dapat berupa bentuk,
warna atau boolean.
reader:
default:
name: Belt Reader
description: Allows to measure the average belt throughput. Outputs the last
read item on the wires layer (once unlocked).
name: Pembaca Sabuk Konveyor
description: Memungkinkan untuk mengukur rata-rata benda yang melewati sabuk konveyor. Mengeluarkan output benda terakhir
yang dilewati pada lapisan kabel (Setelah terbuka).
analyzer:
default:
name: Shape Analyzer
description: Analyzes the top right quadrant of the lowest layer of the shape
and returns its shape and color.
name: Penganalisa bentuk
description: Menganalisa perempat bentuk pada kanan atas dan lapisan terbawah
lalu mengeluarkan bentuk dan warnanya.
comparator:
default:
name: Compare
description: Returns boolean "1" if both signals are exactly equal. Can compare
shapes, items and booleans.
name: Pembanding
description: Mengeluarkan boolean "1" jika kedua sinya adalah sama. Dapat membandingkan
Bentuk, warna dan boolean.
virtual_processor:
default:
name: Virtual Cutter
description: Virtually cuts the shape into two halves.
name: Pemotong Virtual
description: Memotong bentuk secara virtual menjadi dua bagian.
rotater:
name: Virtual Rotater
description: Virtually rotates the shape, both clockwise and counter-clockwise.
name: Pemutar Virtual
description: Memutar bentuk secara virtual, searah jarum jam dan tidak searah jarum jam.
unstacker:
name: Virtual Unstacker
description: Virtually extracts the topmost layer to the right output and the
remaining ones to the left.
name: Pemisah Tumpukan Virtual
description: Memisahkan lapisan teratas secara virtual ke output kanan dan
sisanya ke output kiri.
stacker:
name: Virtual Stacker
description: Virtually stacks the right shape onto the left.
name: Penumpuk Virtual
description: Menumpuk bentuk kanan ke bentuk kiri secara virtual.
painter:
name: Virtual Painter
description: Virtually paints the shape from the bottom input with the shape on
the right input.
name: Pencat Virtual
description: Mengecat bentuk dari input bawah dengan warna
dari input kanan.
item_producer:
default:
name: Item Producer
description: Available in sandbox mode only, outputs the given signal from the
wires layer on the regular layer.
name: Pembuat Artikel
description: Hanya tersedia di dalam mode sandbox , Mengeluarkan sinyal yang diberikan dari
lapisan kabel ke lapisan biasa.
storyRewards:
reward_cutter_and_trash:
title: Memotong Bentuk
desc: You just unlocked the <strong>cutter</strong>, which cuts shapes in half
from top to bottom <strong>regardless of its
orientation</strong>!<br><br>Be sure to get rid of the waste, or
otherwise <strong>it will clog and stall</strong> - For this purpose
I have given you the <strong>trash</strong>, which destroys
everything you put into it!
desc: <strong>Pemotong</strong> telah dibuka, yang dapat memotong bentuk menjadi dua
secara vertikal <strong>apapun
orientasinya</strong>!<br><br>Pastikan untuk membuang sisanya, jika
tidak <strong>ini dapat menghambat dan memperlambat</strong> - karena ini
anda diberikan <strong>Tong sampah</strong>, yang menghapus
semua yang anda masukkan!
reward_rotater:
title: Memutar
desc: <strong>Pemutar</strong> telah dibuka! Ia memutar bentuk-bentuk searah

View File

@ -4,7 +4,7 @@ steamPage:
discordLinkShort: 公式Discord
intro: >-
工場の自動化ゲームはお好きですか?それなら間違いないでしょう!
Shapez.ioは、様々な幾何学的形状を生成するために工場を建設する、落ち着いたゲームです。レベルが上がる毎に生成すべき形はどんどん複雑になり、工場を無限に広がるマップに拡張する必要があります。
しかし、それだけでは不十分です。需要は指数関数的に上昇し、より多くの形状を生産する必要があり――"スケーリング"が、唯一の対抗策と成り得ます。最初は形状を加工するだけですが、後々着色も必要になってきます――それには色を抽出して、混ぜ合わせることが必要です!
@ -120,18 +120,17 @@ dialogs:
title: セーブデータのインポートに成功
text: セーブデータをインポートしました。
gameLoadFailure:
title: ゲームが壊れています
text: >-
セーブデータのロードに失敗しました:
セーブデータのロードに失敗しました:
confirmSavegameDelete:
title: 削除確認
text: >-
本当に削除しますか?<br><br>
レベル<savegameLevel>: '<savegameName>'<br><br>
この操作は取り消しできません!
この操作は取り消しできません!
savegameDeletionError:
title: 削除に失敗
@ -142,7 +141,6 @@ dialogs:
title: 再起動が必要
text: 設定を反映するには再起動が必要です
editKeybinding:
title: キー設定の変更
desc: 割り当てるキーかマウスボタンを押してください。ESCでキャンセルします。
@ -168,33 +166,26 @@ dialogs:
desc: >-
前回からの変更点:
upgradesIntroduction:
title: アップグレード解除
desc: すべての納品された形はアップグレードの解除のためにカウントされています。<strong>作った生産ラインを削除しないようにしてください!</strong> アップグレードタブは画面の右上から確認できます。
massDeleteConfirm:
title: 削除確認
desc: 多数の建造物を削除しようとしています! (<count> 個の選択) 続行しますか?
massCutConfirm:
title: カット確認
desc: 多数の建造物をカットしようとしています! (<count> 個の選択) 続行しますか?
massCutInsufficientConfirm:
title: カット確認
desc: 設置コストが不足しています! 続行しますか?
blueprintsNotUnlocked:
title: 未解除
desc: レベル12をクリアしてブループリント機能を解除してください
keybindingsIntroduction:
title: 便利なキー設定
desc: >-
@ -228,10 +219,8 @@ dialogs:
entityWarning:
title: パフォーマンスの警告
desc: あなたは沢山の工場を配置しましたが、このゲームは無限の建物を処理できるわけではありません。これは友好的なリマインダですが、より工場をコンパクトにすることに挑戦してみてください。
ingame:
keybindingsOverlay:
moveMap: マップ移動
selectBuildings: 範囲選択
@ -253,7 +242,6 @@ ingame:
pipette: ピペット
switchLayers: レイヤーを変更
colors:
red:
green:
@ -265,17 +253,10 @@ ingame:
black:
uncolored: 無色
buildingPlacement:
cycleBuildingVariants: <key> キーを押して変更
hotkeyLabel: "ホットキー: <key>"
infoTexts:
speed: スピード
range: レンジ
@ -285,36 +266,26 @@ ingame:
itemsPerSecondDouble: (x2)
tiles: <x> タイル
levelCompleteNotification:
levelTitle: レベル <level>
completed: 完了
unlockText: <reward> を解除!
buttonNextLevel: 次のレベル
notifications:
newUpgrade: 新しいアップグレードが利用可能です!
gameSaved: ゲームをセーブしました。
freeplayLevelComplete: レベル <level> をクリアしました!
shop:
title: アップグレード
buttonUnlock: アップグレード
tier: 第 <x> 段階
tierLabels:
[I, II, III, IV, V, VI, VII, VIII, IX, X, XI, XII, XIII, XIV, XV, XVI, XVII, XVIII, XIX, XX]
maximumLevel: 最大レベル (スピード x<currentMult>)
statistics:
title: 統計情報
dataSources:
@ -329,16 +300,14 @@ ingame:
description: 中央の建造物に納品された形の総数です。
noShapesProduced: まだ形が生産されていません。
shapesDisplayUnits:
second: <shapes> / 秒
minute: <shapes> / 分
hour: <shapes> / 時間
settingsMenu:
playtime: プレイ時間
buildingsPlaced: 建造物
beltsPlaced: ベルト
@ -347,17 +316,14 @@ ingame:
settings: 設定
menu: メニューに戻る
tutorialHints:
title: ヒントが必要ですか?
showHint: ヒントを見る
hideHint: 閉じる
blueprintPlacer:
cost: コスト
waypoints:
waypoints: マーカー
hub: HUB
@ -370,7 +336,6 @@ ingame:
empty:
copyKey: キーをコピー
interactiveTutorial:
title: チュートリアル
hints:
@ -528,7 +493,7 @@ buildings:
second:
name: *wire
description: *wire_desc
wire_tunnel:
default:
name: &wire_tunnel 交差ワイヤ
@ -572,13 +537,13 @@ buildings:
display:
default:
name: &display ディスプレイ
description: >-
description: >-
入力された信号をディスプレイに表示します。
形状、色、真偽値のいずれでも可能です。
reader:
default:
name: &reader ベルトリーダ
description: >-
description: >-
平均スループットを計測できます。 アンロック後は、
最後に通過したアイテムの情報を出力します。
analyzer:
@ -773,7 +738,7 @@ settings:
title: オートセーブ間隔
description: >-
ゲームが自動的にセーブされる頻度を設定します。無効化することも可能です。
intervals:
one_minute: 1分
two_minutes: 2分
@ -818,7 +783,7 @@ settings:
soundVolume:
title: 音量(SE)
description: 効果音の音量を設定してください。
musicVolume:
title: 音量(BGM)
description: 音楽の音量を設定してください。
@ -866,7 +831,8 @@ settings:
これにより、ゲームの見た目もすっきりします。
clearCursorOnDeleteWhilePlacing:
title: 右クリックで配置をキャンセル
description: デフォルトで有効です。建物を設置しているときに右クリックすると、選択中の建物がキャンセルされます。
description:
デフォルトで有効です。建物を設置しているときに右クリックすると、選択中の建物がキャンセルされます。
無効にすると、建物の設置中に右クリックで建物を削除できます。
lowQualityTextures:
title: 低品質のテクスチャ(視認性低下)
@ -911,20 +877,20 @@ keybindings:
mapMoveLeft: 左移動
mapMoveFaster: より速く移動
centerMap: マップ中央移動
mapZoomIn: ズームイン
mapZoomOut: ズームアウト
createMarker: マーカー設置
menuOpenShop: アップグレード
menuOpenStats: 統計情報
menuClose: メニューを閉じる
toggleHud: HUD切り替え
toggleFPSInfo: FPS、デバッグ情報表示切り替え
switchLayers: レイヤを変更
exportScreenshot: 工場の全体像を画像出力
# --- Do not translate the values in this section
belt: *belt
balancer: *balancer
@ -951,7 +917,7 @@ keybindings:
comparator: *comparator
item_producer: なんでも抽出機(サンドボックス)
# ---
pipette: スポイト
rotateWhilePlacing: 回転
rotateInverseModifier: >-

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@ steamPage:
discordLinkShort: Official Discord
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
automated production of geometric shapes.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.

View File

@ -222,7 +222,7 @@ ingame:
placeBuilding: Plaats gebouw
createMarker: Plaats markering
delete: Vernietig
pasteLastBlueprint: Plak laatst gekopiëerde blauwdruk
pasteLastBlueprint: Plak laatst gekopieerde blauwdruk
lockBeltDirection: Gebruik lopende band planner
plannerSwitchSide: Draai de richting van de planner
cutSelection: Knip
@ -325,7 +325,7 @@ ingame:
1_2_conveyor: "Verbind de ontginner met een <strong>lopende band</strong> aan je
hub!<br><br>Tip: <strong>Klik en sleep</strong> de lopende band
met je muis!"
1_3_expand: "Dit is <strong>GEEN</strong> nietsdoen-spel! bouw meer ontginners
1_3_expand: "Dit is <strong>GEEN</strong> nietsdoen-spel! Bouw meer ontginners
en lopende banden om het doel sneller te behalen.<br><br>Tip:
Houd <strong>SHIFT</strong> ingedrukt om meerdere ontginners te
plaatsen en gebruik <strong>R</strong> om ze te draaien."
@ -342,7 +342,7 @@ ingame:
shapeViewer:
title: Lagen
empty: Leeg
copyKey: Kopiëer sleutel
copyKey: Kopieer sleutel
connectedMiners:
one_miner: 1 Miner
n_miners: <amount> Miners
@ -861,7 +861,7 @@ settings:
kunt spelen wanneer je kleurenblind bent.
rotationByBuilding:
title: Rotatie per type gebouw
description: Elk type gebouw onthoud apart de rotatie waarin je het voor het
description: Elk type gebouw onthoudt apart de rotatie waarin je het voor het
laatst geplaatst hebt. Dit kan handig zijn wanneer je vaak
tussen verschillende soorten gebouwen wisselt.
soundVolume:

View File

@ -5,7 +5,7 @@ steamPage:
discordLinkShort: Oficjalny serwer Discord
intro: >-
Shapez.io jest spokojną grą, której celem jest budowanie automatycznych fabryk
produkujących różne kształty geometryczne.
produkujących różne kształty geometryczne.
W miarę zwiększania się poziomów, kształty będą stawać się coraz bardziej skomplikowane, a Twoja fabryka będzie musiała się rozpszetrzenić na mapie o nieskończonej wielkości.
@ -705,7 +705,7 @@ storyRewards:
title: Stały sygnał
desc: >-
Właśnie odblokowałeś budynek emitujący <strong>stały sygnał</strong> na warstwie przewodów!
Jest on przydatny na przykład: do ustawiania <strong>filtrów</strong><br><br>
Jest on przydatny na przykład: do ustawiania <strong>filtrów</strong><br><br>
Sygnał może być <strong>kształtem</strong>, <strong>kolorem</strong> lub wartością
<strong>Prawda/Fałsz</strong>.
reward_logic_gates:

View File

@ -4,7 +4,7 @@ steamPage:
discordLinkShort: Discord Oficial
intro: >-
Shapez.io é um jogo relaxante no qual você deve construir fábricas para
produzir formas geométricas automaticamente.
produzir formas geométricas automaticamente.
Conforme os níveis aumentam, as formas se tornam mais complexas, e você terá que explorar o mapa infinito.
@ -25,7 +25,7 @@ steamPage:
- Me ajuda! ❤️
title_future: Conteúdo Planejado
planned:
- Biblioteca de esquemas (Exclusivo para a versão completa)
- Biblioteca de projetos (Exclusivo para a versão completa)
- Conquistas da Steam
- Modo Puzzle
- Minimapa
@ -94,7 +94,7 @@ mainMenu:
completa ou baixe o Chrome para obter uma experiência completa.
savegameLevel: Nível <x>
savegameLevelUnknown: Nível desconhecido
savegameUnnamed: Unnamed
savegameUnnamed: Sem nome
dialogs:
buttons:
ok: OK
@ -119,9 +119,9 @@ dialogs:
text: "Houve uma falha ao carregar seu jogo salvo:"
confirmSavegameDelete:
title: Confirmar exclusão
text: Are you sure you want to delete the following game?<br><br>
'<savegameName>' at level <savegameLevel><br><br> This can not be
undone!
text: Tem certeza que deseja deletar o jogo a seguir?<br><br>
'<savegameName>' no nível <savegameLevel><br><br> Isso não
pode ser revertido!
savegameDeletionError:
title: Falha ao deletar
text: "Houve uma falha ao deletar seu jogo salvo:"
@ -135,14 +135,14 @@ dialogs:
title: Resetar controles
desc: Essa opção deixa os controles nas definições padrão.
keybindingsResetOk:
title: Resetar controles
title: Controles resetados
desc: Os controles foram resetados para as definições padrão.
featureRestriction:
title: Versão Demo
desc: Você tentou acessar um recurso (<feature>) que não está disponível na
demo. Considere obter a versão completa para a proceder!
oneSavegameLimit:
title: Jogo salvo limitado
title: Limite de jogos salvos
desc: Você pode ter apenas um jogo salvo por vez na versão demo. Remova o
existente ou obtenha a versão completa!
updateSummary:
@ -164,7 +164,7 @@ dialogs:
continuar?
massCutInsufficientConfirm:
title: Confirmar Corte?
desc: You can not afford to paste this area! Are you sure you want to cut it?
desc: Você não conseguirá colar essa área! Tem certeza que quer cortá-la??
blueprintsNotUnlocked:
title: Não desbloqueado ainda
desc: Os projetos ainda não foram desbloqueados! Complete mais níveis para
@ -181,8 +181,8 @@ dialogs:
createMarker:
title: Nova Marcação
titleEdit: Editar Marcador
desc: Give it a meaningful name, you can also include a <strong>short
key</strong> of a shape (Which you can generate <link>here</link>)
desc: Dê um nome significativo, você também pode incluir um <strong>código
</strong> de uma forma (Você pode gerá-lo <link>aqui</link>)
markerDemoLimit:
desc: Você só pode criar dois marcadores na versão demo. Adquira a versão
completa para marcadores ilimitados!
@ -404,7 +404,7 @@ buildings:
description: Permite transportar energia.
second:
name: Fio
description: Transfere sinais, que podem ser de itens, cores or binários (1 /
description: Transfere sinais, que podem ser de itens, cores ou binários (1 /
0). Fios com cores diferentes não se conectam.
miner:
default:
@ -466,9 +466,9 @@ buildings:
description: Colore as formas na entrada esquerda com a cor da entrada superior.
quad:
name: Pintor (Quádruplo)
description: Allows you to color each quadrant of the shape individually. Only
slots with a <strong>truthy signal</strong> on the wires layer
will be painted!
description: Permite que você pinte cada quadrante da forma individualmente. Apenas
entradas com um <strong>sinal verdadeiro</strong> no plano de fios
serão pintadas!
trash:
default:
name: Lixo
@ -514,31 +514,31 @@ buildings:
default:
name: Portão E (AND)
description: Emite um sinal binário "1" se ambas as entradas forem verdadeiras.
(Ser verdadeira significa receber um sinal de forma, cor or
(Ser verdadeira significa receber um sinal de forma, cor ou
binário "1")
not:
name: Portão NEGAR (NOT)
description: Emite um sinal binário "1" se a entrada for falsa. (Ser verdadeira
significa receber um sinal de forma, cor or binário "1")
significa receber um sinal de forma, cor ou binário "1")
xor:
name: Portão OU EXCLUSIVO (XOR)
description: Emite um sinal binário "1" se uma das entradas for verdadeira, mas
não duas. (Ser verdadeira significa receber um sinal de forma,
cor or binário "1")
cor ou binário "1")
or:
name: Portão OU (OR)
description: Emite um sinal binário "1" se uma das entradas for verdadeira. (Ser
verdadeira significa receber um sinal de forma, cor or binário
verdadeira significa receber um sinal de forma, cor ou binário
"1")
transistor:
default:
name: Transistor
description: Envia o sinal adiante se a entrada for verdadeira. (Ser verdadeira
significa receber um sinal de forma, cor or binário "1")
significa receber um sinal de forma, cor ou binário "1")
mirrored:
name: Transistor
description: Envia o sinal adiante se a entrada for verdadeira. (Ser verdadeira
significa receber um sinal de forma, cor or binário "1")
significa receber um sinal de forma, cor ou binário "1")
filter:
default:
name: Filtro
@ -593,12 +593,12 @@ buildings:
storyRewards:
reward_cutter_and_trash:
title: Cortando formas
desc: You just unlocked the <strong>cutter</strong>, which cuts shapes in half
from top to bottom <strong>regardless of its
orientation</strong>!<br><br>Be sure to get rid of the waste, or
otherwise <strong>it will clog and stall</strong> - For this purpose
I have given you the <strong>trash</strong>, which destroys
everything you put into it!
desc: Você acabou de desbloquear o <strong>cortador</strong>, que corta formas pela metade
de cima para baixo <strong>independente de sua
orientação</strong>!<br><br>Lembre-se de se livrar do lixo, caso
contrário, <strong>a máquina irá entupir</strong> - Por isso
eu te dei o <strong>lixo</strong>, que destrói
tudo que você coloca nele!
reward_rotater:
title: Rotação
desc: O <strong>rotacionador</strong> foi desbloqueado! Gira as formas no
@ -620,10 +620,10 @@ storyRewards:
da outra, serão <strong>fundidas</strong>. Caso contrário, a entrada
direita é <strong>empilhada em cima</strong> da entrada esquerda!
reward_splitter:
title: Distribuidor
desc: You have unlocked a <strong>splitter</strong> variant of the
<strong>balancer</strong> - It accepts one input and splits them
into two!
title: Divisor
desc: Você desbloqueou uma variante <strong>divisora</strong> do
<strong>balanceador</strong> - Ela aceita uma entrada e a divide
em duas saídas!
reward_tunnel:
title: Túnel
desc: O <strong>túnel</strong> foi desbloqueado - Agora você pode transportar
@ -662,13 +662,13 @@ storyRewards:
output, so you can also use it as an <strong>overflow gate</strong>!
reward_freeplay:
title: Modo Livre
desc: You did it! You unlocked the <strong>free-play mode</strong>! This means
that shapes are now <strong>randomly</strong> generated!<br><br>
Since the hub will require a <strong>throughput</strong> from now
on, I highly recommend to build a machine which automatically
delivers the requested shape!<br><br> The HUB outputs the requested
shape on the wires layer, so all you have to do is to analyze it and
automatically configure your factory based on that.
desc: Você conseguiu! Você desbloqueou o <strong>modo livre</strong>! Isso significa
que formas agora são geradas <strong>aleatóriamente</strong>!<br><br>
Já que o HUB vai precisar de uma entrada <strong>constante</strong> a partir de
agora, eu altamente recomendo que você construa uma máquina que entregue
automaticamente as formas pedidas!<br><br> O HUB emite a forma pedida
no plano dos fios, então tudo que você precisa fazer é analizá-la e
automaticamente configurar sua fábrica baseado nessa análise.
reward_blueprints:
title: Projetos
desc: Agora você pode <strong>copiar e colar</strong> partes de sua fábrica!
@ -706,10 +706,10 @@ storyRewards:
Ele permite que você rotacione uma forma em 180 graus (Surpresa! :D)
reward_display:
title: Display
desc: "You have unlocked the <strong>Display</strong> - Connect a signal on the
wires layer to visualize it!<br><br> PS: Did you notice the belt
reader and storage output their last read item? Try showing it on a
display!"
desc: "Você desbloqueou o <strong>Display</strong> - Conecte um sinal no
plano de fios para poder vê-lo!<br><br> PS: Você percebeu que ambos o leitor
de esteiras e o armazenamento emitem o último item lido? Tente mostrar
isso em um display!"
reward_constant_signal:
title: Sinal Constante
desc: Você desbloqueou a construção que emite um <strong>sinal
@ -1082,4 +1082,3 @@ tips:
- Pressione F4 para mostrar seu FPS e taxa de tiques.
- Pressione F4 duas vezes para mostrar o ladrilho do seu mouse e da câmera.
- Você pode clicar em uma forma fixada na esquerda para tirá-la de lá.
- null

View File

@ -4,47 +4,49 @@ steamPage:
complexas num mapa infinito.
discordLinkShort: Discord Oficial
intro: >-
Shapez.io é um jogo relaxante onde tens que construir fábricas para a produção automatizada de formas geométricas.
Shapez.io é um jogo relaxante onde tens de construir fábricas para
autumatizar a produção de formas geométricas.
Enquanto o nível aumenta, as formas ficam cada vez mais e mais complexas, e tens de te expandir por um mapa infinito.
Com o aumento do nível, as formas começam a ser cada vez mais e mais complexas, e tu terás de te expandir num mapa infinito.
E como se isso não fosse suficiente, também tens de produzir cada vez mais para satisfazer a demanda - a única coisa que ajuda é aumentar!
E como se isso não fosse suficiente, tu também terás de produzir de forma exponencial para satisfazeres as tuas necessidades - a única coisa que ajuda é aumentar!
Enquanto só podes processar formas no inicio, vais ter de as colorir mais tarde - para isto vais ter de extrair e juntar cores!
Embora no inicio apenas tenhas de processar formas, mais tarde, vais ter de as colorir - para isto terás de extrair e misturar cores!
Comprar o jogo na Steam dá-te acesso à versão completa, mas também podes jogar a demo em shapez.io primeiro e decidir depois!
title_advantages: Vantagens Standalone
Comprar o jogo na Steam dar-te-á acesso à versão completa, mas também podes jogar a versão demo em shapez.io primeiro e decidir mais tarde!
title_advantages: Vantagens da versão completa
advantages:
- <b>12 Novos Níveis</b> para um total de 26 níveis
- <b>18 Novos Edifícios</b> para uma fábrica totalmente automatizada!
- <b>20 Níveis de Upgrade</b> para muitas horas de diversão!
- <b>Atualização de Fios</b> para uma completamente nova dimensão!
- <b>Modo Escuro</b>!
- Savegames Ilimitados
- Marcos Ilimitados
- Suporta-me! ❤️
- <b>12 Novos Níveis</b> para um total de 26 Níves
- <b>18 Novas Construções</b> para uma fábrica totalmente automatizada!
- <b>20 Níveis de Melhoria</b> para muitas horas de diversão!
- <b>Atualização de Fios</b> para uma dimensão totalmente nova!
- <b>Modo escuro</b>!
- Savegames ilimitados
- Marcos ilimitados
- Ajuda-me! ❤️
title_future: Conteúdo Planeado
planned:
- Blueprint Library (Exclusivo Standalone)
- Steam Achievements
- Biblioteca Blueprint (Exclusivo na versão Completa)
- Conquistas na Steam
- Modo Puzzle
- Minimap
- Mods
- Minimapa
- Modos
- Modo Sandbox
- ... e muito mais!
title_open_source: Este jogo é código aberto!
- ... e Muito Mais!
title_open_source: Este jogo é open source (código aberto)!
title_links: Links
links:
discord: Discord Oficial
roadmap: Roadmap
roadmap: Roteiro de desenvolvimento
subreddit: Subreddit
source_code: Source code (GitHub)
translate: Ajuda a traduzir
source_code: Código Fonte (GitHub)
translate: Ajuda a Traduzir
text_open_source: >-
Qualquer pessoa pode contribuir, estou ativamente envolvido na comunidade e
tento rever todas as sugestões e levo o feedback em consideração sempre que possível.
Qualquer um pode contribuir, estou ativamente envolvido na comunidade e
tento ver todas as sugestões e ter em consideração o feedback recebido
assim que possível.
Verifique o meu trello board para o roadmap completo!
Segue o meu trello board para veres todo o roteiro de desenvolvimento!
global:
loading: A Carregar
@ -97,7 +99,6 @@ mainMenu:
madeBy: Criado por <author-link>
subreddit: Reddit
savegameUnnamed: Sem Nome
dialogs:
buttons:
ok: OK
@ -122,8 +123,9 @@ dialogs:
text: "Erro ao carregar o teu savegame:"
confirmSavegameDelete:
title: Confirmar eliminação
text: Tens que queres apagar o seguinte jogo?<br><br>
'<savegameName>' no Nível <savegameLevel><br><br> Isto não pode
text: >-
Tens a certeza que queres apagar o seguinte jogo?<br><br>
'<savegameName>' no nível <savegameLevel><br><br> Isto não pode ser
desfeito!
savegameDeletionError:
title: Erro de eliminação
@ -179,8 +181,8 @@ dialogs:
class='keybinding'>ALT</code>: Inverte as posições.<br>"
createMarker:
title: Novo Marco
desc: Dá-lhe um nome com significado, também poderás adicionar <strong>um
pequeno código</strong> de uma forma (Que podes gerar aqui <link>here</link>)
desc: Dá-lhe um nome com significado, também poderás adicionar um <strong>pequeno
código</strong> de uma forma. (Pode ser gerado <link>aqui</link>)
titleEdit: Editar Marco
markerDemoLimit:
desc: Apenas podes criar dois marcos na versão Demo. Adquire o jogo completo
@ -199,17 +201,18 @@ dialogs:
desc: Não consegues pagar para colar esta área! Tens a certeza que pretendes
cortá-la?
editSignal:
title: Definir Sinal
title: Define o Sinal
descItems: "Escolhe um item pre-definido:"
descShortKey: ... ou entra o <strong>atalho</strong> duma forma (Que podes
gerar <link>aqui</link>)
descShortKey: ... ou insere o <strong>pequeno código</strong> de uma forma (Pode ser
gerado <link>aqui</link>)
renameSavegame:
title: Renomear Savegame
desc: Podes renomear o teu savegame aqui.
entityWarning:
title: Aviso de Desempenho
desc: Tu colocaste muitos edifícios, isto é apenas um lembrete amigável que o jogo não consegue aguentar com um número infinito de edifícios - Tenta meter as tuas fábricas compactas!
desc: Tu colocaste muitas contruções, isto é apenas um lembrete amigável de que
o que o jogo não aguenta com um número infinito de contruções - Sendo assim tenta
manter as tuas fábricas compactas!
ingame:
keybindingsOverlay:
moveMap: Mover
@ -295,7 +298,6 @@ ingame:
second: <shapes> / s
minute: <shapes> / m
hour: <shapes> / h
settingsMenu:
playtime: Tempo de jogo
buildingsPlaced: Construções
@ -345,41 +347,41 @@ ingame:
empty: Vazio
copyKey: Chave de cópia
connectedMiners:
one_miner: 1 Minerador
n_miners: <amount> Mineradores
limited_items: Limitado a <max_throughput>
one_miner: 1 Extrator
n_miners: <amount> Extratores
limited_items: Limite de <max_throughput>
watermark:
title: Versão Demo
desc: Clica aqui para ver as vantagens da versão Steam!
get_on_steam: Compra na steam
desc: Clica aqui para veres as vantagens da versão na Steam!
get_on_steam: Compra na Steam
standaloneAdvantages:
title: Obtém a versão completa!
no_thanks: Não, obrigado!
points:
levels:
title: 12 Novos Níveis
desc: Para um total de 26 níveis!
desc: Para um total de 26 Níveis!
buildings:
title: 18 Novos Edifícios
desc: Automatiza completamente a tua fábrica!
title: 18 Novas contruções
desc: Para uma fábrica totalmente automatizada!
savegames:
title: Savegames ∞
desc: Quantos o teu coração quiser!
desc: Tantos quanto o teu corção desejar!
upgrades:
title: 20 Níveis de Upgrades
desc: Esta versão demo só tem 5!
title: 20 Níveis de melhoria
desc: Nesta versão demo apenas tens 5!
markers:
title: Marcos ∞
desc: Nunca te percas na tua fábrica!
desc: Nunca te percas na tua Fábrica!
wires:
title: Fios
desc: Uma completamente nova dimensão!
desc: Uma dimensão totalmente nova!!
darkmode:
title: Modo Escuro
desc: Para de magoar os meus olhos!
desc: Não magoes os teus olhos!
support:
title: Suporta-me
desc: Eu desenvolvo o jogo no meu tempo livre!
title: Ajuda-me
desc: Eu desenvolvo este jogo no meu tempo livre!
shopUpgrades:
belt:
@ -398,7 +400,7 @@ buildings:
belt:
default:
name: Tapete Rolante
description: Transporta items. Mantém pressionado e arrasta para colocar vários.
description: Transporta itens. Mantém pressionado e arrasta para colocar vários.
miner:
default:
name: Extrator
@ -431,11 +433,12 @@ buildings:
description: Roda as formas 90º no sentido dos ponteiros do relógio.
ccw:
name: Rodar (CCW)
description: Roda as formas 90º no sentido contrário ao dos ponteiros do relógio.
description: Roda as formas 90º no sentido contrário ao dos ponteiros do
relógio.
rotate180:
name: Rodar (180)
description: Roda as formas 180º.
name: Rodar (180º)
description: Roda as formas 180º.
stacker:
default:
name: Empilhador
@ -456,7 +459,9 @@ buildings:
entrada superior.
quad:
name: Pintor (Quádruplo)
description: Pinta cada quadrante da forma geométrica com uma cor diferente. Apenas slots com um <strong>sinal verdadeiro</strong> na camada de fios vão ser pintados!
description: Permite colorir cada quadrante da forma individualmente. Apenas
entradas com um <strong>sinal verdadeira</strong> na camada de fios
irá ser pintada!
mirrored:
name: Pintor
description: Pinta a forma geométrica da entrada esquerda com a cor da entrada
@ -465,28 +470,30 @@ buildings:
default:
name: Lixo
description: Aceita entradas de todos os lados e destrói-os. Para sempre.
hub:
deliver: Entrega
toUnlock: para desbloquear
levelShortcut: NVL
endOfDemo: Fim do Demo
wire:
default:
name: Fio Elétrico
description: Transfere sinais, que podem ser itens, cores ou boleanos (1 / 0). Fios com cores diferentes não conectam.
description: Tranfere sinais, que podem ser itens, cores ou um sinal binário (1 ou 0).
Fios de cores diferestes não se conectam.
second:
name: Fio Elétrico
description: Transfere sinais, que podem ser itens, cores ou boleanos (1 / 0). Fios com cores diferentes não conectam.
description: Tranfere sinais, que podem ser itens, cores ou um sinal binário (1 ou 0).
Fios de cores diferestes não se conectam.
balancer:
default:
name: Balanceador
description: Multifuncional - Distribui uniformemente todas as entradas para todas as saídas.
name: Distribuidor
description: Multifunções - Distribui igualmente todas as entradas por todas as saídas.
merger:
name: Junção (compacto)
description: Junta um tapete rolante em dois.
name: Misturador (comp.)
description: Junta dois tapetes rolantes num só.
merger-inverse:
name: Junção (compacto)
description: Junta um tapete rolante em dois.
name: Misturador (comp.)
description: Junta dois tapetes rolantes num só.
splitter:
name: Divisor (compacto)
description: Divide um tapete rolante em dois.
@ -494,99 +501,115 @@ buildings:
name: Divisor (compacto)
description: Divide um tapete rolante em dois.
storage:
default:
name: Armazém
description: >-
Guarda itens em excesso, até uma quantidade determinada. Prioritiza a entrada esquerda
e pode ser usada como um portão de transbordar.
default:
name: Armazém
description: Armazena itens em excesso, até uma determinada capacidade. Dá prioridade à saída da
esquerda e pode ser usado como uma porta de transbordo.
wire_tunnel:
default:
name: Túnel de Fio
description: Permite cruzar dois fios sem os conectar.
default:
name: Túnel de Fios
description: Permite que dois fios cruzem sem conectarem-se um ao outro.
constant_signal:
default:
name: Sinal constante
description: >-
Emite um sinal constante, que pode ser uma forma, cor ou um booleano (1 / 0).
default:
name: Sinal Constante
description: Emite um sinal constante , que pode ser uma forma, cor ou
um sinal binário (1 ou 0).
lever:
default:
name: Interruptor
description: >-
Pode ser alternado para emitir um sinal booleano (1 / 0) na camada de fios, que pode então ser usada
para controlar por exemplo um filtro de itens.
default:
name: Interruptor
description: Pode emitir alternadamente um sinal binário (1 ou 0) na camada de fios,
que pode posteriormente ser usado, por exemplo, num filtro de itens.
logic_gate:
default:
name: Portão AND
description: Emite um booleano "1" se ambas as entradas são verdadeiras. (Verdadeiro significa forma, cor ou booleano "1")
description: >-
Emite um sinal binário "1" se ambas as entradas forem verdadeiras. (Verdadeiro significa:
forma, cor ou sinal binário "1")
not:
name: Portão NOT
description: Emite um booleano "1" se a entrada não é verdadeira. (Verdadeiro significa forma, cor ou booleano "1")
description: >-
Emite um sinal binário "1" se a entrada não for verdadeira. (Verdadeiro significa:
forma, cor ou sinal binário "1")
xor:
name: Portão XOR
description: Emite um booleano "1" se uma das entradas é verdadeira, mas não as duas. (Verdadeiro significa forma, cor ou booleano "1")
description: >-
Emite um sinal binário "1" se uma das entradas for verdadeira, mas não as duas. (Verdadeiro significa:
forma, cor ou sinal binário "1")
or:
name: Portão OR
description: Emite um booleano "1" se uma entrada é verdadeira. (Verdadeiro significa forma, cor ou booleano "1")
description: >-
Emite um sinal binário "1" se uma entrada é verdadeira. (Verdadeiro significa:
forma, cor ou sinal binário "1")
transistor:
default:
name: Transistor
description: Encaminha a entrada inferior se a entrada lateral for verdade (uma forma, cor ou "1").
name: Transístor
description: Encaminha a entrada inferior se a entrada lateral for verdade
(uma forma, cor ou "1").
mirrored:
name: Transistor
description: Encaminha a entrada inferior se a entrada lateral for verdade (uma forma, cor ou "1").
name: Transístor
description: Encaminha a entrada inferior se a entrada lateral for verdade
(uma forma, cor ou "1").
filter:
default:
name: Filtro
description: Conecta um sinal para encaminhar todos os itens correspondentes para o topo e o resto
para a direita. Pode ser controlado com sinais booleanos também.
name: Filtro de Itens
description: Conecta um sinal que irá encaminhar todos os itens correspondentes
para o topo e os restantes para a direita. Também pode ser
controlado com sinais binários.
display:
default:
name: Display
description: Conecta um sinal para mostrar no display - Pode ser uma forma, cor ou
booleano.
default:
name: Visor
description: Conecta um sinal para mostrar no Visor - Pode ser uma forma, cor
ou um sinal binário.
reader:
default:
name: Leitor de Tapete
description: Permite medir o rendimento do tapete. Produz o último item lido na camada de
fios (quando desbloqueada).
default:
name: Leitor de Tapete
description:
Permite medir a passagem média de itens no tapete. Fornece o último item lido na camada de
fios (quando desbloqueada).
analyzer:
default:
name: Analizador de Forma
description: Analiza o quadrante do topo direito da camada mais baixa da forma e produz
a forma ou cor.
default:
name: Analizador de Forma
description: Analiza o quadrante superior direito da camada mais baixa da forma e retorna
a forma ou cor.
comparator:
default:
name: Comparar
description: Produz o booleano "1" se ambos os itens são exatamente iguais. Pode comparar formas,
itens e booleanos.
default:
name: Comparador
description:
Produz o sinal binário "1" se ambos os itens são exatamente iguais. Pode comparar formas,
itens e sinais binários.
virtual_processor:
default:
name: Cortador Virtual
description: Computa
description: Virtualmente, corta as formas em duas metades.
rotater:
name: Rodar Virtual
description: Roda virtualmente as formas 90º no sentido dos ponteiros do relógio.
name: Rodador Virtual
description: Virtualmente, roda a forma tanto no sentido horário quanto no anti-horário.
unstacker:
name: Desempilhador Virtual
description: Produz a camada no topo para a direita, e o resto para esquerda.
description: Virtualmente, remove a camada do topo para a saída da direita
e o restante para a da esquerda.
stacker:
name: Empilhador Virtual
description: Empilha virtualmente o item da direita em cima do item da esquerda.
description: Virtualmente empilhada a forma da direita em cima do item da esquerda.
painter:
name: Pintor Virtual
description: Pinta virtualmente a forma de baixo com a forma da direita.
description: Virtualmente, pinta a forma a forma da entrada de baixo
com o item da entrada da direita.
item_producer:
default:
name: Produtor de Itens
description: Disponível apenas no modo sandbox, produz o sinal dado da camada de fios na camala normal.
description: Disponível apenas no modo sandbox, produz o sinal dado na
camada de fios na camada normal.
storyRewards:
reward_cutter_and_trash:
title: Corte de formas
desc: Acabaste de desbloquear o <strong>Cortador</strong>, que corta as formas
geométricas ao meio de cima para baixo <strong>independentemente da orientação</strong>!<br><br>Certifica-te de que te
livras do desperdício, caso contrário <strong>encravará</strong> -
Por isso, dou-te um lixo, que destruirá tudo o que lá colocares!
desc: Acabaste de desbloquear o <strong>cortador</strong>, que corta as formas ao meio
de cima para baixo <strong>independentemente da sua
orientação</strong>!<br><br>Certefica-te de que te livras do desperdício,
caso contrário <strong>irá encravar e parar</strong> - Para este propósito
eu dei-te um <strong>lixo</strong>, que destrói
tudo o que lá colocares!
reward_rotater:
title: Rotação
desc: O <strong>Rodador</strong> foi desbloqueado! Ele roda as formas
@ -610,8 +633,9 @@ storyRewards:
esquerda!
reward_splitter:
title: Divisor
desc: Desbloqueaste o <strong>divisor</strong>, variante do
<strong>balanceador</strong> - Aceita uma entrada e divide-a em duas!
desc: Desbloqueaste o <strong>dvisor</strong> uma variante do
<strong>distribuidor</strong> - Aceita uma entradae divide-a
em duas!
reward_tunnel:
title: Túnel
desc: O <strong>Túnel</strong> foi desbloqueado - Com ele podes passar itens
@ -624,94 +648,120 @@ storyRewards:
variantes</strong>!
reward_miner_chainable:
title: Extração em série
desc: >-
Desbloqueaste o <strong>Extrator em série</strong>! Permite <strong>enviar
o recurso extraído</strong> para outros extratores, permitindo uma
extração mais eficiente!<br><br> PS: O velho extrator já foi trocado na tua toolbar!
desc: "Desbloqueaste o <strong>extrator em séire</strong>! Permite
<strong>enviar os recursos</strong> para outros extratores, sendo assim
permite uma extração de recursos mais eficiente!<br><br> PS: O extrator
antigo já foi trocado na tua lista de construções!"
reward_underground_belt_tier_2:
title: Túnel Nível II
desc: Desbloqueaste uma nova variante do <strong>Túnel</strong> - Tem um <strong>maior alcance</strong>, e podes interlaçar as duas variantes entre si!
desc: Desbloqueaste uma nova variante do <strong>Túnel</strong> - Tem um
<strong>maior alcance</strong>, e podes interlaçar as duas variantes
entre si!
reward_cutter_quad:
title: Corte quádruplo
desc: Desbloqueaste a variante do <strong>Cortador</strong> - Permite cortar formas geométricas em <strong>quatro partes</strong> em vez de apenas duas!
desc: Desbloqueaste a variante do <strong>Cortador</strong> - Permite cortar
formas geométricas em <strong>quatro partes</strong> em vez de
apenas duas!
reward_painter_double:
title: Pintura dupla
desc: Desbloqueaste uma variante do <strong>Pintor</strong> - Funciona como um pintor normal mas processa <strong>duas formas ao mesmo tempo</strong> consumindo apenas uma cor em vez de duas!
desc: Desbloqueaste uma variante do <strong>Pintor</strong> - Funciona como um
pintor normal mas processa <strong>duas formas ao mesmo
tempo</strong> consumindo apenas uma cor em vez de duas!
reward_storage:
title: Armazém
desc: Desbloqueaste uma variante do <strong>Lixo</strong> - Permite armazenar items até uma determinada capacidade!<br><br> Prioritiza a saída esquerda, por isso também o podes usar como um portão de transbordar.
desc: Desbloqueaste uma variante do <strong>lixo</strong> - Permite armazenar
itens, até uma determinada capacidade!<br><br> Dá prioridade à saída da
esquerda e pode ser usado como uma <strong>porta de transbordo</strong>!
reward_freeplay:
title: Jogo livre
desc: Conseguiste! Desbloqueaste o <strong>modo jogo livre</strong>! Isto significa que agora as formas são geradas <strong>aleatoriamente</strong>!<br><br>
Como o edifício central vai precisar de uma <strong>taxa de transferência</strong> a partir de
agora, eu recomendo contruires uma máquina que automaticamente
entrega a forma pedida!<br><br> O edifício central produz a forma pedida na camada de fios,
então tudo o que tens de fazer é analizar-la e automaticamente configurar a tua fábrica à volta disso.
desc: Conseguiste! Desbloqueaste o <strong>modo jogo livre</strong>! Isto
significa que agora as formas são geradas <strong>aleatoriamente</strong>!<br><br>
Como o edifício central vai precisar de uma <strong>taxa de rendimento</strong> a partir
de agora, recomendo vivamente a contruires uma máquina que, automaticamente,
entraga as formas pedidas!<br><br> O edifício central emite a forma pedida
na camada de fios,sendo assim tudo o que tens a fazer é analiza-la e
automaticamente configurares a tua fábrica baseada nisso.
reward_blueprints:
title: Projetos
desc: Agora podes <strong>copiar e colar</strong> partes da tua fábrica! Seleciona uma área (Mantém pressionado CTRL e arrasta com o rato), e pressiona 'C' para copiar.<br><br>Colar não é <strong>gratuito</strong>, precisas de produzir <strong>formas projeto</strong> para o pagares! (Aquelas que acabaste de entregar).
desc: Agora podes <strong>copiar e colar</strong> partes da tua fábrica!
Seleciona uma área (Mantém pressionado CTRL e arrasta com o rato), e
pressiona 'C' para copiar.<br><br>Colar não é
<strong>gratuito</strong>, precisas de produzir <strong>formas
projeto</strong> para o pagares! (Aquelas que acabaste de entregar).
no_reward:
title: Próximo nível
desc: >-
Este nível não te deu nenhuma recompensa, mas o próximo dará! <br><br> PS: É melhor não destruires a tua fábrica atual - Precisarás de <strong>todas</strong> essas formas no futuro para <strong>desbloquear upgrades</strong>!
desc: "Este nível não te deu nenhuma recompensa, mas o próximo dará! <br><br>
PS: É melhor não destruires a tua fábrica atual - Precisarás de
<strong>todas</strong> essas formas no futuro para
<strong>desbloquear upgrades</strong>!"
no_reward_freeplay:
title: Próximo nível
desc: Parabéns! Já agora, está planeado mais conteúdo para o jogo completo!
reward_balancer:
title: Balanceador
desc: O multifunctional <strong>balanceador</strong> foi desbloqueado - Pode ser usado
para construir fábricas maiores <strong>dividindo e juntando itens</strong>
por vários tapetes!<br><br>
title: Distribuidor
desc: O multifunctional <strong>distribuidor</strong> foi desbloqueado - Pode
ser usado para construbir fábricas maiores <strong>dividindo e juntando
itens</strong> por vários tapetes rolantes!<br><br>
reward_merger:
title: Junção (Compacto)
desc: Destravaste a <strong>junção</strong> variante do
<strong>balanceador</strong> - Aceita duas entradas e junta-as num só tapete!
title: Misturador (compacto)
desc: Desbloqueaste um <strong>misturador</strong>, uma variante do
<strong>distribuidor</strong> - Aceita duas entradas e junta-as num só
tapete rolante!
reward_belt_reader:
title: Leitor de Tapete
desc: Tu desbloqueaste o <strong>leitor de tapete</strong>! Permite-te medir
o rendimento dum tapete.<br><br>E espera até desbloqueares fios - aí é que é super útil!
title: Leitor de Tapete
desc: Desbloqueaste o <strong>leitor de tapete</strong>! Permite-te medires
a passagem média de itens no tapete.<br><br>E espera por desbloqueares
os fios - aí é que vão ser bastante úteis!
reward_rotater_180:
title: Rodar (180 degrees)
desc: Acabaste de desbloquear a versão de 180º do <strong>Rotador</strong>! - Deixa-te rodar formas por 180º (Surpresa! :D)
title: Rodar (180º)
desc: Desbloqueaste o <strong>rodador</strong> de 180 graus! - Permite-te
rodares formas 180 graus (Surpresa! :D)
reward_display:
title: Display
desc: >-
Destravaste o <strong>Display</strong> - Conecta um sinal elétrico na camada de fios para visualizar-lo!<br><br> PS:
Reparaste que o leitor de tapetes e o armazém produz o último item lido por eles na camada de fios? Tenta mostrar isso num display!
title: Visor
desc: "Desbloqueaste o <strong>Visor</strong> - Conecta um sinal na
camada de fios para o visualizares!<br><br> PS: Reparaste que o leitor
de tapete e o armazém emitem o último item lido por eles? tenta mostrar isso
num visor!"
reward_constant_signal:
title: Sinal Constante
desc: Acabaste de destravar o edifício <strong>sinal constante</strong> na camada de fios!
Isto é útil conectado a um <strong>filtro de itens</strong> por exemplo.<br><br>
O sinal constante pode emitir uma <strong>forma</strong>,
<strong>cor</strong> ou um <strong>booleano</strong> (1 / 0).
title: Sinal Constante
desc: Desbloqueaste o <strong>sinal constante</strong> contruido na camada
de fios! Isto é útil conectado com um <strong>filtro de itens</strong>
por exemplo.<br><br> O sinal constante pode emitir uma
<strong>forma</strong>, <strong>cor</strong> ou
<strong>sinal binário</strong> (1 ou 0).
reward_logic_gates:
title: Portões Lógicos
desc: "Tu desbloqueaste os <strong>portões lógicos</strong>! Não tens de estar excitado sobre isto,
mas é na verdade super fixe!<br><br> Com estes portões agora podes fazer operações booleanas
AND, OR, XOR e NOT!"
desc: Desbloqueaste os <strong>portões lógicos</strong>! N tens de te excitar
com isto, mas é realmente super fixe!<br><br> Com estes portões
agora podes realizar operações AND, OR, XOR and NOT.<br><br> Como um
bónus anteriormente já de dei um <strong>transístor</strong>!
reward_virtual_processing:
title: Processamento Virtual
desc: >-
Acabei de te dar um monte de novos edifícios que permitem-te
<strong>simular o processamento de formas</strong>!<br><br> Podes agora
simular um cortador, rodar, empilhador e mais na camada de fios!<br><br>
Com isto tens agora três opções para continuar o jogo:<br><br> - Construir
uma <strong>máquina automatizada</strong> para criar qualquer forma requerida
pelo edifício central (Isto é fixe, eu prometo!).<br><br> - Construir algo fixe com
fios.<br><br> - Continuar a jogar regularmente. Seja lá o que escolheres, lembra-te de te divertires!
desc: Acadei de te dar um monte de novas construções, que te vão permitir
<strong>simular o processamento de formas</strong>!<br><br> Agora podes
simular um cortador,um rodador, um empilhador e muito mais na camada de fios!
Com isto, agora tens três opções para continuares o jogo:<br><br> -
Construir uma <strong>máquina automática</strong> para criar qualquer forma
possível pedida pelo Edifício Central (Reconmento-te a experimentares!).<br><br> - Contruir
algo fixe com os fios.<br><br> - Continuar a jogar
regularmente.<br><br> Independentemente da tua escolha, lembra-te de te divertires!
reward_wires_painter_and_levers:
title: Fios e Pintor Quádruplo
desc: "Acabaste de desbloquear a <strong>Camada de Fios</strong>: É uma camada separada
no topo da camada normal e introduz um monte de novas mecânicas!<br><br>
Para o início eu dei-te o <strong>Pintor Quádruplo</strong> - Conecta os slots que queres pintar na
camada de fios!<br><br> Para trocar para a camada de fios, pressiona <strong>E</strong>."
title: Fios & Pintor Quádruplo
desc: "Desbloquaste a <strong>Camada de Fios</strong>: É uma camada separada
no topo da camada normal e introduz um monte de novas
mecânicas!<br><br> Para o inicio eu dei-te o <strong>Pintor
Quádruplo</strong> - Conecta as entradasque queres pintar na
camada de fios!<br><br> Para trocares para a camada de fios, pressiona a tecla
<strong>E</strong>."
reward_filter:
title: Filtro de Itens
desc: Desbloqueaste o <strong>Filtro de Itens</strong>! Vai mandar items para a saída de topo ou para a saída da direita
dependendo se são iguais ao sinal da camada de fios.<br><br> Também podes passar um sinal booleano (1 / 0) para ativar-lo ou desativar-lo completamente.
desc: Desbloquaste o <strong>Filtro de Itens</strong>! Vai mandar itens ou
para o topo ou para a saída da esquerda dependendo depending se são iguais ao
sinal da camada de fios ou não.<br><br> Também podes passar um
sinal binário (1 ou 0) para ativa-lo ou desativa-lo totalmente.
reward_demo_end:
title: Fim da Demo
desc: Chegaste ao fim da versão demo!
desc: Tu chegaste ao fim da versão demo!
settings:
title: Definições
categories:
@ -727,8 +777,9 @@ settings:
labels:
uiScale:
title: Escala da interface
description: >-
Altera o tamanho da interface do utilizador. A interface será redimensionada com base na resolução do teu dispositivo, mas esta definição controla a escala.
description: Altera o tamanho da interface do utilizador. A interface será
redimensionada com base na resolução do teu dispositivo, mas
esta definição controla a escala.
scales:
super_small: Super pequeno
small: Pequeno
@ -737,8 +788,7 @@ settings:
huge: Enorme
scrollWheelSensitivity:
title: Sensibilidade do zoom
description: >-
Define o quão sensível é o zoom (Roda do rato ou trackpad).
description: Define o quão sensível é o zoom (Roda do rato ou trackpado).
sensitivity:
super_slow: Muito lento
slow: Lento
@ -747,39 +797,41 @@ settings:
super_fast: Muito rápido
language:
title: Língua
description: >-
Muda a língua. Todas as traduções são contribuições dos utilizadores e podem estar incompletas!
description: Muda a língua. Todas as traduções são contribuições dos
utilizadores e podem estar incompletas!
fullscreen:
title: Ecrã inteiro
description: >-
É recomendado jogar o jogo em ecrã inteiro para a melhor experiência. Apenas disponível no jogo completo.
description: É recomendado jogar o jogo em ecrã inteiro para a melhor
experiência. Apenas disponível no jogo completo.
soundsMuted:
title: Desativar sons
description: >-
Se ativado, desativa todos os sons.
description: Se ativado, desativa todos os sons.
musicMuted:
title: Desativar música
description: >-
Se ativado, desativa todas as músicas.
description: Se ativado, desativa todas as músicas.
theme:
title: Tema do jogo
description: >-
Escolhe o tema do jogo (claro / escuro).
description: Escolhe o tema do jogo (claro / escuro).
themes:
dark: Escuro
light: Claro
refreshRate:
title: Frequência
description: >-
Isto determina quantos game ticks ocorrem por segundo. No geral, uma frequência alta significa melhor precisão mas também pior desempenho. Em frequências baixas, o rendimento pode não ser exato.
description: Se tens um monitor 144hz, muda a frequência para que o jogo simule
corretamente frequências de autalização altas. Isto pode
resultar em perda de FPS se o teu computador for demasiado
lento.
alwaysMultiplace:
title: Colocação múltipla
description: >-
Se ativado, todas as construções permanecerão selecionadas após a colocação até cancelares. Isto é equivalente a pressionares SHIFT permanentemente.
description: Se ativado, todas as construções permanecerão selecionadas após a
colocação até cancelares. Isto é equivalente a pressionares
SHIFT permanentemente.
offerHints:
title: Dicas e tutoriais
description: >-
Se ativado, dá dicas e tutoriais de apoio ao jogo. Adicionalmente, esconde certos elementos da interface do utilizador até ao nível em que são desbloqueados de forma a simplificar o início do jogo.
description: Se ativado, dá dicas e tutoriais de apoio ao jogo. Adicionalmente,
esconde certos elementos da interface do utilizador até ao nível
em que são desbloqueados de forma a simplificar o início do
jogo.
movementSpeed:
title: Velocidade de movimentação
description: Define quão rápida é a movimentação usando o teclado.
@ -792,19 +844,17 @@ settings:
extremely_fast: Extremamente rápida
enableTunnelSmartplace:
title: Túneis inteligentes
description: >-
Quando ativado, a colocação de túneis removerá tapetes desnecessários automaticamente.
Isto também permite arrastar túneis e túneis em excesso serão removidos.
description: Quando ativado, a colocação de túneis removerá tapetes
desnecessários automaticamente. Isto também permite arrastar
túneis e túneis em excesso serão removidos.
vignette:
title: Vinheta
description: >-
Ativa a vinheta, que escurece os cantos do ecrã e torna a leitura do texto
mais fácil.
description: Ativa a vinheta, que escurece os cantos do ecrã e torna a leitura
do texto mais fácil.
autosaveInterval:
title: Intervalo de gravação automática
description: >-
Define o quão frequentemente o jogo grava automaticamente. Também podes desativar
aqui.
description: Define o quão frequentemente o jogo grava automaticamente. Também
podes desativar aqui.
intervals:
one_minute: 1 Minuto
two_minutes: 2 Minutos
@ -814,69 +864,68 @@ settings:
disabled: Desligado
compactBuildingInfo:
title: Informações de construções compactas
description: >-
Encurta caixas de informação e apenas mostra os respetivos rácios. Caso contrário
é mostrada a descrição e a imagem.
description: Encurta caixas de informação e apenas mostra os respetivos rácios.
Caso contrário é mostrada a descrição e a imagem.
disableCutDeleteWarnings:
title: Desativar Avisos de Corte/Eliminação
description: >-
Desativa os avisos mostrados quando é feito o corte ou a eliminação de mais de 100
entidades.
description: Desativa os avisos mostrados quando é feito o corte ou a eliminação
de mais de 100 entidades.
enableColorBlindHelper:
title: Modo Daltónico
description: Ativa várias ferramentas que te permitirão jogar o jogo se fores daltónico.
description: Ativa várias ferramentas que te permitirão jogar o jogo se fores
daltónico.
rotationByBuilding:
title: Rotação por tipo de construção
description: >-
Cada tipo de construção lembra-se da última rotação que definiste.
Esta definição pode ser mais confortável se alterares frequentemente
a colocação de diferentes tipos de construções.
description: Cada tipo construção lembra-se da última rotação que definiste.
Esta definição pode ser mais confortável se alterares
frequentemente a colocação de diferentes tipos de construções.
soundVolume:
title: Volume do Som
description: Define o volume para efeitos sonoros
title: Volume do Som
description: Define o volume para efeitos sonoros
musicVolume:
title: Volume da Música
description: Define o volume para música
title: Volume da Música
description: Define o volume para música
lowQualityMapResources:
title: Recursos de Mapa de Baixa Qualidade
description: >-
Simplifica a renderização de recursos quanto o mapa está ampliado para melhorar o desempenho. Até parece mais limpo, então lembra-te de experimentar!
title: Recursos de Mapa de Baixa Qualidade
description: Simplifica a renderização de recursos quando o mapa está ampliado para
melhorar o desempenho. Até parece mais limpo, então lembra-te de
experimentares!
disableTileGrid:
title: Desativar Grelha
description: >-
Desativar a grelha pode ajudar com o desempenho. Isto também faz o jogo estar mais limpo!
title: Desativar Grelha
description: Desativar a grelha pode ajudar com o desempenho. Isto também
fazz o jogo parecer mais limpo!
clearCursorOnDeleteWhilePlacing:
title: Limpar Cursor com Clique Direito
description: >-
Ativado por default, limpa o cursos sempre que clicas no botão direito do rato enquanto tens um edifício selecionado para colocamento.
Se desativado, podes apagar edifícios fazendo um clique direito enquanto colocas um edifício.
title: Limpar Cursor com Clique Direito
description: Ativado por padrão, limpa o cursor sempre que pressionas o botão direito do rato
enquanto tens um edifício para colocamento. Se desativado,
podes apagar construções pressionando o botão direito do rato enquanto colocas um
edifício.
lowQualityTextures:
title: Texturas de baixa qualidade (Feio)
description: >-
Usa texturas de baixa qualidade para melhorar o desempenho. Isto vai tornar o jogo muito feio!
title: Texturas de baixa qualidade (Feio)
description: sa texturas de baixa qualidade para melhorar o desempenho. sto vai tornar o
jogo parecer muito feio!
displayChunkBorders:
title: Mostrar bordas de Chunks
description: >-
O jogo está dividido em pedaços de 16x16 quadrados, se esta definição estiver ativada
as bordas de cada pedaço são mostradas.
title: Mostrar bordas de limites (chunk borders)
description: O jogo está dividido em partes de 16x16 quadrados, se esta dedinição estiver
ativada as bordas de cada limitece são mostradas.
pickMinerOnPatch:
title: Selecionar extrator num remendo de recursos
description: >-
Ativado por default, seleciona o extrator se usares a pipeta enquanto estás num remendo de recursos.
title: Selecionar extrator num quadrado de recurso
description: Ativado por padrão, seleciona um extrator se usares a pipeta quando
estiveres com o rato em cima de um quadrado de recurso.
simplifiedBelts:
title: Tapetes rolantes simplificados (Feio)
description: >-
Não renderiza itens em tapetes excepto quando tens o rato em cima do tapete para salvar desempenho.
Não recomendo jogares com esta definição a menos que absolutamente precisas do desempenho.
title: Tapetes rolantes simplificados (Feio)
description: Não renderiza os intens nos tapetes excepto quando tens o rato em cima do tapete
para melhorar o desempenho. Não recomendo a jogares com esta definição ativada
a não ser que precises mesmo de melhorar o desempenho.
enableMousePan:
title: Ativar Mouse Pan
description: >-
Permite-te mover o mapa apenas movendo o rato aos cantos do ecrã. A velocidade depende da definição Velocidade de movimentação.
title: Ativar rato panorâmico
description: Permite-te mover o mapa movento o rato nos cantos do
ecrâ. A velociade depende da definição de velocidade de movimentação.
rangeSliderPercentage: <amount> %
keybindings:
title: Atalhos
hint: >-
Tip: Utiliza o CTRL, o SHIFT e o ALT! Eles permitem diferentes opções de posicionamento.
hint: "Dica: Utiliza o CTRL, o SHIFT e o ALT! Eles permitem diferentes opções de
posicionamento."
resetKeybindings: Resetar Atalhos
categoryLabels:
general: Aplicação
@ -931,13 +980,12 @@ keybindings:
menuClose: Fechar Menu
switchLayers: Troca de camadas
wire: Fio Elétrico
balancer: Balanceador
balancer: Distribuidor
storage: Armazém
constant_signal: Sinal Constante
logic_gate: Portões Lógicos
lever: Interruptor (normal)
lever_wires: Interruptor (fios)
filter: Filtro
filter: Filtro de itens
wire_tunnel: Túnel de Fio
display: Display
reader: Leitor de Tapete
@ -972,13 +1020,13 @@ demo:
exportingBase: Exportar base como uma imagem
settingNotAvailable: Não disponível no Demo.
tips:
- 'O edifício central aceita qualquer entrada, não apenas a forma atual!'
- "O edifício central aceita qualquer entrada, não apenas a forma atual!"
- Tem a certeza que as tuas fábricas são modulares - vai valer a pena!
- 'Não construas demasiado perto do edifício, ou vai ser um grande caos!'
- 'Se empilhar não funciona, tenta trocar as entradas.'
- "Não construas demasiado perto do edifício, ou vai ser um grande caos!"
- "Se empilhar não funciona, tenta trocar as entradas."
- Podes alternar a direção do planeador de tapete rolante ao pressionar <b>R</b>.
- Ao segurar <b>CTRL</b> podes arrastar tapetes rolantes sem auto-orientação.
- 'Os rácios continuam os mesmos, desde que todos os upgrades estejam no mesmo Nível.'
- "Os rácios continuam os mesmos, desde que todos os upgrades estejam no mesmo Nível."
- Execução em série é mais eficiente que em paralelo.
- Vais desbloquear mais variações de edifícios mais tarde no jogo!
- Podes usar <b>T</b> para trocar entre as diferentes variantes.
@ -987,11 +1035,11 @@ tips:
- Tenta construir fábricas compactas - vai valer a pena!
- O pintor tem uma variante espelhada que podes selectionar com <b>T</b>
- Ter os rácios de edifícios corretos vai maximizar a eficiência.
- 'No nível máximo, 5 extratores vão encher um tapete.'
- "No nível máximo, 5 extratores vão encher um tapete."
- Não te esqueças dos túneis!
- Não tens de dividir os itens uniformemente para eficiência máxima.
- Segurar <b>SHIFT</b> vai ativar o planeador de tapetes, deixando-te colocar longas linhas de tapetes facilmente.
- 'Os cortadores cortam sempre verticalmente, independentemente da sua orientação.'
- "Os cortadores cortam sempre verticalmente, independentemente da sua orientação."
- Para obter branco junta as três cores.
- O buffer do armazém prioritiza a primeira saída.
- Investe tempo para costruir designs repetiveis - vale a pena!
@ -999,32 +1047,32 @@ tips:
- Podes segurar <b>ALT</b> para inverter a direção de tapetes colocados.
- Eficiência é a solução!
- As formas que estão mais longes do edifício central são mais complexas.
- 'As Máquinas têm uma velocidade limitada, divide-as para eficiência máxima.'
- "As Máquinas têm uma velocidade limitada, divide-as para eficiência máxima."
- Usa balanceadores para maximizar a tua eficiência.
- Organização é importante. Tenta não cruzar tapetes demasiado.
- 'Planeja antecipadamente, ou vai ser um grande caos!'
- "Planeja antecipadamente, ou vai ser um grande caos!"
- Não removas as tuas fábricas antigas! Vais precisar delas para desbloquear upgrades.
- Tenta superar o nível 18 sozinho sem procurar ajuda!
- 'Não complicas as coisas, tenta continuar simples e irás muito longe.'
- "Não complicas as coisas, tenta continuar simples e irás muito longe."
- Talvez precises de reusar fábricas mais tarde no jogo. Planeia as tuas fábricas para serem reutilizáveis.
- Às vezes, podes encontrar uma forma necessária no mapa sem criar-la com empilhadoras.
- Moinhos de vento e cataventos completos nunca aparecem naturalmente.
- Pinta as tuas formas antes de cortar-las para eficiência máxima.
- 'Com módulos, o espaço é apenas uma percepção; uma preocupação para pessoas mortais.'
- "Com módulos, o espaço é apenas uma percepção; uma preocupação para pessoas mortais."
- Faz uma fábrica de diagramas separada. São importantes para módulos.
- 'Dá uma olhada ao misturador de cores, e as tuas questões serão respondidas.'
- "Dá uma olhada ao misturador de cores, e as tuas questões serão respondidas."
- Use <b>CTRL</b> + Clique para selecionar uma área.
- Construir demasiado perto do edifício central pode ficar no caminho de projetos futuros.
- O ícone de alfinete perto duma forma na lista de upgrades vai afixar-la ao ecrã.
- Junta todas as cores primárias juntas para fazer branco!
- 'Tu tens um mapa infinito, não limites a tua fábrica, expande!'
- "Tu tens um mapa infinito, não limites a tua fábrica, expande!"
- Tenta também Factorio! É o meu jogo favorito.
- O cortador quádruplo corta no sentido dos ponteiros começando no canto superior direito!
- Podes fazer download dos teus savegames no menu principal!
- Este jogo tem muitos atalhos de teclado úteis! Não te esqueças de verificar a página de configurações.
- 'Este jogo tem muitas definições, não te esqueças de as verificar!'
- "Este jogo tem muitas definições, não te esqueças de as verificar!"
- O marco para o teu edifício central tem uma pequena bússola para indicar a sua direção!
- 'Para limpar tapetes, corta a área e cola-a na mesma localização.'
- "Para limpar tapetes, corta a área e cola-a na mesma localização."
- Pressiona F4 para mostrar os teus FPS e Tick Rate.
- Pressiona F4 duas vezes para mostrar a tile do teu rato e câmara.
- Podes clicar numa forma afixada no lado direito para desafixar-la.

View File

@ -5,7 +5,7 @@ steamPage:
discordLinkShort: Official Discord
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
automated production of geometric shapes.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.

View File

@ -1,51 +1,50 @@
steamPage:
shortText: shapez.io — это игра о строительстве фабрик для автоматизации
создания и объединения все более сложных фигур на бесконечной карте.
discordLinkShort: Official Discord
discordLinkShort: Официальный Discord сервер
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
Shapez.io - это спокойная игра о создании фабрик для автоматизации создания сложных геометрических фигур.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.
По мере управления уровня, фигуры становятся все сложнее, так что придется расширять фабрику засчет бесконечной карты.
And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling!
Если этого мало, то Вам так же придется экспоненциально производить все больше и больше фигур, чтобы удовлетворить потребности Вашей фабрики. Ключ к успеху - расширение!
While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors!
Вначале игры Вам понадобится производить только фигуры, однако позже фигуры надо будет окрашивать. Для этого добывайте и смешивайте краски!
Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later!
title_advantages: Standalone Advantages
Приобретение игры в Steam предоставляет доступ к полной версии игры, но вы можете опробовать демоверсию игры на shapez.io и принять решение позже!
title_advantages: Преимущества полной версии
advantages:
- <b>12 New Level</b> for a total of 26 levels
- <b>18 New Buildings</b> for a fully automated factory!
- <b>20 Upgrade Tiers</b> for many hours of fun!
- <b>Wires Update</b> for an entirely new dimension!
- <b>Dark Mode</b>!
- Unlimited Savegames
- Unlimited Markers
- Support me! ❤️
title_future: Planned Content
- <b>12 новых уровней</b> - всего 26 уровней!
- <b>18 новых построек</b> для полностью автоматизированной фабрики!
- <b>20 стадий улучшения</b> для долгих часов веселья!
- <b>Провода</b> - открывает полноценное новое измерение!
- <b>Темная тема</b>!
- Неограниченные сохранения
- Неограниченные маркеры
- Поддержите меня! ❤️
title_future: Запланированный контент
planned:
- Blueprint Library (Standalone Exclusive)
- Steam Achievements
- Puzzle Mode
- Minimap
- Mods
- Sandbox mode
- ... and a lot more!
title_open_source: This game is open source!
title_links: Links
- Библиотека чертежей (только для Полной версии)
- Достижения Steam
- Режим головоломок
- Мини-карта
- Моды
- Режим песочницы
- ... и многое другое!
title_open_source: Эта игра находится в открытом доступе!
title_links: Ссылки
links:
discord: Official Discord
roadmap: Roadmap
discord: Официальный Discord сервер
roadmap: Планы
subreddit: Subreddit
source_code: Source code (GitHub)
translate: Help translate
source_code: Исходный код (GitHub)
translate: Помочь с переводом
text_open_source: >-
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!
Не забудьте заглянуть на мой Trello board, чтобы ознакомиться с планами на будущее!
global:
loading: Загрузка
error: Ошибка
@ -78,7 +77,7 @@ global:
shift: SHIFT
space: ПРОБЕЛ
demoBanners:
title: Демо-версия
title: Демоверсия
intro: Приобретите полную версию, чтобы разблокировать все возможности!
mainMenu:
play: Играть
@ -121,9 +120,9 @@ dialogs:
text: Не удалось загрузить сохранение игры.
confirmSavegameDelete:
title: Подтвердите удаление.
text: Are you sure you want to delete the following game?<br><br>
'<savegameName>' at level <savegameLevel><br><br> This can not be
undone!
text: Вы уверены, что хотите удалить это сохранение?<br><br>
'<savegameName>' на уровне <savegameLevel><br><br> Это не может
быть отменено!
savegameDeletionError:
title: Ошибка удаления
text: Не удалось удалить сохранение игры.
@ -142,13 +141,13 @@ dialogs:
title: Сброс управления
desc: Настройки управления сброшены до соответствующих значений по умолчанию!
featureRestriction:
title: Демо-версия
title: Демоверсия
desc: Вы попытались получить доступ к функции (<feature>), которая недоступна в
демо-версии. Вы можете приобрести полную версию чтобы пользоваться
демоверсии. Вы можете приобрести полную версию чтобы пользоваться
всеми функциями!
oneSavegameLimit:
title: Лимит сохранений
desc: Вы можете иметь только одно сохранение игры в демо-версии. Пожалуйста,
desc: Вы можете иметь только одно сохранение игры в демоверсии. Пожалуйста,
удалите существующее или приобретите полную версию!
updateSummary:
title: Новое обновление!
@ -177,11 +176,11 @@ dialogs:
Инвертировать направление размещаемых конвейерных лент.<br>"
createMarker:
title: Новый маркер
desc: Give it a meaningful name, you can also include a <strong>short
key</strong> of a shape (Which you can generate <link>here</link>)
desc: Дайте ему значимое название, вы также можете добавить <strong>короткий
ключ</strong> фигуры (Который можно сгенерировать <link>здесь</link>)
titleEdit: Редактирование маркера
markerDemoLimit:
desc: Вы можете создать только 2 своих маркера в демо-версии. Приобретите полную
desc: Вы можете создать только 2 своих маркера в демоверсии. Приобретите полную
версию для безлимитных маркеров.
massCutConfirm:
title: Подтвердите вырезку
@ -197,18 +196,17 @@ dialogs:
desc: Вы не можете позволить себе вставить эту область! Вы уверены, что хотите
вырезать ее?
editSignal:
title: Set Signal
descItems: "Choose a pre-defined item:"
descShortKey: ... or enter the <strong>short key</strong> of a shape (Which you
can generate <link>here</link>)
title: Установить Сигнал
descItems: "Выберите объект:"
descShortKey: ... или введите <strong>короткий
ключ</strong> фигуры (Который можно сгенерировать <link>здесь</link>)
renameSavegame:
title: Rename Savegame
desc: You can rename your savegame here.
title: Переименовать Сохранение
desc: Здесь вы можете изменить название своего сохранения.
entityWarning:
title: Performance Warning
desc: You have placed a lot of buildings, this is just a friendly reminder that
the game can not handle an endless count of buildings - So try to
keep your factories compact!
title: Вы разместили очень много построек, это просто напоминание о том, что игра
не может справиться с бесконечным количеством построек. Так что
постарайтесь создавать компактные фабрики!
ingame:
keybindingsOverlay:
moveMap: Передвижение
@ -217,7 +215,7 @@ ingame:
rotateBuilding: Повернуть постройку
placeMultiple: Поставить несколько
reverseOrientation: Реверсировать направление
disableAutoOrientation: Отключить авто-определение направления
disableAutoOrientation: Отключить автоопределение направления
toggleHud: Переключить HUD
placeBuilding: Разместить постройку
createMarker: Создать маркер
@ -343,41 +341,41 @@ ingame:
empty: Пусто
copyKey: Копировать
connectedMiners:
one_miner: 1 Miner
n_miners: <amount> Miners
limited_items: Limited to <max_throughput>
one_miner: 1 Экстрактор
n_miners: <amount> Экстрактора(-ов)
limited_items: Ограничено до <max_throughput>
watermark:
title: Demo version
desc: Click here to see the Steam version advantages!
get_on_steam: Get on steam
title: Демоверсия
desc: Нажмите сюда, чтобы узнать о преимуществах Steam версии!
get_on_steam: Приобрести в Steam
standaloneAdvantages:
title: Get the full version!
no_thanks: No, thanks!
title: Приобретите полную версию!
no_thanks: Нет, спасибо!
points:
levels:
title: 12 New Levels
desc: For a total of 26 levels!
title: 12 Новых Уровней!
desc: Всего 26 уровней!
buildings:
title: 18 New Buildings
desc: Fully automate your factory!
title: 18 новых Построек!
desc: Полностью автоматизируйте свою фабрику!
savegames:
title: Savegames
desc: As many as your heart desires!
title: Сохранений
desc: Сколько Вашей душе угодно!
upgrades:
title: 20 Upgrade Tiers
desc: This demo version has only 5!
title: 20 стадий улучшений!
desc: В демоверсии всего 5!
markers:
title: Markers
desc: Never get lost in your factory!
title: Маркеров!
desc: Никогда не теряйтесь в своей фабрике!
wires:
title: Wires
desc: An entirely new dimension!
title: Провода!
desc: Полноценное дополнительное измерение!
darkmode:
title: Dark Mode
desc: Stop hurting your eyes!
title: Темная Тема!
desc: Дайте глазам отдохнуть!
support:
title: Support me
desc: I develop it in my spare time!
title: Поддержите меня
desc: Я занимаюсь разработкой в свободное время!
shopUpgrades:
belt:
name: Конвейеры, Разделители & Туннели
@ -417,11 +415,11 @@ buildings:
default:
name: Резак
description: Разрезает фигуры сверху вниз и выводит обе половины. <strong>Если
Вы собираетесь использовать только одну часть, уничтожьте
вы собираетесь использовать только одну часть, уничтожьте
другую, иначе производство остановится!</strong>
quad:
name: Резак (4Вых.)
description: Разрезает фигуры на четыре части. <strong>Если Вы собираетесь
description: Разрезает фигуры на четыре части. <strong>Если вы собираетесь
использовать не все части - уничтожьте оставшиеся, иначе
производство остановится!</strong>
rotater:
@ -432,8 +430,8 @@ buildings:
name: Вращатель (Обр.)
description: Поворачивает фигуры против часовой стрелки на 90 градусов.
rotate180:
name: Rotate (180)
description: Rotates shapes by 180 degrees.
name: Вращатель (180)
description: Поворачивает фигуры на 180 градусов.
stacker:
default:
name: Объединитель
@ -452,9 +450,9 @@ buildings:
description: Красит фигуру из левых входов красителем из перпендикулярного.
quad:
name: Покрасчик (4Вх.)
description: Allows you to color each quadrant of the shape individually. Only
slots with a <strong>truthy signal</strong> on the wires layer
will be painted!
description: Позволяет отдельно окрашивать каждую часть фигуры. Только ячейки с
<strong>положительным сигналом</strong> на слое с проводами
будут окрашены!
mirrored:
name: Покрасчик
description: Красит всю фигуру из левого входа красителем из перпендикулярного.
@ -466,134 +464,132 @@ buildings:
deliver: Доставить
toUnlock: чтобы открыть
levelShortcut: Ур.
endOfDemo: End of Demo
endOfDemo: Конец Демо
wire:
default:
name: Энерг. провод
description: Позволяет транспортировать энергию.
second:
name: Wire
description: Transfers signals, which can be items, colors or booleans (1 / 0).
Different colored wires do not connect.
name: Энерг. провод
description: Передает сигналы, которые могут быть ресурсами, цветами или логическими значениями (1 / 0).
Провода разных цветов не соединяются.
balancer:
default:
name: Balancer
description: Multifunctional - Evenly distributes all inputs onto all outputs.
name: Балансер
description: Многофункциональный - равномерно распределяет все входы на выходы.
merger:
name: Merger (compact)
description: Merges two conveyor belts into one.
name: Соединитель (компактный)
description: Соединяет две линии ковейера в одну.
merger-inverse:
name: Merger (compact)
description: Merges two conveyor belts into one.
name: Соединитель (компактный)
description: Соединяет две линии ковейера в одну.
splitter:
name: Splitter (compact)
description: Splits one conveyor belt into two.
name: Разделитель (компактный)
description: Разделяет одну линию конвейера на две.
splitter-inverse:
name: Splitter (compact)
description: Splits one conveyor belt into two.
name: Разделитель (компактный)
description: Разделяет одну линию конвейера на две.
storage:
default:
name: Storage
description: Stores excess items, up to a given capacity. Prioritizes the left
output and can be used as an overflow gate.
name: Буферное Хранилище
description: Хранит излишние ресурсы пока есть место. Левый выход в приоритете, может быть использован как буфер.
wire_tunnel:
default:
name: Wire Crossing
description: Allows to cross two wires without connecting them.
name: Пересечение Проводов
description: Позволяет пересекать провода при этом их не соединяя.
constant_signal:
default:
name: Constant Signal
description: Emits a constant signal, which can be either a shape, color or
boolean (1 / 0).
name: Постоянный Сигнал
description: Издает постоянный сигнал, который может быть ресурсом, цветом или логическим значением (1 / 0).
lever:
default:
name: Switch
description: Can be toggled to emit a boolean signal (1 / 0) on the wires layer,
which can then be used to control for example an item filter.
name: Переключатель
description: Может быть переключен, чтобы издавать логический сигнал (1 / 0) на слое с проводами,
который может быть использован для управления Фильтром, например.
logic_gate:
default:
name: AND Gate
description: Emits a boolean "1" if both inputs are truthy. (Truthy means shape,
color or boolean "1")
description: Издает значение "1" если оба входа положительны. (Положительный - значит ресурс,
цвет или логическое значение "1")
not:
name: NOT Gate
description: Emits a boolean "1" if the input is not truthy. (Truthy means
shape, color or boolean "1")
description: Издает значение "1" если вход не положительный. (Положительный - значит ресурс,
цвет или логическое значение "1")
xor:
name: XOR Gate
description: Emits a boolean "1" if one of the inputs is truthy, but not both.
(Truthy means shape, color or boolean "1")
description: Издает значение "1" только один из входов положительный.
(Положительный - значит ресурс,
цвет или логическое значение "1")
or:
name: OR Gate
description: Emits a boolean "1" if one of the inputs is truthy. (Truthy means
shape, color or boolean "1")
transistor:
default:
name: Transistor
description: Forwards the bottom input if the side input is truthy (a shape,
color or "1").
mirrored:
name: Transistor
description: Forwards the bottom input if the side input is truthy (a shape,
color or "1").
description: Издает значение "1" если хотя бы один вход положительный. (Положительный - значит ресурс,
цвет или логическое значение "1").
transistor:
default:
name: Транзистор
description: Пропускает нижний сигнал, если боковой сигнал положительный (ресурс,
цвет или логическое значение "1").
mirrored:
name: Транзистор
description: Пропускает нижний сигнал, если боковой сигнал положительный (ресурс,
цвет или логическое значение "1").
filter:
default:
name: Filter
description: Connect a signal to route all matching items to the top and the
remaining to the right. Can be controlled with boolean signals
too.
name: Фильтр
description: Подключите сигнал, чтобы направить все подходящие ресурсы наверх,
а остальные направо. Также контролируемо логическими
сигналами.
display:
default:
name: Display
description: Connect a signal to show it on the display - It can be a shape,
color or boolean.
name: Экран
description: Подключите сигнал, чтобы отобразить его на экране. Это может ресурс, цвет
или логическое значение (1 / 0).
reader:
default:
name: Belt Reader
description: Allows to measure the average belt throughput. Outputs the last
read item on the wires layer (once unlocked).
name: Измеритель
description: Позволяет измерять среднюю пропускную способность конвейера. Отображает последний
прошедший ресурс на слое с проводами (когда разблокировано).
analyzer:
default:
name: Shape Analyzer
description: Analyzes the top right quadrant of the lowest layer of the shape
and returns its shape and color.
name: Анализатор Фигур
description: Анализирует правую верхнюю часть низшего слоя фигуры
и возвращает ее форму и цвет.
comparator:
default:
name: Compare
description: Returns boolean "1" if both signals are exactly equal. Can compare
shapes, items and booleans.
name: Сравнить
description: Возвращает значение "1" если оба сигнала полностью одинаковы. Может сравнивать
фигуры, цвета и логические значения.
virtual_processor:
default:
name: Virtual Cutter
description: Virtually cuts the shape into two halves.
name: Виртуальный Резак
description: Виртуально разрезает фигуру пополам.
rotater:
name: Virtual Rotater
description: Virtually rotates the shape, both clockwise and counter-clockwise.
name: Виртуальный Вращатель
description: Виртуально вращает фигуру как по часовой стрелке, так и против часовой стрелки.
unstacker:
name: Virtual Unstacker
description: Virtually extracts the topmost layer to the right output and the
remaining ones to the left.
name: Виртуальный Разъединитель
description: Виртуально извлекает самый верхний слой фигуры направо,
а все остальное направо.
stacker:
name: Virtual Stacker
description: Virtually stacks the right shape onto the left.
name: Виртуальный Объединитель
description: Виртуально помещает правый предмет поверх левого.
painter:
name: Virtual Painter
description: Virtually paints the shape from the bottom input with the shape on
the right input.
name: Виртуальный Покрасчик
description: Виртуально окрашивает фигуру из нижнего входа цветом из
правого входа.
item_producer:
default:
name: Item Producer
description: Available in sandbox mode only, outputs the given signal from the
wires layer on the regular layer.
name: Генератор Ресурсов
description: Доступен только в режиме песочницы, производит заданный на
слое с проводами сигнал на обычном слое.
storyRewards:
reward_cutter_and_trash:
title: Разрезание Фигур
desc: You just unlocked the <strong>cutter</strong>, which cuts shapes in half
from top to bottom <strong>regardless of its
orientation</strong>!<br><br>Be sure to get rid of the waste, or
otherwise <strong>it will clog and stall</strong> - For this purpose
I have given you the <strong>trash</strong>, which destroys
everything you put into it!
desc: Разблокирован <strong>резак</strong>, который разрезает фигуры пополам
по вертикали <strong>независимо от ориентации</strong>!<br><br>Не забудьте избавляться от излишков,
иначе <strong>он забьется и остановится</strong> - для этого
я также открыл для Вас <strong>мусорку</strong>, которая уничтожает
все, что в нее попадает!
reward_rotater:
title: Вращение
desc: Разблокирован <strong>вращатель</strong>! Он поворачивает фигуры по
@ -616,9 +612,9 @@ storyRewards:
правого входа <strong>наложится</strong> на фигуру из левого!
reward_splitter:
title: Разделитель / Соединитель
desc: You have unlocked a <strong>splitter</strong> variant of the
<strong>balancer</strong> - It accepts one input and splits them
into two!
desc: Разблокирован <strong>разделитель</strong> один из вариантов
<strong>балансера</strong> - он принимает один вход и разделяет его
на два!
reward_tunnel:
title: Туннель
desc: Разблокирован <strong>туннель</strong>! Теперь вы можете транспортировать
@ -630,10 +626,9 @@ storyRewards:
<strong>нажмите 'T', чтобы переключить вариант</strong>!
reward_miner_chainable:
title: Цепной Экстрактор
desc: "You have unlocked the <strong>chained extractor</strong>! It can
<strong>forward its resources</strong> to other extractors so you
can more efficiently extract resources!<br><br> PS: The old
extractor has been replaced in your toolbar now!"
desc: "Разблокирован <strong>цепной экстрактор</strong>! Он может <strong>передавать
свои ресурсы</strong> другим экстракторам, чтобы вы могли эффективнее извлекать ресурсы!<br><br>
PS: Старый экстрактор был заменен в панели инструментов!"
reward_underground_belt_tier_2:
title: Туннель II
desc: Разблокирован новый вариант <strong>туннеля</strong> с <strong>большей
@ -649,18 +644,18 @@ storyRewards:
одновременно</strong>, потребляя только один краситель вместо двух!
reward_storage:
title: Буферное Хранилище
desc: You have unlocked the <strong>storage</strong> building - It allows you to
store items up to a given capacity!<br><br> It priorities the left
output, so you can also use it as an <strong>overflow gate</strong>!
desc: Разблокировано <strong>буферное хранилище</strong> - оно позволяет
хранить в нем ресурсы пока есть место! <br><br> Левый выход в
приоритете, может быть использован как <strong>буфер</strong>!
reward_freeplay:
title: Свободная игра
desc: You did it! You unlocked the <strong>free-play mode</strong>! This means
that shapes are now <strong>randomly</strong> generated!<br><br>
Since the hub will require a <strong>throughput</strong> from now
on, I highly recommend to build a machine which automatically
delivers the requested shape!<br><br> The HUB outputs the requested
shape on the wires layer, so all you have to do is to analyze it and
automatically configure your factory based on that.
desc: У Вас получилось! Разблокирован <strong>свободный режим</strong>! Это означает
что теперь фигуры будут генерироваться <strong>случайно</strong>!<br><br>
Так как ХАБ отныне будет требовать определенную <strong>пропускную способность</strong>,
я настоятельно рекомендую построить механизм, автоматически
доставляющий запрашиваемую фигуру!<br><br> ХАБ выводит запрашиваемую
фигуру на слое с проводами, так что все, что необходимо сделать, - это
проанализировать ее и автоматически настроить вашу фабрику.
reward_blueprints:
title: Чертежи
desc: Теперь вы можете <strong>копировать и вставлять</strong> части вашей
@ -678,70 +673,69 @@ storyRewards:
title: Следующий уровень
desc: Поздравляем! Кстати, больше контента планируется для полной версии!
reward_balancer:
title: Balancer
desc: The multifunctional <strong>balancer</strong> has been unlocked - It can
be used to build bigger factories by <strong>splitting and merging
items</strong> onto multiple belts!<br><br>
title: Балансер
desc: Многофункциональный <strong>балансер</strong> разблокирован - он может
быть использован для строительства огромных фабрик, <strong>разделяя и объединяя ресурсы</strong>
на множество конвейеров!<br><br>
reward_merger:
title: Compact Merger
desc: You have unlocked a <strong>merger</strong> variant of the
<strong>balancer</strong> - It accepts two inputs and merges them
into one belt!
title: Компактный Соединитель
desc: Разблокирован <strong>соединитель</strong> - вариант
<strong>балансера</strong> - он принимает два входа и объединяет их
в один конвейер.
reward_belt_reader:
title: Belt reader
desc: You have now unlocked the <strong>belt reader</strong>! It allows you to
measure the throughput of a belt.<br><br>And wait until you unlock
wires - then it gets really useful!
title: Измеритель
desc: Разблокирован <strong>измеритель</strong>! Он позволяет
измерять пропускную способность конвейера.<br><br>А как полезен он будет,
когда вы разблокируете провода!
reward_rotater_180:
title: Rotater (180 degrees)
desc: You just unlocked the 180 degress <strong>rotater</strong>! - It allows
you to rotate a shape by 180 degress (Surprise! :D)
title: Вращатель (180 градусов)
desc: Разблокирован <strong>rotater</strong> на 180 градусов! - Он позволяет
вращать фигур на 180 градусов (Сюрприз! :D)
reward_display:
title: Display
desc: "You have unlocked the <strong>Display</strong> - Connect a signal on the
wires layer to visualize it!<br><br> PS: Did you notice the belt
reader and storage output their last read item? Try showing it on a
display!"
title: Экран
desc: "Разблокирован <strong>Экран</strong> - Подключите сигнал на слое
с проводами чтобы отобразить его!<br><br> PS: Заметили ли вы, что измеритель
и буферное хранилище отображают последний ресурс, прошедший через них? Попробуйте
отобразить его на экране!"
reward_constant_signal:
title: Constant Signal
desc: You unlocked the <strong>constant signal</strong> building on the wires
layer! This is useful to connect it to <strong>item filters</strong>
for example.<br><br> The constant signal can emit a
<strong>shape</strong>, <strong>color</strong> or
<strong>boolean</strong> (1 / 0).
title: Постоянный Сигнал
desc: Разблокирован <strong>постоянный сигнал</strong> на слое с проводами!
Он полезен для подключения к <strong>фильтрам</strong>,
например.<br><br> Постоянный сигнал может издавать <strong>фигуру</strong>, <strong>цвет</strong> или
<strong>логическое значение</strong> (1 / 0).
reward_logic_gates:
title: Logic Gates
desc: You unlocked <strong>logic gates</strong>! You don't have to be excited
about this, but it's actually super cool!<br><br> With those gates
you can now compute AND, OR, XOR and NOT operations.<br><br> As a
bonus on top I also just gave you a <strong>transistor</strong>!
title: Логические Элементы
desc: Разблокированы <strong>логические элементы</strong>! Вы не обязаны радоваться
по этому поводу, но вообще это очень круто!<br><br> С этими элементами
теперь вы можете производить И, ИЛИ, исключающее ИЛИ и НЕ операции.<br><br>
Как бонус, я также дал вам <strong>транзистор</strong>!
reward_virtual_processing:
title: Virtual Processing
desc: I just gave a whole bunch of new buildings which allow you to
<strong>simulate the processing of shapes</strong>!<br><br> You can
now simulate a cutter, rotater, stacker and more on the wires layer!
With this you now have three options to continue the game:<br><br> -
Build an <strong>automated machine</strong> to create any possible
shape requested by the HUB (I recommend to try it!).<br><br> - Build
something cool with wires.<br><br> - Continue to play
regulary.<br><br> Whatever you choose, remember to have fun!
title: Виртуальное Производство
desc: Только что я открыл вам множество новых построек, которые позволят
<strong>симулировать производство фигур</strong>!<br><br> Теперь вы
можете симулировать резак, вращатель, объединитель и др. на слое с проводами!
Теперь у вас есть три варианта продолжения игры:<br><br> - Построить
<strong>автоматический механизм</strong> для производства любой
фигуры, запрашиваемой ХАБ (рекомендую попробовать!).<br><br> - Построить
что-то клевое, используя провода.<br><br> - Продолжить обычную
игру. <br><br> Что бы вы не выбрали, не забывайте хорошо проводить время!
reward_wires_painter_and_levers:
title: Wires & Quad Painter
desc: "You just unlocked the <strong>Wires Layer</strong>: It is a separate
layer on top of the regular layer and introduces a lot of new
mechanics!<br><br> For the beginning I unlocked you the <strong>Quad
Painter</strong> - Connect the slots you would like to paint with on
the wires layer!<br><br> To switch to the wires layer, press
title: Провода & Покрасчик (4 входа)
desc: "Разблокирован <strong>Слой с Проводами</strong>. Это отдельный
слой поверх обычного слоя, добавляющий множество новых
механик!<br><br> Для начала я разблокировал <strong>Покрасчик
на четыре входа</strong> - соедините ячейки, которые вы бы хотели окрасить на
слое с проводами!<br><br> Чтобы переключиться на слой слой с проводами, нажмите
<strong>E</strong>."
reward_filter:
title: Item Filter
desc: You unlocked the <strong>Item Filter</strong>! It will route items either
to the top or the right output depending on whether they match the
signal from the wires layer or not.<br><br> You can also pass in a
boolean signal (1 / 0) to entirely activate or disable it.
title: Фильтр
desc: Разблокирован <strong>Фильтр</strong>! Он направит ресурсы
наверх или направо в зависмости от того, совпадают ли они с
установленным сигналом. <br><br> Вы также можете передавать
логические значения (1 / 0), чтобы полностью отключить или включить его.
reward_demo_end:
title: End of Demo
desc: You have reached the end of the demo version!
title: Конец Демо
desc: Вы достигли конца демоверсии игры!
settings:
title: Настройки
categories:
@ -835,9 +829,9 @@ settings:
description: Включает виньетирование, которое затемняет углы экрана и облегчает
чтение текста.
autosaveInterval:
title: Интервал авто-сохранения
title: Интервал автосохранения
description: Управляет тем, как часто игра автоматически сохраняется. Также
здесь можно полностью отключить авто-сохранение.
здесь можно полностью отключить автосохранение.
intervals:
one_minute: 1 Минута
two_minutes: 2 Минуты
@ -864,47 +858,46 @@ settings:
установлен. С этой настройкой может быть удобнее, при частом
переключении между различными типами зданий.
soundVolume:
title: Sound Volume
description: Set the volume for sound effects
title: Громкость Звука
description: Задает громкость звуковых эффектов.
musicVolume:
title: Music Volume
description: Set the volume for music
description: Задает громкость музыки.
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!
title: Низкое качество ресурсов на карте
description: Упрощает отображение ресурсов на карте при приближении для
улучшения производительности. Оно даже выглядит аккуратнее, поэтому обязательно
попробуйте!
disableTileGrid:
title: Disable Grid
description: Disabling the tile grid can help with the performance. This also
makes the game look cleaner!
title: Отключить Сетку
description: Отключение разделительной сетки может помочь улучшить производительность. Кроме того,
делает игру визуально проще!
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.
title: Очистить Курсор на ПКМ
description: Включено по умолчанию, очищает курсор от выбранной постройки
при нажатии на ПКМ. Если отключено, вы можете удалять постройки
при нажатии на ПКМ во время строительства.
lowQualityTextures:
title: Low quality textures (Ugly)
description: Uses low quality textures to save performance. This will make the
game look very ugly!
title: Низкое качество текстур (Некрасиво)
description: Использует низкое качество текстур, чтобы улучшить производительность. Это сделает
игру очень некрасивой!
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.
title: Отображать границы чанков
description: Эта игра разделена на чанки, состоящие из 16x16 ячеек, если эта настройка
включена, границы чанков будут отображаться.
pickMinerOnPatch:
title: Pick miner on resource patch
description: Enabled by default, selects the miner if you use the pipette when
hovering a resource patch.
title: Выбрать Экстрактор над Жилой
description: Включено по умолчанию, выбирает экстрактор, если использовать пипетку
над жилой с ресурсами.
simplifiedBelts:
title: Simplified Belts (Ugly)
description: Does not render belt items except when hovering the belt to save
performance. I do not recommend to play with this setting if you
do not absolutely need the performance.
title: Упрощенные Конвейеры (Некрасиво)
description: Не отображает ресурсы, находящиеся на конвейере, если не навести курсор для улучшения
производительности. Не рекомендую играть с этой настройкой, если вас
устраивает производительность.
enableMousePan:
title: Enable Mouse Pan
description: Allows to move the map by moving the cursor to the edges of the
screen. The speed depends on the Movement Speed setting.
title: Включить Перемещение Мышкой
description: Позволяет двигать карту, перемещая курсор к краям
экрана. Скорость зависит от настройки Скорости движения.
rangeSliderPercentage: <amount> %
keybindings:
title: Настройки управления
@ -951,9 +944,9 @@ keybindings:
massSelectStart: Модификатор для выделения области
massSelectSelectMultiple: Выбрать несколько областей
massSelectCopy: Копировать область
placementDisableAutoOrientation: Отключить авто-определение направления
placementDisableAutoOrientation: Отключить автоопределение направления
placeMultiple: Оставаться в режиме размещения
placeInverse: Инвертировать авто-определение направления конвейеров
placeInverse: Инвертировать автоопределение направления конвейеров
pasteLastBlueprint: Вставить последний чертеж
massSelectCut: Вырезать область
exportScreenshot: Экспорт всей Базы в виде Изображения
@ -964,21 +957,21 @@ keybindings:
menuClose: Закрыть меню
switchLayers: Переключить слои
wire: Энергетический провод
balancer: Balancer
storage: Storage
constant_signal: Constant Signal
logic_gate: Logic Gate
lever: Switch (regular)
filter: Filter
wire_tunnel: Wire Crossing
display: Display
reader: Belt Reader
virtual_processor: Virtual Cutter
transistor: Transistor
analyzer: Shape Analyzer
comparator: Compare
item_producer: Item Producer (Sandbox)
copyWireValue: "Wires: Copy value below cursor"
balancer: Балансер
storage: Буферное Хранилище
constant_signal: Постоянный Сигнал
logic_gate: Логический Элемент
lever: Переключатель (обычный)
filter: Фильтр
wire_tunnel: Пересечение Проводов
display: Экран
reader: Измеритель
virtual_processor: Виртуальный Резак
transistor: Транзистор
analyzer: Анализатор Фигур
comparator: Сравнить
item_producer: Генератор Ресурсов (Песочница)
copyWireValue: "Провода: скопировать значение под курсором"
about:
title: Об игре
body: >-
@ -988,7 +981,8 @@ about:
Если вы хотите внести свой вклад игре - <a href="<githublink>" target="_blank">shapez.io в github</a>.<br><br>
Эта игра не была бы возможна без большого сообщества в дискорде, которое собралось вокруг моих игр - Вам действительно стоит присоединиться к <a href="<discordlink>" target="_blank">серверу Discord!</a>!<br><br>
Эта игра не была бы возможна без большого сообщества в дискорде, которое собралось вокруг моих игр - Вам действительно стоит присоединиться к
<a href="<discordlink>" target="_blank">серверу Discord!</a>!<br><br>
Саундтрек сделал <a href="https://soundcloud.com/pettersumelius" target="_blank">Peppsen</a> - Он потрясающий.<br><br>
@ -1002,65 +996,57 @@ demo:
oneGameLimit: Ограниченность одним сохранением игры
customizeKeybindings: Пользовательская настройка Управления
exportingBase: Экспорт всей Базы в виде Изображения
settingNotAvailable: Не доступно в демо-версии.
settingNotAvailable: Недоступно в демоверсии.
tips:
- The hub accepts input of any kind, not just the current shape!
- Make sure your factories are modular - it will pay out!
- Don't build too close to the hub, or it will be a huge chaos!
- If stacking does not work, try switching the inputs.
- You can toggle the belt planner direction by pressing <b>R</b>.
- Holding <b>CTRL</b> allows dragging of belts without auto-orientation.
- Ratios stay the same, as long as all upgrades are on the same Tier.
- Serial execution is more efficient than parallel.
- You will unlock more variants of buildings later in the game!
- You can use <b>T</b> to switch between different variants.
- Symmetry is key!
- You can weave different tiers of tunnels.
- Try to build compact factories - it will pay out!
- The painter has a mirrored variant which you can select with <b>T</b>
- Having the right building ratios will maximize efficiency.
- At maximum level, 5 extractors will fill a single belt.
- Don't forget about tunnels!
- You don't need to divide up items evenly for full efficiency.
- Holding <b>SHIFT</b> will activate the belt planner, letting you place
long lines of belts easily.
- Cutters always cut vertically, regardless of their orientation.
- To get white mix all three colors.
- The storage buffer priorities the first output.
- Invest time to build repeatable designs - it's worth it!
- Holding <b>CTRL</b> allows to place multiple buildings.
- You can hold <b>ALT</b> to invert the direction of placed belts.
- Efficiency is key!
- Shape patches that are further away from the hub are more complex.
- Machines have a limited speed, divide them up for maximum efficiency.
- Use balancers to maximize your efficiency.
- Organization is important. Try not to cross conveyors too much.
- Plan in advance, or it will be a huge chaos!
- Don't remove your old factories! You'll need them to unlock upgrades.
- Try beating level 20 on your own before seeking for help!
- Don't complicate things, try to stay simple and you'll go far.
- You may need to re-use factories later in the game. Plan your factories to
be re-usable.
- Sometimes, you can find a needed shape in the map without creating it with
stackers.
- Full windmills / pinwheels can never spawn naturally.
- Color your shapes before cutting for maximum efficiency.
- With modules, space is merely a perception; a concern for mortal men.
- Make a separate blueprint factory. They're important for modules.
- Have a closer look on the color mixer, and your questions will be answered.
- Use <b>CTRL</b> + Click to select an area.
- Building too close to the hub can get in the way of later projects.
- The pin icon next to each shape in the upgrade list pins it to the screen.
- Mix all primary colors together to make white!
- You have an infinite map, don't cramp your factory, expand!
- Also try Factorio! It's my favorite game.
- The quad cutter cuts clockwise starting from the top right!
- You can download your savegames in the main menu!
- This game has a lot of useful keybindings! Be sure to check out the
settings page.
- This game has a lot of settings, be sure to check them out!
- The marker to your hub has a small compass to indicate its direction!
- To clear belts, cut the area and then paste it at the same location.
- Press F4 to show your FPS and Tick Rate.
- Press F4 twice to show the tile of your mouse and camera.
- You can click a pinned shape on the left side to unpin it.
- ХАБ принимает любые ресурсы, не только текущую фигуру!
- Старайтесь создавать модульные фабрики - вы не пожалеете!
- Не стройте слишком близко к ХАБ-у, иначе начнется ужасный хаос!
- Если не получается объединить фигуры, попробуйте поменять входы местами!
- Вы можете изменить направление конвейера при строительстве, нажав <b>R</b>.
- Удерживая <b>CTRL</b>, вы можете перемещать конвейеры без авто ориентации.
- Соотношения всегда одинаковы, если уровни улучшений равны.
- Последовательное выполнение эффективнее, чем параллельное.
- Вам будут открываться новые варианты построек по мере прохождения!
- Нажмите <b>T</b>, чтобы переключаться между различными вариантами.
- Симметрия - ключ к успеху!
- Вы можете переплетать между собой туннели разных уровней.
- Попробуйте строить компактные фабрики - вы не пожалеете!
- Покрасчик имеет зеркальный вариант, который может быть выбран, нажав <b>T</b>.
- Правильные соотношения построек позволяет улучшить эффективность фабрики.
- Удерживая <b>SHIFT</b>, вы можете легко строить длинные конвейерные линии.
- Резаки всегда разрезают пополам по вертикали вне зависимости от ориентации.
- Чтобы получить белый цвет, смешайте все три цвета.
- Левый выход Буферного Хранилища - в приоритете.
- Вкладывайте время в строительство повторяемых механизмов - оно того стоит!
- Удерживая <b>CTRL</b>, можно размещать много построек за раз.
- Вы можете удерживать <b>ALT</b>, чтобы инвертировать направление размещенных конвейеров.
- Эффективность - ключ к успеху!
- Чем дальше от ХАБ-а, тем Жилы с ресурсами сложнее.
- Механизмы имеют ограниченную скорость, разбивайте их для максимальной эффективности.
- Используйте балансеры, чтобы максимизировать эффективность.
- Организация очень важна, старайтесь не пересекать конвейеры слишком часто.
- Планируйте заранее, иначе начнется ужасный хаос!
- Не удаляйте свои старые фабрики! Они понадобятся вам, чтобы открывать улучшения.
- Попробуйте пройти 20-ый уровень самостоятельно, прежде чем искать помощи!
- Не усложняйте себе жизнь, старайтесь думать проще и вы достигните больших высот.
- Вам может снова понадобиться ваша старая фабрика позже в игре. Старайтесь планировать фабрику, чтобы она могла быть повторно использована.
- Иногда, вы можете найти необходимую фигуру на карте, вместо того, чтобы создавать ее самостоятельно.
- Полноценные мельницы/вертушки никогда не генерируются натурально.
- Окрашивайте свои фигуры, прежде чем разрезать для максимальной эффективности.
- С модулями теряется восприятие пространства; забота смертных.
- Создайте отдельную фабрику чертежей. Они очень важны для модулей.
- Взгляните внимательнее на смешиватель и вы найдете ответы на свои вопросы.
- Чтобы выделить область, используйте <b>CTRL</b> + Клик.
- Строительство вблизи ХАБ-а может помешать будущим проектам.
- Иконка булавки на каждой фигуре закрепляет ее на экране.
- Смешайте все три основных цвета, чтобы получить белый!
- В вашем распоряжении бесконечная карта! Не загромождайте вашу фабрику, расширяйтесь!
- Также попробуйте Factorio. Это моя любимая игра.
- Резак(4 входа) разрезает по часовой стрелке, начиная с правой верхней части!
- Вы можете скачать свои сохранения в главном меню!
- В этой игре множество полезных комбинаций клавиш. Загляните в настройки, чтобы ознакомиться с ними.
- В этой игре множество настроек, не забудьте с ними ознакомиться.
- Маркер ХАБ-а имеет небольшой компас, указывающий его направление.
- Нажмите F4, чтобы показать FPS и Частоту Обновления.
- Нажмите F4 дважды, чтобы показать координаты курсора и камеры.
- Вы можете нажать на закрепленную фигуру слева, чтобы открепить ее.

View File

@ -5,7 +5,7 @@ steamPage:
discordLinkShort: Official Discord
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
automated production of geometric shapes.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.

View File

@ -4,7 +4,7 @@ steamPage:
discordLinkShort: Official Discord
intro: >-
Shapez.io is a relaxed game in which you have to build factories for the
automated production of geometric shapes.
automated production of geometric shapes.
As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.

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