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-plugin-replace": "^1.1.1",
"webpack-strip-block": "^0.2.0", "webpack-strip-block": "^0.2.0",
"whatwg-fetch": "^3.0.0", "whatwg-fetch": "^3.0.0",
"worker-loader": "^2.0.0" "worker-loader": "^2.0.0",
"yaml": "^1.10.0"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "^9.4.3", "autoprefixer": "^9.4.3",

@ -25,6 +25,7 @@ function gulptasksTranslations($, gulp) {
files files
.filter(name => name.endsWith(".yaml")) .filter(name => name.endsWith(".yaml"))
.forEach(fname => { .forEach(fname => {
console.log("Loading", fname);
const languageName = fname.replace(".yaml", ""); const languageName = fname.replace(".yaml", "");
const abspath = path.join(translationsSourceDir, fname); const abspath = path.join(translationsSourceDir, fname);
@ -40,39 +41,13 @@ function gulptasksTranslations($, gulp) {
${storePage.intro.replace(/\n/gi, "\n\n")} ${storePage.intro.replace(/\n/gi, "\n\n")}
[h2]${storePage.title_advantages}[/h2] [h2]${storePage.what_others_say}[/h2]
[list] [list]
${storePage.advantages [*] [i]${storePage.northernlion_comment}[/i] [b]- Northernlion, YouTube[/b]
.map(x => "[*] " + x.replace(/<b>/, "[b]").replace(/<\/b>/, "[/b]")) [*] [i]${storePage.notch_comment}[/i] [b]- Notch[/b]
.join("\n")} [*] [i]${storePage.steam_review_comment}[/i] [b]- Steam User[/b]
[/list] [/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")), { 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 "application_error";
@import "textual_game_state"; @import "textual_game_state";
@import "adinplay"; @import "adinplay";
@import "changelog_skins";
@import "states/preload"; @import "states/preload";
@import "states/main_menu"; @import "states/main_menu";
@ -56,8 +57,8 @@
@import "ingame_hud/cat_memes"; @import "ingame_hud/cat_memes";
// prettier-ignore // prettier-ignore
$elements: $elements:
// Base // Base
ingame_Canvas, ingame_Canvas,
ingame_VignetteOverlay, ingame_VignetteOverlay,
@ -119,11 +120,3 @@ body.uiHidden {
display: none !important; 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 { .updateLabel {
position: absolute; position: absolute;
transform: translateX(50%) rotate(-5deg); transform: translateX(50%) rotate(-5deg);
color: rgb(231, 78, 58); color: rgb(133, 58, 231);
@include Heading; @include Heading;
font-weight: bold; font-weight: bold;
@include S(right, 40px); @include S(right, 40px);

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

@ -1,10 +1,13 @@
export const CHANGELOG = [ export const CHANGELOG = [
{ {
version: "1.2.3", version: "1.3.0",
date: "unreleased", date: "12.03.2020",
skin: "achievements",
entries: [ entries: [
"There are now <strong>45 Steam Achievements!</strong>",
"Fixed constant signals being editable from the regular layer", "Fixed constant signals being editable from the regular layer",
"Fixed items still overlapping sometimes between buildings and belts", "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 ROTATER = "rotater";
const DEFAULT = "default"; const DEFAULT = "default";
const BELT = "belt";
const LEVEL_26 = 26;
export class AchievementProxy { export class AchievementProxy {
/** @param {GameRoot} root */ /** @param {GameRoot} root */
@ -22,7 +20,9 @@ export class AchievementProxy {
this.provider = this.root.app.achievementProvider; this.provider = this.root.app.achievementProvider;
this.disabled = true; this.disabled = true;
if (!this.provider.hasAchievements()) { if (G_IS_DEV && globalConfig.debug.testAchievements) {
// still enable the proxy
} else if (!this.provider.hasAchievements()) {
return; return;
} }
@ -34,7 +34,8 @@ export class AchievementProxy {
} }
onLoad() { onLoad() {
this.provider.onLoad(this.root) this.provider
.onLoad(this.root)
.then(() => { .then(() => {
this.disabled = false; this.disabled = false;
logger.log("Recieving achievement signals"); logger.log("Recieving achievement signals");
@ -50,6 +51,8 @@ export class AchievementProxy {
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.darkMode); this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.darkMode);
if (this.has(ACHIEVEMENTS.mam)) { 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); this.root.signals.storyGoalCompleted.add(this.onStoryGoalCompleted, this);
} }
@ -57,10 +60,6 @@ export class AchievementProxy {
this.root.signals.entityAdded.add(this.onEntityAdded, this); this.root.signals.entityAdded.add(this.onEntityAdded, this);
} }
if (this.has(ACHIEVEMENTS.noBeltUpgradesUntilBp)) {
this.root.signals.upgradePurchased.add(this.onUpgradePurchased, this);
}
this.startSlice(); this.startSlice();
} }
@ -73,27 +72,38 @@ export class AchievementProxy {
// Every other slice // Every other slice
if (this.sliceIteration % 2 === 0) { if (this.sliceIteration % 2 === 0) {
this.root.signals.bulkAchievementCheck.dispatch( this.root.signals.bulkAchievementCheck.dispatch(
ACHIEVEMENTS.throughputBp25, this.sliceTime, ACHIEVEMENTS.throughputBp25,
ACHIEVEMENTS.throughputBp50, this.sliceTime, this.sliceTime,
ACHIEVEMENTS.throughputLogo25, this.sliceTime, ACHIEVEMENTS.throughputBp50,
ACHIEVEMENTS.throughputLogo50, this.sliceTime, this.sliceTime,
ACHIEVEMENTS.throughputRocket10, this.sliceTime, ACHIEVEMENTS.throughputLogo25,
ACHIEVEMENTS.throughputRocket20, this.sliceTime this.sliceTime,
ACHIEVEMENTS.throughputLogo50,
this.sliceTime,
ACHIEVEMENTS.throughputRocket10,
this.sliceTime,
ACHIEVEMENTS.throughputRocket20,
this.sliceTime
); );
} }
// Every 3rd slice // Every 3rd slice
if (this.sliceIteration % 3 === 0) { if (this.sliceIteration % 3 === 0) {
this.root.signals.bulkAchievementCheck.dispatch( this.root.signals.bulkAchievementCheck.dispatch(
ACHIEVEMENTS.play1h, this.sliceTime, ACHIEVEMENTS.play1h,
ACHIEVEMENTS.play10h, this.sliceTime, this.sliceTime,
ACHIEVEMENTS.play20h, this.sliceTime ACHIEVEMENTS.play10h,
this.sliceTime,
ACHIEVEMENTS.play20h,
this.sliceTime
); );
} }
// Every 10th slice // Every 10th slice
if (this.sliceIteration % 10 === 0) { if (this.sliceIteration % 10 === 0) {
this.provider.collection.clean(); if (this.provider.collection) {
this.provider.collection.clean();
}
} }
if (this.sliceIteration === this.sliceIterationLimit) { if (this.sliceIteration === this.sliceIterationLimit) {
@ -118,6 +128,9 @@ export class AchievementProxy {
* @returns {boolean} * @returns {boolean}
*/ */
has(key) { has(key) {
if (!this.provider.collection) {
return false;
}
return this.provider.collection.map.has(key); return this.provider.collection.map.has(key);
} }
@ -127,7 +140,7 @@ export class AchievementProxy {
return; return;
} }
const building = getBuildingDataFromCode(entity.components.StaticMapEntity.code) const building = getBuildingDataFromCode(entity.components.StaticMapEntity.code);
if (building.metaInstance.id !== ROTATER) { if (building.metaInstance.id !== ROTATER) {
return; return;
@ -143,28 +156,18 @@ export class AchievementProxy {
/** @param {number} level */ /** @param {number} level */
onStoryGoalCompleted(level) { onStoryGoalCompleted(level) {
if (level === LEVEL_26) { if (level > 26) {
this.root.signals.entityAdded.add(this.onMamFailure, this); this.root.signals.entityAdded.add(this.onMamFailure, this);
this.root.signals.entityDestroyed.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() { onMamFailure() {
this.root.savegame.currentData.stats.failedMam = true; 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. // Called with an achievement key and necessary args to validate it can be unlocked.
achievementCheck: /** @type {TypedSignal<[string, *]>} */ (new Signal()), achievementCheck: /** @type {TypedSignal<[string, *]>} */ (new Signal()),
bulkAchievementCheck: /** @type {TypedSignal<[string, any]...>} */ (new Signal()), bulkAchievementCheck: /** @type {TypedSignal<(string|any)[]>} */ (new Signal()),
}; };
// RNG's // RNG's

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

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

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

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

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

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

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

@ -30,49 +30,20 @@ steamPage:
intro: >- intro: >-
Do you like automation games? Then you are in the right place! 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 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.
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 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!
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! 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 what_others_say: What people say about shapez.io
advantages:
- <b>12 New Levels</b> for a total of 26 levels nothernlion_comment: >-
- <b>18 New Buildings</b> for a fully automated factory! This game is great - I'm having a wonderful time playing, and time has flown by.
- <b>Unlimited Upgrade Tiers</b> for many hours of fun! notch_comment: >-
- <b>Wires Update</b> for an entirely new dimension! Oh crap. I really should sleep, but I think I just figured out how to make a computer in shapez.io
- <b>Dark Mode</b>! steam_review_comment: >-
- Unlimited Savegames 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.
- 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
global: global:
loading: Loading loading: Loading
@ -1207,11 +1178,6 @@ demo:
settingNotAvailable: Not available in the demo. settingNotAvailable: Not available in the demo.
achievements:
painting:
displayName: Painting
description: Paint a shape
tips: tips:
- The hub will accept any input, not just the current shape! - The hub will accept any input, not just the current shape!
- Make sure your factories are modular - it will pay out! - Make sure your factories are modular - it will pay out!

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

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