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

Add more achievements. Add bulk achievement check signal

This commit is contained in:
Greg Considine 2021-03-07 14:26:05 -05:00
parent dc2ae5504c
commit a722c3562d
11 changed files with 214 additions and 69 deletions

View File

@ -24,30 +24,27 @@ export class AchievementProxy {
} }
onLoad() { onLoad() {
if (this.provider.hasLoaded()) {
this.disabled = false;
return;
}
this.provider.onLoad(this.root) this.provider.onLoad(this.root)
.then(() => { .then(() => {
logger.log("Listening for unlocked achievements"); logger.log("Recieving achievement signals");
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.darkMode); this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.darkMode);
this.startSlice(); this.startSlice();
this.disabled = false; this.disabled = false;
}) })
.catch(err => { .catch(err => {
this.disabled = true; this.disabled = true;
logger.error("Ignoring achievement signals", err); logger.error("Ignoring achievement signals", err);
}) });
} }
startSlice() { startSlice() {
this.lastSlice = this.root.time.now(); this.lastSlice = this.root.time.now();
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.play1h, this.lastSlice); this.root.signals.bulkAchievementCheck.dispatch(
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.play10h, this.lastSlice); ACHIEVEMENTS.play1h, this.lastSlice,
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.play20h, this.lastSlice); ACHIEVEMENTS.play10h, this.lastSlice,
ACHIEVEMENTS.play20h, this.lastSlice
);
} }
update() { update() {

View File

@ -149,7 +149,7 @@ export class Blueprint {
*/ */
tryPlace(root, tile) { tryPlace(root, tile) {
return root.logic.performBulkOperation(() => { return root.logic.performBulkOperation(() => {
let anyPlaced = false; let count = 0;
for (let i = 0; i < this.entities.length; ++i) { for (let i = 0; i < this.entities.length; ++i) {
const entity = this.entities[i]; const entity = this.entities[i];
if (!root.logic.checkCanPlaceEntity(entity, tile)) { if (!root.logic.checkCanPlaceEntity(entity, tile)) {
@ -161,12 +161,15 @@ export class Blueprint {
root.logic.freeEntityAreaBeforeBuild(clone); root.logic.freeEntityAreaBeforeBuild(clone);
root.map.placeStaticEntity(clone); root.map.placeStaticEntity(clone);
root.entityMgr.registerEntity(clone); root.entityMgr.registerEntity(clone);
anyPlaced = true; count++;
} }
root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.placeBlueprint, anyPlaced); root.signals.bulkAchievementCheck.dispatch(
ACHIEVEMENTS.placeBlueprint, count,
ACHIEVEMENTS.placeBp1000, count
);
return anyPlaced; return count !== 0;
}); });
} }
} }

View File

@ -8,6 +8,7 @@ import { globalConfig } from "../../../core/config";
import { makeDiv, formatBigNumber, formatBigNumberFull } from "../../../core/utils"; import { makeDiv, formatBigNumber, formatBigNumberFull } from "../../../core/utils";
import { DynamicDomAttach } from "../dynamic_dom_attach"; import { DynamicDomAttach } from "../dynamic_dom_attach";
import { createLogger } from "../../../core/logging"; import { createLogger } from "../../../core/logging";
import { ACHIEVEMENTS } from "../../../platform/achievement_provider";
import { enumMouseButton } from "../../camera"; import { enumMouseButton } from "../../camera";
import { T } from "../../../translations"; import { T } from "../../../translations";
import { KEYMAPPINGS } from "../../key_action_mapper"; import { KEYMAPPINGS } from "../../key_action_mapper";
@ -100,6 +101,7 @@ export class HUDMassSelector extends BaseHUDPart {
*/ */
const mapUidToEntity = this.root.entityMgr.getFrozenUidSearchMap(); const mapUidToEntity = this.root.entityMgr.getFrozenUidSearchMap();
let count = 0;
this.root.logic.performBulkOperation(() => { this.root.logic.performBulkOperation(() => {
for (let i = 0; i < entityUids.length; ++i) { for (let i = 0; i < entityUids.length; ++i) {
const uid = entityUids[i]; const uid = entityUids[i];
@ -111,8 +113,12 @@ export class HUDMassSelector extends BaseHUDPart {
if (!this.root.logic.tryDeleteBuilding(entity)) { if (!this.root.logic.tryDeleteBuilding(entity)) {
logger.error("Error in mass delete, could not remove building"); logger.error("Error in mass delete, could not remove building");
} else {
count++;
} }
} }
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.destroy1000, count);
}); });
// Clear uids later // Clear uids later

View File

@ -15,6 +15,7 @@ import {
removeAllChildren, removeAllChildren,
} from "../../../core/utils"; } from "../../../core/utils";
import { Vector } from "../../../core/vector"; import { Vector } from "../../../core/vector";
import { ACHIEVEMENTS } from "../../../platform/achievement_provider";
import { T } from "../../../translations"; import { T } from "../../../translations";
import { BaseItem } from "../../base_item"; import { BaseItem } from "../../base_item";
import { MetaHubBuilding } from "../../buildings/hub"; import { MetaHubBuilding } from "../../buildings/hub";
@ -349,6 +350,10 @@ export class HUDWaypoints extends BaseHUDPart {
T.ingame.waypoints.creationSuccessNotification, T.ingame.waypoints.creationSuccessNotification,
enumNotificationType.success enumNotificationType.success
); );
this.root.signals.achievementCheck.dispatch(
ACHIEVEMENTS.mapMarkers15,
this.waypoints.length - 1 // Disregard HUB
);
// Re-render the list and thus add it // Re-render the list and thus add it
this.rerenderWaypointList(); this.rerenderWaypointList();

View File

@ -181,7 +181,8 @@ export class GameRoot {
freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()), freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
// 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.
achievementUnlocked: /** @type {TypedSignal<[string, ...*]>} */ (new Signal()), achievementCheck: /** @type {TypedSignal<[string, *]>} */ (new Signal()),
bulkAchievementCheck: /** @type {TypedSignal<[string, ...*]>} */ (new Signal()),
}; };
// RNG's // RNG's

View File

@ -97,7 +97,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
const rightSide = definition.cloneFilteredByQuadrants([2, 3]); const rightSide = definition.cloneFilteredByQuadrants([2, 3]);
const leftSide = definition.cloneFilteredByQuadrants([0, 1]); const leftSide = definition.cloneFilteredByQuadrants([0, 1]);
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.cutShape); this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.cutShape);
return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key] = [ return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key] = [
this.registerOrReturnHandle(rightSide), this.registerOrReturnHandle(rightSide),
@ -140,7 +140,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
const rotated = definition.cloneRotateCW(); const rotated = definition.cloneRotateCW();
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.rotateShape); this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.rotateShape);
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
rotated rotated
@ -195,7 +195,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
return /** @type {ShapeDefinition} */ (this.operationCache[key]); return /** @type {ShapeDefinition} */ (this.operationCache[key]);
} }
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.stackShape); this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.stackShape);
const stacked = lowerDefinition.cloneAndStackWith(upperDefinition); const stacked = lowerDefinition.cloneAndStackWith(upperDefinition);
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
@ -215,7 +215,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
return /** @type {ShapeDefinition} */ (this.operationCache[key]); return /** @type {ShapeDefinition} */ (this.operationCache[key]);
} }
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.paintShape); this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.paintShape);
const colorized = definition.cloneAndPaintWith(color); const colorized = definition.cloneAndPaintWith(color);
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
@ -252,11 +252,14 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
} }
this.shapeKeyToDefinition[id] = definition; this.shapeKeyToDefinition[id] = definition;
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.oldLevel17, definition); this.root.signals.bulkAchievementCheck.dispatch(
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.produceLogo, definition); ACHIEVEMENTS.logoBefore18, definition,
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.produceMsLogo, definition); ACHIEVEMENTS.oldLevel17, definition,
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.produceRocket, definition); ACHIEVEMENTS.produceLogo, definition,
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.stack4Layers, definition); ACHIEVEMENTS.produceMsLogo, definition,
ACHIEVEMENTS.produceRocket, definition,
ACHIEVEMENTS.stack4Layers, definition
);
// logger.log("Registered shape with key (2)", id); // logger.log("Registered shape with key (2)", id);
return definition; return definition;

View File

@ -276,7 +276,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
if (storageComp.canAcceptItem(item)) { if (storageComp.canAcceptItem(item)) {
storageComp.takeItem(item); storageComp.takeItem(item);
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.storeShape, storageComp); this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.storeShape, storageComp);
return true; return true;
} }

View File

@ -698,7 +698,7 @@ export class WireSystem extends GameSystemWithFilter {
return; return;
} }
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.place5000Wires, entity); this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.place5000Wires, entity);
// Invalidate affected area // Invalidate affected area
const originalRect = staticComp.getTileSpaceBounds(); const originalRect = staticComp.getTileSpaceBounds();

View File

@ -13,14 +13,18 @@ export const ACHIEVEMENTS = {
completeLvl26: "completeLvl26", completeLvl26: "completeLvl26",
cutShape: "cutShape", cutShape: "cutShape",
darkMode: "darkMode", darkMode: "darkMode",
destroy1000: "destroy1000",
irrelevantShape: "irrelevantShape", irrelevantShape: "irrelevantShape",
level100: "level100", level100: "level100",
level50: "level50", level50: "level50",
logoBefore18: "logoBefore18",
mapMarkers15: "mapMarkers15",
oldLevel17: "oldLevel17", oldLevel17: "oldLevel17",
openWires: "openWires", openWires: "openWires",
paintShape: "paintShape", paintShape: "paintShape",
place5000Wires: "place5000Wires", place5000Wires: "place5000Wires",
placeBlueprint: "placeBlueprint", placeBlueprint: "placeBlueprint",
placeBp1000: "placeBp1000",
play1h: "play1h", play1h: "play1h",
play10h: "play10h", play10h: "play10h",
play20h: "play20h", play20h: "play20h",
@ -33,6 +37,8 @@ export const ACHIEVEMENTS = {
store100Unique: "store100Unique", store100Unique: "store100Unique",
storeShape: "storeShape", storeShape: "storeShape",
unlockWires: "unlockWires", unlockWires: "unlockWires",
upgradesTier5: "upgradesTier5",
upgradesTier8: "upgradesTier8",
}; };
const DARK_MODE = "dark"; const DARK_MODE = "dark";
@ -149,14 +155,23 @@ export class AchievementCollection {
this.createAndSet(ACHIEVEMENTS.darkMode, { this.createAndSet(ACHIEVEMENTS.darkMode, {
isValid: this.isDarkModeValid, isValid: this.isDarkModeValid,
}); });
/* this.createAndSet(ACHIEVEMENTS.destroy1000, {
*this.createAndSet(ACHIEVEMENTS.irrelevantShape, { isValid: this.isDestroy1000Valid,
* isValid: this.isIrrelevantShapeValid, });
* signal: "shapeDelivered", this.createAndSet(ACHIEVEMENTS.irrelevantShape, {
*}); isValid: this.isIrrelevantShapeValid,
*/ signal: "shapeDelivered",
});
this.createAndSet(ACHIEVEMENTS.level100, this.createLevelOptions(100)); this.createAndSet(ACHIEVEMENTS.level100, this.createLevelOptions(100));
this.createAndSet(ACHIEVEMENTS.level50, this.createLevelOptions(50)); this.createAndSet(ACHIEVEMENTS.level50, this.createLevelOptions(50));
this.createAndSet(ACHIEVEMENTS.logoBefore18, {
isRelevant: this.isLogoBefore18Relevant,
isValid: this.isLogoBefore18Valid,
});
this.createAndSet(ACHIEVEMENTS.mapMarkers15, {
isRelevant: this.isMapMarkers15Relevant,
isValid: this.isMapMarkers15Valid,
});
this.createAndSet(ACHIEVEMENTS.oldLevel17, this.createShapeOptions(SHAPE_OLD_LEVEL_17)); this.createAndSet(ACHIEVEMENTS.oldLevel17, this.createShapeOptions(SHAPE_OLD_LEVEL_17));
this.createAndSet(ACHIEVEMENTS.openWires, { this.createAndSet(ACHIEVEMENTS.openWires, {
isValid: this.isOpenWiresValid, isValid: this.isOpenWiresValid,
@ -169,6 +184,9 @@ export class AchievementCollection {
this.createAndSet(ACHIEVEMENTS.placeBlueprint, { this.createAndSet(ACHIEVEMENTS.placeBlueprint, {
isValid: this.isPlaceBlueprintValid, isValid: this.isPlaceBlueprintValid,
}); });
this.createAndSet(ACHIEVEMENTS.placeBp1000, {
isValid: this.isPlaceBp1000Valid,
});
this.createAndSet(ACHIEVEMENTS.play1h, this.createTimeOptions(HOUR_1)); this.createAndSet(ACHIEVEMENTS.play1h, this.createTimeOptions(HOUR_1));
this.createAndSet(ACHIEVEMENTS.play10h, this.createTimeOptions(HOUR_10)); this.createAndSet(ACHIEVEMENTS.play10h, this.createTimeOptions(HOUR_10));
this.createAndSet(ACHIEVEMENTS.play20h, this.createTimeOptions(HOUR_20)); this.createAndSet(ACHIEVEMENTS.play20h, this.createTimeOptions(HOUR_20));
@ -190,12 +208,15 @@ export class AchievementCollection {
signal: "entityGotNewComponent", signal: "entityGotNewComponent",
}); });
this.createAndSet(ACHIEVEMENTS.unlockWires, this.createLevelOptions(20)); this.createAndSet(ACHIEVEMENTS.unlockWires, this.createLevelOptions(20));
this.createAndSet(ACHIEVEMENTS.upgradesTier5, this.createUpgradeOptions(5));
this.createAndSet(ACHIEVEMENTS.upgradesTier8, this.createUpgradeOptions(8));
} }
/** @param {GameRoot} root */ /** @param {GameRoot} root */
initialize(root) { initialize(root) {
this.root = root; this.root = root;
this.root.signals.achievementUnlocked.add(this.unlock, this); this.root.signals.achievementCheck.add(this.unlock, this);
this.root.signals.bulkAchievementCheck.add(this.bulkUnlock, this);
for (let [key, achievement] of this.map.entries()) { for (let [key, achievement] of this.map.entries()) {
if (!achievement.isRelevant()) { if (!achievement.isRelevant()) {
@ -210,7 +231,7 @@ export class AchievementCollection {
} }
if (!this.hasDefaultReceivers()) { if (!this.hasDefaultReceivers()) {
this.root.signals.achievementUnlocked.remove(this.unlock); this.root.signals.achievementCheck.remove(this.unlock);
} }
} }
@ -222,6 +243,10 @@ export class AchievementCollection {
* @param {string} [options.signal] * @param {string} [options.signal]
*/ */
createAndSet(key, options = {}) { createAndSet(key, options = {}) {
if (G_IS_DEV) {
assert(ACHIEVEMENTS[key], "Achievement key not found: ", key);
}
const achievement = new Achievement(key); const achievement = new Achievement(key);
achievement.activate = this.activate; achievement.activate = this.activate;
@ -241,6 +266,12 @@ export class AchievementCollection {
this.map.set(key, achievement); this.map.set(key, achievement);
} }
bulkUnlock() {
for (let i = 0; i < arguments.length; i += 2) {
this.unlock(arguments[i], arguments[i + 1]);
}
}
/** /**
* @param {string} key - Maps to an Achievement * @param {string} key - Maps to an Achievement
* @param {*[]} [arguments] - Additional arguments received from signal dispatches * @param {*[]} [arguments] - Additional arguments received from signal dispatches
@ -271,11 +302,11 @@ export class AchievementCollection {
* @param {?Error} err - Error is null if activation was successful * @param {?Error} err - Error is null if activation was successful
* @param {string} key - Maps to an Achievement * @param {string} key - Maps to an Achievement
*/ */
onActivate (err, key) { onActivate(err, key) {
this.remove(key); this.remove(key);
if (!this.hasDefaultReceivers()) { if (!this.hasDefaultReceivers()) {
this.root.signals.achievementUnlocked.remove(this.unlock); this.root.signals.achievementCheck.remove(this.unlock);
} }
} }
@ -304,25 +335,45 @@ export class AchievementCollection {
return false; return false;
} }
createLevelOptions (level) { hasAllUpgradesAtTier(tier) {
const upgrades = this.root.gameMode.getUpgrades();
for (let upgradeId in upgrades) {
if (this.root.hubGoals.getUpgradeLevel(upgradeId) < tier - 1) {
return false;
}
}
return true;
}
createLevelOptions(level) {
return { return {
isRelevant: () => this.root.hubGoals.level < level, isRelevant: () => this.root.hubGoals.level < level,
isValid: (key, currentLevel) => currentLevel === level, isValid: (key, currentLevel) => currentLevel === level,
signal: "storyGoalCompleted" signal: "storyGoalCompleted",
} };
} }
createShapeOptions (shape) { createShapeOptions(shape) {
return { return {
isValid: (key, definition) => definition.cachedHash === shape isValid: (key, definition) => definition.cachedHash === shape,
} };
} }
createTimeOptions (duration) { createTimeOptions(duration) {
return { return {
isRelevant: () => this.root.time.now() < duration, isRelevant: () => this.root.time.now() < duration,
isValid: () => this.root.time.now() >= duration, isValid: () => this.root.time.now() >= duration,
} };
}
createUpgradeOptions(tier) {
return {
isRelevant: () => !this.hasAllUpgradesAtTier(tier),
isValid: () => this.hasAllUpgradesAtTier(tier),
signal: "upgradePurchased",
};
} }
/** /**
@ -340,8 +391,10 @@ export class AchievementCollection {
* @returns {boolean} * @returns {boolean}
*/ */
isBlueprint100kValid(key, definition) { isBlueprint100kValid(key, definition) {
return definition.cachedHash === SHAPE_BLUEPRINT && return (
this.root.hubGoals.storedShapes[SHAPE_BLUEPRINT] >= 100000; definition.cachedHash === SHAPE_BLUEPRINT &&
this.root.hubGoals.storedShapes[SHAPE_BLUEPRINT] >= 100000
);
} }
/** /**
@ -350,8 +403,10 @@ export class AchievementCollection {
* @returns {boolean} * @returns {boolean}
*/ */
isBlueprint1mValid(key, definition) { isBlueprint1mValid(key, definition) {
return definition.cachedHash === SHAPE_BLUEPRINT && return (
this.root.hubGoals.storedShapes[SHAPE_BLUEPRINT] >= 1000000; definition.cachedHash === SHAPE_BLUEPRINT &&
this.root.hubGoals.storedShapes[SHAPE_BLUEPRINT] >= 1000000
);
} }
/** /**
@ -362,13 +417,66 @@ export class AchievementCollection {
return this.root.app.settings.currentData.settings.theme === DARK_MODE; return this.root.app.settings.currentData.settings.theme === DARK_MODE;
} }
/**
* @param {string} key
* @param {number} count - The count of selected entities destroyed
* @returns {boolean}
*/
isDestroy1000Valid(key, count) {
return count >= 1000;
}
/** /**
* @param {string} key * @param {string} key
* @param {ShapeDefinition} definition * @param {ShapeDefinition} definition
* @returns {boolean} * @returns {boolean}
*/ */
isIrrelevantShapeValid(key, definition) { isIrrelevantShapeValid(key, definition) {
//return definition.cachedHash !== this.hubGoals.currentGoal.definition.cachedHash if (definition.cachedHash === this.root.hubGoals.currentGoal.definition.cachedHash) {
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;
}
}
}
return true;
}
/** @returns {boolean} */
isLogoBefore18Relevant() {
return this.root.hubGoals.level < 18;
}
/**
* @param {string} key
* @param {ShapeDefinition} definition
* @returns {boolean}
*/
isLogoBefore18Valid(key, definition) {
return this.root.hubGoals.level < 18 && definition.cachedHash === SHAPE_LOGO;
}
/** @returns {boolean} */
isMapMarkers15Relevant() {
return this.root.hud.parts.waypoints.waypoints.length < 16; // 16 - HUB
}
/**
* @param {string} key
* @param {number} count - Count of map markers excluding HUB
* @returns {boolean}
*/
isMapMarkers15Valid(key, count) {
return count === 15;
} }
/** /**
@ -386,18 +494,29 @@ export class AchievementCollection {
* @returns {boolean} * @returns {boolean}
*/ */
isPlace5000WiresValid(key, entity) { isPlace5000WiresValid(key, entity) {
return entity.components.Wire && return (
entity.components.Wire &&
entity.registered && entity.registered &&
entity.root.entityMgr.componentToEntity.Wire.length === 5000; entity.root.entityMgr.componentToEntity.Wire.length === 5000
);
} }
/** /**
* @param {string} key * @param {string} key
* @param {boolean} anyPlaced * @param {number} count
* @returns {boolean} * @returns {boolean}
*/ */
isPlaceBlueprintValid(key, anyPlaced) { isPlaceBlueprintValid(key, count) {
return anyPlaced; return count != 0;
}
/**
* @param {string} key
* @param {number} count
* @returns {boolean}
*/
isPlaceBp1000Valid(key, count) {
return count >= 1000;
} }
/** /**

View File

@ -14,7 +14,7 @@ export class NoAchievementProvider extends AchievementProviderInterface {
} }
onLoad() { onLoad() {
return Promise.resolve(); return Promise.reject(new Error("No achievements to load"));
} }
activate() { activate() {

View File

@ -16,14 +16,18 @@ const ACHIEVEMENT_IDS = {
[ACHIEVEMENTS.completeLvl26]: "complete_lvl_26", [ACHIEVEMENTS.completeLvl26]: "complete_lvl_26",
[ACHIEVEMENTS.cutShape]: "cut_shape", [ACHIEVEMENTS.cutShape]: "cut_shape",
[ACHIEVEMENTS.darkMode]: "dark_mode", [ACHIEVEMENTS.darkMode]: "dark_mode",
[ACHIEVEMENTS.destroy1000]: "destroy_1000",
[ACHIEVEMENTS.irrelevantShape]: "irrelevant_shape", [ACHIEVEMENTS.irrelevantShape]: "irrelevant_shape",
[ACHIEVEMENTS.level100]: "level_100", [ACHIEVEMENTS.level100]: "level_100",
[ACHIEVEMENTS.level50]: "level_50", [ACHIEVEMENTS.level50]: "level_50",
[ACHIEVEMENTS.logoBefore18]: "logo_before_18",
[ACHIEVEMENTS.mapMarkers15]: "map_markers_15",
[ACHIEVEMENTS.openWires]: "open_wires", [ACHIEVEMENTS.openWires]: "open_wires",
[ACHIEVEMENTS.oldLevel17]: "old_level_17", [ACHIEVEMENTS.oldLevel17]: "old_level_17",
[ACHIEVEMENTS.paintShape]: "paint_shape", [ACHIEVEMENTS.paintShape]: "paint_shape",
[ACHIEVEMENTS.place5000Wires]: "place_5000_wires", [ACHIEVEMENTS.place5000Wires]: "place_5000_wires",
[ACHIEVEMENTS.placeBlueprint]: "place_blueprint", [ACHIEVEMENTS.placeBlueprint]: "place_blueprint",
[ACHIEVEMENTS.placeBp1000]: "place_bp_1000",
[ACHIEVEMENTS.play1h]: "play_1h", [ACHIEVEMENTS.play1h]: "play_1h",
[ACHIEVEMENTS.play10h]: "play_10h", [ACHIEVEMENTS.play10h]: "play_10h",
[ACHIEVEMENTS.play20h]: "play_20h", [ACHIEVEMENTS.play20h]: "play_20h",
@ -36,6 +40,8 @@ const ACHIEVEMENT_IDS = {
[ACHIEVEMENTS.store100Unique]: "store_100_unique", [ACHIEVEMENTS.store100Unique]: "store_100_unique",
[ACHIEVEMENTS.storeShape]: "store_shape", [ACHIEVEMENTS.storeShape]: "store_shape",
[ACHIEVEMENTS.unlockWires]: "unlock_wires", [ACHIEVEMENTS.unlockWires]: "unlock_wires",
[ACHIEVEMENTS.upgradesTier5]: "upgrades_tier_5",
[ACHIEVEMENTS.upgradesTier8]: "upgrades_tier_8",
}; };
export class SteamAchievementProvider extends AchievementProviderInterface { export class SteamAchievementProvider extends AchievementProviderInterface {
@ -44,9 +50,15 @@ export class SteamAchievementProvider extends AchievementProviderInterface {
super(app); super(app);
this.initialized = false; this.initialized = false;
this.loaded = false; this.saveId = null;
this.collection = new AchievementCollection(this.activate.bind(this)); this.collection = new AchievementCollection(this.activate.bind(this));
if (G_IS_DEV) {
for (let key in ACHIEVEMENT_IDS) {
assert(this.collection.map.has(key), "Key not found in collection: " + key);
}
}
logger.log("Collection created with", this.collection.map.size, "achievements"); logger.log("Collection created with", this.collection.map.size, "achievements");
} }
@ -55,27 +67,26 @@ export class SteamAchievementProvider extends AchievementProviderInterface {
return true; return true;
} }
/** @returns {boolean} */
hasLoaded() {
return this.loaded;
}
/** /**
* @param {GameRoot} root * @param {GameRoot} root
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
onLoad(root) { onLoad(root) {
if (this.loaded) { this.root = root;
return Promise.resolve();
}
try { try {
this.collection.initialize(root); if (!this.saveId || this.saveId === this.root.savegame.internalId) {
this.loaded = true; this.collection.initialize(root);
logger.log(this.collection.map.size, "achievements are relevant and initialized"); } else {
this.collection = new AchievementCollection(this.activate.bind(this));
this.collection.initialize(root);
}
logger.log("Initialized", this.collection.map.size, "relevant achievements");
this.saveId = this.root.savegame.internalId;
return Promise.resolve(); return Promise.resolve();
} catch (err) { } catch (err) {
logger.error("Failed to initialize the achievement collection"); logger.error("Failed to initialize the collection");
return Promise.reject(err); return Promise.reject(err);
} }
} }