diff --git a/src/js/game/hud/parts/achievements.js b/src/js/game/hud/parts/achievements.js index a2d07f55..0896a512 100644 --- a/src/js/game/hud/parts/achievements.js +++ b/src/js/game/hud/parts/achievements.js @@ -1,10 +1,6 @@ import { InputReceiver } from "../../../core/input_receiver"; import { makeDiv } from "../../../core/utils"; -import { - ACHIEVEMENTS, - enum_achievement_mappings, - HIDDEN_ACHIEVEMENTS, -} from "../../../platform/achievement_provider"; +import { ACHIEVEMENTS, HIDDEN_ACHIEVEMENTS } from "../../../platform/achievement_provider"; import { T } from "../../../translations"; import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; import { BaseHUDPart } from "../base_hud_part"; @@ -63,10 +59,7 @@ export class HUDAchievements extends BaseHUDPart { signals.ok.add(() => { for (const achievementKey in ACHIEVEMENTS) { if (!this.root.achievementProxy.provider.collection.map.has(achievementKey)) - this.root.achievementProxy.provider.collection.lock( - achievementKey, - enum_achievement_mappings[ACHIEVEMENTS[achievementKey]] - ); + this.root.achievementProxy.provider.collection.lock(ACHIEVEMENTS[achievementKey]); } }); }); @@ -105,10 +98,7 @@ export class HUDAchievements extends BaseHUDPart { handle.elem.appendChild(handle.resetButton); this.trackClicks(handle.resetButton, () => { - this.root.achievementProxy.provider.collection.lock( - achievementKey, - enum_achievement_mappings[ACHIEVEMENTS[achievementKey]] - ); + this.root.achievementProxy.provider.collection.lock(ACHIEVEMENTS[achievementKey]); }); // Assign handle diff --git a/src/js/platform/achievement_provider.js b/src/js/platform/achievement_provider.js index c01c219a..9e84f413 100644 --- a/src/js/platform/achievement_provider.js +++ b/src/js/platform/achievement_provider.js @@ -69,214 +69,6 @@ export const HIDDEN_ACHIEVEMENTS = [ ACHIEVEMENTS.oldLevel17, ]; -export const enum_achievement_mappings = { - [ACHIEVEMENTS.belt500Tiles]: function () { - return { - isValid: this.isBelt500TilesValid, - signal: "entityAdded", - }; - }, - [ACHIEVEMENTS.blueprint100k]: function () { - // @ts-ignore - return this.createBlueprintOptions(100000); - }, - [ACHIEVEMENTS.blueprint1m]: function () { - // @ts-ignore - return this.createBlueprintOptions(1000000); - }, - [ACHIEVEMENTS.completeLvl26]: function () { - // @ts-ignore - return this.createLevelOptions(26); - }, - [ACHIEVEMENTS.cutShape]: function () { - return {}; - }, - [ACHIEVEMENTS.darkMode]: function () { - return { - isValid: this.isDarkModeValid, - }; - }, - [ACHIEVEMENTS.destroy1000]: function () { - return { - isValid: this.isDestroy1000Valid, - }; - }, - [ACHIEVEMENTS.irrelevantShape]: function () { - return { - isValid: this.isIrrelevantShapeValid, - signal: "shapeDelivered", - }; - }, - [ACHIEVEMENTS.level100]: function () { - // @ts-ignore - return this.createLevelOptions(100); - }, - [ACHIEVEMENTS.level50]: function () { - // @ts-ignore - return this.createLevelOptions(50); - }, - [ACHIEVEMENTS.logoBefore18]: function () { - return { - isValid: this.isLogoBefore18Valid, - signal: "itemProduced", - }; - }, - [ACHIEVEMENTS.mam]: function () { - return { - isValid: this.isMamValid, - }; - }, - [ACHIEVEMENTS.mapMarkers15]: function () { - return { - isValid: this.isMapMarkers15Valid, - }; - }, - [ACHIEVEMENTS.noBeltUpgradesUntilBp]: function () { - return { - isValid: this.isNoBeltUpgradesUntilBpValid, - signal: "storyGoalCompleted", - }; - }, - [ACHIEVEMENTS.noInverseRotater]: function () { - return { - init: this.initNoInverseRotater, - isValid: this.isNoInverseRotaterValid, - signal: "storyGoalCompleted", - }; - }, - [ACHIEVEMENTS.oldLevel17]: function () { - // @ts-ignore - return this.createShapeOptions(SHAPE_OLD_LEVEL_17); - }, - [ACHIEVEMENTS.openWires]: function () { - return { - isValid: this.isOpenWiresValid, - signal: "editModeChanged", - }; - }, - [ACHIEVEMENTS.paintShape]: function () { - return {}; - }, - [ACHIEVEMENTS.place5000Wires]: function () { - return { - isValid: this.isPlace5000WiresValid, - }; - }, - [ACHIEVEMENTS.placeBlueprint]: function () { - return { - isValid: this.isPlaceBlueprintValid, - }; - }, - [ACHIEVEMENTS.placeBp1000]: function () { - return { - isValid: this.isPlaceBp1000Valid, - }; - }, - [ACHIEVEMENTS.play1h]: function () { - // @ts-ignore - return this.createTimeOptions(HOUR_1); - }, - [ACHIEVEMENTS.play10h]: function () { - // @ts-ignore - return this.createTimeOptions(HOUR_10); - }, - [ACHIEVEMENTS.play20h]: function () { - // @ts-ignore - return this.createTimeOptions(HOUR_20); - }, - [ACHIEVEMENTS.produceLogo]: function () { - // @ts-ignore - return this.createShapeOptions(SHAPE_LOGO); - }, - [ACHIEVEMENTS.produceRocket]: function () { - // @ts-ignore - return this.createShapeOptions(SHAPE_ROCKET); - }, - [ACHIEVEMENTS.produceMsLogo]: function () { - // @ts-ignore - return this.createShapeOptions(SHAPE_MS_LOGO); - }, - [ACHIEVEMENTS.rotateShape]: function () { - return {}; - }, - [ACHIEVEMENTS.speedrunBp30]: function () { - // @ts-ignore - return this.createSpeedOptions(12, MINUTE_30); - }, - [ACHIEVEMENTS.speedrunBp60]: function () { - // @ts-ignore - return this.createSpeedOptions(12, MINUTE_60); - }, - [ACHIEVEMENTS.speedrunBp120]: function () { - // @ts-ignore - return this.createSpeedOptions(12, MINUTE_120); - }, - [ACHIEVEMENTS.stack4Layers]: function () { - return { - isValid: this.isStack4LayersValid, - signal: "itemProduced", - }; - }, - [ACHIEVEMENTS.stackShape]: function () { - return {}; - }, - [ACHIEVEMENTS.store100Unique]: function () { - return { - init: this.initStore100Unique, - isValid: this.isStore100UniqueValid, - signal: "shapeDelivered", - }; - }, - [ACHIEVEMENTS.storeShape]: function () { - return { - init: this.initStoreShape, - isValid: this.isStoreShapeValid, - }; - }, - [ACHIEVEMENTS.throughputBp25]: function () { - // @ts-ignore - return this.createRateOptions(SHAPE_BP, 25); - }, - [ACHIEVEMENTS.throughputBp50]: function () { - // @ts-ignore - return this.createRateOptions(SHAPE_BP, 50); - }, - [ACHIEVEMENTS.throughputLogo25]: function () { - // @ts-ignore - return this.createRateOptions(SHAPE_LOGO, 25); - }, - [ACHIEVEMENTS.throughputLogo50]: function () { - // @ts-ignore - return this.createRateOptions(SHAPE_LOGO, 50); - }, - [ACHIEVEMENTS.throughputRocket10]: function () { - // @ts-ignore - return this.createRateOptions(SHAPE_ROCKET, 25); - }, - [ACHIEVEMENTS.throughputRocket20]: function () { - // @ts-ignore - return this.createRateOptions(SHAPE_ROCKET, 50); - }, - [ACHIEVEMENTS.trash1000]: function () { - return { - init: this.initTrash1000, - isValid: this.isTrash1000Valid, - }; - }, - [ACHIEVEMENTS.unlockWires]: function () { - // @ts-ignore - return this.createLevelOptions(20); - }, - [ACHIEVEMENTS.upgradesTier5]: function () { - // @ts-ignore - return this.createUpgradeOptions(5); - }, - [ACHIEVEMENTS.upgradesTier8]: function () { - // @ts-ignore - return this.createUpgradeOptions(8); - }, -}; - /** @type {keyof typeof THEMES} */ const DARK_MODE = "dark"; @@ -399,10 +191,107 @@ export class AchievementCollection { this.deactivate = deactivate; for (const key in ACHIEVEMENTS) { - this.add(ACHIEVEMENTS[key], enum_achievement_mappings[ACHIEVEMENTS[key]].bind(this)()); + this.add(ACHIEVEMENTS[key], this.getAchievementOptions(ACHIEVEMENTS[key])); } } + getAchievementOptions(key) { + const enum_achievement_mappings = { + [ACHIEVEMENTS.belt500Tiles]: { + isValid: this.isBelt500TilesValid, + signal: "entityAdded", + }, + [ACHIEVEMENTS.blueprint100k]: this.createBlueprintOptions(100000), + [ACHIEVEMENTS.blueprint1m]: this.createBlueprintOptions(1000000), + [ACHIEVEMENTS.completeLvl26]: this.createLevelOptions(26), + [ACHIEVEMENTS.cutShape]: {}, + [ACHIEVEMENTS.darkMode]: { + isValid: this.isDarkModeValid, + }, + [ACHIEVEMENTS.destroy1000]: { + isValid: this.isDestroy1000Valid, + }, + [ACHIEVEMENTS.irrelevantShape]: { + isValid: this.isIrrelevantShapeValid, + signal: "shapeDelivered", + }, + [ACHIEVEMENTS.level100]: this.createLevelOptions(100), + [ACHIEVEMENTS.level50]: this.createLevelOptions(50), + [ACHIEVEMENTS.logoBefore18]: { + isValid: this.isLogoBefore18Valid, + signal: "itemProduced", + }, + [ACHIEVEMENTS.mam]: { + isValid: this.isMamValid, + }, + [ACHIEVEMENTS.mapMarkers15]: { + isValid: this.isMapMarkers15Valid, + }, + [ACHIEVEMENTS.noBeltUpgradesUntilBp]: { + isValid: this.isNoBeltUpgradesUntilBpValid, + signal: "storyGoalCompleted", + }, + [ACHIEVEMENTS.noInverseRotater]: { + init: this.initNoInverseRotater, + isValid: this.isNoInverseRotaterValid, + signal: "storyGoalCompleted", + }, + [ACHIEVEMENTS.oldLevel17]: this.createShapeOptions(SHAPE_OLD_LEVEL_17), + [ACHIEVEMENTS.openWires]: { + isValid: this.isOpenWiresValid, + signal: "editModeChanged", + }, + [ACHIEVEMENTS.paintShape]: {}, + [ACHIEVEMENTS.place5000Wires]: { + isValid: this.isPlace5000WiresValid, + }, + [ACHIEVEMENTS.placeBlueprint]: { + isValid: this.isPlaceBlueprintValid, + }, + [ACHIEVEMENTS.placeBp1000]: { + isValid: this.isPlaceBp1000Valid, + }, + [ACHIEVEMENTS.play1h]: this.createTimeOptions(HOUR_1), + [ACHIEVEMENTS.play10h]: this.createTimeOptions(HOUR_10), + [ACHIEVEMENTS.play20h]: this.createTimeOptions(HOUR_20), + [ACHIEVEMENTS.produceLogo]: this.createShapeOptions(SHAPE_LOGO), + [ACHIEVEMENTS.produceRocket]: this.createShapeOptions(SHAPE_ROCKET), + [ACHIEVEMENTS.produceMsLogo]: this.createShapeOptions(SHAPE_MS_LOGO), + [ACHIEVEMENTS.rotateShape]: {}, + [ACHIEVEMENTS.speedrunBp30]: this.createSpeedOptions(12, MINUTE_30), + [ACHIEVEMENTS.speedrunBp60]: this.createSpeedOptions(12, MINUTE_60), + [ACHIEVEMENTS.speedrunBp120]: this.createSpeedOptions(12, MINUTE_120), + [ACHIEVEMENTS.stack4Layers]: { + isValid: this.isStack4LayersValid, + signal: "itemProduced", + }, + [ACHIEVEMENTS.stackShape]: {}, + [ACHIEVEMENTS.store100Unique]: { + init: this.initStore100Unique, + isValid: this.isStore100UniqueValid, + signal: "shapeDelivered", + }, + [ACHIEVEMENTS.storeShape]: { + init: this.initStoreShape, + isValid: this.isStoreShapeValid, + }, + [ACHIEVEMENTS.throughputBp25]: this.createRateOptions(SHAPE_BP, 25), + [ACHIEVEMENTS.throughputBp50]: this.createRateOptions(SHAPE_BP, 50), + [ACHIEVEMENTS.throughputLogo25]: this.createRateOptions(SHAPE_LOGO, 25), + [ACHIEVEMENTS.throughputLogo50]: this.createRateOptions(SHAPE_LOGO, 50), + [ACHIEVEMENTS.throughputRocket10]: this.createRateOptions(SHAPE_ROCKET, 25), + [ACHIEVEMENTS.throughputRocket20]: this.createRateOptions(SHAPE_ROCKET, 50), + [ACHIEVEMENTS.trash1000]: { + init: this.initTrash1000, + isValid: this.isTrash1000Valid, + }, + [ACHIEVEMENTS.unlockWires]: this.createLevelOptions(20), + [ACHIEVEMENTS.upgradesTier5]: this.createUpgradeOptions(5), + [ACHIEVEMENTS.upgradesTier8]: this.createUpgradeOptions(8), + }; + return enum_achievement_mappings[key]; + } + /** @param {GameRoot} root */ initialize(root) { this.root = root; @@ -433,7 +322,7 @@ export class AchievementCollection { * @param {function} [options.isValid] * @param {string} [options.signal] */ - add(key, options = {}) { + add(key, options = {}, init = false) { if (G_IS_DEV) { assert(ACHIEVEMENTS[key], "Achievement key not found: ", key); } @@ -455,6 +344,15 @@ export class AchievementCollection { } this.map.set(key, achievement); + + if (init) { + if (achievement.signal) { + achievement.receiver = this.unlock.bind(this, key, false); + this.root.signals[achievement.signal].add(achievement.receiver); + } + + if (achievement.init) achievement.init(); + } } bulkUnlock() { @@ -474,9 +372,7 @@ export class AchievementCollection { const achievement = this.map.get(key); - if (!force && !achievement.isValid(data)) { - return; - } + if (force !== true && !achievement.isValid(data)) return; achievement .unlock() @@ -492,8 +388,8 @@ export class AchievementCollection { /** * @param {string} key - Maps to an Achievement */ - lock(key, options) { - this.add(key, options); + lock(key) { + this.add(key, this.getAchievementOptions(key), true); this.deactivate(key); } @@ -621,7 +517,7 @@ export class AchievementCollection { createUpgradeOptions(tier) { return { - init: ({ key }) => this.unlock(key, null), + init: ({ key }) => this.unlock(key, false, null), isValid: () => this.hasAllUpgradesAtLeastAtTier(tier), signal: "upgradePurchased", }; @@ -746,7 +642,7 @@ export class AchievementCollection { /** @param {Achievement} achievement */ initStore100Unique({ key }) { - this.unlock(key, null); + this.unlock(key, false, null); } /** @returns {boolean} */ @@ -756,7 +652,7 @@ export class AchievementCollection { /** @param {Achievement} achievement */ initStoreShape({ key }) { - this.unlock(key, null); + this.unlock(key, false, null); } /** @returns {boolean} */ diff --git a/src/js/states/achievements.js b/src/js/states/achievements.js index dc82d75f..07813332 100644 --- a/src/js/states/achievements.js +++ b/src/js/states/achievements.js @@ -1,10 +1,6 @@ import { TextualGameState } from "../core/textual_game_state"; import { makeDiv } from "../core/utils"; -import { - ACHIEVEMENTS, - enum_achievement_mappings, - HIDDEN_ACHIEVEMENTS, -} from "../platform/achievement_provider"; +import { ACHIEVEMENTS, HIDDEN_ACHIEVEMENTS } from "../platform/achievement_provider"; import { T } from "../translations"; export class AchievementsState extends TextualGameState { @@ -66,10 +62,7 @@ export class AchievementsState extends TextualGameState { signals.ok.add(() => { for (const achievementKey in ACHIEVEMENTS) { if (!this.app.achievementProvider.collection.map.has(achievementKey)) - this.app.achievementProvider.collection.lock( - achievementKey, - enum_achievement_mappings[ACHIEVEMENTS[achievementKey]] - ); + this.app.achievementProvider.collection.lock(ACHIEVEMENTS[achievementKey]); } }); }); @@ -108,10 +101,7 @@ export class AchievementsState extends TextualGameState { handle.elem.appendChild(handle.resetButton); this.trackClicks(handle.resetButton, () => { - this.app.achievementProvider.collection.lock( - achievementKey, - enum_achievement_mappings[ACHIEVEMENTS[achievementKey]] - ); + this.app.achievementProvider.collection.lock(ACHIEVEMENTS[achievementKey]); }); // Assign handle