diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40053d64..b7a362ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,7 @@ jobs: run: | cd gulp yarn gulp translations.fullBuild + yarn gulp localConfig.findOrCreate cd .. yarn tslint diff --git a/src/js/game/achievement_proxy.js b/src/js/game/achievement_proxy.js index 2ae9ee52..ed05b700 100644 --- a/src/js/game/achievement_proxy.js +++ b/src/js/game/achievement_proxy.js @@ -46,7 +46,7 @@ export class AchievementProxy { } initialize() { - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.darkMode); + this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.darkMode, null); if (this.has(ACHIEVEMENTS.mam)) { this.root.signals.entityAdded.add(this.onMamFailure, this); @@ -136,7 +136,7 @@ export class AchievementProxy { this.root.signals.entityDestroyed.add(this.onMamFailure, this); } - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.mam); + this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.mam, null); // reset on every level this.root.savegame.currentData.stats.failedMam = false; diff --git a/src/js/game/blueprint.js b/src/js/game/blueprint.js index 9512adc5..3aaef831 100644 --- a/src/js/game/blueprint.js +++ b/src/js/game/blueprint.js @@ -165,8 +165,10 @@ export class Blueprint { } root.signals.bulkAchievementCheck.dispatch( - ACHIEVEMENTS.placeBlueprint, count, - ACHIEVEMENTS.placeBp1000, count + ACHIEVEMENTS.placeBlueprint, + count, + ACHIEVEMENTS.placeBp1000, + count ); return count !== 0; diff --git a/src/js/game/buildings/trash.js b/src/js/game/buildings/trash.js index c8f11f9b..0ad5bfd7 100644 --- a/src/js/game/buildings/trash.js +++ b/src/js/game/buildings/trash.js @@ -43,7 +43,7 @@ export class MetaTrashBuilding extends MetaBuilding { return; } - const itemProcessor = entity.components.ItemProcessor + const itemProcessor = entity.components.ItemProcessor; const tryTakeItem = itemProcessor.tryTakeItem.bind(itemProcessor); itemProcessor.tryTakeItem = () => { @@ -54,7 +54,7 @@ export class MetaTrashBuilding extends MetaBuilding { } return taken; - } + }; } /** diff --git a/src/js/game/root.js b/src/js/game/root.js index 15985fe2..82d1e49f 100644 --- a/src/js/game/root.js +++ b/src/js/game/root.js @@ -181,7 +181,7 @@ export class GameRoot { freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()), // Called with an achievement key and necessary args to validate it can be unlocked. - achievementCheck: /** @type {TypedSignal<[string, *]>} */ (new Signal()), + achievementCheck: /** @type {TypedSignal<[string, any]>} */ (new Signal()), bulkAchievementCheck: /** @type {TypedSignal<(string|any)[]>} */ (new Signal()), }; diff --git a/src/js/game/shape_definition_manager.js b/src/js/game/shape_definition_manager.js index 92fc9538..89203f1e 100644 --- a/src/js/game/shape_definition_manager.js +++ b/src/js/game/shape_definition_manager.js @@ -97,7 +97,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject { const rightSide = definition.cloneFilteredByQuadrants([2, 3]); const leftSide = definition.cloneFilteredByQuadrants([0, 1]); - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.cutShape); + this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.cutShape, null); return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key] = [ this.registerOrReturnHandle(rightSide), @@ -140,7 +140,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject { const rotated = definition.cloneRotateCW(); - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.rotateShape); + this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.rotateShape, null); return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( rotated @@ -195,7 +195,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject { return /** @type {ShapeDefinition} */ (this.operationCache[key]); } - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.stackShape); + this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.stackShape, null); const stacked = lowerDefinition.cloneAndStackWith(upperDefinition); return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( @@ -215,7 +215,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject { return /** @type {ShapeDefinition} */ (this.operationCache[key]); } - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.paintShape); + this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.paintShape, null); const colorized = definition.cloneAndPaintWith(color); return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( diff --git a/src/js/globals.d.ts b/src/js/globals.d.ts index 4a903437..d1fb5305 100644 --- a/src/js/globals.d.ts +++ b/src/js/globals.d.ts @@ -19,7 +19,7 @@ declare const G_BUILD_VERSION: string; declare const G_ALL_UI_IMAGES: Array; declare const G_IS_RELEASE: boolean; -declare const G_CHINA_VERSION : boolean; +declare const G_CHINA_VERSION: boolean; // Polyfills declare interface String { diff --git a/src/js/platform/achievement_provider.js b/src/js/platform/achievement_provider.js index 5d7f94bc..31973880 100644 --- a/src/js/platform/achievement_provider.js +++ b/src/js/platform/achievement_provider.js @@ -2,11 +2,11 @@ import { Application } from "../application"; 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 { ShapeDefinition } from "../game/shape_definition"; import { ShapeItem } from "../game/items/shape_item"; import { globalConfig } from "../core/config"; @@ -171,14 +171,8 @@ export class AchievementCollection { isValid: this.isBelt500TilesValid, signal: "entityAdded", }); - this.add(ACHIEVEMENTS.blueprint100k, { - isValid: this.isBlueprint100kValid, - signal: "shapeDelivered", - }); - this.add(ACHIEVEMENTS.blueprint1m, { - isValid: this.isBlueprint1mValid, - signal: "shapeDelivered", - }); + this.add(ACHIEVEMENTS.blueprint100k, this.createBlueprintOptions(100000)); + this.add(ACHIEVEMENTS.blueprint1m, this.createBlueprintOptions(1000000)); this.add(ACHIEVEMENTS.completeLvl26, this.createLevelOptions(26)); this.add(ACHIEVEMENTS.cutShape); this.add(ACHIEVEMENTS.darkMode, { @@ -243,10 +237,12 @@ export class AchievementCollection { }); this.add(ACHIEVEMENTS.stackShape); this.add(ACHIEVEMENTS.store100Unique, { + init: this.initStore100Unique, isValid: this.isStore100UniqueValid, signal: "shapeDelivered", }); this.add(ACHIEVEMENTS.storeShape, { + init: this.initStoreShape, isValid: this.isStoreShapeValid, }); this.add(ACHIEVEMENTS.throughputBp25, this.createRateOptions(SHAPE_BP, 25)); @@ -271,14 +267,14 @@ export class AchievementCollection { this.root.signals.bulkAchievementCheck.add(this.bulkUnlock, this); for (let [key, achievement] of this.map.entries()) { - if (achievement.init) { - achievement.init(); - } - if (achievement.signal) { achievement.receiver = this.unlock.bind(this, key); this.root.signals[achievement.signal].add(achievement.receiver); } + + if (achievement.init) { + achievement.init(); + } } if (!this.hasDefaultReceivers()) { @@ -326,7 +322,7 @@ export class AchievementCollection { /** * @param {string} key - Maps to an Achievement - * @param {?*} data - Data received from signal dispatches for validation + * @param {any} data - Data received from signal dispatches for validation */ unlock(key, data) { if (!this.map.has(key)) { @@ -419,8 +415,18 @@ export class AchievementCollection { return item.getItemType() === ITEM_SHAPE && item.definition.getHash() === shape; } + createBlueprintOptions(count) { + return { + init: ({ key }) => this.unlock(key, ShapeDefinition.fromShortKey(SHAPE_BP)), + isValid: definition => + definition.cachedHash === SHAPE_BP && this.root.hubGoals.storedShapes[SHAPE_BP] >= count, + signal: "shapeDelivered", + }; + } + createLevelOptions(level) { return { + init: ({ key }) => this.unlock(key, this.root.hubGoals.level), isValid: currentLevel => currentLevel >= level, signal: "storyGoalCompleted", }; @@ -463,6 +469,7 @@ export class AchievementCollection { createUpgradeOptions(tier) { return { + init: ({ key }) => this.unlock(key, null), isValid: () => this.hasAllUpgradesAtLeastAtTier(tier), signal: "upgradePurchased", }; @@ -473,16 +480,6 @@ export class AchievementCollection { return entity.components.Belt && entity.components.Belt.assignedPath.totalLength >= 500; } - /** @param {ShapeDefinition} definition @returns {boolean} */ - isBlueprint100kValid(definition) { - 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; - } - /** @returns {boolean} */ isDarkModeValid() { return this.root.app.settings.currentData.settings.theme === DARK_MODE; @@ -523,7 +520,7 @@ export class AchievementCollection { return this.root.hubGoals.level < 18 && this.isShape(item, SHAPE_LOGO); } - /** @params {number} level @returns {boolean} */ + /** @returns {boolean} */ isMamValid() { return this.root.hubGoals.level > 27 && !this.root.savegame.currentData.stats.failedMam; } @@ -595,11 +592,21 @@ export class AchievementCollection { return item.getItemType() === ITEM_SHAPE && item.definition.layers.length === 4; } + /** @param {Achievement} achievement */ + initStore100Unique({ key }) { + this.unlock(key, null); + } + /** @returns {boolean} */ isStore100UniqueValid() { return Object.keys(this.root.hubGoals.storedShapes).length >= 100; } + /** @param {Achievement} achievement */ + initStoreShape({ key }) { + this.unlock(key, null); + } + /** @returns {boolean} */ isStoreShapeValid() { const entities = this.root.systemMgr.systems.storage.allEntities; @@ -617,15 +624,17 @@ export class AchievementCollection { return false; } - initTrash1000() { + /** @param {Achievement} achievement */ + initTrash1000({ key }) { if (Number(this.root.savegame.currentData.stats.trashedCount)) { + this.unlock(key, 0); return; } this.root.savegame.currentData.stats.trashedCount = 0; } - /** @params {number} count @returns {boolean} */ + /** @param {number} count @returns {boolean} */ isTrash1000Valid(count) { this.root.savegame.currentData.stats.trashedCount += count; diff --git a/src/js/platform/electron/steam_achievement_provider.js b/src/js/platform/electron/steam_achievement_provider.js index c78453cb..f99205e6 100644 --- a/src/js/platform/electron/steam_achievement_provider.js +++ b/src/js/platform/electron/steam_achievement_provider.js @@ -5,11 +5,7 @@ import { GameRoot } from "../../game/root"; import { createLogger } from "../../core/logging"; import { getIPCRenderer } from "../../core/utils"; -import { - ACHIEVEMENTS, - AchievementCollection, - AchievementProviderInterface -} from "../achievement_provider"; +import { ACHIEVEMENTS, AchievementCollection, AchievementProviderInterface } from "../achievement_provider"; const logger = createLogger("achievements/steam"); @@ -111,16 +107,15 @@ export class SteamAchievementProvider extends AchievementProviderInterface { this.ipc = getIPCRenderer(); - return this.ipc.invoke("steam:is-initialized") - .then(initialized => { - this.initialized = initialized; + return this.ipc.invoke("steam:is-initialized").then(initialized => { + this.initialized = initialized; - if (!this.initialized) { - logger.warn("Steam failed to intialize. Achievements won't sync."); - } else { - logger.log("Steam achievement provider initialized"); - } - }); + if (!this.initialized) { + logger.warn("Steam failed to intialize. Achievements won't sync."); + } else { + logger.log("Steam achievement provider initialized"); + } + }); } /** diff --git a/src/js/platform/electron/wrapper.js b/src/js/platform/electron/wrapper.js index c807315f..501b7258 100644 --- a/src/js/platform/electron/wrapper.js +++ b/src/js/platform/electron/wrapper.js @@ -23,8 +23,9 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser { this.app.storage = new StorageImplElectron(this); this.app.achievementProvider = new SteamAchievementProvider(this.app); - return this.initializeAchievementProvider() - .then(() => PlatformWrapperInterface.prototype.initialize.call(this)); + return this.initializeAchievementProvider().then(() => + PlatformWrapperInterface.prototype.initialize.call(this) + ); } steamOverlayFixRedrawCanvas() { @@ -58,12 +59,11 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser { } initializeAchievementProvider() { - 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); + }); } getSupportsFullscreen() {