mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Add achievements and accommodations for switching states
- Fix startup code to avoid clobbering achievements on state switch - Add a few more achievements
This commit is contained in:
parent
835317477d
commit
f5bfd9cf07
@ -3,6 +3,7 @@ import { GameRoot } from "./root";
|
||||
/* typehints:end */
|
||||
|
||||
import { createLogger } from "../core/logging";
|
||||
import { ACHIEVEMENTS } from "../platform/achievement_provider";
|
||||
|
||||
const logger = createLogger("achievement_proxy");
|
||||
|
||||
@ -20,9 +21,10 @@ export class AchievementProxy {
|
||||
}
|
||||
|
||||
onLoad() {
|
||||
this.provider.initialize(this.root)
|
||||
this.provider.onLoad(this.root)
|
||||
.then(() => {
|
||||
logger.log("Listening for unlocked achievements");
|
||||
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.darkMode);
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error("Ignoring achievement signals", err);
|
||||
|
@ -180,8 +180,8 @@ export class GameRoot {
|
||||
// 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()),
|
||||
// Called with an achievement key and necessary args to validate it can be unlocked.
|
||||
achievementUnlocked: /** @type {TypedSignal<[string, ...*]>} */ (new Signal()),
|
||||
};
|
||||
|
||||
// RNG's
|
||||
|
@ -254,6 +254,7 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
|
||||
|
||||
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.theLogo, definition);
|
||||
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.toTheMoon, definition);
|
||||
this.root.signals.achievementUnlocked.dispatch(ACHIEVEMENTS.fourLayers, definition);
|
||||
|
||||
// logger.log("Registered shape with key (2)", id);
|
||||
return definition;
|
||||
|
@ -7,30 +7,34 @@ import { ShapeDefinition } from "../game/shape_definition";
|
||||
/* typehints:end */
|
||||
|
||||
export const ACHIEVEMENTS = {
|
||||
painting: "painting",
|
||||
blueprints: "blueprints",
|
||||
cutting: "cutting",
|
||||
darkMode: "darkMode",
|
||||
fourLayers: "fourLayers",
|
||||
freedom: "freedom",
|
||||
hundredShapes: "hundredShapes",
|
||||
longBelt: "longBelt",
|
||||
millionBlueprintShapes: "millionBlueprintShapes",
|
||||
networked: "networked",
|
||||
painting: "painting",
|
||||
rotating: "rotating",
|
||||
stacking: "stacking",
|
||||
blueprints: "blueprints",
|
||||
wires: "wires",
|
||||
storage: "storage",
|
||||
freedom: "freedom",
|
||||
networked: "networked",
|
||||
theLogo: "theLogo",
|
||||
toTheMoon: "toTheMoon",
|
||||
millionBlueprintShapes: "millionBlueprintShapes",
|
||||
|
||||
hundredShapes: "hundredShapes",
|
||||
wires: "wires",
|
||||
};
|
||||
|
||||
const BLUEPRINT_SHAPE = "CbCbCbRb:CwCwCwCw";
|
||||
const DARK_MODE = "dark";
|
||||
const FREEDOM_LEVEL = 26;
|
||||
const LOGO_SHAPE = "RuCw--Cw:----Ru--";
|
||||
const LONG_BELT_COUNT = 200;
|
||||
const NETWORKED_WIRE_COUNT = 100;
|
||||
const ONE_HUNDRED = 100;
|
||||
const ONE_MILLION = 1000000;
|
||||
const BLUEPRINT_SHAPE = "CbCbCbRb:CwCwCwCw";
|
||||
const LOGO_SHAPE = "RuCw--Cw:----Ru--";
|
||||
const ROCKET_SHAPE = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
|
||||
const FREEDOM_LEVEL = 26;
|
||||
const WIRES_LEVEL = 20;
|
||||
const NETWORKED_WIRE_COUNT = 100;
|
||||
|
||||
export class AchievementProviderInterface {
|
||||
/** @param {Application} app */
|
||||
@ -47,6 +51,26 @@ export class AchievementProviderInterface {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opportunity to do additional initialization work with the GameRoot.
|
||||
* @param {GameRoot} root
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
onLoad(root) {
|
||||
abstract;
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to activate an achievement with the provider
|
||||
* @param {string} key - Maps to an Achievement
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
activate(key) {
|
||||
abstract;
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if achievements are supported in the current build
|
||||
* @returns {boolean}
|
||||
@ -61,14 +85,17 @@ export class Achievement {
|
||||
/** @param {string} key - An ACHIEVEMENTS key */
|
||||
constructor(key) {
|
||||
this.key = key;
|
||||
this.unlocked = false;
|
||||
this.signal = null;
|
||||
this.receiver = null;
|
||||
this.activate = null;
|
||||
this.activatePromise = null;
|
||||
this.receiver = null;
|
||||
this.signal = null;
|
||||
}
|
||||
|
||||
isValid () {
|
||||
isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
isRelevant() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -83,18 +110,62 @@ export class Achievement {
|
||||
|
||||
export class AchievementCollection {
|
||||
/**
|
||||
* @param {string[]} keys - An array of ACHIEVEMENTS keys
|
||||
* @param {function} [activate] - Resolves when provider activation is complete
|
||||
* @param {function} activate - Resolves when provider activation is complete
|
||||
*/
|
||||
constructor(keys, activate) {
|
||||
constructor(activate) {
|
||||
this.map = new Map();
|
||||
this.activate = activate;
|
||||
this.initialized = false;
|
||||
|
||||
assert(Object.keys(ACHIEVEMENTS).length === keys.length, "Mismatched achievements");
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
assert(ACHIEVEMENTS[keys[i]], "Achievement does not exist: " + keys[i]);
|
||||
}
|
||||
this.createAndSet(ACHIEVEMENTS.blueprints, {
|
||||
isValid: this.isBlueprintsValid
|
||||
});
|
||||
this.createAndSet(ACHIEVEMENTS.cutting);
|
||||
this.createAndSet(ACHIEVEMENTS.darkMode, {
|
||||
isValid: this.isDarkModeValid
|
||||
});
|
||||
this.createAndSet(ACHIEVEMENTS.fourLayers, {
|
||||
isValid: this.isFourLayersValid
|
||||
});
|
||||
this.createAndSet(ACHIEVEMENTS.freedom, {
|
||||
isRelevant: this.isFreedomRelevant,
|
||||
isValid: this.isFreedomValid,
|
||||
signal: "storyGoalCompleted"
|
||||
});
|
||||
this.createAndSet(ACHIEVEMENTS.hundredShapes, {
|
||||
isRelevant: this.isHundredShapesRelevant,
|
||||
isValid: this.isHundredShapesValid,
|
||||
signal: "shapeDelivered"
|
||||
});
|
||||
this.createAndSet(ACHIEVEMENTS.longBelt, {
|
||||
isValid: this.isLongBeltValid,
|
||||
signal: "entityAdded"
|
||||
});
|
||||
this.createAndSet(ACHIEVEMENTS.millionBlueprintShapes, {
|
||||
isValid: this.isMillionBlueprintShapesValid,
|
||||
signal: "shapeDelivered"
|
||||
});
|
||||
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"
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {GameRoot} root */
|
||||
@ -102,58 +173,49 @@ export class AchievementCollection {
|
||||
this.root = root;
|
||||
this.root.signals.achievementUnlocked.add(this.unlock, this);
|
||||
|
||||
this.createAndSet(ACHIEVEMENTS.painting)
|
||||
this.createAndSet(ACHIEVEMENTS.cutting)
|
||||
this.createAndSet(ACHIEVEMENTS.rotating)
|
||||
this.createAndSet(ACHIEVEMENTS.stacking)
|
||||
this.createAndSet(ACHIEVEMENTS.blueprints, this.isBlueprintsValid);
|
||||
for (let [key, achievement] of this.map.entries()) {
|
||||
if (!achievement.isRelevant()) {
|
||||
this.remove(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.isWiresRelevant()) {
|
||||
this.createAndSet(ACHIEVEMENTS.wires, this.isWiresValid, "storyGoalCompleted");
|
||||
if (achievement.signal) {
|
||||
achievement.receiver = this.unlock.bind(this, key);
|
||||
this.root.signals[achievement.signal].add(achievement.receiver);
|
||||
}
|
||||
}
|
||||
|
||||
this.createAndSet(ACHIEVEMENTS.storage, this.isStorageValid, "entityGotNewComponent");
|
||||
|
||||
if (this.isFreedomRelevant()) { // ...is it?
|
||||
this.createAndSet(ACHIEVEMENTS.freedom, this.isFreedomValid, "storyGoalCompleted");
|
||||
if (!this.hasDefaultReceivers()) {
|
||||
this.root.signals.achievementUnlocked.remove(this.unlock);
|
||||
}
|
||||
|
||||
this.createAndSet(ACHIEVEMENTS.networked, this.isNetworkedValid);
|
||||
this.createAndSet(ACHIEVEMENTS.theLogo, this.isTheLogoValid);
|
||||
this.createAndSet(ACHIEVEMENTS.toTheMoon, this.isToTheMoonValid);
|
||||
this.createAndSet(
|
||||
ACHIEVEMENTS.millionBlueprintShapes,
|
||||
this.isMillionBlueprintShapesValid,
|
||||
"shapeDelivered"
|
||||
);
|
||||
|
||||
if (this.isHundredShapesRelevant()) {
|
||||
this.createAndSet(
|
||||
ACHIEVEMENTS.hundredShapes,
|
||||
this.isHundredShapesValid,
|
||||
"shapeDelivered"
|
||||
);
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key - Maps to an Achievement
|
||||
* @param {function} [isValid] - Validates achievement when a signal message is received
|
||||
* @param {string} [signal] - Signal name to listen to for unlock attempts
|
||||
* @param {object} [options]
|
||||
* @param {function} [options.isValid]
|
||||
* @param {function} [options.isRelevant]
|
||||
* @param {string} [options.signal]
|
||||
*/
|
||||
createAndSet(key, isValid, signal) {
|
||||
createAndSet(key, options) {
|
||||
const achievement = new Achievement(key);
|
||||
|
||||
achievement.activate = this.activate;
|
||||
|
||||
if (isValid) {
|
||||
achievement.isValid = isValid.bind(this);
|
||||
}
|
||||
if (options) {
|
||||
if (options.isValid) {
|
||||
achievement.isValid = options.isValid.bind(this);
|
||||
}
|
||||
|
||||
if (signal) {
|
||||
achievement.signal = signal;
|
||||
achievement.receiver = this.unlock.bind(this, key);
|
||||
this.root.signals[achievement.signal].add(achievement.receiver);
|
||||
if (options.isRelevant) {
|
||||
achievement.isRelevant = options.isRelevant.bind(this);
|
||||
}
|
||||
|
||||
if (options.signal) {
|
||||
achievement.signal = options.signal;
|
||||
}
|
||||
}
|
||||
|
||||
this.map.set(key, achievement);
|
||||
@ -165,39 +227,42 @@ export class AchievementCollection {
|
||||
*/
|
||||
unlock(key) {
|
||||
if (!this.map.has(key)) {
|
||||
console.log("Achievement unlocked or irrelevant:", key);
|
||||
return;
|
||||
}
|
||||
|
||||
const achievement = this.map.get(key);
|
||||
|
||||
if (!achievement.isValid(...arguments)) {
|
||||
console.log("Achievement is invalid:", key);
|
||||
return;
|
||||
}
|
||||
|
||||
return achievement.unlock()
|
||||
.finally(() => {
|
||||
if (achievement.receiver) {
|
||||
this.root.signals[achievement.signal].remove(achievement.receiver);
|
||||
console.log("Achievement receiver removed:", key);
|
||||
}
|
||||
|
||||
this.map.delete(key);
|
||||
this.remove(key);
|
||||
|
||||
if (!this.hasDefaultReceivers()) {
|
||||
this.root.signals.achievementUnlocked.remove(this.unlock);
|
||||
console.log("removed achievementUnlocked receiver");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {string} key - Maps to an Achievement */
|
||||
remove(key) {
|
||||
const achievement = this.map.get(key);
|
||||
|
||||
if (achievement.receiver) {
|
||||
this.root.signals[achievement.signal].remove(achievement.receiver);
|
||||
}
|
||||
|
||||
this.map.delete(key);
|
||||
}
|
||||
|
||||
hasDefaultReceivers() {
|
||||
if (!this.map.size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(let achievement of this.map.values()) {
|
||||
for (let achievement of this.map.values()) {
|
||||
if (!achievement.signal) {
|
||||
return true;
|
||||
}
|
||||
@ -303,4 +368,32 @@ export class AchievementCollection {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isDarkModeValid(key) {
|
||||
return this.root.app.settings.currentData.settings.theme === DARK_MODE;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,11 @@ export class NoAchievementProvider extends AchievementProviderInterface {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
unlock() {
|
||||
onLoad() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
activate() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import { GamedistributionAdProvider } from "../ad_providers/gamedistribution";
|
||||
import { NoAdProvider } from "../ad_providers/no_ad_provider";
|
||||
import { SteamAchievementProvider } from "../electron/steam_achievement_provider";
|
||||
import { PlatformWrapperInterface } from "../wrapper";
|
||||
import { NoAchievementProvider } from "./no_achievement_provider";
|
||||
import { StorageImplBrowser } from "./storage";
|
||||
import { StorageImplBrowserIndexedDB } from "./storage_indexed_db";
|
||||
|
||||
@ -70,13 +71,9 @@ 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.achievementProvider = new SteamAchievementProvider(this.app);
|
||||
}
|
||||
|
||||
return this.detectStorageImplementation()
|
||||
.then(() => this.initializeAdProvider())
|
||||
.then(() => this.initializeAchievementProvider())
|
||||
.then(() => super.initialize());
|
||||
}
|
||||
|
||||
@ -202,6 +199,21 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
||||
});
|
||||
}
|
||||
|
||||
initializeAchievementProvider() {
|
||||
if (G_IS_DEV && globalConfig.debug.testAchievements) {
|
||||
this.app.achievementProvider = new SteamAchievementProvider(this.app);
|
||||
|
||||
return this.app.achievementProvider.initialize()
|
||||
.catch(err => {
|
||||
logger.error("Failed to initialize achievement provider, disabling:", err);
|
||||
|
||||
this.app.achievementProvider = new NoAchievementProvider(this.app);
|
||||
});
|
||||
}
|
||||
|
||||
return this.app.achievementProvider.initialize();
|
||||
}
|
||||
|
||||
exitApp() {
|
||||
// Can not exit app
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* typehints:start */
|
||||
import { Application } from "../../application";
|
||||
import { Achievement } from "../achievement_provider";
|
||||
import { GameRoot } from "../../game/root";
|
||||
/* typehints:end */
|
||||
|
||||
import { createLogger } from "../../core/logging";
|
||||
@ -14,20 +14,22 @@ import {
|
||||
const logger = createLogger("achievements/steam");
|
||||
|
||||
const ACHIEVEMENT_IDS = {
|
||||
[ACHIEVEMENTS.painting]: "<id>",
|
||||
[ACHIEVEMENTS.blueprints]: "<id>",
|
||||
[ACHIEVEMENTS.cutting]: "achievement_01", // Test ID
|
||||
[ACHIEVEMENTS.darkMode]: "<id>",
|
||||
[ACHIEVEMENTS.fourLayers]: "<id>",
|
||||
[ACHIEVEMENTS.freedom]: "<id>",
|
||||
[ACHIEVEMENTS.hundredShapes]: "<id>",
|
||||
[ACHIEVEMENTS.longBelt]: "<id>",
|
||||
[ACHIEVEMENTS.millionBlueprintShapes]: "<id>",
|
||||
[ACHIEVEMENTS.networked]: "<id>",
|
||||
[ACHIEVEMENTS.painting]: "<id>",
|
||||
[ACHIEVEMENTS.rotating]: "<id>",
|
||||
[ACHIEVEMENTS.stacking]: "<id>",
|
||||
[ACHIEVEMENTS.blueprints]: "<id>",
|
||||
[ACHIEVEMENTS.wires]: "<id>",
|
||||
[ACHIEVEMENTS.storage]: "<id>",
|
||||
[ACHIEVEMENTS.freedom]: "<id>",
|
||||
[ACHIEVEMENTS.networked]: "<id>",
|
||||
[ACHIEVEMENTS.theLogo]: "<id>",
|
||||
[ACHIEVEMENTS.toTheMoon]: "<id>",
|
||||
[ACHIEVEMENTS.millionBlueprintShapes]: "<id>",
|
||||
|
||||
[ACHIEVEMENTS.hundredShapes]: "<id>",
|
||||
[ACHIEVEMENTS.wires]: "<id>",
|
||||
};
|
||||
|
||||
export class SteamAchievementProvider extends AchievementProviderInterface {
|
||||
@ -36,10 +38,9 @@ export class SteamAchievementProvider extends AchievementProviderInterface {
|
||||
super(app);
|
||||
|
||||
this.initialized = false;
|
||||
this.keys = Object.keys(ACHIEVEMENT_IDS);
|
||||
this.collection = new AchievementCollection(this.keys, this.activate.bind(this));
|
||||
this.collection = new AchievementCollection(this.activate.bind(this));
|
||||
|
||||
logger.log("Steam achievement collection created");
|
||||
logger.log("Collection created with", this.collection.map.size, "achievements");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,9 +50,23 @@ export class SteamAchievementProvider extends AchievementProviderInterface {
|
||||
return true;
|
||||
}
|
||||
|
||||
initialize (root) {
|
||||
this.collection.initialize(root);
|
||||
/** @param {GameRoot} root */
|
||||
onLoad(root) {
|
||||
if (this.collection.initialized) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
try {
|
||||
this.collection.initialize(root);
|
||||
logger.log(this.collection.map.size, "achievements are relevant and initialized");
|
||||
return Promise.resolve();
|
||||
} catch (err) {
|
||||
logger.error("Failed to initialize the achievement collection");
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
initialize() {
|
||||
if (!G_IS_STANDALONE) {
|
||||
logger.warn("Steam unavailable. Achievements won't sync.");
|
||||
return Promise.resolve();
|
||||
@ -68,11 +83,7 @@ export class SteamAchievementProvider extends AchievementProviderInterface {
|
||||
} else {
|
||||
logger.log("Steam achievement provider initialized");
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error("Steam achievement provider error", err);
|
||||
throw err;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,10 +101,10 @@ export class SteamAchievementProvider extends AchievementProviderInterface {
|
||||
|
||||
return promise
|
||||
.then(() => {
|
||||
logger.log("Achievement unlocked:", key);
|
||||
logger.log("Achievement activated:", key);
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error("Failed to unlock achievement:", key, err);
|
||||
logger.error("Failed to activate achievement:", key, err);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { NoAchievementProvider } from "../browser/no_achievement_provider";
|
||||
import { PlatformWrapperImplBrowser } from "../browser/wrapper";
|
||||
import { getIPCRenderer } from "../../core/utils";
|
||||
import { createLogger } from "../../core/logging";
|
||||
@ -22,7 +23,8 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
|
||||
this.app.storage = new StorageImplElectron(this);
|
||||
this.app.achievementProvider = new SteamAchievementProvider(this.app);
|
||||
|
||||
return PlatformWrapperInterface.prototype.initialize.call(this);
|
||||
return this.initializeAchievementProvider()
|
||||
.then(() => PlatformWrapperInterface.prototype.initialize.call(this));
|
||||
}
|
||||
|
||||
steamOverlayFixRedrawCanvas() {
|
||||
@ -55,6 +57,15 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
initializeAchievementProvider() {
|
||||
return this.app.achievementProvider.initialize()
|
||||
.catch(err => {
|
||||
logger.error("Failed to initialize achievement provider, disabling:", err);
|
||||
|
||||
this.app.achievementProvider = new NoAchievementProvider(this.app);
|
||||
});
|
||||
}
|
||||
|
||||
getSupportsFullscreen() {
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user