1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-12-17 12:11:51 +00:00

Updated to 1.4.4

This commit is contained in:
DJ1TJOO 2021-09-01 17:36:02 +02:00
commit bd1602903f
117 changed files with 5182 additions and 2719 deletions

View File

@ -39,7 +39,7 @@ You can use [Gitpod](https://www.gitpod.io/) (an Online Open Source VS Code-like
- install all of the dependencies. - install all of the dependencies.
- start `gulp` in `gulp/` directory. - start `gulp` in `gulp/` directory.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/from-referrer/) [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/tobspr/shapez.io)
## Helping translate ## Helping translate

View File

@ -0,0 +1 @@
To build, place the lib64 folder from the wegame sdk for electron 13 in `wegame_sdk` and run the `wegame.main.standalone` gulp task.

View File

@ -51,8 +51,9 @@ function createWindow() {
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
webSecurity: false, webSecurity: false,
contextIsolation: false,
}, },
// allowRunningInsecureContent: false, allowRunningInsecureContent: false,
}); });
if (isLocal) { if (isLocal) {

View File

@ -10,7 +10,7 @@
"start": "electron --disable-direct-composition --in-process-gpu ." "start": "electron --disable-direct-composition --in-process-gpu ."
}, },
"devDependencies": { "devDependencies": {
"electron": "3.1.13" "electron": "^13.1.6"
}, },
"dependencies": { "dependencies": {
"async-lock": "^1.2.8" "async-lock": "^1.2.8"

View File

@ -1,5 +1,5 @@
const railsdk = require("./wegame_sdk/railsdk.js"); const railsdk = require("./wegame_sdk/railsdk.js");
const { dialog } = require("electron"); const { dialog, app, remote, ipcMain } = require("electron");
function init(isDev) { function init(isDev) {
console.log("Step 1: wegame: init"); console.log("Step 1: wegame: init");
@ -39,7 +39,7 @@ function init(isDev) {
event.state === railsdk.RailSystemState.kSystemStatePlatformExit || event.state === railsdk.RailSystemState.kSystemStatePlatformExit ||
event.state === railsdk.RailSystemState.kSystemStateGameExitByAntiAddiction event.state === railsdk.RailSystemState.kSystemStateGameExitByAntiAddiction
) { ) {
remote.app.exit(); app.exit();
} }
} }
}); });
@ -47,6 +47,17 @@ function init(isDev) {
function listen() { function listen() {
console.log("wegame: listen"); console.log("wegame: listen");
ipcMain.handle("profanity-check", async (event, data) => {
if (data.length === 0) {
return "";
}
const result = railsdk.RailUtils.DirtyWordsFilter(data, true);
if (result.check_result.dirty_type !== 0 /** kRailDirtyWordsTypeNormalAllowWords */) {
return result.check_result.replace_string;
}
return data;
});
} }
module.exports = { init, listen }; module.exports = { init, listen };

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -137,16 +137,20 @@
button.continue { button.continue {
background: #555; background: #555;
@include S(margin-right, 10px);
} }
button.menu { button.menu {
background: #555;
}
button.nextPuzzle {
background-color: $colorGreenBright; background-color: $colorGreenBright;
} }
> button { > button {
@include S(min-width, 100px); @include S(min-width, 100px);
@include S(padding, 10px, 20px); @include S(padding, 8px, 16px);
@include S(margin, 0, 6px);
@include IncreasedClickArea(0px); @include IncreasedClickArea(0px);
} }
} }

View File

@ -0,0 +1,41 @@
#ingame_HUD_PuzzleNextPuzzle {
position: absolute;
@include S(top, 17px);
@include S(right, 10px);
display: flex;
flex-direction: column;
align-items: flex-end;
backdrop-filter: blur(D(1px));
padding: D(3px);
> .button {
@include ButtonText;
@include IncreasedClickArea(0px);
pointer-events: all;
cursor: pointer;
position: relative;
color: #333438;
transition: all 0.12s ease-in-out;
text-transform: uppercase;
transition-property: opacity, transform;
@include PlainText;
@include S(padding-right, 25px);
opacity: 1;
@include DarkThemeInvert;
&:hover {
opacity: 0.9 !important;
}
&.pressed {
transform: scale(0.95) !important;
}
& {
/* @load-async */
background: uiResource("icons/state_next_button.png") right center / D(15px) no-repeat;
}
}
}

View File

@ -21,6 +21,7 @@
@import "adinplay"; @import "adinplay";
@import "changelog_skins"; @import "changelog_skins";
@import "states/wegame_splash";
@import "states/preload"; @import "states/preload";
@import "states/main_menu"; @import "states/main_menu";
@import "states/ingame"; @import "states/ingame";
@ -65,6 +66,7 @@
@import "ingame_hud/puzzle_play_settings"; @import "ingame_hud/puzzle_play_settings";
@import "ingame_hud/puzzle_play_metadata"; @import "ingame_hud/puzzle_play_metadata";
@import "ingame_hud/puzzle_complete_notification"; @import "ingame_hud/puzzle_complete_notification";
@import "ingame_hud/puzzle_next";
// prettier-ignore // prettier-ignore
$elements: $elements:
@ -83,6 +85,7 @@ ingame_HUD_PinnedShapes,
ingame_HUD_GameMenu, ingame_HUD_GameMenu,
ingame_HUD_KeybindingOverlay, ingame_HUD_KeybindingOverlay,
ingame_HUD_PuzzleBackToMenu, ingame_HUD_PuzzleBackToMenu,
ingame_HUD_PuzzleNextPuzzle,
ingame_HUD_PuzzleEditorReview, ingame_HUD_PuzzleEditorReview,
ingame_HUD_PuzzleEditorControls, ingame_HUD_PuzzleEditorControls,
ingame_HUD_PuzzleEditorTitle, ingame_HUD_PuzzleEditorTitle,
@ -135,6 +138,7 @@ body.uiHidden {
#ingame_HUD_GameMenu, #ingame_HUD_GameMenu,
#ingame_HUD_PinnedShapes, #ingame_HUD_PinnedShapes,
#ingame_HUD_PuzzleBackToMenu, #ingame_HUD_PuzzleBackToMenu,
#ingame_HUD_PuzzleNextPuzzle,
#ingame_HUD_PuzzleEditorReview, #ingame_HUD_PuzzleEditorReview,
#ingame_HUD_Notifications, #ingame_HUD_Notifications,
#ingame_HUD_TutorialHints, #ingame_HUD_TutorialHints,

View File

@ -556,6 +556,16 @@
} }
} }
#crosspromo {
position: absolute;
@include S(bottom, 50px);
@include S(right, 20px);
@include S(width, 190px);
@include S(height, 100px);
pointer-events: all;
border: 0;
}
.footer { .footer {
display: grid; display: grid;
flex-grow: 1; flex-grow: 1;
@ -567,10 +577,45 @@
box-sizing: border-box; box-sizing: border-box;
@include S(grid-gap, 4px); @include S(grid-gap, 4px);
&.china { &.noLinks {
grid-template-columns: auto 1fr; grid-template-columns: auto 1fr;
} }
&.wegameDisclaimer {
@include SuperSmallText;
display: grid;
justify-content: center;
grid-template-columns: 1fr auto 1fr;
text-align: center;
> .disclaimer {
grid-column: 2 / 3;
@include DarkThemeOverride {
color: #fff;
}
}
> .rating {
grid-column: 3 / 4;
justify-self: end;
align-self: end;
@include S(width, 32px);
@include S(height, 40px);
background: green;
cursor: pointer !important;
pointer-events: all;
@include S(border-radius, 4px);
overflow: hidden;
& {
/* @load-async */
background: #fff uiResource("wegame_isbn_rating.jpg") center center / contain no-repeat;
}
}
}
.author { .author {
flex-grow: 1; flex-grow: 1;
text-align: right; text-align: right;

View File

@ -15,8 +15,81 @@
} }
> .container { > .container {
.searchForm {
display: flex;
align-items: center;
justify-content: center;
color: #333;
background: $accentColorBright;
@include S(padding, 5px);
@include S(border-radius, $globalBorderRadius);
flex-wrap: wrap;
@include DarkThemeOverride {
background: $accentColorDark;
}
input.search {
color: #333;
margin: 0;
display: inline-block;
flex-grow: 1;
@include S(padding, 5px, 10px);
@include S(min-width, 50px);
&::placeholder {
color: #aaa;
}
}
select {
color: #333;
border: 0;
@include S(padding, 5px);
@include S(border-radius, $globalBorderRadius);
@include S(padding, 7px, 10px);
@include S(margin-left, 5px);
@include PlainText;
}
.filterCompleted {
@include S(margin-left, 20px);
pointer-events: all;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
text-transform: uppercase;
@include PlainText;
@include S(margin-right, 10px);
@include DarkThemeOverride {
color: #bbbbc4;
}
input {
@include S(width, 15px);
@include S(height, 15px);
@include S(margin-right, 5px);
@include S(border-radius, $globalBorderRadius);
border: 0;
}
}
button[type="submit"] {
@include S(padding, 7px, 10px, 5px);
@include S(margin-left, 20px);
@include S(margin-top, 4px);
@include S(margin-bottom, 4px);
margin-left: auto;
}
}
> .mainContent { > .mainContent {
overflow: hidden; overflow: hidden;
display: flex;
flex-direction: column;
> .categoryChooser { > .categoryChooser {
> .categories { > .categories {
@ -79,8 +152,8 @@
@include S(grid-gap, 7px); @include S(grid-gap, 7px);
@include S(margin-top, 10px); @include S(margin-top, 10px);
@include S(padding-right, 4px); @include S(padding-right, 4px);
@include S(height, 320px);
overflow-y: scroll; overflow-y: scroll;
flex-grow: 1;
pointer-events: all; pointer-events: all;
position: relative; position: relative;
@ -246,6 +319,9 @@
&.stage--hard { &.stage--hard {
color: $colorRedBright; color: $colorRedBright;
} }
&.stage--unknown {
color: #888;
}
} }
} }

View File

@ -50,7 +50,8 @@
} }
button.categoryButton, button.categoryButton,
button.about { button.about,
button.privacy {
background-color: $colorCategoryButton; background-color: $colorCategoryButton;
color: #777a7f; color: #777a7f;
@ -68,6 +69,10 @@
} }
} }
button.privacy {
@include S(margin-top, 4px);
}
.versionbar { .versionbar {
@include S(margin-top, 10px); @include S(margin-top, 10px);
@ -180,7 +185,8 @@
.container .content { .container .content {
.sidebar { .sidebar {
button.categoryButton, button.categoryButton,
button.about { button.about,
button.privacy {
color: #ccc; color: #ccc;
background-color: darken($darkModeControlsBackground, 5); background-color: darken($darkModeControlsBackground, 5);

View File

@ -0,0 +1,38 @@
#state_WegameSplashState {
background: #000 !important;
display: flex;
align-items: center;
justify-content: center;
.wrapper {
opacity: 0;
@include InlineAnimation(5.9s ease-in-out) {
0% {
opacity: 0;
}
20% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
opacity: 0;
}
}
text-align: center;
color: #fff;
@include Heading;
strong {
display: block;
@include SuperHeading;
@include S(margin-bottom, 20px);
}
div {
@include S(margin-bottom, 10px);
}
}
}

View File

@ -35,6 +35,7 @@ import { AchievementsState } from "./states/achievements";
import { PuzzleMenuState } from "./states/puzzle_menu"; import { PuzzleMenuState } from "./states/puzzle_menu";
import { ClientAPI } from "./platform/api"; import { ClientAPI } from "./platform/api";
import { LoginState } from "./states/login"; import { LoginState } from "./states/login";
import { WegameSplashState } from "./states/wegame_splash";
/** /**
* @typedef {import("./platform/achievement_provider").AchievementProviderInterface} AchievementProviderInterface * @typedef {import("./platform/achievement_provider").AchievementProviderInterface} AchievementProviderInterface
@ -156,6 +157,7 @@ export class Application {
registerStates() { registerStates() {
/** @type {Array<typeof GameState>} */ /** @type {Array<typeof GameState>} */
const states = [ const states = [
WegameSplashState,
PreloadState, PreloadState,
MobileWarningState, MobileWarningState,
MainMenuState, MainMenuState,
@ -332,8 +334,12 @@ export class Application {
Loader.linkAppAfterBoot(this); Loader.linkAppAfterBoot(this);
if (G_WEGAME_VERSION) {
this.stateMgr.moveToState("WegameSplashState");
}
// Check for mobile // Check for mobile
if (IS_MOBILE) { else if (IS_MOBILE) {
this.stateMgr.moveToState("MobileWarningState"); this.stateMgr.moveToState("MobileWarningState");
} else { } else {
this.stateMgr.moveToState("PreloadState"); this.stateMgr.moveToState("PreloadState");

View File

@ -1,4 +1,27 @@
export const CHANGELOG = [ export const CHANGELOG = [
{
version: "1.4.4",
date: "29.08.2021",
entries: [
"Hotfix: Fixed the balancer not distributing items evenly, caused by the 1.4.3 update. Sorry for any inconveniences!",
],
},
{
version: "1.4.3",
date: "28.08.2021",
entries: [
"You can now hold 'ALT' while hovering a building to see its output! (Thanks to Sense101) (PS: There is now a setting to have it always on!)",
"The map overview should now be much more performant! As a consequence, you can now zoom out farther! (Thanks to PFedak)",
"Puzzle DLC: There is now a 'next puzzle' button!",
"Puzzle DLC: There is now a search function!",
"Edit signal dialog now has the previous signal filled (Thanks to EmeraldBlock)",
"Further performance improvements (Thanks to PFedak)",
"Improved puzzle validation (Thanks to Sense101)",
"Input fields in dialogs should now automatically focus",
"Fix selected building being deselected at level up (Thanks to EmeraldBlock)",
"Updated translations",
],
},
{ {
version: "1.4.2", version: "1.4.2",
date: "24.06.2021", date: "24.06.2021",
@ -6,6 +29,8 @@ export const CHANGELOG = [
"Puzzle DLC: Goal acceptors now reset after getting no items for a while (This should prevent being able to 'cheat' puzzles) (by Sense101)", "Puzzle DLC: Goal acceptors now reset after getting no items for a while (This should prevent being able to 'cheat' puzzles) (by Sense101)",
"Puzzle DLC: Added button to clear all buildings / reset the puzzle (by Sense101)", "Puzzle DLC: Added button to clear all buildings / reset the puzzle (by Sense101)",
"Puzzle DLC: Allow copy-paste in puzzle mode (by Sense101)", "Puzzle DLC: Allow copy-paste in puzzle mode (by Sense101)",
"Fixed level achievements being given on the wrong level (by DJ1TJOO)",
"Fixed blueprint not properly clearing on right click",
"Updated translations", "Updated translations",
], ],
}, },

View File

@ -167,4 +167,25 @@ export class BufferMaintainer {
}); });
return canvas; return canvas;
} }
/**
* @param {object} param0
* @param {string} param0.key
* @param {string} param0.subKey
* @returns {HTMLCanvasElement?}
*
*/
getForKeyOrNullNoUpdate({ key, subKey }) {
let parent = this.cache.get(key);
if (!parent) {
return null;
}
// Now search for sub key
const cacheHit = parent.get(subKey);
if (cacheHit) {
return cacheHit.canvas;
}
return null;
}
} }

View File

@ -7,7 +7,7 @@ export const IS_DEBUG =
export const SUPPORT_TOUCH = false; export const SUPPORT_TOUCH = false;
export const IS_MAC = navigator.platform.toLowerCase().indexOf("mac") >= 0; export const IS_MAC = navigator.platform.toLowerCase().indexOf("mac") >= 0 && !G_IS_DEV;
const smoothCanvas = true; const smoothCanvas = true;
@ -17,7 +17,10 @@ export const THIRDPARTY_URLS = {
reddit: "https://www.reddit.com/r/shapezio", reddit: "https://www.reddit.com/r/shapezio",
shapeViewer: "https://viewer.shapez.io", shapeViewer: "https://viewer.shapez.io",
privacyPolicy: "https://tobspr.io/privacy.html",
standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/", standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/",
stanaloneCampaignLink: "https://get.shapez.io",
puzzleDlcStorePage: "https://store.steampowered.com/app/1625400/shapezio__Puzzle_DLC", puzzleDlcStorePage: "https://store.steampowered.com/app/1625400/shapezio__Puzzle_DLC",
levelTutorialVideos: { levelTutorialVideos: {
@ -54,6 +57,7 @@ export const globalConfig = {
// Map // Map
mapChunkSize: 16, mapChunkSize: 16,
chunkAggregateSize: 4,
mapChunkOverviewMinZoom: 0.9, mapChunkOverviewMinZoom: 0.9,
mapChunkWorldSize: null, // COMPUTED mapChunkWorldSize: null, // COMPUTED
@ -72,7 +76,7 @@ export const globalConfig = {
readerAnalyzeIntervalSeconds: 10, readerAnalyzeIntervalSeconds: 10,
goalAcceptorItemsRequired: 10, goalAcceptorItemsRequired: 12,
goalAcceptorsPerProducer: 5, goalAcceptorsPerProducer: 5,
puzzleModeSpeed: 3, puzzleModeSpeed: 3,
puzzleMinBoundsSize: 2, puzzleMinBoundsSize: 2,

View File

@ -53,7 +53,7 @@ export default {
// Replace all translations with emojis to see which texts are translateable // Replace all translations with emojis to see which texts are translateable
// testTranslations: true, // testTranslations: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Enables an inspector which shows information about the entity below the curosr // Enables an inspector which shows information about the entity below the cursor
// enableEntityInspector: true, // enableEntityInspector: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Enables ads in the local build (normally they are deactivated there) // Enables ads in the local build (normally they are deactivated there)

View File

@ -123,6 +123,4 @@ function catchErrors(message, source, lineno, colno, error) {
return true; return true;
} }
if (!G_IS_DEV) { window.onerror = catchErrors;
window.onerror = catchErrors;
}

View File

@ -91,26 +91,6 @@ export class GameState {
} }
} }
/**
*
* @param {string} nextStateId
* @param {object=} nextStatePayload
*/
watchAdAndMoveToState(nextStateId, nextStatePayload = {}) {
if (this.app.adProvider.getCanShowVideoAd() && this.app.isRenderable()) {
this.moveToState(
"WatchAdState",
{
nextStateId,
nextStatePayload,
},
true
);
} else {
this.moveToState(nextStateId, nextStatePayload);
}
}
/** /**
* Tracks clicks on a given element and calls the given callback *on this state*. * Tracks clicks on a given element and calls the given callback *on this state*.
* If you want to call another function wrap it inside a lambda. * If you want to call another function wrap it inside a lambda.

View File

@ -1,6 +1,7 @@
import { BaseItem } from "../game/base_item"; import { BaseItem } from "../game/base_item";
import { ClickDetector } from "./click_detector"; import { ClickDetector } from "./click_detector";
import { Signal } from "./signal"; import { Signal } from "./signal";
import { getIPCRenderer } from "./utils";
/* /*
* *************************************************** * ***************************************************
@ -107,6 +108,19 @@ export class FormElementInput extends FormElement {
updateErrorState() { updateErrorState() {
this.element.classList.toggle("errored", !this.isValid()); this.element.classList.toggle("errored", !this.isValid());
// profanity filter
if (G_WEGAME_VERSION) {
const value = String(this.element.value);
getIPCRenderer()
.invoke("profanity-check", value)
.then(newValue => {
if (value !== newValue && this.element) {
this.element.value = newValue;
}
});
}
} }
isValid() { isValid() {
@ -124,6 +138,7 @@ export class FormElementInput extends FormElement {
focus() { focus() {
this.element.focus(); this.element.focus();
this.element.select();
} }
} }

View File

@ -89,6 +89,11 @@ export class RestrictionManager extends ReadWriteProxy {
return false; return false;
} }
if (queryParamOptions.embedProvider === "gamedistribution") {
// also full version on gamedistribution
return false;
}
if (G_IS_DEV) { if (G_IS_DEV) {
return typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0; return typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0;
} }

View File

@ -734,6 +734,10 @@ const romanLiteralsCache = ["0"];
* @returns {string} * @returns {string}
*/ */
export function getRomanNumber(number) { export function getRomanNumber(number) {
if (G_WEGAME_VERSION) {
return String(number);
}
number = Math.max(0, Math.round(number)); number = Math.max(0, Math.round(number));
if (romanLiteralsCache[number]) { if (romanLiteralsCache[number]) {
return romanLiteralsCache[number]; return romanLiteralsCache[number];

View File

@ -56,4 +56,12 @@ export class GoalAcceptorComponent extends Component {
(globalConfig.puzzleModeSpeed * globalConfig.beltSpeedItemsPerSecond) (globalConfig.puzzleModeSpeed * globalConfig.beltSpeedItemsPerSecond)
); );
} }
/**
* Copy the current state to another component
* @param {GoalAcceptorComponent} otherComponent
*/
copyAdditionalStateTo(otherComponent) {
otherComponent.item = this.item;
}
} }

View File

@ -11,6 +11,7 @@ import { typeItemSingleton } from "../item_resolver";
* pos: Vector, * pos: Vector,
* direction: enumDirection, * direction: enumDirection,
* item: BaseItem, * item: BaseItem,
* lastItem: BaseItem,
* progress: number?, * progress: number?,
* cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot, * cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot,
* cachedBeltPath?: BeltPath, * cachedBeltPath?: BeltPath,
@ -51,6 +52,7 @@ export class ItemEjectorComponent extends Component {
clear() { clear() {
for (const slot of this.slots) { for (const slot of this.slots) {
slot.item = null; slot.item = null;
slot.lastItem = null;
slot.progress = 0; slot.progress = 0;
} }
} }
@ -67,6 +69,7 @@ export class ItemEjectorComponent extends Component {
pos: slot.pos, pos: slot.pos,
direction: slot.direction, direction: slot.direction,
item: null, item: null,
lastItem: null,
progress: 0, progress: 0,
cachedDestSlot: null, cachedDestSlot: null,
cachedTargetEntity: null, cachedTargetEntity: null,
@ -131,6 +134,7 @@ export class ItemEjectorComponent extends Component {
return false; return false;
} }
this.slots[slotIndex].item = item; this.slots[slotIndex].item = item;
this.slots[slotIndex].lastItem = item;
this.slots[slotIndex].progress = 0; this.slots[slotIndex].progress = 0;
return true; return true;
} }

View File

@ -73,6 +73,12 @@ export class ItemProcessorComponent extends Component {
// Type of processing requirement // Type of processing requirement
this.processingRequirement = processingRequirement; this.processingRequirement = processingRequirement;
/**
* Our current inputs
* @type {Map<number, BaseItem>}
*/
this.inputSlots = new Map();
this.clear(); this.clear();
} }
@ -82,11 +88,13 @@ export class ItemProcessorComponent extends Component {
// sure the outputs always match // sure the outputs always match
this.nextOutputSlot = 0; this.nextOutputSlot = 0;
this.inputSlots.clear();
/** /**
* Our current inputs * Current input count
* @type {Array<{ item: BaseItem, sourceSlot: number }>} * @type {number}
*/ */
this.inputSlots = []; this.inputCount = 0;
/** /**
* What we are currently processing, empty if we don't produce anything rn * What we are currently processing, empty if we don't produce anything rn
@ -115,19 +123,17 @@ export class ItemProcessorComponent extends Component {
this.type === enumItemProcessorTypes.goal this.type === enumItemProcessorTypes.goal
) { ) {
// Hub has special logic .. not really nice but efficient. // Hub has special logic .. not really nice but efficient.
this.inputSlots.push({ item, sourceSlot }); this.inputSlots.set(this.inputCount, item);
this.inputCount++;
return true; return true;
} }
// Check that we only take one item per slot // Check that we only take one item per slot
for (let i = 0; i < this.inputSlots.length; ++i) { if (this.inputSlots.has(sourceSlot)) {
const slot = this.inputSlots[i]; return false;
if (slot.sourceSlot === sourceSlot) {
return false;
}
} }
this.inputSlots.set(sourceSlot, item);
this.inputSlots.push({ item, sourceSlot }); this.inputCount++;
return true; return true;
} }
} }

View File

@ -119,7 +119,7 @@ export class GameMode extends BasicSerializableObject {
/** @returns {number} */ /** @returns {number} */
getMinimumZoom() { getMinimumZoom() {
return 0.1; return 0.06;
} }
/** @returns {number} */ /** @returns {number} */

View File

@ -7,6 +7,7 @@ import { GameRoot } from "../root";
import { ShapeDefinition } from "../shape_definition"; import { ShapeDefinition } from "../shape_definition";
import { HUDAchievements } from "./parts/achievements"; import { HUDAchievements } from "./parts/achievements";
import { HUDBetaOverlay } from "./parts/beta_overlay"; import { HUDBetaOverlay } from "./parts/beta_overlay";
import { HUDBlueprintPlacer } from "./parts/blueprint_placer";
import { HUDBuildingsToolbar } from "./parts/buildings_toolbar"; import { HUDBuildingsToolbar } from "./parts/buildings_toolbar";
import { HUDBuildingPlacer } from "./parts/building_placer"; import { HUDBuildingPlacer } from "./parts/building_placer";
import { HUDColorBlindHelper } from "./parts/color_blind_helper"; import { HUDColorBlindHelper } from "./parts/color_blind_helper";
@ -20,6 +21,7 @@ import { HUDMassSelector } from "./parts/mass_selector";
import { HUDModalDialogs } from "./parts/modal_dialogs"; import { HUDModalDialogs } from "./parts/modal_dialogs";
import { enumNotificationType } from "./parts/notifications"; import { enumNotificationType } from "./parts/notifications";
import { HUDSettingsMenu } from "./parts/settings_menu"; import { HUDSettingsMenu } from "./parts/settings_menu";
import { HUDShapeTooltip } from "./parts/shape_tooltip";
import { HUDShop } from "./parts/shop"; import { HUDShop } from "./parts/shop";
import { HUDStatistics } from "./parts/statistics"; import { HUDStatistics } from "./parts/statistics";
import { HUDUnlockNotification } from "./parts/unlock_notification"; import { HUDUnlockNotification } from "./parts/unlock_notification";
@ -54,6 +56,8 @@ export class GameHUD {
this.parts = { this.parts = {
buildingsToolbar: new HUDBuildingsToolbar(this.root), buildingsToolbar: new HUDBuildingsToolbar(this.root),
blueprintPlacer: new HUDBlueprintPlacer(this.root),
buildingPlacer: new HUDBuildingPlacer(this.root), buildingPlacer: new HUDBuildingPlacer(this.root),
unlockNotification: new HUDUnlockNotification(this.root), unlockNotification: new HUDUnlockNotification(this.root),
gameMenu: new HUDGameMenu(this.root), gameMenu: new HUDGameMenu(this.root),
@ -66,6 +70,8 @@ export class GameHUD {
leverToggle: new HUDLeverToggle(this.root), leverToggle: new HUDLeverToggle(this.root),
constantSignalEdit: new HUDConstantSignalEdit(this.root), constantSignalEdit: new HUDConstantSignalEdit(this.root),
shapeTooltip: new HUDShapeTooltip(this.root),
// Must always exist // Must always exist
settingsMenu: new HUDSettingsMenu(this.root), settingsMenu: new HUDSettingsMenu(this.root),
debugInfo: new HUDDebugInfo(this.root), debugInfo: new HUDDebugInfo(this.root),
@ -206,6 +212,7 @@ export class GameHUD {
"colorBlindHelper", "colorBlindHelper",
"changesDebugger", "changesDebugger",
"minerHighlight", "minerHighlight",
"shapeTooltip",
]; ];
for (let i = 0; i < partsOrder.length; ++i) { for (let i = 0; i < partsOrder.length; ++i) {

View File

@ -0,0 +1,25 @@
import { makeDiv } from "../../../core/utils";
import { T } from "../../../translations";
import { PuzzlePlayGameMode } from "../../modes/puzzle_play";
import { BaseHUDPart } from "../base_hud_part";
export class HUDPuzzleNextPuzzle extends BaseHUDPart {
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_PuzzleNextPuzzle");
this.button = document.createElement("button");
this.button.classList.add("button");
this.button.innerText = T.ingame.puzzleCompletion.nextPuzzle;
this.element.appendChild(this.button);
this.trackClicks(this.button, this.nextPuzzle);
}
initialize() {}
nextPuzzle() {
const gameMode = /** @type {PuzzlePlayGameMode} */ (this.root.gameMode);
this.root.gameState.moveToState("PuzzleMenuState", {
continueQueue: gameMode.nextPuzzles,
});
}
}

View File

@ -128,7 +128,6 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
this.root.hud.signals.buildingsSelectedForCopy.add(this.abortPlacement, this); this.root.hud.signals.buildingsSelectedForCopy.add(this.abortPlacement, this);
this.root.hud.signals.pasteBlueprintRequested.add(this.abortPlacement, this); this.root.hud.signals.pasteBlueprintRequested.add(this.abortPlacement, this);
this.root.signals.storyGoalCompleted.add(() => this.signals.variantChanged.dispatch()); this.root.signals.storyGoalCompleted.add(() => this.signals.variantChanged.dispatch());
this.root.signals.storyGoalCompleted.add(() => this.currentMetaBuilding.set(null));
this.root.signals.upgradePurchased.add(() => this.signals.variantChanged.dispatch()); this.root.signals.upgradePurchased.add(() => this.signals.variantChanged.dispatch());
this.root.signals.editModeChanged.add(this.onEditModeChanged, this); this.root.signals.editModeChanged.add(this.onEditModeChanged, this);

View File

@ -158,8 +158,13 @@ export class HUDInteractiveTutorial extends BaseHUDPart {
onHintChanged(hintId) { onHintChanged(hintId) {
this.elementDescription.innerHTML = T.ingame.interactiveTutorial.hints[hintId]; this.elementDescription.innerHTML = T.ingame.interactiveTutorial.hints[hintId];
const folder = G_WEGAME_VERSION
? "interactive_tutorial.cn.noinline"
: "interactive_tutorial.noinline";
this.elementGif.style.backgroundImage = this.elementGif.style.backgroundImage =
"url('" + cachebust("res/ui/interactive_tutorial.noinline/" + hintId + ".gif") + "')"; "url('" + cachebust("res/ui/" + folder + "/" + hintId + ".gif") + "')";
this.element.classList.toggle("animEven"); this.element.classList.toggle("animEven");
this.element.classList.toggle("animOdd"); this.element.classList.toggle("animOdd");
} }

View File

@ -125,7 +125,7 @@ export class HUDModalDialogs extends BaseHUDPart {
dialog.buttonSignals.getStandalone.add(() => { dialog.buttonSignals.getStandalone.add(() => {
this.app.analytics.trackUiClick("demo_dialog_click"); this.app.analytics.trackUiClick("demo_dialog_click");
window.open(THIRDPARTY_URLS.standaloneStorePage + "?ref=ddc"); window.open(THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_demo_dialog");
}); });
return dialog.buttonSignals; return dialog.buttonSignals;

View File

@ -6,13 +6,8 @@ import { InputReceiver } from "../../../core/input_receiver";
import { makeDiv } from "../../../core/utils"; import { makeDiv } from "../../../core/utils";
import { SOUNDS } from "../../../platform/sound"; import { SOUNDS } from "../../../platform/sound";
import { T } from "../../../translations"; import { T } from "../../../translations";
import { enumColors } from "../../colors";
import { ColorItem } from "../../items/color_item";
import { finalGameShape, rocketShape } from "../../modes/regular";
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach"; import { DynamicDomAttach } from "../dynamic_dom_attach";
import { ShapeItem } from "../../items/shape_item";
import { ShapeDefinition } from "../../shape_definition";
export class HUDPuzzleCompleteNotification extends BaseHUDPart { export class HUDPuzzleCompleteNotification extends BaseHUDPart {
initialize() { initialize() {
@ -68,10 +63,21 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart {
this.menuBtn.classList.add("menu", "styledButton"); this.menuBtn.classList.add("menu", "styledButton");
this.menuBtn.innerText = T.ingame.puzzleCompletion.menuBtn; this.menuBtn.innerText = T.ingame.puzzleCompletion.menuBtn;
buttonBar.appendChild(this.menuBtn); buttonBar.appendChild(this.menuBtn);
this.trackClicks(this.menuBtn, () => { this.trackClicks(this.menuBtn, () => {
this.close(true); this.close(true);
}); });
const gameMode = /** @type {PuzzlePlayGameMode} */ (this.root.gameMode);
if (gameMode.nextPuzzles.length > 0) {
this.nextPuzzleBtn = document.createElement("button");
this.nextPuzzleBtn.classList.add("nextPuzzle", "styledButton");
this.nextPuzzleBtn.innerText = T.ingame.puzzleCompletion.nextPuzzle;
buttonBar.appendChild(this.nextPuzzleBtn);
this.trackClicks(this.nextPuzzleBtn, () => {
this.nextPuzzle();
});
}
} }
updateState() { updateState() {
@ -93,6 +99,15 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart {
return this.visible; return this.visible;
} }
nextPuzzle() {
const gameMode = /** @type {PuzzlePlayGameMode} */ (this.root.gameMode);
gameMode.trackCompleted(this.userDidLikePuzzle, Math.round(this.timeOfCompletion)).then(() => {
this.root.gameState.moveToState("PuzzleMenuState", {
continueQueue: gameMode.nextPuzzles,
});
});
}
close(toMenu) { close(toMenu) {
/** @type {PuzzlePlayGameMode} */ (this.root.gameMode) /** @type {PuzzlePlayGameMode} */ (this.root.gameMode)
.trackCompleted(this.userDidLikePuzzle, Math.round(this.timeOfCompletion)) .trackCompleted(this.userDidLikePuzzle, Math.round(this.timeOfCompletion))

View File

@ -81,7 +81,7 @@ export class HUDPuzzleEditorReview extends BaseHUDPart {
closeLoading(); closeLoading();
//if it took so little ticks that it must have autocompeted //if it took so little ticks that it must have autocompeted
if (simulatedTicks <= 300) { if (simulatedTicks <= 500) {
this.root.hud.parts.dialogs.showWarning( this.root.hud.parts.dialogs.showWarning(
T.puzzleMenu.validation.title, T.puzzleMenu.validation.title,
T.puzzleMenu.validation.autoComplete T.puzzleMenu.validation.autoComplete

View File

@ -1,5 +1,3 @@
/* typehints:start */
/* typehints:end */
import { globalConfig } from "../../../core/config"; import { globalConfig } from "../../../core/config";
import { gMetaBuildingRegistry } from "../../../core/global_registries"; import { gMetaBuildingRegistry } from "../../../core/global_registries";
import { createLogger } from "../../../core/logging"; import { createLogger } from "../../../core/logging";
@ -50,7 +48,7 @@ export class HUDPuzzleEditorSettings extends BaseHUDPart {
</div> </div>
<div class="buildingsButton"> <div class="buildingsButton">
<button class="styledButton clearBuildings">${T.ingame.puzzleEditorSettings.clearBuildings}</button> <button class="styledButton resetPuzzle">${T.ingame.puzzleEditorSettings.resetPuzzle}</button>
</div> </div>
</div>` </div>`
@ -62,7 +60,7 @@ export class HUDPuzzleEditorSettings extends BaseHUDPart {
bind(".zoneHeight .plus", () => this.modifyZone(0, 1)); bind(".zoneHeight .plus", () => this.modifyZone(0, 1));
bind("button.trim", this.trim); bind("button.trim", this.trim);
bind("button.clearItems", this.clearItems); bind("button.clearItems", this.clearItems);
bind("button.clearBuildings", this.clearBuildings); bind("button.resetPuzzle", this.resetPuzzle);
} }
} }
@ -70,9 +68,14 @@ export class HUDPuzzleEditorSettings extends BaseHUDPart {
this.root.logic.clearAllBeltsAndItems(); this.root.logic.clearAllBeltsAndItems();
} }
clearBuildings() { resetPuzzle() {
for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) { for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) {
const staticComp = entity.components.StaticMapEntity; const staticComp = entity.components.StaticMapEntity;
const goalComp = entity.components.GoalAcceptor;
if (goalComp) {
goalComp.clear();
}
if ( if (
[MetaGoalAcceptorBuilding, MetaConstantProducerBuilding, MetaBlockBuilding] [MetaGoalAcceptorBuilding, MetaConstantProducerBuilding, MetaBlockBuilding]
@ -146,8 +149,9 @@ export class HUDPuzzleEditorSettings extends BaseHUDPart {
assertAlways(false, "Failed to re-place building in trim"); assertAlways(false, "Failed to re-place building in trim");
} }
if (building.components.ConstantSignal) { for (const key in building.components) {
result.components.ConstantSignal.signal = building.components.ConstantSignal.signal; /** @type {import("../../../core/global_registries").Component} */ (building
.components[key]).copyAdditionalStateTo(result.components[key]);
} }
} }
}); });

View File

@ -1,10 +1,6 @@
import { gMetaBuildingRegistry } from "../../../core/global_registries";
import { createLogger } from "../../../core/logging"; import { createLogger } from "../../../core/logging";
import { makeDiv } from "../../../core/utils"; import { makeDiv } from "../../../core/utils";
import { T } from "../../../translations"; import { T } from "../../../translations";
import { MetaBlockBuilding } from "../../buildings/block";
import { MetaConstantProducerBuilding } from "../../buildings/constant_producer";
import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor";
import { StaticMapEntityComponent } from "../../components/static_map_entity"; import { StaticMapEntityComponent } from "../../components/static_map_entity";
import { BaseHUDPart } from "../base_hud_part"; import { BaseHUDPart } from "../base_hud_part";
@ -23,13 +19,13 @@ export class HUDPuzzlePlaySettings extends BaseHUDPart {
["section"], ["section"],
` `
<button class="styledButton clearItems">${T.ingame.puzzleEditorSettings.clearItems}</button> <button class="styledButton clearItems">${T.ingame.puzzleEditorSettings.clearItems}</button>
<button class="styledButton clearBuildings">${T.ingame.puzzleEditorSettings.resetPuzzle}</button> <button class="styledButton resetPuzzle">${T.ingame.puzzleEditorSettings.resetPuzzle}</button>
` `
); );
bind("button.clearItems", this.clearItems); bind("button.clearItems", this.clearItems);
bind("button.clearBuildings", this.clearBuildings); bind("button.resetPuzzle", this.resetPuzzle);
} }
} }
@ -37,20 +33,19 @@ export class HUDPuzzlePlaySettings extends BaseHUDPart {
this.root.logic.clearAllBeltsAndItems(); this.root.logic.clearAllBeltsAndItems();
} }
clearBuildings() { resetPuzzle() {
for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) { for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) {
const staticComp = entity.components.StaticMapEntity; const staticComp = entity.components.StaticMapEntity;
const goalComp = entity.components.GoalAcceptor;
if ( if (goalComp) {
[MetaGoalAcceptorBuilding, MetaConstantProducerBuilding, MetaBlockBuilding] goalComp.clear();
.map(metaClass => gMetaBuildingRegistry.findByClass(metaClass).id)
.includes(staticComp.getMetaBuilding().id)
) {
continue;
} }
this.root.map.removeStaticEntity(entity); if (staticComp.getMetaBuilding().getIsRemovable(this.root)) {
this.root.entityMgr.destroyEntity(entity); this.root.map.removeStaticEntity(entity);
this.root.entityMgr.destroyEntity(entity);
}
} }
this.root.entityMgr.processDestroyList(); this.root.entityMgr.processDestroyList();
} }

View File

@ -0,0 +1,100 @@
import { DrawParameters } from "../../../core/draw_parameters";
import { enumDirectionToVector, Vector } from "../../../core/vector";
import { Entity } from "../../entity";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { THEME } from "../../theme";
import { BaseHUDPart } from "../base_hud_part";
export class HUDShapeTooltip extends BaseHUDPart {
createElements(parent) {}
initialize() {
/** @type {Vector} */
this.currentTile = new Vector(0, 0);
/** @type {Entity} */
this.currentEntity = null;
this.isPlacingBuilding = false;
this.root.signals.entityQueuedForDestroy.add(() => {
this.currentEntity = null;
}, this);
this.root.hud.signals.selectedPlacementBuildingChanged.add(metaBuilding => {
this.isPlacingBuilding = metaBuilding;
}, this);
}
isActive() {
const hudParts = this.root.hud.parts;
const active =
this.root.app.settings.getSetting("shapeTooltipAlwaysOn") ||
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.showShapeTooltip).pressed;
// return false if any other placer is active
return (
active &&
!this.isPlacingBuilding &&
!hudParts.massSelector.currentSelectionStartWorld &&
hudParts.massSelector.selectedUids.size < 1 &&
!hudParts.blueprintPlacer.currentBlueprint.get()
);
}
/**
*
* @param {DrawParameters} parameters
*/
draw(parameters) {
if (this.isActive()) {
const mousePos = this.root.app.mousePosition;
if (mousePos) {
const tile = this.root.camera.screenToWorld(mousePos.copy()).toTileSpace();
if (!tile.equals(this.currentTile)) {
this.currentTile = tile;
const entity = this.root.map.getLayerContentXY(tile.x, tile.y, this.root.currentLayer);
if (entity && entity.components.ItemProcessor && entity.components.ItemEjector) {
this.currentEntity = entity;
} else {
this.currentEntity = null;
}
}
}
if (!this.currentEntity) {
return;
}
const ejectorComp = this.currentEntity.components.ItemEjector;
const staticComp = this.currentEntity.components.StaticMapEntity;
const bounds = staticComp.getTileSize();
const totalArea = bounds.x * bounds.y;
const maxSlots = totalArea < 2 ? 1 : 1e10;
let slotsDrawn = 0;
for (let i = 0; i < ejectorComp.slots.length; ++i) {
const slot = ejectorComp.slots[i];
if (!slot.lastItem) {
continue;
}
if (++slotsDrawn > maxSlots) {
continue;
}
/** @type {Vector} */
const drawPos = staticComp.localTileToWorld(slot.pos).toWorldSpaceCenterOfTile();
slot.lastItem.drawItemCenteredClipped(drawPos.x, drawPos.y, parameters, 25);
}
}
}
}

View File

@ -42,7 +42,7 @@ export class HUDStandaloneAdvantages extends BaseHUDPart {
this.trackClicks(this.contentDiv.querySelector("button.steamLinkButton"), () => { this.trackClicks(this.contentDiv.querySelector("button.steamLinkButton"), () => {
this.root.app.analytics.trackUiClick("standalone_advantage_visit_steam"); this.root.app.analytics.trackUiClick("standalone_advantage_visit_steam");
this.root.app.platformWrapper.openExternalLink( this.root.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.standaloneStorePage + "?ref=savs&prc=" + A_B_TESTING_LINK_TYPE THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_std_advg"
); );
this.close(); this.close();
}); });

View File

@ -27,7 +27,9 @@ export class HUDWatermark extends BaseHUDPart {
); );
this.trackClicks(this.linkElement, () => { this.trackClicks(this.linkElement, () => {
this.root.app.analytics.trackUiClick("watermark_click_2_direct"); this.root.app.analytics.trackUiClick("watermark_click_2_direct");
this.root.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.standaloneStorePage + "?ref=wtmd"); this.root.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_watermark"
);
}); });
} }

View File

@ -45,7 +45,7 @@ export class HUDWaypoints extends BaseHUDPart {
*/ */
createElements(parent) { createElements(parent) {
// Create the helper box on the lower right when zooming out // Create the helper box on the lower right when zooming out
if (this.root.app.settings.getAllSettings().offerHints) { if (this.root.app.settings.getAllSettings().offerHints && !G_WEGAME_VERSION) {
this.hintElement = makeDiv( this.hintElement = makeDiv(
parent, parent,
"ingame_HUD_Waypoints_Hint", "ingame_HUD_Waypoints_Hint",
@ -121,10 +121,12 @@ export class HUDWaypoints extends BaseHUDPart {
} }
// Catch mouse and key events // Catch mouse and key events
this.root.camera.downPreHandler.add(this.onMouseDown, this); if (!G_WEGAME_VERSION) {
this.root.keyMapper this.root.camera.downPreHandler.add(this.onMouseDown, this);
.getBinding(KEYMAPPINGS.navigation.createMarker) this.root.keyMapper
.add(() => this.requestSaveMarker({})); .getBinding(KEYMAPPINGS.navigation.createMarker)
.add(() => this.requestSaveMarker({}));
}
/** /**
* Stores at how much opacity the markers should be rendered on the map. * Stores at how much opacity the markers should be rendered on the map.

View File

@ -33,6 +33,8 @@ export const KEYMAPPINGS = {
toggleFPSInfo: { keyCode: 115 }, // F4 toggleFPSInfo: { keyCode: 115 }, // F4
switchLayers: { keyCode: key("E") }, switchLayers: { keyCode: key("E") },
showShapeTooltip: { keyCode: 18 }, // ALT
}, },
navigation: { navigation: {

View File

@ -80,6 +80,15 @@ export class GameLogic {
} }
// Perform additional placement checks // Perform additional placement checks
if (this.root.gameMode.getIsEditor()) {
const toolbar = this.root.hud.parts.buildingsToolbar;
const id = entity.components.StaticMapEntity.getMetaBuilding().getId();
if (toolbar.buildingHandles[id].puzzleLocked) {
return false;
}
}
if (this.root.signals.prePlacementCheck.dispatch(entity, offset) === STOP_PROPAGATION) { if (this.root.signals.prePlacementCheck.dispatch(entity, offset) === STOP_PROPAGATION) {
return false; return false;
} }

View File

@ -3,6 +3,7 @@ import { Vector } from "../core/vector";
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
import { BaseItem } from "./base_item"; import { BaseItem } from "./base_item";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { MapChunkAggregate } from "./map_chunk_aggregate";
import { MapChunkView } from "./map_chunk_view"; import { MapChunkView } from "./map_chunk_view";
import { GameRoot } from "./root"; import { GameRoot } from "./root";
@ -31,6 +32,11 @@ export class BaseMap extends BasicSerializableObject {
* Mapping of 'X|Y' to chunk * Mapping of 'X|Y' to chunk
* @type {Map<string, MapChunkView>} */ * @type {Map<string, MapChunkView>} */
this.chunksById = new Map(); this.chunksById = new Map();
/**
* Mapping of 'X|Y' to chunk aggregate
* @type {Map<string, MapChunkAggregate>} */
this.aggregatesById = new Map();
} }
/** /**
@ -55,6 +61,39 @@ export class BaseMap extends BasicSerializableObject {
return null; return null;
} }
/**
* Returns the chunk aggregate containing a given chunk
* @param {number} chunkX
* @param {number} chunkY
*/
getAggregateForChunk(chunkX, chunkY, createIfNotExistent = false) {
const aggX = Math.floor(chunkX / globalConfig.chunkAggregateSize);
const aggY = Math.floor(chunkY / globalConfig.chunkAggregateSize);
return this.getAggregate(aggX, aggY, createIfNotExistent);
}
/**
* Returns the given chunk aggregate by index
* @param {number} aggX
* @param {number} aggY
*/
getAggregate(aggX, aggY, createIfNotExistent = false) {
const aggIdentifier = aggX + "|" + aggY;
let storedAggregate;
if ((storedAggregate = this.aggregatesById.get(aggIdentifier))) {
return storedAggregate;
}
if (createIfNotExistent) {
const instance = new MapChunkAggregate(this.root, aggX, aggY);
this.aggregatesById.set(aggIdentifier, instance);
return instance;
}
return null;
}
/** /**
* Gets or creates a new chunk if not existent for the given tile * Gets or creates a new chunk if not existent for the given tile
* @param {number} tileX * @param {number} tileX

View File

@ -0,0 +1,154 @@
import { globalConfig } from "../core/config";
import { DrawParameters } from "../core/draw_parameters";
import { drawSpriteClipped } from "../core/draw_utils";
import { safeModulo } from "../core/utils";
import { GameRoot } from "./root";
export const CHUNK_OVERLAY_RES = 3;
export class MapChunkAggregate {
/**
*
* @param {GameRoot} root
* @param {number} x
* @param {number} y
*/
constructor(root, x, y) {
this.root = root;
this.x = x;
this.y = y;
/**
* Whenever something changes, we increase this number - so we know we need to redraw
*/
this.renderIteration = 0;
this.dirty = false;
/** @type {Array<boolean>} */
this.dirtyList = new Array(globalConfig.chunkAggregateSize ** 2).fill(true);
this.markDirty(0, 0);
}
/**
* Marks this chunk as dirty, rerendering all caches
* @param {number} chunkX
* @param {number} chunkY
*/
markDirty(chunkX, chunkY) {
const relX = safeModulo(chunkX, globalConfig.chunkAggregateSize);
const relY = safeModulo(chunkY, globalConfig.chunkAggregateSize);
this.dirtyList[relY * globalConfig.chunkAggregateSize + relX] = true;
if (this.dirty) {
return;
}
this.dirty = true;
++this.renderIteration;
this.renderKey = this.x + "/" + this.y + "@" + this.renderIteration;
}
/**
*
* @param {HTMLCanvasElement} canvas
* @param {CanvasRenderingContext2D} context
* @param {number} w
* @param {number} h
* @param {number} dpi
*/
generateOverlayBuffer(canvas, context, w, h, dpi) {
const prevKey = this.x + "/" + this.y + "@" + (this.renderIteration - 1);
const prevBuffer = this.root.buffers.getForKeyOrNullNoUpdate({
key: "agg@" + this.root.currentLayer,
subKey: prevKey,
});
const overlaySize = globalConfig.mapChunkSize * CHUNK_OVERLAY_RES;
let onlyDirty = false;
if (prevBuffer) {
context.drawImage(prevBuffer, 0, 0);
onlyDirty = true;
}
for (let x = 0; x < globalConfig.chunkAggregateSize; x++) {
for (let y = 0; y < globalConfig.chunkAggregateSize; y++) {
if (onlyDirty && !this.dirtyList[globalConfig.chunkAggregateSize * y + x]) continue;
this.root.map
.getChunk(
this.x * globalConfig.chunkAggregateSize + x,
this.y * globalConfig.chunkAggregateSize + y,
true
)
.generateOverlayBuffer(
context,
overlaySize,
overlaySize,
x * overlaySize,
y * overlaySize
);
}
}
this.dirty = false;
this.dirtyList.fill(false);
}
/**
* Overlay
* @param {DrawParameters} parameters
*/
drawOverlay(parameters) {
const aggregateOverlaySize =
globalConfig.mapChunkSize * globalConfig.chunkAggregateSize * CHUNK_OVERLAY_RES;
const sprite = this.root.buffers.getForKey({
key: "agg@" + this.root.currentLayer,
subKey: this.renderKey,
w: aggregateOverlaySize,
h: aggregateOverlaySize,
dpi: 1,
redrawMethod: this.generateOverlayBuffer.bind(this),
});
const dims = globalConfig.mapChunkWorldSize * globalConfig.chunkAggregateSize;
const extrude = 0.05;
// Draw chunk "pixel" art
parameters.context.imageSmoothingEnabled = false;
drawSpriteClipped({
parameters,
sprite,
x: this.x * dims - extrude,
y: this.y * dims - extrude,
w: dims + 2 * extrude,
h: dims + 2 * extrude,
originalW: aggregateOverlaySize,
originalH: aggregateOverlaySize,
});
parameters.context.imageSmoothingEnabled = true;
const resourcesScale = this.root.app.settings.getAllSettings().mapResourcesScale;
// Draw patch items
if (
this.root.currentLayer === "regular" &&
resourcesScale > 0.05 &&
this.root.camera.zoomLevel > 0.1
) {
const diameter = (70 / Math.pow(parameters.zoomLevel, 0.35)) * (0.2 + 2 * resourcesScale);
for (let x = 0; x < globalConfig.chunkAggregateSize; x++) {
for (let y = 0; y < globalConfig.chunkAggregateSize; y++) {
this.root.map
.getChunk(
this.x * globalConfig.chunkAggregateSize + x,
this.y * globalConfig.chunkAggregateSize + y,
true
)
.drawOverlayPatches(
parameters,
this.x * dims + x * globalConfig.mapChunkWorldSize,
this.y * dims + y * globalConfig.mapChunkWorldSize,
diameter
);
}
}
}
}
}

View File

@ -33,6 +33,7 @@ export class MapChunkView extends MapChunk {
markDirty() { markDirty() {
++this.renderIteration; ++this.renderIteration;
this.renderKey = this.x + "/" + this.y + "@" + this.renderIteration; this.renderKey = this.x + "/" + this.y + "@" + this.renderIteration;
this.root.map.getAggregateForChunk(this.x, this.y, true).markDirty(this.x, this.y);
} }
/** /**
@ -82,73 +83,41 @@ export class MapChunkView extends MapChunk {
} }
/** /**
* Overlay
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
* @param {number} xoffs
* @param {number} yoffs
* @param {number} diameter
*/ */
drawOverlay(parameters) { drawOverlayPatches(parameters, xoffs, yoffs, diameter) {
const overlaySize = globalConfig.mapChunkSize * CHUNK_OVERLAY_RES; for (let i = 0; i < this.patches.length; ++i) {
const sprite = this.root.buffers.getForKey({ const patch = this.patches[i];
key: "chunk@" + this.root.currentLayer, if (patch.item.getItemType() === "shape") {
subKey: this.renderKey, const destX = xoffs + patch.pos.x * globalConfig.tileSize;
w: overlaySize, const destY = yoffs + patch.pos.y * globalConfig.tileSize;
h: overlaySize, patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
dpi: 1,
redrawMethod: this.generateOverlayBuffer.bind(this),
});
const dims = globalConfig.mapChunkWorldSize;
const extrude = 0.05;
// Draw chunk "pixel" art
parameters.context.imageSmoothingEnabled = false;
drawSpriteClipped({
parameters,
sprite,
x: this.x * dims - extrude,
y: this.y * dims - extrude,
w: dims + 2 * extrude,
h: dims + 2 * extrude,
originalW: overlaySize,
originalH: overlaySize,
});
parameters.context.imageSmoothingEnabled = true;
const resourcesScale = this.root.app.settings.getAllSettings().mapResourcesScale;
// Draw patch items
if (this.root.currentLayer === "regular" && resourcesScale > 0.05) {
const diameter = (70 / Math.pow(parameters.zoomLevel, 0.35)) * (0.2 + 2 * resourcesScale);
for (let i = 0; i < this.patches.length; ++i) {
const patch = this.patches[i];
if (patch.item.getItemType() === "shape") {
const destX = this.x * dims + patch.pos.x * globalConfig.tileSize;
const destY = this.y * dims + patch.pos.y * globalConfig.tileSize;
patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
}
} }
} }
} }
/** /**
* *
* @param {HTMLCanvasElement} canvas
* @param {CanvasRenderingContext2D} context * @param {CanvasRenderingContext2D} context
* @param {number} w * @param {number} w
* @param {number} h * @param {number} h
* @param {number} dpi * @param {number=} xoffs
* @param {number=} yoffs
*/ */
generateOverlayBuffer(canvas, context, w, h, dpi) { generateOverlayBuffer(context, w, h, xoffs, yoffs) {
context.fillStyle = context.fillStyle =
this.containedEntities.length > 0 this.containedEntities.length > 0
? THEME.map.chunkOverview.filled ? THEME.map.chunkOverview.filled
: THEME.map.chunkOverview.empty; : THEME.map.chunkOverview.empty;
context.fillRect(0, 0, w, h); context.fillRect(xoffs, yoffs, w, h);
if (this.root.app.settings.getAllSettings().displayChunkBorders) { if (this.root.app.settings.getAllSettings().displayChunkBorders) {
context.fillStyle = THEME.map.chunkBorders; context.fillStyle = THEME.map.chunkBorders;
context.fillRect(0, 0, w, 1); context.fillRect(xoffs, yoffs, w, 1);
context.fillRect(0, 1, 1, h); context.fillRect(xoffs, yoffs + 1, 1, h);
} }
for (let x = 0; x < globalConfig.mapChunkSize; ++x) { for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
@ -174,8 +143,8 @@ export class MapChunkView extends MapChunk {
if (lowerContent) { if (lowerContent) {
context.fillStyle = lowerContent.getBackgroundColorAsResource(); context.fillStyle = lowerContent.getBackgroundColorAsResource();
context.fillRect( context.fillRect(
x * CHUNK_OVERLAY_RES, xoffs + x * CHUNK_OVERLAY_RES,
y * CHUNK_OVERLAY_RES, yoffs + y * CHUNK_OVERLAY_RES,
CHUNK_OVERLAY_RES, CHUNK_OVERLAY_RES,
CHUNK_OVERLAY_RES CHUNK_OVERLAY_RES
); );
@ -190,8 +159,8 @@ export class MapChunkView extends MapChunk {
const isFilled = overlayMatrix[dx + dy * 3]; const isFilled = overlayMatrix[dx + dy * 3];
if (isFilled) { if (isFilled) {
context.fillRect( context.fillRect(
x * CHUNK_OVERLAY_RES + dx, xoffs + x * CHUNK_OVERLAY_RES + dx,
y * CHUNK_OVERLAY_RES + dy, yoffs + y * CHUNK_OVERLAY_RES + dy,
1, 1,
1 1
); );
@ -206,8 +175,8 @@ export class MapChunkView extends MapChunk {
data.rotationVariant data.rotationVariant
); );
context.fillRect( context.fillRect(
x * CHUNK_OVERLAY_RES, xoffs + x * CHUNK_OVERLAY_RES,
y * CHUNK_OVERLAY_RES, yoffs + y * CHUNK_OVERLAY_RES,
CHUNK_OVERLAY_RES, CHUNK_OVERLAY_RES,
CHUNK_OVERLAY_RES CHUNK_OVERLAY_RES
); );
@ -220,8 +189,8 @@ export class MapChunkView extends MapChunk {
if (lowerContent) { if (lowerContent) {
context.fillStyle = lowerContent.getBackgroundColorAsResource(); context.fillStyle = lowerContent.getBackgroundColorAsResource();
context.fillRect( context.fillRect(
x * CHUNK_OVERLAY_RES, xoffs + x * CHUNK_OVERLAY_RES,
y * CHUNK_OVERLAY_RES, yoffs + y * CHUNK_OVERLAY_RES,
CHUNK_OVERLAY_RES, CHUNK_OVERLAY_RES,
CHUNK_OVERLAY_RES CHUNK_OVERLAY_RES
); );
@ -233,7 +202,7 @@ export class MapChunkView extends MapChunk {
// Draw wires overlay // Draw wires overlay
context.fillStyle = THEME.map.wires.overlayColor; context.fillStyle = THEME.map.wires.overlayColor;
context.fillRect(0, 0, w, h); context.fillRect(xoffs, yoffs, w, h);
for (let x = 0; x < globalConfig.mapChunkSize; ++x) { for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
const wiresArray = this.wireContents[x]; const wiresArray = this.wireContents[x];
@ -244,8 +213,8 @@ export class MapChunkView extends MapChunk {
} }
MapChunkView.drawSingleWiresOverviewTile({ MapChunkView.drawSingleWiresOverviewTile({
context, context,
x: x * CHUNK_OVERLAY_RES, x: xoffs + x * CHUNK_OVERLAY_RES,
y: y * CHUNK_OVERLAY_RES, y: yoffs + y * CHUNK_OVERLAY_RES,
entity: content, entity: content,
tileSizePixels: CHUNK_OVERLAY_RES, tileSizePixels: CHUNK_OVERLAY_RES,
}); });

View File

@ -5,6 +5,7 @@ import { freeCanvas, makeOffscreenBuffer } from "../core/buffer_utils";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { THEME } from "./theme"; import { THEME } from "./theme";
import { MapChunkView } from "./map_chunk_view"; import { MapChunkView } from "./map_chunk_view";
import { MapChunkAggregate } from "./map_chunk_aggregate";
/** /**
* This is the view of the map, it extends the map which is the raw model and allows * This is the view of the map, it extends the map which is the raw model and allows
@ -164,6 +165,40 @@ export class MapView extends BaseMap {
} }
} }
/**
* Calls a given method on all given chunks
* @param {DrawParameters} parameters
* @param {function} method
*/
drawVisibleAggregates(parameters, method) {
const cullRange = parameters.visibleRect.allScaled(1 / globalConfig.tileSize);
const top = cullRange.top();
const right = cullRange.right();
const bottom = cullRange.bottom();
const left = cullRange.left();
const border = 0;
const minY = top - border;
const maxY = bottom + border;
const minX = left - border;
const maxX = right + border;
const aggregateTiles = globalConfig.chunkAggregateSize * globalConfig.mapChunkSize;
const aggStartX = Math.floor(minX / aggregateTiles);
const aggStartY = Math.floor(minY / aggregateTiles);
const aggEndX = Math.floor(maxX / aggregateTiles);
const aggEndY = Math.floor(maxY / aggregateTiles);
// Render y from top down for proper blending
for (let aggX = aggStartX; aggX <= aggEndX; ++aggX) {
for (let aggY = aggStartY; aggY <= aggEndY; ++aggY) {
const aggregate = this.root.map.getAggregate(aggX, aggY, true);
method.call(aggregate, parameters);
}
}
}
/** /**
* Draws the wires foreground * Draws the wires foreground
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
@ -177,7 +212,7 @@ export class MapView extends BaseMap {
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
*/ */
drawOverlay(parameters) { drawOverlay(parameters) {
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawOverlay); this.drawVisibleAggregates(parameters, MapChunkAggregate.prototype.drawOverlay);
} }
/** /**

View File

@ -7,7 +7,6 @@ import { types } from "../../savegame/serialization";
import { enumGameModeTypes, GameMode } from "../game_mode"; import { enumGameModeTypes, GameMode } from "../game_mode";
import { HUDPuzzleBackToMenu } from "../hud/parts/puzzle_back_to_menu"; import { HUDPuzzleBackToMenu } from "../hud/parts/puzzle_back_to_menu";
import { HUDPuzzleDLCLogo } from "../hud/parts/puzzle_dlc_logo"; import { HUDPuzzleDLCLogo } from "../hud/parts/puzzle_dlc_logo";
import { HUDBlueprintPlacer } from "../hud/parts/blueprint_placer";
import { HUDMassSelector } from "../hud/parts/mass_selector"; import { HUDMassSelector } from "../hud/parts/mass_selector";
export class PuzzleGameMode extends GameMode { export class PuzzleGameMode extends GameMode {
@ -32,7 +31,6 @@ export class PuzzleGameMode extends GameMode {
this.additionalHudParts = { this.additionalHudParts = {
puzzleBackToMenu: HUDPuzzleBackToMenu, puzzleBackToMenu: HUDPuzzleBackToMenu,
puzzleDlcLogo: HUDPuzzleDLCLogo, puzzleDlcLogo: HUDPuzzleDLCLogo,
blueprintPlacer: HUDBlueprintPlacer,
massSelector: HUDMassSelector, massSelector: HUDMassSelector,
}; };

View File

@ -30,6 +30,7 @@ import { HUDPuzzlePlaySettings } from "../hud/parts/puzzle_play_settings";
import { MetaBlockBuilding } from "../buildings/block"; import { MetaBlockBuilding } from "../buildings/block";
import { MetaBuilding } from "../meta_building"; import { MetaBuilding } from "../meta_building";
import { gMetaBuildingRegistry } from "../../core/global_registries"; import { gMetaBuildingRegistry } from "../../core/global_registries";
import { HUDPuzzleNextPuzzle } from "../hud/parts/HUDPuzzleNextPuzzle";
const logger = createLogger("puzzle-play"); const logger = createLogger("puzzle-play");
const copy = require("clipboard-copy"); const copy = require("clipboard-copy");
@ -43,8 +44,9 @@ export class PuzzlePlayGameMode extends PuzzleGameMode {
* @param {GameRoot} root * @param {GameRoot} root
* @param {object} payload * @param {object} payload
* @param {import("../../savegame/savegame_typedefs").PuzzleFullData} payload.puzzle * @param {import("../../savegame/savegame_typedefs").PuzzleFullData} payload.puzzle
* @param {Array<number> | undefined} payload.nextPuzzles
*/ */
constructor(root, { puzzle }) { constructor(root, { puzzle, nextPuzzles }) {
super(root); super(root);
/** @type {Array<typeof MetaBuilding>} */ /** @type {Array<typeof MetaBuilding>} */
@ -95,6 +97,15 @@ export class PuzzlePlayGameMode extends PuzzleGameMode {
root.signals.postLoadHook.add(this.loadPuzzle, this); root.signals.postLoadHook.add(this.loadPuzzle, this);
this.puzzle = puzzle; this.puzzle = puzzle;
/**
* @type {Array<number>}
*/
this.nextPuzzles = nextPuzzles || [];
if (this.nextPuzzles.length > 0) {
this.additionalHudParts.puzzleNext = HUDPuzzleNextPuzzle;
}
} }
loadPuzzle() { loadPuzzle() {

View File

@ -10,7 +10,6 @@ import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode";
import { ShapeDefinition } from "../shape_definition"; import { ShapeDefinition } from "../shape_definition";
import { enumHubGoalRewards } from "../tutorial_goals"; import { enumHubGoalRewards } from "../tutorial_goals";
import { HUDWiresToolbar } from "../hud/parts/wires_toolbar"; import { HUDWiresToolbar } from "../hud/parts/wires_toolbar";
import { HUDBlueprintPlacer } from "../hud/parts/blueprint_placer";
import { HUDUnlockNotification } from "../hud/parts/unlock_notification"; import { HUDUnlockNotification } from "../hud/parts/unlock_notification";
import { HUDMassSelector } from "../hud/parts/mass_selector"; import { HUDMassSelector } from "../hud/parts/mass_selector";
import { HUDShop } from "../hud/parts/shop"; import { HUDShop } from "../hud/parts/shop";
@ -343,63 +342,63 @@ export function generateLevelDefinitions(limitedVersion = false) {
reward: enumHubGoalRewards.reward_rotater_ccw, 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: chinaShapes ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", // painting t3
required: 3800,
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
},
// DEMO STOPS HERE // DEMO STOPS HERE
...(limitedVersion ...(limitedVersion
? [ ? [
{ {
shape: chinaShapes ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", shape: "CrCrCrCr",
required: 0, required: 0,
reward: enumHubGoalRewards.reward_demo_end, reward: enumHubGoalRewards.reward_demo_end,
}, },
] ]
: [ : [
// 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: chinaShapes ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", // painting t3
required: 3800,
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
},
// 14 // 14
// Belt reader // Belt reader
{ {
@ -546,7 +545,6 @@ export class RegularGameMode extends GameMode {
this.additionalHudParts = { this.additionalHudParts = {
wiresToolbar: HUDWiresToolbar, wiresToolbar: HUDWiresToolbar,
blueprintPlacer: HUDBlueprintPlacer,
unlockNotification: HUDUnlockNotification, unlockNotification: HUDUnlockNotification,
massSelector: HUDMassSelector, massSelector: HUDMassSelector,
shop: HUDShop, shop: HUDShop,
@ -577,7 +575,9 @@ export class RegularGameMode extends GameMode {
} }
if (this.root.app.settings.getAllSettings().offerHints) { if (this.root.app.settings.getAllSettings().offerHints) {
this.additionalHudParts.tutorialHints = HUDPartTutorialHints; if (!G_WEGAME_VERSION) {
this.additionalHudParts.tutorialHints = HUDPartTutorialHints;
}
this.additionalHudParts.interactiveTutorial = HUDInteractiveTutorial; this.additionalHudParts.interactiveTutorial = HUDInteractiveTutorial;
} }
@ -587,12 +587,12 @@ export class RegularGameMode extends GameMode {
} }
/** @type {(typeof MetaBuilding)[]} */ /** @type {(typeof MetaBuilding)[]} */
this.hiddenBuildings = [ this.hiddenBuildings = [MetaConstantProducerBuilding, MetaGoalAcceptorBuilding, MetaBlockBuilding];
MetaConstantProducerBuilding,
MetaGoalAcceptorBuilding, // @ts-expect-error
MetaBlockBuilding, if (!(G_IS_DEV || window.sandboxMode || queryParamOptions.sandboxMode)) {
MetaItemProducerBuilding, this.hiddenBuildings.push(MetaItemProducerBuilding);
]; }
} }
/** /**

View File

@ -49,11 +49,12 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
// Ok, query, but also save the uid because it could get stale // Ok, query, but also save the uid because it could get stale
const uid = entity.uid; const uid = entity.uid;
const signal = entity.components.ConstantSignal.signal;
const signalValueInput = new FormElementInput({ const signalValueInput = new FormElementInput({
id: "signalValue", id: "signalValue",
label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer), label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer),
placeholder: "", placeholder: "",
defaultValue: "", defaultValue: signal ? signal.getAsCopyableKey() : "",
validator: val => this.parseSignalCode(entity, val), validator: val => this.parseSignalCode(entity, val),
}); });

View File

@ -67,8 +67,9 @@ export class GoalAcceptorSystem extends GameSystemWithFilter {
const staticComp = contents[i].components.StaticMapEntity; const staticComp = contents[i].components.StaticMapEntity;
const item = goalComp.item; const item = goalComp.item;
const requiredItemsForSuccess = globalConfig.goalAcceptorItemsRequired; const requiredItems = globalConfig.goalAcceptorItemsRequired;
const percentage = clamp(goalComp.currentDeliveredItems / requiredItemsForSuccess, 0, 1);
const fillPercentage = clamp(goalComp.currentDeliveredItems / requiredItems, 0, 1);
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
if (item) { if (item) {
@ -81,7 +82,7 @@ export class GoalAcceptorSystem extends GameSystemWithFilter {
); );
} }
const isValid = item && goalComp.currentDeliveredItems >= requiredItemsForSuccess; const isValid = item && goalComp.currentDeliveredItems >= requiredItems;
parameters.context.translate(center.x, center.y); parameters.context.translate(center.x, center.y);
parameters.context.rotate((staticComp.rotation / 180) * Math.PI); parameters.context.rotate((staticComp.rotation / 180) * Math.PI);
@ -93,7 +94,7 @@ export class GoalAcceptorSystem extends GameSystemWithFilter {
// progress arc // progress arc
goalComp.displayPercentage = lerp(goalComp.displayPercentage, percentage, 0.2); goalComp.displayPercentage = lerp(goalComp.displayPercentage, fillPercentage, 0.2);
const startAngle = Math.PI * 0.595; const startAngle = Math.PI * 0.595;
const maxAngle = Math.PI * 1.82; const maxAngle = Math.PI * 1.82;

View File

@ -32,8 +32,8 @@ const MAX_QUEUED_CHARGES = 2;
* Type of a processor implementation * Type of a processor implementation
* @typedef {{ * @typedef {{
* entity: Entity, * entity: Entity,
* items: Array<{ item: BaseItem, sourceSlot: number }>, * items: Map<number, BaseItem>,
* itemsBySlot: Object<number, BaseItem>, * inputCount: number,
* outItems: Array<ProducedItem> * outItems: Array<ProducedItem>
* }} ProcessorImplementationPayload * }} ProcessorImplementationPayload
*/ */
@ -189,7 +189,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// DEFAULT // DEFAULT
// By default, we can start processing once all inputs are there // By default, we can start processing once all inputs are there
case null: { case null: {
return processorComp.inputSlots.length >= processorComp.inputsPerCharge; return processorComp.inputCount >= processorComp.inputsPerCharge;
} }
// QUAD PAINTER // QUAD PAINTER
@ -197,18 +197,12 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
case enumItemProcessorRequirements.painterQuad: { case enumItemProcessorRequirements.painterQuad: {
const pinsComp = entity.components.WiredPins; const pinsComp = entity.components.WiredPins;
/** @type {Object.<number, { item: BaseItem, sourceSlot: number }>} */
const itemsBySlot = {};
for (let i = 0; i < processorComp.inputSlots.length; ++i) {
itemsBySlot[processorComp.inputSlots[i].sourceSlot] = processorComp.inputSlots[i];
}
// First slot is the shape, so if it's not there we can't do anything // First slot is the shape, so if it's not there we can't do anything
if (!itemsBySlot[0]) { const shapeItem = /** @type {ShapeItem} */ (processorComp.inputSlots.get(0));
if (!shapeItem) {
return false; return false;
} }
const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
const slotStatus = []; const slotStatus = [];
// Check which slots are enabled // Check which slots are enabled
@ -233,7 +227,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// Check if all colors of the enabled slots are there // Check if all colors of the enabled slots are there
for (let i = 0; i < slotStatus.length; ++i) { for (let i = 0; i < slotStatus.length; ++i) {
if (slotStatus[i] && !itemsBySlot[1 + i]) { if (slotStatus[i] && !processorComp.inputSlots.get(1 + i)) {
// A slot which is enabled wasn't enabled. Make sure if there is anything on the quadrant, // A slot which is enabled wasn't enabled. Make sure if there is anything on the quadrant,
// it is not possible to paint, but if there is nothing we can ignore it // it is not possible to paint, but if there is nothing we can ignore it
for (let j = 0; j < 4; ++j) { for (let j = 0; j < 4; ++j) {
@ -262,13 +256,6 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// First, take items // First, take items
const items = processorComp.inputSlots; const items = processorComp.inputSlots;
processorComp.inputSlots = [];
/** @type {Object<string, BaseItem>} */
const itemsBySlot = {};
for (let i = 0; i < items.length; ++i) {
itemsBySlot[items[i].sourceSlot] = items[i].item;
}
/** @type {Array<ProducedItem>} */ /** @type {Array<ProducedItem>} */
const outItems = []; const outItems = [];
@ -281,8 +268,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
handler({ handler({
entity, entity,
items, items,
itemsBySlot,
outItems, outItems,
inputCount: processorComp.inputCount,
}); });
// Track produced items // Track produced items
@ -304,6 +291,9 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
items: outItems, items: outItems,
remainingTime: timeToProcess, remainingTime: timeToProcess,
}); });
processorComp.inputSlots.clear();
processorComp.inputCount = 0;
} }
/** /**
@ -317,12 +307,14 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const availableSlots = payload.entity.components.ItemEjector.slots.length; const availableSlots = payload.entity.components.ItemEjector.slots.length;
const processorComp = payload.entity.components.ItemProcessor; const processorComp = payload.entity.components.ItemProcessor;
const nextSlot = processorComp.nextOutputSlot++ % availableSlots; for (let i = 0; i < 2; ++i) {
const item = payload.items.get(i);
for (let i = 0; i < payload.items.length; ++i) { if (!item) {
continue;
}
payload.outItems.push({ payload.outItems.push({
item: payload.items[i].item, item,
preferredSlot: (nextSlot + i) % availableSlots, preferredSlot: processorComp.nextOutputSlot++ % availableSlots,
doNotTrack: true, doNotTrack: true,
}); });
} }
@ -333,20 +325,25 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_CUTTER(payload) { process_CUTTER(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items[0].item); const inputItem = /** @type {ShapeItem} */ (payload.items.get(0));
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape"); assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
const inputDefinition = inputItem.definition; const inputDefinition = inputItem.definition;
const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutHalf(inputDefinition); const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutHalf(inputDefinition);
const ejectorComp = payload.entity.components.ItemEjector;
for (let i = 0; i < cutDefinitions.length; ++i) { for (let i = 0; i < cutDefinitions.length; ++i) {
const definition = cutDefinitions[i]; const definition = cutDefinitions[i];
if (!definition.isEntirelyEmpty()) {
payload.outItems.push({ if (definition.isEntirelyEmpty()) {
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition), ejectorComp.slots[i].lastItem = null;
requiredSlot: i, continue;
});
} }
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
requiredSlot: i,
});
} }
} }
@ -354,20 +351,25 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_CUTTER_QUAD(payload) { process_CUTTER_QUAD(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items[0].item); const inputItem = /** @type {ShapeItem} */ (payload.items.get(0));
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape"); assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
const inputDefinition = inputItem.definition; const inputDefinition = inputItem.definition;
const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutQuad(inputDefinition); const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutQuad(inputDefinition);
const ejectorComp = payload.entity.components.ItemEjector;
for (let i = 0; i < cutDefinitions.length; ++i) { for (let i = 0; i < cutDefinitions.length; ++i) {
const definition = cutDefinitions[i]; const definition = cutDefinitions[i];
if (!definition.isEntirelyEmpty()) {
payload.outItems.push({ if (definition.isEntirelyEmpty()) {
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition), ejectorComp.slots[i].lastItem = null;
requiredSlot: i, continue;
});
} }
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
requiredSlot: i,
});
} }
} }
@ -375,7 +377,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_ROTATER(payload) { process_ROTATER(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items[0].item); const inputItem = /** @type {ShapeItem} */ (payload.items.get(0));
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition; const inputDefinition = inputItem.definition;
@ -389,7 +391,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_ROTATER_CCW(payload) { process_ROTATER_CCW(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items[0].item); const inputItem = /** @type {ShapeItem} */ (payload.items.get(0));
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition; const inputDefinition = inputItem.definition;
@ -403,7 +405,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_ROTATER_180(payload) { process_ROTATER_180(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items[0].item); const inputItem = /** @type {ShapeItem} */ (payload.items.get(0));
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition; const inputDefinition = inputItem.definition;
@ -417,8 +419,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_STACKER(payload) { process_STACKER(payload) {
const lowerItem = /** @type {ShapeItem} */ (payload.itemsBySlot[0]); const lowerItem = /** @type {ShapeItem} */ (payload.items.get(0));
const upperItem = /** @type {ShapeItem} */ (payload.itemsBySlot[1]); const upperItem = /** @type {ShapeItem} */ (payload.items.get(1));
assert(lowerItem instanceof ShapeItem, "Input for lower stack is not a shape"); assert(lowerItem instanceof ShapeItem, "Input for lower stack is not a shape");
assert(upperItem instanceof ShapeItem, "Input for upper stack is not a shape"); assert(upperItem instanceof ShapeItem, "Input for upper stack is not a shape");
@ -444,8 +446,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
*/ */
process_MIXER(payload) { process_MIXER(payload) {
// Find both colors and combine them // Find both colors and combine them
const item1 = /** @type {ColorItem} */ (payload.items[0].item); const item1 = /** @type {ColorItem} */ (payload.items.get(0));
const item2 = /** @type {ColorItem} */ (payload.items[1].item); const item2 = /** @type {ColorItem} */ (payload.items.get(1));
assert(item1 instanceof ColorItem, "Input for color mixer is not a color"); assert(item1 instanceof ColorItem, "Input for color mixer is not a color");
assert(item2 instanceof ColorItem, "Input for color mixer is not a color"); assert(item2 instanceof ColorItem, "Input for color mixer is not a color");
@ -467,8 +469,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_PAINTER(payload) { process_PAINTER(payload) {
const shapeItem = /** @type {ShapeItem} */ (payload.itemsBySlot[0]); const shapeItem = /** @type {ShapeItem} */ (payload.items.get(0));
const colorItem = /** @type {ColorItem} */ (payload.itemsBySlot[1]); const colorItem = /** @type {ColorItem} */ (payload.items.get(1));
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith( const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith(
shapeItem.definition, shapeItem.definition,
@ -484,9 +486,9 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_PAINTER_DOUBLE(payload) { process_PAINTER_DOUBLE(payload) {
const shapeItem1 = /** @type {ShapeItem} */ (payload.itemsBySlot[0]); const shapeItem1 = /** @type {ShapeItem} */ (payload.items.get(0));
const shapeItem2 = /** @type {ShapeItem} */ (payload.itemsBySlot[1]); const shapeItem2 = /** @type {ShapeItem} */ (payload.items.get(1));
const colorItem = /** @type {ColorItem} */ (payload.itemsBySlot[2]); const colorItem = /** @type {ColorItem} */ (payload.items.get(2));
assert(shapeItem1 instanceof ShapeItem, "Input for painter is not a shape"); assert(shapeItem1 instanceof ShapeItem, "Input for painter is not a shape");
assert(shapeItem2 instanceof ShapeItem, "Input for painter is not a shape"); assert(shapeItem2 instanceof ShapeItem, "Input for painter is not a shape");
@ -514,14 +516,15 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_PAINTER_QUAD(payload) { process_PAINTER_QUAD(payload) {
const shapeItem = /** @type {ShapeItem} */ (payload.itemsBySlot[0]); const shapeItem = /** @type {ShapeItem} */ (payload.items.get(0));
assert(shapeItem instanceof ShapeItem, "Input for painter is not a shape"); assert(shapeItem instanceof ShapeItem, "Input for painter is not a shape");
/** @type {Array<enumColors>} */ /** @type {Array<enumColors>} */
const colors = [null, null, null, null]; const colors = [null, null, null, null];
for (let i = 0; i < 4; ++i) { for (let i = 0; i < 4; ++i) {
if (payload.itemsBySlot[i + 1]) { const colorItem = /** @type {ColorItem} */ (payload.items.get(i + 1));
colors[i] = /** @type {ColorItem} */ (payload.itemsBySlot[i + 1]).color; if (colorItem) {
colors[i] = colorItem.color;
} }
} }
@ -540,7 +543,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
*/ */
process_READER(payload) { process_READER(payload) {
// Pass through the item // Pass through the item
const item = payload.itemsBySlot[0]; const item = payload.items.get(0);
payload.outItems.push({ payload.outItems.push({
item, item,
doNotTrack: true, doNotTrack: true,
@ -559,8 +562,12 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const hubComponent = payload.entity.components.Hub; const hubComponent = payload.entity.components.Hub;
assert(hubComponent, "Hub item processor has no hub component"); assert(hubComponent, "Hub item processor has no hub component");
for (let i = 0; i < payload.items.length; ++i) { // Hardcoded
const item = /** @type {ShapeItem} */ (payload.items[i].item); for (let i = 0; i < payload.inputCount; ++i) {
const item = /** @type {ShapeItem} */ (payload.items.get(i));
if (!item) {
continue;
}
this.root.hubGoals.handleDefinitionDelivered(item.definition); this.root.hubGoals.handleDefinitionDelivered(item.definition);
} }
} }
@ -570,7 +577,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
*/ */
process_GOAL(payload) { process_GOAL(payload) {
const goalComp = payload.entity.components.GoalAcceptor; const goalComp = payload.entity.components.GoalAcceptor;
const item = payload.items[0].item; const item = payload.items.get(0);
const now = this.root.time.now(); const now = this.root.time.now();
if (goalComp.item && !item.equals(goalComp.item)) { if (goalComp.item && !item.equals(goalComp.item)) {
@ -584,7 +591,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
if (this.root.gameMode.getIsEditor()) { if (this.root.gameMode.getIsEditor()) {
// while playing in editor, assign the item // while playing in editor, assign the item
goalComp.item = payload.items[0].item; goalComp.item = item;
} }
goalComp.lastDelivery = { goalComp.lastDelivery = {

View File

@ -59,5 +59,10 @@
"outline": "#111418", "outline": "#111418",
"outlineWidth": 0.75, "outlineWidth": 0.75,
"circleBackground": "rgba(20, 30, 40, 0.3)" "circleBackground": "rgba(20, 30, 40, 0.3)"
},
"shapeTooltip": {
"background": "rgba(242, 245, 254, 0.9)",
"outline": "#44464e"
} }
} }

View File

@ -60,5 +60,10 @@
"outline": "#55575a", "outline": "#55575a",
"outlineWidth": 0.75, "outlineWidth": 0.75,
"circleBackground": "rgba(40, 50, 65, 0.1)" "circleBackground": "rgba(40, 50, 65, 0.1)"
},
"shapeTooltip": {
"background": "#dee1ea",
"outline": "#54565e"
} }
} }

View File

@ -12,7 +12,9 @@ export const LANGUAGES = {
"zh-CN": { "zh-CN": {
// simplified chinese // simplified chinese
name: "简体中文", name: "简体中文",
data: require("./built-temp/base-zh-CN.json"), data: G_WEGAME_VERSION
? require("./built-temp/base-zh-CN-ISBN.json")
: require("./built-temp/base-zh-CN.json"),
code: "zh", code: "zh",
region: "CN", region: "CN",
}, },

View File

@ -475,7 +475,7 @@ export class AchievementCollection {
createLevelOptions(level) { createLevelOptions(level) {
return { return {
init: ({ key }) => this.unlock(key, this.root.hubGoals.level), init: ({ key }) => this.unlock(key, this.root.hubGoals.level),
isValid: currentLevel => currentLevel >= level, isValid: currentLevel => currentLevel > level,
signal: "storyGoalCompleted", signal: "storyGoalCompleted",
}; };
} }

View File

@ -95,6 +95,10 @@ export class GamedistributionAdProvider extends AdProviderInterface {
document.body.classList.add("externalAdOpen"); document.body.classList.add("externalAdOpen");
logger.log("Set sound volume to 0");
this.app.sound.setMusicVolume(0);
this.app.sound.setSoundVolume(0);
return new Promise(resolve => { return new Promise(resolve => {
// So, wait for the remove call but also remove after N seconds // So, wait for the remove call but also remove after N seconds
this.videoAdResolveFunction = () => { this.videoAdResolveFunction = () => {
@ -119,6 +123,11 @@ export class GamedistributionAdProvider extends AdProviderInterface {
}) })
.then(() => { .then(() => {
document.body.classList.remove("externalAdOpen"); document.body.classList.remove("externalAdOpen");
logger.log("Restored sound volume");
this.app.sound.setMusicVolume(this.app.settings.getSetting("musicVolume"));
this.app.sound.setSoundVolume(this.app.settings.getSetting("soundVolume"));
}); });
} }
} }

View File

@ -143,6 +143,20 @@ export class ClientAPI {
return this._request("/v1/puzzles/list/" + category, {}); return this._request("/v1/puzzles/list/" + category, {});
} }
/**
* @param {{ searchTerm: string; difficulty: string; duration: string }} searchOptions
* @returns {Promise<import("../savegame/savegame_typedefs").PuzzleMetadata[]>}
*/
apiSearchPuzzles(searchOptions) {
if (!this.isLoggedIn()) {
return Promise.reject("not-logged-in");
}
return this._request("/v1/puzzles/search", {
method: "POST",
body: searchOptions,
});
}
/** /**
* @param {number} puzzleId * @param {number} puzzleId
* @returns {Promise<import("../savegame/savegame_typedefs").PuzzleFullData>} * @returns {Promise<import("../savegame/savegame_typedefs").PuzzleFullData>}
@ -169,7 +183,7 @@ export class ClientAPI {
} }
/** /**
* @param {number} shortKey * @param {string} shortKey
* @returns {Promise<import("../savegame/savegame_typedefs").PuzzleFullData>} * @returns {Promise<import("../savegame/savegame_typedefs").PuzzleFullData>}
*/ */
apiDownloadPuzzleByKey(shortKey) { apiDownloadPuzzleByKey(shortKey) {

View File

@ -95,6 +95,15 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
); );
} }
/**
* Makes sure a DLC is activated on steam
* @param {string} dlc
*/
activateDlc(dlc) {
logger.log("Activating dlc:", dlc);
return this.sendToApi("/v1/activate-dlc/" + dlc, {});
}
/** /**
* Sends a request to the api * Sends a request to the api
* @param {string} endpoint Endpoint without base url * @param {string} endpoint Endpoint without base url
@ -102,6 +111,10 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
sendToApi(endpoint, data) { sendToApi(endpoint, data) {
if (G_WEGAME_VERSION) {
return Promise.resolve();
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject("Request to " + endpoint + " timed out"), 20000); const timeout = setTimeout(() => reject("Request to " + endpoint + " timed out"), 20000);

View File

@ -135,15 +135,7 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
openExternalLink(url, force = false) { openExternalLink(url, force = false) {
logger.log("Opening external:", url); logger.log("Opening external:", url);
if (force || this.embedProvider.externalLinks) { window.open(url);
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() { performRestart() {

View File

@ -83,6 +83,17 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
res => { res => {
logger.log("Got DLC ownership:", res); logger.log("Got DLC ownership:", res);
this.dlcs.puzzle = Boolean(res); this.dlcs.puzzle = Boolean(res);
if (this.dlcs.puzzle && !G_IS_DEV) {
this.app.gameAnalytics.activateDlc("puzzle").then(
() => {
logger.log("Puzzle DLC successfully activated");
},
error => {
logger.error("Failed to activate puzzle DLC:", error);
}
);
}
}, },
err => { err => {
logger.error("Failed to get DLC ownership:", err); logger.error("Failed to get DLC ownership:", err);

View File

@ -39,4 +39,13 @@ export class GameAnalyticsInterface {
* @param {number} level * @param {number} level
*/ */
handleUpgradeUnlocked(id, level) {} handleUpgradeUnlocked(id, level) {}
/**
* Activates a DLC
* @param {string} dlc
*/
activateDlc(dlc) {
abstract;
return Promise.resolve();
}
} }

View File

@ -257,6 +257,7 @@ export const allApplicationSettings = [
}), }),
new BoolSetting("enableMousePan", enumCategories.advanced, (app, value) => {}), new BoolSetting("enableMousePan", enumCategories.advanced, (app, value) => {}),
new BoolSetting("shapeTooltipAlwaysOn", enumCategories.advanced, (app, value) => {}),
new BoolSetting("alwaysMultiplace", enumCategories.advanced, (app, value) => {}), new BoolSetting("alwaysMultiplace", enumCategories.advanced, (app, value) => {}),
new BoolSetting("zoomToCursor", enumCategories.advanced, (app, value) => {}), new BoolSetting("zoomToCursor", enumCategories.advanced, (app, value) => {}),
new BoolSetting("clearCursorOnDeleteWhilePlacing", enumCategories.advanced, (app, value) => {}), new BoolSetting("clearCursorOnDeleteWhilePlacing", enumCategories.advanced, (app, value) => {}),
@ -272,7 +273,7 @@ export const allApplicationSettings = [
new EnumSetting("refreshRate", { new EnumSetting("refreshRate", {
options: refreshRateOptions, options: refreshRateOptions,
valueGetter: rate => rate, valueGetter: rate => rate,
textGetter: rate => rate + " Hz", textGetter: rate => T.settings.tickrateHz.replace("<amount>", rate),
category: enumCategories.performance, category: enumCategories.performance,
restartRequired: false, restartRequired: false,
changeCb: (app, id) => {}, changeCb: (app, id) => {},
@ -307,6 +308,7 @@ class SettingsStorage {
this.autosaveInterval = "two_minutes"; this.autosaveInterval = "two_minutes";
this.alwaysMultiplace = false; this.alwaysMultiplace = false;
this.shapeTooltipAlwaysOn = false;
this.offerHints = true; this.offerHints = true;
this.enableTunnelSmartplace = true; this.enableTunnelSmartplace = true;
this.vignette = true; this.vignette = true;
@ -536,7 +538,7 @@ export class ApplicationSettings extends ReadWriteProxy {
} }
getCurrentVersion() { getCurrentVersion() {
return 30; return 31;
} }
/** @param {{settings: SettingsStorage, version: number}} data */ /** @param {{settings: SettingsStorage, version: number}} data */
@ -683,6 +685,11 @@ export class ApplicationSettings extends ReadWriteProxy {
data.version = 30; data.version = 30;
} }
if (data.version < 31) {
data.settings.shapeTooltipAlwaysOn = false;
data.version = 31;
}
return ExplainedResult.good(); return ExplainedResult.good();
} }
} }

View File

@ -64,6 +64,24 @@ export class Savegame extends ReadWriteProxy {
return savegameInterfaces[Savegame.getCurrentVersion()]; return savegameInterfaces[Savegame.getCurrentVersion()];
} }
/**
*
* @param {Application} app
* @returns
*/
static createPuzzleSavegame(app) {
return new Savegame(app, {
internalId: "puzzle",
metaDataRef: {
internalId: "puzzle",
lastUpdate: 0,
version: 0,
level: 0,
name: "puzzle",
},
});
}
/** /**
* @returns {number} * @returns {number}
*/ */

View File

@ -17,6 +17,7 @@ import {
waitNextFrame, waitNextFrame,
} from "../core/utils"; } from "../core/utils";
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
import { PlatformWrapperImplElectron } from "../platform/electron/wrapper"; import { PlatformWrapperImplElectron } from "../platform/electron/wrapper";
import { getApplicationSettingById } from "../profile/application_settings"; import { getApplicationSettingById } from "../profile/application_settings";
import { T } from "../translations"; import { T } from "../translations";
@ -34,36 +35,58 @@ export class MainMenuState extends GameState {
} }
getInnerHTML() { getInnerHTML() {
const showLanguageIcon = !G_CHINA_VERSION && !G_WEGAME_VERSION;
const showExitAppButton = G_IS_STANDALONE;
const showUpdateLabel = !G_WEGAME_VERSION;
const showBrowserWarning = !G_IS_STANDALONE && !isSupportedBrowser();
const showPuzzleDLC = !G_WEGAME_VERSION && (G_IS_STANDALONE || G_IS_DEV);
const showWegameFooter = G_WEGAME_VERSION;
let showExternalLinks = true;
if (G_IS_STANDALONE) {
if (G_WEGAME_VERSION || G_CHINA_VERSION) {
showExternalLinks = false;
}
} else {
const wrapper = /** @type {PlatformWrapperImplBrowser} */ (this.app.platformWrapper);
if (!wrapper.embedProvider.externalLinks) {
showExternalLinks = false;
}
}
let showDiscordLink = showExternalLinks;
if (G_CHINA_VERSION) {
showDiscordLink = true;
}
const showCrosspromo = !G_IS_STANDALONE && showExternalLinks;
const showDemoAdvertisement =
showExternalLinks && this.app.restrictionMgr.getIsStandaloneMarketingActive();
const ownsPuzzleDLC =
G_IS_DEV ||
(G_IS_STANDALONE &&
/** @type { PlatformWrapperImplElectron}*/ (this.app.platformWrapper).dlcs.puzzle);
const bannerHtml = ` const bannerHtml = `
<h3>${T.demoBanners.title}</h3> <h3>${T.demoBanners.title}</h3>
<p>${T.demoBanners.intro}</p> <p>${T.demoBanners.intro}</p>
<a href="#" class="steamLink ${A_B_TESTING_LINK_TYPE}" target="_blank">Get the shapez.io standalone!</a>
<a href="#" class="steamLink ${A_B_TESTING_LINK_TYPE}" target="_blank">Get the shapez.io standalone!</a>
`; `;
const showDemoBadges = this.app.restrictionMgr.getIsStandaloneMarketingActive();
const puzzleDlc =
G_IS_STANDALONE &&
/** @type { PlatformWrapperImplElectron
}*/ (this.app.platformWrapper).dlcs.puzzle;
return ` return `
<div class="topButtons"> <div class="topButtons">
${ ${
G_CHINA_VERSION || G_WEGAME_VERSION showLanguageIcon
? "" ? `<button class="languageChoose" data-languageicon="${this.app.settings.getLanguage()}"></button>`
: `<button class="languageChoose" data-languageicon="${this.app.settings.getLanguage()}"></button>` : ""
} }
<button class="achievementsButton"></button> <button class="achievementsButton"></button>
<button class="settingsButton"></button> <button class="settingsButton"></button>
${ ${showExitAppButton ? `<button class="exitAppButton"></button>` : ""}
G_IS_STANDALONE || G_IS_DEV
? `
<button class="exitAppButton"></button>
`
: ""
}
</div> </div>
<video autoplay muted loop class="fullscreenBackgroundVideo"> <video autoplay muted loop class="fullscreenBackgroundVideo">
@ -72,27 +95,25 @@ export class MainMenuState extends GameState {
<div class="logo"> <div class="logo">
<img src="${cachebust("res/" + getLogoSprite())}" alt="shapez.io Logo"> <img src="${cachebust("res/" + getLogoSprite())}" alt="shapez.io Logo">
${G_WEGAME_VERSION ? "" : `<span class="updateLabel">v${G_BUILD_VERSION}!</span>`} ${showUpdateLabel ? `<span class="updateLabel">v${G_BUILD_VERSION}!</span>` : ""}
</div> </div>
<div class="mainWrapper ${showDemoBadges ? "demo" : "noDemo"}" data-columns="${ <div class="mainWrapper" data-columns="${showDemoAdvertisement || showPuzzleDLC ? 2 : 1}">
G_IS_STANDALONE ? 2 : showDemoBadges ? 2 : 1
}">
<div class="sideContainer"> <div class="sideContainer">
${showDemoBadges ? `<div class="standaloneBanner">${bannerHtml}</div>` : ""} ${showDemoAdvertisement ? `<div class="standaloneBanner">${bannerHtml}</div>` : ""}
</div> </div>
<div class="mainContainer"> <div class="mainContainer">
${ ${
G_IS_STANDALONE || isSupportedBrowser() showBrowserWarning
? "" ? `<div class="browserWarning">${T.mainMenu.browserWarning}</div>`
: `<div class="browserWarning">${T.mainMenu.browserWarning}</div>` : ""
} }
<div class="buttons"></div> <div class="buttons"></div>
</div> </div>
${ ${
(!G_WEGAME_VERSION && G_IS_STANDALONE && puzzleDlc) || G_IS_DEV showPuzzleDLC && ownsPuzzleDLC
? ` ? `
<div class="puzzleContainer"> <div class="puzzleContainer">
<img class="dlcLogo" src="${cachebust( <img class="dlcLogo" src="${cachebust(
@ -106,7 +127,7 @@ export class MainMenuState extends GameState {
} }
${ ${
!G_WEGAME_VERSION && G_IS_STANDALONE && !puzzleDlc showPuzzleDLC && !ownsPuzzleDLC
? ` ? `
<div class="puzzleContainer notOwned"> <div class="puzzleContainer notOwned">
<span class="badge"> <span class="badge">
@ -130,40 +151,60 @@ export class MainMenuState extends GameState {
</div> </div>
${ ${
G_WEGAME_VERSION showWegameFooter
? "<div class='footer wegame'></div>" ? `
<div class='footer wegameDisclaimer'>
<div class="disclaimer">
健康游戏忠告
<br>
抵制不良游戏,拒绝盗版游戏注意自我保护,谨防受骗上当<br>
适度游戏益脑,沉迷游戏伤身合理安排时间,享受健康生活
</div>
<div class="rating"></div>
</div>
`
: ` : `
<div class="footer ${G_CHINA_VERSION ? "china" : ""} ">
${ <div class="footer ${showExternalLinks ? "" : "noLinks"} ">
G_CHINA_VERSION ${
? "" showExternalLinks
: ` ? `
<a class="githubLink boxLink" target="_blank"> <a class="githubLink boxLink" target="_blank">
${T.mainMenu.openSourceHint} ${T.mainMenu.openSourceHint}
<span class="thirdpartyLogo githubLogo"></span> <span class="thirdpartyLogo githubLogo"></span>
</a>` </a>`
} : ""
}
<a class="discordLink boxLink" target="_blank"> ${
${T.mainMenu.discordLink} showDiscordLink
<span class="thirdpartyLogo discordLogo"></span> ? `<a class="discordLink boxLink" target="_blank">
</a>
<div class="sidelinks"> ${T.mainMenu.discordLink}
${G_CHINA_VERSION ? "" : `<a class="redditLink">${T.mainMenu.subreddit}</a>`} <span class="thirdpartyLogo discordLogo"></span>
</a>`
: ""
}
${G_CHINA_VERSION ? "" : `<a class="changelog">${T.changelog.title}</a>`} <div class="sidelinks">
${showExternalLinks ? `<a class="redditLink">${T.mainMenu.subreddit}</a>` : ""}
${G_CHINA_VERSION ? "" : `<a class="helpTranslate">${T.mainMenu.helpTranslate}</a>`} ${showExternalLinks ? `<a class="changelog">${T.changelog.title}</a>` : ""}
${showExternalLinks ? `<a class="helpTranslate">${T.mainMenu.helpTranslate}</a>` : ""}
</div>
<div class="author">${T.mainMenu.madeBy.replace(
"<author-link>",
'<a class="producerLink" target="_blank">Tobias Springer</a>'
)}</div>
</div> </div>
${
<div class="author">${T.mainMenu.madeBy.replace( showCrosspromo
"<author-link>", ? `<iframe id="crosspromo" src="https://crosspromo.tobspr.io?src=shapez_web"></iframe>`
'<a class="producerLink" target="_blank">Tobias Springer</a>' : ""
)}</div> }
</div>
` `
} }
`; `;
@ -252,8 +293,6 @@ export class MainMenuState extends GameState {
); );
} }
const qs = this.htmlElement.querySelector.bind(this.htmlElement);
if (G_IS_DEV && globalConfig.debug.testPuzzleMode) { if (G_IS_DEV && globalConfig.debug.testPuzzleMode) {
this.onPuzzleModeButtonClicked(true); this.onPuzzleModeButtonClicked(true);
return; return;
@ -277,72 +316,39 @@ export class MainMenuState extends GameState {
} }
}); });
this.trackClicks(qs(".settingsButton"), this.onSettingsButtonClicked); const clickHandling = {
this.trackClicks(qs(".achievementsButton"), this.onAchievementsButtonClicked); ".settingsButton": this.onSettingsButtonClicked,
".achievementsButton": this.onAchievementsButtonClicked,
".languageChoose": this.onLanguageChooseClicked,
".redditLink": this.onRedditClicked,
".changelog": this.onChangelogClicked,
".helpTranslate": this.onTranslationHelpLinkClicked,
".exitAppButton": this.onExitAppButtonClicked,
".steamLink": this.onSteamLinkClicked,
".discordLink": () => {
this.app.analytics.trackUiClick("main_menu_link_discord");
this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.discord);
},
".githubLink": () => {
this.app.analytics.trackUiClick("main_menu_link_github");
this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.github);
},
".producerLink": () => this.app.platformWrapper.openExternalLink("https://tobspr.io"),
".puzzleDlcPlayButton": this.onPuzzleModeButtonClicked,
".puzzleDlcGetButton": this.onPuzzleWishlistButtonClicked,
".wegameDisclaimer > .rating": this.onWegameRatingClicked,
};
if (!G_CHINA_VERSION && !G_WEGAME_VERSION) { for (const key in clickHandling) {
this.trackClicks(qs(".languageChoose"), this.onLanguageChooseClicked); const handler = clickHandling[key];
this.trackClicks(qs(".redditLink"), this.onRedditClicked); const element = this.htmlElement.querySelector(key);
this.trackClicks(qs(".changelog"), this.onChangelogClicked); if (element) {
this.trackClicks(qs(".helpTranslate"), this.onTranslationHelpLinkClicked); this.trackClicks(element, handler, { preventClick: true });
} }
if (G_IS_STANDALONE) {
this.trackClicks(qs(".exitAppButton"), this.onExitAppButtonClicked);
} }
this.renderMainMenu(); this.renderMainMenu();
this.renderSavegames(); this.renderSavegames();
const steamLink = this.htmlElement.querySelector(".steamLink");
if (steamLink) {
this.trackClicks(steamLink, () => this.onSteamLinkClicked(), { preventClick: true });
}
const discordLink = this.htmlElement.querySelector(".discordLink");
if (discordLink) {
this.trackClicks(
discordLink,
() => {
this.app.analytics.trackUiClick("main_menu_link_discord");
this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.discord);
},
{ preventClick: true }
);
}
const githubLink = this.htmlElement.querySelector(".githubLink");
if (githubLink) {
this.trackClicks(
githubLink,
() => {
this.app.analytics.trackUiClick("main_menu_link_github");
this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.github);
},
{ preventClick: true }
);
}
const producerLink = this.htmlElement.querySelector(".producerLink");
if (producerLink) {
this.trackClicks(
producerLink,
() => this.app.platformWrapper.openExternalLink("https://tobspr.io"),
{
preventClick: true,
}
);
}
const puzzleModeButton = qs(".puzzleDlcPlayButton");
if (puzzleModeButton) {
this.trackClicks(puzzleModeButton, () => this.onPuzzleModeButtonClicked());
}
const puzzleWishlistButton = qs(".puzzleDlcGetButton");
if (puzzleWishlistButton) {
this.trackClicks(puzzleWishlistButton, () => this.onPuzzleWishlistButtonClicked());
}
} }
renderMainMenu() { renderMainMenu() {
@ -400,9 +406,7 @@ export class MainMenuState extends GameState {
} }
onPuzzleWishlistButtonClicked() { onPuzzleWishlistButtonClicked() {
this.app.platformWrapper.openExternalLink( this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.puzzleDlcStorePage + "?utm_medium=mmsl2");
THIRDPARTY_URLS.puzzleDlcStorePage + "?ref=mmsl2&prc=" + A_B_TESTING_LINK_TYPE
);
} }
onBackButtonClicked() { onBackButtonClicked() {
@ -412,9 +416,7 @@ export class MainMenuState extends GameState {
onSteamLinkClicked() { onSteamLinkClicked() {
this.app.analytics.trackUiClick("main_menu_steam_link_" + A_B_TESTING_LINK_TYPE); this.app.analytics.trackUiClick("main_menu_steam_link_" + A_B_TESTING_LINK_TYPE);
this.app.platformWrapper.openExternalLink( this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_mainmenu");
THIRDPARTY_URLS.standaloneStorePage + "?ref=mmsl2&prc=" + A_B_TESTING_LINK_TYPE
);
return false; return false;
} }
@ -517,9 +519,12 @@ export class MainMenuState extends GameState {
downloadButton.classList.add("styledButton", "downloadGame"); downloadButton.classList.add("styledButton", "downloadGame");
elem.appendChild(downloadButton); elem.appendChild(downloadButton);
const renameButton = document.createElement("button"); if (!G_WEGAME_VERSION) {
renameButton.classList.add("styledButton", "renameGame"); const renameButton = document.createElement("button");
name.appendChild(renameButton); renameButton.classList.add("styledButton", "renameGame");
name.appendChild(renameButton);
this.trackClicks(renameButton, () => this.requestRenameSavegame(games[i]));
}
const resumeButton = document.createElement("button"); const resumeButton = document.createElement("button");
resumeButton.classList.add("styledButton", "resumeGame"); resumeButton.classList.add("styledButton", "resumeGame");
@ -528,7 +533,6 @@ export class MainMenuState extends GameState {
this.trackClicks(deleteButton, () => this.deleteGame(games[i])); this.trackClicks(deleteButton, () => this.deleteGame(games[i]));
this.trackClicks(downloadButton, () => this.downloadGame(games[i])); this.trackClicks(downloadButton, () => this.downloadGame(games[i]));
this.trackClicks(resumeButton, () => this.resumeGame(games[i])); this.trackClicks(resumeButton, () => this.resumeGame(games[i]));
this.trackClicks(renameButton, () => this.requestRenameSavegame(games[i]));
} }
} }
} }
@ -643,7 +647,9 @@ export class MainMenuState extends GameState {
); );
getStandalone.add(() => { getStandalone.add(() => {
this.app.analytics.trackUiClick("visit_steampage_from_slot_limit"); this.app.analytics.trackUiClick("visit_steampage_from_slot_limit");
this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.standaloneStorePage + "?reF=ssll"); this.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_slotlimit"
);
}); });
} }
@ -683,6 +689,18 @@ export class MainMenuState extends GameState {
}); });
} }
onWegameRatingClicked() {
this.dialogs.showInfo(
"提示说明",
`
1本游戏是一款休闲建造类单机游戏画面简洁而乐趣充足适用于年满8周岁及以上的用户建议未成年人在家长监护下使用游戏产品<br>
2本游戏模拟简单的生产流水线剧情简单且积极向上没有基于真实历史和现实事件的改编内容游戏玩法为摆放简单的部件完成生产目标游戏为单机作品没有基于文字和语音的陌生人社交系统<br>
3本游戏中有用户实名认证系统认证为未成年人的用户将接受以下管理未满8周岁的用户不能付费8周岁以上未满16周岁的未成年人用户单次充值金额不得超过50元人民币每月充值金额累计不得超过200元人民币16周岁以上的未成年人用户单次充值金额不得超过100元人民币每月充值金额累计不得超过400元人民币未成年人用户每日22点到次日8点不得使用法定节假日每天不得使用超过3小时其他时间每天不得使用超过1.5小时<br>
4游戏功能说明一款关于传送带自动化生产特定形状产品的工厂流水线模拟游戏画面简洁而乐趣充足可以让玩家在轻松愉快的氛围下获得各种游戏乐趣体验完成目标的成就感游戏没有失败功能自动存档不存在较强的挫折体验
`
);
}
onContinueButtonClicked() { onContinueButtonClicked() {
let latestLastUpdate = 0; let latestLastUpdate = 0;
let latestInternalId; let latestInternalId;
@ -694,11 +712,14 @@ export class MainMenuState extends GameState {
}); });
const savegame = this.app.savegameMgr.getSavegameById(latestInternalId); const savegame = this.app.savegameMgr.getSavegameById(latestInternalId);
savegame.readAsync().then(() => { savegame
this.moveToState("InGameState", { .readAsync()
savegame, .then(() => this.app.adProvider.showVideoAd())
.then(() => {
this.moveToState("InGameState", {
savegame,
});
}); });
});
} }
onLeave() { onLeave() {

View File

@ -22,7 +22,7 @@ export class MobileWarningState extends GameState {
<a href="${ <a href="${
THIRDPARTY_URLS.standaloneStorePage + "?ref=mobile" THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_mobile"
}" class="standaloneLink" target="_blank">Get the shapez.io standalone!</a> }" class="standaloneLink" target="_blank">Get the shapez.io standalone!</a>
`; `;
} }

View File

@ -81,9 +81,11 @@ export class PreloadState extends GameState {
} catch (ex) { } catch (ex) {
logger.error("Failed to read/write local storage:", ex); logger.error("Failed to read/write local storage:", ex);
return new Promise(() => { return new Promise(() => {
alert(`Your brower does not support thirdparty cookies or you have disabled it in your security settings.\n\n alert(
In Chrome this setting is called "Block third-party cookies and site data".\n\n "Your brower does not support thirdparty cookies or you have disabled it in your security settings.\n\n" +
Please allow third party cookies and then reload the page.`); "In Chrome this setting is called 'Block third-party cookies and site data'.\n\n" +
"Please allow third party cookies and then reload the page."
);
// Never return // Never return
}); });
} }

View File

@ -13,17 +13,30 @@ const navigation = {
categories: ["official", "top-rated", "trending", "trending-weekly", "new"], categories: ["official", "top-rated", "trending", "trending-weekly", "new"],
difficulties: ["easy", "medium", "hard"], difficulties: ["easy", "medium", "hard"],
account: ["mine", "completed"], account: ["mine", "completed"],
search: ["search"],
}; };
const logger = createLogger("puzzle-menu"); const logger = createLogger("puzzle-menu");
let lastCategory = "official"; let lastCategory = "official";
let lastSearchOptions = {
searchTerm: "",
difficulty: "any",
duration: "any",
includeCompleted: false,
};
export class PuzzleMenuState extends TextualGameState { export class PuzzleMenuState extends TextualGameState {
constructor() { constructor() {
super("PuzzleMenuState"); super("PuzzleMenuState");
this.loading = false; this.loading = false;
this.activeCategory = ""; this.activeCategory = "";
/**
* @type {Array<import("../savegame/savegame_typedefs").PuzzleMetadata>}
*/
this.puzzles = [];
} }
getThemeMusic() { getThemeMusic() {
@ -99,13 +112,23 @@ export class PuzzleMenuState extends TextualGameState {
activeCategory.classList.remove("active"); activeCategory.classList.remove("active");
} }
this.htmlElement.querySelector(`[data-category="${category}"]`).classList.add("active"); const categoryElement = this.htmlElement.querySelector(`[data-category="${category}"]`);
if (categoryElement) {
categoryElement.classList.add("active");
}
const container = this.htmlElement.querySelector("#mainContainer"); const container = this.htmlElement.querySelector("#mainContainer");
while (container.firstChild) { while (container.firstChild) {
container.removeChild(container.firstChild); container.removeChild(container.firstChild);
} }
if (category === "search") {
this.loading = false;
this.startSearch();
return;
}
const loadingElement = document.createElement("div"); const loadingElement = document.createElement("div");
loadingElement.classList.add("loader"); loadingElement.classList.add("loader");
loadingElement.innerText = T.global.loading + "..."; loadingElement.innerText = T.global.loading + "...";
@ -160,23 +183,148 @@ export class PuzzleMenuState extends TextualGameState {
} }
const children = navigation[rootCategory]; const children = navigation[rootCategory];
for (const category of children) { if (children.length > 1) {
const button = document.createElement("button"); for (const category of children) {
button.setAttribute("data-category", category); const button = document.createElement("button");
button.classList.add("styledButton", "category", "child"); button.setAttribute("data-category", category);
button.innerText = T.puzzleMenu.categories[category]; button.classList.add("styledButton", "category", "child");
this.trackClicks(button, () => this.selectCategory(category)); button.innerText = T.puzzleMenu.categories[category];
subContainer.appendChild(button); this.trackClicks(button, () => this.selectCategory(category));
subContainer.appendChild(button);
}
}
if (rootCategory === "search") {
this.renderSearchForm(subContainer);
} }
this.selectCategory(subCategory); this.selectCategory(subCategory);
} }
renderSearchForm(parent) {
const container = document.createElement("form");
container.classList.add("searchForm");
// Search
const searchField = document.createElement("input");
searchField.value = lastSearchOptions.searchTerm;
searchField.classList.add("search");
searchField.setAttribute("type", "text");
searchField.setAttribute("placeholder", T.puzzleMenu.search.placeholder);
searchField.addEventListener("input", () => {
lastSearchOptions.searchTerm = searchField.value.trim();
});
container.appendChild(searchField);
// Difficulty
const difficultyFilter = document.createElement("select");
for (const difficulty of ["any", "easy", "medium", "hard"]) {
const option = document.createElement("option");
option.value = difficulty;
option.innerText = T.puzzleMenu.search.difficulties[difficulty];
if (option.value === lastSearchOptions.difficulty) {
option.setAttribute("selected", "selected");
}
difficultyFilter.appendChild(option);
}
difficultyFilter.addEventListener("change", () => {
const option = difficultyFilter.value;
lastSearchOptions.difficulty = option;
});
container.appendChild(difficultyFilter);
// Duration
const durationFilter = document.createElement("select");
for (const duration of ["any", "short", "medium", "long"]) {
const option = document.createElement("option");
option.value = duration;
option.innerText = T.puzzleMenu.search.durations[duration];
if (option.value === lastSearchOptions.duration) {
option.setAttribute("selected", "selected");
}
durationFilter.appendChild(option);
}
durationFilter.addEventListener("change", () => {
const option = durationFilter.value;
lastSearchOptions.duration = option;
});
container.appendChild(durationFilter);
// Include completed
const labelCompleted = document.createElement("label");
labelCompleted.classList.add("filterCompleted");
const inputCompleted = document.createElement("input");
inputCompleted.setAttribute("type", "checkbox");
if (lastSearchOptions.includeCompleted) {
inputCompleted.setAttribute("checked", "checked");
}
inputCompleted.addEventListener("change", () => {
lastSearchOptions.includeCompleted = inputCompleted.checked;
});
labelCompleted.appendChild(inputCompleted);
const text = document.createTextNode(T.puzzleMenu.search.includeCompleted);
labelCompleted.appendChild(text);
container.appendChild(labelCompleted);
// Submit
const submitButton = document.createElement("button");
submitButton.classList.add("styledButton");
submitButton.setAttribute("type", "submit");
submitButton.innerText = T.puzzleMenu.search.action;
container.appendChild(submitButton);
container.addEventListener("submit", event => {
event.preventDefault();
console.log("Search:", searchField.value.trim());
this.startSearch();
});
parent.appendChild(container);
}
startSearch() {
if (this.loading) {
return;
}
this.loading = true;
const container = this.htmlElement.querySelector("#mainContainer");
while (container.firstChild) {
container.removeChild(container.firstChild);
}
const loadingElement = document.createElement("div");
loadingElement.classList.add("loader");
loadingElement.innerText = T.global.loading + "...";
container.appendChild(loadingElement);
this.asyncChannel
.watch(this.app.clientApi.apiSearchPuzzles(lastSearchOptions))
.then(
puzzles => this.renderPuzzles(puzzles),
error => {
this.dialogs.showWarning(
T.dialogs.puzzleLoadFailed.title,
T.dialogs.puzzleLoadFailed.desc + " " + error
);
this.renderPuzzles([]);
}
)
.then(() => (this.loading = false));
}
/** /**
* *
* @param {import("../savegame/savegame_typedefs").PuzzleMetadata[]} puzzles * @param {import("../savegame/savegame_typedefs").PuzzleMetadata[]} puzzles
*/ */
renderPuzzles(puzzles) { renderPuzzles(puzzles) {
this.puzzles = puzzles;
const container = this.htmlElement.querySelector("#mainContainer"); const container = this.htmlElement.querySelector("#mainContainer");
while (container.firstChild) { while (container.firstChild) {
container.removeChild(container.firstChild); container.removeChild(container.firstChild);
@ -209,10 +357,7 @@ export class PuzzleMenuState extends TextualGameState {
stats.classList.add("stats"); stats.classList.add("stats");
elem.appendChild(stats); elem.appendChild(stats);
if ( if (!["official", "easy", "medium", "hard"].includes(this.activeCategory)) {
puzzle.downloads > 3 &&
!["official", "easy", "medium", "hard"].includes(this.activeCategory)
) {
const difficulty = document.createElement("div"); const difficulty = document.createElement("div");
difficulty.classList.add("difficulty"); difficulty.classList.add("difficulty");
@ -223,15 +368,18 @@ export class PuzzleMenuState extends TextualGameState {
difficulty.innerText = completionPercentage + "%"; difficulty.innerText = completionPercentage + "%";
stats.appendChild(difficulty); stats.appendChild(difficulty);
if (completionPercentage < 40) { if (puzzle.difficulty === null) {
difficulty.classList.add("stage--hard"); difficulty.classList.add("stage--unknown");
difficulty.innerText = T.puzzleMenu.difficulties.hard; difficulty.innerText = T.puzzleMenu.difficulties.unknown;
} else if (completionPercentage < 80) { } else if (puzzle.difficulty < 0.2) {
difficulty.classList.add("stage--medium");
difficulty.innerText = T.puzzleMenu.difficulties.medium;
} else {
difficulty.classList.add("stage--easy"); difficulty.classList.add("stage--easy");
difficulty.innerText = T.puzzleMenu.difficulties.easy; difficulty.innerText = T.puzzleMenu.difficulties.easy;
} else if (puzzle.difficulty > 0.6) {
difficulty.classList.add("stage--hard");
difficulty.innerText = T.puzzleMenu.difficulties.hard;
} else {
difficulty.classList.add("stage--medium");
difficulty.innerText = T.puzzleMenu.difficulties.medium;
} }
} }
@ -275,7 +423,7 @@ export class PuzzleMenuState extends TextualGameState {
container.appendChild(elem); container.appendChild(elem);
this.trackClicks(elem, () => this.playPuzzle(puzzle)); this.trackClicks(elem, () => this.playPuzzle(puzzle.id));
} }
if (puzzles.length === 0) { if (puzzles.length === 0) {
@ -328,20 +476,26 @@ export class PuzzleMenuState extends TextualGameState {
/** /**
* *
* @param {import("../savegame/savegame_typedefs").PuzzleMetadata} puzzle * @param {number} puzzleId
* @param {Array<number>=} nextPuzzles
*/ */
playPuzzle(puzzle) { playPuzzle(puzzleId, nextPuzzles) {
const closeLoading = this.dialogs.showLoadingDialog(); const closeLoading = this.dialogs.showLoadingDialog();
this.app.clientApi.apiDownloadPuzzle(puzzle.id).then( this.asyncChannel.watch(this.app.clientApi.apiDownloadPuzzle(puzzleId)).then(
puzzleData => { puzzleData => {
closeLoading(); closeLoading();
logger.log("Got puzzle:", puzzleData);
this.startLoadedPuzzle(puzzleData); nextPuzzles =
nextPuzzles || this.puzzles.filter(puzzle => !puzzle.completed).map(puzzle => puzzle.id);
nextPuzzles = nextPuzzles.filter(id => id !== puzzleId);
logger.log("Got puzzle:", puzzleData, "next puzzles:", nextPuzzles);
this.startLoadedPuzzle(puzzleData, nextPuzzles);
}, },
err => { err => {
closeLoading(); closeLoading();
logger.error("Failed to download puzzle", puzzle.id, ":", err); logger.error("Failed to download puzzle", puzzleId, ":", err);
this.dialogs.showWarning( this.dialogs.showWarning(
T.dialogs.puzzleDownloadError.title, T.dialogs.puzzleDownloadError.title,
T.dialogs.puzzleDownloadError.desc + " " + err T.dialogs.puzzleDownloadError.desc + " " + err
@ -353,19 +507,26 @@ export class PuzzleMenuState extends TextualGameState {
/** /**
* *
* @param {import("../savegame/savegame_typedefs").PuzzleFullData} puzzle * @param {import("../savegame/savegame_typedefs").PuzzleFullData} puzzle
* @param {Array<number>=} nextPuzzles
*/ */
startLoadedPuzzle(puzzle) { startLoadedPuzzle(puzzle, nextPuzzles) {
const savegame = this.createEmptySavegame(); const savegame = Savegame.createPuzzleSavegame(this.app);
this.moveToState("InGameState", { this.moveToState("InGameState", {
gameModeId: enumGameModeIds.puzzlePlay, gameModeId: enumGameModeIds.puzzlePlay,
gameModeParameters: { gameModeParameters: {
puzzle, puzzle,
nextPuzzles,
}, },
savegame, savegame,
}); });
} }
onEnter(payload) { onEnter(payload) {
if (payload.continueQueue) {
logger.log("Continuing puzzle queue:", payload);
this.playPuzzle(payload.continueQueue[0], payload.continueQueue.slice(1));
}
// Find old category // Find old category
let rootCategory = "categories"; let rootCategory = "categories";
for (const [id, children] of Object.entries(navigation)) { for (const [id, children] of Object.entries(navigation)) {
@ -390,26 +551,13 @@ export class PuzzleMenuState extends TextualGameState {
this.trackClicks(this.htmlElement.querySelector("button.loadPuzzle"), () => this.loadPuzzle()); this.trackClicks(this.htmlElement.querySelector("button.loadPuzzle"), () => this.loadPuzzle());
} }
createEmptySavegame() {
return new Savegame(this.app, {
internalId: "puzzle",
metaDataRef: {
internalId: "puzzle",
lastUpdate: 0,
version: 0,
level: 0,
name: "puzzle",
},
});
}
loadPuzzle() { loadPuzzle() {
const shortKeyInput = new FormElementInput({ const shortKeyInput = new FormElementInput({
id: "shortKey", id: "shortKey",
label: null, label: null,
placeholder: "", placeholder: "",
defaultValue: "", defaultValue: "",
validator: val => ShapeDefinition.isValidShortKey(val), validator: val => ShapeDefinition.isValidShortKey(val) || val.startsWith("/"),
}); });
const dialog = new DialogWithForm({ const dialog = new DialogWithForm({
@ -422,9 +570,16 @@ export class PuzzleMenuState extends TextualGameState {
this.dialogs.internalShowDialog(dialog); this.dialogs.internalShowDialog(dialog);
dialog.buttonSignals.ok.add(() => { dialog.buttonSignals.ok.add(() => {
const searchTerm = shortKeyInput.getValue();
if (searchTerm === "/apikey") {
alert("Your api key is: " + this.app.clientApi.token);
return;
}
const closeLoading = this.dialogs.showLoadingDialog(); const closeLoading = this.dialogs.showLoadingDialog();
this.app.clientApi.apiDownloadPuzzleByKey(shortKeyInput.getValue()).then( this.app.clientApi.apiDownloadPuzzleByKey(searchTerm).then(
puzzle => { puzzle => {
closeLoading(); closeLoading();
this.startLoadedPuzzle(puzzle); this.startLoadedPuzzle(puzzle);
@ -451,7 +606,7 @@ export class PuzzleMenuState extends TextualGameState {
return; return;
} }
const savegame = this.createEmptySavegame(); const savegame = Savegame.createPuzzleSavegame(this.app);
this.moveToState("InGameState", { this.moveToState("InGameState", {
gameModeId: enumGameModeIds.puzzleEdit, gameModeId: enumGameModeIds.puzzleEdit,
savegame, savegame,

View File

@ -1,3 +1,4 @@
import { THIRDPARTY_URLS } from "../core/config";
import { TextualGameState } from "../core/textual_game_state"; import { TextualGameState } from "../core/textual_game_state";
import { formatSecondsToTimeAgo } from "../core/utils"; import { formatSecondsToTimeAgo } from "../core/utils";
import { allApplicationSettings, enumCategories } from "../profile/application_settings"; import { allApplicationSettings, enumCategories } from "../profile/application_settings";
@ -34,6 +35,8 @@ export class SettingsState extends TextualGameState {
? "" ? ""
: ` : `
<button class="styledButton about">${T.about.title}</button> <button class="styledButton about">${T.about.title}</button>
<button class="styledButton privacy">Privacy Policy</button>
` `
} }
<div class="versionbar"> <div class="versionbar">
@ -109,6 +112,9 @@ export class SettingsState extends TextualGameState {
this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, { this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, {
preventDefault: false, preventDefault: false,
}); });
this.trackClicks(this.htmlElement.querySelector(".privacy"), this.onPrivacyClicked, {
preventDefault: false,
});
} }
const keybindingsButton = this.htmlElement.querySelector(".editKeybindings"); const keybindingsButton = this.htmlElement.querySelector(".editKeybindings");
@ -180,6 +186,10 @@ export class SettingsState extends TextualGameState {
this.moveToStateAddGoBack("AboutState"); this.moveToStateAddGoBack("AboutState");
} }
onPrivacyClicked() {
this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.privacyPolicy);
}
onKeybindingsClicked() { onKeybindingsClicked() {
this.moveToStateAddGoBack("KeybindingsState"); this.moveToStateAddGoBack("KeybindingsState");
} }

View File

@ -0,0 +1,27 @@
import { GameState } from "../core/game_state";
export class WegameSplashState extends GameState {
constructor() {
super("WegameSplashState");
}
getInnerHTML() {
return `
<div class="wrapper">
<strong>健康游戏忠告</strong>
<div>抵制不良游戏,拒绝盗版游戏</div>
<div>注意自我保护,谨防受骗上当</div>
<div>适度游戏益脑,沉迷游戏伤身</div>
<div>合理安排时间,享受健康生活</div>
</div>
`;
}
onEnter() {
setTimeout(
() => {
this.app.stateMgr.moveToState("PreloadState");
},
G_IS_DEV ? 1 : 6000
);
}
}

View File

@ -456,6 +456,7 @@ ingame:
titleRatingDesc: Your rating will help me to make you better suggestions in the future titleRatingDesc: Your rating will help me to make you better suggestions in the future
continueBtn: Keep Playing continueBtn: Keep Playing
menuBtn: Menu menuBtn: Menu
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Author author: Author
shortKey: Short Key shortKey: Short Key
@ -1003,7 +1004,12 @@ settings:
title: Map Resources Size title: Map Resources Size
description: Controls the size of the shapes on the map overview (when zooming description: Controls the size of the shapes on the map overview (when zooming
out). out).
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Keybindings title: Keybindings
hint: "Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different hint: "Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different
@ -1085,6 +1091,7 @@ keybindings:
goal_acceptor: Goal Acceptor goal_acceptor: Goal Acceptor
block: Block block: Block
massSelectClear: Clear belts massSelectClear: Clear belts
showShapeTooltip: Show shape output tooltip
about: about:
title: About this Game title: About this Game
body: >- body: >-
@ -1212,8 +1219,23 @@ puzzleMenu:
easy: Easy easy: Easy
medium: Medium medium: Medium
hard: Hard hard: Hard
unknown: Unrated
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking dlcHint: Purchased the DLC already? Make sure it is activated by right clicking
shapez.io in your library, selecting Properties > DLCs. shapez.io in your library, selecting Properties > DLCs.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: You are performing your actions too frequent. Please wait a bit. ratelimit: You are performing your actions too frequent. Please wait a bit.
invalid-api-key: Failed to communicate with the backend, please try to invalid-api-key: Failed to communicate with the backend, please try to

View File

@ -466,6 +466,7 @@ ingame:
titleRatingDesc: Your rating will help me to make you better suggestions in the future titleRatingDesc: Your rating will help me to make you better suggestions in the future
continueBtn: Keep Playing continueBtn: Keep Playing
menuBtn: Menu menuBtn: Menu
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Author author: Author
shortKey: Short Key shortKey: Short Key
@ -1030,7 +1031,12 @@ settings:
title: Map Resources Size title: Map Resources Size
description: Controls the size of the shapes on the map overview (when zooming description: Controls the size of the shapes on the map overview (when zooming
out). out).
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Combinacions de tecles title: Combinacions de tecles
hint: "Tip: Assegura't d'emprar CTRL, SHIFT i ALT! Et permeten col·locar hint: "Tip: Assegura't d'emprar CTRL, SHIFT i ALT! Et permeten col·locar
@ -1112,6 +1118,7 @@ keybindings:
goal_acceptor: Goal Acceptor goal_acceptor: Goal Acceptor
block: Block block: Block
massSelectClear: Clear belts massSelectClear: Clear belts
showShapeTooltip: Show shape output tooltip
about: about:
title: Sobre aquest Joc title: Sobre aquest Joc
body: >- body: >-
@ -1254,8 +1261,23 @@ puzzleMenu:
easy: Easy easy: Easy
medium: Medium medium: Medium
hard: Hard hard: Hard
unknown: Unrated
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking dlcHint: Purchased the DLC already? Make sure it is activated by right clicking
shapez.io in your library, selecting Properties > DLCs. shapez.io in your library, selecting Properties > DLCs.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: You are performing your actions too frequent. Please wait a bit. ratelimit: You are performing your actions too frequent. Please wait a bit.
invalid-api-key: Failed to communicate with the backend, please try to invalid-api-key: Failed to communicate with the backend, please try to

View File

@ -75,7 +75,7 @@ mainMenu:
puzzleDlcText: Baví vás zmenšování a optimalizace továren? Pořiďte si nyní puzzleDlcText: Baví vás zmenšování a optimalizace továren? Pořiďte si nyní
Puzzle DLC na Steamu pro ještě více zábavy! Puzzle DLC na Steamu pro ještě více zábavy!
puzzleDlcWishlist: Přidejte si nyní na seznam přání! puzzleDlcWishlist: Přidejte si nyní na seznam přání!
puzzleDlcViewNow: View Dlc puzzleDlcViewNow: Zobrazit DLC
dialogs: dialogs:
buttons: buttons:
ok: OK ok: OK
@ -431,8 +431,8 @@ ingame:
clearItems: Vymazat tvary clearItems: Vymazat tvary
share: Sdílet share: Sdílet
report: Nahlásit report: Nahlásit
clearBuildings: Clear Buildings clearBuildings: Vymazat budovy
resetPuzzle: Reset Puzzle resetPuzzle: Resetovat puzzle
puzzleEditorControls: puzzleEditorControls:
title: Puzzle editor title: Puzzle editor
instructions: instructions:
@ -457,6 +457,7 @@ ingame:
titleRatingDesc: Vaše hodnocení nám pomůže podat vám v budoucnu lepší návrhy titleRatingDesc: Vaše hodnocení nám pomůže podat vám v budoucnu lepší návrhy
continueBtn: Hrát dál continueBtn: Hrát dál
menuBtn: Menu menuBtn: Menu
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Autor author: Autor
shortKey: Krátký klíč shortKey: Krátký klíč
@ -994,7 +995,12 @@ settings:
mapResourcesScale: mapResourcesScale:
title: Velikost zdrojů na mapě title: Velikost zdrojů na mapě
description: Určuje velikost ikon tvarů na náhledu mapy (při oddálení). description: Určuje velikost ikon tvarů na náhledu mapy (při oddálení).
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Klávesové zkratky title: Klávesové zkratky
hint: "Tip: Nezapomeňte používat CTRL, SHIFT a ALT! Díky nim můžete měnit způsob hint: "Tip: Nezapomeňte používat CTRL, SHIFT a ALT! Díky nim můžete měnit způsob
@ -1076,6 +1082,7 @@ keybindings:
goal_acceptor: Přijemce cílů goal_acceptor: Přijemce cílů
block: Blok block: Blok
massSelectClear: Vymazat pásy massSelectClear: Vymazat pásy
showShapeTooltip: Show shape output tooltip
about: about:
title: O hře title: O hře
body: >- body: >-
@ -1213,8 +1220,24 @@ puzzleMenu:
easy: Lehká easy: Lehká
medium: Střední medium: Střední
hard: Těžká hard: Těžká
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking unknown: Unrated
shapez.io in your library, selecting Properties > DLCs. dlcHint: Již jste toto DLC zakoupili? Ujistěte se, že je aktivováno kliknutím
pravého tlačítka na shapez.io ve své knihovně, vybráním Vlastnosti >
DLC.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: Provádíte své akce příliš často. Počkejte prosím. ratelimit: Provádíte své akce příliš často. Počkejte prosím.
invalid-api-key: Komunikace s back-endem se nezdařila, prosím zkuste invalid-api-key: Komunikace s back-endem se nezdařila, prosím zkuste

File diff suppressed because it is too large Load Diff

View File

@ -5,11 +5,15 @@ steamPage:
intro: >- intro: >-
Du magst Automatisierungsspiele? Dann bist du hier genau richtig! Du magst Automatisierungsspiele? Dann bist du hier genau richtig!
shapez.io ist ein entspanntes Spiel, in dem du Fabriken zur automatisierten Produktion von geometrischen Formen bauen musst. Shapez.io ist ein entspanntes 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! Mit steigendem Level werden die Formen immer komplexer und du musst dich auf der unendlich großen Karte ausbreiten.
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! 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, müssen diese später auch eingefärbt werden. 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!
what_others_say: Was andere über shapez.io sagen what_others_say: Was andere über shapez.io sagen
nothernlion_comment: This game is great - I'm having a wonderful time playing, nothernlion_comment: This game is great - I'm having a wonderful time playing,
and time has flown by. and time has flown by.
@ -21,6 +25,7 @@ steamPage:
global: global:
loading: Laden loading: Laden
error: Fehler error: Fehler
loggingIn: Einloggen
thousandsDivider: . thousandsDivider: .
decimalSeparator: "," decimalSeparator: ","
suffix: suffix:
@ -49,9 +54,8 @@ global:
escape: ESC escape: ESC
shift: UMSCH shift: UMSCH
space: LEER space: LEER
loggingIn: Logging in
demoBanners: demoBanners:
title: Demo-Version title: Demoversion
intro: Kauf die Vollversion für alle Features! intro: Kauf die Vollversion für alle Features!
mainMenu: mainMenu:
play: Spielen play: Spielen
@ -66,16 +70,77 @@ mainMenu:
madeBy: Ein Spiel von <author-link> madeBy: Ein Spiel von <author-link>
browserWarning: Sorry, aber das Spiel wird in deinem Browser langsamer laufen! browserWarning: Sorry, aber das Spiel wird in deinem Browser langsamer laufen!
Kaufe die Vollversion oder verwende Google Chrome für die beste Kaufe die Vollversion oder verwende Google Chrome für die beste
Erfahrung! Erfahrung.
savegameLevel: Level <x> savegameLevel: Level <x>
savegameLevelUnknown: Unbekanntes Level savegameLevelUnknown: Unbekanntes Level
savegameUnnamed: Unbenannt savegameUnnamed: Unbenannt
puzzleMode: Puzzlemodus puzzleMode: Puzzlemodus
back: Zurück back: Zurück
puzzleDlcText: Du hast Spaß daran, deine Fabriken zu optimieren und effizienter puzzleDlcText: Du hast Spaß daran, deine Fabriken zu optimieren und effizienter
zu machen? Hol dir das Puzzle DLC auf Steam für noch mehr Spaß! zu machen? Hol dir das Puzzle-DLC auf Steam für noch mehr Spaß!
puzzleDlcWishlist: Jetzt zur Wunschliste hinzufügen! puzzleDlcWishlist: Jetzt zur Wunschliste hinzufügen!
puzzleDlcViewNow: View Dlc puzzleDlcViewNow: DLC anzeigen
puzzleMenu:
play: Spielen
edit: Bearbeiten
title: Puzzlemodus
createPuzzle: Puzzle erstellen
loadPuzzle: Laden
reviewPuzzle: Überprüfen & Veröffentlichen
validatingPuzzle: Puzzle wird überprüft
submittingPuzzle: Puzzle wird veröffentlicht
noPuzzles: Hier gibt es bisher noch keine Puzzles.
dlcHint: DLC schon gekauft? Stelle sicher, dass es aktiviert ist, indem du in
der Steam-Bibliothek shapez.io rechtsklickst und es unter Eigenschaften
> Zusatzinhalte (DLC) aktivierst.
categories:
levels: Levels
new: Neu
top-rated: Am besten bewertet
mine: Meine Puzzles
easy: Einfach
medium: Mittel
hard: Schwierig
completed: Abgeschlossen
official: Vorgestellt
trending: Heute beliebt
trending-weekly: Diese Woche beliebt
categories: Kategorien
difficulties: Nach Schwierigkeit
account: Meine Puzzles
search: Suche
difficulties:
easy: Einfach
medium: Mittel
hard: Schwer
unknown: Unrated
validation:
title: Ungültiges Puzzle
noProducers: Bitte platziere einen Itemproduzent!
noGoalAcceptors: Bitte platziere einen Zielakzeptor!
goalAcceptorNoItem: Ein oder mehrere Zielakzeptoren haben noch kein zugewiesenes
Item. Beliefere sie mit einer Form, um ein Ziel zu setzen.
goalAcceptorRateNotMet: Ein oder mehrere Zielakzeptoren bekommen nicht genügend
Items. Stelle sicher, dass alle ihre Indikatoren grün leuchten.
buildingOutOfBounds: Ein oder mehrere Gebäude befinden sich außerhalb des
bebaubaren Bereichs. Vergrößere den Bereich oder entferne die
Gebäude.
autoComplete: Dein Puzzle schließt sich selbst ab! Bitte stelle sicher, dass
deine Itemproduzenten nicht direkt an deine Zielakzeptoren liefern.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
dialogs: dialogs:
buttons: buttons:
ok: OK ok: OK
@ -88,7 +153,7 @@ dialogs:
deleteGame: Ich weiß, was ich tue deleteGame: Ich weiß, was ich tue
viewUpdate: Update anzeigen viewUpdate: Update anzeigen
showUpgrades: Upgrades anzeigen showUpgrades: Upgrades anzeigen
showKeybindings: Kürzel anzeigen showKeybindings: Hotkeys anzeigen
retry: Erneut versuchen retry: Erneut versuchen
continue: Fortsetzen continue: Fortsetzen
playOffline: Offline spielen playOffline: Offline spielen
@ -123,9 +188,9 @@ dialogs:
title: Tastenbelegung zurückgesetzt title: Tastenbelegung zurückgesetzt
desc: Die Tastenbelegung wurde auf den Standard zurückgesetzt! desc: Die Tastenbelegung wurde auf den Standard zurückgesetzt!
featureRestriction: featureRestriction:
title: Demo-Version title: Demoversion
desc: Du hast ein Feature benutzt (<feature>), welches nicht in der Demo desc: Du hast ein Feature benutzt (<feature>), welches nicht in der Demo
enthalten ist. Erwerbe die Vollversion für das volle Erlebnis! enthalten ist. Erwerbe die Vollversion für das ganze Erlebnis!
oneSavegameLimit: oneSavegameLimit:
title: Begrenzte Speicherstände title: Begrenzte Speicherstände
desc: Du kannst in der Demo nur einen Speicherstand haben. Bitte lösche den desc: Du kannst in der Demo nur einen Speicherstand haben. Bitte lösche den
@ -135,9 +200,10 @@ dialogs:
desc: "Hier sind die Änderungen, seitdem du das letzte Mal gespielt hast:" desc: "Hier sind die Änderungen, seitdem du das letzte Mal gespielt hast:"
upgradesIntroduction: upgradesIntroduction:
title: Upgrades freischalten title: Upgrades freischalten
desc: Viele deiner Formen können noch benutzt werden, um Upgrades freizuschalten desc: Viele deiner Formen können noch benutzt werden, um Upgrades
- <strong>Zerstöre deine alten Fabriken nicht!</strong> Den freizuschalten, also <strong>zerstöre deine alten Fabriken
Upgrade-Tab findest du oben rechts im Bildschirm. nicht!</strong> Den Upgrade-Tab findest du oben rechts im
Bildschirm.
massDeleteConfirm: massDeleteConfirm:
title: Löschen bestätigen title: Löschen bestätigen
desc: Du löscht viele Gebäude (<count> um genau zu sein)! Bist du dir sicher? desc: Du löscht viele Gebäude (<count> um genau zu sein)! Bist du dir sicher?
@ -157,25 +223,27 @@ dialogs:
desc: >- desc: >-
Dieses Spiel hat viele Hotkeys, die den Bau von Fabriken Dieses Spiel hat viele Hotkeys, die den Bau von Fabriken
vereinfachen und beschleunigen. Hier sind ein paar Beispiele, aber vereinfachen und beschleunigen. Hier sind ein paar Beispiele, aber
prüfe am besten die prüfe am besten die <strong>Tastenbelegung</strong> in den
<strong>Tastenbelegung-Einstellungen</strong>!<br><br> Einstellungen!<br><br>
<code class='keybinding'>STRG</code> + Ziehen: Wähle Bereich aus.<br> <code class='keybinding'>STRG</code> + Ziehen: Wähle Bereich aus.<br>
<code class='keybinding'>UMSCH</code>: Halten, um mehrere Gebäude zu platzieren.<br> <code class='keybinding'>UMSCH</code>: Halten, um mehrere Gebäude zu platzieren.<br>
<code class='keybinding'>ALT</code>: Invertiere die Platzierungsrichtung der Fließbänder.<br> <code class='keybinding'>ALT</code>: Invertiere die Platzierungsrichtung der Fließbänder.<br>
createMarker: createMarker:
title: Neuer Marker title: Neue Markierung
titleEdit: Marker bearbeiten titleEdit: Markierung bearbeiten
desc: Gib ihm einen griffigen Namen. Du kannst auch den desc: Gib ihm einen griffigen Namen. Du kannst auch den
<strong>Kurz-Code</strong> einer Form eingeben (Welchen du <strong>Kurzschlüssel</strong> einer Form eingeben
<link>hier</link> generieren kannst). (<link>Hier</link> geht es zum Generator).
editSignal: editSignal:
title: Signal setzen title: Signal setzen
descItems: "Wähle ein vordefiniertes Item:" descItems: "Wähle ein vordefiniertes Item:"
descShortKey: ... oder gib den <strong>Kurz-Code</strong> einer Form an (Welchen descShortKey: ... oder gib den <strong>Kurzschlüssel</strong> einer Form an
du <link>hier</link> generieren kannst). (<link>Hier</link> geht es zum Generator).
editConstantProducer:
title: Item wählen
markerDemoLimit: markerDemoLimit:
desc: Du kannst nur 2 Marker in der Demo benutzen. Hole dir die Vollversion, um desc: Du kannst nur 2 Markierungen in der Demo benutzen. Hole dir die
unendlich viele Marker zu erstellen! Vollversion, um unbegrenzt viele Markierungen zu setzen!
exportScreenshotWarning: exportScreenshotWarning:
title: Bildschirmfoto exportieren title: Bildschirmfoto exportieren
desc: Hier kannst du ein Bildschirmfoto von deiner ganzen Fabrik erstellen. Für desc: Hier kannst du ein Bildschirmfoto von deiner ganzen Fabrik erstellen. Für
@ -186,76 +254,73 @@ dialogs:
desc: Hier kannst du deinen Speicherstand umbenennen. desc: Hier kannst du deinen Speicherstand umbenennen.
tutorialVideoAvailable: tutorialVideoAvailable:
title: Tutorial verfügbar title: Tutorial verfügbar
desc: Für dieses Level ist ein Tutorial-Video verfügbar. Willst du es anschauen? desc: Für dieses Level ist ein Anleitungsvideo verfügbar. Willst du es
anschauen?
tutorialVideoAvailableForeignLanguage: tutorialVideoAvailableForeignLanguage:
title: Tutorial verfügbar title: Tutorial verfügbar
desc: Für dieses Level ist ein Tutorial-Video verfügbar, allerdings nur auf desc: Für dieses Level ist ein Anleitungsvideo verfügbar, allerdings nur auf
Englisch. Willst du es trotzdem anschauen? Englisch. Willst du es trotzdem anschauen?
editConstantProducer:
title: Item setzen
puzzleLoadFailed: puzzleLoadFailed:
title: Puzzle konnten nicht geladen werden title: Puzzles konnten nicht geladen werden
desc: "Leider konnten die Puzzle nicht geladen werden:" desc: "Leider konnten die Puzzles nicht geladen werden:"
submitPuzzle: submitPuzzle:
title: Puzzle veröffentlichen title: Puzzle veröffentlichen
descName: "Gib deinem Puzzle einen Namen:" descName: "Gib deinem Puzzle einen Namen:"
descIcon: "Bitte gib einen eindeutigen Kurzschlüssel ein, der als Symbol für descIcon: "Bitte gib einen eindeutigen Kurzschlüssel ein, der als Symbol für
dein Puzzle genutzt wird (Du kannst diesen auch <link>hier</link> dein Puzzle genutzt wird (<link>Hier</link> geht es zum Generator
generieren, oder wähle unten einen der zufällig generierten oder wähle unten einen der zufälligen Vorschläge):"
Vorschläge):" placeholderName: Puzzlename
placeholderName: Puzzle Name
puzzleResizeBadBuildings: puzzleResizeBadBuildings:
title: Größenänderung nicht möglich title: Größenänderung nicht möglich
desc: Du kannst die Zone nicht weiter verkleinern, da ansonsten einige Gebäude desc: Du kannst die Zone nicht weiter verkleinern, da ansonsten einige Gebäude
außerhalb der Zone liegen würden. außerhalb der erlaubten Zone liegen würden.
puzzleLoadError: puzzleLoadError:
title: Schlechtes Puzzle title: Fehlerhaftes Puzzle
desc: "Das Puzzle konnte nicht geladen werden:" desc: "Das Puzzle konnte nicht geladen werden:"
offlineMode: offlineMode:
title: Offline Modus title: Offlinemodus
desc: Die Server konnten nicht erreicht werden, daher läuft das Spiel im Offline desc: Die Server konnten nicht erreicht werden, daher läuft das Spiel im
Modus. Bitte sorge dafür, dass du eine aktive Internetverbindung Offlinemodus. Bitte stelle sicher, dass du eine aktive
hast. Internetverbindung hast.
puzzleDownloadError: puzzleDownloadError:
title: Download Fehler title: Downloadfehler
desc: "Der Download des Puzzles ist fehlgeschlagen:" desc: "Der Download des Puzzles ist fehlgeschlagen:"
puzzleSubmitError: puzzleSubmitError:
title: Übertragungsfehler title: Übermittlungsfehler
desc: "Das Puzzle konnte nicht übertragen werden:" desc: "Das Puzzle konnte nicht eingereicht werden:"
puzzleSubmitOk: puzzleSubmitOk:
title: Puzzle veröffentlicht title: Puzzle veröffentlicht
desc: Herzlichen Glückwunsch! Dein Rätsel wurde veröffentlicht und kann nun von desc: Herzlichen Glückwunsch! Dein Puzzle wurde veröffentlicht und kann nun von
anderen gespielt werden. Du kannst es jetzt im Bereich "Meine anderen gespielt werden. Du kannst es jetzt im Bereich "Meine
Puzzle" finden. Puzzles" finden.
puzzleCreateOffline: puzzleCreateOffline:
title: Offline Modus title: Offlinemodus
desc: Da du offline bist, bist du nicht in der Lage dei Puzzle zu speichern desc: Da du offline bist, kannst du dein Puzzle nicht speichern und/oder
und/oder zu veröffentlichen. Möchtest du trotzdem fortfahren? veröffentlichen. Möchtest du trotzdem fortfahren?
puzzlePlayRegularRecommendation: puzzlePlayRegularRecommendation:
title: Empfehlung title: Empfehlung
desc: ch empfehle <strong>stark</strong>, das normale Spiel bis Level 12 zu desc: Ich empfehle dir <strong>sehr</strong>, bis Level 12 zu spielen, bevor du
spielen, bevor du dich an das Puzzle DLC wagst, sonst stößt du dich an das Puzzle-DLC wagst. Du stößt sonst möglicherweise auf dir
möglicherweise auf noch nicht eingeführte Mechaniken. Möchtest du noch nicht bekannte Mechaniken. Möchtest du trotzdem fortfahren?
trotzdem fortfahren?
puzzleShare: puzzleShare:
title: Kurzschlüssel kopiert title: Kurzschlüssel kopiert
desc: Der Kurzschlüssel des Puzzles (<key>) wurde in die Zwischenablage kopiert! desc: Der Kurzschlüssel des Puzzles (<key>) wurde in die Zwischenablage kopiert!
Dieser kann im Puzzle Menü genutzt werden, um das Puzzle zu laden. Dieser kann im Puzzlemenü genutzt werden, um das Puzzle zu laden.
puzzleReport: puzzleReport:
title: Puzzle Melden title: Puzzle melden
options: options:
profane: Profan profane: Missbrauch
unsolvable: Nicht lösbar unsolvable: Nicht lösbar
trolling: Trolling trolling: Trolling
puzzleReportComplete: puzzleReportComplete:
title: Danke für das Feedback! title: Danke für dein Feedback!
desc: Das Puzzle wurde markiert. desc: Das Puzzle wurde vermerkt.
puzzleReportError: puzzleReportError:
title: Melden fehlgeschlagen title: Melden fehlgeschlagen
desc: "Deine Meldung konnte nicht verarbeitet werden:" desc: "Deine Meldung konnte nicht verarbeitet werden:"
puzzleLoadShortKey: puzzleLoadShortKey:
title: Kurzschlüssel eingeben title: Kurzschlüssel eingeben
desc: Trage einen Kurzschlüssel ein um das Puzzle zu laden. desc: Trage einen Kurzschlüssel ein, um das Puzzle zu laden.
puzzleDelete: puzzleDelete:
title: Puzzle löschen? title: Puzzle löschen?
desc: Bist du sicher, dass du '<title>' löschen möchtest? Dies kann nicht desc: Bist du sicher, dass du '<title>' löschen möchtest? Dies kann nicht
@ -263,7 +328,7 @@ dialogs:
ingame: ingame:
keybindingsOverlay: keybindingsOverlay:
moveMap: Bewegen moveMap: Bewegen
selectBuildings: Areal markieren selectBuildings: Bereich markieren
stopPlacement: Platzierung stoppen stopPlacement: Platzierung stoppen
rotateBuilding: Gebäude rotieren rotateBuilding: Gebäude rotieren
placeMultiple: Mehrere platzieren placeMultiple: Mehrere platzieren
@ -271,17 +336,17 @@ ingame:
disableAutoOrientation: Auto-Orientierung deaktivieren disableAutoOrientation: Auto-Orientierung deaktivieren
toggleHud: HUD-Sichtbarkeit an/aus toggleHud: HUD-Sichtbarkeit an/aus
placeBuilding: Gebäude platzieren placeBuilding: Gebäude platzieren
createMarker: Marker erstellen createMarker: Markierung erstellen
delete: Löschen delete: Löschen
pasteLastBlueprint: Letzte Blaupause einfügen pasteLastBlueprint: Letzte Blaupause einfügen
lockBeltDirection: Bandplaner aktivieren lockBeltDirection: Bandplaner aktivieren
plannerSwitchSide: "Planer: Seite wechseln" plannerSwitchSide: "Planer: Seite wechseln"
cutSelection: Ausschneiden cutSelection: Ausschneiden
copySelection: Kopieren copySelection: Kopieren
clearBelts: Fließbänder räumen
clearSelection: Auswahl aufheben clearSelection: Auswahl aufheben
pipette: Pipette pipette: Pipette
switchLayers: Ebenen wechseln switchLayers: Ebenen wechseln
clearBelts: Clear belts
colors: colors:
red: Rot red: Rot
green: Grün green: Grün
@ -293,8 +358,8 @@ ingame:
black: Schwarz black: Schwarz
uncolored: Grau uncolored: Grau
buildingPlacement: buildingPlacement:
cycleBuildingVariants: Drücke <key> zum Wechseln cycleBuildingVariants: Drücke <key> zum Wechseln.
hotkeyLabel: "Taste: <key>" hotkeyLabel: "Hotkey: <key>"
infoTexts: infoTexts:
speed: Geschw. speed: Geschw.
range: Reichweite range: Reichweite
@ -302,7 +367,7 @@ ingame:
oneItemPerSecond: 1 Item / s oneItemPerSecond: 1 Item / s
itemsPerSecond: <x> Items / s itemsPerSecond: <x> Items / s
itemsPerSecondDouble: (x2) itemsPerSecondDouble: (x2)
tiles: <x> Felder tiles: <x> Kacheln
levelCompleteNotification: levelCompleteNotification:
levelTitle: Level <level> levelTitle: Level <level>
completed: Abgeschlossen completed: Abgeschlossen
@ -345,13 +410,13 @@ ingame:
blueprintPlacer: blueprintPlacer:
cost: Kosten cost: Kosten
waypoints: waypoints:
waypoints: Marker waypoints: Markierungen
hub: Hub hub: Hub
description: Linksklick auf einen Marker, um dort hinzugelangen. Rechtsklick, um description: Linksklick auf eine Markierung, um dort hinzugelangen. Rechtsklick,
ihn zu löschen.<br><br>Drücke <keybinding>, um einen Marker aus um sie zu löschen.<br><br>Drücke <keybinding>, um eine Markierung
deinem Blickwinkel, oder <strong>rechtsklicke</strong>, um einen aus deinem Blickwinkel, oder <strong>rechtsklicke</strong>, um eine
Marker auf der ausgewählten Position zu erschaffen. Markierung auf der ausgewählten Position zu erschaffen.
creationSuccessNotification: Marker wurde erstellt. creationSuccessNotification: Markierung wurde erstellt.
shapeViewer: shapeViewer:
title: Ebenen title: Ebenen
empty: Leer empty: Leer
@ -359,47 +424,48 @@ ingame:
interactiveTutorial: interactiveTutorial:
title: Einführung title: Einführung
hints: hints:
1_1_extractor: Platziere einen <strong>Extrahierer</strong> auf der 1_1_extractor: Platziere einen <strong>Extraktor</strong> auf der
<strong>Kreisform</strong>, um sie zu extrahieren! <strong>Kreisform</strong>, um sie zu extrahieren!
1_2_conveyor: "Verbinde den Extrahierer mit einem <strong>Fließband</strong> und 1_2_conveyor: "Verbinde den Extraktor mit einem <strong>Fließband</strong> und
schließe ihn am Hub an!<br><br>Tipp: <strong>Drücke und schließe ihn am Hub an!<br><br>Tipp: <strong>Drücke und
ziehe</strong> das Fließband 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 1_3_expand: "Dies ist <strong>KEIN</strong> Idle-Game! Baue mehr Extraktoren und
Fließbänder, um das Ziel schneller zu erreichen.<br><br>Tipp: Fließbänder, um das Ziel schneller zu erreichen.<br><br>Tipp:
Halte <strong>UMSCH</strong>, um mehrere Gebäude zu platzieren Halte <strong>UMSCH</strong>, um mehrere Gebäude zu platzieren
und nutze <strong>R</strong>, um sie zu rotieren." und nutze <strong>R</strong>, um sie zu rotieren."
2_1_place_cutter: "Platziere nun einen <strong>Schneider</strong> um die Kreise 2_1_place_cutter: "Platziere nun einen <strong>Schneider</strong>, um die Kreise
in zwei Hälften zu zerteilen.<br><br> Übrigens: Der Schneider zu halbieren.<br><br> Übrigens: Der Schneider teilt Items immer
schneidet immer von <strong>oben nach unten</strong>, unabhängig von <strong>oben nach unten</strong>, unabhängig von seiner
seiner Orientierung!" Orientierung!"
2_2_place_trash: Der Schneider kann <strong>verstopfen</strong>!<br><br>Benutze 2_2_place_trash: Der Schneider kann <strong>verstopfen</strong>!<br><br>Benutze
einen <strong>Mülleimer</strong> um den aktuell (!) nicht einen <strong>Mülleimer</strong> um den aktuell (!) nicht
benötigten Rest loszuwerden. benötigten Rest loszuwerden.
2_3_more_cutters: "Gut gemacht! Platziere noch <strong>2 mehr Schneider</strong> 2_3_more_cutters: "Gut gemacht! Platziere noch <strong>zwei andere
um das ganze zu beschleunigen.<br><br> Übrigens: Benutze die Schneider</strong> um das Ganze zu beschleunigen.<br><br>
<strong>Tasten 0-9</strong> um Gebäude schneller auszuwählen!" Übrigens: Benutze die <strong>Hotkeys 0-9</strong> um Gebäude
3_1_rectangles: "Lass uns ein paar Quadrate extrahieren! <strong>Baue 4 schneller auszuwählen!"
Extrahierer</strong> und verbinde sie mit deinem HUB.<br><br> 3_1_rectangles: "Lass uns ein paar Quadrate extrahieren! <strong>Baue vier
PS: Halte <strong>SHIFT</strong> während du ein Fließband Extraktoren</strong> und verbinde sie mit deinem Hub.<br><br>
PS: Halte <strong>SHIFT</strong>, während du ein Fließband
ziehst, um den Fließbandplaner zu aktivieren!" ziehst, um den Fließbandplaner zu aktivieren!"
21_1_place_quad_painter: Platzier den <strong>Vierfach-Färber</strong> und 21_1_place_quad_painter: Platziere den <strong>vierfachen Färber</strong> und
organisier ein paar <strong>Kreise</strong>, organisiere ein paar <strong>Kreise</strong>, sowie
<strong>weiße</strong> und <strong>rote</strong> Farbe! <strong>weiße</strong> und <strong>rote</strong> Farbe!
21_2_switch_to_wires: Wechsle in die Wires-Ebene indem du <strong>E</strong> 21_2_switch_to_wires: Wechsle in die Wires-Ebene, indem du <strong>E</strong>
drückst!<br><br> Verbinde danach <strong>alle vier drückst!<br><br> Verbinde danach <strong>alle vier
Eingänge</strong> mit Signalkabeln! Eingänge</strong> mit Signalkabeln!
21_3_place_button: Perfekt! Platziere jetzt einen <strong>Schalter</strong> und 21_3_place_button: Perfekt! Platziere jetzt einen <strong>Schalter</strong> und
verbinde ihn mit Signalkabeln. verbinde ihn mit Signalkabeln.
21_4_press_button: "Drücke den Schalter damit er ein <strong>wahres 21_4_press_button: "Drücke den Schalter, damit er ein <strong>wahres
Signal</strong> ausgibt, und damit den Färber aktiviert.<br><br> Signal</strong> ausgibt und den Färber aktiviert.<br><br> PS: Du
PS: Du musst nicht alle Eingänge verbinden! Probiere mal nur 2 musst nicht alle Eingänge verbinden! Probiere es auch mal mit
aus." zwei."
connectedMiners: connectedMiners:
one_miner: Ein Extrahierer one_miner: Ein Extraktor
n_miners: <amount> Extrahierer n_miners: <amount> Extraktoren
limited_items: Begrenzt auf <max_throughput> limited_items: Begrenzt auf <max_throughput>
watermark: watermark:
title: Demo-Version title: Demoversion
desc: Klicke hier, um die Vorteile der Vollversion zu sehen! desc: Klicke hier, um die Vorteile der Vollversion zu sehen!
get_on_steam: Zur Vollversion get_on_steam: Zur Vollversion
standaloneAdvantages: standaloneAdvantages:
@ -412,62 +478,60 @@ ingame:
buildings: buildings:
title: 18 Neue Gebäude title: 18 Neue Gebäude
desc: Automatisiere deine Fabrik! desc: Automatisiere deine Fabrik!
achievements:
title: Errungenschaften
desc: Hol sie dir alle!
upgrades: upgrades:
title: ∞ Upgrade-Stufen title: ∞ Upgrade-Stufen
desc: Diese Demo hat nur 5! desc: Diese Demo hat nur 5!
markers: markers:
title: ∞ Marker title: ∞ Markierungen
desc: Verliere nie den Überblick! desc: Verliere nie den Überblick!
wires: wires:
title: Wires-Ebene title: Wires-Ebene
desc: Eine ganz neue Dimension! desc: Eine ganz neue Dimension!
darkmode: darkmode:
title: Dark-Mode title: Dark-Mode
desc: Werde nicht mehr geblendet! desc: Gönn deinen Augen eine Auszeit!
support: support:
title: Unterstütze Mich title: Unterstütze Mich
desc: Ich entwickle in meiner Freizeit! desc: Ich entwickle das Spiel in meiner Freizeit!
achievements:
title: Errungenschaften
desc: Hol sie dir alle!
puzzleEditorSettings: puzzleEditorSettings:
zoneTitle: Zone zoneTitle: Zone
zoneWidth: Breite zoneWidth: Breite
zoneHeight: Höhe zoneHeight: Höhe
trimZone: Zuschneiden trimZone: Zuschneiden
clearItems: Items löschen clearItems: Items löschen
clearBuildings: Gebäude löschen
resetPuzzle: Puzzle zurücksetzen
share: Teilen share: Teilen
report: Melden report: Melden
clearBuildings: Clear Buildings
resetPuzzle: Reset Puzzle
puzzleEditorControls: puzzleEditorControls:
title: Puzzle Editor title: Puzzle-Editor
instructions: instructions:
- 1. Plaziere einen <strong>Item-Produzent</strong> um Shapes und - 1. Platziere einen <strong>Itemproduzenten</strong> um Formen und
Farben für den Spieler bereitzustellen Farben für den Spieler bereitzustellen
- 2. Produziere ein oder mehrere Shapes, die der Spieler herstellen - 2. Produziere eine oder mehrere Formen, die der Spieler herstellen
soll und liefere dieze zu einem oder mehreren soll und liefere diese zu einem oder mehreren
<strong>Ziel-Akzeptoren</strong> <strong>Zielakzeptoren</strong>
- 3. Sobald ein Ziel-Akzeptor ein Shape für eine gewisse Zeit - 3. Sobald ein Zielakzeptor eine Form für eine gewisse Zeit erhält,
erhällt, <strong>speichert dieser es als Ziel</strong>, welches <strong>speichert dieser sie als Ziel</strong> (Angezeigt durch
der Spieler später herstellen muss (Angezeigt durch den den <strong>grünen Punkt</strong>).
<strong>grünen Punkt</strong>). - 4. Klicke auf das <strong>Schloss</strong>, um ein Gebäude für den
- 4. Klicke den <strong>sperren Button</strong> um die Gebäude zu Spieler zu sperren.
sperren. - 5. Sobald du auf Überprüfen gedrückt hast, wird dein Puzzle
- 5. Sobald du auf Überprüfen gedrückt hast, wird dei Puzzel geprüft `validiert und du kannst es veröffentlichen.
und du kannst es veröffentlichen.
- 6. Bei der Freigabe werden <strong>alle Gebäude entfernt</strong>. - 6. Bei der Freigabe werden <strong>alle Gebäude entfernt</strong>.
Ausgenommen sind die Produzenten und Akzeptoren - Das ist Ausgenommen sind die Produzenten und Akzeptoren. Den Rest soll der
schließlich der Teil, den die Spieler selbst herausfinden sollen Spieler schließlich selbst herausfinden :)
:)
puzzleCompletion: puzzleCompletion:
title: Puzzle abgeschlossen! title: Puzzle geschafft!
titleLike: "Klicke auf das Herz, wenn dier das Puzzle gefallen hat:" titleLike: "Klicke auf das Herz, wenn dir das Puzzle gefallen hat:"
titleRating: Wie schwierig fandest du das Puzzle? titleRating: Wie schwierig fandest du das Puzzle?
titleRatingDesc: Deine Bewertung wird mir helfen, in Zukunft bessere Vorschläge titleRatingDesc: Deine Bewertung hilft mir, in Zukunft bessere Vorschläge zu machen
zu machen continueBtn: Weiterspielen
continueBtn: Weiter spielen
menuBtn: Menü menuBtn: Menü
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Ersteller author: Ersteller
shortKey: Kurzschlüssel shortKey: Kurzschlüssel
@ -479,7 +543,7 @@ shopUpgrades:
name: Fließbänder, Verteiler & Tunnel name: Fließbänder, Verteiler & Tunnel
description: Geschw. x<currentMult> → x<newMult> description: Geschw. x<currentMult> → x<newMult>
miner: miner:
name: Extrahierer name: Extraktor
description: Geschw. x<currentMult> → x<newMult> description: Geschw. x<currentMult> → x<newMult>
processors: processors:
name: Schneider, Rotierer & Stapler name: Schneider, Rotierer & Stapler
@ -499,10 +563,10 @@ buildings:
description: Transportiert Items. Halte und ziehe, um mehrere zu platzieren. description: Transportiert Items. Halte und ziehe, um mehrere zu platzieren.
miner: miner:
default: default:
name: Extrahierer name: Extraktor
description: Platziere ihn auf einer Form oder Farbe, um sie zu extrahieren. description: Platziere ihn auf einer Form oder Farbe, um sie zu extrahieren.
chainable: chainable:
name: Extrahierer (Kette) name: Extraktor (Kette)
description: Platziere ihn auf einer Form oder Farbe, um sie zu extrahieren. description: Platziere ihn auf einer Form oder Farbe, um sie zu extrahieren.
Kann verkettet werden. Kann verkettet werden.
underground_belt: underground_belt:
@ -654,9 +718,9 @@ buildings:
reader: reader:
default: default:
name: Fließbandkontrolle name: Fließbandkontrolle
description: Misst den gemittelten Durchsatz des Fließbandes. Gibt zuätzlich den description: Misst den gemittelten Durchsatz des Fließbandes. Gibt zusätzlich
zuletzt passierten Gegenstand auf der Wires-Ebene aus (sobald den zuletzt passierten Gegenstand auf der Wires-Ebene aus
freigeschaltet). (sobald freigeschaltet).
analyzer: analyzer:
default: default:
name: Formanalyse name: Formanalyse
@ -687,30 +751,30 @@ buildings:
rechten Eingang. rechten Eingang.
item_producer: item_producer:
default: default:
name: Item-Produzent name: Itemproduzent
description: Nur im Sandkastenmodus verfügbar. Gibt das Signal aus der description: Nur im Sandkastenmodus verfügbar. Gibt das Signal aus der
Wires-Ebene als Item aus. Wires-Ebene als Item aus.
constant_producer: constant_producer:
default: default:
name: Item-Produzent name: Itemproduzent
description: Gibt dauerhaft ein Shape oder eine Farbe aus. description: Gibt dauerhaft ein Form oder eine Farbe aus.
goal_acceptor: goal_acceptor:
default: default:
name: Ziel Akzeptor name: Zielakzeptor
description: Liefere ein Shape an, um dieses als Ziel festzulegen. description: Liefere gleichartige Formen, um diese als Ziel festzulegen.
block: block:
default: default:
name: Sperre name: Sperrblock
description: Ermöglicht das Blockieren einer Kachel. description: Ermöglicht das Blockieren einer Kachel.
storyRewards: storyRewards:
reward_cutter_and_trash: reward_cutter_and_trash:
title: Formen zerschneiden title: Formen zerschneiden
desc: Du hast gerade den <strong>Schneider</strong> freigeschaltet, der Formen desc: Du hast gerade den <strong>Schneider</strong> freigeschaltet, der Formen
in zwei Hälften schneidet, von oben nach unten, <b>unabhängig der vertikal halbiert, <b>unabhängig von seiner
Orientierung</b>!<br><br>Achte darauf, den Abfall loszuwerden, oder Orientierung</b>!<br><br> Achte darauf, die Überreste loszuwerden,
<b>er wird verstopfen und blockieren</b> - Zu diesem Zweck habe ich sonst <b>verstopft und blockiert er</b> - Zu diesem Zweck habe ich
dir den <strong>Mülleimer</strong> gegeben, der alles entsorgt, was dir den <strong>Mülleimer</strong> gegeben. Er entsorgt alles, was
du hineintust! du ihm fütterst!
reward_rotater: reward_rotater:
title: Rotieren title: Rotieren
desc: Der <strong>Rotierer</strong> wurde freigeschaltet! Er rotiert Formen im desc: Der <strong>Rotierer</strong> wurde freigeschaltet! Er rotiert Formen im
@ -734,25 +798,25 @@ storyRewards:
<strong>gestapelt</strong>. <strong>gestapelt</strong>.
reward_balancer: reward_balancer:
title: Verteiler title: Verteiler
desc: Der multifunktionale <strong>Verteiler</strong> wurde freigeschaltet! Er desc: Der multifunktionale <strong>Verteiler</strong> wurde freigeschaltet!
kann benutzt werden, um größere Fabriken zu bauen, indem Fließbänder Damit kannst du nun endlich größere Fabriken bauen. Er kann
<strong>aufgeteilt oder zusammengelegt</strong> werden! Fließbänder <strong>aufteilen oder zusammenlegen</strong>!
reward_tunnel: reward_tunnel:
title: Tunnel title: Tunnel
desc: Der <strong>Tunnel</strong> wurde freigeschaltet! Du kannst Items nun desc: Der <strong>Tunnel</strong> wurde freigeschaltet! Du kannst Items nun
unter Gebäuden oder Fließbändern hindurchleiten. unter Gebäuden oder Fließbändern hindurchleiten.
reward_rotater_ccw: reward_rotater_ccw:
title: Gegen UZS Rotieren title: Gegen den UZS rotieren
desc: Du hast eine zweite Variante des <strong>Rotierers</strong> desc: Du hast eine zweite Variante des <strong>Rotierers</strong>
freigeschaltet! Damit können Items gegen den Uhrzeigensinn gedreht freigeschaltet! Damit können Items gegen den Uhrzeigensinn gedreht
werden. Wähle den Rotierer aus und <strong>drücke 'T', um auf werden. Wähle den Rotierer aus und <strong>drücke 'T', um auf
verschiedene Varianten zuzugreifen</strong>. verschiedene Varianten zuzugreifen</strong>.
reward_miner_chainable: reward_miner_chainable:
title: Extrahierer (Kette) title: Extraktor (Kette)
desc: "Du hast den <strong>Kettenextrahierer</strong> freigeschaltet! Er kann desc: "Du hast den <strong>Kettenextraktor</strong> freigeschaltet! Er kann
seine Ressourcen an andere Extrahierer seine Ressourcen an andere Extraktoren <strong>weitergeben</strong>.
<strong>weiterleiten</strong>. <br><br> PS: Der alte Extrahierer <br><br> PS: Der alte Extraktor wurde jetzt in deiner Symbolleiste
wurde jetzt in deiner Symbolleiste ersetzt!" ersetzt!"
reward_underground_belt_tier_2: reward_underground_belt_tier_2:
title: Tunnel Stufe II title: Tunnel Stufe II
desc: Du hast eine neue Variante des <strong>Tunnels</strong> freigeschaltet! desc: Du hast eine neue Variante des <strong>Tunnels</strong> freigeschaltet!
@ -811,8 +875,8 @@ storyRewards:
<strong>vierfachen Färber</strong>. Schließe die Eingänge, mit denen <strong>vierfachen Färber</strong>. Schließe die Eingänge, mit denen
du die Quadranten färben möchtest, an ein Signalkabel auf der du die Quadranten färben möchtest, an ein Signalkabel auf der
Wires-Ebene an!<br><br> Mit <strong>E</strong> wechselst du zwischen Wires-Ebene an!<br><br> Mit <strong>E</strong> wechselst du zwischen
den Ebenen. <br><br>PS: <strong>Aktiviere Hinweise</strong> in den den Ebenen. <br><br>PS: <strong>Aktiviere die Hinweise</strong> in
Einstellungen um das Tutorial anzuzeigen!" den Einstellungen, um das Tutorial anzuzeigen!"
reward_filter: reward_filter:
title: Itemfilter title: Itemfilter
desc: Du hast den <strong>Itemfilter</strong> freigeschaltet! Items, die dem desc: Du hast den <strong>Itemfilter</strong> freigeschaltet! Items, die dem
@ -841,7 +905,7 @@ storyRewards:
ODER-, XODER- und NICHT-Operationen ausführen.<br><br> Als ODER-, XODER- und NICHT-Operationen ausführen.<br><br> Als
Sahnehäubchen obendrauf stelle ich dir noch einen Sahnehäubchen obendrauf stelle ich dir noch einen
<strong>Transistor</strong> zur Verfügung. Houston, wir sind <strong>Transistor</strong> zur Verfügung. Houston, wir sind
Turing-vollständig! turingmächtig!
reward_virtual_processing: reward_virtual_processing:
title: Virtuelle Verarbeitung title: Virtuelle Verarbeitung
desc: "Du hast gerade eine Menge neue Gebäude freigeschaltet! Mit ihnen kannst desc: "Du hast gerade eine Menge neue Gebäude freigeschaltet! Mit ihnen kannst
@ -853,8 +917,8 @@ storyRewards:
beliebige Form am Hub abgreift und herstellt. (Probiere es beliebige Form am Hub abgreift und herstellt. (Probiere es
wenigstens!)<br><br> - Werde kreativ und lasse dir etwas Cooles wenigstens!)<br><br> - Werde kreativ und lasse dir etwas Cooles
einfallen, das du auf der Wires-Ebene umsetzen kannst. (Und teile es einfallen, das du auf der Wires-Ebene umsetzen kannst. (Und teile es
auf dem Discord!)<br><br> - Spiele dich weiter durch die Level. Auf auf dem Discord!)<br><br> - Spiele dich weiter durch die Levels. Auf
deine Art!<br><br> Das Wichstigste an deiner Entscheidung ist: deine Art!<br><br> Das Wichtigste an deiner Entscheidung ist:
Vergiss nicht, dabei Spaß zu haben!" Vergiss nicht, dabei Spaß zu haben!"
no_reward: no_reward:
title: Nächstes Level title: Nächstes Level
@ -867,14 +931,14 @@ storyRewards:
desc: Du hast das nächste Level freigeschaltet! desc: Du hast das nächste Level freigeschaltet!
reward_freeplay: reward_freeplay:
title: Freies Spiel title: Freies Spiel
desc: Du hast es geschafft! Du bist im <strong>Freispiel-Modus</strong> desc: Du hast es geschafft! Du bist im <strong>Freispielmodus</strong>
angekommen! Das bedeutet, dass die abzuliefernden Formen jetzt angekommen! Das bedeutet, dass die abzuliefernden Formen jetzt
<strong>zufällig</strong> erzeugt werden!<br><br> Da der Hub ab <strong>zufällig</strong> erzeugt werden!<br><br> Da der Hub ab
jetzt einen bestimmten <strong>Durchsatz</strong> benötigt, empfehle jetzt einen bestimmten <strong>Durchsatz</strong> benötigt, empfehle
ich dringend, eine Maschine zu bauen, die automatisch die gewünschte ich dringend, eine Maschine zu bauen, die automatisch die gewünschte
Form liefert!<br><br> Der Hub gibt die gewünschte Form auf der Form liefert!<br><br> Der Hub gibt die gewünschte Form auf der
Wires-Ebene aus. Also musst du sie nur analysieren und basierend Wires-Ebene aus, also musst du sie nur analysieren und deine Fabrik
darauf automatisch deine Fabrik konfigurieren. dementsprechend konfigurieren (lassen).
reward_demo_end: reward_demo_end:
title: Ende der Demo title: Ende der Demo
desc: Du bist am Ende der Demo angekommen! desc: Du bist am Ende der Demo angekommen!
@ -894,7 +958,7 @@ settings:
labels: labels:
uiScale: uiScale:
title: HUD Größe title: HUD Größe
description: Ändert die Größe der Benutzeroberfläche, basierend auf der description: Ändert die Größe der Benutzeroberfläche basierend auf der
Bildschirmauflösung. Bildschirmauflösung.
scales: scales:
super_small: Sehr klein super_small: Sehr klein
@ -915,7 +979,7 @@ settings:
disabled: Deaktiviert disabled: Deaktiviert
scrollWheelSensitivity: scrollWheelSensitivity:
title: Zoomempfindlichkeit title: Zoomempfindlichkeit
description: Ändert die Empfindlichkeit des Zooms (Sowohl Mausrad, als auch description: Ändert die Empfindlichkeit des Zooms (Sowohl Mausrad als auch
Trackpad). Trackpad).
sensitivity: sensitivity:
super_slow: Sehr langsam super_slow: Sehr langsam
@ -945,7 +1009,7 @@ settings:
fullscreen: fullscreen:
title: Vollbild title: Vollbild
description: Für das beste Erlebnis im Spiel wird der Vollbildmodus empfohlen description: Für das beste Erlebnis im Spiel wird der Vollbildmodus empfohlen
(Nur in der Standalone-Version verfügbar). (Nur in der Vollversion verfügbar).
soundsMuted: soundsMuted:
title: Geräusche stummschalten title: Geräusche stummschalten
description: Bei Aktivierung werden alle Geräusche stummgeschaltet. description: Bei Aktivierung werden alle Geräusche stummgeschaltet.
@ -978,18 +1042,20 @@ settings:
halten. halten.
offerHints: offerHints:
title: Hinweise & Tutorials title: Hinweise & Tutorials
description: Schaltet Hinweise und das Tutorial beim Spielen an und aus. description: Schaltet Hinweise und das Tutorial beim Spielen an oder aus.
Außerdem werden zu den Levels bestimmte Textfelder versteckt, Außerdem werden zu den Levels bestimmte Textfelder versteckt,
die den Einstieg erleichtern sollen. die dir den Einstieg erleichtern sollen.
enableTunnelSmartplace: enableTunnelSmartplace:
title: Intelligente Tunnel title: Intelligente Tunnel
description: Aktiviert das automatische Entfernen von überflüssigen Fließbändern description: Aktiviert das automatische Entfernen von überflüssigen Fließbändern
bei der Platzierung von Tunneln. Außerdem funktioniert das bei der Platzierung von Tunneln. Außerdem funktioniert das
Ziehen von Tunneln und überschüssige werden ebenfalls entfernt. Ziehen von Tunneln und überschüssige Tunnel werden ebenfalls
entfernt.
vignette: vignette:
title: Vignette title: Vignette
description: Aktiviert den Vignetteneffekt, der den Rand des Bildschirms description: Aktiviert den Vignetteneffekt, der den Rand des Bildschirms
zunehmend verdunkelt und das Lesen der Textfelder vereinfacht. zunehmend verdunkelt und damit das Lesen der Textfelder
vereinfacht.
rotationByBuilding: rotationByBuilding:
title: Rotation pro Gebäudetyp title: Rotation pro Gebäudetyp
description: Jeder Gebäudetyp merkt sich eigenständig, in welche Richtung er description: Jeder Gebäudetyp merkt sich eigenständig, in welche Richtung er
@ -1001,12 +1067,12 @@ settings:
Arbeitsgeschwindigkeit. Anderenfalls wird ein Bild mit Arbeitsgeschwindigkeit. Anderenfalls wird ein Bild mit
Beschreibung angezeigt. Beschreibung angezeigt.
disableCutDeleteWarnings: disableCutDeleteWarnings:
title: Deaktiviere Warnungsdialog beim Löschen title: Deaktiviere Warndialog beim Löschen
description: Deaktiviert die Warnung, welche beim Löschen und Ausschneiden von description: Deaktiviert die Warnung, welche beim Löschen und Ausschneiden von
mehr als 100 Feldern angezeigt wird. mehr als 100 Kacheln angezeigt wird.
lowQualityMapResources: lowQualityMapResources:
title: Minimalistische Ressourcen title: Minimalistische Ressourcen
description: Vereinfacht die Darstellung der Ressourcen auf der hereingezoomten description: Vereinfacht die Darstellung der Ressourcen auf der hineingezoomten
Karte zur Verbesserung der Leistung. Die Darstellung ist Karte zur Verbesserung der Leistung. Die Darstellung ist
übersichtlicher, also probiere es ruhig aus! übersichtlicher, also probiere es ruhig aus!
disableTileGrid: disableTileGrid:
@ -1015,23 +1081,24 @@ settings:
Außerdem vereinfacht es die Darstellung! Außerdem vereinfacht es die Darstellung!
clearCursorOnDeleteWhilePlacing: clearCursorOnDeleteWhilePlacing:
title: Abwählen mit Rechtsklick title: Abwählen mit Rechtsklick
description: Standardmäßig eingeschaltet, wählt es das aktuelle, zur Platzierung description: Ist standardmäßig eingeschaltet und wählt das aktuell ausgewählte
ausgewählte Gebäude ab, wenn du die rechte Masutaste drückst. Gebäude ab, wenn du die rechte Maustaste drückst. Wenn du es
Wenn du es abschaltest, kannst du mit der rechten Maustaste abschaltest, kannst du mit der rechten Maustaste Gebäude
Gebäude löschen, während du im Platzierungsmodus bist. löschen, während du im Platzierungsmodus bist.
lowQualityTextures: lowQualityTextures:
title: Niedrige Texturqualität (Unschön) title: Niedrige Texturqualität (Unschön)
description: Das Spiel verwendet eine niedrigere Auflösung bei den Texturen. description: Das Spiel verwendet eine niedrigere Auflösung bei den Texturen.
Allerdings leidet die Grafik des Spiels sehr darunter! Allerdings leidet die Grafik des Spiels sehr darunter!
displayChunkBorders: displayChunkBorders:
title: Chunk-Ränder anzeigen title: Chunk-Ränder anzeigen
description: Das Spiel ist in Blöcke (Chunks) aus je 16x16 Feldern aufgeteilt. description: Das Spiel ist in Blöcke (Chunks) aus je 16x16 Kacheln aufgeteilt.
Diese Einstellung lässt dich die Grenzen zwischen den Chunks Diese Einstellung lässt dich die Grenzen zwischen den Chunks
anzeigen. anzeigen.
pickMinerOnPatch: pickMinerOnPatch:
title: Automatisch Extrahierer auswählen title: Automatisch Extraktor auswählen
description: Standardmäßig eingeschaltet, wählst du automatisch den Extrahierer, description: Ist standardmäßig eingeschaltet und du wählst automatisch den
wenn du mit der Pipette auf einen Ressourcenfleck zeigst Extraktor, wenn du mit der Pipette auf einen Ressourcenfleck
zeigst.
simplifiedBelts: simplifiedBelts:
title: Minimalistische Fließbänder (Unschön) title: Minimalistische Fließbänder (Unschön)
description: Zur Verbesserung der Leistung werden die Items auf Fließbändern nur description: Zur Verbesserung der Leistung werden die Items auf Fließbändern nur
@ -1042,15 +1109,20 @@ settings:
title: Scrollen am Bildschirmrand title: Scrollen am Bildschirmrand
description: Damit kannst du dich über die Karte bewegen, indem du deinen description: Damit kannst du dich über die Karte bewegen, indem du deinen
Mauszeiger am Bildschirmrand platzierst. Die Geschwindigkeit Mauszeiger am Bildschirmrand platzierst. Die Geschwindigkeit
stimmt dabei mit den Tasten überein. stimmt dabei mit den Pfeiltasten überein.
zoomToCursor: zoomToCursor:
title: Zoom towards Cursor title: In Richtung des Cursors zoomen
description: If activated the zoom will happen in the direction of your mouse description: Wenn aktiviert, erfolgt der Zoom in Richtung deiner Mausposition,
position, otherwise in the middle of the screen. statt in die Mitte des Bildschirms.
mapResourcesScale: mapResourcesScale:
title: Größe der Ressourcen auf der Karte title: Größe der Ressourcen auf der Karte
description: Legt die Größe der Ressourcen auf der Karte (beim Herauszoomen) description: Legt die Größe der Ressourcen auf der Karte (beim Herauszoomen)
fest. fest.
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Tastenbelegung title: Tastenbelegung
hint: "Tipp: Benutze STRG, UMSCH and ALT! Sie aktivieren verschiedene hint: "Tipp: Benutze STRG, UMSCH and ALT! Sie aktivieren verschiedene
@ -1075,7 +1147,7 @@ keybindings:
centerMap: Karte zentrieren centerMap: Karte zentrieren
mapZoomIn: Reinzoomen mapZoomIn: Reinzoomen
mapZoomOut: Rauszoomen mapZoomOut: Rauszoomen
createMarker: Marker erstellen createMarker: Markierung erstellen
menuOpenShop: Upgrades menuOpenShop: Upgrades
menuOpenStats: Statistiken menuOpenStats: Statistiken
menuClose: Menü schließen menuClose: Menü schließen
@ -1086,7 +1158,7 @@ keybindings:
belt: Fließband belt: Fließband
balancer: Verteiler balancer: Verteiler
underground_belt: Tunnel underground_belt: Tunnel
miner: Extrahierer miner: Extraktor
cutter: Schneider cutter: Schneider
rotater: Rotierer (90°) rotater: Rotierer (90°)
stacker: Stapler stacker: Stapler
@ -1106,10 +1178,17 @@ keybindings:
transistor: Transistor transistor: Transistor
analyzer: Formanalyse analyzer: Formanalyse
comparator: Vergleich comparator: Vergleich
item_producer: Item-Produzent (Sandkastenmodus) item_producer: Itemproduzent (Sandkastenmodus)
constant_producer: Itemproduzent
goal_acceptor: Zielakzeptor
block: Sperrblock
pipette: Pipette pipette: Pipette
rotateWhilePlacing: Rotieren rotateWhilePlacing: Rotieren
rotateInverseModifier: "Modifikator: stattdessen gegen den UZS rotieren" rotateInverseModifier: "Modifikator: stattdessen gegen den UZS rotieren"
rotateToUp: "Rotieren: Nach oben zeigend"
rotateToDown: "Rotieren: Nach unten zeigend"
rotateToRight: "Rotieren: Nach rechts zeigend"
rotateToLeft: "Rotieren: Nach links zeigend"
cycleBuildingVariants: Nächste Variante auswählen cycleBuildingVariants: Nächste Variante auswählen
confirmMassDelete: Löschen bestätigen confirmMassDelete: Löschen bestätigen
pasteLastBlueprint: Letzte Blaupause einfügen pasteLastBlueprint: Letzte Blaupause einfügen
@ -1118,20 +1197,14 @@ keybindings:
switchDirectionLockSide: "Bandplaner: Seite wechseln" switchDirectionLockSide: "Bandplaner: Seite wechseln"
copyWireValue: "Kabel: Wert unter Mauszeiger kopieren" copyWireValue: "Kabel: Wert unter Mauszeiger kopieren"
massSelectStart: Halten und ziehen zum Beginnen massSelectStart: Halten und ziehen zum Beginnen
massSelectSelectMultiple: Mehrere Areale markieren massSelectSelectMultiple: Mehrere Bereiche markieren
massSelectCopy: Areal kopieren massSelectCopy: Bereich kopieren
massSelectCut: Areal ausschneiden massSelectCut: Bereich ausschneiden
massSelectClear: Fließbänder räumen
placementDisableAutoOrientation: Automatische Orientierung deaktivieren placementDisableAutoOrientation: Automatische Orientierung deaktivieren
placeMultiple: Im Platziermodus bleiben placeMultiple: Im Platzierungsmodus bleiben
placeInverse: Automatische Fließbandorientierung invertieren placeInverse: Automatische Fließbandorientierung invertieren
rotateToUp: "Rotieren: Nach oben zeigend" showShapeTooltip: Show shape output tooltip
rotateToDown: "Rotieren: Nach unten zeigend"
rotateToRight: "Rotieren: Nach rechts zeigend"
rotateToLeft: "Rotieren: Nach links zeigend"
constant_producer: Constant Producer
goal_acceptor: Goal Acceptor
block: Block
massSelectClear: Clear belts
about: about:
title: Über dieses Spiel title: Über dieses Spiel
body: Dieses Spiel ist quelloffen (Open Source) und wurde von <a body: Dieses Spiel ist quelloffen (Open Source) und wurde von <a
@ -1148,15 +1221,41 @@ about:
target="_blank">Niklas</a> danken! Ohne unsere etlichen gemeinsamen target="_blank">Niklas</a> danken! Ohne unsere etlichen gemeinsamen
Stunden in Factorio wäre dieses Projekt nie zustande gekommen. Stunden in Factorio wäre dieses Projekt nie zustande gekommen.
changelog: changelog:
title: Änderungen title: Änderungsprotokoll
demo: demo:
features: features:
restoringGames: Spiele wiederherstellen restoringGames: Spielstände wiederherstellen
importingGames: Spiele importieren importingGames: Spielstände importieren
oneGameLimit: Beschränkt auf einen Spielstand oneGameLimit: Beschränkt auf einen Spielstand
customizeKeybindings: Tastenbelegung anpassen customizeKeybindings: Tastenbelegung anpassen
exportingBase: Ganze Fabrik als Foto exportieren exportingBase: Ganze Fabrik als Foto exportieren
settingNotAvailable: Nicht verfügbar in der Demo. settingNotAvailable: Nicht verfügbar in der Demo.
backendErrors:
ratelimit: Du führst deine Aktionen zu schnell aus. Bitte warte kurz.
invalid-api-key: Kommunikation mit dem Back-End fehlgeschlagen. Versuche das
Spiel neu zu starten oder zu updaten (Ungültiger Api-Schlüssel).
unauthorized: Kommunikation mit dem Back-End fehlgeschlagen. Versuche das Spiel
neu zu starten oder zu updaten (Nicht autorisiert).
bad-token: Kommunikation mit dem Back-End fehlgeschlagen. Versuche das Spiel neu
zu starten oder zu updaten (Ungültiger Token).
bad-id: Ungültige Puzzle-ID.
not-found: Das Puzzle konnte nicht gefunden werden.
bad-category: Die Kategorie konnte nicht gefunden werden.
bad-short-key: Dieser Kurzschlüssel ist ungültig.
profane-title: Dein Puzzletitel enthält gesperrte Wörter.
bad-title-too-many-spaces: Dein Puzzletitel ist zu kurz.
bad-shape-key-in-emitter: Einem Itemproduzent wurde ein ungültiges Item zugewiesen.
bad-shape-key-in-goal: Einem Zielakzeptor wurde ein ungültiges Item zugewiesen.
no-emitters: Dein Puzzle enthält keine Itemproduzenten.
no-goals: Dein Puzzle enthält keine Zielakzeptoren.
short-key-already-taken: Dieser Kurzschlüssel ist bereits vergeben, bitte wähle einen anderen.
can-not-report-your-own-puzzle: Du kannst dein eigenes Puzzle nicht melden.
bad-payload: Die Anfrage beinhaltet ungültige Daten.
bad-building-placement: Dein Puzzle beinhaltet ungültig platzierte Gebäude.
timeout: Es kam zu einer Zeitüberschreitung bei der Anfrage.
too-many-likes-already: Dieses Puzzle ist in der Community sehr beliebt. Wenn du
es trotzdem löschen möchtest, wende dich bitte an support@shapez.io!
no-permission: Dir fehlt die Berechtigung, um diese Aktion auszuführen.
tips: tips:
- Der Hub akzeptiert alle Formen, nicht nur die aktuell geforderten! - Der Hub akzeptiert alle Formen, nicht nur die aktuell geforderten!
- Stelle sicher, dass deine Fabriken modular sind. Es zahlt sich irgendwann - Stelle sicher, dass deine Fabriken modular sind. Es zahlt sich irgendwann
@ -1179,14 +1278,14 @@ tips:
- Der Färber hat eine spiegelverkehrte Variante, die du mit <b>T</b> - Der Färber hat eine spiegelverkehrte Variante, die du mit <b>T</b>
auswählen kannst. auswählen kannst.
- Das richtige Verhältnis der Gebäude maximiert die Effizienz. - Das richtige Verhältnis der Gebäude maximiert die Effizienz.
- Auf der gleichen Upgrade-Stufe genügen 5 Extrahierer für ein ganzes - Auf der gleichen Upgrade-Stufe genügen 5 Extraktoren für ein ganzes
Fließband. Fließband.
- Vergiss die Tunnel nicht! - Vergiss die Tunnel nicht!
- Für maximale Effizienz musst du die Items nicht gleichmässig aufteilen. - Für maximale Effizienz musst du die Items nicht gleichmässig aufteilen.
- Das Halten von <b>UMSCH</b> aktiviert den Bandplaner, der lange - Das Halten von <b>UMSCH</b> aktiviert den Bandplaner, der lange
Fließbänder ganz einfach platziert. Fließbänder ganz einfach platziert.
- Schneider teilen die Form immer vertikal, unabhängig von der Orientierung. - Schneider teilen die Form immer vertikal, unabhängig von der Orientierung.
- Weiß erhälst du aus der Kombination aller 3 Grundfarben. - Weiß erhältst du aus der Kombination aller 3 Grundfarben.
- Das Lager gibt Items immer zuerst am linken Ausgang ab. - Das Lager gibt Items immer zuerst am linken Ausgang ab.
- Es lohnt sich, Zeit in den Bau von wiederverwendbaren Designs zu stecken! - 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. - Das Halten von <b>STRG</b> ermöglicht dir, mehrere Gebäude zu platzieren.
@ -1220,9 +1319,10 @@ tips:
- Die Reißzwecke neben Formen in der Upgrade-Liste lässt sie dich am - Die Reißzwecke neben Formen in der Upgrade-Liste lässt sie dich am
Bildschirm anheften. Bildschirm anheften.
- Mische alle drei Grundfarben, um Weiß zu erhalten! - Mische alle drei Grundfarben, um Weiß zu erhalten!
- Du hast eine unendlich grosse Karte, nutze den Platz und expandiere! - Du hast eine unendlich große Karte, nutze den Platz und expandiere!
- Probier auch mal Factorio! Es ist mein Lieblingsspiel. - Probier auch mal Factorio! Es ist mein Lieblingsspiel.
- Der Vierfachschneider schneidet im Uhrzeigersinn von oben rechts beginnend! - Der vierfache Schneider schneidet im Uhrzeigersinn von oben rechts
beginnend!
- Du kannst deine Speicherstände im Hauptmenü herunterladen! - Du kannst deine Speicherstände im Hauptmenü herunterladen!
- Diese Spiel hat viele nützliche Tastenbelegungen! Schau sie dir in den - Diese Spiel hat viele nützliche Tastenbelegungen! Schau sie dir in den
Einstellungen an. Einstellungen an.
@ -1235,76 +1335,3 @@ tips:
- Drücke doppelt F4 um den Standort des Mauszeigers und der Kamera zu - Drücke doppelt F4 um den Standort des Mauszeigers und der Kamera zu
bestimmen. bestimmen.
- Du kannst die angehefteten Formen am linken Rand wieder entfernen. - Du kannst die angehefteten Formen am linken Rand wieder entfernen.
puzzleMenu:
play: Spielen
edit: bearbeiten
title: Puzzle Modus
createPuzzle: Puzzle erstellen
loadPuzzle: Laden
reviewPuzzle: Überprüfen & Veröffentlichen
validatingPuzzle: Puzzle wird überprüft
submittingPuzzle: Puzzle wird veröffentlicht
noPuzzles: Hier gibt es bisher noch keine Puzzles.
categories:
levels: Levels
new: Neu
top-rated: Am besten bewertet
mine: Meine Puzzles
easy: Einfach
hard: Schwierig
completed: Abgeschlossen
medium: Mittel
official: Vorgestellt
trending: Trend - Heute
trending-weekly: Trend - Woche
categories: Kategorien
difficulties: Nach Schwierigkeit
account: Meine Puzzle
search: Suche
validation:
title: Ungültiges Puzzle
noProducers: Bitte plaziere einen Item-Produzent!
noGoalAcceptors: Bitte plaziere einen Ziel-Akzeptor!
goalAcceptorNoItem: Einer oder mehrere Ziel-Akzeptoren haben noch kein
zugewiesenes Item. Liefere eine Form zu diesen, um ein Ziel zu
setzen.
goalAcceptorRateNotMet: Einer oder mehrere Ziel-Aktzeptoren bekommen nicht
genügend Items. Stelle sicher, dass alle Akzeptatorindikatoren grün
sind.
buildingOutOfBounds: Ein oder mehrere Gebäude befinden sich außerhalb des
beabauren Bereichs. Vergrößere den Bereich oder entferene die
Gebäude.
autoComplete: Dein Puzzle schließt sich selbst ab! Bitte stelle sicher, dass
deine Item-Produzent nicht direkt an deine Ziel-Akzeptoren lieferen.
difficulties:
easy: Einfach
medium: Mittel
hard: Schwer
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking
shapez.io in your library, selecting Properties > DLCs.
backendErrors:
ratelimit: Du führst Aktionen zu schnell aus. Bitte warte kurz.
invalid-api-key: Kommunikation mit dem Back-End fehlgeschlagen, veruche das
Spiel neuzustarten oder zu updaten (Ungültiger Api-Schlüssel).
unauthorized: Kommunikation mit dem Back-End fehlgeschlagen, veruche das Spiel
neustarten oder zu updaten (Nicht autorisiert).
bad-token: Kommunikation mit dem Back-End fehlgeschlagen, veruche das Spiel
neustarten oder zu updaten (Ungültiger Token).
bad-id: Ungültige Puzzle Identifikation.
not-found: Das gegebene Puzzle konnte nicht gefunden werden.
bad-category: Die gegebene Kategorie konnte nicht gefunden werden.
bad-short-key: Der gegebene Kurzschlüssel ist ungültig.
profane-title: Dein Puzzletitel enthält ungültige Wörter.
bad-title-too-many-spaces: Dein Puzzletitel ist zu kurz.
bad-shape-key-in-emitter: Einem konstanten Produzenten wurde ein ungültiges Item zugewiesen.
bad-shape-key-in-goal: Einem Ziel-Akzeptor wurde ein ungültiges Item zugewiesen.
no-emitters: Dein Puzzle enthält keine konstanten Produzenten.
no-goals: Dein Puzzle enthält keine Ziel-Akzeptoren.
short-key-already-taken: Dieser Kurzschlüssel ist bereits vergeben, bitte wähle einen anderen.
can-not-report-your-own-puzzle: Du kannst nicht dein eigenes Puzzle melden.
bad-payload: Die Anfrage beinhaltet ungültige Daten.
bad-building-placement: Dein Puzzle beinhaltet Gebäude, die sich an ungültigen Stellen befinden.
timeout: Es kam zu einer Zeitüberschreitung bei der Anfrage.
too-many-likes-already: Dieses Puzzle hat schon zu viele Likes erhalten. Wenn du
es trotzdem löschen möchtest, wende dich an support@shapez.io!
no-permission: Du hast nicht die Berechtigung diese Aktion auszuführen.

View File

@ -475,6 +475,7 @@ ingame:
titleRatingDesc: Your rating will help me to make you better suggestions in the future titleRatingDesc: Your rating will help me to make you better suggestions in the future
continueBtn: Keep Playing continueBtn: Keep Playing
menuBtn: Menu menuBtn: Menu
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Author author: Author
shortKey: Short Key shortKey: Short Key
@ -1038,7 +1039,12 @@ settings:
title: Map Resources Size title: Map Resources Size
description: Controls the size of the shapes on the map overview (when zooming description: Controls the size of the shapes on the map overview (when zooming
out). out).
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Συνδιασμοί πλήκτρων title: Συνδιασμοί πλήκτρων
hint: "Συμβουλή: Φρόντισε να χρησιμοποιήσεις τα πλήκτρα CTRL, SHIFT και ALT! hint: "Συμβουλή: Φρόντισε να χρησιμοποιήσεις τα πλήκτρα CTRL, SHIFT και ALT!
@ -1120,6 +1126,7 @@ keybindings:
goal_acceptor: Goal Acceptor goal_acceptor: Goal Acceptor
block: Block block: Block
massSelectClear: Clear belts massSelectClear: Clear belts
showShapeTooltip: Show shape output tooltip
about: about:
title: Σχετικά με αυτό το παιχνίδι title: Σχετικά με αυτό το παιχνίδι
body: >- body: >-
@ -1254,8 +1261,23 @@ puzzleMenu:
easy: Easy easy: Easy
medium: Medium medium: Medium
hard: Hard hard: Hard
unknown: Unrated
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking dlcHint: Purchased the DLC already? Make sure it is activated by right clicking
shapez.io in your library, selecting Properties > DLCs. shapez.io in your library, selecting Properties > DLCs.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: You are performing your actions too frequent. Please wait a bit. ratelimit: You are performing your actions too frequent. Please wait a bit.
invalid-api-key: Failed to communicate with the backend, please try to invalid-api-key: Failed to communicate with the backend, please try to

View File

@ -155,10 +155,28 @@ puzzleMenu:
account: My Puzzles account: My Puzzles
search: Search search: Search
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
difficulties: difficulties:
easy: Easy easy: Easy
medium: Medium medium: Medium
hard: Hard hard: Hard
unknown: Unrated
validation: validation:
title: Invalid Puzzle title: Invalid Puzzle
@ -665,6 +683,7 @@ ingame:
continueBtn: Keep Playing continueBtn: Keep Playing
menuBtn: Menu menuBtn: Menu
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Author author: Author
@ -1137,7 +1156,7 @@ storyRewards:
title: Storage title: Storage
desc: >- desc: >-
You have unlocked the <strong>storage</strong> building - It allows you to store items up to a given capacity!<br><br> You have unlocked the <strong>storage</strong> building - It allows you to store items up to a given capacity!<br><br>
It prioritises the left output, so you can also use it as an <strong>overflow gate</strong>! It prioritizes the left output, so you can also use it as an <strong>overflow gate</strong>!
reward_blueprints: reward_blueprints:
title: Blueprints title: Blueprints
@ -1232,7 +1251,7 @@ settings:
staging: Staging staging: Staging
prod: Production prod: Production
buildDate: Built <at-date> buildDate: Built <at-date>
tickrateHz: <amount> Hz
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
labels: labels:
@ -1419,6 +1438,11 @@ settings:
description: >- description: >-
Controls the size of the shapes on the map overview (when zooming out). Controls the size of the shapes on the map overview (when zooming out).
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: >-
Whether to always show the shape tooltip when hovering buildings, instead of having to hold 'ALT'.
keybindings: keybindings:
title: Keybindings title: Keybindings
hint: >- hint: >-
@ -1516,6 +1540,8 @@ keybindings:
placeMultiple: Stay in placement mode placeMultiple: Stay in placement mode
placeInverse: Invert automatic belt orientation placeInverse: Invert automatic belt orientation
showShapeTooltip: Show shape output tooltip
about: about:
title: About this Game title: About this Game
body: >- body: >-
@ -1562,7 +1588,7 @@ backendErrors:
bad-payload: The request contains invalid data. bad-payload: The request contains invalid data.
bad-building-placement: Your puzzle contains invalid placed buildings. bad-building-placement: Your puzzle contains invalid placed buildings.
timeout: The request timed out. timeout: The request timed out.
too-many-likes-already: The puzzle alreay got too many likes. If you still want to remove it, please contact support@shapez.io! too-many-likes-already: The puzzle already got too many likes. If you still want to remove it, please contact support@shapez.io!
no-permission: You do not have the permission to perform this action. no-permission: You do not have the permission to perform this action.
tips: tips:

View File

@ -25,8 +25,8 @@ steamPage:
global: global:
loading: Cargando loading: Cargando
error: Error error: Error
thousandsDivider: "," thousandsDivider: .
decimalSeparator: . decimalSeparator: ","
suffix: suffix:
thousands: k thousands: k
millions: M millions: M
@ -78,7 +78,7 @@ mainMenu:
puzzleDlcText: ¿Disfrutas compactando y optimizando fábricas? ¡Consigue ahora el puzzleDlcText: ¿Disfrutas compactando y optimizando fábricas? ¡Consigue ahora el
DLC de Puzles en Steam para aún más diversión! DLC de Puzles en Steam para aún más diversión!
puzzleDlcWishlist: ¡Añádelo ahora a tu lista de deseos! puzzleDlcWishlist: ¡Añádelo ahora a tu lista de deseos!
puzzleDlcViewNow: View Dlc puzzleDlcViewNow: Ver Dlc
dialogs: dialogs:
buttons: buttons:
ok: OK ok: OK
@ -198,7 +198,7 @@ dialogs:
desc: Hay un video tutorial disponible para este nivel, pero solo está desc: Hay un video tutorial disponible para este nivel, pero solo está
disponible en inglés ¿Te gustaría verlo? disponible en inglés ¿Te gustaría verlo?
editConstantProducer: editConstantProducer:
title: Set Item title: Elegir item
puzzleLoadFailed: puzzleLoadFailed:
title: Fallo al cargar los Puzles title: Fallo al cargar los Puzles
desc: Desafortunadamente, no se pudieron cargar los puzles. desc: Desafortunadamente, no se pudieron cargar los puzles.
@ -248,12 +248,12 @@ dialogs:
puzzleReport: puzzleReport:
title: Reportar Puzle title: Reportar Puzle
options: options:
profane: Lenguaje soez profane: Profanidades
unsolvable: Imposible de resolver unsolvable: Imposible de resolver
trolling: Troll trolling: Troll
puzzleReportComplete: puzzleReportComplete:
title: ¡Gracias por tu aporte! title: ¡Gracias por tu aporte!
desc: El puzle ha sido marcado como abuso. desc: El puzle ha sido reportado exitosamente.
puzzleReportError: puzzleReportError:
title: No se pudo reportar title: No se pudo reportar
desc: "No pudimos procesar tu informe:" desc: "No pudimos procesar tu informe:"
@ -261,8 +261,8 @@ dialogs:
title: Introducir clave title: Introducir clave
desc: Introduce la clave del puzle para cargarlo. desc: Introduce la clave del puzle para cargarlo.
puzzleDelete: puzzleDelete:
title: Delete Puzzle? title: ¿Eliminar Puzle?
desc: Are you sure you want to delete '<title>'? This can not be undone! desc: ¿Estas seguro de querer eliminar '<title>'? ¡Esto no se puede deshacer!
ingame: ingame:
keybindingsOverlay: keybindingsOverlay:
moveMap: Mover moveMap: Mover
@ -284,7 +284,7 @@ ingame:
clearSelection: Limpiar selección clearSelection: Limpiar selección
pipette: Cuentagotas pipette: Cuentagotas
switchLayers: Cambiar capas switchLayers: Cambiar capas
clearBelts: Clear belts clearBelts: Borrar cintas
colors: colors:
red: Rojo red: Rojo
green: Verde green: Verde
@ -473,6 +473,7 @@ ingame:
titleRatingDesc: Tu puntuación me ayudará a hacerte mejores sugerencias en el futuro titleRatingDesc: Tu puntuación me ayudará a hacerte mejores sugerencias en el futuro
continueBtn: Continuar Jugando continueBtn: Continuar Jugando
menuBtn: Menú menuBtn: Menú
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Autor author: Autor
shortKey: Clave shortKey: Clave
@ -621,29 +622,29 @@ buildings:
default: default:
name: Puerta AND name: Puerta AND
description: Emite el valor booleano "1" si ambas entradas son verdaderas. description: Emite el valor booleano "1" si ambas entradas son verdaderas.
(Verdadeas significa una forma, color o valor booleano "1") (Verdaderas significa una forma, color o valor booleano "1")
not: not:
name: Puerta NOT name: Puerta NOT
description: Emite el valor booleano "1" si ambas entradas no son verdaderas. description: Emite el valor booleano "1" si ambas entradas no son verdaderas.
(Verdadeas significa una forma, color o valor booleano "1") (Verdaderas significa una forma, color o valor booleano "1")
xor: xor:
name: Puerta XOR name: Puerta XOR
description: Emite el valor booleano "1" si una de las entradas es verdadera, description: Emite el valor booleano "1" si una de las entradas es verdadera,
pero no si ambas lo son. (Verdadeas significa una forma, color o pero no si ambas lo son. (Verdaderas significa una forma, color
valor booleano "1") o valor booleano "1")
or: or:
name: Puerta OR name: Puerta OR
description: Emite el valor booleano "1" Si una de las entradas es verdadera. description: Emite el valor booleano "1" Si una de las entradas es verdadera.
(Verdadeas significa una forma, color o valor booleano "1") (Verdaderas significa una forma, color o valor booleano "1")
transistor: transistor:
default: default:
name: Transistor name: Transistor
description: Envia la señal de abajo si la señal del costado es verdadera description: Envia la señal de abajo si la señal del costado es verdadera
(Verdadeas significa una forma, color o valor booleano "1"). (Verdaderas significa una forma, color o valor booleano "1").
mirrored: mirrored:
name: Transistor name: Transistor
description: Envia la señal de abajo si la señal del costado es verdadera description: Envia la señal de abajo si la señal del costado es verdadera
(Verdadeas significa una forma, color o valor booleano "1"). (Verdaderas significa una forma, color o valor booleano "1").
filter: filter:
default: default:
name: Filtro name: Filtro
@ -664,7 +665,7 @@ buildings:
analyzer: analyzer:
default: default:
name: Analizador de formas name: Analizador de formas
description: analiza el cuadrante de arriba a la derecha de la capa más baja de description: Analiza el cuadrante de arriba a la derecha de la capa más baja de
la forma y devuelve su figura y color. la forma y devuelve su figura y color.
comparator: comparator:
default: default:
@ -1046,7 +1047,12 @@ settings:
title: Tamaño de recursos en el mapa title: Tamaño de recursos en el mapa
description: Controla el tamaño de los recursos en la vista de aérea del mapa description: Controla el tamaño de los recursos en la vista de aérea del mapa
(Al hacer zoom mínimo). (Al hacer zoom mínimo).
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Atajos de teclado title: Atajos de teclado
hint: "Pista: ¡Asegúrate de usar CTRL, SHIFT y ALT! Habilitan distintas opciones hint: "Pista: ¡Asegúrate de usar CTRL, SHIFT y ALT! Habilitan distintas opciones
@ -1128,6 +1134,7 @@ keybindings:
goal_acceptor: Goal Acceptor goal_acceptor: Goal Acceptor
block: Block block: Block
massSelectClear: Clear belts massSelectClear: Clear belts
showShapeTooltip: Show shape output tooltip
about: about:
title: Sobre el juego title: Sobre el juego
body: >- body: >-
@ -1244,22 +1251,22 @@ puzzleMenu:
levels: Niveles levels: Niveles
new: Nuevos new: Nuevos
top-rated: Los mejor valorados top-rated: Los mejor valorados
mine: Mis Puzles mine: Mios
easy: Fáciles easy: Fáciles
hard: Difíciles hard: Difíciles
completed: Completados completed: Completados
medium: Medium medium: Medios
official: Official official: Oficiales
trending: Trending today trending: Tendencias de hoy
trending-weekly: Trending weekly trending-weekly: Tendencias de la semana
categories: Categories categories: Categorías
difficulties: By Difficulty difficulties: Por dificultad
account: My Puzzles account: Mis puzles
search: Search search: Buscar
validation: validation:
title: Puzle no válido title: Puzle no válido
noProducers: Por favor, ¡pon un Productor de una sola pieza! noProducers: Por favor, ¡Pon un Productor de una sola pieza!
noGoalAcceptors: Por favor , ¡pon un Aceptador de objetivos! noGoalAcceptors: Por favor, ¡Pon un Aceptador de objetivos!
goalAcceptorNoItem: Uno o más aceptadores de objetivos no tienen asignado un goalAcceptorNoItem: Uno o más aceptadores de objetivos no tienen asignado un
elemento. Transporta una forma hacia ellos para poner un objetivo. elemento. Transporta una forma hacia ellos para poner un objetivo.
goalAcceptorRateNotMet: Uno o más aceptadores de objetivos no están recibiendo goalAcceptorRateNotMet: Uno o más aceptadores de objetivos no están recibiendo
@ -1271,11 +1278,26 @@ puzzleMenu:
solo elemento no están conectados directamente a tus aceptadores de solo elemento no están conectados directamente a tus aceptadores de
objetivos. objetivos.
difficulties: difficulties:
easy: Easy easy: Fácil
medium: Medium medium: Medio
hard: Hard hard: Dificil
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking unknown: Unrated
shapez.io in your library, selecting Properties > DLCs. dlcHint: ¿Ya compraste el DLC? Asegurate de tenerlo activado haciendo click
derecho a shapez.io en tu biblioteca, selecionando propiedades > DLCs.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: Estás haciendo tus acciones con demasiada frecuencia. Por favor, ratelimit: Estás haciendo tus acciones con demasiada frecuencia. Por favor,
espera un poco. espera un poco.
@ -1289,7 +1311,7 @@ backendErrors:
not-found: No pudimos encontrar ese puzle. not-found: No pudimos encontrar ese puzle.
bad-category: No pudimos encontar esa categoría. bad-category: No pudimos encontar esa categoría.
bad-short-key: La clave que nos diste no es válida. bad-short-key: La clave que nos diste no es válida.
profane-title: El título de tu puzle contiene lenguaje soez. profane-title: El título de tu puzle contiene lenguaje profano.
bad-title-too-many-spaces: El título de tu puzle es demasiado breve. bad-title-too-many-spaces: El título de tu puzle es demasiado breve.
bad-shape-key-in-emitter: Un productor de un solo elemento tiene un elemento no válido. bad-shape-key-in-emitter: Un productor de un solo elemento tiene un elemento no válido.
bad-shape-key-in-goal: Un aceptador de objetivos tiene un elemento no válido. bad-shape-key-in-goal: Un aceptador de objetivos tiene un elemento no válido.
@ -1300,6 +1322,7 @@ backendErrors:
bad-payload: La petición contiene datos no válidos. bad-payload: La petición contiene datos no válidos.
bad-building-placement: Tu puzle contiene edificios en posiciones no válidas. bad-building-placement: Tu puzle contiene edificios en posiciones no válidas.
timeout: El tiempo para la solicitud ha expirado. timeout: El tiempo para la solicitud ha expirado.
too-many-likes-already: The puzzle alreay got too many likes. If you still want too-many-likes-already: El puzle ha recibido demasiados me gusta ¡Si todavía
to remove it, please contact support@shapez.io! quieres eliminarlo, por favor contacta a support@shapez.io! (Solo
no-permission: You do not have the permission to perform this action. inglés)
no-permission: No tienes los permisos necesarios para llevar a cabo esta acción.

View File

@ -458,6 +458,7 @@ ingame:
titleRatingDesc: Your rating will help me to make you better suggestions in the future titleRatingDesc: Your rating will help me to make you better suggestions in the future
continueBtn: Keep Playing continueBtn: Keep Playing
menuBtn: Menu menuBtn: Menu
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Author author: Author
shortKey: Short Key shortKey: Short Key
@ -1004,7 +1005,12 @@ settings:
mapResourcesScale: mapResourcesScale:
title: Kartan resurssien koko title: Kartan resurssien koko
description: Määrittää muotojen koon kartalla (loitonnettaessa). description: Määrittää muotojen koon kartalla (loitonnettaessa).
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Pikanäppäimet title: Pikanäppäimet
hint: "Vinkki: Muista käyttää CTRL, SHIFT ja ALT! Ne ottavat käyttöön erilaisia hint: "Vinkki: Muista käyttää CTRL, SHIFT ja ALT! Ne ottavat käyttöön erilaisia
@ -1086,6 +1092,7 @@ keybindings:
goal_acceptor: Goal Acceptor goal_acceptor: Goal Acceptor
block: Block block: Block
massSelectClear: Clear belts massSelectClear: Clear belts
showShapeTooltip: Show shape output tooltip
about: about:
title: Tietoja tästä pelistä title: Tietoja tästä pelistä
body: >- body: >-
@ -1214,8 +1221,23 @@ puzzleMenu:
easy: Easy easy: Easy
medium: Medium medium: Medium
hard: Hard hard: Hard
unknown: Unrated
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking dlcHint: Purchased the DLC already? Make sure it is activated by right clicking
shapez.io in your library, selecting Properties > DLCs. shapez.io in your library, selecting Properties > DLCs.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: You are performing your actions too frequent. Please wait a bit. ratelimit: You are performing your actions too frequent. Please wait a bit.
invalid-api-key: Failed to communicate with the backend, please try to invalid-api-key: Failed to communicate with the backend, please try to

View File

@ -11,14 +11,14 @@ steamPage:
Et en plus, vous devrez aussi produire de plus en plus pour satisfaire la demande. La seule solution est de construire en plus grand! Au début vous ne ferez que découper les formes, mais plus tard vous devrez les peindre  et pour ça vous devrez extraire et mélanger des couleurs! Et en plus, vous devrez aussi produire de plus en plus pour satisfaire la demande. La seule solution est de construire en plus grand! Au début vous ne ferez que découper les formes, mais plus tard vous devrez les peindre  et pour ça vous devrez extraire et mélanger des couleurs!
En achetant le jeu sur Steam, vous aurez accès à la version complète, mais vous pouvez aussi jouer à une démo sur shapez.io et vous décider ensuite! En achetant le jeu sur Steam, vous aurez accès à la version complète, mais vous pouvez aussi jouer à une démo sur shapez.io et vous décider ensuite!
what_others_say: Ce que les gens pensent de shapez.io what_others_say: Ce que les gens pensent de Shapez.io
nothernlion_comment: This game is great - I'm having a wonderful time playing, nothernlion_comment: Ce jeu est génial - Je passe un merveilleux moment à jouer,
and time has flown by. et le temps s'est envolé.
notch_comment: Mince ! Je devrais vraiment me coucher, mais je crois que j'ai notch_comment: Mince ! Je devrais vraiment me coucher, mais je crois que j'ai
trouvé comment faire un ordinateur dans shapez.io trouvé comment faire un ordinateur dans Shapez.io
steam_review_comment: This game has stolen my life and I don't want it back. steam_review_comment: Ce jeu a volé ma vie et je ne veux pas la récupérer. Jeu
Very chill factory game that won't let me stop making my lines more d'usine très cool qui ne me laissera pas arrêter de rendre mes lignes
efficient. plus efficaces.
global: global:
loading: Chargement loading: Chargement
error: Erreur error: Erreur
@ -71,11 +71,11 @@ mainMenu:
savegameLevel: Niveau <x> savegameLevel: Niveau <x>
savegameLevelUnknown: Niveau inconnu savegameLevelUnknown: Niveau inconnu
savegameUnnamed: Sans titre savegameUnnamed: Sans titre
puzzleMode: Puzzle Mode puzzleMode: Mode Puzzle
back: Back back: Retour
puzzleDlcText: Vous aimez compacter et optimiser vos usines ? Achetez le DLC sur puzzleDlcText: Vous aimez compacter et optimiser vos usines ? Achetez le DLC sur
Steam dés maintenant pour encore plus d'amusement ! Steam dès maintenant pour encore plus d'amusement!
puzzleDlcWishlist: Wishlist now! puzzleDlcWishlist: Ajoute à ta liste de souhaits maintenant !
puzzleDlcViewNow: View Dlc puzzleDlcViewNow: View Dlc
dialogs: dialogs:
buttons: buttons:
@ -90,7 +90,7 @@ dialogs:
viewUpdate: Voir les mises à jour viewUpdate: Voir les mises à jour
showUpgrades: Montrer les améliorations showUpgrades: Montrer les améliorations
showKeybindings: Montrer les raccourcis showKeybindings: Montrer les raccourcis
retry: Réesayer retry: Réessayer
continue: Continuer continue: Continuer
playOffline: Jouer Hors-ligne playOffline: Jouer Hors-ligne
importSavegameError: importSavegameError:
@ -200,63 +200,66 @@ dialogs:
desc: "Malheuresement, le puzzle n'a pas pu être chargé :" desc: "Malheuresement, le puzzle n'a pas pu être chargé :"
submitPuzzle: submitPuzzle:
title: Envoyer le Puzzle title: Envoyer le Puzzle
descName: "Donnez un nom à votre puzzle :" descName: "Donnez un nom à votre puzzle:"
descIcon: "Please enter a unique short key, which will be shown as the icon of descIcon: "Veuillez entrer un raccourci de forme unique, qui sera affichée comme
your puzzle (You can generate them <link>here</link>, or choose one icône de votre puzzle (Vous pouvez générer le raccourci d'une forme
of the randomly suggested shapes below):" <link>ici</link>, ou en choisir une parmi les formes suggérées
placeholderName: Puzzle Title alétoirement ci-dessous):"
placeholderName: Titre du Puzzle
puzzleResizeBadBuildings: puzzleResizeBadBuildings:
title: Resize not possible title: Impossible de redimensionner
desc: You can't make the zone any smaller, because then some buildings would be desc: Vous ne pouvez pas rétrécir la zone, car certains bâtiments seraient en
outside the zone. dehors de la zone
puzzleLoadError: puzzleLoadError:
title: Mauvais Puzzle title: Mauvais Puzzle
desc: "Le chargement du puzzle a échoué :" desc: "Le chargement du puzzle a échoué:"
offlineMode: offlineMode:
title: Mode hors-ligne title: Mode hors-ligne
desc: We couldn't reach the servers, so the game has to run in offline mode. desc: Nous n'avons pas pu atteindre les serveurs, donc le jeu doit être mis en
Please make sure you have an active internet connection. mode hors ligne. Veuillez vous assurez que vous disposez d'une
connexion Internet active.
puzzleDownloadError: puzzleDownloadError:
title: Erreur de téléchargment title: Erreur de téléchargment
desc: "Le téléchargement à échoué :" desc: "Le téléchargement a échoué:"
puzzleSubmitError: puzzleSubmitError:
title: Erreur d'envoi title: Erreur d'envoi
desc: "L'envoi à échoué :" desc: "L'envoi a échoué:"
puzzleSubmitOk: puzzleSubmitOk:
title: Puzzle envoyé title: Puzzle envoyé
desc: Félicitation ! Votre puzzle à été envoyé et peut maintenant être joué. desc: Félicitations ! Votre puzzle a été envoyé et peut maintenant être joué.
Vous pouvez maintenant le retrouver dans la section "Mes Puzzles". Vous pouvez maintenant le retrouver dans la section "Mes Puzzles".
puzzleCreateOffline: puzzleCreateOffline:
title: Mode Hors-ligne title: Mode Hors-ligne
desc: Since you are offline, you will not be able to save and/or publish your desc: Puisque vous êtes hors ligne, vous ne pourrez pas enregistrer et/ou
puzzle. Would you still like to continue? publier votre puzzle. Souhaitez-vous toujours continuer ?
puzzlePlayRegularRecommendation: puzzlePlayRegularRecommendation:
title: Recommendation title: Recommandation
desc: I <strong>strongly</strong> recommend playing the normal game to level 12 desc: Je recommande <strong>fortement</strong> de jouer au jeu normal jusqu'au
before attempting the puzzle DLC, otherwise you may encounter niveau 12 avant d'essayer le Puzzle DLC, sinon vous risqez de
mechanics not yet introduced. Do you still want to continue? rencontrer des méchanismes pas encore introduits. Voulez-vous
toujours continuer ?
puzzleShare: puzzleShare:
title: Short Key Copied title: Code copié
desc: The short key of the puzzle (<key>) has been copied to your clipboard! It desc: Le code du puzzle (<key>) a été copié dans ton presse-papiers ! Il peut
can be entered in the puzzle menu to access the puzzle. être entré dans le menu des puzzles pour accéder au puzzle.
puzzleReport: puzzleReport:
title: Report Puzzle title: Signaler le Puzzle
options: options:
profane: Profane profane: Profane
unsolvable: Not solvable unsolvable: Irrésolvable
trolling: Trolling trolling: Troll
puzzleReportComplete: puzzleReportComplete:
title: Merci pour votre retour ! title: Merci pour votre retour!
desc: Le puzzle a été marqué. desc: Le puzzle a été marqué.
puzzleReportError: puzzleReportError:
title: Failed to report title: Échec du signalement
desc: "Your report could not get processed:" desc: "Votre signalement n'a pas pu être effectué:"
puzzleLoadShortKey: puzzleLoadShortKey:
title: Enter short key title: Entrer un code
desc: Enter the short key of the puzzle to load it. desc: Entrer le code du puzzle pour le charger.
puzzleDelete: puzzleDelete:
title: Delete Puzzle? title: Supprimer le puzzle ?
desc: Are you sure you want to delete '<title>'? This can not be undone! desc: Êtes-vous sûr de vouloir supprimer '<title>' ? Cela sera irréversible !
ingame: ingame:
keybindingsOverlay: keybindingsOverlay:
moveMap: Déplacer moveMap: Déplacer
@ -430,28 +433,30 @@ ingame:
title: Me soutenir title: Me soutenir
desc: Je le développe pendant mon temps libre! desc: Je le développe pendant mon temps libre!
achievements: achievements:
title: Achievements title: Succès
desc: Hunt them all! desc: Débloquez-les tous !
puzzleEditorSettings: puzzleEditorSettings:
zoneTitle: Zone zoneTitle: Zone
zoneWidth: Width zoneWidth: Largeur
zoneHeight: Height zoneHeight: Hauteur
trimZone: Trim trimZone: Optimiser la taille
clearItems: Clear Items clearItems: Supprimer les objets
share: Share share: Partager
report: Report report: Signaler
clearBuildings: Clear Buildings clearBuildings: Clear Buildings
resetPuzzle: Reset Puzzle resetPuzzle: Reset Puzzle
puzzleEditorControls: puzzleEditorControls:
title: Puzzle Creator title: Créateur de Puzzles
instructions: instructions:
- 1. Place <strong>Constant Producers</strong> to provide shapes and - 1. Placez des <strong>Producteurs Constants</strong> pour fournir
colors to the player des formes et des couleurs au joueur
- 2. Build one or more shapes you want the player to build later and - 2. Fabriquez une ou plusieurs formes que vous voulez que le joueur
deliver it to one or more <strong>Goal Acceptors</strong> fabrique plus tard et délivrez-la/les à un ou plusieurs
- 3. Once a Goal Acceptor receives a shape for a certain amount of <strong>Récepteurs d'Objectif</strong>
time, it <strong>saves it as a goal</strong> that the player must - 3. Une fois qu'un Récépteur d'Objectif a reçu une forme pendant un
produce later (Indicated by the <strong>green badge</strong>). certain temps, il <strong>l'enregistre zn tant
qu'objectif</strong> que le joueur devra produire plus tard
(Indiqué par le <strong>badge vert</strong>).
- 4. Click the <strong>lock button</strong> on a building to disable - 4. Click the <strong>lock button</strong> on a building to disable
it. it.
- 5. Once you click review, your puzzle will be validated and you - 5. Once you click review, your puzzle will be validated and you
@ -460,18 +465,19 @@ ingame:
except for the Producers and Goal Acceptors - That's the part that except for the Producers and Goal Acceptors - That's the part that
the player is supposed to figure out for themselves, after all :) the player is supposed to figure out for themselves, after all :)
puzzleCompletion: puzzleCompletion:
title: Puzzle Completed! title: Puzzle Résolu !
titleLike: "Click the heart if you liked the puzzle:" titleLike: "Cliquez sur le cœur si vous avez aimé le Puzzle:"
titleRating: How difficult did you find the puzzle? titleRating: À quel point avez-vous trouvé le puzzle diffcile ?
titleRatingDesc: Your rating will help me to make you better suggestions in the future titleRatingDesc: Votre note m'aidera à vous faire de meilleures suggestions à l'avenir
continueBtn: Keep Playing continueBtn: Continuer à jouer
menuBtn: Menu menuBtn: Menu
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Author author: Auteur
shortKey: Short Key shortKey: Short Key
rating: Difficulty score rating: Niveau de difficulté
averageDuration: Avg. Duration averageDuration: Durée moyenne
completionRate: Completion rate completionRate: Taux de réussite
shopUpgrades: shopUpgrades:
belt: belt:
name: Convoyeurs, distributeurs et tunnels name: Convoyeurs, distributeurs et tunnels
@ -691,25 +697,26 @@ buildings:
calque de câblage sur le calque normal. calque de câblage sur le calque normal.
constant_producer: constant_producer:
default: default:
name: Constant Producer name: Producteur Constabnt
description: Constantly outputs a specified shape or color. description: Sort constamment une forme ou une couleur spécifiée.
goal_acceptor: goal_acceptor:
default: default:
name: Goal Acceptor name: Récepteur d'Objetcif
description: Deliver shapes to the goal acceptor to set them as a goal. description: Délivrez des formes au récepteur d'objectif pour les définir comme
objectif.
block: block:
default: default:
name: Block name: Bloc
description: Allows you to block a tile. description: Permet de bloquer une case.
storyRewards: storyRewards:
reward_cutter_and_trash: reward_cutter_and_trash:
title: Découpage de formes title: Découpage de formes
desc: You just unlocked the <strong>cutter</strong>, which cuts shapes in half desc: Vous venez de déverrouiller le <strong>découpeur</strong>, qui coupe les
from top to bottom <strong>regardless of its formes en deux de haut en bas <strong>indépendamment de son
orientation</strong>!<br><br>Be sure to get rid of the waste, or orientation</strong>!<br><br>Assurez-vous de vous débarrasser des
otherwise <strong>it will clog and stall</strong> - For this purpose déchets, ou sinon <strong>il se bouchera et se bloquera</strong> - À
I have given you the <strong>trash</strong>, which destroys cet effet, Je vous ai donné la <strong>poubelle</strong>, qui
everything you put into it! détruit tout ce que vous mettez dedans !
reward_rotater: reward_rotater:
title: Rotation title: Rotation
desc: Le <strong>pivoteur</strong> a été débloqué! Il pivote les formes de 90 desc: Le <strong>pivoteur</strong> a été débloqué! Il pivote les formes de 90
@ -735,9 +742,10 @@ storyRewards:
<strong>placée au-dessus</strong> de la forme de gauche. <strong>placée au-dessus</strong> de la forme de gauche.
reward_balancer: reward_balancer:
title: Répartiteur title: Répartiteur
desc: The multifunctional <strong>balancer</strong> has been unlocked - It can desc: Le <strong>répartiteur</strong> multifonctionnel a été débloqué - Il peut
be used to build bigger factories by <strong>splitting and merging être utilisé pour construire de plus grandes usines en
items</strong> onto multiple belts! <strong>divisant et en rassemblant des objets</strong> sur plusieurs
convoyeurs !
reward_tunnel: reward_tunnel:
title: Tunnel title: Tunnel
desc: Le <strong>tunnel</strong> a été débloqué. Vous pouvez maintenant faire desc: Le <strong>tunnel</strong> a été débloqué. Vous pouvez maintenant faire
@ -843,14 +851,17 @@ storyRewards:
gâteau : je vous donne aussi le <strong>transistor</strong>!" gâteau : je vous donne aussi le <strong>transistor</strong>!"
reward_virtual_processing: reward_virtual_processing:
title: Traitement virtuel title: Traitement virtuel
desc: I just gave a whole bunch of new buildings which allow you to desc: Je viens de vous donner tout un tas de nouveaux bâtiments qui vous
<strong>simulate the processing of shapes</strong>!<br><br> You can permettent de <strong>simuler le traitement des
now simulate a cutter, rotator, stacker and more on the wires layer! formes</strong>!<br><br> Vous pouvez maintenant simuler un
With this you now have three options to continue the game:<br><br> - découpeur, un pivoteur, un assembleur et plus encore sur la couche
Build an <strong>automated machine</strong> to create any possible des fils ! Avec cela vous avez maintenant trois options pour
shape requested by the HUB (I recommend to try it!).<br><br> - Build continuer le jeu:<br><br> - Construire une <strong>machine
something cool with wires.<br><br> - Continue to play automatique</strong> pour créer toute forme possible demandée par le
normally.<br><br> Whatever you choose, remember to have fun! HUB (Je recommande d'essayer de le faire !).<br><br> - Construire
quelque chose de cool avec les fils.<br><br> - Continuer à jouer
normalement.<br><br> Quoi que vous choisissiez, n'oubliez pas de
vous amuser !
no_reward: no_reward:
title: Niveau suivant title: Niveau suivant
desc: "Ce niveau na pas de récompense mais le prochain, si!<br><br> PS : Ne desc: "Ce niveau na pas de récompense mais le prochain, si!<br><br> PS : Ne
@ -1049,6 +1060,11 @@ settings:
title: Taille des ressources sur la carte title: Taille des ressources sur la carte
description: Contrôle la taille des formes sur la vue densemble de la carte description: Contrôle la taille des formes sur la vue densemble de la carte
visible en dézoomant. visible en dézoomant.
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Contrôles title: Contrôles
hint: "Astuce : Noubliez pas dutiliser CTRL, MAJ et ALT! Ces touches activent hint: "Astuce : Noubliez pas dutiliser CTRL, MAJ et ALT! Ces touches activent
@ -1127,10 +1143,11 @@ keybindings:
rotateToDown: "Rotate: Point Down" rotateToDown: "Rotate: Point Down"
rotateToRight: "Rotate: Point Right" rotateToRight: "Rotate: Point Right"
rotateToLeft: "Rotate: Point Left" rotateToLeft: "Rotate: Point Left"
constant_producer: Constant Producer constant_producer: Producteur Constant
goal_acceptor: Goal Acceptor goal_acceptor: Récepteur d'Objectif
block: Block block: Bloc
massSelectClear: Clear belts massSelectClear: Vider les convoyeurs
showShapeTooltip: Show shape output tooltip
about: about:
title: À propos de ce jeu title: À propos de ce jeu
body: >- body: >-
@ -1156,7 +1173,7 @@ demo:
exportingBase: Exporter une image de toute la base exportingBase: Exporter une image de toute la base
settingNotAvailable: Indisponible dans la démo. settingNotAvailable: Indisponible dans la démo.
tips: tips:
- Le centre nimporte quelle forme, pas seulement la forme actuelle! - Le centre accepte nimporte quelle forme, pas seulement la forme actuelle!
- Assurez-vous que vos usines soient modulaires, cela paiera! - Assurez-vous que vos usines soient modulaires, cela paiera!
- Ne construisez pas trop près du centre, ou ce sera un énorme chaos! - Ne construisez pas trop près du centre, ou ce sera un énorme chaos!
- Si lempilement ne fonctionne pas, essayez déchanger les entrées. - Si lempilement ne fonctionne pas, essayez déchanger les entrées.
@ -1237,7 +1254,7 @@ puzzleMenu:
edit: Éditer edit: Éditer
title: Mode Puzzle title: Mode Puzzle
createPuzzle: Créer un Puzzle createPuzzle: Créer un Puzzle
loadPuzzle: charger loadPuzzle: Charger
reviewPuzzle: Revoir & Publier reviewPuzzle: Revoir & Publier
validatingPuzzle: Validation du Puzzle validatingPuzzle: Validation du Puzzle
submittingPuzzle: Publication du Puzzle submittingPuzzle: Publication du Puzzle
@ -1245,19 +1262,19 @@ puzzleMenu:
categories: categories:
levels: Niveaux levels: Niveaux
new: Nouveau new: Nouveau
top-rated: Les-mieux notés top-rated: Les mieux notés
mine: Mes puzzles mine: Mes puzzles
easy: Facile easy: Facile
hard: Difficile hard: Difficile
completed: Complété completed: Complété
medium: Medium medium: Medium
official: Official official: Officiel
trending: Trending today trending: Trending today
trending-weekly: Trending weekly trending-weekly: Trending weekly
categories: Categories categories: Catégories
difficulties: By Difficulty difficulties: Par Difficulté
account: My Puzzles account: Mes Puzzles
search: Search search: Rechercher
validation: validation:
title: Puzzle invalide title: Puzzle invalide
noProducers: Veuillez placer un producteur constant ! noProducers: Veuillez placer un producteur constant !
@ -1276,8 +1293,23 @@ puzzleMenu:
easy: Easy easy: Easy
medium: Medium medium: Medium
hard: Hard hard: Hard
unknown: Unrated
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking dlcHint: Purchased the DLC already? Make sure it is activated by right clicking
shapez.io in your library, selecting Properties > DLCs. shapez.io in your library, selecting Properties > DLCs.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: Vous effectuez vos actions trop fréquemment. Veuillez attendre un peu ratelimit: Vous effectuez vos actions trop fréquemment. Veuillez attendre un peu
s'il vous plait. s'il vous plait.
@ -1302,6 +1334,6 @@ backendErrors:
bad-payload: La demande contient des données invalides. bad-payload: La demande contient des données invalides.
bad-building-placement: Votre puzzle contient des bâtiments placés non valides. bad-building-placement: Votre puzzle contient des bâtiments placés non valides.
timeout: La demande a expiré. timeout: La demande a expiré.
too-many-likes-already: The puzzle alreay got too many likes. If you still want too-many-likes-already: The puzzle already got too many likes. If you still want
to remove it, please contact support@shapez.io! to remove it, please contact support@shapez.io!
no-permission: You do not have the permission to perform this action. no-permission: You do not have the permission to perform this action.

View File

@ -444,6 +444,7 @@ ingame:
titleRatingDesc: Your rating will help me to make you better suggestions in the future titleRatingDesc: Your rating will help me to make you better suggestions in the future
continueBtn: Keep Playing continueBtn: Keep Playing
menuBtn: Menu menuBtn: Menu
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Author author: Author
shortKey: Short Key shortKey: Short Key
@ -952,6 +953,11 @@ settings:
mapResourcesScale: mapResourcesScale:
title: גודל המשאבים במפה title: גודל המשאבים במפה
description: .שולט על הגודל של הצורות על המפה (בתצוגה המוקטנת) description: .שולט על הגודל של הצורות על המפה (בתצוגה המוקטנת)
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
tickrateHz: <amount> Hz
keybindings: keybindings:
title: מקשים title: מקשים
hint: ".הם מאפשרים הרבה אפשרויות השמה !ALTו SHIFT ,CTRLטיפ: השתמש ב" hint: ".הם מאפשרים הרבה אפשרויות השמה !ALTו SHIFT ,CTRLטיפ: השתמש ב"
@ -1032,6 +1038,7 @@ keybindings:
goal_acceptor: Goal Acceptor goal_acceptor: Goal Acceptor
block: Block block: Block
massSelectClear: Clear belts massSelectClear: Clear belts
showShapeTooltip: Show shape output tooltip
about: about:
title: בנוגע למשחק הזה title: בנוגע למשחק הזה
body: >- body: >-
@ -1157,8 +1164,23 @@ puzzleMenu:
easy: Easy easy: Easy
medium: Medium medium: Medium
hard: Hard hard: Hard
unknown: Unrated
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking dlcHint: Purchased the DLC already? Make sure it is activated by right clicking
shapez.io in your library, selecting Properties > DLCs. shapez.io in your library, selecting Properties > DLCs.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: You are performing your actions too frequent. Please wait a bit. ratelimit: You are performing your actions too frequent. Please wait a bit.
invalid-api-key: Failed to communicate with the backend, please try to invalid-api-key: Failed to communicate with the backend, please try to

View File

@ -458,6 +458,7 @@ ingame:
titleRatingDesc: Your rating will help me to make you better suggestions in the future titleRatingDesc: Your rating will help me to make you better suggestions in the future
continueBtn: Keep Playing continueBtn: Keep Playing
menuBtn: Menu menuBtn: Menu
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Author author: Author
shortKey: Short Key shortKey: Short Key
@ -998,7 +999,12 @@ settings:
title: Map Resources Size title: Map Resources Size
description: Controls the size of the shapes on the map overview (when zooming description: Controls the size of the shapes on the map overview (when zooming
out). out).
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Tipka title: Tipka
hint: "Savjet: Be sure to make use of CTRL, SHIFT and ALT! They enable different hint: "Savjet: Be sure to make use of CTRL, SHIFT and ALT! They enable different
@ -1080,6 +1086,7 @@ keybindings:
goal_acceptor: Goal Acceptor goal_acceptor: Goal Acceptor
block: Block block: Block
massSelectClear: Clear belts massSelectClear: Clear belts
showShapeTooltip: Show shape output tooltip
about: about:
title: O Igri title: O Igri
body: >- body: >-
@ -1207,8 +1214,23 @@ puzzleMenu:
easy: Easy easy: Easy
medium: Medium medium: Medium
hard: Hard hard: Hard
unknown: Unrated
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking dlcHint: Purchased the DLC already? Make sure it is activated by right clicking
shapez.io in your library, selecting Properties > DLCs. shapez.io in your library, selecting Properties > DLCs.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: You are performing your actions too frequent. Please wait a bit. ratelimit: You are performing your actions too frequent. Please wait a bit.
invalid-api-key: Failed to communicate with the backend, please try to invalid-api-key: Failed to communicate with the backend, please try to

View File

@ -464,6 +464,7 @@ ingame:
jövőben jövőben
continueBtn: Játék Folytatása continueBtn: Játék Folytatása
menuBtn: Menü menuBtn: Menü
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Szerző author: Szerző
shortKey: Gyorskód shortKey: Gyorskód
@ -1025,7 +1026,12 @@ settings:
title: Erőforrások Mérete a Térképen title: Erőforrások Mérete a Térképen
description: Kizoomolt állapotban a térképen megjelenő erőforrások (alakzatok és description: Kizoomolt állapotban a térképen megjelenő erőforrások (alakzatok és
színek) méretét állítja. színek) méretét állítja.
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Gyorsbillentyűk title: Gyorsbillentyűk
hint: "Tipp: Használd ki a CTRL, SHIFT és ALT billentyűket, amelyek különféle hint: "Tipp: Használd ki a CTRL, SHIFT és ALT billentyűket, amelyek különféle
@ -1107,6 +1113,7 @@ keybindings:
goal_acceptor: Elfogadó goal_acceptor: Elfogadó
block: Blokkolás block: Blokkolás
massSelectClear: Futószalagok Kiürítése massSelectClear: Futószalagok Kiürítése
showShapeTooltip: Show shape output tooltip
about: about:
title: A Játékról title: A Játékról
body: >- body: >-
@ -1242,8 +1249,23 @@ puzzleMenu:
easy: Easy easy: Easy
medium: Medium medium: Medium
hard: Hard hard: Hard
unknown: Unrated
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking dlcHint: Purchased the DLC already? Make sure it is activated by right clicking
shapez.io in your library, selecting Properties > DLCs. shapez.io in your library, selecting Properties > DLCs.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: Túl gyorsan csinálsz dolgokat. Kérlek, várj egy kicsit. ratelimit: Túl gyorsan csinálsz dolgokat. Kérlek, várj egy kicsit.
invalid-api-key: Valami nem oké a játékkal. Próbáld meg frissíteni vagy invalid-api-key: Valami nem oké a játékkal. Próbáld meg frissíteni vagy

View File

@ -467,6 +467,7 @@ ingame:
titleRatingDesc: Your rating will help me to make you better suggestions in the future titleRatingDesc: Your rating will help me to make you better suggestions in the future
continueBtn: Keep Playing continueBtn: Keep Playing
menuBtn: Menu menuBtn: Menu
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Author author: Author
shortKey: Short Key shortKey: Short Key
@ -1057,7 +1058,12 @@ settings:
title: Ukuran Sumber Daya Peta title: Ukuran Sumber Daya Peta
description: Mengontrol ukuran bentuk-bentuk pada gambaran peta (ketika zoom description: Mengontrol ukuran bentuk-bentuk pada gambaran peta (ketika zoom
out). out).
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Tombol Pintas title: Tombol Pintas
hint: "Petunjuk: Pastikan kamu menggunakan CTRL, SHIFT and ALT! Mereka hint: "Petunjuk: Pastikan kamu menggunakan CTRL, SHIFT and ALT! Mereka
@ -1139,6 +1145,7 @@ keybindings:
goal_acceptor: Goal Acceptor goal_acceptor: Goal Acceptor
block: Block block: Block
massSelectClear: Clear belts massSelectClear: Clear belts
showShapeTooltip: Show shape output tooltip
about: about:
title: Tentang permainan ini title: Tentang permainan ini
body: >- body: >-
@ -1288,8 +1295,23 @@ puzzleMenu:
easy: Easy easy: Easy
medium: Medium medium: Medium
hard: Hard hard: Hard
unknown: Unrated
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking dlcHint: Purchased the DLC already? Make sure it is activated by right clicking
shapez.io in your library, selecting Properties > DLCs. shapez.io in your library, selecting Properties > DLCs.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: You are performing your actions too frequent. Please wait a bit. ratelimit: You are performing your actions too frequent. Please wait a bit.
invalid-api-key: Failed to communicate with the backend, please try to invalid-api-key: Failed to communicate with the backend, please try to

View File

@ -447,7 +447,7 @@ ingame:
clearItems: Elimina oggetti clearItems: Elimina oggetti
share: Condividi share: Condividi
report: Segnala report: Segnala
clearBuildings: Clear Buildings clearBuildings: Cancella edifici
resetPuzzle: Reset Puzzle resetPuzzle: Reset Puzzle
puzzleEditorControls: puzzleEditorControls:
title: Creazione puzzle title: Creazione puzzle
@ -477,6 +477,7 @@ ingame:
in futuro in futuro
continueBtn: Continua a giocare continueBtn: Continua a giocare
menuBtn: Menù menuBtn: Menù
nextPuzzle: Puzzle successivo
puzzleMetadata: puzzleMetadata:
author: Autore author: Autore
shortKey: Codice shortKey: Codice
@ -745,7 +746,7 @@ storyRewards:
reward_rotater_ccw: reward_rotater_ccw:
title: Rotazione antioraria title: Rotazione antioraria
desc: Hai sbloccato una variante del <strong>ruotatore</strong>! Consente di desc: Hai sbloccato una variante del <strong>ruotatore</strong>! Consente di
ruotare in senso antiorario! Per costruirla, seleziona la ruotatrice ruotare in senso antiorario! Per costruirla, seleziona il ruotatore
e <strong>premi 'T' per cambiare variante</strong>! e <strong>premi 'T' per cambiare variante</strong>!
reward_miner_chainable: reward_miner_chainable:
title: Estrattore a catena title: Estrattore a catena
@ -823,7 +824,7 @@ storyRewards:
la portata di un nastro.<br><br>E aspetta di sbloccare i cavi, la portata di un nastro.<br><br>E aspetta di sbloccare i cavi,
allora sì che sarà molto utile! allora sì che sarà molto utile!
reward_rotater_180: reward_rotater_180:
title: Ruotatrice (180 gradi) title: Ruotatore (180 gradi)
desc: Hai appena sbloccato il <strong>ruotatore</strong> a 180 gradi! Consente desc: Hai appena sbloccato il <strong>ruotatore</strong> a 180 gradi! Consente
di ruotare una forma di 180 gradi (Sorpresa! :D) di ruotare una forma di 180 gradi (Sorpresa! :D)
reward_display: reward_display:
@ -833,7 +834,7 @@ storyRewards:
lettore di nastri e il magazzino mostrano l'ultimo oggetto da loro lettore di nastri e il magazzino mostrano l'ultimo oggetto da loro
letto? Prova a mostrarlo su di un display!" letto? Prova a mostrarlo su di un display!"
reward_constant_signal: reward_constant_signal:
title: Sengale costante title: Segnale costante
desc: Hai sbloccato l'edificio <strong>segnale costante</strong> sul livello desc: Hai sbloccato l'edificio <strong>segnale costante</strong> sul livello
elettrico! È utile collegarlo ai <strong>filtri di oggetti</strong> elettrico! È utile collegarlo ai <strong>filtri di oggetti</strong>
per esempio.<br><br> Il segnale costante può emettere una per esempio.<br><br> Il segnale costante può emettere una
@ -859,7 +860,7 @@ storyRewards:
normalmente. <br><br> Qualsiasi cosa tu scelga, ricordati di normalmente. <br><br> Qualsiasi cosa tu scelga, ricordati di
divertirti! divertirti!
reward_wires_painter_and_levers: reward_wires_painter_and_levers:
title: Cavi e Verniciatrice quadrupla title: Cavi e Verniciatore quadruplo
desc: "Hai appena sbloccato il <strong>Livello Elettrico</strong>: è un livello desc: "Hai appena sbloccato il <strong>Livello Elettrico</strong>: è un livello
separato dal livelo normale e introduce molte altre meccaniche di separato dal livelo normale e introduce molte altre meccaniche di
gioco!<br><br> Per il momento ti ho sbloccato il gioco!<br><br> Per il momento ti ho sbloccato il
@ -1048,7 +1049,12 @@ settings:
title: Grandezza delle Risorse sulla Mappa title: Grandezza delle Risorse sulla Mappa
description: Controlla la grandezza delle forme visualizzate sulla mappa (quando description: Controlla la grandezza delle forme visualizzate sulla mappa (quando
si fa lo zoom indietro). si fa lo zoom indietro).
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Determina se mostrare sempre la forma in uscita da un edificio quando si passa sopra di esso col cursore,
invece di dover premere 'ALT'.
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
tickrateHz: <amount> Hz
keybindings: keybindings:
title: Comandi title: Comandi
hint: "Suggerimento: Usa spesso CTRL, MAIUSC e ALT! Abilitano opzioni di hint: "Suggerimento: Usa spesso CTRL, MAIUSC e ALT! Abilitano opzioni di
@ -1065,7 +1071,7 @@ keybindings:
mappings: mappings:
confirm: Conferma confirm: Conferma
back: Indietro back: Indietro
mapMoveUp: Spostati sù mapMoveUp: Spostati su
mapMoveRight: Spostati a destra mapMoveRight: Spostati a destra
mapMoveDown: Spostati giù mapMoveDown: Spostati giù
mapMoveLeft: Spostati a sinistra mapMoveLeft: Spostati a sinistra
@ -1130,6 +1136,7 @@ keybindings:
goal_acceptor: Accettore di obiettivi goal_acceptor: Accettore di obiettivi
block: Blocco block: Blocco
massSelectClear: Sgombra nastri massSelectClear: Sgombra nastri
showShapeTooltip: Mostra forma di uscita di un edificio
about: about:
title: Riguardo questo gioco title: Riguardo questo gioco
body: >- body: >-
@ -1272,8 +1279,23 @@ puzzleMenu:
easy: Facile easy: Facile
medium: Medio medium: Medio
hard: Difficile hard: Difficile
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking unknown: Non classificato
shapez.io in your library, selecting Properties > DLCs. dlcHint: Hai già acquistato il DLC? Assicurati che sia attivo facendo clic destro
su shapez.io nella tua libreria e selezionando Proprietà > DLC.
search:
action: Cerca
placeholder: Inserisci il nome di un puzzle o di un autore
includeCompleted: Include Completed
difficulties:
any: Qualsiasi difficoltà
easy: Facile
medium: Medio
hard: Difficile
durations:
any: Qualsiasi durata
short: Breve (< 2 minuti)
medium: Normale
long: Lunga (> 10 minuti)
backendErrors: backendErrors:
ratelimit: Stai facendo troppe azioni velocemente. Per favore attendi un attimo. ratelimit: Stai facendo troppe azioni velocemente. Per favore attendi un attimo.
invalid-api-key: Comunicazione con il backend fallita, per favore prova ad invalid-api-key: Comunicazione con il backend fallita, per favore prova ad

View File

@ -422,6 +422,7 @@ ingame:
titleRatingDesc: Your rating will help me to make you better suggestions in the future titleRatingDesc: Your rating will help me to make you better suggestions in the future
continueBtn: Keep Playing continueBtn: Keep Playing
menuBtn: Menu menuBtn: Menu
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: Author author: Author
shortKey: Short Key shortKey: Short Key
@ -868,6 +869,11 @@ settings:
mapResourcesScale: mapResourcesScale:
title: 資源アイコンのサイズ title: 資源アイコンのサイズ
description: ズームアウトしたときの図形のサイズを調節します。 description: ズームアウトしたときの図形のサイズを調節します。
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
tickrateHz: <amount> Hz
keybindings: keybindings:
title: キー設定 title: キー設定
hint: "Tip: CTRL, SHIFT, ALTを活用してください。建造物配置の際の追加機能がそれぞれ割り当てられています。" hint: "Tip: CTRL, SHIFT, ALTを活用してください。建造物配置の際の追加機能がそれぞれ割り当てられています。"
@ -948,6 +954,7 @@ keybindings:
goal_acceptor: Goal Acceptor goal_acceptor: Goal Acceptor
block: Block block: Block
massSelectClear: Clear belts massSelectClear: Clear belts
showShapeTooltip: Show shape output tooltip
about: about:
title: このゲームについて title: このゲームについて
body: >- body: >-
@ -1070,8 +1077,23 @@ puzzleMenu:
easy: Easy easy: Easy
medium: Medium medium: Medium
hard: Hard hard: Hard
unknown: Unrated
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking dlcHint: Purchased the DLC already? Make sure it is activated by right clicking
shapez.io in your library, selecting Properties > DLCs. shapez.io in your library, selecting Properties > DLCs.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: You are performing your actions too frequent. Please wait a bit. ratelimit: You are performing your actions too frequent. Please wait a bit.
invalid-api-key: Failed to communicate with the backend, please try to invalid-api-key: Failed to communicate with the backend, please try to

View File

@ -69,9 +69,9 @@ mainMenu:
savegameUnnamed: 이름 없음 savegameUnnamed: 이름 없음
puzzleMode: 퍼즐 모드 puzzleMode: 퍼즐 모드
back: Back back: Back
puzzleDlcText: 공장의 크기를 줄이고 최적화하는데 관심이 많으신가요? 지금 퍼즐 DLC를 구입하세요! puzzleDlcText: 공장의 크기를 줄이고 최적화하는 데 관심이 많으신가요? 지금 퍼즐 DLC를 구입하세요!
puzzleDlcWishlist: 지금 찜 목록에 추가하세요! puzzleDlcWishlist: 지금 찜 목록에 추가하세요!
puzzleDlcViewNow: View Dlc puzzleDlcViewNow: DLC 보러 가기
dialogs: dialogs:
buttons: buttons:
ok: 확인 ok: 확인
@ -250,7 +250,7 @@ ingame:
clearSelection: 지우기 clearSelection: 지우기
pipette: 피펫 pipette: 피펫
switchLayers: 레이어 전환 switchLayers: 레이어 전환
clearBelts: Clear belts clearBelts: 벨트 청소하기
buildingPlacement: buildingPlacement:
cycleBuildingVariants: <key> 키를 눌러 변형 전환 cycleBuildingVariants: <key> 키를 눌러 변형 전환
hotkeyLabel: "단축키: <key>" hotkeyLabel: "단축키: <key>"
@ -320,9 +320,8 @@ ingame:
1_3_expand: "이 게임은 방치형 게임이 <strong>아닙니다</strong>! 더 많은 추출기와 벨트를 만들어 지정된 목표를 빨리 1_3_expand: "이 게임은 방치형 게임이 <strong>아닙니다</strong>! 더 많은 추출기와 벨트를 만들어 지정된 목표를 빨리
달성하세요.<br><br> 팁: <strong>SHIFT</strong> 키를 누른 상태에서는 빠르게 배치할 수 달성하세요.<br><br> 팁: <strong>SHIFT</strong> 키를 누른 상태에서는 빠르게 배치할 수
있고, <strong>R</strong> 키를 눌러 회전할 수 있습니다." 있고, <strong>R</strong> 키를 눌러 회전할 수 있습니다."
2_1_place_cutter: "Now place a <strong>Cutter</strong> to cut the circles in two 2_1_place_cutter: "이제 <strong>절단기</strong>를 배치해 원형 도형을 반으로 잘라보세요!<br><br> 참고:
halves!<br><br> PS: The cutter always cuts from <strong>top to 절단기는 놓는 방향에 상관없이 항상 <strong>위에서 아래로만</strong> 자릅니다."
bottom</strong> regardless of its orientation."
2_2_place_trash: 절단기가 <strong>막히거나 멈출 수 있습니다</strong>!<br><br> 2_2_place_trash: 절단기가 <strong>막히거나 멈출 수 있습니다</strong>!<br><br>
<strong>휴지통</strong>을 사용하여 현재 필요없는 쓰레기 도형 (!)을 제거하세요. <strong>휴지통</strong>을 사용하여 현재 필요없는 쓰레기 도형 (!)을 제거하세요.
2_3_more_cutters: "잘하셨습니다! 느린 처리 속도를 보완하기 위해 <strong>절단기를 두 개</strong> 이상 2_3_more_cutters: "잘하셨습니다! 느린 처리 속도를 보완하기 위해 <strong>절단기를 두 개</strong> 이상
@ -416,6 +415,7 @@ ingame:
titleRatingDesc: 당신의 평가는 제가 미래에 더 나은 평가를 만들수 있도록 도울 수 있습니다. titleRatingDesc: 당신의 평가는 제가 미래에 더 나은 평가를 만들수 있도록 도울 수 있습니다.
continueBtn: 계속 플레이하기 continueBtn: 계속 플레이하기
menuBtn: 메뉴 menuBtn: 메뉴
nextPuzzle: Next Puzzle
puzzleMetadata: puzzleMetadata:
author: 제작자 author: 제작자
shortKey: 짧은 키 shortKey: 짧은 키
@ -886,7 +886,12 @@ settings:
mapResourcesScale: mapResourcesScale:
title: 지도 자원 크기 title: 지도 자원 크기
description: 지도를 축소할 때 나타나는 도형의 크기를 제어합니다. description: 지도를 축소할 때 나타나는 도형의 크기를 제어합니다.
shapeTooltipAlwaysOn:
title: Shape Tooltip - Show Always
description: Whether to always show the shape tooltip when hovering buildings,
instead of having to hold 'ALT'.
rangeSliderPercentage: <amount> % rangeSliderPercentage: <amount> %
tickrateHz: <amount> Hz
keybindings: keybindings:
title: 조작법 title: 조작법
hint: "팁: CTRL, SHIFT, ALT를 적절히 사용하세요! 건물을 배치할 때 유용합니다." hint: "팁: CTRL, SHIFT, ALT를 적절히 사용하세요! 건물을 배치할 때 유용합니다."
@ -967,6 +972,7 @@ keybindings:
goal_acceptor: 목표 수집기 goal_acceptor: 목표 수집기
block: 블록 block: 블록
massSelectClear: 벨트 초기화 massSelectClear: 벨트 초기화
showShapeTooltip: Show shape output tooltip
about: about:
title: 게임 정보 title: 게임 정보
body: >- body: >-
@ -1085,8 +1091,23 @@ puzzleMenu:
easy: 쉬움 easy: 쉬움
medium: 중간 medium: 중간
hard: 어려움 hard: 어려움
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking unknown: Unrated
shapez.io in your library, selecting Properties > DLCs. dlcHint: DLC를 이미 구입하셨나요? 라이브러리에서 shapez.io를 오른쪽 클릭한 다음 속성… > DLC 메뉴를 선택해서
활성화되었는지 확인해주세요.
search:
action: Search
placeholder: Enter a puzzle or author name
includeCompleted: Include Completed
difficulties:
any: Any Difficulty
easy: Easy
medium: Medium
hard: Hard
durations:
any: Any Duration
short: Short (< 2 min)
medium: Normal
long: Long (> 10 min)
backendErrors: backendErrors:
ratelimit: 너무 빠른 시간 내 작업을 반복하고 있습니다. 조금만 기다려 주세요. ratelimit: 너무 빠른 시간 내 작업을 반복하고 있습니다. 조금만 기다려 주세요.
invalid-api-key: 백엔드 서버와 통신할 수 없습니다. 게임을 업데이트하거나 재시작해 주세요 (잘못된 API 키). invalid-api-key: 백엔드 서버와 통신할 수 없습니다. 게임을 업데이트하거나 재시작해 주세요 (잘못된 API 키).

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