mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
[WIP] Add achievements. Start savefile migration
This commit is contained in:
parent
a722c3562d
commit
10b90a8df2
@ -41,7 +41,7 @@ export const globalConfig = {
|
|||||||
shapesSharpness: 1.4,
|
shapesSharpness: 1.4,
|
||||||
|
|
||||||
// Achievements
|
// Achievements
|
||||||
achievementSliceDuration: 30, // Seconds
|
achievementSliceDuration: 10, // Seconds
|
||||||
|
|
||||||
// Production analytics
|
// Production analytics
|
||||||
statisticsGraphDpi: 2.5,
|
statisticsGraphDpi: 2.5,
|
||||||
|
@ -5,21 +5,42 @@ import { GameRoot } from "./root";
|
|||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
import { ACHIEVEMENTS } from "../platform/achievement_provider";
|
import { ACHIEVEMENTS } from "../platform/achievement_provider";
|
||||||
|
import { BasicSerializableObject } from "../savegame/serialization";
|
||||||
|
//import { typeAchievementCollection } from "./achievement_resolver";
|
||||||
|
|
||||||
const logger = createLogger("achievement_proxy");
|
const logger = createLogger("achievement_proxy");
|
||||||
|
|
||||||
export class AchievementProxy {
|
export class AchievementProxy extends BasicSerializableObject {
|
||||||
|
static getId() {
|
||||||
|
return "AchievementProxy";
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSchema() {
|
||||||
|
return {
|
||||||
|
// collection: typeAchievementCollection
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialize(data, root) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/** @param {GameRoot} root */
|
/** @param {GameRoot} root */
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
|
super();
|
||||||
|
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.provider = this.root.app.achievementProvider;
|
this.provider = this.root.app.achievementProvider;
|
||||||
this.lastSlice = 0;
|
|
||||||
this.disabled = true;
|
this.disabled = true;
|
||||||
|
|
||||||
if (!this.provider.hasAchievements()) {
|
if (!this.provider.hasAchievements()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.sliceTime = 0;
|
||||||
|
this.sliceIteration = 0;
|
||||||
|
this.sliceIterationLimit = 2;
|
||||||
|
|
||||||
this.root.signals.postLoadHook.add(this.onLoad, this);
|
this.root.signals.postLoadHook.add(this.onLoad, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,22 +58,49 @@ export class AchievementProxy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Have certain checks every 30 seconds, 10 seconds, etc.
|
||||||
|
// Re-check relevance every so often
|
||||||
|
// Consider disabling checks if no longer relevant
|
||||||
startSlice() {
|
startSlice() {
|
||||||
this.lastSlice = this.root.time.now();
|
this.sliceTime = this.root.time.now();
|
||||||
|
|
||||||
|
// Every slice
|
||||||
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.storeShape, this.sliceTime);
|
||||||
|
|
||||||
|
// Every other slice
|
||||||
|
if (this.sliceIteration % 2 === 0) {
|
||||||
this.root.signals.bulkAchievementCheck.dispatch(
|
this.root.signals.bulkAchievementCheck.dispatch(
|
||||||
ACHIEVEMENTS.play1h, this.lastSlice,
|
ACHIEVEMENTS.throughputBp25, this.sliceTime,
|
||||||
ACHIEVEMENTS.play10h, this.lastSlice,
|
ACHIEVEMENTS.throughputBp50, this.sliceTime,
|
||||||
ACHIEVEMENTS.play20h, this.lastSlice
|
ACHIEVEMENTS.throughputLogo25, this.sliceTime,
|
||||||
|
ACHIEVEMENTS.throughputLogo50, this.sliceTime,
|
||||||
|
ACHIEVEMENTS.throughputRocket10, this.sliceTime,
|
||||||
|
ACHIEVEMENTS.throughputRocket20, this.sliceTime
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Every 3rd slice
|
||||||
|
if (this.sliceIteration % 3 === 0) {
|
||||||
|
this.root.signals.bulkAchievementCheck.dispatch(
|
||||||
|
ACHIEVEMENTS.play1h, this.sliceTime,
|
||||||
|
ACHIEVEMENTS.play10h, this.sliceTime,
|
||||||
|
ACHIEVEMENTS.play20h, this.sliceTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sliceIteration === this.sliceIterationLimit) {
|
||||||
|
this.sliceIteration = 0;
|
||||||
|
} else {
|
||||||
|
this.sliceIteration++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
if (this.disabled) {
|
if (this.disabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.root.time.now() - this.lastSlice > globalConfig.achievementSliceDuration) {
|
if (this.root.time.now() - this.sliceTime > globalConfig.achievementSliceDuration) {
|
||||||
this.startSlice();
|
this.startSlice();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
src/js/game/achievement_resolver.js
Normal file
4
src/js/game/achievement_resolver.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export function achievementResolver(root, data) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
|||||||
import { generateMatrixRotations } from "../../core/utils";
|
import { generateMatrixRotations } from "../../core/utils";
|
||||||
import { enumDirection, Vector } from "../../core/vector";
|
import { enumDirection, Vector } from "../../core/vector";
|
||||||
|
import { ACHIEVEMENTS } from "../../platform/achievement_provider";
|
||||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
@ -37,6 +38,25 @@ export class MetaTrashBuilding extends MetaBuilding {
|
|||||||
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash);
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addAchievementReceiver(entity) {
|
||||||
|
if (!entity.root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemProcessor = entity.components.ItemProcessor
|
||||||
|
const tryTakeItem = itemProcessor.tryTakeItem.bind(itemProcessor);
|
||||||
|
|
||||||
|
itemProcessor.tryTakeItem = () => {
|
||||||
|
const taken = tryTakeItem(...arguments);
|
||||||
|
|
||||||
|
if (taken) {
|
||||||
|
entity.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.trash1000, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return taken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the entity at the given location
|
* Creates the entity at the given location
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
@ -57,11 +77,14 @@ export class MetaTrashBuilding extends MetaBuilding {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
entity.addComponent(
|
entity.addComponent(
|
||||||
new ItemProcessorComponent({
|
new ItemProcessorComponent({
|
||||||
inputsPerCharge: 1,
|
inputsPerCharge: 1,
|
||||||
processorType: enumItemProcessorTypes.trash,
|
processorType: enumItemProcessorTypes.trash,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.addAchievementReceiver(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,16 +251,6 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
|
|||||||
return this.shapeKeyToDefinition[id];
|
return this.shapeKeyToDefinition[id];
|
||||||
}
|
}
|
||||||
this.shapeKeyToDefinition[id] = definition;
|
this.shapeKeyToDefinition[id] = definition;
|
||||||
|
|
||||||
this.root.signals.bulkAchievementCheck.dispatch(
|
|
||||||
ACHIEVEMENTS.logoBefore18, definition,
|
|
||||||
ACHIEVEMENTS.oldLevel17, definition,
|
|
||||||
ACHIEVEMENTS.produceLogo, 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;
|
||||||
}
|
}
|
||||||
|
@ -275,9 +275,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
// It's a storage
|
// It's a storage
|
||||||
if (storageComp.canAcceptItem(item)) {
|
if (storageComp.canAcceptItem(item)) {
|
||||||
storageComp.takeItem(item);
|
storageComp.takeItem(item);
|
||||||
|
|
||||||
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.storeShape, storageComp);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { Application } from "../application";
|
import { Application } from "../application";
|
||||||
|
import { BaseItem } from "./base_item";
|
||||||
import { StorageComponent } from "../game/components/storage";
|
import { StorageComponent } from "../game/components/storage";
|
||||||
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";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
|
import { enumAnalyticsDataSource } from "../game/production_analytics";
|
||||||
|
|
||||||
export const ACHIEVEMENTS = {
|
export const ACHIEVEMENTS = {
|
||||||
belt500Tiles: "belt500Tiles",
|
belt500Tiles: "belt500Tiles",
|
||||||
blueprint100k: "blueprint100k",
|
blueprint100k: "blueprint100k",
|
||||||
@ -32,25 +35,41 @@ export const ACHIEVEMENTS = {
|
|||||||
produceMsLogo: "produceMsLogo",
|
produceMsLogo: "produceMsLogo",
|
||||||
produceRocket: "produceRocket",
|
produceRocket: "produceRocket",
|
||||||
rotateShape: "rotateShape",
|
rotateShape: "rotateShape",
|
||||||
|
speedrunBp30: "speedrunBp30",
|
||||||
|
speedrunBp60: "speedrunBp60",
|
||||||
|
speedrunBp120: "speedrunBp120",
|
||||||
stack4Layers: "stack4Layers",
|
stack4Layers: "stack4Layers",
|
||||||
stackShape: "stackShape",
|
stackShape: "stackShape",
|
||||||
store100Unique: "store100Unique",
|
store100Unique: "store100Unique",
|
||||||
storeShape: "storeShape",
|
storeShape: "storeShape",
|
||||||
|
throughputBp25: "throughputBp25",
|
||||||
|
throughputBp50: "throughputBp50",
|
||||||
|
throughputLogo25: "throughputLogo25",
|
||||||
|
throughputLogo50: "throughputLogo50",
|
||||||
|
throughputRocket10: "throughputRocket10",
|
||||||
|
throughputRocket20: "throughputRocket20",
|
||||||
|
trash1000: "trash1000",
|
||||||
unlockWires: "unlockWires",
|
unlockWires: "unlockWires",
|
||||||
upgradesTier5: "upgradesTier5",
|
upgradesTier5: "upgradesTier5",
|
||||||
upgradesTier8: "upgradesTier8",
|
upgradesTier8: "upgradesTier8",
|
||||||
};
|
};
|
||||||
|
|
||||||
const DARK_MODE = "dark";
|
const DARK_MODE = "dark";
|
||||||
const WIRE_LAYER = "wires";
|
|
||||||
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 SHAPE_BLUEPRINT = "CbCbCbRb:CwCwCwCw";
|
const ITEM_SHAPE = "shape";
|
||||||
|
const MINUTE_30 = 1800; // Seconds
|
||||||
|
const MINUTE_60 = MINUTE_30 * 2;
|
||||||
|
const MINUTE_120 = MINUTE_30 * 4;
|
||||||
|
const PRODUCED = "produced";
|
||||||
|
const RATE_SLICE_COUNT = 10;
|
||||||
|
const SHAPE_BP = "CbCbCbRb:CwCwCwCw";
|
||||||
const SHAPE_LOGO = "RuCw--Cw:----Ru--";
|
const SHAPE_LOGO = "RuCw--Cw:----Ru--";
|
||||||
const SHAPE_MS_LOGO = "RgRyRbRr";
|
const SHAPE_MS_LOGO = "RgRyRbRr";
|
||||||
const SHAPE_ROCKET = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
|
const SHAPE_ROCKET = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
|
||||||
const SHAPE_OLD_LEVEL_17 = "WrRgWrRg:CwCrCwCr:SgSgSgSg";
|
const SHAPE_OLD_LEVEL_17 = "WrRgWrRg:CwCrCwCr:SgSgSgSg";
|
||||||
|
const WIRE_LAYER = "wires";
|
||||||
|
|
||||||
export class AchievementProviderInterface {
|
export class AchievementProviderInterface {
|
||||||
/** @param {Application} app */
|
/** @param {Application} app */
|
||||||
@ -167,6 +186,7 @@ export class AchievementCollection {
|
|||||||
this.createAndSet(ACHIEVEMENTS.logoBefore18, {
|
this.createAndSet(ACHIEVEMENTS.logoBefore18, {
|
||||||
isRelevant: this.isLogoBefore18Relevant,
|
isRelevant: this.isLogoBefore18Relevant,
|
||||||
isValid: this.isLogoBefore18Valid,
|
isValid: this.isLogoBefore18Valid,
|
||||||
|
signal: "itemProduced"
|
||||||
});
|
});
|
||||||
this.createAndSet(ACHIEVEMENTS.mapMarkers15, {
|
this.createAndSet(ACHIEVEMENTS.mapMarkers15, {
|
||||||
isRelevant: this.isMapMarkers15Relevant,
|
isRelevant: this.isMapMarkers15Relevant,
|
||||||
@ -194,8 +214,12 @@ export class AchievementCollection {
|
|||||||
this.createAndSet(ACHIEVEMENTS.produceRocket, this.createShapeOptions(SHAPE_ROCKET));
|
this.createAndSet(ACHIEVEMENTS.produceRocket, this.createShapeOptions(SHAPE_ROCKET));
|
||||||
this.createAndSet(ACHIEVEMENTS.produceMsLogo, this.createShapeOptions(SHAPE_MS_LOGO));
|
this.createAndSet(ACHIEVEMENTS.produceMsLogo, this.createShapeOptions(SHAPE_MS_LOGO));
|
||||||
this.createAndSet(ACHIEVEMENTS.rotateShape);
|
this.createAndSet(ACHIEVEMENTS.rotateShape);
|
||||||
|
this.createAndSet(ACHIEVEMENTS.speedrunBp30, this.createSpeedOptions(12, MINUTE_30));
|
||||||
|
this.createAndSet(ACHIEVEMENTS.speedrunBp60, this.createSpeedOptions(12, MINUTE_60));
|
||||||
|
this.createAndSet(ACHIEVEMENTS.speedrunBp120, this.createSpeedOptions(12, MINUTE_120));
|
||||||
this.createAndSet(ACHIEVEMENTS.stack4Layers, {
|
this.createAndSet(ACHIEVEMENTS.stack4Layers, {
|
||||||
isValid: this.isStack4LayersValid,
|
isValid: this.isStack4LayersValid,
|
||||||
|
signal: "itemProduced",
|
||||||
});
|
});
|
||||||
this.createAndSet(ACHIEVEMENTS.stackShape);
|
this.createAndSet(ACHIEVEMENTS.stackShape);
|
||||||
this.createAndSet(ACHIEVEMENTS.store100Unique, {
|
this.createAndSet(ACHIEVEMENTS.store100Unique, {
|
||||||
@ -205,7 +229,16 @@ export class AchievementCollection {
|
|||||||
});
|
});
|
||||||
this.createAndSet(ACHIEVEMENTS.storeShape, {
|
this.createAndSet(ACHIEVEMENTS.storeShape, {
|
||||||
isValid: this.isStoreShapeValid,
|
isValid: this.isStoreShapeValid,
|
||||||
signal: "entityGotNewComponent",
|
});
|
||||||
|
this.createAndSet(ACHIEVEMENTS.throughputBp25, this.createRateOptions(SHAPE_BP, 25));
|
||||||
|
this.createAndSet(ACHIEVEMENTS.throughputBp50, this.createRateOptions(SHAPE_BP, 50));
|
||||||
|
this.createAndSet(ACHIEVEMENTS.throughputLogo25, this.createRateOptions(SHAPE_LOGO, 25));
|
||||||
|
this.createAndSet(ACHIEVEMENTS.throughputLogo50, this.createRateOptions(SHAPE_LOGO, 50));
|
||||||
|
this.createAndSet(ACHIEVEMENTS.throughputRocket10, this.createRateOptions(SHAPE_ROCKET, 25));
|
||||||
|
this.createAndSet(ACHIEVEMENTS.throughputRocket20, this.createRateOptions(SHAPE_ROCKET, 50));
|
||||||
|
this.createAndSet(ACHIEVEMENTS.trash1000, {
|
||||||
|
init: this.initTrash1000,
|
||||||
|
isValid: this.isTrash1000Valid,
|
||||||
});
|
});
|
||||||
this.createAndSet(ACHIEVEMENTS.unlockWires, this.createLevelOptions(20));
|
this.createAndSet(ACHIEVEMENTS.unlockWires, this.createLevelOptions(20));
|
||||||
this.createAndSet(ACHIEVEMENTS.upgradesTier5, this.createUpgradeOptions(5));
|
this.createAndSet(ACHIEVEMENTS.upgradesTier5, this.createUpgradeOptions(5));
|
||||||
@ -219,6 +252,10 @@ export class AchievementCollection {
|
|||||||
this.root.signals.bulkAchievementCheck.add(this.bulkUnlock, 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.init) {
|
||||||
|
achievement.init();
|
||||||
|
}
|
||||||
|
|
||||||
if (!achievement.isRelevant()) {
|
if (!achievement.isRelevant()) {
|
||||||
this.remove(key);
|
this.remove(key);
|
||||||
continue;
|
continue;
|
||||||
@ -251,6 +288,10 @@ export class AchievementCollection {
|
|||||||
|
|
||||||
achievement.activate = this.activate;
|
achievement.activate = this.activate;
|
||||||
|
|
||||||
|
if (options.init) {
|
||||||
|
achievement.init = options.init.bind(this, achievement);
|
||||||
|
}
|
||||||
|
|
||||||
if (options.isValid) {
|
if (options.isValid) {
|
||||||
achievement.isValid = options.isValid.bind(this);
|
achievement.isValid = options.isValid.bind(this);
|
||||||
}
|
}
|
||||||
@ -274,16 +315,16 @@ export class AchievementCollection {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} key - Maps to an Achievement
|
* @param {string} key - Maps to an Achievement
|
||||||
* @param {*[]} [arguments] - Additional arguments received from signal dispatches
|
* @param {?*} data - Data received from signal dispatches for validation
|
||||||
*/
|
*/
|
||||||
unlock(key) {
|
unlock(key, data) {
|
||||||
if (!this.map.has(key)) {
|
if (!this.map.has(key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const achievement = this.map.get(key);
|
const achievement = this.map.get(key);
|
||||||
|
|
||||||
if (!achievement.isValid(...arguments)) {
|
if (!achievement.isValid(data, achievement.state)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,17 +388,46 @@ export class AchievementCollection {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {BaseItem} item
|
||||||
|
* @param {string} shape
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isShape(item, shape) {
|
||||||
|
return item.getItemType() === ITEM_SHAPE && item.definition.getHash() === shape;
|
||||||
|
}
|
||||||
|
|
||||||
createLevelOptions(level) {
|
createLevelOptions(level) {
|
||||||
return {
|
return {
|
||||||
isRelevant: () => this.root.hubGoals.level < level,
|
isRelevant: () => this.root.hubGoals.level < level,
|
||||||
isValid: (key, currentLevel) => currentLevel === level,
|
isValid: (currentLevel) => currentLevel === level,
|
||||||
signal: "storyGoalCompleted",
|
signal: "storyGoalCompleted",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createRateOptions(shape, rate) {
|
||||||
|
return {
|
||||||
|
isValid: () => {
|
||||||
|
return this.root.productionAnalytics.getCurrentShapeRate(
|
||||||
|
enumAnalyticsDataSource.produced,
|
||||||
|
this.root.shapeDefinitionMgr.getShapeFromShortKey(shape)
|
||||||
|
) >= rate;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
createShapeOptions(shape) {
|
createShapeOptions(shape) {
|
||||||
return {
|
return {
|
||||||
isValid: (key, definition) => definition.cachedHash === shape,
|
isValid: (item) => this.isShape(item, shape),
|
||||||
|
signal: "itemProduced",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createSpeedOptions(level, time) {
|
||||||
|
return {
|
||||||
|
isRelevant: () => this.root.hubGoals.level <= level && this.root.time.now() < time,
|
||||||
|
isValid: (currentLevel) => currentLevel === level && this.root.time.now() < time,
|
||||||
|
signal: "storyGoalCompleted",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,62 +446,39 @@ export class AchievementCollection {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {Entity} entity @returns {boolean} */
|
||||||
* @param {string} key
|
isBelt500TilesValid(entity) {
|
||||||
* @param {Entity} entity
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isBelt500TilesValid(key, entity) {
|
|
||||||
return entity.components.Belt && entity.components.Belt.assignedPath.totalLength >= 500;
|
return entity.components.Belt && entity.components.Belt.assignedPath.totalLength >= 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {ShapeDefinition} definition @returns {boolean} */
|
||||||
* @param {string} key
|
isBlueprint100kValid(definition) {
|
||||||
* @param {ShapeDefinition} definition
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isBlueprint100kValid(key, definition) {
|
|
||||||
return (
|
return (
|
||||||
definition.cachedHash === SHAPE_BLUEPRINT &&
|
definition.cachedHash === SHAPE_BP &&
|
||||||
this.root.hubGoals.storedShapes[SHAPE_BLUEPRINT] >= 100000
|
this.root.hubGoals.storedShapes[SHAPE_BP] >= 100000
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {ShapeDefinition} definition @returns {boolean} */
|
||||||
* @param {string} key
|
isBlueprint1mValid(definition) {
|
||||||
* @param {ShapeDefinition} definition
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isBlueprint1mValid(key, definition) {
|
|
||||||
return (
|
return (
|
||||||
definition.cachedHash === SHAPE_BLUEPRINT &&
|
definition.cachedHash === SHAPE_BP &&
|
||||||
this.root.hubGoals.storedShapes[SHAPE_BLUEPRINT] >= 1000000
|
this.root.hubGoals.storedShapes[SHAPE_BP] >= 1000000
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @returns {boolean} */
|
||||||
* @param {string} key
|
isDarkModeValid() {
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isDarkModeValid(key) {
|
|
||||||
return this.root.app.settings.currentData.settings.theme === DARK_MODE;
|
return this.root.app.settings.currentData.settings.theme === DARK_MODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {number} count @returns {boolean} */
|
||||||
* @param {string} key
|
isDestroy1000Valid(count) {
|
||||||
* @param {number} count - The count of selected entities destroyed
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isDestroy1000Valid(key, count) {
|
|
||||||
return count >= 1000;
|
return count >= 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {ShapeDefinition} definition @returns {boolean} */
|
||||||
* @param {string} key
|
isIrrelevantShapeValid(definition) {
|
||||||
* @param {ShapeDefinition} definition
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isIrrelevantShapeValid(key, definition) {
|
|
||||||
if (definition.cachedHash === this.root.hubGoals.currentGoal.definition.cachedHash) {
|
if (definition.cachedHash === this.root.hubGoals.currentGoal.definition.cachedHash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -456,13 +503,9 @@ export class AchievementCollection {
|
|||||||
return this.root.hubGoals.level < 18;
|
return this.root.hubGoals.level < 18;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {BaseItem} item @returns {boolean} */
|
||||||
* @param {string} key
|
isLogoBefore18Valid(item) {
|
||||||
* @param {ShapeDefinition} definition
|
return this.root.hubGoals.level < 18 && this.isShape(item, SHAPE_LOGO);
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isLogoBefore18Valid(key, definition) {
|
|
||||||
return this.root.hubGoals.level < 18 && definition.cachedHash === SHAPE_LOGO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {boolean} */
|
/** @returns {boolean} */
|
||||||
@ -470,30 +513,18 @@ export class AchievementCollection {
|
|||||||
return this.root.hud.parts.waypoints.waypoints.length < 16; // 16 - HUB
|
return this.root.hud.parts.waypoints.waypoints.length < 16; // 16 - HUB
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {number} count @returns {boolean} */
|
||||||
* @param {string} key
|
isMapMarkers15Valid(count) {
|
||||||
* @param {number} count - Count of map markers excluding HUB
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isMapMarkers15Valid(key, count) {
|
|
||||||
return count === 15;
|
return count === 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {string} currentLayer @returns {boolean} */
|
||||||
* @param {string} key
|
isOpenWiresValid(currentLayer) {
|
||||||
* @param {string} currentLayer
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isOpenWiresValid(key, currentLayer) {
|
|
||||||
return currentLayer === WIRE_LAYER;
|
return currentLayer === WIRE_LAYER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {Entity} entity @returns {boolean} */
|
||||||
* @param {string} key
|
isPlace5000WiresValid(entity) {
|
||||||
* @param {Entity} entity
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isPlace5000WiresValid(key, entity) {
|
|
||||||
return (
|
return (
|
||||||
entity.components.Wire &&
|
entity.components.Wire &&
|
||||||
entity.registered &&
|
entity.registered &&
|
||||||
@ -501,31 +532,19 @@ export class AchievementCollection {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {number} count @returns {boolean} */
|
||||||
* @param {string} key
|
isPlaceBlueprintValid(count) {
|
||||||
* @param {number} count
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isPlaceBlueprintValid(key, count) {
|
|
||||||
return count != 0;
|
return count != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {number} count @returns {boolean} */
|
||||||
* @param {string} key
|
isPlaceBp1000Valid(count) {
|
||||||
* @param {number} count
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isPlaceBp1000Valid(key, count) {
|
|
||||||
return count >= 1000;
|
return count >= 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param {string} key @param {BaseItem} item @returns {boolean} */
|
||||||
* @param {string} key
|
isStack4LayersValid(item) {
|
||||||
* @param {ShapeDefinition} definition
|
return item.getItemType() === ITEM_SHAPE && item.definition.layers.length === 4;
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isStack4LayersValid(key, definition) {
|
|
||||||
return definition.layers.length === 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {boolean} */
|
/** @returns {boolean} */
|
||||||
@ -533,20 +552,43 @@ export class AchievementCollection {
|
|||||||
return Object.keys(this.root.hubGoals.storedShapes).length < 100;
|
return Object.keys(this.root.hubGoals.storedShapes).length < 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @returns {boolean} */
|
||||||
* @param {string} key
|
isStore100UniqueValid() {
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isStore100UniqueValid(key) {
|
|
||||||
return Object.keys(this.root.hubGoals.storedShapes).length === 100;
|
return Object.keys(this.root.hubGoals.storedShapes).length === 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {StorageComponent} storage @returns {boolean} */
|
||||||
|
isStoreShapeValid() {
|
||||||
|
const entities = this.root.systemMgr.systems.storage.allEntities;
|
||||||
|
|
||||||
|
if (entities.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < entities.length; i++) {
|
||||||
|
if (entities[i].components.Storage.storedCount > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
initTrash1000(achievement) {
|
||||||
|
// get state from root
|
||||||
|
console.log(this.root.savegame.currentData.dump);
|
||||||
|
|
||||||
|
achievement.state = achievement.state || {
|
||||||
|
count: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @param {string} key
|
* @params {number} count
|
||||||
* @param {StorageComponent} storage
|
* @params {object} state - The achievement's current state
|
||||||
* @returns {boolean}
|
* @returns {boolean} */
|
||||||
*/
|
isTrash1000Valid(count, state) {
|
||||||
isStoreShapeValid(key, storage) {
|
state.count += count;
|
||||||
return storage.storedCount >= 1;
|
|
||||||
|
return state.count >= 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,20 @@ const ACHIEVEMENT_IDS = {
|
|||||||
[ACHIEVEMENTS.produceMsLogo]: "produce_ms_logo",
|
[ACHIEVEMENTS.produceMsLogo]: "produce_ms_logo",
|
||||||
[ACHIEVEMENTS.produceRocket]: "produce_rocket",
|
[ACHIEVEMENTS.produceRocket]: "produce_rocket",
|
||||||
[ACHIEVEMENTS.rotateShape]: "rotate_shape",
|
[ACHIEVEMENTS.rotateShape]: "rotate_shape",
|
||||||
|
[ACHIEVEMENTS.speedrunBp30]: "speedrun_bp_30",
|
||||||
|
[ACHIEVEMENTS.speedrunBp60]: "speedrun_bp_60",
|
||||||
|
[ACHIEVEMENTS.speedrunBp120]: "speedrun_bp_120",
|
||||||
[ACHIEVEMENTS.stack4Layers]: "stack_4_layers",
|
[ACHIEVEMENTS.stack4Layers]: "stack_4_layers",
|
||||||
[ACHIEVEMENTS.stackShape]: "stack_shape",
|
[ACHIEVEMENTS.stackShape]: "stack_shape",
|
||||||
[ACHIEVEMENTS.store100Unique]: "store_100_unique",
|
[ACHIEVEMENTS.store100Unique]: "store_100_unique",
|
||||||
[ACHIEVEMENTS.storeShape]: "store_shape",
|
[ACHIEVEMENTS.storeShape]: "store_shape",
|
||||||
|
[ACHIEVEMENTS.throughputBp25]: "throughput_bp_25",
|
||||||
|
[ACHIEVEMENTS.throughputBp50]: "throughput_bp_50",
|
||||||
|
[ACHIEVEMENTS.throughputLogo25]: "throughput_logo_25",
|
||||||
|
[ACHIEVEMENTS.throughputLogo50]: "throughput_logo_50",
|
||||||
|
[ACHIEVEMENTS.throughputRocket10]: "throughput_rocket_10",
|
||||||
|
[ACHIEVEMENTS.throughputRocket20]: "throughput_rocket_20",
|
||||||
|
[ACHIEVEMENTS.trash1000]: "trash_1000",
|
||||||
[ACHIEVEMENTS.unlockWires]: "unlock_wires",
|
[ACHIEVEMENTS.unlockWires]: "unlock_wires",
|
||||||
[ACHIEVEMENTS.upgradesTier5]: "upgrades_tier_5",
|
[ACHIEVEMENTS.upgradesTier5]: "upgrades_tier_5",
|
||||||
[ACHIEVEMENTS.upgradesTier8]: "upgrades_tier_8",
|
[ACHIEVEMENTS.upgradesTier8]: "upgrades_tier_8",
|
||||||
|
26
src/js/savegame/schemas/1008.js
Normal file
26
src/js/savegame/schemas/1008.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { createLogger } from "../../core/logging.js";
|
||||||
|
import { SavegameInterface_V1007 } from "./1007.js";
|
||||||
|
|
||||||
|
const schema = require("./1008.json");
|
||||||
|
const logger = createLogger("savegame_interface/1008");
|
||||||
|
|
||||||
|
export class SavegameInterface_V1008 extends SavegameInterface_V1007 {
|
||||||
|
getVersion() {
|
||||||
|
return 1008;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaUncached() {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("../savegame_typedefs.js").SavegameData} data
|
||||||
|
*/
|
||||||
|
static migrate1007to1008(data) {
|
||||||
|
logger.log("Migrating 1007 to 1008");
|
||||||
|
const dump = data.dump;
|
||||||
|
if (!dump) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
src/js/savegame/schemas/1008.json
Normal file
5
src/js/savegame/schemas/1008.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [],
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user