Merge branch 'tobspr:master' into zone_indicators

pull/1254/head
Sense101 3 years ago committed by GitHub
commit bb0c58e784
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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

@ -1,5 +1,5 @@
const railsdk = require("./wegame_sdk/railsdk.js"); const railsdk = require("./wegame_sdk/railsdk.js");
const { dialog, remote } = 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 };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

@ -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";

@ -550,6 +550,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;
@ -565,6 +575,40 @@
grid-template-columns: auto 1fr; grid-template-columns: auto 1fr;
} }
&.wegameDisclaimer {
@include SuperSmallText;
display: grid;
justify-content: center;
grid-template-columns: 1fr auto 1fr;
> .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;

@ -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);
}
}
}

@ -34,6 +34,7 @@ import { RestrictionManager } from "./core/restriction_manager";
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
@ -155,6 +156,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,
@ -330,8 +332,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");

@ -1,4 +1,16 @@
export const CHANGELOG = [ export const CHANGELOG = [
// Not finished yet
{
version: "1.4.3",
date: "preview",
entries: [
"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",
"Updated translations",
],
},
{ {
version: "1.4.2", version: "1.4.2",
date: "24.06.2021", date: "24.06.2021",

@ -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();
} }
} }

@ -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;
} }
} }

@ -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

@ -585,12 +585,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);
]; }
} }
/** /**

@ -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),
}); });

@ -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;
} }
/** /**
@ -319,9 +309,14 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const nextSlot = processorComp.nextOutputSlot++ % availableSlots; const nextSlot = processorComp.nextOutputSlot++ % availableSlots;
for (let i = 0; i < payload.items.length; ++i) { // Hardcoded to 2, to avoid accessing the length
for (let i = 0; i < 2; ++i) {
const item = payload.items.get(i);
if (!item) {
continue;
}
payload.outItems.push({ payload.outItems.push({
item: payload.items[i].item, item,
preferredSlot: (nextSlot + i) % availableSlots, preferredSlot: (nextSlot + i) % availableSlots,
doNotTrack: true, doNotTrack: true,
}); });
@ -333,7 +328,7 @@ 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;
@ -354,7 +349,7 @@ 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;
@ -375,7 +370,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 +384,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 +398,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 +412,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 +439,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 +462,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 +479,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 +509,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 +536,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 +555,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 +570,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 +584,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 = {

@ -111,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);

@ -130,7 +130,17 @@ export class MainMenuState extends GameState {
${ ${
G_WEGAME_VERSION G_WEGAME_VERSION
? "<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 ${G_CHINA_VERSION ? "china" : ""} ">
@ -163,6 +173,15 @@ export class MainMenuState extends GameState {
'<a class="producerLink" target="_blank">Tobias Springer</a>' '<a class="producerLink" target="_blank">Tobias Springer</a>'
)}</div> )}</div>
</div> </div>
${
G_IS_STANDALONE
? ""
: `
<iframe id="crosspromo" src="https://crosspromo.tobspr.io?src=shapez_web"></iframe>
`
}
` `
} }
`; `;
@ -341,6 +360,11 @@ export class MainMenuState extends GameState {
if (puzzleWishlistButton) { if (puzzleWishlistButton) {
this.trackClicks(puzzleWishlistButton, () => this.onPuzzleWishlistButtonClicked()); this.trackClicks(puzzleWishlistButton, () => this.onPuzzleWishlistButtonClicked());
} }
const wegameRating = qs(".wegameDisclaimer > .rating");
if (wegameRating) {
this.trackClicks(wegameRating, () => this.onWegameRatingClicked());
}
} }
renderMainMenu() { renderMainMenu() {
@ -675,6 +699,22 @@ export class MainMenuState extends GameState {
}); });
} }
onWegameRatingClicked() {
this.dialogs.showInfo(
"",
`
1本游戏是一款休闲建造类单机游戏适用于年满8周岁及以上的用户<br>
2本游戏模拟简单的生产流水线剧情简单且积极向上没有基于真实
历史和现实事件的改编内容游戏玩法为摆放简单的部件完成生产目标
游戏为单机作品没有基于文字和语音的陌生人社交系统<br>
3游戏中有用户实名认证系统认证为未成年人的用户将接受以下管理
游戏为买断制不存在后续充值付费内容未成年人用户每日22点到次日
8点不得使用法定节假日每天不得使用超过3小时其它时间每天使用游
戏不得超过1.5小时
`
);
}
onContinueButtonClicked() { onContinueButtonClicked() {
let latestLastUpdate = 0; let latestLastUpdate = 0;
let latestInternalId; let latestInternalId;

@ -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
);
}
}

File diff suppressed because it is too large Load Diff

@ -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é
trouvé comment faire un ordinateur dans shapez.io 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.
Very chill factory game that won't let me stop making my lines more Jeu d'usine très cool qui ne me laissera pas arrêter de rendre mes lignes plus
efficient. 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
Steam dés maintenant pour encore plus d'amusement ! sur 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,61 @@ 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 icône de
your puzzle (You can generate them <link>here</link>, or choose one votre puzzle (Vous pouvez générer le raccourci d'une forme <link>ici</link>, ou en choisir une
of the randomly suggested shapes below):" parmi les formes suggérées alétoirement ci-dessous):"
placeholderName: Puzzle Title 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 dehors de la zone
outside the 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 mode hors ligne.
Please make sure you have an active internet connection. 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 publier votre puzzle. Souhaitez-vous toujours continuer ?
puzzle. Would you still like to continue?
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 niveau 12
before attempting the puzzle DLC, otherwise you may encounter avant d'essayer le Puzzle DLC, sinon vous risqez de rencontrer
mechanics not yet introduced. Do you still want to continue? 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
can be entered in the puzzle menu to access the puzzle. peut ê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 +428,26 @@ 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
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 des formes et
colors to the player 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 fabrique plus tard et
deliver it to one or more <strong>Goal Acceptors</strong> délivrez-la/les à un ou plusieurs <strong>Récepteurs d'Objectif</strong>
- 3. Once a Goal Acceptor receives a shape for a certain amount of - 3. Une fois qu'un Récépteur d'Objectif a reçu une forme pendant un certain
time, it <strong>saves it as a goal</strong> that the player must temps, il <strong>l'enregistre zn tant qu'objectif</strong> que le joueur devra
produce later (Indicated by the <strong>green badge</strong>). 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 +456,18 @@ 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
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 +687,25 @@ 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 formes en deux
from top to bottom <strong>regardless of its 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 déchets, ou
otherwise <strong>it will clog and stall</strong> - For this purpose sinon <strong>il se bouchera et se bloquera</strong> - À cet effet,
I have given you the <strong>trash</strong>, which destroys Je vous ai donné la <strong>poubelle</strong>, qui détruit
everything you put into it! 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 +731,9 @@ 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 <strong>divisant et en rassemblant
items</strong> onto multiple belts! 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 +839,14 @@ 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 permettent de
<strong>simulate the processing of shapes</strong>!<br><br> You can <strong>simuler le traitement des formes</strong>!<br><br> Vous pouvez
now simulate a cutter, rotator, stacker and more on the wires layer! maintenant simuler un découpeur, un pivoteur, un assembleur et plus encore sur la couche des fils !
With this you now have three options to continue the game:<br><br> - Avec cela vous avez maintenant trois options pour continuer le jeu:<br><br> -
Build an <strong>automated machine</strong> to create any possible Construire une <strong>machine automatique</strong> pour créer toute forme
shape requested by the HUB (I recommend to try it!).<br><br> - Build possible demandée par le HUB (Je recommande d'essayer de le faire !).<br><br> - Construire
something cool with wires.<br><br> - Continue to play quelque chose de cool avec les fils.<br><br> - Continuer à jouer
normally.<br><br> Whatever you choose, remember to have fun! 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
@ -1127,10 +1123,10 @@ 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
about: about:
title: À propos de ce jeu title: À propos de ce jeu
body: >- body: >-
@ -1156,7 +1152,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 +1233,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 +1241,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 !
@ -1302,6 +1298,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.

@ -14,7 +14,7 @@ steamPage:
Ondanks het feit dat je in het begin alleen vormen maakt, komt er een punt waarop je ze gaat kleuren. Deze kleuren kun je vinden en mengen! Ondanks het feit dat je in het begin alleen vormen maakt, komt er een punt waarop je ze gaat kleuren. Deze kleuren kun je vinden en mengen!
Door het spel op Steam te kopen kun je de volledige versie spelen. Je kunt echter ook een demo versie spelen op shapez.io en later beslissen om over te schakelen zonder voortgang te verliezen. Door het spel op Steam te kopen kun je de volledige versie spelen. Je kunt echter ook een demo versie spelen op shapez.io en later beslissen om over te schakelen zonder voortgang te verliezen.
what_others_say: What people say about shapez.io what_others_say: Wat anderen vinden van shapez.io
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.
notch_comment: Oh crap. I really should sleep, but I think I just figured out notch_comment: Oh crap. I really should sleep, but I think I just figured out
@ -1166,7 +1166,6 @@ tips:
wordt de planner geactiveerd, zodat je gemakkelijk lange rijen kunt wordt de planner geactiveerd, zodat je gemakkelijk lange rijen kunt
plaatsen. plaatsen.
- Knippers knippen altijd verticaal, ongeacht hun oriëntatie. - Knippers knippen altijd verticaal, ongeacht hun oriëntatie.
- Meng alle drie de kleuren om wit te krijgen.
- De opslagbuffer geeft prioriteit aan de eerste uitvoer. - De opslagbuffer geeft prioriteit aan de eerste uitvoer.
- Investeer tijd om herhaalbare ontwerpen te maken - het is het waard! - Investeer tijd om herhaalbare ontwerpen te maken - het is het waard!
- Door <b>SHIFT</b> ingedrukt te houden, kunnen meerdere gebouwen worden - Door <b>SHIFT</b> ingedrukt te houden, kunnen meerdere gebouwen worden

@ -1108,10 +1108,10 @@ keybindings:
placementDisableAutoOrientation: Отключить автоопределение направления placementDisableAutoOrientation: Отключить автоопределение направления
placeMultiple: Оставаться в режиме размещения placeMultiple: Оставаться в режиме размещения
placeInverse: Инвертировать автоопределение направления конвейеров placeInverse: Инвертировать автоопределение направления конвейеров
constant_producer: Constant Producer constant_producer: Постоянный генератор
goal_acceptor: Goal Acceptor goal_acceptor: Приёмник предметов
block: Block block: Блок
massSelectClear: Clear belts massSelectClear: Очистить конвейеры
about: about:
title: Об игре title: Об игре
body: >- body: >-
@ -1245,11 +1245,11 @@ puzzleMenu:
постоянные производители не доставляют фигуры напрямую приемникам постоянные производители не доставляют фигуры напрямую приемникам
цели. цели.
difficulties: difficulties:
easy: Лего easy: Легко
medium: Средне medium: Средне
hard: Сложно hard: Сложно
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking dlcHint: Уже купили DLC? Проверьте, что оно активировано, нажав правый клик на
shapez.io in your library, selecting Properties > DLCs. shapez.io в своей библиотеке, и далее Свойства > Доп. Контент
backendErrors: backendErrors:
ratelimit: Вы слишком часто выполняете свои действия. Подождите немного. ratelimit: Вы слишком часто выполняете свои действия. Подождите немного.
invalid-api-key: Не удалось связаться с сервером, попробуйте invalid-api-key: Не удалось связаться с сервером, попробуйте

File diff suppressed because it is too large Load Diff

@ -1 +1 @@
1.4.2 1.4.3
Loading…
Cancel
Save