mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
[WIP] Refactor achievements, jsdoc fixes, add npm script
- Refactor achievements to make use of Signals - Move implemented achievement interfaces to appropriate platform folders (SteamAchievements in currently in use in browser wrapper for testing) - Fix invalid jsdocs - Add dev-standalone script to package.json scripts
This commit is contained in:
parent
85999f3f8b
commit
c41ad97eaf
1610
electron/package-lock.json
generated
Normal file
1610
electron/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "cd gulp && yarn gulp main.serveDev",
|
||||
"dev-standalone": "cd gulp && yarn gulp main.serveStandalone",
|
||||
"tslint": "cd src/js && tsc",
|
||||
"lint": "eslint src/js",
|
||||
"prettier-all": "prettier --write src/**/*.* && prettier --write gulp/**/*.*",
|
||||
|
@ -12,9 +12,7 @@ import { getPlatformName, waitNextFrame } from "./core/utils";
|
||||
import { Vector } from "./core/vector";
|
||||
import { AdProviderInterface } from "./platform/ad_provider";
|
||||
import { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
|
||||
import { AchievementsInterface } from "./platform/achievements";
|
||||
import { NoAchievements } from "./platform/achievements/no_achievements";
|
||||
import { Achievements } from "./platform/achievements/achievements";
|
||||
import { NoAchievements } from "./platform/browser/no_achievements";
|
||||
import { AnalyticsInterface } from "./platform/analytics";
|
||||
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
||||
import { SoundImplBrowser } from "./platform/browser/sound";
|
||||
@ -35,6 +33,7 @@ import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
||||
import { RestrictionManager } from "./core/restriction_manager";
|
||||
|
||||
/**
|
||||
* @typedef {import("./platform/achievements").AchievementsInterface} AchievementsInterface
|
||||
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
|
||||
* @typedef {import("./platform/sound").SoundInterface} SoundInterface
|
||||
* @typedef {import("./platform/storage").StorageInterface} StorageInterface
|
||||
|
@ -12,22 +12,14 @@ export class AchievementManager {
|
||||
this.root = root;
|
||||
this.achievements = this.root.app.achievements;
|
||||
|
||||
this.load()
|
||||
}
|
||||
if (!this.achievements.hasAchievements()) {
|
||||
logger.log("Achievements disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
load () {
|
||||
return this.achievements.load()
|
||||
.then(() => {
|
||||
if (!this.achievements.hasAchievements()) {
|
||||
logger.log("Achievements disabled");
|
||||
return;
|
||||
}
|
||||
logger.log("There are", this.achievements.count, "achievements");
|
||||
|
||||
logger.log("There are", this.achievements.count, "achievements");
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error("Achievements failed to load", err);
|
||||
})
|
||||
this.root.signals.achievementUnlocked.add(this.unlock, this);
|
||||
}
|
||||
|
||||
unlock (key) {
|
||||
|
@ -179,6 +179,9 @@ export class GameRoot {
|
||||
// Called before actually placing an entity, use to perform additional logic
|
||||
// for freeing space before actually placing.
|
||||
freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||
|
||||
// Called with the key of the unlocked achievement
|
||||
achievementUnlocked: /** @type {TypedSignal<[string]>} */ (new Signal()),
|
||||
};
|
||||
|
||||
// RNG's
|
||||
|
@ -97,7 +97,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
|
||||
const rightSide = definition.cloneFilteredByQuadrants([2, 3]);
|
||||
const leftSide = definition.cloneFilteredByQuadrants([0, 1]);
|
||||
|
||||
this.root.achievementMgr.unlock(ACHIEVEMENTS.cutting);
|
||||
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.cutting);
|
||||
|
||||
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.achievementMgr.unlock(ACHIEVEMENTS.rotating);
|
||||
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.rotating);
|
||||
|
||||
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
||||
rotated
|
||||
@ -208,13 +208,11 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
|
||||
*/
|
||||
shapeActionPaintWith(definition, color) {
|
||||
const key = "paint/" + definition.getHash() + "/" + color;
|
||||
logger.debug("shapePainted", definition, color);
|
||||
if (this.operationCache[key]) {
|
||||
logger.debug("shapePaintedCache", definition, color);
|
||||
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
|
||||
}
|
||||
|
||||
this.root.achievementMgr.unlock(ACHIEVEMENTS.painting);
|
||||
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.painting);
|
||||
|
||||
const colorized = definition.cloneAndPaintWith(color);
|
||||
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
||||
|
@ -11,8 +11,8 @@ export const ACHIEVEMENTS = {
|
||||
}
|
||||
|
||||
export class AchievementsInterface {
|
||||
/** @param {Application} app */
|
||||
constructor(app) {
|
||||
/** @type {Application} */
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
@ -30,13 +30,12 @@ export class AchievementsInterface {
|
||||
|
||||
/**
|
||||
* Call to unlock an achievement
|
||||
* @params [key] - A property within the ACHIEVEMENTS enum or empty if
|
||||
* @params {string} [key] - A property within the ACHIEVEMENTS enum or empty if
|
||||
* bypassing.
|
||||
* @returns {(undefined|Promise)}
|
||||
* @returns {void}
|
||||
*/
|
||||
unlock(key) {
|
||||
abstract;
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,10 +1,6 @@
|
||||
import { AchievementsInterface } from "../achievements";
|
||||
|
||||
export class NoAchievements extends AchievementsInterface {
|
||||
initialize() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
load() {
|
||||
return Promise.resolve();
|
||||
}
|
@ -4,7 +4,7 @@ import { queryParamOptions } from "../../core/query_parameters";
|
||||
import { clamp } from "../../core/utils";
|
||||
import { GamedistributionAdProvider } from "../ad_providers/gamedistribution";
|
||||
import { NoAdProvider } from "../ad_providers/no_ad_provider";
|
||||
import { Achievements } from "../achievements/achievements";
|
||||
import { SteamAchievements } from "../electron/steam_achievements";
|
||||
import { PlatformWrapperInterface } from "../wrapper";
|
||||
import { StorageImplBrowser } from "./storage";
|
||||
import { StorageImplBrowserIndexedDB } from "./storage_indexed_db";
|
||||
@ -70,9 +70,13 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
||||
|
||||
logger.log("Embed provider:", this.embedProvider.id);
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.testAchievements) {
|
||||
logger.log("Testing achievements");
|
||||
this.app.achievements = new SteamAchievements(this.app);
|
||||
}
|
||||
|
||||
return this.detectStorageImplementation()
|
||||
.then(() => this.initializeAdProvider())
|
||||
.then(() => this.initializeAchievements())
|
||||
.then(() => super.initialize());
|
||||
}
|
||||
|
||||
@ -198,16 +202,6 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
||||
});
|
||||
}
|
||||
|
||||
initializeAchievements() {
|
||||
if (G_IS_STANDALONE || (G_IS_DEV && globalConfig.debug.testAchievements)) {
|
||||
this.app.achievements = new Achievements(this.app);
|
||||
return this.app.achievements.initialize();
|
||||
}
|
||||
|
||||
logger.log("Achievements are not supported in this environment");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
exitApp() {
|
||||
// Can not exit app
|
||||
}
|
||||
|
@ -12,8 +12,21 @@ const IDS = {
|
||||
blueprints: "<id>"
|
||||
}
|
||||
|
||||
export class Achievements extends AchievementsInterface {
|
||||
initialize() {
|
||||
/** @typedef {object} SteamAchievementMap
|
||||
* @property {string} id
|
||||
* @property {string} key
|
||||
* @property {boolean} unlocked
|
||||
* @property {boolean} relevant
|
||||
* @property {?Promise} activate
|
||||
*/
|
||||
|
||||
/** @typedef {Map<string, SteamAchievement>} SteamAchievementMap */
|
||||
|
||||
export class SteamAchievements extends AchievementsInterface {
|
||||
constructor(app) {
|
||||
super(app);
|
||||
|
||||
/** @type {AchievementMap} */
|
||||
this.map = new Map();
|
||||
this.type = "Steam";
|
||||
this.count = 0;
|
||||
@ -21,21 +34,22 @@ export class Achievements extends AchievementsInterface {
|
||||
logger.log("Initializing", this.type, "achievements");
|
||||
|
||||
for (let key in ACHIEVEMENTS) {
|
||||
this.map[key] = new Map();
|
||||
this.map[key].id = IDS[key];
|
||||
this.map[key].key = key;
|
||||
this.map[key].unlocked = false;
|
||||
this.map[key].relevant = true;
|
||||
this.map.set(key, {
|
||||
id: IDS[key],
|
||||
key: key,
|
||||
unlocked: false,
|
||||
relevant: true,
|
||||
activate: null
|
||||
});
|
||||
|
||||
this.count++;
|
||||
}
|
||||
|
||||
this.logOnly = globalConfig.debug.testAchievements;
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
load () {
|
||||
// TODO: inspect safe file and update achievements
|
||||
// TODO: inspect save file and update achievements
|
||||
// Consider removing load since there's no async behavior anticipated
|
||||
return Promise.resolve();
|
||||
}
|
||||
@ -44,15 +58,15 @@ export class Achievements extends AchievementsInterface {
|
||||
* @param {string} key
|
||||
*/
|
||||
unlock (key) {
|
||||
let achievement = this.map[key];
|
||||
|
||||
if (!achievement) {
|
||||
if (!this.map.has(key)) {
|
||||
logger.error("Achievement does not exist:", key);
|
||||
return;
|
||||
}
|
||||
|
||||
let achievement = this.map.get(key);
|
||||
|
||||
if (!achievement.relevant) {
|
||||
logger.debug("Achievement unlocked/irrelevant:", key);
|
||||
console.log("Achievement unlocked/irrelevant:", key);
|
||||
return;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { PlatformWrapperImplBrowser } from "../browser/wrapper";
|
||||
import { getIPCRenderer } from "../../core/utils";
|
||||
import { createLogger } from "../../core/logging";
|
||||
import { StorageImplElectron } from "./storage";
|
||||
import { SteamAchievements } from "./steam_achievements";
|
||||
import { PlatformWrapperInterface } from "../wrapper";
|
||||
|
||||
const logger = createLogger("electron-wrapper");
|
||||
@ -9,6 +10,8 @@ const logger = createLogger("electron-wrapper");
|
||||
export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
|
||||
initialize() {
|
||||
this.app.storage = new StorageImplElectron(this);
|
||||
this.app.achievements = new SteamAchievements(this.app);
|
||||
|
||||
return PlatformWrapperInterface.prototype.initialize.call(this);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user