Prepare the achievements update

pull/1089/head
Tobias Springer 3 years ago
parent 837b0d8007
commit 226149a40f

@ -62,7 +62,8 @@
"webpack-plugin-replace": "^1.1.1",
"webpack-strip-block": "^0.2.0",
"whatwg-fetch": "^3.0.0",
"worker-loader": "^2.0.0"
"worker-loader": "^2.0.0",
"yaml": "^1.10.0"
},
"devDependencies": {
"autoprefixer": "^9.4.3",

@ -25,6 +25,7 @@ function gulptasksTranslations($, gulp) {
files
.filter(name => name.endsWith(".yaml"))
.forEach(fname => {
console.log("Loading", fname);
const languageName = fname.replace(".yaml", "");
const abspath = path.join(translationsSourceDir, fname);
@ -40,39 +41,13 @@ function gulptasksTranslations($, gulp) {
${storePage.intro.replace(/\n/gi, "\n\n")}
[h2]${storePage.title_advantages}[/h2]
[h2]${storePage.what_others_say}[/h2]
[list]
${storePage.advantages
.map(x => "[*] " + x.replace(/<b>/, "[b]").replace(/<\/b>/, "[/b]"))
.join("\n")}
[*] [i]${storePage.northernlion_comment}[/i] [b]- Northernlion, YouTube[/b]
[*] [i]${storePage.notch_comment}[/i] [b]- Notch[/b]
[*] [i]${storePage.steam_review_comment}[/i] [b]- Steam User[/b]
[/list]
[h2]${storePage.title_future}[/h2]
[list]
${storePage.planned
.map(x => "[*] " + x.replace(/<b>/, "[b]").replace(/<\/b>/, "[/b]"))
.join("\n")}
[/list]
[h2]${storePage.title_open_source}[/h2]
${storePage.text_open_source.replace(/\n/gi, "\n\n")}
[h2]${storePage.title_links}[/h2]
[list]
[*] [url=https://discord.com/invite/HN7EVzV]${storePage.links.discord}[/url]
[*] [url=https://trello.com/b/ISQncpJP/shapezio]${storePage.links.roadmap}[/url]
[*] [url=https://www.reddit.com/r/shapezio]${storePage.links.subreddit}[/url]
[*] [url=https://github.com/tobspr/shapez.io]${storePage.links.source_code}[/url]
[*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]${
storePage.links.translate
}[/url]
[/list]
`;
fs.writeFileSync(destpath, trim(content.replace(/(\n[ \t\r]*)/gi, "\n")), {

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 KiB

@ -0,0 +1,18 @@
[data-changelog-skin="achievements"] {
background: #f8f8f8;
@include DarkThemeOverride {
background: rgba(0, 10, 20, 0.2);
}
@include S(border-radius, 5px);
&::before {
content: " ";
width: 100%;
display: block;
background: uiResource("changelog_skins/achievements.noinline.png") center center / cover no-repeat !important;
@include S(height, 80px);
@include S(border-radius, 5px);
@include S(margin-bottom, 5px);
}
}

@ -19,6 +19,7 @@
@import "application_error";
@import "textual_game_state";
@import "adinplay";
@import "changelog_skins";
@import "states/preload";
@import "states/main_menu";
@ -56,8 +57,8 @@
@import "ingame_hud/cat_memes";
// prettier-ignore
$elements:
// Base
$elements:
// Base
ingame_Canvas,
ingame_VignetteOverlay,
@ -119,11 +120,3 @@ body.uiHidden {
display: none !important;
}
}
body.modalDialogActive,
body.externalAdOpen,
body.ingameDialogOpen {
> *:not(.ingameDialog):not(.modalDialogParent):not(.loadingDialog):not(.gameLoadingOverlay):not(#ingame_HUD_ModalDialogs):not(.noBlur) {
// filter: blur(5px) !important;
}
}

@ -184,7 +184,7 @@
.updateLabel {
position: absolute;
transform: translateX(50%) rotate(-5deg);
color: rgb(231, 78, 58);
color: rgb(133, 58, 231);
@include Heading;
font-weight: bold;
@include S(right, 40px);

@ -14,6 +14,7 @@
padding: 10px;
box-sizing: border-box;
background: #eef1f4;
@include S(border-radius, 3px);
@include DarkThemeOverride {
background: #424242;

@ -1,10 +1,13 @@
export const CHANGELOG = [
{
version: "1.2.3",
date: "unreleased",
version: "1.3.0",
date: "12.03.2020",
skin: "achievements",
entries: [
"There are now <strong>45 Steam Achievements!</strong>",
"Fixed constant signals being editable from the regular layer",
"Fixed items still overlapping sometimes between buildings and belts",
"Updated translations (Thanks to all contributors!)",
],
},
{

@ -12,8 +12,6 @@ const logger = createLogger("achievement_proxy");
const ROTATER = "rotater";
const DEFAULT = "default";
const BELT = "belt";
const LEVEL_26 = 26;
export class AchievementProxy {
/** @param {GameRoot} root */
@ -22,7 +20,9 @@ export class AchievementProxy {
this.provider = this.root.app.achievementProvider;
this.disabled = true;
if (!this.provider.hasAchievements()) {
if (G_IS_DEV && globalConfig.debug.testAchievements) {
// still enable the proxy
} else if (!this.provider.hasAchievements()) {
return;
}
@ -34,7 +34,8 @@ export class AchievementProxy {
}
onLoad() {
this.provider.onLoad(this.root)
this.provider
.onLoad(this.root)
.then(() => {
this.disabled = false;
logger.log("Recieving achievement signals");
@ -50,6 +51,8 @@ export class AchievementProxy {
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.darkMode);
if (this.has(ACHIEVEMENTS.mam)) {
this.root.signals.entityAdded.add(this.onMamFailure, this);
this.root.signals.entityDestroyed.add(this.onMamFailure, this);
this.root.signals.storyGoalCompleted.add(this.onStoryGoalCompleted, this);
}
@ -57,10 +60,6 @@ export class AchievementProxy {
this.root.signals.entityAdded.add(this.onEntityAdded, this);
}
if (this.has(ACHIEVEMENTS.noBeltUpgradesUntilBp)) {
this.root.signals.upgradePurchased.add(this.onUpgradePurchased, this);
}
this.startSlice();
}
@ -73,27 +72,38 @@ export class AchievementProxy {
// Every other slice
if (this.sliceIteration % 2 === 0) {
this.root.signals.bulkAchievementCheck.dispatch(
ACHIEVEMENTS.throughputBp25, this.sliceTime,
ACHIEVEMENTS.throughputBp50, this.sliceTime,
ACHIEVEMENTS.throughputLogo25, this.sliceTime,
ACHIEVEMENTS.throughputLogo50, this.sliceTime,
ACHIEVEMENTS.throughputRocket10, this.sliceTime,
ACHIEVEMENTS.throughputRocket20, this.sliceTime
ACHIEVEMENTS.throughputBp25,
this.sliceTime,
ACHIEVEMENTS.throughputBp50,
this.sliceTime,
ACHIEVEMENTS.throughputLogo25,
this.sliceTime,
ACHIEVEMENTS.throughputLogo50,
this.sliceTime,
ACHIEVEMENTS.throughputRocket10,
this.sliceTime,
ACHIEVEMENTS.throughputRocket20,
this.sliceTime
);
}
// Every 3rd slice
if (this.sliceIteration % 3 === 0) {
this.root.signals.bulkAchievementCheck.dispatch(
ACHIEVEMENTS.play1h, this.sliceTime,
ACHIEVEMENTS.play10h, this.sliceTime,
ACHIEVEMENTS.play20h, this.sliceTime
ACHIEVEMENTS.play1h,
this.sliceTime,
ACHIEVEMENTS.play10h,
this.sliceTime,
ACHIEVEMENTS.play20h,
this.sliceTime
);
}
// Every 10th slice
if (this.sliceIteration % 10 === 0) {
this.provider.collection.clean();
if (this.provider.collection) {
this.provider.collection.clean();
}
}
if (this.sliceIteration === this.sliceIterationLimit) {
@ -118,6 +128,9 @@ export class AchievementProxy {
* @returns {boolean}
*/
has(key) {
if (!this.provider.collection) {
return false;
}
return this.provider.collection.map.has(key);
}
@ -127,7 +140,7 @@ export class AchievementProxy {
return;
}
const building = getBuildingDataFromCode(entity.components.StaticMapEntity.code)
const building = getBuildingDataFromCode(entity.components.StaticMapEntity.code);
if (building.metaInstance.id !== ROTATER) {
return;
@ -143,28 +156,18 @@ export class AchievementProxy {
/** @param {number} level */
onStoryGoalCompleted(level) {
if (level === LEVEL_26) {
if (level > 26) {
this.root.signals.entityAdded.add(this.onMamFailure, this);
this.root.signals.entityDestroyed.add(this.onMamFailure, this);
} else if (level === LEVEL_26 + 1) {
this.root.signals.storyGoalCompleted.remove(this.onStoryGoalCompleted, this);
}
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.mam);
// reset on every level
this.root.savegame.currentData.stats.failedMam = false;
}
onMamFailure() {
this.root.savegame.currentData.stats.failedMam = true;
this.root.signals.entityAdded.remove(this.onMamFailure);
this.root.signals.entityDestroyed.remove(this.onMamFailure);
this.root.signals.storyGoalCompleted.remove(this.onStoryGoalCompleted);
}
/** @param {string} upgrade */
onUpgradePurchased(upgrade) {
if (upgrade !== BELT) {
return;
}
this.root.savegame.currentData.stats.upgradedBelt = true;
this.root.signals.upgradePurchased.remove(this.onUpgradePurchased);
}
}

@ -182,7 +182,7 @@ export class GameRoot {
// Called with an achievement key and necessary args to validate it can be unlocked.
achievementCheck: /** @type {TypedSignal<[string, *]>} */ (new Signal()),
bulkAchievementCheck: /** @type {TypedSignal<[string, any]...>} */ (new Signal()),
bulkAchievementCheck: /** @type {TypedSignal<(string|any)[]>} */ (new Signal()),
};
// RNG's

@ -1,12 +1,14 @@
/* typehints:start */
import { Application } from "../application";
import { StorageComponent } from "../game/components/storage";
import { ShapeItem } from "../game/items/shape_item";
import { Entity } from "../game/entity";
import { GameRoot } from "../game/root";
import { ShapeDefinition } from "../game/shape_definition";
import { THEMES } from "../game/theme";
/* typehints:end */
import { enumAnalyticsDataSource } from "../game/production_analytics";
import { ShapeItem } from "../game/items/shape_item";
export const ACHIEVEMENTS = {
belt500Tiles: "belt500Tiles",
blueprint100k: "blueprint100k",
@ -55,16 +57,16 @@ export const ACHIEVEMENTS = {
upgradesTier8: "upgradesTier8",
};
/** @type {keyof typeof THEMES} */
const DARK_MODE = "dark";
const HOUR_1 = 3600; // Seconds
const HOUR_10 = HOUR_1 * 10;
const HOUR_20 = HOUR_1 * 20;
const ITEM_SHAPE = "shape";
const ITEM_SHAPE = ShapeItem.getId();
const MINUTE_30 = 1800; // Seconds
const MINUTE_60 = MINUTE_30 * 2;
const MINUTE_120 = MINUTE_30 * 4;
const PRODUCED = "produced";
const RATE_SLICE_COUNT = 10;
const ROTATER_CCW_CODE = 12;
const ROTATER_180_CODE = 13;
const SHAPE_BP = "CbCbCbRb:CwCwCwCw";
@ -72,9 +74,15 @@ const SHAPE_LOGO = "RuCw--Cw:----Ru--";
const SHAPE_MS_LOGO = "RgRyRbRr";
const SHAPE_OLD_LEVEL_17 = "WrRgWrRg:CwCrCwCr:SgSgSgSg";
const SHAPE_ROCKET = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
/** @type {Layer} */
const WIRE_LAYER = "wires";
export class AchievementProviderInterface {
/* typehints:start */
collection = /** @type {AchievementCollection|undefined} */ (null);
/* typehints:end */
/** @param {Application} app */
constructor(app) {
this.app = app;
@ -135,9 +143,7 @@ export class Achievement {
this.signal = null;
}
init() {
}
init() {}
isValid() {
return true;
@ -193,19 +199,16 @@ export class AchievementCollection {
this.add(ACHIEVEMENTS.logoBefore18, {
isRelevant: this.isLogoBefore18Relevant,
isValid: this.isLogoBefore18Valid,
signal: "itemProduced"
signal: "itemProduced",
});
this.add(ACHIEVEMENTS.mam, {
isRelevant: this.isMamRelevant,
isValid: this.isMamValid,
signal: "storyGoalCompleted",
});
this.add(ACHIEVEMENTS.mapMarkers15, {
isRelevant: this.isMapMarkers15Relevant,
isValid: this.isMapMarkers15Valid,
});
this.add(ACHIEVEMENTS.noBeltUpgradesUntilBp, {
init: this.initNoBeltUpgradesUntilBp,
isRelevant: this.isNoBeltUpgradesUntilBpRelevant,
isValid: this.isNoBeltUpgradesUntilBpValid,
signal: "storyGoalCompleted",
@ -354,7 +357,8 @@ export class AchievementCollection {
return;
}
achievement.unlock()
achievement
.unlock()
.then(() => {
this.onActivate(null, key);
})
@ -380,12 +384,13 @@ export class AchievementCollection {
/** @param {string} key - Maps to an Achievement */
remove(key) {
const achievement = this.map.get(key);
if (achievement) {
if (achievement.receiver) {
this.root.signals[achievement.signal].remove(achievement.receiver);
}
if (achievement.receiver) {
this.root.signals[achievement.signal].remove(achievement.receiver);
this.map.delete(key);
}
this.map.delete(key);
}
/**
@ -447,7 +452,7 @@ export class AchievementCollection {
createLevelOptions(level) {
return {
isRelevant: () => this.root.hubGoals.level < level,
isValid: (currentLevel) => currentLevel === level,
isValid: currentLevel => currentLevel === level,
signal: "storyGoalCompleted",
};
}
@ -455,17 +460,19 @@ export class AchievementCollection {
createRateOptions(shape, rate) {
return {
isValid: () => {
return this.root.productionAnalytics.getCurrentShapeRate(
PRODUCED,
this.root.shapeDefinitionMgr.getShapeFromShortKey(shape)
) >= rate;
}
return (
this.root.productionAnalytics.getCurrentShapeRate(
enumAnalyticsDataSource.delivered,
this.root.shapeDefinitionMgr.getShapeFromShortKey(shape)
) >= rate
);
},
};
}
createShapeOptions(shape) {
return {
isValid: (item) => this.isShape(item, shape),
isValid: item => this.isShape(item, shape),
signal: "itemProduced",
};
}
@ -473,7 +480,7 @@ export class AchievementCollection {
createSpeedOptions(level, time) {
return {
isRelevant: () => this.root.hubGoals.level <= level && this.root.time.now() < time,
isValid: (currentLevel) => currentLevel === level && this.root.time.now() < time,
isValid: currentLevel => currentLevel === level && this.root.time.now() < time,
signal: "storyGoalCompleted",
};
}
@ -500,18 +507,12 @@ export class AchievementCollection {
/** @param {ShapeDefinition} definition @returns {boolean} */
isBlueprint100kValid(definition) {
return (
definition.cachedHash === SHAPE_BP &&
this.root.hubGoals.storedShapes[SHAPE_BP] >= 100000
);
return definition.cachedHash === SHAPE_BP && this.root.hubGoals.storedShapes[SHAPE_BP] >= 100000;
}
/** @param {ShapeDefinition} definition @returns {boolean} */
isBlueprint1mValid(definition) {
return (
definition.cachedHash === SHAPE_BP &&
this.root.hubGoals.storedShapes[SHAPE_BP] >= 1000000
);
return definition.cachedHash === SHAPE_BP && this.root.hubGoals.storedShapes[SHAPE_BP] >= 1000000;
}
/** @returns {boolean} */
@ -530,14 +531,18 @@ export class AchievementCollection {
return false;
}
if (definition.cachedHash === this.root.gameMode.getBlueprintShapeKey()) {
return false;
}
const upgrades = this.root.gameMode.getUpgrades();
for (let upgradeId in upgrades) {
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
const requiredShapes = upgrades[upgradeId][currentTier].required;
for (let i = 0; i < requiredShapes.length; i++) {
if (definition.cachedHash === requiredShapes[i].shape) {
return false;
for (const tier in upgrades[upgradeId]) {
const requiredShapes = upgrades[upgradeId][tier].required;
for (let i = 0; i < requiredShapes.length; i++) {
if (definition.cachedHash === requiredShapes[i].shape) {
return false;
}
}
}
}
@ -555,28 +560,9 @@ export class AchievementCollection {
return this.root.hubGoals.level < 18 && this.isShape(item, SHAPE_LOGO);
}
initMam() {
const stats = this.root.savegame.currentData.stats;
if (stats.failedMam === true) {
return;
}
if (this.root.hubGoals.level === 26 && stats.failedMam === false) {
return;
}
stats.failedMam = this.root.hubGoals.level < 26;
}
/** @returns {boolean} */
isMamRelevant() {
return this.root.hubGoals.level <= 26 && !this.root.savegame.currentData.stats.failedMam;
}
/** @params {number} level @returns {boolean} */
isMamValid(level) {
return level === 27 && !this.root.savegame.currentData.stats.failedMam;
isMamValid() {
return this.root.hubGoals.level > 27 && !this.root.savegame.currentData.stats.failedMam;
}
/** @returns {boolean} */
@ -589,16 +575,6 @@ export class AchievementCollection {
return count === 15;
}
initNoBeltUpgradesUntilBp() {
const stats = this.root.savegame.currentData.stats;
if (stats.upgradedBelt === true) {
return;
}
stats.upgradedBelt = this.root.hubGoals.upgradeLevels.belt > 0;
}
/** @returns {boolean} */
isNoBeltUpgradesUntilBpRelevant() {
return this.root.hubGoals.level <= 12 && this.root.hubGoals.upgradeLevels.belt === 0;
@ -631,8 +607,7 @@ export class AchievementCollection {
/** @returns {boolean} */
isNoInverseRotaterRelevant() {
return this.root.hubGoals.level < 14 &&
!this.root.savegame.currentData.stats.usedInverseRotater;
return this.root.hubGoals.level < 14 && !this.root.savegame.currentData.stats.usedInverseRotater;
}
/** @param {number} level @returns {boolean} */

@ -203,12 +203,11 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
if (G_IS_DEV && globalConfig.debug.testAchievements) {
this.app.achievementProvider = new SteamAchievementProvider(this.app);
return this.app.achievementProvider.initialize()
.catch(err => {
logger.error("Failed to initialize achievement provider, disabling:", err);
return this.app.achievementProvider.initialize().catch(err => {
logger.error("Failed to initialize achievement provider, disabling:", err);
this.app.achievementProvider = new NoAchievementProvider(this.app);
});
this.app.achievementProvider = new NoAchievementProvider(this.app);
});
}
return this.app.achievementProvider.initialize();

@ -78,7 +78,11 @@ export class Savegame extends ReadWriteProxy {
return {
version: this.getCurrentVersion(),
dump: null,
stats: {},
stats: {
failedMam: false,
trashedCount: 0,
usedInverseRotater: false,
},
lastUpdate: Date.now(),
};
}

@ -24,9 +24,9 @@ export class SavegameInterface_V1008 extends SavegameInterface_V1007 {
}
Object.assign(data.stats, {
failedMam: false,
failedMam: true,
trashedCount: 0,
usedInverseRotater: false
usedInverseRotater: true,
});
}
}

@ -19,7 +19,7 @@ export class ChangelogState extends TextualGameState {
for (let i = 0; i < entries.length; ++i) {
const entry = entries[i];
html += `
<div class="entry">
<div class="entry" data-changelog-skin="${entry.skin || "default"}">
<span class="version">${entry.version}</span>
<span class="date">${entry.date}</span>
<ul class="changes">

@ -66,7 +66,7 @@ export class MainMenuState extends GameState {
<img src="${cachebust(
G_CHINA_VERSION ? "res/logo_cn.png" : "res/logo.png"
)}" alt="shapez.io Logo">
<span class="updateLabel">v${G_BUILD_VERSION}</span>
<span class="updateLabel">v${G_BUILD_VERSION} - Achievements!</span>
</div>
<div class="mainWrapper ${showDemoBadges ? "demo" : "noDemo"}">

@ -199,7 +199,9 @@ export class PreloadState extends GameState {
for (let i = 0; i < changelogEntries.length; ++i) {
const entry = changelogEntries[i];
dialogHtml += `
<div class="changelogDialogEntry">
<div class="changelogDialogEntry" data-changelog-skin="${
entry.skin || "default"
}">
<span class="version">${entry.version}</span>
<span class="date">${entry.date}</span>
<ul class="changes">

@ -30,49 +30,20 @@ steamPage:
intro: >-
Do you like automation games? Then you are in the right place!
shapez.io is a relaxed game in which you have to build factories for the automated production of geometric shapes. As the level increases, the shapes become more and more complex, and you
have to spread out on the infinite map.
shapez.io is a relaxed game in which you have to build factories for the automated production of geometric shapes. As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.
And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! While you only have to process shapes at the
beginning, you will later have to color them - by extracting and mixing colors!
And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! While you only have to process shapes at the beginning, you will later have to color them - by extracting and mixing colors!
Buying the game on Steam gives you access to the full version, but you can also play a demo at shapez.io first and decide later!
title_advantages: Standalone Advantages
advantages:
- <b>12 New Levels</b> for a total of 26 levels
- <b>18 New Buildings</b> for a fully automated factory!
- <b>Unlimited Upgrade Tiers</b> for many hours of fun!
- <b>Wires Update</b> for an entirely new dimension!
- <b>Dark Mode</b>!
- Unlimited Savegames
- Unlimited Markers
- Support me! ❤️
title_future: Planned Content
planned:
- Blueprint Library
- Steam Achievements
- Puzzle Mode
- Minimap
- Mods
- Sandbox mode
- ... and a lot more!
title_open_source: This game is open source!
text_open_source: >-
Anybody can contribute, I'm actively involved in the community and attempt to review all suggestions and take feedback into consideration where possible.
Be sure to check out my trello board for the full roadmap!
title_links: Links
links:
discord: Official Discord
roadmap: Roadmap
subreddit: Subreddit
source_code: Source code (GitHub)
translate: Help translate
what_others_say: What people say about shapez.io
nothernlion_comment: >-
This game is great - I'm having a wonderful time playing, and time has flown by.
notch_comment: >-
Oh crap. I really should sleep, but I think I just figured out how to make a computer in shapez.io
steam_review_comment: >-
This game has stolen my life and I don't want it back. Very chill factory game that won't let me stop making my lines more efficient.
global:
loading: Loading
@ -1207,11 +1178,6 @@ demo:
settingNotAvailable: Not available in the demo.
achievements:
painting:
displayName: Painting
description: Paint a shape
tips:
- The hub will accept any input, not just the current shape!
- Make sure your factories are modular - it will pay out!

@ -168,13 +168,14 @@ dialogs:
desc: Suorita taso 12 avataksesi piirustukset!
keybindingsIntroduction:
title: Hyödyllisiä pikanäppäimiä
desc: "Tässä pelissä on paljon pikanäppäimiä, jotka tekevät isojen tehtaiden
desc: >-
Tässä pelissä on paljon pikanäppäimiä, jotka tekevät isojen tehtaiden
rakentamisesta helpompaa. Tässä on muutama, mutta <strong>katso
kaikki pikanäppäimet</strong>!<br><br> <code
class='keybinding'>CTRL</code> + Raahaus: Valitse alue.<br> <code
class='keybinding'>SHIFT</code>: Pidä pohjassa sijoittaaksesi
useita samoja rakennuksia.<br> <code class='keybinding'>ALT</code>:
Käännä sijoitettavien kuljettimien suunta.<br>"
Käännä sijoitettavien kuljettimien suunta.<br>
createMarker:
title: Uusi merkki
desc: Anna merkille kuvaava nimi. Voit myös liittää <strong>lyhyen koodin</strong>
@ -306,26 +307,31 @@ ingame:
hints:
1_1_extractor: Laita <strong>Poimija</strong> <strong>ympyrämuodon</strong>
päälle käyttääksesi sitä!
1_2_conveyor: "Yhdistä poimija <strong>kuljettimella</strong>
1_2_conveyor: >-
Yhdistä poimija <strong>kuljettimella</strong>
keskusrakennukseen!<br><br>Vihje: <strong>Paina ja
raahaa</strong> kuljetinta hiirellä!"
1_3_expand: "Tämä <strong>EI OLE</strong> tyhjäkäyntipeli! Rakenna lisää
raahaa</strong> kuljetinta hiirellä!
1_3_expand: >-
Tämä <strong>EI OLE</strong> tyhjäkäyntipeli! Rakenna lisää
poimijoita ja kuljettimia saavuttaaksesi tavoitteen nopeammin
valmiiksi.<br><br>Vihje: Pidä <strong>SHIFT</strong> pohjassa
laittaaksesi useampia poimijoita ja käytä <strong>R</strong>
kääntääksesi niitä."
2_1_place_cutter: "Nyt aseta <strong>Leikkuri</strong> leikataksesi ympyrä
kääntääksesi niitä.
2_1_place_cutter: >-
Nyt aseta <strong>Leikkuri</strong> leikataksesi ympyrä
puoliksi!<br><br> PS: Leikkuri aina leikkaa <strong>ylhäältä alaspäin</strong>
riippumatta sen asennosta."
riippumatta sen asennosta.
2_2_place_trash: Leikkuri voi <strong>tukkeutua</strong>!<br><br> Käytä
<strong>roskakoria</strong> hävittääksesi (vielä!) tarpeeton jäte.
2_3_more_cutters: "Hienoa! Lisää <strong>kaksi leikkuria</strong> nopeuttaaksesi
2_3_more_cutters: >-
Hienoa! Lisää <strong>kaksi leikkuria</strong> nopeuttaaksesi
hidasta prosessia!<br><br> PS: Käytä <strong>pikanäppäimiä 0-9</strong>
valitaksesi rakennuksen nopeammin!"
3_1_rectangles: "Poimitaanpa nyt neliöitä! <strong>Rakenna 4
valitaksesi rakennuksen nopeammin!
3_1_rectangles: >-
Poimitaanpa nyt neliöitä! <strong>Rakenna 4
poimijaa</strong> ja yhdistä ne keskusrakennukseen.<br><br> PS:
Pidä <strong>SHIFT</strong> painettuna, kun raahaat kuljetinta
aktivoidaksesi kuljetinsuunnittelijan!"
aktivoidaksesi kuljetinsuunnittelijan!
21_1_place_quad_painter: Aseta <strong>nelimaalain</strong> ja hanki
<strong>ympyröitä</strong>, <strong>valkoista</strong> ja
<strong>punaista</strong> väriä!
@ -333,9 +339,10 @@ ingame:
<strong>E</strong>!<br><br> Sitten <strong>yhdistä kaikki neljä tuloa</strong> maalaimeen johdoilla!
21_3_place_button: MahtaVATA! Aseta nyt <strong>kytkin</strong> ja yhdistä
se johdoilla!
21_4_press_button: "Paina kytkintä <strong>lähettääksesi tosi-
21_4_press_button: >-
Paina kytkintä <strong>lähettääksesi tosi-
signaalin</strong> ja aktivoidaksesi maalaimen.<br><br> PS: Kaikkia
tuloja ei tarvitse kytkeä! Kokeile vaikka vain kahta."
tuloja ei tarvitse kytkeä! Kokeile vaikka vain kahta.
connectedMiners:
one_miner: 1 poimija
n_miners: <amount> poimijaa
@ -587,10 +594,11 @@ storyRewards:
desc: Avasit <strong>Kääntäjän</strong>! Se kääntää muotoja myötäpäivään 90 astetta.
reward_painter:
title: Värjäys
desc: "Avasit <strong>Maalaimen</strong> - Poimi joitain värialueita
desc: >-
Avasit <strong>Maalaimen</strong> - Poimi joitain värialueita
(Samoin kuin muotoja) ja yhdistä se muotoon maalaimen
avulla!<br><br>PS: Jos olet värisokea, asetuksissa on <strong> tila
värisokeille</strong>!"
värisokeille</strong>!
reward_mixer:
title: Värin Sekoitus
desc: Avasit <strong>Värinsekoittajan</strong> - Yhdistä kaksi väriä
@ -617,10 +625,11 @@ storyRewards:
<strong>painamalla 'T' vaihtaaksesi sen versioita</strong>!
reward_miner_chainable:
title: Sarjapoimija
desc: "Avasit juuri <strong>Sarjapoimijan</strong>! Se voi
desc: >-
Avasit juuri <strong>Sarjapoimijan</strong>! Se voi
<strong>siirtää resurssejaan</strong> muihin poimijoihin, joten
voit hankkia resursseja tehokkaammin!<br><br> PS: Vanha
poimija on nyt korvattu työkalupalkissa!"
poimija on nyt korvattu työkalupalkissa!
reward_underground_belt_tier_2:
title: Tunneli Taso II
desc: Avasit uuden version <strong>Tunnelista</strong> - Siinä on <strong>pidempi
@ -657,9 +666,10 @@ storyRewards:
jotta sinulla on varaa siihen! (Ne mitkä juuri toimitit).
no_reward:
title: Seuraava taso
desc: "Et saanut palkintoa tältä tasolta, mutta seuraavalta tasolta saat! <br><br> PS: Parempi
desc: >-
Et saanut palkintoa tältä tasolta, mutta seuraavalta tasolta saat! <br><br> PS: Parempi
olla tuhoamatta vanhoja tehtaita - Tarvitset <strong>kaikkia</strong>
muotoja myöhemmin <strong>avataksesi päivityksiä</strong>!"
muotoja myöhemmin <strong>avataksesi päivityksiä</strong>!
no_reward_freeplay:
title: Seuraava taso
desc: Onnittelut! Muuten, lisää sisältöä on suunniteltu täysversioon!
@ -682,8 +692,9 @@ storyRewards:
kääntää muotoa 180 astetta (Ylläripylläri! :D)
reward_display:
title: Näyttö
desc: "Avasit juuri <strong>Näytön</strong> - Yhdistä signaali näyttöön
Johto-tasolla visualisoidaksesi sen<br><br> PS: Huomasitko, että kuljetinanturi ja varasto näyttävät viimeisimmän esineen? Yritäpä saada se näkyviin näytölle!"
desc: >-
Avasit juuri <strong>Näytön</strong> - Yhdistä signaali näyttöön
Johto-tasolla visualisoidaksesi sen<br><br> PS: Huomasitko, että kuljetinanturi ja varasto näyttävät viimeisimmän esineen? Yritäpä saada se näkyviin näytölle!
reward_constant_signal:
title: Jatkuva Signaali
desc: Avasit <strong>Jatkuvan Signaalin</strong> laitteen johtotasolla!
@ -708,12 +719,13 @@ storyRewards:
tavallisesti.<br><br> Mitä valitsetkin, muista pitää hauskaa!
reward_wires_painter_and_levers:
title: Johdot & Nelimaalain
desc: "Avasit juuri <strong>Johtotason</strong>: Se on erillinen
desc: >-
Avasit juuri <strong>Johtotason</strong>: Se on erillinen
taso tavallisen tason päällä ja sieltä löytyy useita uusia
mekaniikkoja!<br><br> Aluksi avasin sinulle <strong>Nelimaalaimen</strong>
- Yhdistä johtotasolla lokerot, joihin haluat maalia<br><br>
Vaihtaaksesi johtotasolle, paina <strong>E</strong>. <br><br>
PS: <strong>Aktivoi vinkit</strong> asetuksissa nähdäksesi Johdot-tutoriaalin!"
PS: <strong>Aktivoi vinkit</strong> asetuksissa nähdäksesi Johdot-tutoriaalin!
reward_filter:
title: Esinesuodatin
desc: Olet avannut <strong>Esinesuodattimen</strong>! Se lähettää esineet
@ -878,8 +890,9 @@ settings:
rangeSliderPercentage: <amount> %
keybindings:
title: Pikanäppäimet
hint: "Vinkki: Muista käyttää CTRL, SHIFT ja ALT! Ne ottavat käyttöön erilaisia
sijoitteluvaihtoehtoja."
hint: >-
Vinkki: Muista käyttää CTRL, SHIFT ja ALT! Ne ottavat käyttöön erilaisia
sijoitteluvaihtoehtoja.
resetKeybindings: Nollaa pikanäppäimet
categoryLabels:
general: Sovellus

@ -1 +1 @@
1.2.2
1.3.0
Loading…
Cancel
Save