mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Fix/achievements (#1091)
* [WIP] Fix achievements with unlock attempt on init. Fix lint * Add init checks for more achievements * Fix tslint errors * Update CI step to include config.local copy from template if not present
This commit is contained in:
parent
4685f813e8
commit
d1ef3f834e
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -42,6 +42,7 @@ jobs:
|
||||
run: |
|
||||
cd gulp
|
||||
yarn gulp translations.fullBuild
|
||||
yarn gulp localConfig.findOrCreate
|
||||
cd ..
|
||||
yarn tslint
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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()),
|
||||
};
|
||||
|
||||
|
@ -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(
|
||||
|
2
src/js/globals.d.ts
vendored
2
src/js/globals.d.ts
vendored
@ -19,7 +19,7 @@ declare const G_BUILD_VERSION: string;
|
||||
declare const G_ALL_UI_IMAGES: Array<string>;
|
||||
declare const G_IS_RELEASE: boolean;
|
||||
|
||||
declare const G_CHINA_VERSION : boolean;
|
||||
declare const G_CHINA_VERSION: boolean;
|
||||
|
||||
// Polyfills
|
||||
declare interface String {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user