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

Add achievements, ids. Update names, keys for consistency

This commit is contained in:
Greg Considine 2021-03-04 11:41:00 -05:00
parent f5bfd9cf07
commit f827e1ba2f
6 changed files with 192 additions and 209 deletions

View File

@ -164,7 +164,7 @@ export class Blueprint {
anyPlaced = true; anyPlaced = true;
} }
root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.blueprints, anyPlaced); root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.placeBlueprint, anyPlaced);
return anyPlaced; return anyPlaced;
}); });

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.cutting); this.root.signals.achievementUnlocked.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.rotating); this.root.signals.achievementUnlocked.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.stacking); this.root.signals.achievementUnlocked.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.painting); this.root.signals.achievementUnlocked.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,9 +252,10 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
} }
this.shapeKeyToDefinition[id] = definition; this.shapeKeyToDefinition[id] = definition;
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.theLogo, definition); this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.produceLogo, definition);
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.toTheMoon, definition); this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.produceMsLogo, definition);
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.fourLayers, definition); this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.produceRocket, definition);
this.root.signals.achievementUnlocked.dispatch(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.storage, storageComp); this.root.signals.achievementUnlocked.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.networked, entity); this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.place5000Wires, entity);
// Invalidate affected area // Invalidate affected area
const originalRect = staticComp.getTileSpaceBounds(); const originalRect = staticComp.getTileSpaceBounds();

View File

@ -7,34 +7,33 @@ import { ShapeDefinition } from "../game/shape_definition";
/* typehints:end */ /* typehints:end */
export const ACHIEVEMENTS = { export const ACHIEVEMENTS = {
blueprints: "blueprints", belt500Tiles: "belt500Tiles",
cutting: "cutting", blueprint100k: "blueprint100k",
blueprint1m: "blueprint1m",
completeLvl26: "completeLvl26",
cutShape: "cutShape",
darkMode: "darkMode", darkMode: "darkMode",
fourLayers: "fourLayers", level100: "level100",
freedom: "freedom", level50: "level50",
hundredShapes: "hundredShapes", paintShape: "paintShape",
longBelt: "longBelt", place5000Wires: "place5000Wires",
millionBlueprintShapes: "millionBlueprintShapes", placeBlueprint: "placeBlueprint",
networked: "networked", produceLogo: "produceLogo",
painting: "painting", produceMsLogo: "produceMsLogo",
rotating: "rotating", produceRocket: "produceRocket",
stacking: "stacking", rotateShape: "rotateShape",
storage: "storage", stack4Layers: "stack4Layers",
theLogo: "theLogo", stackShape: "stackShape",
toTheMoon: "toTheMoon", store100Unique: "store100Unique",
wires: "wires", storeShape: "storeShape",
unlockWires: "unlockWires",
}; };
const BLUEPRINT_SHAPE = "CbCbCbRb:CwCwCwCw";
const DARK_MODE = "dark"; const DARK_MODE = "dark";
const FREEDOM_LEVEL = 26; const SHAPE_BLUEPRINT = "CbCbCbRb:CwCwCwCw";
const LOGO_SHAPE = "RuCw--Cw:----Ru--"; const SHAPE_LOGO = "RuCw--Cw:----Ru--";
const LONG_BELT_COUNT = 200; const SHAPE_MS_LOGO = "RgRyRbRr";
const NETWORKED_WIRE_COUNT = 100; const SHAPE_ROCKET = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
const ONE_HUNDRED = 100;
const ONE_MILLION = 1000000;
const ROCKET_SHAPE = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
const WIRES_LEVEL = 20;
export class AchievementProviderInterface { export class AchievementProviderInterface {
/** @param {Application} app */ /** @param {Application} app */
@ -117,55 +116,50 @@ export class AchievementCollection {
this.activate = activate; this.activate = activate;
this.initialized = false; this.initialized = false;
this.createAndSet(ACHIEVEMENTS.blueprints, { this.createAndSet(ACHIEVEMENTS.belt500Tiles, {
isValid: this.isBlueprintsValid isValid: this.isBelt500TilesValid,
signal: "entityAdded",
}); });
this.createAndSet(ACHIEVEMENTS.cutting); this.createAndSet(ACHIEVEMENTS.blueprint100k, {
isValid: this.isBlueprint100kValid,
signal: "shapeDelivered",
});
this.createAndSet(ACHIEVEMENTS.blueprint1m, {
isValid: this.isBlueprint1mValid,
signal: "shapeDelivered",
});
this.createAndSet(ACHIEVEMENTS.completeLvl26, this.createLevelOptions(26));
this.createAndSet(ACHIEVEMENTS.cutShape);
this.createAndSet(ACHIEVEMENTS.darkMode, { this.createAndSet(ACHIEVEMENTS.darkMode, {
isValid: this.isDarkModeValid isValid: this.isDarkModeValid,
}); });
this.createAndSet(ACHIEVEMENTS.fourLayers, { this.createAndSet(ACHIEVEMENTS.level100, this.createLevelOptions(100));
isValid: this.isFourLayersValid this.createAndSet(ACHIEVEMENTS.level50, this.createLevelOptions(50));
this.createAndSet(ACHIEVEMENTS.paintShape);
this.createAndSet(ACHIEVEMENTS.place5000Wires, {
isValid: this.isPlace5000WiresValid,
}); });
this.createAndSet(ACHIEVEMENTS.freedom, { this.createAndSet(ACHIEVEMENTS.placeBlueprint, {
isRelevant: this.isFreedomRelevant, isValid: this.isPlaceBlueprintValid,
isValid: this.isFreedomValid,
signal: "storyGoalCompleted"
}); });
this.createAndSet(ACHIEVEMENTS.hundredShapes, { this.createAndSet(ACHIEVEMENTS.produceLogo, this.createShapeOptions(SHAPE_LOGO));
isRelevant: this.isHundredShapesRelevant, this.createAndSet(ACHIEVEMENTS.produceRocket, this.createShapeOptions(SHAPE_ROCKET));
isValid: this.isHundredShapesValid, this.createAndSet(ACHIEVEMENTS.produceMsLogo, this.createShapeOptions(SHAPE_MS_LOGO));
signal: "shapeDelivered" this.createAndSet(ACHIEVEMENTS.rotateShape);
this.createAndSet(ACHIEVEMENTS.stack4Layers, {
isValid: this.isStack4LayersValid,
}); });
this.createAndSet(ACHIEVEMENTS.longBelt, { this.createAndSet(ACHIEVEMENTS.stackShape);
isValid: this.isLongBeltValid, this.createAndSet(ACHIEVEMENTS.store100Unique, {
signal: "entityAdded" isRelevant: this.isStore100UniqueRelevant,
isValid: this.isStore100UniqueValid,
signal: "shapeDelivered",
}); });
this.createAndSet(ACHIEVEMENTS.millionBlueprintShapes, { this.createAndSet(ACHIEVEMENTS.storeShape, {
isValid: this.isMillionBlueprintShapesValid, isValid: this.isStoreShapeValid,
signal: "shapeDelivered" signal: "entityGotNewComponent",
});
this.createAndSet(ACHIEVEMENTS.networked, {
isValid: this.isNetworkedValid,
});
this.createAndSet(ACHIEVEMENTS.painting);
this.createAndSet(ACHIEVEMENTS.rotating);
this.createAndSet(ACHIEVEMENTS.stacking);
this.createAndSet(ACHIEVEMENTS.storage, {
isValid: this.isStorageValid,
signal: "entityGotNewComponent"
});
this.createAndSet(ACHIEVEMENTS.theLogo, {
isValid: this.isTheLogoValid
});
this.createAndSet(ACHIEVEMENTS.toTheMoon, {
isValid: this.isToTheMoonValid
});
this.createAndSet(ACHIEVEMENTS.wires, {
isRelevant: this.isWiresRelevant,
isValid: this.isWiresValid,
signal: "storyGoalCompleted"
}); });
this.createAndSet(ACHIEVEMENTS.unlockWires, this.createLevelOptions(20));
} }
/** @param {GameRoot} root */ /** @param {GameRoot} root */
@ -199,23 +193,21 @@ export class AchievementCollection {
* @param {function} [options.isRelevant] * @param {function} [options.isRelevant]
* @param {string} [options.signal] * @param {string} [options.signal]
*/ */
createAndSet(key, options) { createAndSet(key, options = {}) {
const achievement = new Achievement(key); const achievement = new Achievement(key);
achievement.activate = this.activate; achievement.activate = this.activate;
if (options) { if (options.isValid) {
if (options.isValid) { achievement.isValid = options.isValid.bind(this);
achievement.isValid = options.isValid.bind(this); }
}
if (options.isRelevant) { if (options.isRelevant) {
achievement.isRelevant = options.isRelevant.bind(this); achievement.isRelevant = options.isRelevant.bind(this);
} }
if (options.signal) { if (options.signal) {
achievement.signal = options.signal; achievement.signal = options.signal;
}
} }
this.map.set(key, achievement); this.map.set(key, achievement);
@ -236,16 +228,29 @@ export class AchievementCollection {
return; return;
} }
return achievement.unlock() achievement.unlock()
.finally(() => { .then(() => {
this.remove(key); this.onActivate(null, key);
})
if (!this.hasDefaultReceivers()) { .catch(err => {
this.root.signals.achievementUnlocked.remove(this.unlock); this.onActivate(err, key);
}
}); });
} }
/**
* Cleans up after achievement activation attempt with the provider. Could
* utilize err to retry some number of times if needed.
* @param {?Error} err - Error is null if activation was successful
* @param {string} key - Maps to an Achievement
*/
onActivate (err, key) {
this.remove(key);
if (!this.hasDefaultReceivers()) {
this.root.signals.achievementUnlocked.remove(this.unlock);
}
}
/** @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);
@ -271,50 +276,18 @@ export class AchievementCollection {
return false; return false;
} }
/** createLevelOptions (level) {
* @param {string} key return {
* @param {boolean} anyPlaced isRelevant: () => this.root.hubGoals.level < level,
* @returns {boolean} isValid: (key, currentLevel) => currentLevel === level,
*/ signal: "storyGoalCompleted"
isBlueprintsValid(key, anyPlaced) { }
return anyPlaced;
} }
/** @returns {boolean} */ createShapeOptions (shape) {
isWiresRelevant() { return {
return this.root.hubGoals.level < WIRES_LEVEL; isValid: (key, definition) => definition.cachedHash === shape
} }
/**
* @param {string} key
* @param {number} level
* @returns {boolean}
*/
isWiresValid(key, level) {
return level === WIRES_LEVEL;
}
/**
* @param {string} key
* @param {StorageComponent} storage
* @returns {boolean}
*/
isStorageValid(key, storage) {
return storage.storedCount >= 1;
}
/** @returns {boolean} */
isFreedomRelevant() {
return this.root.hubGoals.level < FREEDOM_LEVEL;
}
/**
* @param {string} key
* @param {number} level
* @returns {boolean}
*/
isFreedomValid(key, level) {
return level === FREEDOM_LEVEL;
} }
/** /**
@ -322,10 +295,8 @@ export class AchievementCollection {
* @param {Entity} entity * @param {Entity} entity
* @returns {boolean} * @returns {boolean}
*/ */
isNetworkedValid(key, entity) { isBelt500TilesValid(key, entity) {
return entity.components.Wire && return entity.components.Belt && entity.components.Belt.assignedPath.totalLength >= 500;
entity.registered &&
entity.root.entityMgr.componentToEntity.Wire.length === NETWORKED_WIRE_COUNT;
} }
/** /**
@ -333,8 +304,9 @@ export class AchievementCollection {
* @param {ShapeDefinition} definition * @param {ShapeDefinition} definition
* @returns {boolean} * @returns {boolean}
*/ */
isTheLogoValid(key, definition) { isBlueprint100kValid(key, definition) {
return definition.layers.length === 2 && definition.cachedHash === LOGO_SHAPE; return definition.cachedHash === SHAPE_BLUEPRINT &&
this.root.hubGoals.storedShapes[SHAPE_BLUEPRINT] >= 100000;
} }
/** /**
@ -342,50 +314,9 @@ export class AchievementCollection {
* @param {ShapeDefinition} definition * @param {ShapeDefinition} definition
* @returns {boolean} * @returns {boolean}
*/ */
isToTheMoonValid(key, definition) { isBlueprint1mValid(key, definition) {
return definition.layers.length === 4 && definition.cachedHash === ROCKET_SHAPE; return definition.cachedHash === SHAPE_BLUEPRINT &&
} this.root.hubGoals.storedShapes[SHAPE_BLUEPRINT] >= 1000000;
/**
* @param {string} key
* @param {ShapeDefinition} definition
* @returns {boolean}
*/
isMillionBlueprintShapesValid(key, definition) {
return definition.cachedHash === BLUEPRINT_SHAPE &&
this.root.hubGoals.storedShapes[BLUEPRINT_SHAPE] >= ONE_MILLION;
}
/** @returns {boolean} */
isHundredShapesRelevant() {
return Object.keys(this.root.hubGoals.storedShapes).length < ONE_HUNDRED;
}
/**
* @param {string} key
* @returns {boolean}
*/
isHundredShapesValid(key) {
return Object.keys(this.root.hubGoals.storedShapes).length === ONE_HUNDRED;
}
/**
* @param {string} key
* @param {ShapeDefinition} definition
* @returns {boolean}
*/
isFourLayersValid(key, definition) {
return definition.layers.length === 4;
}
/**
* @param {string} key
* @param {Entity} entity
* @returns {boolean}
*/
isLongBeltValid(key, entity) {
return entity.components.Belt &&
entity.components.Belt.assignedPath.totalLength >= LONG_BELT_COUNT;
} }
/** /**
@ -396,4 +327,54 @@ 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 {Entity} entity
* @returns {boolean}
*/
isPlace5000WiresValid(key, entity) {
return entity.components.Wire &&
entity.registered &&
entity.root.entityMgr.componentToEntity.Wire.length === 5000;
}
/**
* @param {string} key
* @param {boolean} anyPlaced
* @returns {boolean}
*/
isPlaceBlueprintValid(key, anyPlaced) {
return anyPlaced;
}
/**
* @param {string} key
* @param {ShapeDefinition} definition
* @returns {boolean}
*/
isStack4LayersValid(key, definition) {
return definition.layers.length === 4;
}
/** @returns {boolean} */
isStore100UniqueRelevant() {
return Object.keys(this.root.hubGoals.storedShapes).length < 100;
}
/**
* @param {string} key
* @returns {boolean}
*/
isStore100UniqueValid(key) {
return Object.keys(this.root.hubGoals.storedShapes).length === 100;
}
/**
* @param {string} key
* @param {StorageComponent} storage
* @returns {boolean}
*/
isStoreShapeValid(key, storage) {
return storage.storedCount >= 1;
}
} }

View File

@ -5,31 +5,31 @@ import { GameRoot } from "../../game/root";
import { createLogger } from "../../core/logging"; import { createLogger } from "../../core/logging";
import { getIPCRenderer } from "../../core/utils"; import { getIPCRenderer } from "../../core/utils";
import { import { ACHIEVEMENTS, AchievementCollection, AchievementProviderInterface } from "../achievement_provider";
ACHIEVEMENTS,
AchievementCollection,
AchievementProviderInterface
} from "../achievement_provider";
const logger = createLogger("achievements/steam"); const logger = createLogger("achievements/steam");
const ACHIEVEMENT_IDS = { const ACHIEVEMENT_IDS = {
[ACHIEVEMENTS.blueprints]: "<id>", [ACHIEVEMENTS.belt500Tiles]: "belt_500_tiles",
[ACHIEVEMENTS.cutting]: "achievement_01", // Test ID [ACHIEVEMENTS.blueprint100k]: "blueprint_100k",
[ACHIEVEMENTS.darkMode]: "<id>", [ACHIEVEMENTS.blueprint1m]: "blueprint_1m",
[ACHIEVEMENTS.fourLayers]: "<id>", [ACHIEVEMENTS.completeLvl26]: "complete_lvl_26",
[ACHIEVEMENTS.freedom]: "<id>", [ACHIEVEMENTS.cutShape]: "cut_shape",
[ACHIEVEMENTS.hundredShapes]: "<id>", [ACHIEVEMENTS.darkMode]: "dark_mode",
[ACHIEVEMENTS.longBelt]: "<id>", [ACHIEVEMENTS.level100]: "level_100",
[ACHIEVEMENTS.millionBlueprintShapes]: "<id>", [ACHIEVEMENTS.level50]: "level_50",
[ACHIEVEMENTS.networked]: "<id>", [ACHIEVEMENTS.paintShape]: "paint_shape",
[ACHIEVEMENTS.painting]: "<id>", [ACHIEVEMENTS.place5000Wires]: "place_5000_wires",
[ACHIEVEMENTS.rotating]: "<id>", [ACHIEVEMENTS.placeBlueprint]: "place_blueprint",
[ACHIEVEMENTS.stacking]: "<id>", [ACHIEVEMENTS.produceLogo]: "produce_logo",
[ACHIEVEMENTS.storage]: "<id>", [ACHIEVEMENTS.produceMsLogo]: "produce_ms_logo",
[ACHIEVEMENTS.theLogo]: "<id>", [ACHIEVEMENTS.produceRocket]: "produce_rocket",
[ACHIEVEMENTS.toTheMoon]: "<id>", [ACHIEVEMENTS.rotateShape]: "rotate_shape",
[ACHIEVEMENTS.wires]: "<id>", [ACHIEVEMENTS.stack4Layers]: "stack_4_layers",
[ACHIEVEMENTS.stackShape]: "stack_shape",
[ACHIEVEMENTS.store100Unique]: "store_100_unique",
[ACHIEVEMENTS.storeShape]: "store_shape",
[ACHIEVEMENTS.unlockWires]: "unlock_wires",
}; };
export class SteamAchievementProvider extends AchievementProviderInterface { export class SteamAchievementProvider extends AchievementProviderInterface {
@ -90,7 +90,7 @@ export class SteamAchievementProvider extends AchievementProviderInterface {
* @param {string} key - An ACHIEVEMENTS key * @param {string} key - An ACHIEVEMENTS key
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
activate (key) { activate(key) {
let promise; let promise;
if (!this.initialized) { if (!this.initialized) {
@ -105,6 +105,7 @@ export class SteamAchievementProvider extends AchievementProviderInterface {
}) })
.catch(err => { .catch(err => {
logger.error("Failed to activate achievement:", key, err); logger.error("Failed to activate achievement:", key, err);
}) throw err;
});
} }
} }