1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-13 13:04:03 +00:00

Merge remote-tracking branch 'origin/master' into better-building-variants

This commit is contained in:
Exund 2020-09-19 14:37:08 +02:00
commit 88d457af0e
65 changed files with 6103 additions and 5684 deletions

View File

@ -64,7 +64,7 @@ This project is based on ES5. Some ES2015 features are used but most of them are
5. Add a constructor. **The constructor must be called with optional parameters only!** `new MyFancyComponent({})` should always work. 5. Add a constructor. **The constructor must be called with optional parameters only!** `new MyFancyComponent({})` should always work.
6. Add any props you need in the constructor. 6. Add any props you need in the constructor.
7. Add the component in `src/js/game/component_registry.js` 7. Add the component in `src/js/game/component_registry.js`
8. Add the componetn in `src/js/game/entity_components.js` 8. Add the component in `src/js/game/entity_components.js`
9. Done! You can use your component now 9. Done! You can use your component now
#### Adding a new building #### Adding a new building
@ -96,6 +96,6 @@ This project is based on ES5. Some ES2015 features are used but most of them are
For most assets I use Adobe Photoshop, you can find them in `assets/`. For most assets I use Adobe Photoshop, you can find them in `assets/`.
You will need a <a href="https://www.codeandweb.com/texturepacker" target="_blank">Texture Packer</a> license in order to regenerate the atlas. If you don't have one but want to contribute assets, let me know and I might compile it for you. I'm currently switching to an open source solution but I can't give an estimate when thats done. You will need a <a href="https://www.codeandweb.com/texturepacker" target="_blank">Texture Packer</a> license in order to regenerate the atlas. If you don't have one but want to contribute assets, let me know and I might compile it for you. I'm currently switching to an open source solution but I can't give an estimate when that's done.
<img src="https://i.imgur.com/W25Fkl0.png" alt="shapez.io Screenshot"> <img src="https://i.imgur.com/W25Fkl0.png" alt="shapez.io Screenshot">

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

After

Width:  |  Height:  |  Size: 276 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 KiB

After

Width:  |  Height:  |  Size: 688 KiB

View File

@ -283,6 +283,7 @@
<key type="filename">sprites/blueprints/virtual_processor-analyzer.png</key> <key type="filename">sprites/blueprints/virtual_processor-analyzer.png</key>
<key type="filename">sprites/blueprints/virtual_processor-rotater.png</key> <key type="filename">sprites/blueprints/virtual_processor-rotater.png</key>
<key type="filename">sprites/blueprints/virtual_processor-shapecompare.png</key> <key type="filename">sprites/blueprints/virtual_processor-shapecompare.png</key>
<key type="filename">sprites/blueprints/virtual_processor-stacker.png</key>
<key type="filename">sprites/blueprints/virtual_processor-unstacker.png</key> <key type="filename">sprites/blueprints/virtual_processor-unstacker.png</key>
<key type="filename">sprites/blueprints/virtual_processor.png</key> <key type="filename">sprites/blueprints/virtual_processor.png</key>
<key type="filename">sprites/blueprints/wire_tunnel-coating.png</key> <key type="filename">sprites/blueprints/wire_tunnel-coating.png</key>
@ -310,6 +311,7 @@
<key type="filename">sprites/buildings/virtual_processor-analyzer.png</key> <key type="filename">sprites/buildings/virtual_processor-analyzer.png</key>
<key type="filename">sprites/buildings/virtual_processor-rotater.png</key> <key type="filename">sprites/buildings/virtual_processor-rotater.png</key>
<key type="filename">sprites/buildings/virtual_processor-shapecompare.png</key> <key type="filename">sprites/buildings/virtual_processor-shapecompare.png</key>
<key type="filename">sprites/buildings/virtual_processor-stacker.png</key>
<key type="filename">sprites/buildings/virtual_processor-unstacker.png</key> <key type="filename">sprites/buildings/virtual_processor-unstacker.png</key>
<key type="filename">sprites/buildings/virtual_processor.png</key> <key type="filename">sprites/buildings/virtual_processor.png</key>
<key type="filename">sprites/buildings/wire_tunnel-coating.png</key> <key type="filename">sprites/buildings/wire_tunnel-coating.png</key>

View File

@ -468,13 +468,24 @@ canvas {
display: flex; display: flex;
flex-grow: 1; flex-grow: 1;
} }
.hint {
position: absolute;
@include S(left, 20px);
@include S(right, 20px);
@include S(bottom, 60px);
@include Text;
color: #666;
}
.loadingStatus { .loadingStatus {
position: absolute; position: absolute;
@include S(left, 20px); @include S(left, 20px);
@include S(right, 20px); @include S(right, 20px);
@include S(bottom, 30px); @include S(bottom, 30px);
@include Text; @include Text;
@include TextShadow3D(#aaa); @include PlainText;
color: #aaa;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;

View File

@ -1,43 +1,79 @@
#ingame_HUD_EntityDebugger { #ingame_HUD_EntityDebugger {
position: absolute; position: absolute;
background: $ingameHudBg;
@include S(padding, 5px);
@include S(right, 30px); @include S(right, 30px);
@include S(top, 200px);
font-size: 14px; top: 50%;
line-height: 16px; transform: translateY(-50%);
color: #fff;
background: rgba(0, 10, 20, 0.7); @include SuperSmallText;
padding: 10px; color: #eee;
display: flex;
flex-direction: column;
> label {
text-transform: uppercase;
}
.hint {
color: #aaa;
}
&, &,
* { * {
pointer-events: all; pointer-events: all;
} }
.flag { .propertyTable {
display: inline-block; @include S(margin-top, 8px);
background: #333438;
@include S(padding, 2px);
@include S(margin-right, 2px);
u {
opacity: 0.5;
}
} }
.components { .propertyTable,
@include S(margin-top, 4px); .entityComponents,
.entityComponents .object > div {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr auto;
@include S(grid-gap, 3px); @include S(column-gap, 10px);
.component { }
@include S(padding, 2px);
background: #333;
display: flex;
flex-direction: column;
.data { .entityComponents {
@include S(width, 150px); grid-column: 1 / 3;
@include S(height, 130px); @include S(margin-top, 5px);
font-family: "Roboto Mono", "Fira Code", monospace;
font-size: 90%;
@include S(letter-spacing, -0.5px);
label,
span {
line-height: 1.5em;
&:not(span) {
opacity: 0.5;
}
}
&,
* {
@include SuperSmallText;
@include S(font-size, 7px, $important: true);
@include S(line-height, 12px, $important: true);
}
.object {
grid-column: 1 / 3;
line-height: 1.5em;
> summary {
transition: opacity 0.1s ease-in-out;
cursor: pointer;
&:hover {
opacity: 0.8;
}
}
> div {
@include S(margin-left, 4px);
cursor: pointer;
} }
} }
} }

View File

@ -56,6 +56,17 @@
transform: scale(1.1, 1.1); transform: scale(1.1, 1.1);
} }
} }
&.saving {
@include InlineAnimation(0.4s ease-in-out infinite) {
50% {
opacity: 0.5;
transform: scale(0.8);
}
}
pointer-events: none;
cursor: default;
}
} }
&.settings { &.settings {

View File

@ -18,7 +18,6 @@
@include S(margin-bottom, 4px); @include S(margin-bottom, 4px);
color: #333438; color: #333438;
// text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2); // text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2);
filter: drop-shadow(#{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2));
&.unpinable { &.unpinable {
> canvas { > canvas {

View File

@ -39,13 +39,6 @@
} }
} }
.seperator {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
.layer { .layer {
position: relative; position: relative;
background: #eee; background: #eee;

View File

@ -14,6 +14,15 @@
flex-direction: column; flex-direction: column;
} }
.hint {
position: absolute;
@include S(bottom, 40px);
@include S(left, 20px);
@include S(right, 20px);
@include PlainText;
text-align: center;
}
#ingame_Canvas { #ingame_Canvas {
position: absolute; position: absolute;
top: 0; top: 0;

View File

@ -109,7 +109,7 @@ export const CHANGELOG = [
date: "17.06.2020", date: "17.06.2020",
entries: [ entries: [
"You can now place straight belts (and tunnels) by holding SHIFT! (For you, @giantwaffle ❤️)", "You can now place straight belts (and tunnels) by holding SHIFT! (For you, @giantwaffle ❤️)",
"Added continue button to main menu and add seperate 'New game' button (by jaysc)", "Added continue button to main menu and add separate 'New game' button (by jaysc)",
"Added setting to disable smart tunnel placement introduced with the last update", "Added setting to disable smart tunnel placement introduced with the last update",
"Added setting to disable vignette", "Added setting to disable vignette",
"Update translations", "Update translations",

View File

@ -13,7 +13,7 @@ import { round1Digit } from "./utils";
const logger = createLogger("buffers"); const logger = createLogger("buffers");
const bufferGcDurationSeconds = 5; const bufferGcDurationSeconds = 0.5;
export class BufferMaintainer { export class BufferMaintainer {
/** /**

View File

@ -25,17 +25,43 @@ export function disableImageSmoothing(context) {
context.webkitImageSmoothingEnabled = false; context.webkitImageSmoothingEnabled = false;
} }
const registeredCanvas = []; /**
const freeCanvasList = []; * @typedef {{
* canvas: HTMLCanvasElement,
* context: CanvasRenderingContext2D
* }} CanvasCacheEntry
*/
let vramUsage = 0; /**
let bufferCount = 0; * @type {Array<CanvasCacheEntry>}
*/
const registeredCanvas = [];
/**
* Buckets for each width * height combination
* @type {Map<number, Array<CanvasCacheEntry>>}
*/
const freeCanvasBuckets = new Map();
/**
* Track statistics
*/
const stats = {
vramUsage: 0,
backlogVramUsage: 0,
bufferCount: 0,
numReused: 0,
numCreated: 0,
};
/** /**
* *
* @param {HTMLCanvasElement} canvas * @param {HTMLCanvasElement} canvas
*/ */
export function getBufferVramUsageBytes(canvas) { export function getBufferVramUsageBytes(canvas) {
assert(canvas, "no canvas given");
assert(Number.isFinite(canvas.width), "bad canvas width: " + canvas.width);
assert(Number.isFinite(canvas.height), "bad canvas height" + canvas.height);
return canvas.width * canvas.height * 4; return canvas.width * canvas.height * 4;
} }
@ -43,17 +69,31 @@ export function getBufferVramUsageBytes(canvas) {
* Returns stats on the allocated buffers * Returns stats on the allocated buffers
*/ */
export function getBufferStats() { export function getBufferStats() {
let numBuffersFree = 0;
freeCanvasBuckets.forEach(bucket => {
numBuffersFree += bucket.length;
});
return { return {
vramUsage, ...stats,
bufferCount, backlogKeys: freeCanvasBuckets.size,
backlog: freeCanvasList.length, backlogSize: numBuffersFree,
}; };
} }
/**
* Clears the backlog buffers if they grew too much
*/
export function clearBufferBacklog() { export function clearBufferBacklog() {
while (freeCanvasList.length > 50) { freeCanvasBuckets.forEach(bucket => {
freeCanvasList.pop(); while (bucket.length > 500) {
} const entry = bucket[bucket.length - 1];
stats.backlogVramUsage -= getBufferVramUsageBytes(entry.canvas);
delete entry.canvas;
delete entry.context;
bucket.pop();
}
});
} }
/** /**
@ -84,53 +124,29 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe
let canvas = null; let canvas = null;
let context = null; let context = null;
let bestMatchingOne = null;
let bestMatchingPixelsDiff = 1e50;
const currentPixels = w * h;
// Ok, search in cache first // Ok, search in cache first
for (let i = 0; i < freeCanvasList.length; ++i) { const bucket = freeCanvasBuckets.get(w * h) || [];
const { canvas: useableCanvas, context: useableContext } = freeCanvasList[i];
for (let i = 0; i < bucket.length; ++i) {
const { canvas: useableCanvas, context: useableContext } = bucket[i];
if (useableCanvas.width === w && useableCanvas.height === h) { if (useableCanvas.width === w && useableCanvas.height === h) {
// Ok we found one // Ok we found one
canvas = useableCanvas; canvas = useableCanvas;
context = useableContext; context = useableContext;
fastArrayDelete(freeCanvasList, i); // Restore past state
context.restore();
context.save();
context.clearRect(0, 0, canvas.width, canvas.height);
delete canvas.style.width;
delete canvas.style.height;
stats.numReused++;
stats.backlogVramUsage -= getBufferVramUsageBytes(canvas);
fastArrayDelete(bucket, i);
break; break;
} }
const otherPixels = useableCanvas.width * useableCanvas.height;
const diff = Math.abs(otherPixels - currentPixels);
if (diff < bestMatchingPixelsDiff) {
bestMatchingPixelsDiff = diff;
bestMatchingOne = {
canvas: useableCanvas,
context: useableContext,
index: i,
};
}
}
// Ok none matching, reuse one though
if (!canvas && bestMatchingOne) {
canvas = bestMatchingOne.canvas;
context = bestMatchingOne.context;
canvas.width = w;
canvas.height = h;
fastArrayDelete(freeCanvasList, bestMatchingOne.index);
}
// Reset context
if (context) {
// Restore past state
context.restore();
context.save();
context.clearRect(0, 0, canvas.width, canvas.height);
delete canvas.style.width;
delete canvas.style.height;
} }
// None found , create new one // None found , create new one
@ -138,6 +154,8 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe
canvas = document.createElement("canvas"); canvas = document.createElement("canvas");
context = canvas.getContext("2d" /*, { alpha } */); context = canvas.getContext("2d" /*, { alpha } */);
stats.numCreated++;
canvas.width = w; canvas.width = w;
canvas.height = h; canvas.height = h;
@ -145,6 +163,7 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe
context.save(); context.save();
} }
// @ts-ignore
canvas.label = label; canvas.label = label;
if (smooth) { if (smooth) {
@ -167,8 +186,9 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe
export function registerCanvas(canvas, context) { export function registerCanvas(canvas, context) {
registeredCanvas.push({ canvas, context }); registeredCanvas.push({ canvas, context });
bufferCount += 1; stats.bufferCount += 1;
vramUsage += getBufferVramUsageBytes(canvas); const bytesUsed = getBufferVramUsageBytes(canvas);
stats.vramUsage += bytesUsed;
} }
/** /**
@ -180,6 +200,7 @@ export function freeCanvas(canvas) {
let index = -1; let index = -1;
let data = null; let data = null;
for (let i = 0; i < registeredCanvas.length; ++i) { for (let i = 0; i < registeredCanvas.length; ++i) {
if (registeredCanvas[i].canvas === canvas) { if (registeredCanvas[i].canvas === canvas) {
index = i; index = i;
@ -193,8 +214,18 @@ export function freeCanvas(canvas) {
return; return;
} }
fastArrayDelete(registeredCanvas, index); fastArrayDelete(registeredCanvas, index);
freeCanvasList.push(data);
bufferCount -= 1; const key = canvas.width * canvas.height;
vramUsage -= getBufferVramUsageBytes(canvas); const bucket = freeCanvasBuckets.get(key);
if (bucket) {
bucket.push(data);
} else {
freeCanvasBuckets.set(key, [data]);
}
stats.bufferCount -= 1;
const bytesUsed = getBufferVramUsageBytes(canvas);
stats.vramUsage -= bytesUsed;
stats.backlogVramUsage += bytesUsed;
} }

View File

@ -84,8 +84,8 @@ export const globalConfig = {
// Global game speed // Global game speed
gameSpeed: 1, gameSpeed: 1,
warmupTimeSecondsFast: 0.1, warmupTimeSecondsFast: 0.5,
warmupTimeSecondsRegular: 1, warmupTimeSecondsRegular: 3,
smoothing: { smoothing: {
smoothMainCanvas: smoothCanvas && true, smoothMainCanvas: smoothCanvas && true,
@ -132,5 +132,5 @@ if (G_IS_DEV && globalConfig.debug.renderForTrailer) {
} }
if (globalConfig.debug.fastGameEnter) { if (globalConfig.debug.fastGameEnter) {
globalConfig.debug.noArtificalDelays = true; globalConfig.debug.noArtificialDelays = true;
} }

View File

@ -30,10 +30,10 @@ export class Dialog {
* @param {string} param0.title Title of the dialog * @param {string} param0.title Title of the dialog
* @param {string} param0.contentHTML Inner dialog html * @param {string} param0.contentHTML Inner dialog html
* @param {Array<string>} param0.buttons * @param {Array<string>} param0.buttons
* Button list, each button contains of up to 3 parts seperated by ':'. * Button list, each button contains of up to 3 parts separated by ':'.
* Part 0: The id, one of the one defined in dialog_buttons.yaml * Part 0: The id, one of the one defined in dialog_buttons.yaml
* Part 1: The style, either good, bad or misc * Part 1: The style, either good, bad or misc
* Part 2 (optional): Additional parameters seperated by '/', available are: * Part 2 (optional): Additional parameters separated by '/', available are:
* timeout: This button is only available after some waiting time * timeout: This button is only available after some waiting time
* kb_enter: This button is triggered by the enter key * kb_enter: This button is triggered by the enter key
* kb_escape This button is triggered by the escape key * kb_escape This button is triggered by the escape key

View File

@ -224,7 +224,7 @@ export class ReadWriteProxy {
return rawData; return rawData;
}) })
// Parse JSON, this could throw but thats fine // Parse JSON, this could throw but that's fine
.then(res => { .then(res => {
try { try {
return JSON.parse(res); return JSON.parse(res);

View File

@ -5,6 +5,8 @@ import { round3Digits } from "./utils";
export const ORIGINAL_SPRITE_SCALE = "0.75"; export const ORIGINAL_SPRITE_SCALE = "0.75";
export const FULL_CLIP_RECT = new Rectangle(0, 0, 1, 1); export const FULL_CLIP_RECT = new Rectangle(0, 0, 1, 1);
const EXTRUDE = 0.1;
export class BaseSprite { export class BaseSprite {
/** /**
* Returns the raw handle * Returns the raw handle
@ -206,15 +208,15 @@ export class AtlasSprite extends BaseSprite {
srcX, srcX,
srcY, srcY,
// atlas src siize // atlas src size
srcW, srcW,
srcH, srcH,
// dest pos and size // dest pos and size
destX, destX - EXTRUDE,
destY, destY - EXTRUDE,
destW, destW + 2 * EXTRUDE,
destH destH + 2 * EXTRUDE
); );
} }
@ -267,10 +269,10 @@ export class AtlasSprite extends BaseSprite {
srcH, srcH,
// dest pos and size // dest pos and size
destX, destX - EXTRUDE,
destY, destY - EXTRUDE,
destW, destW + 2 * EXTRUDE,
destH destH + 2 * EXTRUDE
); );
} }

View File

@ -40,9 +40,9 @@ export class StaleAreaDetector {
* Makes this detector recompute the area of an entity whenever * Makes this detector recompute the area of an entity whenever
* it changes in any way * it changes in any way
* @param {Array<typeof Component>} components * @param {Array<typeof Component>} components
* @param {number} tilesAround * @param {number} tilesAround How many tiles arround to expand the area
*/ */
recomputeOnComponentsChanged(components, tilesAround = 1) { recomputeOnComponentsChanged(components, tilesAround) {
const componentIds = components.map(component => component.getId()); const componentIds = components.map(component => component.getId());
/** /**
@ -50,6 +50,10 @@ export class StaleAreaDetector {
* @param {Entity} entity * @param {Entity} entity
*/ */
const checker = entity => { const checker = entity => {
if (!this.root.gameInitialized) {
return;
}
// Check for all components // Check for all components
for (let i = 0; i < componentIds.length; ++i) { for (let i = 0; i < componentIds.length; ++i) {
if (entity.components[componentIds[i]]) { if (entity.components[componentIds[i]]) {

View File

@ -2,7 +2,7 @@ import { globalConfig } from "../core/config";
import { DrawParameters } from "../core/draw_parameters"; import { DrawParameters } from "../core/draw_parameters";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { Rectangle } from "../core/rectangle"; import { Rectangle } from "../core/rectangle";
import { epsilonCompare, round4Digits, clamp } from "../core/utils"; import { clamp, epsilonCompare, round4Digits } from "../core/utils";
import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../core/vector"; import { enumDirection, enumDirectionToVector, enumInvertedDirections, 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";
@ -1069,12 +1069,14 @@ export class BeltPath extends BasicSerializableObject {
// Trigger animation on the acceptor comp // Trigger animation on the acceptor comp
const targetAcceptorComp = this.acceptorTarget.entity.components.ItemAcceptor; const targetAcceptorComp = this.acceptorTarget.entity.components.ItemAcceptor;
if (targetAcceptorComp) { if (targetAcceptorComp) {
targetAcceptorComp.onItemAccepted( if (!this.root.app.settings.getAllSettings().simplifiedBelts) {
this.acceptorTarget.slot, targetAcceptorComp.onItemAccepted(
this.acceptorTarget.direction, this.acceptorTarget.slot,
item, this.acceptorTarget.direction,
remainingProgress item,
); remainingProgress
);
}
} }
return true; return true;
@ -1091,7 +1093,7 @@ export class BeltPath extends BasicSerializableObject {
computePositionFromProgress(progress) { computePositionFromProgress(progress) {
let currentLength = 0; let currentLength = 0;
// floating point issuses .. // floating point issues ..
assert(progress <= this.totalLength + 0.02, "Progress too big: " + progress); assert(progress <= this.totalLength + 0.02, "Progress too big: " + progress);
for (let i = 0; i < this.entityPath.length; ++i) { for (let i = 0; i < this.entityPath.length; ++i) {
@ -1179,6 +1181,35 @@ export class BeltPath extends BasicSerializableObject {
parameters.context.fillRect(firstItemIndicator.x - 3, firstItemIndicator.y - 1, 6, 2); parameters.context.fillRect(firstItemIndicator.x - 3, firstItemIndicator.y - 1, 6, 2);
} }
/**
* Checks if this belt path should render simplified
*/
checkIsPotatoMode() {
// POTATO Mode: Only show items when belt is hovered
if (!this.root.app.settings.getAllSettings().simplifiedBelts) {
return false;
}
const mousePos = this.root.app.mousePosition;
if (!mousePos) {
// Mouse not registered
return true;
}
const tile = this.root.camera.screenToWorld(mousePos).toTileSpace();
const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular");
if (!contents || !contents.components.Belt) {
// Nothing below
return true;
}
if (contents.components.Belt.assignedPath !== this) {
// Not this path
return true;
}
return false;
}
/** /**
* Draws the path * Draws the path
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
@ -1193,6 +1224,30 @@ export class BeltPath extends BasicSerializableObject {
return; return;
} }
if (this.checkIsPotatoMode()) {
const firstItem = this.items[0];
if (this.entityPath.length > 1 && firstItem) {
const medianBeltIndex = clamp(
Math.round(this.entityPath.length / 2 - 1),
0,
this.entityPath.length - 1
);
const medianBelt = this.entityPath[medianBeltIndex];
const beltComp = medianBelt.components.Belt;
const staticComp = medianBelt.components.StaticMapEntity;
const centerPosLocal = beltComp.transformBeltToLocalSpace(
this.entityPath.length % 2 === 0 ? beltComp.getEffectiveLengthTiles() : 0.5
);
const centerPos = staticComp.localTileToWorld(centerPosLocal).toWorldSpaceCenterOfTile();
parameters.context.globalAlpha = 0.5;
firstItem[_item].drawItemCenteredClipped(centerPos.x, centerPos.y, parameters);
parameters.context.globalAlpha = 1;
}
return;
}
let currentItemPos = this.spacingToFirstItem; let currentItemPos = this.spacingToFirstItem;
let currentItemIndex = 0; let currentItemIndex = 0;
@ -1206,7 +1261,7 @@ export class BeltPath extends BasicSerializableObject {
// Check if the current items are on the belt // Check if the current items are on the belt
while (trackPos + beltLength >= currentItemPos - 1e-5) { while (trackPos + beltLength >= currentItemPos - 1e-5) {
// Its on the belt, render it now // It's on the belt, render it now
const staticComp = entity.components.StaticMapEntity; const staticComp = entity.components.StaticMapEntity;
assert( assert(
currentItemPos - trackPos >= 0, currentItemPos - trackPos >= 0,

View File

@ -25,16 +25,27 @@ export const gBuildingVariants = {
// Set later // Set later
}; };
/**
* Mapping from 'metaBuildingId/variant/rotationVariant' to building code
* @type {Map<string, string>}
*/
const variantsCache = new Map();
/** /**
* Registers a new variant * Registers a new variant
* @param {number | string} id * @param {number | string} code
* @param {typeof MetaBuilding} meta * @param {typeof MetaBuilding} meta
* @param {typeof MetaBuildingVariant} variant * @param {typeof MetaBuildingVariant} variant
* @param {number} rotationVariant * @param {number} rotationVariant
*/ */
export function registerBuildingVariant(id, meta, variant, rotationVariant = 0) { export function registerBuildingVariant(
assert(!gBuildingVariants[id], "Duplicate id: " + id); code,
gBuildingVariants[id.toString()] = { meta,
variant,
rotationVariant = 0
) {
assert(!gBuildingVariants[code], "Duplicate id: " + code);
gBuildingVariants[code.toString()] = {
metaClass: meta, metaClass: meta,
variant, variant,
rotationVariant, rotationVariant,
@ -53,31 +64,29 @@ export function getBuildingDataFromCode(code) {
return gBuildingVariants[code]; return gBuildingVariants[code];
} }
/**
* Builds the cache for the codes
*/
export function buildBuildingCodeCache() {
for (const code in gBuildingVariants) {
const data = gBuildingVariants[code];
const hash = data.metaInstance.getId() + "/" + data.variant + "/" + data.rotationVariant;
variantsCache.set(hash, +code);
}
}
/** /**
* Finds the code for a given variant * Finds the code for a given variant
* @param {MetaBuilding} metaBuilding * @param {MetaBuilding} metaBuilding
* @param {typeof MetaBuildingVariant} variant * @param {typeof MetaBuildingVariant} variant
* @param {number} rotationVariant * @param {number} rotationVariant
* @returns {string}
*/ */
export function getCodeFromBuildingData(metaBuilding, variant, rotationVariant) { export function getCodeFromBuildingData(metaBuilding, variant, rotationVariant) {
for (const key in gBuildingVariants) { const hash = metaBuilding.getId() + "/" + variant.getId() + "/" + rotationVariant;
const data = gBuildingVariants[key]; const result = variantsCache.get(hash);
if ( if (G_IS_DEV) {
data.metaInstance.getId() === metaBuilding.getId() && assertAlways(!!result, "Building not found by data: " + hash);
data.variant === variant &&
data.rotationVariant === rotationVariant
) {
return key;
}
} }
assertAlways( return result;
false,
"Building not found by data: " +
metaBuilding.getId() +
" / " +
variant.getId() +
" / " +
rotationVariant
);
return "0";
} }

View File

@ -123,7 +123,7 @@ export class Camera extends BasicSerializableObject {
this.clampZoomLevel(); this.clampZoomLevel();
} }
// Simple geters & setters // Simple getters & setters
addScreenShake(amount) { addScreenShake(amount) {
const currentShakeAmount = this.currentShake.length(); const currentShakeAmount = this.currentShake.length();

View File

@ -2,7 +2,12 @@
import { Application } from "../application"; import { Application } from "../application";
/* typehints:end */ /* typehints:end */
import { BufferMaintainer } from "../core/buffer_maintainer"; import { BufferMaintainer } from "../core/buffer_maintainer";
import { disableImageSmoothing, enableImageSmoothing, registerCanvas } from "../core/buffer_utils"; import {
disableImageSmoothing,
enableImageSmoothing,
getBufferStats,
registerCanvas,
} from "../core/buffer_utils";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { getDeviceDPI, resizeHighDPICanvas } from "../core/dpi_manager"; import { getDeviceDPI, resizeHighDPICanvas } from "../core/dpi_manager";
import { DrawParameters } from "../core/draw_parameters"; import { DrawParameters } from "../core/draw_parameters";
@ -220,9 +225,6 @@ export class GameCore {
lastContext.clearRect(0, 0, lastCanvas.width, lastCanvas.height); lastContext.clearRect(0, 0, lastCanvas.width, lastCanvas.height);
} }
// globalConfig.smoothing.smoothMainCanvas = getDeviceDPI() < 1.5;
// globalConfig.smoothing.smoothMainCanvas = true;
canvas.classList.toggle("smoothed", globalConfig.smoothing.smoothMainCanvas); canvas.classList.toggle("smoothed", globalConfig.smoothing.smoothMainCanvas);
// Oof, use :not() instead // Oof, use :not() instead
@ -375,9 +377,9 @@ export class GameCore {
(zoomLevel / globalConfig.assetsDpi) * getDeviceDPI() * globalConfig.assetsSharpness; (zoomLevel / globalConfig.assetsDpi) * getDeviceDPI() * globalConfig.assetsSharpness;
let desiredAtlasScale = "0.25"; let desiredAtlasScale = "0.25";
if (effectiveZoomLevel > 0.8 && !lowQuality) { if (effectiveZoomLevel > 0.5 && !lowQuality) {
desiredAtlasScale = ORIGINAL_SPRITE_SCALE; desiredAtlasScale = ORIGINAL_SPRITE_SCALE;
} else if (effectiveZoomLevel > 0.4 && !lowQuality) { } else if (effectiveZoomLevel > 0.35 && !lowQuality) {
desiredAtlasScale = "0.5"; desiredAtlasScale = "0.5";
} }
@ -501,18 +503,37 @@ export class GameCore {
); );
const stats = this.root.buffers.getStats(); const stats = this.root.buffers.getStats();
context.fillText( context.fillText(
"Buffers: " + "Maintained Buffers: " +
stats.rootKeys + stats.rootKeys +
" root keys, " + " root keys / " +
stats.subKeys + stats.subKeys +
" sub keys / buffers / VRAM: " + " buffers / VRAM: " +
round2Digits(stats.vramBytes / (1024 * 1024)) + round2Digits(stats.vramBytes / (1024 * 1024)) +
" MB", " MB",
20, 20,
620 620
); );
const internalStats = getBufferStats();
context.fillText(
"Total Buffers: " +
internalStats.bufferCount +
" buffers / " +
internalStats.backlogSize +
" backlog / " +
internalStats.backlogKeys +
" keys in backlog / VRAM " +
round2Digits(internalStats.vramUsage / (1024 * 1024)) +
" MB / Backlog " +
round2Digits(internalStats.backlogVramUsage / (1024 * 1024)) +
" MB / Created " +
internalStats.numCreated +
" / Reused " +
internalStats.numReused,
20,
640
);
} }
if (G_IS_DEV && globalConfig.debug.testClipping) { if (G_IS_DEV && globalConfig.debug.testClipping) {

View File

@ -93,14 +93,6 @@ export class Entity extends BasicSerializableObject {
return clone; return clone;
} }
/**
* Internal destroy callback
*/
internalDestroyCallback() {
assert(!this.destroyed, "Can not destroy entity twice");
this.destroyed = true;
}
/** /**
* Adds a new component, only possible until the entity is registered on the entity manager, * Adds a new component, only possible until the entity is registered on the entity manager,
* after that use @see EntityManager.addDynamicComponent * after that use @see EntityManager.addDynamicComponent

View File

@ -4,6 +4,7 @@ import { GameRoot } from "./root";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { globalConfig } from "../core/config";
const logger = createLogger("entity_manager"); const logger = createLogger("entity_manager");
@ -22,7 +23,7 @@ export class EntityManager extends BasicSerializableObject {
/** @type {Array<Entity>} */ /** @type {Array<Entity>} */
this.entities = []; this.entities = [];
// We store a seperate list with entities to destroy, since we don't destroy // We store a separate list with entities to destroy, since we don't destroy
// them instantly // them instantly
/** @type {Array<Entity>} */ /** @type {Array<Entity>} */
this.destroyList = []; this.destroyList = [];
@ -61,14 +62,13 @@ export class EntityManager extends BasicSerializableObject {
* @param {number=} uid Optional predefined uid * @param {number=} uid Optional predefined uid
*/ */
registerEntity(entity, uid = null) { registerEntity(entity, uid = null) {
assert(this.entities.indexOf(entity) < 0, `RegisterEntity() called twice for entity ${entity}`); if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts) {
assert(this.entities.indexOf(entity) < 0, `RegisterEntity() called twice for entity ${entity}`);
}
assert(!entity.destroyed, `Attempting to register destroyed entity ${entity}`); assert(!entity.destroyed, `Attempting to register destroyed entity ${entity}`);
if (G_IS_DEV && uid !== null) { if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts && uid !== null) {
assert(!this.findByUid(uid, false), "Entity uid already taken: " + uid); assert(!this.findByUid(uid, false), "Entity uid already taken: " + uid);
}
if (uid !== null) {
assert(uid >= 0 && uid < Number.MAX_SAFE_INTEGER, "Invalid uid passed: " + uid); assert(uid >= 0 && uid < Number.MAX_SAFE_INTEGER, "Invalid uid passed: " + uid);
} }
@ -92,18 +92,6 @@ export class EntityManager extends BasicSerializableObject {
this.root.signals.entityAdded.dispatch(entity); this.root.signals.entityAdded.dispatch(entity);
} }
/**
* Sorts all entitiy lists after a resync
*/
sortEntityLists() {
this.entities.sort((a, b) => a.uid - b.uid);
this.destroyList.sort((a, b) => a.uid - b.uid);
for (const key in this.componentToEntity) {
this.componentToEntity[key].sort((a, b) => a.uid - b.uid);
}
}
/** /**
* Generates a new uid * Generates a new uid
* @returns {number} * @returns {number}
@ -167,6 +155,24 @@ export class EntityManager extends BasicSerializableObject {
return null; return null;
} }
/**
* Returns a map which gives a mapping from UID to Entity.
* This map is not updated.
*
* @returns {Map<number, Entity>}
*/
getFrozenUidSearchMap() {
const result = new Map();
const array = this.entities;
for (let i = 0, len = array.length; i < len; ++i) {
const entity = array[i];
if (!entity.queuedForDestroy && !entity.destroyed) {
result.set(entity.uid, entity);
}
}
return result;
}
/** /**
* Returns all entities having the given component * Returns all entities having the given component
* @param {typeof Component} componentHandle * @param {typeof Component} componentHandle
@ -218,7 +224,7 @@ export class EntityManager extends BasicSerializableObject {
this.unregisterEntityComponents(entity); this.unregisterEntityComponents(entity);
entity.registered = false; entity.registered = false;
entity.internalDestroyCallback(); entity.destroyed = true;
this.root.signals.entityDestroyed.dispatch(entity); this.root.signals.entityDestroyed.dispatch(entity);
} }

View File

@ -1,6 +1,8 @@
/* typehints:start */ /* typehints:start */
import { Application } from "../application"; import { Application } from "../application";
/* typehints:end */ /* typehints:end */
import { randomChoice } from "../core/utils";
import { T } from "../translations"; import { T } from "../translations";
export class GameLoadingOverlay { export class GameLoadingOverlay {
@ -43,6 +45,7 @@ export class GameLoadingOverlay {
this.element.classList.add("gameLoadingOverlay"); this.element.classList.add("gameLoadingOverlay");
this.parent.appendChild(this.element); this.parent.appendChild(this.element);
this.internalAddSpinnerAndText(this.element); this.internalAddSpinnerAndText(this.element);
this.internalAddHint(this.element);
} }
/** /**
@ -55,4 +58,15 @@ export class GameLoadingOverlay {
inner.innerText = T.global.loading; inner.innerText = T.global.loading;
element.appendChild(inner); element.appendChild(inner);
} }
/**
* Adds a random hint
* @param {HTMLElement} element
*/
internalAddHint(element) {
const hint = document.createElement("span");
hint.innerHTML = randomChoice(T.tips);
hint.classList.add("hint");
element.appendChild(hint);
}
} }

View File

@ -6,6 +6,7 @@ import { Entity } from "./entity";
import { GameRoot } from "./root"; import { GameRoot } from "./root";
import { GameSystem } from "./game_system"; import { GameSystem } from "./game_system";
import { arrayDelete, arrayDeleteValue } from "../core/utils"; import { arrayDelete, arrayDeleteValue } from "../core/utils";
import { globalConfig } from "../core/config";
export class GameSystemWithFilter extends GameSystem { export class GameSystemWithFilter extends GameSystem {
/** /**
@ -44,7 +45,11 @@ export class GameSystemWithFilter extends GameSystem {
} }
} }
assert(this.allEntities.indexOf(entity) < 0, "entity already in list: " + entity); // This is slow!
if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts) {
assert(this.allEntities.indexOf(entity) < 0, "entity already in list: " + entity);
}
this.internalRegisterEntity(entity); this.internalRegisterEntity(entity);
} }
@ -83,15 +88,16 @@ export class GameSystemWithFilter extends GameSystem {
} }
refreshCaches() { refreshCaches() {
this.allEntities.sort((a, b) => a.uid - b.uid);
// Remove all entities which are queued for destroy // Remove all entities which are queued for destroy
for (let i = 0; i < this.allEntities.length; ++i) { for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i]; const entity = this.allEntities[i];
if (entity.queuedForDestroy || entity.destroyed) { if (entity.queuedForDestroy || entity.destroyed) {
this.allEntities.splice(i, 1); this.allEntities.splice(i, 1);
i -= 1;
} }
} }
this.allEntities.sort((a, b) => a.uid - b.uid);
} }
/** /**

22
src/js/game/hints.js Normal file
View File

@ -0,0 +1,22 @@
import { randomChoice } from "../core/utils";
import { T } from "../translations";
const hintsShown = [];
/**
* Finds a new hint to show about the game which the user hasn't seen within this session
*/
export function getRandomHint() {
let maxTries = 100 * T.tips.length;
while (maxTries-- > 0) {
const hint = randomChoice(T.tips);
if (!hintsShown.includes(hint)) {
hintsShown.push(hint);
return hint;
}
}
// All tips shown so far
return randomChoice(T.tips);
}

View File

@ -330,7 +330,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
if (tileBelow && this.root.app.settings.getAllSettings().pickMinerOnPatch) { if (tileBelow && this.root.app.settings.getAllSettings().pickMinerOnPatch) {
this.currentMetaBuilding.set(gMetaBuildingRegistry.findByClass(MetaMinerBuilding)); this.currentMetaBuilding.set(gMetaBuildingRegistry.findByClass(MetaMinerBuilding));
// Select chained miner if available, since thats always desired once unlocked // Select chained miner if available, since that's always desired once unlocked
if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_miner_chainable)) { if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_miner_chainable)) {
this.currentVariant.set(ChainableMinerVariant); this.currentVariant.set(ChainableMinerVariant);
} }

View File

@ -1,7 +1,13 @@
import { BaseHUDPart } from "../base_hud_part"; /* dev:start */
import { makeDiv, removeAllChildren } from "../../../core/utils"; import { makeDiv, removeAllChildren } from "../../../core/utils";
import { globalConfig } from "../../../core/config"; import { Vector } from "../../../core/vector";
import { Entity } from "../../entity";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
/**
* Allows to inspect entities by pressing F8 while hovering them
*/
export class HUDEntityDebugger extends BaseHUDPart { export class HUDEntityDebugger extends BaseHUDPart {
createElements(parent) { createElements(parent) {
this.element = makeDiv( this.element = makeDiv(
@ -9,65 +15,143 @@ export class HUDEntityDebugger extends BaseHUDPart {
"ingame_HUD_EntityDebugger", "ingame_HUD_EntityDebugger",
[], [],
` `
Tile below cursor: <span class="mousePos"></span><br> <label>Entity Debugger</label>
Chunk below cursor: <span class="chunkPos"></span><br> <span class="hint">Use F8 to toggle this overlay</span>
<div class="entityInfo"></div>
<div class="propertyTable">
<div class="entityComponents"></div>
</div>
` `
); );
this.componentsElem = this.element.querySelector(".entityComponents");
/** @type {HTMLElement} */
this.mousePosElem = this.element.querySelector(".mousePos");
/** @type {HTMLElement} */
this.chunkPosElem = this.element.querySelector(".chunkPos");
this.entityInfoElem = this.element.querySelector(".entityInfo");
} }
initialize() { initialize() {
this.root.camera.downPreHandler.add(this.onMouseDown, this); this.root.gameState.inputReciever.keydown.add(key => {
if (key.keyCode === 119) {
// F8
this.pickEntity();
}
});
/**
* The currently selected entity
* @type {Entity}
*/
this.selectedEntity = null;
this.lastUpdate = 0;
this.domAttach = new DynamicDomAttach(this.root, this.element);
} }
update() { pickEntity() {
const mousePos = this.root.app.mousePosition; const mousePos = this.root.app.mousePosition;
if (!mousePos) { if (!mousePos) {
return; return;
} }
const worldPos = this.root.camera.screenToWorld(mousePos); const worldPos = this.root.camera.screenToWorld(mousePos);
const worldTile = worldPos.toTileSpace(); const worldTile = worldPos.toTileSpace();
const chunk = worldTile.divideScalar(globalConfig.mapChunkSize).floor();
this.mousePosElem.innerText = worldTile.x + " / " + worldTile.y;
this.chunkPosElem.innerText = chunk.x + " / " + chunk.y;
const entity = this.root.map.getTileContent(worldTile, this.root.currentLayer); const entity = this.root.map.getTileContent(worldTile, this.root.currentLayer);
this.selectedEntity = entity;
if (entity) { if (entity) {
removeAllChildren(this.entityInfoElem); this.rerenderFull(entity);
let html = "Entity";
const flag = (name, val) =>
`<span class='flag' data-value='${val ? "1" : "0"}'><u>${name}</u> ${val}</span>`;
html += "<div class='entityFlags'>";
html += flag("registered", entity.registered);
html += flag("uid", entity.uid);
html += flag("destroyed", entity.destroyed);
html += "</div>";
html += "<div class='components'>";
for (const componentId in entity.components) {
const data = entity.components[componentId];
html += "<div class='component'>";
html += "<strong class='name'>" + componentId + "</strong>";
html += "<textarea class='data'>" + JSON.stringify(data.serialize(), null, 2) + "</textarea>";
html += "</div>";
}
html += "</div>";
this.entityInfoElem.innerHTML = html;
} }
} }
onMouseDown() {} /**
*
* @param {string} name
* @param {any} val
* @param {number} indent
* @param {Array} recursion
*/
propertyToHTML(name, val, indent = 0, recursion = []) {
if (val !== null && typeof val === "object") {
// Array is displayed like object, with indexes
recursion.push(val);
// Get type class name (like Array, Object, Vector...)
let typeName = `(${val.constructor ? val.constructor.name : "unknown"})`;
if (Array.isArray(val)) {
typeName = `(Array[${val.length}])`;
}
if (val instanceof Vector) {
typeName = `(Vector[${val.x}, ${val.y}])`;
}
const colorStyle = `color: hsl(${30 * indent}, 100%, 80%)`;
let html = `<details class="object" style="${colorStyle}">
<summary>${name} ${typeName}</summary>
<div>`;
for (const property in val) {
const isRoot = val[property] == this.root;
const isRecursive = recursion.includes(val[property]);
let hiddenValue = isRoot ? "<root>" : null;
if (isRecursive) {
// Avoid recursion by not "expanding" object more than once
hiddenValue = "<recursion>";
}
html += this.propertyToHTML(
property,
hiddenValue ? hiddenValue : val[property],
indent + 1,
[...recursion] // still expand same value in other "branches"
);
}
html += "</div></details>";
return html;
}
const displayValue = (val + "")
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;");
return `<label>${name}</label> <span>${displayValue}</span>`;
}
/**
* Rerenders the whole container
* @param {Entity} entity
*/
rerenderFull(entity) {
removeAllChildren(this.componentsElem);
let html = "";
const property = (strings, val) => `<label>${strings[0]}</label> <span>${val}</span>`;
html += property`registered ${!!entity.registered}`;
html += property`uid ${entity.uid}`;
html += property`destroyed ${!!entity.destroyed}`;
for (const componentId in entity.components) {
const data = entity.components[componentId];
html += "<details class='object'>";
html += "<summary>" + componentId + "</summary><div>";
for (const property in data) {
// Put entity into recursion list, so it won't get "expanded"
html += this.propertyToHTML(property, data[property], 0, [entity]);
}
html += "</div></details>";
}
this.componentsElem.innerHTML = html;
}
update() {
this.domAttach.update(!!this.selectedEntity);
}
} }
/* dev:end */

View File

@ -5,6 +5,7 @@ import { enumNotificationType } from "./notifications";
import { T } from "../../../translations"; import { T } from "../../../translations";
import { KEYMAPPINGS } from "../../key_action_mapper"; import { KEYMAPPINGS } from "../../key_action_mapper";
import { DynamicDomAttach } from "../dynamic_dom_attach"; import { DynamicDomAttach } from "../dynamic_dom_attach";
import { TrackedState } from "../../../core/tracked_state";
export class HUDGameMenu extends BaseHUDPart { export class HUDGameMenu extends BaseHUDPart {
createElements(parent) { createElements(parent) {
@ -97,12 +98,17 @@ export class HUDGameMenu extends BaseHUDPart {
initialize() { initialize() {
this.root.signals.gameSaved.add(this.onGameSaved, this); this.root.signals.gameSaved.add(this.onGameSaved, this);
this.trackedIsSaving = new TrackedState(this.onIsSavingChanged, this);
} }
update() { update() {
let playSound = false; let playSound = false;
let notifications = new Set(); let notifications = new Set();
// Check whether we are saving
this.trackedIsSaving.set(!!this.root.gameState.currentSavePromise);
// Update visibility of buttons // Update visibility of buttons
for (let i = 0; i < this.visibilityToUpdate.length; ++i) { for (let i = 0; i < this.visibilityToUpdate.length; ++i) {
const { condition, domAttach } = this.visibilityToUpdate[i]; const { condition, domAttach } = this.visibilityToUpdate[i];
@ -154,6 +160,10 @@ export class HUDGameMenu extends BaseHUDPart {
}); });
} }
onIsSavingChanged(isSaving) {
this.saveButton.classList.toggle("saving", isSaving);
}
onGameSaved() { onGameSaved() {
this.saveButton.classList.toggle("animEven"); this.saveButton.classList.toggle("animEven");
this.saveButton.classList.toggle("animOdd"); this.saveButton.classList.toggle("animOdd");

View File

@ -48,6 +48,9 @@ export class HUDMassSelector extends BaseHUDPart {
* @param {Entity} entity * @param {Entity} entity
*/ */
onEntityDestroyed(entity) { onEntityDestroyed(entity) {
if (this.root.bulkOperationRunning) {
return;
}
this.selectedUids.delete(entity.uid); this.selectedUids.delete(entity.uid);
} }
@ -90,14 +93,30 @@ export class HUDMassSelector extends BaseHUDPart {
doDelete() { doDelete() {
const entityUids = Array.from(this.selectedUids); const entityUids = Array.from(this.selectedUids);
for (let i = 0; i < entityUids.length; ++i) {
const uid = entityUids[i]; // Build mapping from uid to entity
const entity = this.root.entityMgr.findByUid(uid); /**
if (!this.root.logic.tryDeleteBuilding(entity)) { * @type {Map<number, Entity>}
logger.error("Error in mass delete, could not remove building"); */
this.selectedUids.delete(uid); const mapUidToEntity = this.root.entityMgr.getFrozenUidSearchMap();
this.root.logic.performBulkOperation(() => {
for (let i = 0; i < entityUids.length; ++i) {
const uid = entityUids[i];
const entity = mapUidToEntity.get(uid);
if (!entity) {
logger.error("Entity not found by uid:", uid);
continue;
}
if (!this.root.logic.tryDeleteBuilding(entity)) {
logger.error("Error in mass delete, could not remove building");
}
} }
} });
// Clear uids later
this.selectedUids = new Set();
} }
startCopy() { startCopy() {

View File

@ -1,7 +1,6 @@
import { BaseHUDPart } from "../base_hud_part";
import { makeDiv } from "../../../core/utils"; import { makeDiv } from "../../../core/utils";
import { T } from "../../../translations"; import { T } from "../../../translations";
import { IS_DEMO } from "../../../core/config"; import { BaseHUDPart } from "../base_hud_part";
/** @enum {string} */ /** @enum {string} */
export const enumNotificationType = { export const enumNotificationType = {

View File

@ -204,22 +204,20 @@ export function getStringForKeyCode(code) {
case 115: case 115:
return "F4"; return "F4";
case 116: case 116:
return "F4";
case 117:
return "F5"; return "F5";
case 118: case 117:
return "F6"; return "F6";
case 119: case 118:
return "F7"; return "F7";
case 120: case 119:
return "F8"; return "F8";
case 121: case 120:
return "F9"; return "F9";
case 122: case 121:
return "F10"; return "F10";
case 123: case 122:
return "F11"; return "F11";
case 124: case 123:
return "F12"; return "F12";
case 144: case 144:

View File

@ -88,16 +88,17 @@ export class MapChunkView extends MapChunk {
}); });
const dims = globalConfig.mapChunkWorldSize; const dims = globalConfig.mapChunkWorldSize;
const extrude = 0.05;
// Draw chunk "pixel" art // Draw chunk "pixel" art
parameters.context.imageSmoothingEnabled = false; parameters.context.imageSmoothingEnabled = false;
drawSpriteClipped({ drawSpriteClipped({
parameters, parameters,
sprite, sprite,
x: this.x * dims, x: this.x * dims - extrude,
y: this.y * dims, y: this.y * dims - extrude,
w: dims, w: dims + 2 * extrude,
h: dims, h: dims + 2 * extrude,
originalW: overlaySize, originalW: overlaySize,
originalH: overlaySize, originalH: overlaySize,
}); });

View File

@ -66,32 +66,34 @@ export class MapView extends BaseMap {
* @param {DrawParameters} drawParameters * @param {DrawParameters} drawParameters
*/ */
drawStaticEntityDebugOverlays(drawParameters) { drawStaticEntityDebugOverlays(drawParameters) {
const cullRange = drawParameters.visibleRect.toTileCullRectangle(); if (G_IS_DEV && (globalConfig.debug.showAcceptorEjectors || globalConfig.debug.showEntityBounds)) {
const top = cullRange.top(); const cullRange = drawParameters.visibleRect.toTileCullRectangle();
const right = cullRange.right(); const top = cullRange.top();
const bottom = cullRange.bottom(); const right = cullRange.right();
const left = cullRange.left(); const bottom = cullRange.bottom();
const left = cullRange.left();
const border = 1; const border = 1;
const minY = top - border; const minY = top - border;
const maxY = bottom + border; const maxY = bottom + border;
const minX = left - border; const minX = left - border;
const maxX = right + border - 1; const maxX = right + border - 1;
// Render y from top down for proper blending // Render y from top down for proper blending
for (let y = minY; y <= maxY; ++y) { for (let y = minY; y <= maxY; ++y) {
for (let x = minX; x <= maxX; ++x) { for (let x = minX; x <= maxX; ++x) {
// const content = this.tiles[x][y]; // const content = this.tiles[x][y];
const chunk = this.getChunkAtTileOrNull(x, y); const chunk = this.getChunkAtTileOrNull(x, y);
if (!chunk) { if (!chunk) {
continue; continue;
} }
const content = chunk.getTileContentFromWorldCoords(x, y); const content = chunk.getTileContentFromWorldCoords(x, y);
if (content) { if (content) {
let isBorder = x <= left - 1 || x >= right + 1 || y <= top - 1 || y >= bottom + 1; let isBorder = x <= left - 1 || x >= right + 1 || y <= top - 1 || y >= bottom + 1;
if (!isBorder) { if (!isBorder) {
content.drawDebugOverlays(drawParameters); content.drawDebugOverlays(drawParameters);
}
} }
} }
} }

View File

@ -236,4 +236,7 @@ export function initBuildingCodesAfterResourcesLoaded() {
); );
variant.silhouetteColor = variant.metaInstance.getSilhouetteColor(); variant.silhouetteColor = variant.metaInstance.getSilhouetteColor();
} }
// Update caches
buildBuildingCodeCache();
} }

View File

@ -7,13 +7,13 @@ import { AtlasSprite } from "../../core/sprites";
import { fastArrayDeleteValue } from "../../core/utils"; import { fastArrayDeleteValue } from "../../core/utils";
import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../../core/vector"; import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../../core/vector";
import { BeltPath } from "../belt_path"; import { BeltPath } from "../belt_path";
import { arrayBeltVariantToRotation, MetaBeltBuilding } from "../buildings/belt";
import { getCodeFromBuildingData } from "../building_codes";
import { BeltComponent } from "../components/belt"; import { BeltComponent } from "../components/belt";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter"; import { GameSystemWithFilter } from "../game_system_with_filter";
import { MapChunkView } from "../map_chunk_view"; import { MapChunkView } from "../map_chunk_view";
import { defaultBuildingVariant } from "../meta_building"; import { defaultBuildingVariant } from "../meta_building";
import { getCodeFromBuildingData } from "../building_codes";
import { arrayBeltVariantToRotation, MetaBeltBuilding } from "../buildings/belt";
export const BELT_ANIM_COUNT = 14; export const BELT_ANIM_COUNT = 14;
@ -362,7 +362,7 @@ export class BeltSystem extends GameSystemWithFilter {
const followUpTile = staticComp.origin.add(followUpVector); const followUpTile = staticComp.origin.add(followUpVector);
const followUpEntity = this.root.map.getLayerContentXY(followUpTile.x, followUpTile.y, entity.layer); const followUpEntity = this.root.map.getLayerContentXY(followUpTile.x, followUpTile.y, entity.layer);
// Check if theres a belt at the tile we point to // Check if there's a belt at the tile we point to
if (followUpEntity) { if (followUpEntity) {
const followUpBeltComp = followUpEntity.components.Belt; const followUpBeltComp = followUpEntity.components.Belt;
if (followUpBeltComp) { if (followUpBeltComp) {
@ -392,7 +392,7 @@ export class BeltSystem extends GameSystemWithFilter {
const supplyTile = staticComp.origin.add(supplyVector); const supplyTile = staticComp.origin.add(supplyVector);
const supplyEntity = this.root.map.getLayerContentXY(supplyTile.x, supplyTile.y, entity.layer); const supplyEntity = this.root.map.getLayerContentXY(supplyTile.x, supplyTile.y, entity.layer);
// Check if theres a belt at the tile we point to // Check if there's a belt at the tile we point to
if (supplyEntity) { if (supplyEntity) {
const supplyBeltComp = supplyEntity.components.Belt; const supplyBeltComp = supplyEntity.components.Belt;
if (supplyBeltComp) { if (supplyBeltComp) {
@ -498,14 +498,43 @@ export class BeltSystem extends GameSystemWithFilter {
globalConfig.itemSpacingOnBelts globalConfig.itemSpacingOnBelts
); );
const contents = chunk.containedEntitiesByLayer.regular; const contents = chunk.containedEntitiesByLayer.regular;
for (let i = 0; i < contents.length; ++i) {
const entity = contents[i];
if (entity.components.Belt) {
const direction = entity.components.Belt.direction;
const sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT];
// Culling happens within the static map entity component if (this.root.app.settings.getAllSettings().simplifiedBelts) {
entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite, 0); // POTATO Mode: Only show items when belt is hovered
let hoveredBeltPath = null;
const mousePos = this.root.app.mousePosition;
if (mousePos) {
const tile = this.root.camera.screenToWorld(mousePos).toTileSpace();
const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular");
if (contents && contents.components.Belt) {
hoveredBeltPath = contents.components.Belt.assignedPath;
}
}
for (let i = 0; i < contents.length; ++i) {
const entity = contents[i];
if (entity.components.Belt) {
const direction = entity.components.Belt.direction;
let sprite = this.beltAnimations[direction][0];
if (entity.components.Belt.assignedPath === hoveredBeltPath) {
sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT];
}
// Culling happens within the static map entity component
entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite, 0);
}
}
} else {
for (let i = 0; i < contents.length; ++i) {
const entity = contents[i];
if (entity.components.Belt) {
const direction = entity.components.Belt.direction;
const sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT];
// Culling happens within the static map entity component
entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite, 0);
}
} }
} }
} }

View File

@ -9,14 +9,35 @@ import { MapChunkView } from "../map_chunk_view";
export class ItemAcceptorSystem extends GameSystemWithFilter { export class ItemAcceptorSystem extends GameSystemWithFilter {
constructor(root) { constructor(root) {
super(root, [ItemAcceptorComponent]); super(root, [ItemAcceptorComponent]);
// Well ... it's better to be verbose I guess?
this.accumulatedTicksWhileInMapOverview = 0;
} }
update() { update() {
if (this.root.app.settings.getAllSettings().simplifiedBelts) {
// Disabled in potato mode
return;
}
// This system doesn't render anything while in map overview,
// so simply accumulate ticks
if (this.root.camera.getIsMapOverlayActive()) {
++this.accumulatedTicksWhileInMapOverview;
return;
}
// Compute how much ticks we missed
const numTicks = 1 + this.accumulatedTicksWhileInMapOverview;
const progress = const progress =
this.root.dynamicTickrate.deltaSeconds * this.root.dynamicTickrate.deltaSeconds *
2 * 2 *
this.root.hubGoals.getBeltBaseSpeed() * this.root.hubGoals.getBeltBaseSpeed() *
globalConfig.itemSpacingOnBelts; // * 2 because its only a half tile globalConfig.itemSpacingOnBelts * // * 2 because its only a half tile
numTicks;
// Reset accumulated ticks
this.accumulatedTicksWhileInMapOverview = 0;
for (let i = 0; i < this.allEntities.length; ++i) { for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i]; const entity = this.allEntities[i];
@ -40,6 +61,11 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
* @param {MapChunkView} chunk * @param {MapChunkView} chunk
*/ */
drawChunk(parameters, chunk) { drawChunk(parameters, chunk) {
if (this.root.app.settings.getAllSettings().simplifiedBelts) {
// Disabled in potato mode
return;
}
const contents = chunk.containedEntitiesByLayer.regular; const contents = chunk.containedEntitiesByLayer.regular;
for (let i = 0; i < contents.length; ++i) { for (let i = 0; i < contents.length; ++i) {
const entity = contents[i]; const entity = contents[i];

View File

@ -2,8 +2,11 @@ import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters"; import { DrawParameters } from "../../core/draw_parameters";
import { createLogger } from "../../core/logging"; import { createLogger } from "../../core/logging";
import { Rectangle } from "../../core/rectangle"; import { Rectangle } from "../../core/rectangle";
import { StaleAreaDetector } from "../../core/stale_area_detector";
import { enumDirection, enumDirectionToVector } from "../../core/vector"; import { enumDirection, enumDirectionToVector } from "../../core/vector";
import { BaseItem } from "../base_item"; import { BaseItem } from "../base_item";
import { BeltComponent } from "../components/belt";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector"; import { ItemEjectorComponent } from "../components/item_ejector";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter"; import { GameSystemWithFilter } from "../game_system_with_filter";
@ -15,102 +18,52 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
constructor(root) { constructor(root) {
super(root, [ItemEjectorComponent]); super(root, [ItemEjectorComponent]);
this.root.signals.entityAdded.add(this.checkForCacheInvalidation, this); this.staleAreaDetector = new StaleAreaDetector({
this.root.signals.entityDestroyed.add(this.checkForCacheInvalidation, this); root: this.root,
this.root.signals.postLoadHook.add(this.recomputeCache, this); name: "item-ejector",
recomputeMethod: this.recomputeArea.bind(this),
});
/** this.staleAreaDetector.recomputeOnComponentsChanged(
* @type {Rectangle} [ItemEjectorComponent, ItemAcceptorComponent, BeltComponent],
*/ 1
this.areaToRecompute = null; );
this.root.signals.postLoadHook.add(this.recomputeCacheFull, this);
} }
/** /**
* * Recomputes an area after it changed
* @param {Entity} entity * @param {Rectangle} area
*/ */
checkForCacheInvalidation(entity) { recomputeArea(area) {
if (!this.root.gameInitialized) { /** @type {Set<number>} */
return; const seenUids = new Set();
} for (let x = 0; x < area.w; ++x) {
if (!entity.components.StaticMapEntity) { for (let y = 0; y < area.h; ++y) {
return; const tileX = area.x + x;
} const tileY = area.y + y;
// @NOTICE: Item ejector currently only supports regular layer
// Optimize for the common case: adding or removing one building at a time. Clicking const contents = this.root.map.getLayerContentXY(tileX, tileY, "regular");
// and dragging can cause up to 4 add/remove signals. if (contents && contents.components.ItemEjector) {
const staticComp = entity.components.StaticMapEntity; if (!seenUids.has(contents.uid)) {
const bounds = staticComp.getTileSpaceBounds(); seenUids.add(contents.uid);
const expandedBounds = bounds.expandedInAllDirections(2); this.recomputeSingleEntityCache(contents);
if (this.areaToRecompute) {
this.areaToRecompute = this.areaToRecompute.getUnion(expandedBounds);
} else {
this.areaToRecompute = expandedBounds;
}
}
/**
* Precomputes the cache, which makes up for a huge performance improvement
*/
recomputeCache() {
if (this.areaToRecompute) {
logger.log("Recomputing cache using rectangle");
if (G_IS_DEV && globalConfig.debug.renderChanges) {
this.root.hud.parts.changesDebugger.renderChange(
"ejector-area",
this.areaToRecompute,
"#fe50a6"
);
}
this.recomputeAreaCache();
this.areaToRecompute = null;
} else {
logger.log("Full cache recompute");
if (G_IS_DEV && globalConfig.debug.renderChanges) {
this.root.hud.parts.changesDebugger.renderChange(
"ejector-full",
new Rectangle(-1000, -1000, 2000, 2000),
"#fe50a6"
);
}
// Try to find acceptors for every ejector
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
this.recomputeSingleEntityCache(entity);
}
}
}
/**
* Recomputes the cache in the given area
*/
recomputeAreaCache() {
const area = this.areaToRecompute;
let entryCount = 0;
logger.log("Recomputing area:", area.x, area.y, "/", area.w, area.h);
// Store the entities we already recomputed, so we don't do work twice
const recomputedEntities = new Set();
for (let x = area.x; x < area.right(); ++x) {
for (let y = area.y; y < area.bottom(); ++y) {
const entities = this.root.map.getLayersContentsMultipleXY(x, y);
for (let i = 0; i < entities.length; ++i) {
const entity = entities[i];
// Recompute the entity in case its relevant for this system and it
// hasn't already been computed
if (!recomputedEntities.has(entity.uid) && entity.components.ItemEjector) {
recomputedEntities.add(entity.uid);
this.recomputeSingleEntityCache(entity);
} }
} }
} }
} }
return entryCount; }
/**
* Recomputes the whole cache after the game has loaded
*/
recomputeCacheFull() {
logger.log("Full cache recompute in post load hook");
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
this.recomputeSingleEntityCache(entity);
}
} }
/** /**
@ -183,9 +136,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
} }
update() { update() {
if (this.areaToRecompute) { this.staleAreaDetector.update();
this.recomputeCache();
}
// Precompute effective belt speed // Precompute effective belt speed
let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds; let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds;
@ -251,7 +202,13 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
// Try to hand over the item // Try to hand over the item
if (this.tryPassOverItem(item, destEntity, destSlot.index)) { if (this.tryPassOverItem(item, destEntity, destSlot.index)) {
// Handover successful, clear slot // Handover successful, clear slot
targetAcceptorComp.onItemAccepted(destSlot.index, destSlot.acceptedDirection, item); if (!this.root.app.settings.getAllSettings().simplifiedBelts) {
targetAcceptorComp.onItemAccepted(
destSlot.index,
destSlot.acceptedDirection,
item
);
}
sourceSlot.item = null; sourceSlot.item = null;
continue; continue;
} }
@ -333,6 +290,11 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
* @param {MapChunkView} chunk * @param {MapChunkView} chunk
*/ */
drawChunk(parameters, chunk) { drawChunk(parameters, chunk) {
if (this.root.app.settings.getAllSettings().simplifiedBelts) {
// Disabled in potato mode
return;
}
const contents = chunk.containedEntitiesByLayer.regular; const contents = chunk.containedEntitiesByLayer.regular;
for (let i = 0; i < contents.length; ++i) { for (let i = 0; i < contents.length; ++i) {

View File

@ -553,25 +553,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const bonusTimeToApply = Math.min(originalTime, processorComp.bonusTime); const bonusTimeToApply = Math.min(originalTime, processorComp.bonusTime);
const timeToProcess = originalTime - bonusTimeToApply; const timeToProcess = originalTime - bonusTimeToApply;
// Substract one tick because we already process it this frame
// if (processorComp.bonusTime > originalTime) {
// if (processorComp.type === enumItemProcessorTypes.reader) {
// console.log(
// "Bonus time",
// round4Digits(processorComp.bonusTime),
// "Original time",
// round4Digits(originalTime),
// "Overcomit by",
// round4Digits(processorComp.bonusTime - originalTime),
// "->",
// round4Digits(timeToProcess),
// "reduced by",
// round4Digits(bonusTimeToApply)
// );
// }
// }
processorComp.bonusTime -= bonusTimeToApply; processorComp.bonusTime -= bonusTimeToApply;
processorComp.ongoingCharges.push({ processorComp.ongoingCharges.push({
items: outItems, items: outItems,
remainingTime: timeToProcess, remainingTime: timeToProcess,

View File

@ -1,6 +1,6 @@
import { globalConfig } from "../../core/config"; import { globalConfig } from "../../core/config";
import { Loader } from "../../core/loader"; import { Loader } from "../../core/loader";
import { smoothPulse, round4Digits } from "../../core/utils"; import { smoothPulse } from "../../core/utils";
import { enumItemProcessorRequirements, enumItemProcessorTypes } from "../components/item_processor"; import { enumItemProcessorRequirements, enumItemProcessorTypes } from "../components/item_processor";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { GameSystem } from "../game_system"; import { GameSystem } from "../game_system";
@ -17,7 +17,6 @@ export class ItemProcessorOverlaysSystem extends GameSystem {
this.readerOverlaySprite = Loader.getSprite("sprites/misc/reader_overlay.png"); this.readerOverlaySprite = Loader.getSprite("sprites/misc/reader_overlay.png");
this.drawnUids = new Set(); this.drawnUids = new Set();
this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this); this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this);
} }
@ -40,7 +39,6 @@ export class ItemProcessorOverlaysSystem extends GameSystem {
} }
const requirement = processorComp.processingRequirement; const requirement = processorComp.processingRequirement;
if (!requirement && processorComp.type !== enumItemProcessorTypes.reader) { if (!requirement && processorComp.type !== enumItemProcessorTypes.reader) {
continue; continue;
} }

View File

@ -46,7 +46,6 @@ export class MinerSystem extends GameSystemWithFilter {
} }
// Check if miner is above an actual tile // Check if miner is above an actual tile
if (!minerComp.cachedMinedItem) { if (!minerComp.cachedMinedItem) {
const staticComp = entity.components.StaticMapEntity; const staticComp = entity.components.StaticMapEntity;
const tileBelow = this.root.map.getLowerLayerContentXY( const tileBelow = this.root.map.getLowerLayerContentXY(
@ -171,7 +170,7 @@ export class MinerSystem extends GameSystemWithFilter {
} }
// Draw the item background - this is to hide the ejected item animation from // Draw the item background - this is to hide the ejected item animation from
// the item ejecto // the item ejector
const padding = 3; const padding = 3;
const destX = staticComp.origin.x * globalConfig.tileSize + padding; const destX = staticComp.origin.x * globalConfig.tileSize + padding;

View File

@ -2,6 +2,8 @@ import { globalConfig } from "../../core/config";
import { Loader } from "../../core/loader"; import { Loader } from "../../core/loader";
import { createLogger } from "../../core/logging"; import { createLogger } from "../../core/logging";
import { Rectangle } from "../../core/rectangle"; import { Rectangle } from "../../core/rectangle";
import { StaleAreaDetector } from "../../core/stale_area_detector";
import { fastArrayDelete } from "../../core/utils";
import { import {
enumAngleToDirection, enumAngleToDirection,
enumDirection, enumDirection,
@ -12,7 +14,6 @@ import {
import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components/underground_belt"; import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components/underground_belt";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter"; import { GameSystemWithFilter } from "../game_system_with_filter";
import { fastArrayDelete } from "../../core/utils";
const logger = createLogger("tunnels"); const logger = createLogger("tunnels");
@ -29,41 +30,20 @@ export class UndergroundBeltSystem extends GameSystemWithFilter {
), ),
}; };
this.staleAreaWatcher = new StaleAreaDetector({
root: this.root,
name: "underground-belt",
recomputeMethod: this.recomputeArea.bind(this),
});
this.root.signals.entityManuallyPlaced.add(this.onEntityManuallyPlaced, this); this.root.signals.entityManuallyPlaced.add(this.onEntityManuallyPlaced, this);
/** // NOTICE: Once we remove a tunnel, we need to update the whole area to
* @type {Rectangle} // clear outdated handles
*/ this.staleAreaWatcher.recomputeOnComponentsChanged(
this.areaToRecompute = null; [UndergroundBeltComponent],
globalConfig.undergroundBeltMaxTilesByTier[globalConfig.undergroundBeltMaxTilesByTier.length - 1]
this.root.signals.entityAdded.add(this.onEntityChanged, this);
this.root.signals.entityDestroyed.add(this.onEntityChanged, this);
}
/**
* Called when an entity got added or removed
* @param {Entity} entity
*/
onEntityChanged(entity) {
if (!this.root.gameInitialized) {
return;
}
const undergroundComp = entity.components.UndergroundBelt;
if (!undergroundComp) {
return;
}
const affectedArea = entity.components.StaticMapEntity.getTileSpaceBounds().expandedInAllDirections(
globalConfig.undergroundBeltMaxTilesByTier[
globalConfig.undergroundBeltMaxTilesByTier.length - 1
] + 1
); );
if (this.areaToRecompute) {
this.areaToRecompute = this.areaToRecompute.getUnion(affectedArea);
} else {
this.areaToRecompute = affectedArea;
}
} }
/** /**
@ -91,7 +71,7 @@ export class UndergroundBeltSystem extends GameSystemWithFilter {
const range = globalConfig.undergroundBeltMaxTilesByTier[tier]; const range = globalConfig.undergroundBeltMaxTilesByTier[tier];
// FIND ENTRANCE // FIND ENTRANCE
// Search for the entrance which is furthes apart (this is why we can't reuse logic here) // Search for the entrance which is farthest apart (this is why we can't reuse logic here)
let matchingEntrance = null; let matchingEntrance = null;
for (let i = 0; i < range; ++i) { for (let i = 0; i < range; ++i) {
currentPos.addInplace(offset); currentPos.addInplace(offset);
@ -223,14 +203,9 @@ export class UndergroundBeltSystem extends GameSystemWithFilter {
/** /**
* Recomputes the cache in the given area, invalidating all entries there * Recomputes the cache in the given area, invalidating all entries there
* @param {Rectangle} area
*/ */
recomputeArea() { recomputeArea(area) {
const area = this.areaToRecompute;
logger.log("Recomputing area:", area.x, area.y, "/", area.w, area.h);
if (G_IS_DEV && globalConfig.debug.renderChanges) {
this.root.hud.parts.changesDebugger.renderChange("tunnels", this.areaToRecompute, "#fc03be");
}
for (let x = area.x; x < area.right(); ++x) { for (let x = area.x; x < area.right(); ++x) {
for (let y = area.y; y < area.bottom(); ++y) { for (let y = area.y; y < area.bottom(); ++y) {
const entities = this.root.map.getLayersContentsMultipleXY(x, y); const entities = this.root.map.getLayersContentsMultipleXY(x, y);
@ -240,7 +215,6 @@ export class UndergroundBeltSystem extends GameSystemWithFilter {
if (!undergroundComp) { if (!undergroundComp) {
continue; continue;
} }
undergroundComp.cachedLinkedEntity = null; undergroundComp.cachedLinkedEntity = null;
} }
} }
@ -248,10 +222,7 @@ export class UndergroundBeltSystem extends GameSystemWithFilter {
} }
update() { update() {
if (this.areaToRecompute) { this.staleAreaWatcher.update();
this.recomputeArea();
this.areaToRecompute = null;
}
const delta = this.root.dynamicTickrate.deltaSeconds; const delta = this.root.dynamicTickrate.deltaSeconds;
@ -334,21 +305,13 @@ export class UndergroundBeltSystem extends GameSystemWithFilter {
const undergroundComp = entity.components.UndergroundBelt; const undergroundComp = entity.components.UndergroundBelt;
// Find the current receiver // Find the current receiver
let receiver = undergroundComp.cachedLinkedEntity; let cacheEntry = undergroundComp.cachedLinkedEntity;
if (!receiver) { if (!cacheEntry) {
// We don't have a receiver, compute it // Need to recompute cache
receiver = undergroundComp.cachedLinkedEntity = this.findRecieverForSender(entity); cacheEntry = undergroundComp.cachedLinkedEntity = this.findRecieverForSender(entity);
if (G_IS_DEV && globalConfig.debug.renderChanges) {
this.root.hud.parts.changesDebugger.renderChange(
"sender",
entity.components.StaticMapEntity.getTileSpaceBounds(),
"#fc03be"
);
}
} }
if (!receiver.entity) { if (!cacheEntry.entity) {
// If there is no connection to a receiver, ignore this one // If there is no connection to a receiver, ignore this one
return; return;
} }
@ -364,9 +327,9 @@ export class UndergroundBeltSystem extends GameSystemWithFilter {
if (remainingTime === 0) { if (remainingTime === 0) {
// Check if the receiver can accept it // Check if the receiver can accept it
if ( if (
receiver.entity.components.UndergroundBelt.tryAcceptTunneledItem( cacheEntry.entity.components.UndergroundBelt.tryAcceptTunneledItem(
nextItem, nextItem,
receiver.distance, cacheEntry.distance,
this.root.hubGoals.getUndergroundBeltBaseSpeed() this.root.hubGoals.getUndergroundBeltBaseSpeed()
) )
) { ) {

View File

@ -162,7 +162,7 @@ export class WireSystem extends GameSystemWithFilter {
const tunnelEntities = this.root.entityMgr.getAllWithComponent(WireTunnelComponent); const tunnelEntities = this.root.entityMgr.getAllWithComponent(WireTunnelComponent);
const pinEntities = this.root.entityMgr.getAllWithComponent(WiredPinsComponent); const pinEntities = this.root.entityMgr.getAllWithComponent(WiredPinsComponent);
// Clear all network references, but not on the first update since thats the deserializing one // Clear all network references, but not on the first update since that's the deserializing one
if (!this.isFirstRecompute) { if (!this.isFirstRecompute) {
for (let i = 0; i < wireEntities.length; ++i) { for (let i = 0; i < wireEntities.length; ++i) {
wireEntities[i].components.Wire.linkedNetwork = null; wireEntities[i].components.Wire.linkedNetwork = null;
@ -432,7 +432,7 @@ export class WireSystem extends GameSystemWithFilter {
continue; continue;
} }
// Check if its a tunnel, if so, go to the forwarded item // Check if it's a tunnel, if so, go to the forwarded item
const tunnelComp = entity.components.WireTunnel; const tunnelComp = entity.components.WireTunnel;
if (tunnelComp) { if (tunnelComp) {
if (visitedTunnels.has(entity.uid)) { if (visitedTunnels.has(entity.uid)) {

View File

@ -149,8 +149,6 @@ export class WiredPinsSystem extends GameSystemWithFilter {
} }
} }
update() {}
/** /**
* Draws a given entity * Draws a given entity
* @param {DrawParameters} parameters * @param {DrawParameters} parameters

View File

@ -276,6 +276,7 @@ export const allApplicationSettings = [
new BoolSetting("lowQualityMapResources", enumCategories.performance, (app, value) => {}), new BoolSetting("lowQualityMapResources", enumCategories.performance, (app, value) => {}),
new BoolSetting("disableTileGrid", enumCategories.performance, (app, value) => {}), new BoolSetting("disableTileGrid", enumCategories.performance, (app, value) => {}),
new BoolSetting("lowQualityTextures", enumCategories.performance, (app, value) => {}), new BoolSetting("lowQualityTextures", enumCategories.performance, (app, value) => {}),
new BoolSetting("simplifiedBelts", enumCategories.performance, (app, value) => {}),
]; ];
export function getApplicationSettingById(id) { export function getApplicationSettingById(id) {
@ -313,6 +314,7 @@ class SettingsStorage {
this.lowQualityMapResources = false; this.lowQualityMapResources = false;
this.disableTileGrid = false; this.disableTileGrid = false;
this.lowQualityTextures = false; this.lowQualityTextures = false;
this.simplifiedBelts = false;
/** /**
* @type {Object.<string, number>} * @type {Object.<string, number>}
@ -523,7 +525,7 @@ export class ApplicationSettings extends ReadWriteProxy {
} }
getCurrentVersion() { getCurrentVersion() {
return 26; return 27;
} }
/** @param {{settings: SettingsStorage, version: number}} data */ /** @param {{settings: SettingsStorage, version: number}} data */
@ -646,6 +648,11 @@ export class ApplicationSettings extends ReadWriteProxy {
data.version = 26; data.version = 26;
} }
if (data.version < 27) {
data.settings.simplifiedBelts = false;
data.version = 27;
}
return ExplainedResult.good(); return ExplainedResult.good();
} }
} }

View File

@ -41,7 +41,7 @@ export class SavegameSerializer {
beltPaths: root.systemMgr.systems.belt.serializePaths(), beltPaths: root.systemMgr.systems.belt.serializePaths(),
}; };
if (!G_IS_RELEASE) { if (G_IS_DEV) {
if (sanityChecks) { if (sanityChecks) {
// Sanity check // Sanity check
const sanity = this.verifyLogicalErrors(data); const sanity = this.verifyLogicalErrors(data);
@ -64,7 +64,7 @@ export class SavegameSerializer {
return ExplainedResult.bad("Savegame has no entities"); return ExplainedResult.bad("Savegame has no entities");
} }
const seenUids = []; const seenUids = new Set();
// Check for duplicate UIDS // Check for duplicate UIDS
for (let i = 0; i < savegame.entities.length; ++i) { for (let i = 0; i < savegame.entities.length; ++i) {
@ -75,10 +75,10 @@ export class SavegameSerializer {
if (!Number.isInteger(uid)) { if (!Number.isInteger(uid)) {
return ExplainedResult.bad("Entity has invalid uid: " + uid); return ExplainedResult.bad("Entity has invalid uid: " + uid);
} }
if (seenUids.indexOf(uid) >= 0) { if (seenUids.has(uid)) {
return ExplainedResult.bad("Duplicate uid " + uid); return ExplainedResult.bad("Duplicate uid " + uid);
} }
seenUids.push(uid); seenUids.add(uid);
// Verify components // Verify components
if (!entity.components) { if (!entity.components) {

View File

@ -64,9 +64,15 @@ export class InGameState extends GameState {
this.loadingOverlay = null; this.loadingOverlay = null;
/** @type {Savegame} */ /** @type {Savegame} */
this.savegame; this.savegame = null;
this.boundInputFilter = this.filterInput.bind(this); this.boundInputFilter = this.filterInput.bind(this);
/**
* Whether we are currently saving the game
* @TODO: This doesn't realy fit here
*/
this.currentSavePromise = null;
} }
/** /**
@ -427,12 +433,26 @@ export class InGameState extends GameState {
return Promise.resolve(); return Promise.resolve();
} }
// First update the game data if (this.currentSavePromise) {
logger.warn("Skipping double save and returning same promise");
return this.currentSavePromise;
}
logger.log("Starting to save game ..."); logger.log("Starting to save game ...");
this.core.root.signals.gameSaved.dispatch();
this.savegame.updateData(this.core.root); this.savegame.updateData(this.core.root);
return this.savegame.writeSavegameAndMetadata().catch(err => {
logger.warn("Failed to save:", err); this.currentSavePromise = this.savegame
}); .writeSavegameAndMetadata()
.catch(err => {
// Catch errors
logger.warn("Failed to save:", err);
})
.then(() => {
// Clear promise
logger.log("Saved!");
this.core.root.signals.gameSaved.dispatch();
this.currentSavePromise = null;
});
return this.currentSavePromise;
} }
} }

View File

@ -1,12 +1,13 @@
import { CHANGELOG } from "../changelog";
import { cachebust } from "../core/cachebust";
import { globalConfig } from "../core/config";
import { GameState } from "../core/game_state"; import { GameState } from "../core/game_state";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { findNiceValue } from "../core/utils"; import { findNiceValue } from "../core/utils";
import { cachebust } from "../core/cachebust"; import { getRandomHint } from "../game/hints";
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
import { T, autoDetectLanguageId, updateApplicationLanguage } from "../translations";
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
import { CHANGELOG } from "../changelog"; import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
import { globalConfig } from "../core/config"; import { autoDetectLanguageId, T, updateApplicationLanguage } from "../translations";
const logger = createLogger("state/preload"); const logger = createLogger("state/preload");
@ -24,8 +25,9 @@ export class PreloadState extends GameState {
<span class="inner" style="width: 0%"></span> <span class="inner" style="width: 0%"></span>
<span class="status">0%</span> <span class="status">0%</span>
</span> </span>
</div>
</div> </div>
</div> <span class="hint"></span>
`; `;
} }
@ -59,6 +61,11 @@ export class PreloadState extends GameState {
/** @type {HTMLElement} */ /** @type {HTMLElement} */
this.statusBarText = this.htmlElement.querySelector(".loadingStatus > .bar > .status"); this.statusBarText = this.htmlElement.querySelector(".loadingStatus > .bar > .status");
/** @type {HTMLElement} */
this.hintsText = this.htmlElement.querySelector(".hint");
this.lastHintShown = -1000;
this.nextHintDuration = 0;
this.currentStatus = "booting"; this.currentStatus = "booting";
this.currentIndex = 0; this.currentIndex = 0;
@ -216,6 +223,35 @@ export class PreloadState extends GameState {
); );
} }
update() {
const now = performance.now();
if (now - this.lastHintShown > this.nextHintDuration) {
this.lastHintShown = now;
const hintText = getRandomHint();
this.hintsText.innerHTML = hintText;
/**
* Compute how long the user will need to read the hint.
* We calculate with 130 words per minute, with an average of 5 chars
* that is 650 characters / minute
*/
this.nextHintDuration = Math.max(2500, (hintText.length / 650) * 60 * 1000);
}
}
onRender() {
this.update();
}
onBackgroundTick() {
this.update();
}
/**
*
* @param {string} text
*/
setStatus(text) { setStatus(text) {
logger.log("✅ " + text); logger.log("✅ " + text);
this.currentIndex += 1; this.currentIndex += 1;
@ -269,10 +305,12 @@ export class PreloadState extends GameState {
const resetBtn = subElement.querySelector("button.resetApp"); const resetBtn = subElement.querySelector("button.resetApp");
this.trackClicks(resetBtn, this.showResetConfirm); this.trackClicks(resetBtn, this.showResetConfirm);
this.hintsText.remove();
} }
showResetConfirm() { showResetConfirm() {
if (confirm("Are you sure you want to reset the app? This will delete all your savegames")) { if (confirm("Are you sure you want to reset the app? This will delete all your savegames!")) {
this.resetApp(); this.resetApp();
} }
} }

View File

@ -38,7 +38,7 @@ The base language is English and can be found [here](base-en.yaml).
## Editing existing translations ## Editing existing translations
If you want to edit an existing translation (Fixing typos, Updating it to a newer version, etc), you can just use the github file editor to edit the file. If you want to edit an existing translation (Fixing typos, updating it to a newer version, etc), you can just use the github file editor to edit the file.
- Click the language you want to edit from the list above - Click the language you want to edit from the list above
- Click the small "edit" symbol on the top right - Click the small "edit" symbol on the top right
@ -61,6 +61,8 @@ If you want to edit an existing translation (Fixing typos, Updating it to a newe
Please DM me on Discord (tobspr#5407), so I can add the language template for you. Please DM me on Discord (tobspr#5407), so I can add the language template for you.
**Important: I am currently not accepting new languages until the wires update is out!**
Please use the following template: Please use the following template:
``` ```
@ -77,4 +79,4 @@ PS: I'm super busy, but I'll give my best to do it quickly!
## Updating a language to the latest version ## Updating a language to the latest version
Run `yarn syncTranslations` in the root directory to synchronize all translations to the latest version! This will remove obsolete keys and add newly added keys. (Run `yarn` before to install packes). Run `yarn syncTranslations` in the root directory to synchronize all translations to the latest version! This will remove obsolete keys and add newly added keys. (Run `yarn` before to install packages).

View File

@ -66,7 +66,7 @@ steamPage:
[*] Verschiedene Karten und Herausforderungen (z.B. Karten mit Hindernissen) [*] Verschiedene Karten und Herausforderungen (z.B. Karten mit Hindernissen)
[*] Puzzle (Liefere die geforderte Form mit begrenztem Platz/limitierten Gebäuden) [*] Puzzle (Liefere die geforderte Form mit begrenztem Platz/limitierten Gebäuden)
[*] Eine Kampagne mit Gebäudekosten [*] Eine Kampagne mit Gebäudekosten
[*] Konfigurierbarer Kartengenerator (Ändere die Grösse/Anzahl/Dichte der Ressourcenflecken, den Seed und viel mehr) [*] Konfigurierbarer Kartengenerator (Ändere die Grösse/Anzahl/Dichte der Ressourcenflecken, den Seed und vieles mehr)
[*] Mehr Formentypen [*] Mehr Formentypen
[*] Performanceverbesserungen (Das Spiel läuft bereits sehr gut!) [*] Performanceverbesserungen (Das Spiel läuft bereits sehr gut!)
[*] Und vieles mehr! [*] Und vieles mehr!
@ -256,7 +256,7 @@ dialogs:
title: Nützliche Hotkeys title: Nützliche Hotkeys
desc: >- desc: >-
Dieses Spiel hat viele Hotkeys, die den Bau von Fabriken vereinfachen und beschleunigen. Dieses Spiel hat viele Hotkeys, die den Bau von Fabriken vereinfachen und beschleunigen.
Hier sind ein paar, aber prüfe am besten die <strong>Tastenbelegung-Einstellungen</strong>!<br><br> Hier sind ein paar Beispiele, aber prüfe am besten die <strong>Tastenbelegung-Einstellungen</strong>!<br><br>
<code class='keybinding'>STRG</code> + Ziehen: Wähle Areal aus.<br> <code class='keybinding'>STRG</code> + Ziehen: Wähle Areal 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 Förderbänder.<br> <code class='keybinding'>ALT</code>: Invertiere die Platzierungsrichtung der Förderbänder.<br>
@ -635,7 +635,7 @@ storyRewards:
no_reward: no_reward:
title: Nächstes Level title: Nächstes Level
desc: >- desc: >-
Dieses Level hat dir keine Belohnung gegeben, aber dafür das Nächste schon! <br><br> PS: Denke daran, deine alten Fabriken nicht zu zerstören - Du wirst sie später <strong>alle</strong> noch brauchen, um <strong>Upgrades freizuschalten</strong>! Dieses Level hat dir keine Belohnung gegeben, aber im Nächsten gibt es eine! <br><br> PS: Denke daran, deine alten Fabriken nicht zu zerstören - Du wirst sie später <strong>alle</strong> noch brauchen, um <strong>Upgrades freizuschalten</strong>!
no_reward_freeplay: no_reward_freeplay:
title: Nächstes Level title: Nächstes Level
@ -694,7 +694,7 @@ settings:
movementSpeed: movementSpeed:
title: Bewegungsgeschwindigkeit title: Bewegungsgeschwindigkeit
description: >- description: >-
Ändert die Geschwindigkeit, mit der der Bildschirm durch die Pfeiltasten bewegt wird. Ändert die Geschwindigkeit, mit welcher der Bildschirm durch die Pfeiltasten bewegt wird.
speeds: speeds:
super_slow: Sehr langsam super_slow: Sehr langsam
slow: Langsam slow: Langsam
@ -711,7 +711,7 @@ settings:
enableColorBlindHelper: enableColorBlindHelper:
title: Modus für Farbenblinde title: Modus für Farbenblinde
description: >- description: >-
Aktiviert verschiedene Werkzeuge, die dir das Spielen trotz Farbenblindheit ermöglichen. Aktiviert verschiedene Werkzeuge, welche dir das Spielen trotz Farbenblindheit ermöglichen.
fullscreen: fullscreen:
title: Vollbild title: Vollbild
@ -775,7 +775,7 @@ settings:
rotationByBuilding: rotationByBuilding:
title: Rotation pro Gebäudetyp title: Rotation pro Gebäudetyp
description: >- description: >-
Jeder Gebäudetyp merkt sich einzeln, in welche Richtung er zeigt. Jeder Gebäudetyp merkt sich eigenständig, in welche Richtung er zeigt.
Das fühlt sich möglicherweise besser an, wenn du häufig zwischen verschiedenen Gebäudetypen wechselst. Das fühlt sich möglicherweise besser an, wenn du häufig zwischen verschiedenen Gebäudetypen wechselst.
compactBuildingInfo: compactBuildingInfo:
@ -786,7 +786,7 @@ settings:
disableCutDeleteWarnings: disableCutDeleteWarnings:
title: Deaktiviere Warnungsdialog beim Löschen title: Deaktiviere Warnungsdialog beim Löschen
description: >- description: >-
Deaktiviert die Warnung, die beim Löschen und Ausschneiden von mehr als 100 Feldern angezeigt wird. Deaktiviert die Warnung, welche beim Löschen und Ausschneiden von mehr als 100 Feldern angezeigt wird.
keybindings: keybindings:
title: Tastenbelegung title: Tastenbelegung

View File

@ -898,6 +898,11 @@ settings:
description: >- description: >-
Enabled by default, selects the miner if you use the pipette when hovering a resource patch. Enabled by default, selects the miner if you use the pipette when hovering a resource patch.
simplifiedBelts:
title: Simplified Belts (Ugly)
description: >-
Does not render belt items except when hovering the belt, to save performance.
keybindings: keybindings:
title: Keybindings title: Keybindings
hint: >- hint: >-
@ -1004,3 +1009,53 @@ demo:
exportingBase: Exporting whole Base as Image exportingBase: Exporting whole Base as Image
settingNotAvailable: Not available in the demo. settingNotAvailable: Not available in the demo.
tips:
- The hub accepts input of any kind, not just the current shape!
- Make sure your factories are stackable - it will pay out!
- Don't build too close to the hub, or it will be a huge chaos!
- If stacking does not work, try switching the inputs.
- You can toggle the belt planner direction by pressing <b>R</b>.
- Holding <b>CTRL</b> allows dragging of belts without auto-orientation.
- Ratios stay the same, as long as all upgrades are on the same Tier.
- Serial execution is more efficient than parallel.
- You will unlock more variants of buildings later in the game!
- You can use <b>T</b> to switch between different variants.
- Symmetry is key!
- You can weave different tiers of tunnels.
- Try to build compact factories - it will pay out!
- The painter has a mirrored variant which you can select with <b>T</b>
- Having the right building ratios will maximize efficiency.
- At maximum level, 5 extractors will fill a single belt.
- Don't forget about tunnels!
- You don't need to divide up items evenly for full efficiency.
- Holding <b>SHIFT</b> will activate the belt planner, letting you place long lines of belts easily.
- Cutters always cut vertically, regardless of their orientation.
- To get white mix all three colors.
- The storage buffer priorities the first output.
- Invest time to build repeatable designs - it's worth it!
- Holding <b>CTRL</b> allows to place multiple buildings.
- You can hold <b>ALT</b> to invert the direction of placed belts.
- Efficiency is key!
- Shape patches that are further away from the hub are more complex.
- Machines have a limited speed, divide them up for maximum efficiency.
- Use balancers to maximize your efficiency.
- Organization is important. Try not to cross conveyors too much.
- Plan in advance, or it will be a huge chaos!
- Don't remove your old factories! You'll need them to unlock upgrades.
- Try beating level 18 on your own before seeking for help!
- Don't complicate things, try to stay simple and you'll go far.
- You may need to re-use factories later in the game. Plan your factories to be re-usable.
- Sometimes, you can find a needed shape in the map without creating it with stackers.
- Full windmills / pinwheels can never spawn naturally.
- Color your shapes before cutting for maximum efficiency.
- With modules, space is merely a perception; a concern for mortal men.
- Make a separate blueprint factory. They're important for modules.
- Have a closer look on the color mixer, and your questions will be answered.
- Use <b>CTRL</b> + Click to select an area.
- Building too close to the hub can get in the way of later projects.
- The pin icon next to each shape in the upgrade list pins it to the screen.
- Mix all primary colours together to make white!
- You have an infinite map, don't cramp your factory, expand!
- Also try Factorio! It's my favourite game.
- The quad cutter cuts clockwise starting from the top right!

View File

@ -168,7 +168,7 @@ dialogs:
deleteGame: Tiedän mitä olen tekemässä deleteGame: Tiedän mitä olen tekemässä
viewUpdate: Näytä päivitys viewUpdate: Näytä päivitys
showUpgrades: Näytä Päivitykset showUpgrades: Näytä Päivitykset
showKeybindings: Show Keybindings showKeybindings: Näytä pikanäppäimet
importSavegameError: importSavegameError:
title: Tuonti Virhe title: Tuonti Virhe
@ -258,7 +258,7 @@ dialogs:
createMarker: createMarker:
title: Uusi Merkki title: Uusi Merkki
desc: Anna merkille kuvaava nimi, voit myös sisällyttää muodon <strong>lyhyen avaimen</strong> siihen. (Lyhyen avaimen voit luoda <a href="https://viewer.shapez.io" target="_blank">täällä</a>) desc: Anna merkille kuvaava nimi, voit myös sisällyttää muodon <strong>lyhyen avaimen</strong> siihen. (Lyhyen avaimen voit luoda <a href="https://viewer.shapez.io" target="_blank">täällä</a>)
titleEdit: Edit Marker titleEdit: Muokkaa merkkiä
markerDemoLimit: markerDemoLimit:
desc: Voit tehdä vain kaksi mukautettua merkkiä demoversiossa. Hanki itsenäinen versio saadaksesi loputtoman määrän merkkejä! desc: Voit tehdä vain kaksi mukautettua merkkiä demoversiossa. Hanki itsenäinen versio saadaksesi loputtoman määrän merkkejä!
@ -267,8 +267,8 @@ dialogs:
title: Vie kuvakaappaus title: Vie kuvakaappaus
desc: Pyysit tukikohtasi viemistä kuvakaappauksena. Huomaa, että tämä voi olla melko hidasta isolla tukikohdalla ja voi jopa kaataa pelisi! desc: Pyysit tukikohtasi viemistä kuvakaappauksena. Huomaa, että tämä voi olla melko hidasta isolla tukikohdalla ja voi jopa kaataa pelisi!
massCutInsufficientConfirm: massCutInsufficientConfirm:
title: Confirm cut title: Vahvista leikkaus
desc: You can not afford to paste this area! Are you sure you want to cut it? desc: Sinulla ei ole varaa leikata tätä aluetta! Oletko varma että haluat leikata sen?
ingame: ingame:
# This is shown in the top left corner and displays useful keybindings in # This is shown in the top left corner and displays useful keybindings in
@ -303,8 +303,8 @@ ingame:
purple: Violetti purple: Violetti
cyan: Syaani cyan: Syaani
white: Valkoinen white: Valkoinen
uncolored: Ei Väriä uncolored: Väritön
black: Black black: Musta
# Everything related to placing buildings (I.e. as soon as you selected a building # Everything related to placing buildings (I.e. as soon as you selected a building
# from the toolbar) # from the toolbar)
@ -465,7 +465,7 @@ buildings:
tier2: tier2:
name: Tunneli Taso II name: Tunneli Taso II
description: Sallii resurssien kuljetuksen rakennuksien ja hihnojen alta. description: Sallii resurssien kuljetuksen rakennuksien ja hihnojen alta pidemmältä kantamalta.
splitter: # Internal name for the Balancer splitter: # Internal name for the Balancer
default: default:
@ -498,11 +498,11 @@ buildings:
name: &rotater Kääntäjä name: &rotater Kääntäjä
description: Kääntää muotoja 90 astetta myötäpäivään. description: Kääntää muotoja 90 astetta myötäpäivään.
ccw: ccw:
name: Rotate (Vastapäivään) name: Kääntäjä (Vastapäivään)
description: Kääntää muotoja 90 astetta vastapäivään. description: Kääntää muotoja 90 astetta vastapäivään.
fl: fl:
name: Rotate (180) name: Kääntäkä (180)
description: Rotates shapes by 180 degrees. description: Kääntää muotoja 180 astetta.
stacker: stacker:
default: default:
@ -550,11 +550,11 @@ buildings:
description: Tuottaa sähköä kuluttamalla muotoja. Jokainen sähkögeneraattori vaatii eri muotoja. description: Tuottaa sähköä kuluttamalla muotoja. Jokainen sähkögeneraattori vaatii eri muotoja.
wire_crossings: wire_crossings:
default: default:
name: Wire Splitter name: Johdon jakaja
description: Splits a energy wire into two. description: Jakaa energiajohdon kahteen.
merger: merger:
name: Wire Merger name: Johtojen yhdistäjä
description: Merges two energy wires into one. description: Yhdistää kaksi energiajohtoa yhteen.
storyRewards: storyRewards:
# Those are the rewards gained from completing the store # Those are the rewards gained from completing the store
@ -642,9 +642,9 @@ storyRewards:
settings: settings:
title: Asetukset title: Asetukset
categories: categories:
general: General general: Yleinen
userInterface: User Interface userInterface: Käyttöliittyma
advanced: Advanced advanced: Kehittynyt
versionBadges: versionBadges:
dev: Kehitys dev: Kehitys
@ -736,17 +736,17 @@ settings:
refreshRate: refreshRate:
title: Simulaatiotavoite title: Simulaatiotavoite
description: >- description: >-
Jos sinulla on 144hz näyttö, muuta virkistystaajuus täällä jotta pelin simulaatio toimii oikein isommilla virkistystaajuuksilla. Tämä voi laskea FPS nopeutta jos tietokoneesi on liian hidas. Jos sinulla on 144hz näyttö, muuta virkistystaajuus täällä jotta pelin simulaatio toimii oikein isommilla virkistystaajuuksilla. Tämä voi laskea FPS nopeutta, jos tietokoneesi on liian hidas.
alwaysMultiplace: alwaysMultiplace:
title: Monisijoitus title: Monisijoitus
description: >- description: >-
Jos käytössä, kaikki rakennukset pysyvät valittuina sijoittamisen jälkeen kunnes peruutat sen. Tämä vastaa SHIFT:in pitämistö pohjassa ikuisesti. Jos käytössä, kaikki rakennukset pysyvät valittuina sijoittamisen jälkeen kunnes peruutat sen. Tämä vastaa SHIFT:in pitämistä pohjassa ikuisesti.
offerHints: offerHints:
title: Vihjeet & Oppaat title: Vihjeet & Oppaat
description: >- description: >-
Tarjotaanko pelaamisen aikana vihjeitä ja oppaita. Myös piilottaa tietyt käyttöliittymäelementit tietyn tason mukaan, jotta alkuunpääseminen olisi helpompaa. Tarjoaa pelaamisen aikana vihjeitä ja oppaita. Myös piilottaa tietyt käyttöliittymäelementit tietyn tason mukaan, jotta alkuunpääseminen olisi helpompaa.
enableTunnelSmartplace: enableTunnelSmartplace:
title: Älykkäät Tunnelit title: Älykkäät Tunnelit
@ -771,12 +771,12 @@ settings:
disableCutDeleteWarnings: disableCutDeleteWarnings:
title: Poista Leikkaus/Poisto Varoitukset title: Poista Leikkaus/Poisto Varoitukset
description: >- description: >-
Poista varoitusikkunat dialogs brought up when cutting/deleting more than 100 entities. Poista varoitusikkunat jotka ilmestyy kun leikkaat/poistat enemmän kuin 100 entiteettiä
keybindings: keybindings:
title: Pikanäppäimet title: Pikanäppäimet
hint: >- hint: >-
Tip: Muista käyttää CTRL, VAIHTO and ALT! Ne ottavat käyttöön erilaisia sijoitteluvaihtoehtoja. Tip: Muista käyttää CTRL, VAIHTO ja ALT! Ne ottavat käyttöön erilaisia sijoitteluvaihtoehtoja.
resetKeybindings: Nollaa Pikanäppäimet resetKeybindings: Nollaa Pikanäppäimet
@ -843,13 +843,13 @@ keybindings:
placementDisableAutoOrientation: Poista automaattinen suunta käytöstä placementDisableAutoOrientation: Poista automaattinen suunta käytöstä
placeMultiple: Pysy sijoittamistilassa placeMultiple: Pysy sijoittamistilassa
placeInverse: Käännä automaattinen hihnan suunta placeInverse: Käännä automaattinen hihnan suunta päinvastoin
menuClose: Close Menu menuClose: Sulje valikko
about: about:
title: Tietoja tästä pelistä title: Tietoja tästä pelistä
body: >- body: >-
Tämä peli on avoimen lähdekoodin ja kehitettä on <a href="https://github.com/tobspr" target="_blank">Tobias Springer</a> (tämä on minä).<br><br> Tämä peli on avointa lähdekoodia ja kehittäjä on <a href="https://github.com/tobspr" target="_blank">Tobias Springer</a> (tämä on minä).<br><br>
Jos haluat osallistua, tarkista <a href="<githublink>" target="_blank">shapez.io githubissa</a>.<br><br> Jos haluat osallistua, tarkista <a href="<githublink>" target="_blank">shapez.io githubissa</a>.<br><br>

View File

@ -32,15 +32,15 @@ steamPage:
[img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img] [img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img]
shapez.io est un jeu dans lequel vous devrez construire des usines pour automatiser la création et la combinaison de formes de plus en plus complexes sur une carte infinie. shapez.io est un jeu dans lequel vous devrez construire des usines pour automatiser la création et la combinaison de formes de plus en plus complexes sur une carte infinie.
Lors de la livraison des formes requises vous progresserez et débloquerez des améliorations pour accélerer votre usine. En livrant les formes requises, vous progresserez et débloquerez des améliorations pour accélérer votre usine.
Au vu de l'augmentation des demandes de formes, vous devrez agrandir votre usine pour répondre à la forte demande - Mais n'oubliez pas les ressources, vous drevrez vous étendre au milieu de cette [b]carte infinie[/b] ! Vous devrez agrandir votre usine pour répondre à laugmentation de la demande en formes — Mais noubliez pas les ressources, vous devrez vous étendre au milieu de cette [b]carte infinie[/b]!
Bientôt vous devrez mixer les couleurs et peindre vos formes avec - Combinez les ressources de couleurs rouge, verte et bleue pour produire différentes couleurs et peindre les formes avec pour satisfaire la demande. Bientôt, vous devrez mélanger les couleurs et peindre vos formes avec — Combinez les ressources de couleurs rouge, verte et bleue pour produire différentes couleurs et peindre les formes avec pour satisfaire la demande.
Ce jeu propose 18 niveaux progressifs (qui devraient déjà vous occuper quelques heures !) mais j'ajoute constamment de nouveau contenus - Il y en a beaucoup de prévus ! Ce jeu propose 18 niveaux progressifs (qui devraient déjà vous occuper quelques heures!) mais je développe constamment plus de contenu — Il y a beaucoup de choses prévues!
Acheter le jeu vous donne accès à la version complète qui a des fonctionnalités additionnelles et vous recevrez aussi un accès à des fonctionnalités fraîchement développées. Acheter le jeu vous donne accès à la version complète qui a des fonctionnalités supplémentaires, et vous pourrez aussi accéder aux fonctionnalités fraîchement développées.
[b]Avantages de la version complète (standalone)[/b] [b]Avantages de la version complète (standalone)[/b]
@ -48,30 +48,30 @@ steamPage:
[*] Mode sombre [*] Mode sombre
[*] Balises infinies [*] Balises infinies
[*] Parties infinies [*] Parties infinies
[*] Plus d'options [*] Plus doptions
[*] Prochainement: Câbles et énergie ! Prévu pour (environ) fin Juillet 2020. [*] Prochainement : Câbles et énergie! Prévu pour (environ) fin juillet 2020.
[*] Prochainement: Plus de niveaux [*] Prochainement : Plus de niveaux
[*] Aidez moi à continuer de développer shapez.io ❤️ [*] Aidez-moi à continuer de développer shapez.io ❤️
[/list] [/list]
[b]Mises à jour futures[/b] [b]Mises à jour à venir[/b]
Je fais souvent des mises à jour et essaye d'en sortir une par semaine! Je fais souvent des mises à jour et jessaye den sortir une par semaine!
[list] [list]
[*] Différentes cartes et challenges (e.g. carte avec obstacles) [*] Différentes cartes et challenges (e.g. carte avec obstacles)
[*] Puzzles (Délivrer la forme requise avec une zone limitée/jeu de bâtiments) [*] Casse-tête (Livrer la forme requise avec une zone limitée/jeu de bâtiments)
[*] Un mode histoire où les bâtiments ont un coût [*] Un mode histoire où les bâtiments ont un coût
[*] Générateur de carte configurable (configuration des ressources/formes/taille/densitée, seed et plus) [*] Générateur de carte configurable (configuration des ressources/formes/taille/densité, graine aléatoire et plus)
[*] Plus de formes [*] Plus de niveaux
[*] Amélioration des performances (Le jeu tourne déjà plutot bien !) [*] Amélioration des performances (Le jeu tourne déjà plutôt bien!)
[*] Et bien plus ! [*] Et bien plus!
[/list] [/list]
[b]Ce jeu est open source ![/b] [b]Ce jeu est open source![/b]
Tout le monde peut contribuer, je suis très impliqué dans la communauté et j'essaye de répondre à toutes les suggestions et prendre en compte vos retours si possible. Tout le monde peut contribuer, je suis très impliqué dans la communauté et jessaye de répondre à toutes les suggestions et prendre en compte vos retours si possible.
Jetez un coup d'œil à mon Trello pour le suivi du projet et la planification du développement ! Jetez un coup dœil à mon Trello pour le suivi du projet et les plans de développement!
[b]Liens[/b] [b]Liens[/b]
[list] [list]
@ -82,19 +82,20 @@ steamPage:
[*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]Aidez à traduire[/url] [*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]Aidez à traduire[/url]
[/list] [/list]
discordLink: Discord officiel - Parlez avec moi! discordLink: Discord officiel — Parlez avec moi!
global: global:
loading: Chargement loading: Chargement
error: Erreur error: Erreur
# How big numbers are rendered, e.g. "10,000" # How big numbers are rendered, e.g. "10,000"
thousandsDivider: " " # En français, le séparateur des milliers est lespace (fine) insécable
thousandsDivider: ""
# What symbol to use to seperate the integer part from the fractional part of a number, e.g. "0.4" # What symbol to use to seperate the integer part from the fractional part of a number, e.g. "0.4"
decimalSeparator: "," decimalSeparator: ","
# The suffix for large numbers, e.g. 1.3k, 400.2M, etc. cf wikipedia système international d'unité # The suffix for large numbers, e.g. 1.3k, 400.2M, etc. cf wikipedia système international dunité
# For french: https://fr.wikipedia.org/wiki/Pr%C3%A9fixes_du_Syst%C3%A8me_international_d%27unit%C3%A9s # For french: https://fr.wikipedia.org/wiki/Pr%C3%A9fixes_du_Syst%C3%A8me_international_d%27unit%C3%A9s
suffix: suffix:
thousands: k thousands: k
@ -108,20 +109,20 @@ global:
time: time:
# Used for formatting past time dates # Used for formatting past time dates
oneSecondAgo: il y a une seconde oneSecondAgo: il y a une seconde
xSecondsAgo: il y a <x> secondes xSecondsAgo: il y a <x> secondes
oneMinuteAgo: il y a une minute oneMinuteAgo: il y a une minute
xMinutesAgo: il y a <x> minutes xMinutesAgo: il y a <x> minutes
oneHourAgo: il y a une heure oneHourAgo: il y a une heure
xHoursAgo: il y a <x> heures xHoursAgo: il y a <x> heures
oneDayAgo: il y a un jour oneDayAgo: il y a un jour
xDaysAgo: il y a <x> jours xDaysAgo: il y a <x> jours
# Short formats for times, e.g. '5h 23m' # Short formats for times, e.g. '5h 23m'
secondsShort: <seconds>s secondsShort: <seconds> s
minutesAndSecondsShort: <minutes>m <seconds>s minutesAndSecondsShort: <minutes> m <seconds> s
hoursAndMinutesShort: <hours>h <minutes>m hoursAndMinutesShort: <hours> h <minutes> m
xMinutes: <x> minutes xMinutes: <x> minutes
keys: keys:
tab: TAB tab: TAB
@ -135,21 +136,21 @@ demoBanners:
# This is the "advertisement" shown in the main menu and other various places # This is the "advertisement" shown in the main menu and other various places
title: Version démo title: Version démo
intro: >- intro: >-
Achetez la version complète pour débloquer toutes les fonctionnalités ! Achetez la version complète pour débloquer toutes les fonctionnalités!
mainMenu: mainMenu:
play: Jouer play: Jouer
changelog: Historique changelog: Historique
importSavegame: Importer importSavegame: Importer
openSourceHint: Ce jeu est open source ! openSourceHint: Ce jeu est open source!
discordLink: Serveur Discord officiel discordLink: Serveur Discord officiel
helpTranslate: Contribuez à la traduction ! helpTranslate: Contribuez à la traduction!
# This is shown when using firefox and other browsers which are not supported. # This is shown when using firefox and other browsers which are not supported.
browserWarning: >- browserWarning: >-
Désolé, ce jeu est connu pour tourner lentement sur votre navigateur web ! Procurez-vous la version complète ou téléchargez Chrome pour une meilleure expérience. Désolé, ce jeu sera lent sur votre navigateur web! Procurez-vous la version complète ou téléchargez Chrome pour une meilleure expérience.
savegameLevel: Niveau <x> savegameLevel: Niveau <x>
savegameLevelUnknown: Niveau inconnu savegameLevelUnknown: Niveau inconnu
continue: Continuer continue: Continuer
@ -165,16 +166,16 @@ dialogs:
later: Plus tard later: Plus tard
restart: Relancer restart: Relancer
reset: Réinitialiser reset: Réinitialiser
getStandalone: Se procurer la version complète getStandalone: Obtenir la version complète
deleteGame: Je sais ce que je fais deleteGame: Je sais ce que je fais
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
importSavegameError: importSavegameError:
title: Erreur d'importation title: Erreur dimportation
text: >- text: >-
Impossible d'importer votre sauvegarde: Impossible dimporter votre sauvegarde :
importSavegameSuccess: importSavegameSuccess:
title: Sauvegarde importée title: Sauvegarde importée
@ -184,17 +185,17 @@ dialogs:
gameLoadFailure: gameLoadFailure:
title: La sauvegarde est corrompue title: La sauvegarde est corrompue
text: >- text: >-
Impossible de charger votre sauvegarde: Impossible de charger votre sauvegarde :
confirmSavegameDelete: confirmSavegameDelete:
title: Confirmez la suppression title: Confirmez la suppression
text: >- text: >-
Êtes-vous certains de vouloir supprimer votre partie ? Êtes-vous sûr de vouloir supprimer votre partie?
savegameDeletionError: savegameDeletionError:
title: Impossible de supprimer title: Impossible de supprimer
text: >- text: >-
Impossible de supprimer votre sauvegarde: Impossible de supprimer votre sauvegarde :
restartRequired: restartRequired:
title: Redémarrage requis title: Redémarrage requis
@ -211,91 +212,91 @@ dialogs:
keybindingsResetOk: keybindingsResetOk:
title: Réinitialisation des contrôles title: Réinitialisation des contrôles
desc: Les contrôles ont été réinitialisés dans leur état par défaut respectifs ! desc: Les contrôles ont été remis à défaut!
featureRestriction: featureRestriction:
title: Version démo title: Version démo
desc: Vous avez essayé d'accéder à la fonction (<feature>) qui n'est pas disponible dans la démo. Considérez l'achat de la version complète pour une expérience optimale ! desc: Vous avez essayé daccéder à la fonction (<feature>) qui nest pas disponible dans la démo. Pensez à acheter la version complète pour une expérience optimale!
oneSavegameLimit: oneSavegameLimit:
title: Sauvegardes limitées title: Sauvegardes limitées
desc: Vous ne pouvez avoir qu'une seule sauvegarde en même temps dans la version démo. Merci d'effacer celle en cours ou alternativement de vous procurer la version complète ! desc: Vous ne pouvez avoir quune seule sauvegarde en même temps dans la version démo. Merci deffacer celle en cours ou bien de vous procurer la version complète!
updateSummary: updateSummary:
title: Nouvelle mise à jour ! title: Nouvelle mise à jour!
desc: >- desc: >-
Voici les modifications depuis votre dernière session: Voici les changements depuis votre dernière session :
upgradesIntroduction: upgradesIntroduction:
title: Débloquer les améliorations title: Débloquer les améliorations
desc: >- desc: >-
Toutes les formes que vous produisez peuvent être utilisées pour débloquer des améliorations - <strong>Ne détruisez pas vos anciennes usines !</strong> Toutes les formes que vous produisez peuvent être utilisées pour débloquer des améliorations — <strong>Ne détruisez pas vos anciennes usines!</strong>
L'onglet des améliorations se trouve dans le coin supérieur droit de l'écran. Longlet des améliorations se trouve dans le coin supérieur droit de lécran.
massDeleteConfirm: massDeleteConfirm:
title: Confirmation de suppression title: Confirmation de suppression
desc: >- desc: >-
Vous allez supprimer pas mal de bâtiments (<count> pour être exact) ! Êtes vous certains de vouloir faire ça ? Vous allez supprimer beaucoup de bâtiments (<count> pour être précis)! Êtes-vous sûr de vouloir faire ça?
massCutConfirm: massCutConfirm:
title: Confirmer la coupure title: Confirmer la coupure
desc: >- desc: >-
Vous vous apprêtez à couper beaucoup de bâtiments (<count> pour être précis) ! Êtes-vous certains de vouloir faire ça ? Vous allez couper beaucoup de bâtiments (<count> pour être précis)! Êtes-vous sûr de vouloir faire ça?
blueprintsNotUnlocked: blueprintsNotUnlocked:
title: Pas encore débloqué title: Pas encore débloqué
desc: >- desc: >-
Les patrons n'ont pas encore étés débloqués ! Terminez encore quelques niveaux pour y avoir accès. Les patrons nont pas encore été débloqués! Terminez le niveau 12 pour y avoir accès.
keybindingsIntroduction: keybindingsIntroduction:
title: Raccourcis utiles title: Raccourcis utiles
desc: >- desc: >-
Le jeu a de nombreux raccourcis facilitant la construction de grandes usines. Le jeu a de nombreux raccourcis facilitant la construction de grandes usines.
En voici quelques uns, n'hésitez pas à aller <strong>découvrir les raccourcis</strong> !<br><br> En voici quelques-uns, nhésitez pas à aller <strong>découvrir les raccourcis</strong>!<br><br>
<code class='keybinding'>CTRL</code> + Glisser: Sélectionne une zone à copier / effacer.<br> <code class='keybinding'>CTRL</code> + glisser : Sélectionne une zone à copier/effacer.<br>
<code class='keybinding'>SHIFT</code>: Laissez appuyé pour placer plusieurs fois le même bâtiment.<br> <code class='keybinding'>MAJ</code> : Laissez appuyé pour placer plusieurs fois le même bâtiment.<br>
<code class='keybinding'>ALT</code>: Inverse l'orientation des convoyeurs placés.<br> <code class='keybinding'>ALT</code> : Inverse lorientation des convoyeurs placés.<br>
createMarker: createMarker:
title: Nouvelle balise title: Nouvelle balise
desc: Donnez-lui un nom, vous pouvez aussi inclure <strong>le raccourci </strong> d'une forme (Que vous pouvez générer <a href="https://viewer.shapez.io" target="_blank">ici</a>) desc: Donnez-lui un nom, vous pouvez aussi inclure <strong>le raccourci</strong> dune forme (que vous pouvez générer <a href="https://viewer.shapez.io" target="_blank">ici</a>).
titleEdit: Éditer cette balise titleEdit: Modifier cette balise
markerDemoLimit: markerDemoLimit:
desc: Vous ne pouvez créer que deux balises dans la démo. Achetez la version complète pour en faire autant que vous voulez ! desc: Vous ne pouvez créer que deux balises dans la démo. Achetez la version complète pour en placer autant que vous voulez!
exportScreenshotWarning: exportScreenshotWarning:
title: Exporter une capture d'écran title: Exporter une capture décran
desc: >- desc: >-
Vous avez demandé à exporter votre base sous la forme d'une capture d'écran. Soyez conscient que cela peut s'avérer passablement lent pour une grande base, voire même faire planter votre jeu ! Vous avez demandé à exporter une capture décran de votre base. Soyez conscient que cela peut savérer passablement lent pour une grande base, voire faire planter votre jeu!
massCutInsufficientConfirm: massCutInsufficientConfirm:
title: Confirmer la coupe title: Confirmer la coupe
desc: Vous n'avez pas les moyens de copier cette zone ! Etes vous certain de vouloir la couper ? desc: Vous navez pas les moyens de copier cette zone! Êtes-vous sûr de vouloir la couper?
ingame: ingame:
# This is shown in the top left corner and displays useful keybindings in # This is shown in the top left corner and displays useful keybindings in
# every situation # every situation
keybindingsOverlay: keybindingsOverlay:
moveMap: Déplacer moveMap: Déplacer
selectBuildings: Sélection d'une zone selectBuildings: Sélection dune zone
stopPlacement: Arrêter le placement stopPlacement: Arrêter de placer
rotateBuilding: Tourner le bâtiment rotateBuilding: Tourner le bâtiment
placeMultiple: Placement multiple placeMultiple: Placement multiple
reverseOrientation: Changer l'orientation reverseOrientation: Changer lorientation
disableAutoOrientation: Désactiver l'orientation automatique disableAutoOrientation: Désactiver lorientation automatique
toggleHud: Basculer l'affichage tête haute (ATH) toggleHud: Basculer laffichage tête haute (ATH)
placeBuilding: Placer un bâtiment placeBuilding: Placer un bâtiment
createMarker: Créer une balise createMarker: Créer une balise
delete: Supprimer delete: Supprimer
pasteLastBlueprint: Copier le dernier patron pasteLastBlueprint: Copier le dernier patron
lockBeltDirection: Utiliser le plannificateur de convoyeurs lockBeltDirection: Utiliser le planificateur de convoyeurs
plannerSwitchSide: Échanger la direction du plannificateur plannerSwitchSide: Inverser la direction du planificateur
cutSelection: Couper cutSelection: Couper
copySelection: Copier copySelection: Copier
clearSelection: Effacer la sélection clearSelection: Effacer la sélection
pipette: Pipette pipette: Pipette
switchLayers: Échanger les calques switchLayers: Changer de calque
# Everything related to placing buildings (I.e. as soon as you selected a building # Everything related to placing buildings (I.e. as soon as you selected a building
# from the toolbar) # from the toolbar)
@ -306,29 +307,29 @@ ingame:
# Shows the hotkey in the ui, e.g. "Hotkey: Q" # Shows the hotkey in the ui, e.g. "Hotkey: Q"
hotkeyLabel: >- hotkeyLabel: >-
Raccourci: <key> Raccourci : <key>
infoTexts: infoTexts:
speed: Vitesse speed: Vitesse
range: Portée range: Portée
storage: Espace de stockage storage: Espace de stockage
oneItemPerSecond: 1 forme / s oneItemPerSecond: 1 forme  s
itemsPerSecond: <x> formes / s itemsPerSecond: <x> formes  s
itemsPerSecondDouble: (x2) itemsPerSecondDouble: (×2)
tiles: <x> cases tiles: <x> cases
# The notification when completing a level # The notification when completing a level
levelCompleteNotification: levelCompleteNotification:
# <level> is replaced by the actual level, so this gets 'Level 03' for example. # <level> is replaced by the actual level, so this gets 'Level 03' for example.
levelTitle: Niveau <level> levelTitle: Niveau <level>
completed: Terminé completed: Terminé
unlockText: <reward> débloqué ! unlockText: <reward> débloqué!
buttonNextLevel: Niveau suivant buttonNextLevel: Niveau suivant
# Notifications on the lower right # Notifications on the lower right
notifications: notifications:
newUpgrade: Une nouvelle amélioration est disponible ! newUpgrade: Une nouvelle amélioration est disponible!
gameSaved: Votre partie a été sauvegardée. gameSaved: Votre partie a été sauvegardée.
# The "Upgrades" window # The "Upgrades" window
@ -337,11 +338,11 @@ ingame:
buttonUnlock: Améliorer buttonUnlock: Améliorer
# Gets replaced to e.g. "Tier IX" # Gets replaced to e.g. "Tier IX"
tier: Niveau <x> tier: Niveau <x>
# The roman number for each tier # The roman number for each tier
tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X] tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X]
maximumLevel: NIVEAU MAXIMAL (Vitesse x<currentMult>) maximumLevel: NIVEAU MAXIMAL (Vitesse ×<currentMult>)
# The "Statistics" window # The "Statistics" window
statistics: statistics:
@ -352,14 +353,14 @@ ingame:
description: Affiche le nombre de formes stockées dans votre bâtiment central. description: Affiche le nombre de formes stockées dans votre bâtiment central.
produced: produced:
title: Produit title: Produit
description: Affiche tous les formes que votre usine produit, en incluant les formes intermédiaires. description: Affiche toutes les formes que votre usine produit, y compris les formes intermédiaires.
delivered: delivered:
title: Délivré title: Livré
description: Affiche les formes qui ont été livrées dans votre bâtiment central. description: Affiche les formes qui ont été livrées dans votre bâtiment central.
noShapesProduced: Aucune forme n'a été produite jusqu'à présent. noShapesProduced: Aucune forme produite pour le moment.
# Displays the shapes per minute, e.g. '523 / m' # Displays the shapes per minute, e.g. '523 / m'
shapesPerMinute: <shapes> / m shapesPerMinute: <shapes>  m
# Settings menu, when you press "ESC" # Settings menu, when you press "ESC"
settingsMenu: settingsMenu:
@ -375,7 +376,7 @@ ingame:
# Bottom left tutorial hints # Bottom left tutorial hints
tutorialHints: tutorialHints:
title: Besoin d'aide ? title: Besoin daide?
showHint: Indice showHint: Indice
hideHint: Fermer hideHint: Fermer
@ -387,19 +388,19 @@ ingame:
waypoints: waypoints:
waypoints: Balise waypoints: Balise
hub: Centre hub: Centre
description: Cliquez une balise pour vous y rendre, clic-droit pour l'effacer.<br><br>Appuyez sur <keybinding> pour créer une balise sur la vue actuelle, ou <strong>clic-droit</strong> pour en créer une sur l'endroit pointé. description: Cliquez sur une balise pour vous y rendre, clic-droit pour leffacer.<br><br>Appuyez sur <keybinding> pour créer une balise sur la vue actuelle, ou <strong>clic-droit</strong> pour en créer une sur lendroit pointé.
creationSuccessNotification: La balise a été créée. creationSuccessNotification: La balise a été créée.
# Interactive tutorial # Interactive tutorial
interactiveTutorial: interactiveTutorial:
title: Tutoriel title: Tutoriel
hints: hints:
1_1_extractor: Placez un <strong>extracteur</strong> sur une <strong>forme en cercle</strong> pour l'extraire ! 1_1_extractor: Placez un <strong>extracteur</strong> sur une <strong>forme en cercle</strong> pour lextraire!
1_2_conveyor: >- 1_2_conveyor: >-
Connectez l'extracteur avec un <strong>convoyeur</strong> vers votre centre !<br><br>Astuce: <strong>Cliquez et faites glisser</strong> le convoyeur avec votre souris ! Connectez lextracteur avec un <strong>convoyeur</strong> vers votre centre!<br><br>Astuce : <strong>Cliquez et faites glisser</strong> le convoyeur avec votre souris!
1_3_expand: >- 1_3_expand: >-
Ceci n'est <strong>PAS</strong> un jeu incrémental et inactif ! Construisez plus d'extracteurs et de convoyeurs pour atteindre plus vite votre votre but.<br><br>Astuce: Gardez <strong>MAJ</strong> enfoncé pour placer plusieurs extracteurs, et utilisez <strong>R</strong> pour les faire pivoter. Ceci nest <strong>PAS</strong> un jeu incrémental et inactif! Construisez plus dextracteurs et de convoyeurs pour atteindre plus vite votre but.<br><br>Astuce : Gardez <strong>MAJ</strong> enfoncé pour placer plusieurs extracteurs, et utilisez <strong>R</strong> pour les faire pivoter.
colors: colors:
red: Rouge red: Rouge
@ -409,92 +410,92 @@ ingame:
purple: Violet purple: Violet
cyan: Cyan cyan: Cyan
white: Blanc white: Blanc
uncolored: Non coloré uncolored: Sans couleur
black: Noir black: Noir
shapeViewer: shapeViewer:
title: Calques title: Calques
empty: Vide empty: Vide
copyKey: Copier la clé de forme copyKey: Copier le raccourci de la forme
# All shop upgrades # All shop upgrades
shopUpgrades: shopUpgrades:
belt: belt:
name: Convoyeurs, Distributeurs et Tunnels name: Convoyeurs, distributeurs et tunnels
description: Vitesse x<currentMult> → x<newMult> description: Vitesse ×<currentMult> → ×<newMult>
miner: miner:
name: Extraction name: Extraction
description: Vitesse x<currentMult> → x<newMult> description: Vitesse ×<currentMult> → ×<newMult>
processors: processors:
name: Découpage, Rotation et Empilage name: Découpage, rotation et empilage
description: Vitesse x<currentMult> → x<newMult> description: Vitesse ×<currentMult> → ×<newMult>
painting: painting:
name: Mélange et Peinture name: Mélange et peinture
description: Vitesse x<currentMult> → x<newMult> description: Vitesse ×<currentMult> → ×<newMult>
# Buildings and their name / description # Buildings and their name / description
buildings: buildings:
belt: belt:
default: default:
name: &belt Convoyeur name: &belt Convoyeur
description: Transporte les objects, maintenez et faites glisser pour en placer plusieurs. description: Transporte les objets, maintenez et faites glisser pour en placer plusieurs.
miner: # Internal name for the Extractor miner: # Internal name for the Extractor
default: default:
name: &miner Extracteur name: &miner Extracteur
description: Placez-le au dessus d'une forme ou couleur pour l'extraire. description: Placez-le au-dessus dune forme ou couleur pour lextraire.
chainable: chainable:
name: Extracteur en série name: Extracteur en série
description: Placez-le au dessus d'une forme ou couleur pour l'extraire. Peut être mis en série. description: Placez-le au-dessus dune forme ou couleur pour lextraire. Peut être mis en série.
underground_belt: # Internal name for the Tunnel underground_belt: # Internal name for the Tunnel
default: default:
name: &underground_belt Tunnel name: &underground_belt Tunnel
description: Permet de faire passer des ressources en dessous de bâtiment et de convoyeurs. description: Permet de faire passer des ressources sous les bâtiments et les convoyeurs.
tier2: tier2:
name: Tunnel Niveau II name: Tunnel niveau II
description: Permet de faire passer des ressources en dessous de bâtiment et de convoyeurs. description: Permet de faire passer des ressources sous les bâtiments et les convoyeurs.
splitter: # Internal name for the Balancer splitter: # Internal name for the Balancer
default: default:
name: &splitter Répartiteur name: &splitter Répartiteur
description: Multifonctionnel - Distribue de manière équitable toutes les entrées vers toutes les sorties. description: Multifonctions — Distribue équitablement toutes les entrées vers toutes les sorties.
compact: compact:
name: Fusionneur (compact) name: Fusionneur (compact)
description: Fusionne deux convoyeurs en un. description: Fusionne deux convoyeurs en un seul.
compact-inverse: compact-inverse:
name: Fusionneur (compact) name: Fusionneur (compact)
description: Fusionne deux convoyeurs en un. description: Fusionne deux convoyeurs en un seul.
cutter: cutter:
default: default:
name: &cutter Découpeur name: &cutter Découpeur
description: Coupe une forme de haut en bas et sort les deux parties. <strong>Si vous n'utilisez qu'une seule partie, assurez-vous de détruite l'autre ou sinon, gare au blocage !</strong> description: Coupe une forme de haut en bas et sort les deux parties. <strong>Si vous nutilisez quune seule partie, assurez-vous de détruire lautre ou sinon, gare au blocage!</strong>
quad: quad:
name: Découpeur (Quatre) name: Découpeur (quadruple)
description: Coupe une forme en quatre parties. <strong>Si vous n'utilisez pas toutes les parties, assurez-vous de détruite les autres ou sinon, gare au blocage !</strong> description: Coupe une forme en quatre parties. <strong>Si vous nutilisez pas toutes les parties, assurez-vous de détruire les autres ou sinon, gare au blocage!</strong>
rotater: rotater:
default: default:
name: &rotater Pivoteur name: &rotater Pivoteur
description: Fait pivoter une forme de 90 degrés vers la droite. description: Fait pivoter une forme de 90 degrés vers la droite.
ccw: ccw:
name: Pivoteur inversé name: Pivoteur inversé
description: Fait pivoter une forme de 90 degrés vers la gauche. description: Fait pivoter une forme de 90 degrés vers la gauche.
fl: fl:
name: Retourneur name: Retourneur
description: Tourne la forme de 180 degrés. description: Tourne une forme de 180 degrés.
stacker: stacker:
default: default:
name: &stacker Combineur name: &stacker Combineur
description: Combine deux formes. Si elles ne peuvent pas êtres combinées, la forme de droite est placée sur la forme de gauche. description: Combine deux formes. Si elles ne peuvent pas être combinées, la forme de droite est placée sur la forme de gauche.
mixer: mixer:
default: default:
@ -506,11 +507,11 @@ buildings:
name: &painter Peintre name: &painter Peintre
description: &painter_desc Colorie entièrement la forme de gauche avec la couleur de droite. description: &painter_desc Colorie entièrement la forme de gauche avec la couleur de droite.
double: double:
name: Peintre (Double) name: Peintre (double)
description: Colorie les deux formes de gauche avec la couleur de droite. description: Colorie les deux formes de gauche avec la couleur de droite.
quad: quad:
name: Peintre (Quadruple) name: Peintre (quadruple)
description: Permet de colorier chaque quadrant d'une forme avec une couleur différente. description: Colorie chaque quadrant dune forme avec une couleur différente.
mirrored: mirrored:
name: *painter name: *painter
description: *painter_desc description: *painter_desc
@ -518,125 +519,125 @@ buildings:
trash: trash:
default: default:
name: &trash Poubelle name: &trash Poubelle
description: Accepte des formes de n'importe quel côté et les détruit... pour toujours. description: Accepte des formes de nimporte quel côté et les détruit… pour toujours.
storage: storage:
name: Stockage name: Stockage
description: Stocke les formes en trop jusqu'à une certaine capacité. Peut être utilisé comme tampon. description: Stocke les formes en trop jusqu’à une certaine capacité. Peut être utilisé pour absorber un surplus.
hub: hub:
deliver: Délivrez deliver: Livrez
toUnlock: pour débloquer toUnlock: pour débloquer
levelShortcut: NV levelShortcut: NV
wire: wire:
default: default:
name: Ligne énergétique name: Câble
description: Permet de transporter de l'énergie. description: Permet de transporter de lénergie.
advanced_processor: advanced_processor:
default: default:
name: Inverseur de couleur name: Inverseur de couleur
description: Accepte une couleur ou une forme et l'inverse. description: Accepte une couleur ou une forme, et linverse.
energy_generator: energy_generator:
deliver: Délivrer deliver: Livrer
toGenerateEnergy: Pour toGenerateEnergy: Pour
default: default:
name: Générateur d'énergie name: Générateur dénergie
description: Genère de l'énergie en consommant des formes. description: Génère de lénergie en consommant des formes.
wire_crossings: wire_crossings:
default: default:
name: Duplicateur de ligne name: Duplicateur de câble
description: Sépare une ligne énergétique en deux. description: Sépare un câble en deux.
merger: merger:
name: Fusionneur de ligne name: Fusionneur de câble
description: Fusionne deux lignes énergétiques en une seule. description: Fusionne deux câbles en un seul.
storyRewards: storyRewards:
# Those are the rewards gained from completing the store # Those are the rewards gained from completing the store
reward_cutter_and_trash: reward_cutter_and_trash:
title: Découper des formes title: Découpage de formes
desc: Vous venez de débloquer le <strong>découpeur</strong> - il coupe des formes en deux <strong>de haut en bas</strong> quel que soit son orientation !<br><br>Assurez-vous de vous débarasser des déchets, sinon <strong>gare au blocage</strong> - À cet effet, je mets à votre disposition la poubelle, qui détruit tout ce que vous y mettez ! desc: Vous venez de débloquer le <strong>découpeur</strong> — il coupe des formes en deux <strong>de haut en bas</strong> quelle que soit son orientation!<br><br>Assurez-vous de vous débarrasser des déchets, sinon <strong>gare au blocage</strong> — À cet effet, je mets à votre disposition la poubelle, qui détruit tout ce que vous y mettez!
reward_rotater: reward_rotater:
title: Rotation title: Rotation
desc: Le <strong>pivoteur</strong> a été débloqué ! Il pivote les formes de 90 degrés vers la droite. desc: Le <strong>pivoteur</strong> a été débloqué! Il pivote les formes de 90 degrés vers la droite.
reward_painter: reward_painter:
title: Peintre title: Peintre
desc: >- desc: >-
Le <strong>peintre</strong> a été débloqué - Extrayez des pigments de couleur (comme vous le faites avec les formes) et combinez les avec une forme dans un peintre pour les colorier !<br><br>PS: Si vous êtes daltonien, il y a un <strong>mode daltonien</strong> paramétrable dans les préférences ! Le <strong>peintre</strong> a été débloqué — Extrayez des pigments de couleur (comme vous le faites avec les formes) et combinez-les avec une forme dans un peintre pour les colorier!<br><br>PS : Si vous êtes daltonien, il y a un <strong>mode daltonien</strong> paramétrable dans les préférences!
reward_mixer: reward_mixer:
title: Mélangeur de couleurs title: Mélangeur de couleurs
desc: Le <strong>mélangeur</strong> a été débloqué - Combinez deux couleurs en utilisant <strong>la synthèse additive des couleurs</strong> avec ce bâtiment ! desc: Le <strong>mélangeur</strong> a été débloqué Combinez deux couleurs en utilisant <strong>la synthèse additive des couleurs</strong> avec ce bâtiment!
reward_stacker: reward_stacker:
title: Combineur title: Combineur
desc: Vous pouvez maintenant combiner deux formes avec le <strong>combineur</strong> ! Les deux entrées sont combinées et si elles ne peuvent êtres mises l'une à côté de l'autre, elles sont <strong>fusionnées</strong>. Sinon, la forme de droite est <strong>placée au dessus</strong> de la forme de gauche après avoir été légèrement réduite. desc: Vous pouvez maintenant combiner deux formes avec le <strong>combineur</strong>! Les deux entrées sont combinées et si elles peuvent être mises lune à côté de lautre, elles sont <strong>fusionnées</strong>. Sinon, la forme de droite est <strong>placée au-dessus</strong> de la forme de gauche.
reward_splitter: reward_splitter:
title: Distributeur/Rassembleur title: Distributeur/rassembleur
desc: Le <strong>répartiteur</strong> multifonctionnel a été débloqué - Il peut être utilisé pour construire de plus grandes usines en <strong>distribuant équitablement et rassemblant les formes</strong> entre plusieurs convoyeurs !<br><br> desc: Le <strong>répartiteur</strong> multifonctionnel a été débloqué Il peut être utilisé pour construire de plus grandes usines en <strong>distribuant équitablement et rassemblant les formes</strong> entre plusieurs convoyeurs!<br><br>
reward_tunnel: reward_tunnel:
title: Tunnel title: Tunnel
desc: Le <strong>tunnel</strong> a été débloqué - À présent il devient possible de faire passer des formes sous les convoyeurs et les bâtiments ! desc: Le <strong>tunnel</strong> a été débloqué — Vous pouvez maintenant faire passer des formes sous les convoyeurs et les bâtiments!
reward_rotater_ccw: reward_rotater_ccw:
title: Pivoteur inversé title: Pivoteur inversé
desc: Vous avez débloqué une variante du <strong>pivoteur</strong> - Elle permet de faire pivoter vers la gauche ! Pour le construire, sélectionnez le pivoteur et <strong>appuyez sur 'T' pour alterner entre les variantes</strong> ! desc: Vous avez débloqué une variante du <strong>pivoteur</strong> — Elle permet de faire pivoter vers la gauche! Pour le construire, sélectionnez le pivoteur et <strong>appuyez sur 'T' pour alterner entre les variantes</strong>!
reward_miner_chainable: reward_miner_chainable:
title: Extracteur en série title: Extracteur en série
desc: Vous avez débloqué <strong>l'extracteur en série</strong> ! Il permet de <strong>transférer ses resources</strong> à d'autres extracteurs pour augmenter le débit sortant ! desc: Vous avez débloqué <strong>lextracteur en série</strong>! Il permet de <strong>transférer ses ressources</strong> à dautres extracteurs pour augmenter le débit sortant!
reward_underground_belt_tier_2: reward_underground_belt_tier_2:
title: Tunnel niveau II title: Tunnel niveau II
desc: Vous avez débloqué une nouvelle variante du <strong>tunnel</strong> - Elle a une <strong>portée plus grande</strong>, et vous pouvez à présent superposer les deux variantes de tunnels ! desc: Vous avez débloqué une nouvelle variante du <strong>tunnel</strong> — Elle a une <strong>portée plus grande</strong>, et vous pouvez superposer les deux variantes de tunnels!
reward_splitter_compact: reward_splitter_compact:
title: Répartiteur compact title: Répartiteur compact
desc: >- desc: >-
Vous avez débloqué une variante compacte du <strong>répartiteur</strong> - Elle accepte deux entrées et les rassemble en une sortie ! Vous avez débloqué une variante compacte du <strong>répartiteur</strong> — Elle accepte deux entrées et les rassemble en une sortie!
reward_cutter_quad: reward_cutter_quad:
title: Quadruple découpeur title: Quadruple découpeur
desc: Vous avez débloqué une variante du <strong>découpeur</strong> - Elle permet de découper les formes en <strong>quatre parties</strong> à la place de simplement deux ! desc: Vous avez débloqué une variante du <strong>découpeur</strong> Elle permet de découper les formes en <strong>quatre parties</strong> à la place de simplement deux!
reward_painter_double: reward_painter_double:
title: Double peintre title: Double peintre
desc: Vous avez débloqué une variante du <strong>peintre</strong> - Elle fonctionne comme le peintre de base, mais elle permet de traiter <strong>deux formes à la fois</strong> en ne consommant qu'une couleur au lieu de deux ! desc: Vous avez débloqué une variante du <strong>peintre</strong> Elle fonctionne comme le peintre de base, mais elle permet de traiter <strong>deux formes à la fois</strong> en ne consommant quune couleur au lieu de deux!
reward_painter_quad: reward_painter_quad:
title: Quadruple peintre title: Quadruple peintre
desc: Vous avez débloqué une variante du <strong>peintre</strong> - Elle permet de colorier chaque partie d'une forme individuellement ! desc: Vous avez débloqué une variante du <strong>peintre</strong> — Elle permet de colorier chaque partie dune forme individuellement!
reward_storage: reward_storage:
title: Tampon de stockage title: Tampon de stockage
desc: Vous avez débloqué une variante de <strong>la poubelle</strong> - Elle permet de stocker des formes jusqu'à une certaine limite ! desc: Vous avez débloqué une variante de <strong>la poubelle</strong> — Elle permet de stocker des formes jusquà une certaine limite!
reward_freeplay: reward_freeplay:
title: Mode libre title: Mode libre
desc: Vous y êtes arrivé ! Vous avez débloqué le <strong>mode libre</strong> ! Cela veut dire que dorénavant, les formes sont générées aléatoirement ! (Ne vous en faites pas, plus de contenu est prévu pour la version complète !) desc: Vous y êtes arrivé! Vous avez débloqué le <strong>mode libre</strong>! Cela veut dire que dorénavant, les formes sont générées aléatoirement! (Ne vous en faites pas, encore plus de contenu est prévu pour la version complète!)
reward_blueprints: reward_blueprints:
title: Patrons title: Patrons
desc: Vous pouvez maintenant <strong>copier et coller</strong> des parties de votre usines ! Sélectionnez une zone (Appuyez sur CTRL, et sélectionnez avec votre souris), et appuyez sur 'C' pour la copier.<br><br>Coller n'est <strong>pas gratuit</strong>, vous devez produire <strong>des formes de patrons</strong> pour vous le payer (les mêmes que celles que vous venez de livrer). desc: Vous pouvez maintenant <strong>copier et coller</strong> des parties de votre usine! Sélectionnez une zone (Appuyez sur CTRL, et sélectionnez avec votre souris), et appuyez sur 'C' pour la copier.<br><br>Coller nest <strong>pas gratuit</strong>, vous devez produire <strong>des formes de patrons</strong> pour vous le payer (les mêmes que celles que vous venez de livrer).
# Special reward, which is shown when there is no reward actually # Special reward, which is shown when there is no reward actually
no_reward: no_reward:
title: Niveau suivant title: Niveau suivant
desc: >- desc: >-
Ce niveau n'a pas de récompense mais le prochain, oui ! <br><br>PS: Vous ne devriez pas détruire votre usine actuelle - Vous aurez besoin de <strong>toutes</strong> ces formes plus tard pour <strong>débloquer des améliorations</strong> Ce niveau na pas de récompense mais le prochain, si!<br><br>PS : Ne détruisez pas votre usine actuelle — Vous aurez besoin de <strong>toutes</strong> ces formes plus tard pour <strong>débloquer des améliorations</strong>.
no_reward_freeplay: no_reward_freeplay:
title: Niveau suivant title: Niveau suivant
desc: >- desc: >-
Bravo ! À propos, plus de contenu est prévu pour la version complète ! Bravo! À propos, plus de contenu est prévu pour la version complète!
settings: settings:
title: Options title: Options
categories: categories:
general: Général general: Général
userInterface: Interface Utilisateur userInterface: Interface utilisateur
advanced: Avancé advanced: Avancé
performance: Performance performance: Performance
@ -648,26 +649,26 @@ settings:
labels: labels:
uiScale: uiScale:
title: Taille de l'interface title: Taille de linterface
description: >- description: >-
Change la taille de l'interface utilisateur. Cette interface se redimensionnera suivant la résolution de votre appareil, mais cette option contrôle le facteur de résolution. Change la taille de linterface utilisateur. Cette interface se redimensionnera suivant la résolution de votre écran, mais cette option contrôle le facteur de résolution.
scales: scales:
super_small: Très petite super_small: Très petite
small: Petite small: Petite
regular: Normale regular: Normale
large: Large large: Grande
huge: Très large huge: Très grande
scrollWheelSensitivity: scrollWheelSensitivity:
title: Sensibilité du zoom title: Sensibilité du zoom
description: >- description: >-
Change la sensibilité du zoom (aussi bien de la roulette de la souris que du pavé tactile). Change la sensibilité du zoom (roulette de la souris et pavé tactile).
sensitivity: sensitivity:
super_slow: Super lent super_slow: Très lent
slow: Lent slow: Lent
regular: Normal regular: Normal
fast: Rapide fast: Rapide
super_fast: Super rapide super_fast: Très rapide
fullscreen: fullscreen:
title: Plein écran title: Plein écran
@ -687,7 +688,7 @@ settings:
theme: theme:
title: Thème title: Thème
description: >- description: >-
Choisissez votre thème (clair / sombre). Choisissez votre thème (clair/sombre).
themes: themes:
dark: Sombre dark: Sombre
@ -701,7 +702,7 @@ settings:
alwaysMultiplace: alwaysMultiplace:
title: Placement multiple title: Placement multiple
description: >- description: >-
Si activé, tous les bâtiments resterons sélectionnés tant que vous n'aurez pas annulé. Ceci revient à garder la touche SHIFT appuyée en permanence. Si activé, tous les bâtiments resteront sélectionnés tant que vous naurez pas annulé. Ceci revient à garder la touche MAJ appuyée en permanence.
offerHints: offerHints:
title: Indices title: Indices
@ -711,13 +712,13 @@ settings:
language: language:
title: Langue title: Langue
description: >- description: >-
Change la langue. Toutes les traductions sont des contributions des utilisateurs et pourraient être partiellement incomplètes ! Change la langue. Les traductions sont une contribution des utilisateurs et peuvent être incomplètes!
movementSpeed: movementSpeed:
title: Vitesse de déplacement title: Vitesse de déplacement
description: Change la vitesse à laquelle l'écran se déplace lors de l'utilisation du clavier. description: Change la vitesse de déplacement de lécran avec les touches clavier.
speeds: speeds:
super_slow: Super lent super_slow: Très lent
slow: Lent slow: Lent
regular: Normal regular: Normal
fast: Rapide fast: Rapide
@ -728,75 +729,75 @@ settings:
title: Tunnels intelligents title: Tunnels intelligents
description: >- description: >-
Si cette option est sélectionnée, placer des tunnels effacera automatiquement les convoyeurs inutiles. Si cette option est sélectionnée, placer des tunnels effacera automatiquement les convoyeurs inutiles.
Cela permet aussi d'étirer les tunnels et les tunnels en surnombre seront effacés. Cela permet aussi détirer les tunnels, et les tunnels en surnombre seront effacés.
vignette: vignette:
title: Effet de vignette title: Effet de vignette
description: >- description: >-
Permet l'affichage de l'effet de vignette qui assombrit les coins de l'écran afin de rendre le texte plus facile à lire. Permet laffichage de leffet de vignette qui assombrit les coins de lécran afin de rendre le texte plus facile à lire.
autosaveInterval: autosaveInterval:
title: Fréquence des sauvegardes automatiques title: Fréquence des sauvegardes automatiques
description: >- description: >-
Contrôle avec quelle fréquence le jeu sera sauvegardé automatiquement. Vous pouvez aussi entièrement désactiver cette fonctionnalité ici. Contrôle avec quelle fréquence le jeu sera sauvegardé automatiquement. Vous pouvez aussi entièrement désactiver cette fonctionnalité ici.
intervals: intervals:
one_minute: 1 Minute one_minute: 1 minute
two_minutes: 2 Minutes two_minutes: 2 minutes
five_minutes: 5 Minutes five_minutes: 5 minutes
ten_minutes: 10 Minutes ten_minutes: 10 minutes
twenty_minutes: 20 Minutes twenty_minutes: 20 minutes
disabled: Désactivé disabled: Désactivé
compactBuildingInfo: compactBuildingInfo:
title: Informations réduites sur les bâtiments title: Informations réduites sur les bâtiments
description: >- description: >-
Raccourcit les panneaux d'information sur les bâtiments en n'affichant que les ratios. Dans le cas contraire, une description et une imagine sont présentés. Raccourcit les panneaux dinformation sur les bâtiments en naffichant que les ratios. Si désactivé, montre une description et une image.
disableCutDeleteWarnings: disableCutDeleteWarnings:
title: Désactive les avertissement pour Couper/Effacer title: Désactive les avertissements pour Couper/Effacer
description: >- description: >-
Désactive la boîte de dialogue qui s'affiche lorsque vous vous apprêtez à couper/effacer plus de 100 entités. Désactive la boîte de dialogue qui saffiche lorsque vous vous apprêtez à couper/effacer plus de 100 entités.
enableColorBlindHelper: enableColorBlindHelper:
title: Mode Daltonien title: Mode daltonien
description: Active divers outils qui permettent de jouer à ce jeu si vous êtes daltonien. description: Active divers outils qui permettent de jouer à ce jeu si vous êtes daltonien.
rotationByBuilding: rotationByBuilding:
title: Rotation par catégorie de bâtiment title: Rotation par catégorie de bâtiment
description: >- description: >-
Chaque catégorie de bâtiment enregistre le sens de rotation que vous lui avez assigné la dernière fois, de manière individuelle. Chaque catégorie de bâtiment enregistre le sens de rotation que vous lui avez assigné la dernière fois, de manière individuelle.
Cela sera sans doute plus confortable si vous alternez fréquemment entre le placement de différents types de bâtiments. Cela sera sans doute plus agréable si vous alternez fréquemment entre le placement de différents types de bâtiments.
lowQualityMapResources: lowQualityMapResources:
title: Ressources de la carte de plus basse qualité title: Ressources de la carte de plus basse qualité
description: >- description: >-
Simplifie le rendu des ressources sur la carte lorsqu'elle est zoomée opur améliorer les performances. Simplifie le rendu des ressources sur la carte lorsquelle est zoomée pour améliorer les performances.
C'est encore plus clean, n'oubliez pas d'essayer ! Ça donne un rendu encore plus propre, alors essayez-le!
disableTileGrid: disableTileGrid:
title: Desactiver la grille de placement title: Désactiver la grille de placement
description: >- description: >-
Desactiver la grille de placement peut aider les performances. Ça rend aussi le jeu encore plus uni! Désactiver la grille de placement peut améliorer les performances. Ça rend aussi lapparence plus unie!
clearCursorOnDeleteWhilePlacing: clearCursorOnDeleteWhilePlacing:
title: Effacer le curseur avec clic droit title: Déselectionner avec le clic droit
description: >- description: >-
Activé par défaut, efface le curseur lorsque vous faites un clic droit en ayant un bâtiment selectioné pour la constructio. Si desactivé, vous pouvez detruire les bâtiments en faisant un clic droit tout en placant un bâtiment. Activé par défaut. Désélectionne le bâtiment choisi pour la construction lorsque vous faites un clic droit sur un bâtiment existant. Si désactivé, vous pouvez détruire des bâtiments avec un clic droit puis continuer de placer le bâtiment sélectionné.
lowQualityTextures: lowQualityTextures:
title: Textures de basse résolution (Moche) title: Textures de basse résolution (moche)
description: >- description: >-
Utilise des textures de basse qualité pour augmenter les performances. Cela va rendre le jeu moche! Utilise des textures de basse qualité pour améliorer les performances. Rend le jeu très moche!
displayChunkBorders: displayChunkBorders:
title: Monter les bordures de chunks title: Monter les secteurs
description: >- description: >-
Le jeu est divisé en parties de 16x16 cases, si ce réglage est activé, les bordures de chaque partie sont affichées. Le jeu est divisé en secteurs de 16×16 cases. Si ce réglage est activé, les limites de chaque secteur sont affichées.
keybindings: keybindings:
title: Contrôles title: Contrôles
hint: >- hint: >-
Astuce: Soyez sûr d'utiliser CTRL, SHIFT et ALT ! Ces touches activent différentes options de placement. Astuce : Noubliez pas dutiliser CTRL, MAJ et ALT! Ces touches activent différentes options de placement.
resetKeybindings: Réinitialiser les contrôles resetKeybindings: Réinitialiser les contrôles
@ -805,7 +806,7 @@ keybindings:
ingame: Jeu ingame: Jeu
navigation: Navigation navigation: Navigation
placement: Placement placement: Placement
massSelect: Suppression de zone massSelect: Sélection dune zone
buildings: Raccourcis bâtiment buildings: Raccourcis bâtiment
placementModifiers: Modificateurs de placement placementModifiers: Modificateurs de placement
@ -825,8 +826,8 @@ keybindings:
menuOpenShop: Améliorations menuOpenShop: Améliorations
menuOpenStats: Statistiques menuOpenStats: Statistiques
toggleHud: Basculer l'affichage tête haute (ATH) toggleHud: Basculer laffichage tête haute (ATH)
toggleFPSInfo: Basculer l'affichage des IPS (itérations par seconde) et des informations de débogage toggleFPSInfo: Basculer laffichage des IPS (itérations par seconde) et des informations de débogage
belt: *belt belt: *belt
splitter: *splitter splitter: *splitter
underground_belt: *underground_belt underground_belt: *underground_belt
@ -840,49 +841,49 @@ keybindings:
rotateWhilePlacing: Pivoter rotateWhilePlacing: Pivoter
rotateInverseModifier: >- rotateInverseModifier: >-
Variante: Pivote à gauche Variante : Pivote à gauche
cycleBuildingVariants: Alterner entre les variantes cycleBuildingVariants: Alterner entre les variantes
confirmMassDelete: Confirmer la suppression de la sélection confirmMassDelete: Confirmer la suppression de la sélection
cycleBuildings: Alterner entre les bâtiments cycleBuildings: Alterner entre les bâtiments
massSelectStart: Cliquez et maintenez pour commencer massSelectStart: Cliquez et glissez pour commencer
massSelectSelectMultiple: Sélectionner plusieurs zones massSelectSelectMultiple: Sélectionner plusieurs zones
massSelectCopy: Copier la sélection massSelectCopy: Copier la sélection
placementDisableAutoOrientation: Désactiver l'orientation automatique placementDisableAutoOrientation: Désactiver lorientation automatique
placeMultiple: Rester en mode placement placeMultiple: Rester en mode placement
placeInverse: Inverser le mode d'orientation automatique placeInverse: Inverser le mode dorientation automatique
pasteLastBlueprint: Copier le dernier patron pasteLastBlueprint: Copier le dernier patron
massSelectCut: Couper la sélection massSelectCut: Couper la sélection
exportScreenshot: Exporter toute la base en tant qu'image. exportScreenshot: Exporter une image de toute la base
mapMoveFaster: Se déplacer plus vite mapMoveFaster: Se déplacer plus vite
lockBeltDirection: Utiliser le plannificateur de convoyeurs lockBeltDirection: Utiliser le planificateur de convoyeurs
switchDirectionLockSide: "Plannificateur: changer de côté" switchDirectionLockSide: "Planificateur : changer de côté"
pipette: Pipette pipette: Pipette
menuClose: Fermer le menu menuClose: Fermer le menu
switchLayers: Échanger les calques switchLayers: Basculer le calque
advanced_processor: Inverseur de couleur advanced_processor: Inverseur de couleur
energy_generator: Générateur d'énergie energy_generator: Générateur dénergie
wire: Ligne énergétique wire: Câble
about: about:
title: À propos de ce jeu title: À propos de ce jeu
body: >- body: >-
Ce jeu est open source et développé par <a href="https://github.com/tobspr" Ce jeu est open source et développé par <a href="https://github.com/tobspr"
target="_blank">Tobias Springer</a> (c'est moi).<br><br> target="_blank">Tobias Springer</a> (cest moi).<br><br>
Si vous souhaitez contribuer, allez voir <a href="<githublink>" Si vous souhaitez contribuer, allez voir <a href="<githublink>"
target="_blank">shapez.io sur github</a>.<br><br> target="_blank">shapez.io sur github</a>.<br><br>
Ce jeu n'aurait pu être réalisé sans la précieuse communauté Discord autour de Ce jeu naurait pas pu être réalisé sans la précieuse communauté Discord autour de
mes jeux - Vous devriez vraiment envisager de joindre le <a href="<discordlink>" mes jeux — Vous devriez vraiment rejoindre le <a href="<discordlink>"
target="_blank">serveur Discord</a> !<br><br> target="_blank">serveur Discord</a>!<br><br>
La bande son a été créée par <a href="https://soundcloud.com/pettersumelius" La bande son a été créée par <a href="https://soundcloud.com/pettersumelius"
target="_blank">Peppsen</a> - Il est impressionnant !<br><br> target="_blank">Peppsen</a> — Il est génial!<br><br>
Pour terminer, un immense merci à mon meilleur amis <a Pour terminer, un immense merci à mon meilleur ami <a
href="https://github.com/niklas-dahl" target="_blank">Niklas</a> - Sans nos sessions sur factorio, ce jeu n'aurait jamais existé. href="https://github.com/niklas-dahl" target="_blank">Niklas</a> — Sans nos sessions sur Factorio, ce jeu naurait jamais existé.
changelog: changelog:
title: Historique title: Historique
@ -893,7 +894,7 @@ demo:
importingGames: Importer des sauvegardes importingGames: Importer des sauvegardes
oneGameLimit: Limité à une sauvegarde oneGameLimit: Limité à une sauvegarde
customizeKeybindings: Personnalisation des contrôles customizeKeybindings: Personnalisation des contrôles
exportingBase: Exporter toute la base en tant qu'image exportingBase: Exporter une image de toute la base
settingNotAvailable: Indisponible dans la démo. settingNotAvailable: Indisponible dans la démo.
# #

View File

@ -22,10 +22,10 @@
--- ---
steamPage: steamPage:
# This is the short text appearing on the steam page # This is the short text appearing on the steam page
shortText: shapez.io adalah permainan membangun pabrik-pabrik dengan tujuan untuk mengautomatiskan pembentukan dan pemrosesan bentuk-bentuk yang bertambah semakin kompleks di dalam area permainan yang meluas secara tak terhingga. shortText: Shapez.io adalah game tentang membangun pabrik untuk mengotomatiskan pembuatan dan pemrosesan bentuk-bentuk yang semakin kompleks di peta yang meluas tanpa batas.
# This is the text shown above the discord link # This is the text shown above the discord link
discordLink: Tautan Resmi Discord Obrol dengan saya! discordLink: Tautan Resmi Discord Mari mengobrol dengan saya!
# This is the long description for the steam page - It is contained here so you can help to translate it, and I will regulary update the store page. # This is the long description for the steam page - It is contained here so you can help to translate it, and I will regulary update the store page.
# NOTICE: # NOTICE:
# - Do not translate the first line (This is the gif image at the start of the store) # - Do not translate the first line (This is the gif image at the start of the store)
@ -33,13 +33,13 @@ steamPage:
longText: >- longText: >-
[img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img] [img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img]
shapez.io adalah permainan membangun pabrik-pabrik dengan tujuan untuk mengautomatiskan pembentukan dan pemrosesan bentuk-bentuk yang bertambah semakin kompleks di dalam area permainan yang meluas secara tak terhingga. shapez.io adalah permainan membangun pabrik dengan tujuan untuk mengautomatiskan pembentukan dan pemrosesan bentuk-bentuk yang bertambah semakin kompleks di peta yang meluas tanpa batas.
Setelah pengiriman bentuk-bentuk yang diminta, Anda akan maju dalam permainan dan membuka tingkatan versi-versi mesin selanjutnya untuk mempercepat pabrik Anda. Setelah pengiriman bentuk-bentuk yang diminta, Anda akan maju dalam permainan dan membuka tingkatan versi-versi mesin selanjutnya untuk mempercepat pabrik Anda.
Seiring meningkatnya kesulitan dari bentuk-bentuk yang diminta, Anda harus meningkatkan pabrik Anda untuk mengatasi kesulitan tersebut Jangan lupa dengan sumber-sumber daya, Anda harus memperluas ke seluruh [b]area yang tidak terbatas[/b]! Seiring meningkatnya kesulitan dari bentuk-bentuk yang diminta, Anda harus meningkatkan pabrik Anda untuk mengatasi kesulitan tersebut Jangan lupa dengan sumber daya, Anda harus memperluas ke seluruh [b]area yang tidak terbatas[/b]!
Kemudian Anda harus mencampurkan warna-warna dan mencat bentuk-bentuk dengannya Campurkan merah, hijau, dan biru untuk memproduksi warna-warna lain dan mencat bentuk-bentuk dengannya untuk memenuhi permintaan. Kemudian Anda harus mencampurkan warna-warna dan mencat bentuk-bentuk tersebut Campurkan merah, hijau, dan biru untuk memproduksi warna-warna lain dan mencat bentuk-bentuk tersebut untuk memenuhi permintaan.
Permainan ini mempunyai 18 level-level progresif (yang mana akan membuat Anda sibuk berjam-jam!), akan tetapi saya akan terus menambahkan konten-konten baru Ada banyak yang direncanakan! Permainan ini mempunyai 18 level-level progresif (yang mana akan membuat Anda sibuk berjam-jam!), akan tetapi saya akan terus menambahkan konten-konten baru Ada banyak yang direncanakan!
@ -48,16 +48,16 @@ steamPage:
[b]Keuntungan Versi Penuh[/b] [b]Keuntungan Versi Penuh[/b]
[list] [list]
[*] Versi Permainan Gelap [*] Mode Malam
[*] Titik Arah Tak Terhingga [*] Titik Arah Tak Terhingga
[*] Penyimpanan Permainan Tak Terhingga [*] Penyimpanan Permainan Tak Terhingga
[*] Pengaturan-pengaturan Tambahan [*] Pengaturan-pengaturan Tambahan
[*] Akan datang: Kawat & Energi! Akan dicoba untuk dicapai untuk (kira-kira) akhir Juli 2020. [*] Akan datang: Kawat & Energi! Akan dicoba untuk dicapai sekitar akhir Juli 2020.
[*] Akan datang: Level-level tambahan [*] Akan datang: Level-level tambahan
[*] Memperkenankan saya untuk terus mengembangkan shapez.io ❤️ [*] Mendukung saya untuk terus mengembangkan shapez.io ❤️
[/list] [/list]
[b]Pembaruan di Masa Depan[/b] [b]Pembaruan di masa yang akan datang[/b]
Saya seringkali membarui permainan ini dan terus mencoba untuk menciptakan pembaruan paling sedikit sekali seminggu! Saya seringkali membarui permainan ini dan terus mencoba untuk menciptakan pembaruan paling sedikit sekali seminggu!
@ -87,8 +87,8 @@ steamPage:
[/list] [/list]
global: global:
loading: Memuat loading: Sedang memuat
error: Terdapat kesalahan error: Terjadi kesalahan
# How big numbers are rendered, e.g. "10,000" # How big numbers are rendered, e.g. "10,000"
thousandsDivider: "," thousandsDivider: ","
@ -98,9 +98,9 @@ global:
# The suffix for large numbers, e.g. 1.3k, 400.2M, etc. # The suffix for large numbers, e.g. 1.3k, 400.2M, etc.
suffix: suffix:
thousands: k thousands: K
millions: M millions: J
billions: B billions: M
trillions: T trillions: T
# Shown for infinitely big numbers # Shown for infinitely big numbers
@ -108,13 +108,13 @@ global:
time: time:
# Used for formatting past time dates # Used for formatting past time dates
oneSecondAgo: satu detik yang lalu oneSecondAgo: sedetik yang lalu
xSecondsAgo: <x> detik yang lalu xSecondsAgo: <x> detik yang lalu
oneMinuteAgo: satu menit yang lalu oneMinuteAgo: semenit yang lalu
xMinutesAgo: <x> menit yang lalu xMinutesAgo: <x> menit yang lalu
oneHourAgo: satu jam yang lalu oneHourAgo: sejam yang lalu
xHoursAgo: <x> jam yang lalu xHoursAgo: <x> jam yang lalu
oneDayAgo: satu hari yang lalu oneDayAgo: sehari yang lalu
xDaysAgo: <x> hari yang lalu xDaysAgo: <x> hari yang lalu
# Short formats for times, e.g. '5h 23m' # Short formats for times, e.g. '5h 23m'
@ -142,7 +142,7 @@ mainMenu:
play: Mulai Permainan play: Mulai Permainan
continue: Lanjutkan Permainan continue: Lanjutkan Permainan
newGame: Permainan Baru newGame: Permainan Baru
changelog: Ganti Data Log changelog: Catatan Perubahan
subreddit: Reddit subreddit: Reddit
importSavegame: Impor Data Simpanan importSavegame: Impor Data Simpanan
openSourceHint: Permainan ini bekerja secara open source! openSourceHint: Permainan ini bekerja secara open source!
@ -166,7 +166,7 @@ dialogs:
restart: Mulai Ulang restart: Mulai Ulang
reset: Setel Ulang reset: Setel Ulang
getStandalone: Dapatkan Versi Penuh getStandalone: Dapatkan Versi Penuh
deleteGame: Saya tahu apa yang saya kerjakan deleteGame: Saya tahu apa yang saya lakukan
viewUpdate: Tampilkan Pembaruan viewUpdate: Tampilkan Pembaruan
showUpgrades: Tunjukkan Tingkatan showUpgrades: Tunjukkan Tingkatan
showKeybindings: Tunjukan Tombol Pintas showKeybindings: Tunjukan Tombol Pintas

View File

@ -87,7 +87,7 @@ steamPage:
global: global:
loading: Laden loading: Laden
error: Error error: Fout
# How big numbers are rendered, e.g. "10,000" # How big numbers are rendered, e.g. "10,000"
thousandsDivider: "." thousandsDivider: "."
@ -172,7 +172,7 @@ dialogs:
showKeybindings: Zie Sneltoetsen showKeybindings: Zie Sneltoetsen
importSavegameError: importSavegameError:
title: Importeer error title: Importeerfout
text: >- text: >-
Het importeren van je savegame is mislukt: Het importeren van je savegame is mislukt:
@ -322,7 +322,7 @@ ingame:
# <level> is replaced by the actual level, so this gets 'Level 03' for example. # <level> is replaced by the actual level, so this gets 'Level 03' for example.
levelTitle: Level <level> levelTitle: Level <level>
completed: Voltooid completed: Voltooid
unlockText: Ontgrendeld <reward>! unlockText: <reward> ontgrendeld!
buttonNextLevel: Volgende Level buttonNextLevel: Volgende Level
# Notifications on the lower right # Notifications on the lower right
@ -633,9 +633,9 @@ storyRewards:
settings: settings:
title: Opties title: Opties
categories: categories:
general: General general: Algemeen
userInterface: User Interface userInterface: Opmaak
advanced: Advanced advanced: Geavanceerd
versionBadges: versionBadges:
dev: Ontwikkeling dev: Ontwikkeling
@ -795,8 +795,8 @@ keybindings:
menuOpenShop: Upgrades menuOpenShop: Upgrades
menuOpenStats: Statistieken menuOpenStats: Statistieken
toggleHud: Toggle HUD toggleHud: Schakel HUD
toggleFPSInfo: Toggle FPS en Debug Info toggleFPSInfo: Schakel FPS en Debug Info
belt: *belt belt: *belt
splitter: *splitter splitter: *splitter
underground_belt: *underground_belt underground_belt: *underground_belt

View File

@ -22,7 +22,7 @@
--- ---
steamPage: steamPage:
# This is the short text appearing on the steam page # This is the short text appearing on the steam page
shortText: shapez.io é um jogo sobre construir fábricas, automatizando a criação e combinação de formas cada vez mais complexas num mapa infinito. shortText: Shapez.io é um jogo sobre construir fábricas, automatizando a criação e combinação de formas cada vez mais complexas num mapa infinito.
# This is the text shown above the Discord link # This is the text shown above the Discord link
discordLink: Discord Oficial - Converse comigo! discordLink: Discord Oficial - Converse comigo!
@ -34,17 +34,17 @@ steamPage:
longText: >- longText: >-
[img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img] [img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img]
shapez.io é um jogo sobre construir fábricas, automatizando a criação e combinação de formas cada vez mais complexas num mapa infinito. Shapez.io é um jogo sobre construir fábricas, automatizando a criação e combinação de formas cada vez mais complexas num mapa infinito.
Após a entrega das formas requisitadas você progredirá no jogo e desbloqueará melhorias para acelerar sua fábrica. Após a entrega das formas requisitadas, você avançará no jogo e desbloqueará melhorias para acelerar sua produção.
Conforme sua demanda por formas aumenta, você irá que aumentar sua fábrica para alcançar-la - Mas não se esqueça dos recursos, você precisará expandir pelo [b]mapa infinito[/b]! Conforme sua demanda por formas aumenta, você terá que aumentar sua fábrica para alcançá-la - Mas não se esqueça dos recursos, você precisará expandir pelo [b]mapa infinito[/b]!
Rapidamente você vai ter que misturar cores e pintar suas formas com elas - Combine recursos vermelhos, verdes e azuis para produzir cores diferentes e pintar formas com elas para satisfazer a demanda. Em pouco tempo você terá que misturar cores e pintar suas formas com elas - Combine recursos vermelhos, verdes e azuis para produzir cores diferentes e pintar formas com elas para satisfazer a demanda.
O jogo contém 18 níveis progressivos (Que já devem manter você ocupado por horas!) mas eu adiciono novo contéudo constantemente - Tem bastante coisa já planejada! O jogo contém 18 níveis progressivos (que já devem manter você ocupado por horas!) mas eu adiciono novo contéudo constantemente - Tem bastante coisa já planejada!
Comprando o jogo você terá acesso à versão completa, que contém recursos adicionais, e além disso você também terá acesso aos recursos que seram desenvolvidos. Comprando o jogo você terá acesso à versão completa, que contém recursos adicionais, e além disso você também terá acesso aos recursos que serão desenvolvidos.
[b]Vantagens da versão completa[/b] [b]Vantagens da versão completa[/b]
@ -60,15 +60,15 @@ steamPage:
[b]Atualizações Futuras[/b] [b]Atualizações Futuras[/b]
Eu lanço atualizações frequentemente e estou tentando lançar pelo menos um por semana! Eu lanço atualizações frequentemente e estou tentando lançar pelo menos uma por semana!
[list] [list]
[*] Mapas diferentes e desafios (por exemplo mapas com obstáculos) [*] Mapas diferentes e desafios (por exemplo mapas com obstáculos)
[*] Puzzles (Entregue a forma pedida com uma área restringida ou um certo conjunto de construções) [*] Puzzles (Entregue a forma pedida com uma área restringida ou um certo conjunto de construções)
[*] Um modo história onde as construções têm um custo [*] Um modo história onde as construções têm um custo
[*] Um geredor de mapa customizável (Configure recursos, forma, tamanho, densidade, semente e mais) [*] Um gerador de mapa customizável (configure recursos, formas, tamanho, densidade, semente e mais)
[*] Mais tipos de formas [*] Mais tipos de formas
[*] Melhorias de desempenho (O jogo já roda bem!) [*] Melhorias de desempenho (o jogo já roda bem!)
[*] E muito mais! [*] E muito mais!
[/list] [/list]
@ -130,8 +130,8 @@ global:
control: CTRL control: CTRL
alt: ALT alt: ALT
escape: ESC escape: ESC
shift: SHIFT shift: Shift
space: ESPAÇO space: Espaço
demoBanners: demoBanners:
# This is the "advertisement" shown in the main menu and other various places # This is the "advertisement" shown in the main menu and other various places
@ -145,7 +145,7 @@ mainMenu:
newGame: Novo jogo newGame: Novo jogo
changelog: Changelog changelog: Changelog
subreddit: Reddit subreddit: Reddit
importSavegame: Importar importSavegame: Importar save
openSourceHint: Esse jogo tem código aberto! openSourceHint: Esse jogo tem código aberto!
discordLink: Discord oficial discordLink: Discord oficial
helpTranslate: Ajude a traduzir! helpTranslate: Ajude a traduzir!
@ -183,7 +183,7 @@ dialogs:
Seu jogo salvo foi importado com sucesso. Seu jogo salvo foi importado com sucesso.
gameLoadFailure: gameLoadFailure:
title: Jogo salvo quebrado title: Jogo salvo corrompido
text: >- text: >-
Houve uma falha ao carregar seu jogo salvo: Houve uma falha ao carregar seu jogo salvo:
@ -198,13 +198,13 @@ dialogs:
Houve uma falha ao deletar seu jogo salvo: Houve uma falha ao deletar seu jogo salvo:
restartRequired: restartRequired:
title: Ação necessária title: Reinicialização necessária
text: >- text: >-
Você precisa reiniciar o jogo para aplicar as mudanças. Você precisa reiniciar o jogo para aplicar as mudanças.
editKeybinding: editKeybinding:
title: Alterar tecla title: Alterar tecla
desc: Pressiona a tecla que deseja vincular, ou ESC para cancelar. desc: Pressione a tecla que deseja vincular, ou ESC para cancelar.
resetKeybindingsConfirmation: resetKeybindingsConfirmation:
title: Resetar controles title: Resetar controles
@ -234,17 +234,17 @@ dialogs:
O guia de melhorias pode ser encontrado no canto superior direito da tela. O guia de melhorias pode ser encontrado no canto superior direito da tela.
massDeleteConfirm: massDeleteConfirm:
title: Deletar title: Deletar?
desc: >- desc: >-
Você está deletando vários objetos (<count> para ser exato)! Você quer continuar? Você está deletando vários objetos (<count> para ser exato)! Você quer continuar?
massCutConfirm: massCutConfirm:
title: Confirmar corte title: Confirmar corte?
desc: >- desc: >-
Você está cortando vários objetos (<count> para ser exato)! Você quer continuar? Você está cortando vários objetos (<count> para ser exato)! Você quer continuar?
massCutInsufficientConfirm: massCutInsufficientConfirm:
title: Confirmar Corte title: Confirmar Corte?
desc: >- desc: >-
You can not afford to paste this area! Are you sure you want to cut it? You can not afford to paste this area! Are you sure you want to cut it?
@ -290,8 +290,8 @@ ingame:
createMarker: Criar marcador createMarker: Criar marcador
delete: Destruir delete: Destruir
pasteLastBlueprint: Colar último projeto pasteLastBlueprint: Colar último projeto
lockBeltDirection: Ativar Planejador de Esteiras lockBeltDirection: Ativar Planejamento de Esteiras
plannerSwitchSide: Girar Planejador plannerSwitchSide: Girar Planejamento
cutSelection: Cortar cutSelection: Cortar
copySelection: Copiar copySelection: Copiar
clearSelection: Limpar Seleção clearSelection: Limpar Seleção
@ -363,13 +363,13 @@ ingame:
dataSources: dataSources:
stored: stored:
title: Estoque title: Estoque
description: Exibindo a quantidade de formas armazenadas em sua construção central. description: Exibindo a quantidade de formas armazenadas no seu HUB.
produced: produced:
title: Produção title: Produção
description: Exibindo todas as formas que toda a sua fábrica produz, incluindo produtos intermediários.. description: Exibindo todas as formas que toda a sua fábrica produz, incluindo produtos intermediários..
delivered: delivered:
title: Entregue title: Entregue
description: Exibindo formas entregues na sua construção central. description: Exibindo formas entregues no seu HUB.
noShapesProduced: Nenhuma forma foi produzida até o momento. noShapesProduced: Nenhuma forma foi produzida até o momento.
# Displays the shapes per minute, e.g. '523 / m' # Displays the shapes per minute, e.g. '523 / m'
@ -384,14 +384,14 @@ ingame:
buttons: buttons:
continue: Continuar continue: Continuar
settings: Definições settings: Configurações
menu: Voltar ao menu menu: Voltar ao menu
# Bottom left tutorial hints # Bottom left tutorial hints
tutorialHints: tutorialHints:
title: Quer ajuda? title: Quer ajuda?
showHint: Mostrar dica showHint: Mostrar dica
hideHint: Fechar hideHint: Esconder dica
# When placing a blueprint # When placing a blueprint
blueprintPlacer: blueprintPlacer:
@ -401,7 +401,7 @@ ingame:
waypoints: waypoints:
waypoints: Marcadores waypoints: Marcadores
hub: HUB hub: HUB
description: Clique com o botão esquerdo do mouse em um marcador para pular, clique com o botão direito do mouse para excluí-lo. <br><br> Pressione <keybinding> para criar um marcador a partir da exibição atual ou <strong>clique com o botão direito do mouse</strong> para criar um marcador no local selecionado. description: Clique com o botão esquerdo do mouse em um marcador para pular, clique com o botão direito do mouse para excluí-lo. <br><br> Pressione <keybinding> para criar um marcador à partir da exibição atual ou <strong>clique com o botão direito do mouse</strong> para criar um marcador no local selecionado.
creationSuccessNotification: Marcador criado. creationSuccessNotification: Marcador criado.
# Shape viewer # Shape viewer
@ -419,7 +419,7 @@ ingame:
Conecte o extrator com uma <strong>esteira transportadora</strong> até a sua base!<br><br>Dica, <strong>clique e arraste</strong> a esteira com o mouse! Conecte o extrator com uma <strong>esteira transportadora</strong> até a sua base!<br><br>Dica, <strong>clique e arraste</strong> a esteira com o mouse!
1_3_expand: >- 1_3_expand: >-
Este <strong>NÃO</strong> é um jogo inativo! Construa mais extratores e esteiras para concluir o objetivo mais rapidamente.<br><br>Dica, segure <strong>SHIFT</strong> para colocar vários extratores e use <strong>R</strong> para girá-los. Este <strong>NÃO</strong> é um jogo idle! Construa mais extratores e esteiras para concluir o objetivo mais rapidamente.<br><br>Dica, segure <strong>SHIFT</strong> para colocar vários extratores e use <strong>R</strong> para girá-los.
# All shop upgrades # All shop upgrades
shopUpgrades: shopUpgrades:
@ -433,7 +433,7 @@ shopUpgrades:
name: Corte, Rotação e Montagem name: Corte, Rotação e Montagem
description: Velocidade x<currentMult> → x<newMult> description: Velocidade x<currentMult> → x<newMult>
painting: painting:
name: Mistura e Pintura name: Mistura de cores e Pintura
description: Velocidade x<currentMult> → x<newMult> description: Velocidade x<currentMult> → x<newMult>
# Buildings and their name / description # Buildings and their name / description
@ -469,7 +469,7 @@ buildings:
tier2: tier2:
name: Túnel Classe II name: Túnel Classe II
description: Permite transportar recursos por baixo de construções e esteiras. description: Permite transportar recursos por baixo de construções e outras esteiras.
splitter: # Internal name for the Balancer splitter: # Internal name for the Balancer
default: default:
@ -541,7 +541,7 @@ buildings:
storage: storage:
name: Estoque name: Estoque
description: Armazena itens em excesso, até uma determinada capacidade. Pode ser usado como uma porta de transbordamento. description: Armazena itens em excesso, até uma determinada capacidade. Pode ser usado como uma eclusa.
energy_generator: energy_generator:
deliver: Entregar deliver: Entregar
@ -575,7 +575,7 @@ storyRewards:
reward_painter: reward_painter:
title: Pintura title: Pintura
desc: >- desc: >-
O <strong>Pintor</strong> foi desbloqueado - Extrai alguns pigmentos coloridos (assim como você fez com as formas) e combina-os com uma forma no pintor para os colorir!<br><br>PS: Se for daltônico, existe um <strong>modo daltônico</strong> nas definições! O <strong>Pintor</strong> foi desbloqueado - Extraia alguns pigmentos coloridos (assim como você fez com as formas) e combine-os com uma forma no pintor para colorí-las!<br><br>PS: Se for daltônico, existe um <strong>modo daltônico</strong> nas definições!
reward_mixer: reward_mixer:
title: Misturando cores title: Misturando cores
@ -591,7 +591,7 @@ storyRewards:
reward_tunnel: reward_tunnel:
title: Túnel title: Túnel
desc: O <strong>túnel</strong> foi desbloqueado - Agora você pode canalizar itens sob construções! desc: O <strong>túnel</strong> foi desbloqueado - Agora você pode transportar itens abaixo do solo!
reward_rotater_ccw: reward_rotater_ccw:
title: Rotação anti-horária title: Rotação anti-horária
@ -646,7 +646,7 @@ storyRewards:
Parabéns! Aliás, mais conteúdo vindo na versão completa! Parabéns! Aliás, mais conteúdo vindo na versão completa!
settings: settings:
title: opções title: Opções
categories: categories:
general: Geral general: Geral
userInterface: Interface de Usuário userInterface: Interface de Usuário
@ -671,7 +671,7 @@ settings:
huge: Gigante huge: Gigante
autosaveInterval: autosaveInterval:
title: Intervalo de gravação automática title: Intervalo de save automático
description: >- description: >-
Controla a frequência com que o jogo salva automaticamente. Você também pode desativá-lo totalmente aqui. Controla a frequência com que o jogo salva automaticamente. Você também pode desativá-lo totalmente aqui.
@ -714,7 +714,7 @@ settings:
enableColorBlindHelper: enableColorBlindHelper:
title: Modo daltônico. title: Modo daltônico.
description: >- description: >-
Permite várias ferramentas que permitem jogar se você é daltônico. Habilita várias ferramentas que te permitem jogar se você é daltônico.
fullscreen: fullscreen:
title: Tela Cheia title: Tela Cheia
@ -745,24 +745,24 @@ settings:
Se você possui um monitor de 144 hz, altere a taxa de atualização aqui para que o jogo seja simulado corretamente com taxas de atualização mais altas. Isso diminuir o FPS consideravelmente se o computador for muito lento. Se você possui um monitor de 144 hz, altere a taxa de atualização aqui para que o jogo seja simulado corretamente com taxas de atualização mais altas. Isso diminuir o FPS consideravelmente se o computador for muito lento.
alwaysMultiplace: alwaysMultiplace:
title: Multiplicidade title: Posicionamento Múltiplo
description: >- description: >-
Se ativado, todas as construções permanecerão selecionadas após o posicionamento até que você a cancele. Isso é equivalente a pressionar SHIFT permanentemente. Se ativado, todas as construções permanecerão selecionadas após o posicionamento até que você a cancele. Isso é equivalente a pressionar SHIFT permanentemente.
offerHints: offerHints:
title: Dicas e tutoriais title: Dicas e Tutoriais
description: >- description: >-
Se ativado, oferece dicas e tutoriais enquanto se joga. Além disso, esconde certos elementos da interface até certo ponto, para facilitar o começo do jogo. Se ativado, oferece dicas e tutoriais enquanto se joga. Além disso, esconde certos elementos da interface até certo ponto, para facilitar o começo do jogo.
enableTunnelSmartplace: enableTunnelSmartplace:
title: Túneis inteligentes title: Túneis Inteligentes
description: >- description: >-
Quando colocados, irão remover automaticamente esteiras desnecessárias. Isso também permite arrastar túneis e túneis em excesso serão removidos. Quando colocados, irão remover automaticamente esteiras desnecessárias. Isso também permite arrastar túneis e túneis em excesso serão removidos.
vignette: vignette:
title: Vinheta title: Vinheta
description: >- description: >-
Permite o modo vinheta que escurece os cantos da tela e facilita a leitura do texto. Habilita o modo vinheta que escurece os cantos da tela e facilita a leitura do texto.
rotationByBuilding: rotationByBuilding:
title: Rotação por tipo de construção title: Rotação por tipo de construção
@ -777,7 +777,7 @@ settings:
disableCutDeleteWarnings: disableCutDeleteWarnings:
title: Desativar avisos de recorte / exclusão title: Desativar avisos de recorte / exclusão
description: >- description: >-
Desative as caixas de diálogo de aviso exibidas ao cortar / excluir mais de 100 entidades. Desativa as caixas de diálogo de aviso exibidas ao cortar / excluir mais de 100 entidades.
keybindings: keybindings:
title: Controles title: Controles
@ -790,9 +790,9 @@ keybindings:
general: Geral general: Geral
ingame: Jogo ingame: Jogo
navigation: Navegação navigation: Navegação
placement: Construção placement: Posicionamento
massSelect: Seleção massSelect: Seleção em Massa
buildings: Atalhos de objetos buildings: Construções
placementModifiers: Modificadores placementModifiers: Modificadores
mappings: mappings:
@ -807,7 +807,7 @@ keybindings:
mapZoomIn: Aproximar mapZoomIn: Aproximar
mapZoomOut: Distanciar mapZoomOut: Distanciar
createMarker: Criar marcação createMarker: Criar marcador
menuOpenShop: Melhorias menuOpenShop: Melhorias
menuOpenStats: Estatísticas menuOpenStats: Estatísticas
@ -839,7 +839,7 @@ keybindings:
confirmMassDelete: Confirmar exclusão em massa confirmMassDelete: Confirmar exclusão em massa
pasteLastBlueprint: Colar último projeto pasteLastBlueprint: Colar último projeto
cycleBuildings: Trocar de construção cycleBuildings: Trocar de construção
lockBeltDirection: Ativar planejador de correia lockBeltDirection: Ativar planejamento de esteira
switchDirectionLockSide: >- switchDirectionLockSide: >-
Planejador: Mudar de lado Planejador: Mudar de lado