mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-03-02 03:39:21 +00:00
Prepare the achievements update
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
export const CHANGELOG = [
|
||||
{
|
||||
version: "1.2.3",
|
||||
date: "unreleased",
|
||||
version: "1.3.0",
|
||||
date: "12.03.2020",
|
||||
skin: "achievements",
|
||||
entries: [
|
||||
"There are now <strong>45 Steam Achievements!</strong>",
|
||||
"Fixed constant signals being editable from the regular layer",
|
||||
"Fixed items still overlapping sometimes between buildings and belts",
|
||||
"Updated translations (Thanks to all contributors!)",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -12,8 +12,6 @@ const logger = createLogger("achievement_proxy");
|
||||
|
||||
const ROTATER = "rotater";
|
||||
const DEFAULT = "default";
|
||||
const BELT = "belt";
|
||||
const LEVEL_26 = 26;
|
||||
|
||||
export class AchievementProxy {
|
||||
/** @param {GameRoot} root */
|
||||
@@ -22,7 +20,9 @@ export class AchievementProxy {
|
||||
this.provider = this.root.app.achievementProvider;
|
||||
this.disabled = true;
|
||||
|
||||
if (!this.provider.hasAchievements()) {
|
||||
if (G_IS_DEV && globalConfig.debug.testAchievements) {
|
||||
// still enable the proxy
|
||||
} else if (!this.provider.hasAchievements()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ export class AchievementProxy {
|
||||
}
|
||||
|
||||
onLoad() {
|
||||
this.provider.onLoad(this.root)
|
||||
this.provider
|
||||
.onLoad(this.root)
|
||||
.then(() => {
|
||||
this.disabled = false;
|
||||
logger.log("Recieving achievement signals");
|
||||
@@ -50,6 +51,8 @@ export class AchievementProxy {
|
||||
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.darkMode);
|
||||
|
||||
if (this.has(ACHIEVEMENTS.mam)) {
|
||||
this.root.signals.entityAdded.add(this.onMamFailure, this);
|
||||
this.root.signals.entityDestroyed.add(this.onMamFailure, this);
|
||||
this.root.signals.storyGoalCompleted.add(this.onStoryGoalCompleted, this);
|
||||
}
|
||||
|
||||
@@ -57,10 +60,6 @@ export class AchievementProxy {
|
||||
this.root.signals.entityAdded.add(this.onEntityAdded, this);
|
||||
}
|
||||
|
||||
if (this.has(ACHIEVEMENTS.noBeltUpgradesUntilBp)) {
|
||||
this.root.signals.upgradePurchased.add(this.onUpgradePurchased, this);
|
||||
}
|
||||
|
||||
this.startSlice();
|
||||
}
|
||||
|
||||
@@ -73,27 +72,38 @@ export class AchievementProxy {
|
||||
// Every other slice
|
||||
if (this.sliceIteration % 2 === 0) {
|
||||
this.root.signals.bulkAchievementCheck.dispatch(
|
||||
ACHIEVEMENTS.throughputBp25, this.sliceTime,
|
||||
ACHIEVEMENTS.throughputBp50, this.sliceTime,
|
||||
ACHIEVEMENTS.throughputLogo25, this.sliceTime,
|
||||
ACHIEVEMENTS.throughputLogo50, this.sliceTime,
|
||||
ACHIEVEMENTS.throughputRocket10, this.sliceTime,
|
||||
ACHIEVEMENTS.throughputRocket20, this.sliceTime
|
||||
ACHIEVEMENTS.throughputBp25,
|
||||
this.sliceTime,
|
||||
ACHIEVEMENTS.throughputBp50,
|
||||
this.sliceTime,
|
||||
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
|
||||
ACHIEVEMENTS.play1h,
|
||||
this.sliceTime,
|
||||
ACHIEVEMENTS.play10h,
|
||||
this.sliceTime,
|
||||
ACHIEVEMENTS.play20h,
|
||||
this.sliceTime
|
||||
);
|
||||
}
|
||||
|
||||
// Every 10th slice
|
||||
if (this.sliceIteration % 10 === 0) {
|
||||
this.provider.collection.clean();
|
||||
if (this.provider.collection) {
|
||||
this.provider.collection.clean();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.sliceIteration === this.sliceIterationLimit) {
|
||||
@@ -118,6 +128,9 @@ export class AchievementProxy {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
has(key) {
|
||||
if (!this.provider.collection) {
|
||||
return false;
|
||||
}
|
||||
return this.provider.collection.map.has(key);
|
||||
}
|
||||
|
||||
@@ -127,7 +140,7 @@ export class AchievementProxy {
|
||||
return;
|
||||
}
|
||||
|
||||
const building = getBuildingDataFromCode(entity.components.StaticMapEntity.code)
|
||||
const building = getBuildingDataFromCode(entity.components.StaticMapEntity.code);
|
||||
|
||||
if (building.metaInstance.id !== ROTATER) {
|
||||
return;
|
||||
@@ -143,28 +156,18 @@ export class AchievementProxy {
|
||||
|
||||
/** @param {number} level */
|
||||
onStoryGoalCompleted(level) {
|
||||
if (level === LEVEL_26) {
|
||||
if (level > 26) {
|
||||
this.root.signals.entityAdded.add(this.onMamFailure, this);
|
||||
this.root.signals.entityDestroyed.add(this.onMamFailure, this);
|
||||
} else if (level === LEVEL_26 + 1) {
|
||||
this.root.signals.storyGoalCompleted.remove(this.onStoryGoalCompleted, this);
|
||||
}
|
||||
|
||||
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.mam);
|
||||
|
||||
// reset on every level
|
||||
this.root.savegame.currentData.stats.failedMam = false;
|
||||
}
|
||||
|
||||
onMamFailure() {
|
||||
this.root.savegame.currentData.stats.failedMam = true;
|
||||
this.root.signals.entityAdded.remove(this.onMamFailure);
|
||||
this.root.signals.entityDestroyed.remove(this.onMamFailure);
|
||||
this.root.signals.storyGoalCompleted.remove(this.onStoryGoalCompleted);
|
||||
}
|
||||
|
||||
/** @param {string} upgrade */
|
||||
onUpgradePurchased(upgrade) {
|
||||
if (upgrade !== BELT) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.root.savegame.currentData.stats.upgradedBelt = true;
|
||||
this.root.signals.upgradePurchased.remove(this.onUpgradePurchased);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ export class GameRoot {
|
||||
|
||||
// Called with an achievement key and necessary args to validate it can be unlocked.
|
||||
achievementCheck: /** @type {TypedSignal<[string, *]>} */ (new Signal()),
|
||||
bulkAchievementCheck: /** @type {TypedSignal<[string, any]...>} */ (new Signal()),
|
||||
bulkAchievementCheck: /** @type {TypedSignal<(string|any)[]>} */ (new Signal()),
|
||||
};
|
||||
|
||||
// RNG's
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
/* typehints:start */
|
||||
import { Application } from "../application";
|
||||
import { StorageComponent } from "../game/components/storage";
|
||||
import { ShapeItem } from "../game/items/shape_item";
|
||||
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 { ShapeItem } from "../game/items/shape_item";
|
||||
|
||||
export const ACHIEVEMENTS = {
|
||||
belt500Tiles: "belt500Tiles",
|
||||
blueprint100k: "blueprint100k",
|
||||
@@ -55,16 +57,16 @@ export const ACHIEVEMENTS = {
|
||||
upgradesTier8: "upgradesTier8",
|
||||
};
|
||||
|
||||
/** @type {keyof typeof THEMES} */
|
||||
const DARK_MODE = "dark";
|
||||
|
||||
const HOUR_1 = 3600; // Seconds
|
||||
const HOUR_10 = HOUR_1 * 10;
|
||||
const HOUR_20 = HOUR_1 * 20;
|
||||
const ITEM_SHAPE = "shape";
|
||||
const ITEM_SHAPE = ShapeItem.getId();
|
||||
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 ROTATER_CCW_CODE = 12;
|
||||
const ROTATER_180_CODE = 13;
|
||||
const SHAPE_BP = "CbCbCbRb:CwCwCwCw";
|
||||
@@ -72,9 +74,15 @@ const SHAPE_LOGO = "RuCw--Cw:----Ru--";
|
||||
const SHAPE_MS_LOGO = "RgRyRbRr";
|
||||
const SHAPE_OLD_LEVEL_17 = "WrRgWrRg:CwCrCwCr:SgSgSgSg";
|
||||
const SHAPE_ROCKET = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
|
||||
|
||||
/** @type {Layer} */
|
||||
const WIRE_LAYER = "wires";
|
||||
|
||||
export class AchievementProviderInterface {
|
||||
/* typehints:start */
|
||||
collection = /** @type {AchievementCollection|undefined} */ (null);
|
||||
/* typehints:end */
|
||||
|
||||
/** @param {Application} app */
|
||||
constructor(app) {
|
||||
this.app = app;
|
||||
@@ -135,9 +143,7 @@ export class Achievement {
|
||||
this.signal = null;
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
}
|
||||
init() {}
|
||||
|
||||
isValid() {
|
||||
return true;
|
||||
@@ -193,19 +199,16 @@ export class AchievementCollection {
|
||||
this.add(ACHIEVEMENTS.logoBefore18, {
|
||||
isRelevant: this.isLogoBefore18Relevant,
|
||||
isValid: this.isLogoBefore18Valid,
|
||||
signal: "itemProduced"
|
||||
signal: "itemProduced",
|
||||
});
|
||||
this.add(ACHIEVEMENTS.mam, {
|
||||
isRelevant: this.isMamRelevant,
|
||||
isValid: this.isMamValid,
|
||||
signal: "storyGoalCompleted",
|
||||
});
|
||||
this.add(ACHIEVEMENTS.mapMarkers15, {
|
||||
isRelevant: this.isMapMarkers15Relevant,
|
||||
isValid: this.isMapMarkers15Valid,
|
||||
});
|
||||
this.add(ACHIEVEMENTS.noBeltUpgradesUntilBp, {
|
||||
init: this.initNoBeltUpgradesUntilBp,
|
||||
isRelevant: this.isNoBeltUpgradesUntilBpRelevant,
|
||||
isValid: this.isNoBeltUpgradesUntilBpValid,
|
||||
signal: "storyGoalCompleted",
|
||||
@@ -354,7 +357,8 @@ export class AchievementCollection {
|
||||
return;
|
||||
}
|
||||
|
||||
achievement.unlock()
|
||||
achievement
|
||||
.unlock()
|
||||
.then(() => {
|
||||
this.onActivate(null, key);
|
||||
})
|
||||
@@ -380,12 +384,13 @@ export class AchievementCollection {
|
||||
/** @param {string} key - Maps to an Achievement */
|
||||
remove(key) {
|
||||
const achievement = this.map.get(key);
|
||||
if (achievement) {
|
||||
if (achievement.receiver) {
|
||||
this.root.signals[achievement.signal].remove(achievement.receiver);
|
||||
}
|
||||
|
||||
if (achievement.receiver) {
|
||||
this.root.signals[achievement.signal].remove(achievement.receiver);
|
||||
this.map.delete(key);
|
||||
}
|
||||
|
||||
this.map.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,7 +452,7 @@ export class AchievementCollection {
|
||||
createLevelOptions(level) {
|
||||
return {
|
||||
isRelevant: () => this.root.hubGoals.level < level,
|
||||
isValid: (currentLevel) => currentLevel === level,
|
||||
isValid: currentLevel => currentLevel === level,
|
||||
signal: "storyGoalCompleted",
|
||||
};
|
||||
}
|
||||
@@ -455,17 +460,19 @@ export class AchievementCollection {
|
||||
createRateOptions(shape, rate) {
|
||||
return {
|
||||
isValid: () => {
|
||||
return this.root.productionAnalytics.getCurrentShapeRate(
|
||||
PRODUCED,
|
||||
this.root.shapeDefinitionMgr.getShapeFromShortKey(shape)
|
||||
) >= rate;
|
||||
}
|
||||
return (
|
||||
this.root.productionAnalytics.getCurrentShapeRate(
|
||||
enumAnalyticsDataSource.delivered,
|
||||
this.root.shapeDefinitionMgr.getShapeFromShortKey(shape)
|
||||
) >= rate
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
createShapeOptions(shape) {
|
||||
return {
|
||||
isValid: (item) => this.isShape(item, shape),
|
||||
isValid: item => this.isShape(item, shape),
|
||||
signal: "itemProduced",
|
||||
};
|
||||
}
|
||||
@@ -473,7 +480,7 @@ export class AchievementCollection {
|
||||
createSpeedOptions(level, time) {
|
||||
return {
|
||||
isRelevant: () => this.root.hubGoals.level <= level && this.root.time.now() < time,
|
||||
isValid: (currentLevel) => currentLevel === level && this.root.time.now() < time,
|
||||
isValid: currentLevel => currentLevel === level && this.root.time.now() < time,
|
||||
signal: "storyGoalCompleted",
|
||||
};
|
||||
}
|
||||
@@ -500,18 +507,12 @@ export class AchievementCollection {
|
||||
|
||||
/** @param {ShapeDefinition} definition @returns {boolean} */
|
||||
isBlueprint100kValid(definition) {
|
||||
return (
|
||||
definition.cachedHash === SHAPE_BP &&
|
||||
this.root.hubGoals.storedShapes[SHAPE_BP] >= 100000
|
||||
);
|
||||
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
|
||||
);
|
||||
return definition.cachedHash === SHAPE_BP && this.root.hubGoals.storedShapes[SHAPE_BP] >= 1000000;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
@@ -530,14 +531,18 @@ export class AchievementCollection {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (definition.cachedHash === this.root.gameMode.getBlueprintShapeKey()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const upgrades = this.root.gameMode.getUpgrades();
|
||||
for (let upgradeId in upgrades) {
|
||||
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
|
||||
const requiredShapes = upgrades[upgradeId][currentTier].required;
|
||||
|
||||
for (let i = 0; i < requiredShapes.length; i++) {
|
||||
if (definition.cachedHash === requiredShapes[i].shape) {
|
||||
return false;
|
||||
for (const tier in upgrades[upgradeId]) {
|
||||
const requiredShapes = upgrades[upgradeId][tier].required;
|
||||
for (let i = 0; i < requiredShapes.length; i++) {
|
||||
if (definition.cachedHash === requiredShapes[i].shape) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -555,28 +560,9 @@ export class AchievementCollection {
|
||||
return this.root.hubGoals.level < 18 && this.isShape(item, SHAPE_LOGO);
|
||||
}
|
||||
|
||||
initMam() {
|
||||
const stats = this.root.savegame.currentData.stats;
|
||||
|
||||
if (stats.failedMam === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.root.hubGoals.level === 26 && stats.failedMam === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
stats.failedMam = this.root.hubGoals.level < 26;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
isMamRelevant() {
|
||||
return this.root.hubGoals.level <= 26 && !this.root.savegame.currentData.stats.failedMam;
|
||||
}
|
||||
|
||||
/** @params {number} level @returns {boolean} */
|
||||
isMamValid(level) {
|
||||
return level === 27 && !this.root.savegame.currentData.stats.failedMam;
|
||||
isMamValid() {
|
||||
return this.root.hubGoals.level > 27 && !this.root.savegame.currentData.stats.failedMam;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
@@ -589,16 +575,6 @@ export class AchievementCollection {
|
||||
return count === 15;
|
||||
}
|
||||
|
||||
initNoBeltUpgradesUntilBp() {
|
||||
const stats = this.root.savegame.currentData.stats;
|
||||
|
||||
if (stats.upgradedBelt === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
stats.upgradedBelt = this.root.hubGoals.upgradeLevels.belt > 0;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
isNoBeltUpgradesUntilBpRelevant() {
|
||||
return this.root.hubGoals.level <= 12 && this.root.hubGoals.upgradeLevels.belt === 0;
|
||||
@@ -631,8 +607,7 @@ export class AchievementCollection {
|
||||
|
||||
/** @returns {boolean} */
|
||||
isNoInverseRotaterRelevant() {
|
||||
return this.root.hubGoals.level < 14 &&
|
||||
!this.root.savegame.currentData.stats.usedInverseRotater;
|
||||
return this.root.hubGoals.level < 14 && !this.root.savegame.currentData.stats.usedInverseRotater;
|
||||
}
|
||||
|
||||
/** @param {number} level @returns {boolean} */
|
||||
|
||||
@@ -203,12 +203,11 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
||||
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);
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
return this.app.achievementProvider.initialize();
|
||||
|
||||
@@ -78,7 +78,11 @@ export class Savegame extends ReadWriteProxy {
|
||||
return {
|
||||
version: this.getCurrentVersion(),
|
||||
dump: null,
|
||||
stats: {},
|
||||
stats: {
|
||||
failedMam: false,
|
||||
trashedCount: 0,
|
||||
usedInverseRotater: false,
|
||||
},
|
||||
lastUpdate: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ export class SavegameInterface_V1008 extends SavegameInterface_V1007 {
|
||||
}
|
||||
|
||||
Object.assign(data.stats, {
|
||||
failedMam: false,
|
||||
failedMam: true,
|
||||
trashedCount: 0,
|
||||
usedInverseRotater: false
|
||||
usedInverseRotater: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export class ChangelogState extends TextualGameState {
|
||||
for (let i = 0; i < entries.length; ++i) {
|
||||
const entry = entries[i];
|
||||
html += `
|
||||
<div class="entry">
|
||||
<div class="entry" data-changelog-skin="${entry.skin || "default"}">
|
||||
<span class="version">${entry.version}</span>
|
||||
<span class="date">${entry.date}</span>
|
||||
<ul class="changes">
|
||||
|
||||
@@ -66,7 +66,7 @@ export class MainMenuState extends GameState {
|
||||
<img src="${cachebust(
|
||||
G_CHINA_VERSION ? "res/logo_cn.png" : "res/logo.png"
|
||||
)}" alt="shapez.io Logo">
|
||||
<span class="updateLabel">v${G_BUILD_VERSION}</span>
|
||||
<span class="updateLabel">v${G_BUILD_VERSION} - Achievements!</span>
|
||||
</div>
|
||||
|
||||
<div class="mainWrapper ${showDemoBadges ? "demo" : "noDemo"}">
|
||||
|
||||
@@ -199,7 +199,9 @@ export class PreloadState extends GameState {
|
||||
for (let i = 0; i < changelogEntries.length; ++i) {
|
||||
const entry = changelogEntries[i];
|
||||
dialogHtml += `
|
||||
<div class="changelogDialogEntry">
|
||||
<div class="changelogDialogEntry" data-changelog-skin="${
|
||||
entry.skin || "default"
|
||||
}">
|
||||
<span class="version">${entry.version}</span>
|
||||
<span class="date">${entry.date}</span>
|
||||
<ul class="changes">
|
||||
|
||||
Reference in New Issue
Block a user