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

[WIP] Add boilerplate for achievement implementation

This commit is contained in:
Greg Considine 2021-02-22 09:06:33 -05:00
parent 14d09a7d52
commit 7c69331308
12 changed files with 227 additions and 25 deletions

4
.gitignore vendored
View File

@ -50,3 +50,7 @@ tmp_standalone_files
# Local config # Local config
config.local.js config.local.js
.DS_Store .DS_Store
# Editor artifacts
*.*.swp
*.*.swo

View File

@ -12,6 +12,9 @@ import { getPlatformName, waitNextFrame } from "./core/utils";
import { Vector } from "./core/vector"; import { Vector } from "./core/vector";
import { AdProviderInterface } from "./platform/ad_provider"; import { AdProviderInterface } from "./platform/ad_provider";
import { NoAdProvider } from "./platform/ad_providers/no_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 { AnalyticsInterface } from "./platform/analytics"; import { AnalyticsInterface } from "./platform/analytics";
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics"; import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
import { SoundImplBrowser } from "./platform/browser/sound"; import { SoundImplBrowser } from "./platform/browser/sound";
@ -85,6 +88,9 @@ export class Application {
/** @type {PlatformWrapperInterface} */ /** @type {PlatformWrapperInterface} */
this.platformWrapper = null; this.platformWrapper = null;
/** @type {AchievementsInterface} */
this.achievements = null;
/** @type {AdProviderInterface} */ /** @type {AdProviderInterface} */
this.adProvider = null; this.adProvider = null;
@ -137,6 +143,7 @@ export class Application {
this.sound = new SoundImplBrowser(this); this.sound = new SoundImplBrowser(this);
this.analytics = new GoogleAnalyticsImpl(this); this.analytics = new GoogleAnalyticsImpl(this);
this.gameAnalytics = new ShapezGameAnalytics(this); this.gameAnalytics = new ShapezGameAnalytics(this);
this.achievements = new NoAchievements(this);
} }
/** /**

View File

@ -3,10 +3,10 @@ export default {
/* dev:start */ /* dev:start */
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Quickly enters the game and skips the main menu - good for fast iterating // Quickly enters the game and skips the main menu - good for fast iterating
// fastGameEnter: true, fastGameEnter: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Skips any delays like transitions between states and such // Skips any delays like transitions between states and such
// noArtificialDelays: true, noArtificialDelays: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Disables writing of savegames, useful for testing the same savegame over and over // Disables writing of savegames, useful for testing the same savegame over and over
// disableSavegameWrite: true, // disableSavegameWrite: true,
@ -18,7 +18,7 @@ export default {
// showAcceptorEjectors: true, // showAcceptorEjectors: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Disables the music (Overrides any setting, can cause weird behaviour) // Disables the music (Overrides any setting, can cause weird behaviour)
// disableMusic: true, disableMusic: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Do not render static map entities (=most buildings) // Do not render static map entities (=most buildings)
// doNotRenderStatics: true, // doNotRenderStatics: true,
@ -59,6 +59,9 @@ export default {
// Enables ads in the local build (normally they are deactivated there) // Enables ads in the local build (normally they are deactivated there)
// testAds: true, // testAds: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Allows unlocked achievements to be logged to console in the local build
testAchievements: true,
// -----------------------------------------------------------------------------------
// Disables the automatic switch to an overview when zooming out // Disables the automatic switch to an overview when zooming out
// disableMapOverview: true, // disableMapOverview: true,
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------

View File

@ -0,0 +1,56 @@
/* typehints:start */
import { GameRoot } from "./root";
/* typehints:end */
import { globalConfig } from "../core/config";
import { createLogger } from "../core/logging";
const logger = createLogger("achievement_manager");
export class AchievementManager {
static getId() {
return "AchievementManager";
}
constructor(root) {
this.root = root;
this.achievements = null;
if (!this.root.app.achievements.hasAchievements()) {
logger.debug("Bypassing achievement set up");
// Set adhoc checks to reference a noop, ignore signals.
return;
}
this.init();
}
init () {
return this.root.app.achievements.load()
.then(() => {
this.achievements = this.root.app.achievements.getAchievements();
return this.setChecks();
})
}
setChecks () {
logger.debug("loaded", this.achievements);
// set checks on achievements
//this.root.signals.itemProduced.add(this.onItemProduced, this);
}
/**
* @param {BaseItem} item
*/
onItemProduced(item) {
logger.debug(item);
}
// Have one check function per achievement
isPainted () {
return
}
}

View File

@ -35,6 +35,7 @@ import { RegularGameMode } from "./modes/regular";
import { ProductionAnalytics } from "./production_analytics"; import { ProductionAnalytics } from "./production_analytics";
import { GameRoot } from "./root"; import { GameRoot } from "./root";
import { ShapeDefinitionManager } from "./shape_definition_manager"; import { ShapeDefinitionManager } from "./shape_definition_manager";
import { AchievementManager } from "./achievement_manager";
import { SoundProxy } from "./sound_proxy"; import { SoundProxy } from "./sound_proxy";
import { GameTime } from "./time/game_time"; import { GameTime } from "./time/game_time";
@ -118,6 +119,7 @@ export class GameCore {
root.entityMgr = new EntityManager(root); root.entityMgr = new EntityManager(root);
root.systemMgr = new GameSystemManager(root); root.systemMgr = new GameSystemManager(root);
root.shapeDefinitionMgr = new ShapeDefinitionManager(root); root.shapeDefinitionMgr = new ShapeDefinitionManager(root);
root.achievementMgr = new AchievementManager(root);
root.hubGoals = new HubGoals(root); root.hubGoals = new HubGoals(root);
root.productionAnalytics = new ProductionAnalytics(root); root.productionAnalytics = new ProductionAnalytics(root);
root.buffers = new BufferMaintainer(root); root.buffers = new BufferMaintainer(root);

View File

@ -8,6 +8,7 @@ import { createLogger } from "../core/logging";
import { GameTime } from "./time/game_time"; import { GameTime } from "./time/game_time";
import { EntityManager } from "./entity_manager"; import { EntityManager } from "./entity_manager";
import { GameSystemManager } from "./game_system_manager"; import { GameSystemManager } from "./game_system_manager";
import { AchievementManager } from "./achievement_manager";
import { GameHUD } from "./hud/hud"; import { GameHUD } from "./hud/hud";
import { MapView } from "./map_view"; import { MapView } from "./map_view";
import { Camera } from "./camera"; import { Camera } from "./camera";
@ -119,6 +120,9 @@ export class GameRoot {
/** @type {SoundProxy} */ /** @type {SoundProxy} */
this.soundProxy = null; this.soundProxy = null;
/** @type {AchievementManager} */
this.achievementMgr = null;
/** @type {ShapeDefinitionManager} */ /** @type {ShapeDefinitionManager} */
this.shapeDefinitionMgr = null; this.shapeDefinitionMgr = null;

View File

@ -0,0 +1,28 @@
/* typehints:start */
import { Application } from "../application";
/* typehints:end */
export class AchievementsInterface {
constructor(app) {
/** @type {Application} */
this.app = app;
}
/**
* Initializes the list of achievements
* @returns {Promise<void>}
*/
initialize() {
abstract;
return Promise.reject();
}
/**
* Checks if achievements are supported in the current build
* @returns {boolean}
*/
hasAchievements() {
abstract;
return false;
}
}

View File

@ -0,0 +1,79 @@
import { AchievementsInterface } from "../achievements";
import { globalConfig } from "../../core/config";
import { createLogger } from "../../core/logging";
import { newEmptyMap } from "../../core/utils";
//import { T } from "../../translations";
const logger = createLogger("achievements/default");
// Include API id per key
export const ACHIEVEMENTS = {
painting: "painting"
}
export class Achievements extends AchievementsInterface {
initialize() {
this.authTicket = null;
this.achievementNames = null;
this.achievements = null;
this.connected = false;
this.connectPromise = Promise.resolve();
if (globalConfig.debug.testAchievements) {
return Promise.resolve();
}
// Check for resolve in AchievementManager via load() to not block game state
// transition
this.connectPromise = this.fetchAuthTicket()
.then(() => this.fetchAchievementNames());
return Promise.resolve();
}
fetchAuthTicket () {
return Promise.resolve();
}
fetchAchievementNames () {
return Promise.resolve();
}
load () {
this.achievements = newEmptyMap();
for (let key in ACHIEVEMENTS) {
this.achievements[key] = newEmptyMap();
this.achievements[key].unlocked = false;
this.achievements[key].invalid = false;
}
return this.connectPromise
.then(() => {
// factor in game state, save data, then Steam data (if accessible) as
// source of truth.
})
}
/**
* @param {string} key
*/
fetchAchievement (key) {
return Promise.resolve();
}
/**
* @param {string} key
*/
unlockAchievement (key) {
return Promise.resolve();
}
getAchievements() {
return this.achievements;
}
hasAchievements() {
return true;
}
}

View File

@ -0,0 +1,11 @@
import { AchievementsInterface } from "../achievements";
export class NoAchievements extends AchievementsInterface {
initialize() {
return Promise.resolve();
}
hasAchievements() {
return false;
}
}

View File

@ -4,6 +4,7 @@ import { queryParamOptions } from "../../core/query_parameters";
import { clamp } from "../../core/utils"; import { clamp } from "../../core/utils";
import { GamedistributionAdProvider } from "../ad_providers/gamedistribution"; import { GamedistributionAdProvider } from "../ad_providers/gamedistribution";
import { NoAdProvider } from "../ad_providers/no_ad_provider"; import { NoAdProvider } from "../ad_providers/no_ad_provider";
import { Achievements } from "../achievements/achievements";
import { PlatformWrapperInterface } from "../wrapper"; import { PlatformWrapperInterface } from "../wrapper";
import { StorageImplBrowser } from "./storage"; import { StorageImplBrowser } from "./storage";
import { StorageImplBrowserIndexedDB } from "./storage_indexed_db"; import { StorageImplBrowserIndexedDB } from "./storage_indexed_db";
@ -71,6 +72,7 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
return this.detectStorageImplementation() return this.detectStorageImplementation()
.then(() => this.initializeAdProvider()) .then(() => this.initializeAdProvider())
.then(() => this.initializeAchievements())
.then(() => super.initialize()); .then(() => super.initialize());
} }
@ -196,6 +198,16 @@ 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() { exitApp() {
// Can not exit app // Can not exit app
} }

View File

@ -1207,6 +1207,11 @@ demo:
settingNotAvailable: Not available in the demo. settingNotAvailable: Not available in the demo.
achievements:
painting:
displayName: Painting
description: Paint a shape
tips: tips:
- The hub will accept any input, not just the current shape! - The hub will accept any input, not just the current shape!
- Make sure your factories are modular - it will pay out! - Make sure your factories are modular - it will pay out!

View File

@ -5103,14 +5103,6 @@ levn@^0.4.1:
prelude-ls "^1.2.1" prelude-ls "^1.2.1"
type-check "~0.4.0" type-check "~0.4.0"
line-column@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/line-column/-/line-column-1.0.2.tgz#d25af2936b6f4849172b312e4792d1d987bc34a2"
integrity sha1-0lryk2tvSEkXKzEuR5LR2Ye8NKI=
dependencies:
isarray "^1.0.0"
isobject "^2.0.0"
load-bmfont@^1.3.1, load-bmfont@^1.4.0: load-bmfont@^1.3.1, load-bmfont@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.0.tgz#75f17070b14a8c785fe7f5bee2e6fd4f98093b6b" resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.0.tgz#75f17070b14a8c785fe7f5bee2e6fd4f98093b6b"
@ -5619,10 +5611,10 @@ nan@^2.12.1:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
nanoid@^3.1.12: nanoid@^3.1.20:
version "3.1.12" version "3.1.20"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A== integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
nanomatch@^1.2.9: nanomatch@^1.2.9:
version "1.2.13" version "1.2.13"
@ -6927,6 +6919,15 @@ postcss-zindex@^4.0.1:
postcss "^7.0.0" postcss "^7.0.0"
uniqs "^2.0.0" uniqs "^2.0.0"
postcss@>=5.0.0:
version "8.2.6"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.6.tgz#5d69a974543b45f87e464bc4c3e392a97d6be9fe"
integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg==
dependencies:
colorette "^1.2.1"
nanoid "^3.1.20"
source-map "^0.6.1"
postcss@^5.0.2: postcss@^5.0.2:
version "5.2.18" version "5.2.18"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5"
@ -6955,16 +6956,6 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2
source-map "^0.6.1" source-map "^0.6.1"
supports-color "^6.1.0" supports-color "^6.1.0"
postcss@^8.1.1:
version "8.1.1"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.1.1.tgz#c3a287dd10e4f6c84cb3791052b96a5d859c9389"
integrity sha512-9DGLSsjooH3kSNjTZUOt2eIj2ZTW0VI2PZ/3My+8TC7KIbH2OKwUlISfDsf63EP4aiRUt3XkEWMWvyJHvJelEg==
dependencies:
colorette "^1.2.1"
line-column "^1.0.2"
nanoid "^3.1.12"
source-map "^0.6.1"
prelude-ls@^1.2.1: prelude-ls@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"