Introduce game modes and get rid of global level definitions etc
This commit is contained in:
parent
816fd37b55
commit
94266173d8
|
@ -1,13 +1,9 @@
|
||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { DrawParameters } from "../core/draw_parameters";
|
import { DrawParameters } from "../core/draw_parameters";
|
||||||
import { createLogger } from "../core/logging";
|
|
||||||
import { findNiceIntegerValue } from "../core/utils";
|
import { findNiceIntegerValue } from "../core/utils";
|
||||||
import { Vector } from "../core/vector";
|
import { Vector } from "../core/vector";
|
||||||
import { Entity } from "./entity";
|
import { Entity } from "./entity";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { blueprintShape } from "./upgrades";
|
|
||||||
|
|
||||||
const logger = createLogger("blueprint");
|
|
||||||
|
|
||||||
export class Blueprint {
|
export class Blueprint {
|
||||||
/**
|
/**
|
||||||
|
@ -142,7 +138,7 @@ export class Blueprint {
|
||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
canAfford(root) {
|
canAfford(root) {
|
||||||
return root.hubGoals.getShapesStoredByKey(blueprintShape) >= this.getCost();
|
return root.hubGoals.getShapesStoredByKey(root.gameMode.getBlueprintShapeKey()) >= this.getCost();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -31,6 +31,7 @@ import { KeyActionMapper } from "./key_action_mapper";
|
||||||
import { GameLogic } from "./logic";
|
import { GameLogic } from "./logic";
|
||||||
import { MapView } from "./map_view";
|
import { MapView } from "./map_view";
|
||||||
import { defaultBuildingVariant } from "./meta_building";
|
import { defaultBuildingVariant } from "./meta_building";
|
||||||
|
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";
|
||||||
|
@ -101,6 +102,9 @@ export class GameCore {
|
||||||
// Needs to come first
|
// Needs to come first
|
||||||
root.dynamicTickrate = new DynamicTickrate(root);
|
root.dynamicTickrate = new DynamicTickrate(root);
|
||||||
|
|
||||||
|
// Init game mode
|
||||||
|
root.gameMode = new RegularGameMode(root);
|
||||||
|
|
||||||
// Init classes
|
// Init classes
|
||||||
root.camera = new Camera(root);
|
root.camera = new Camera(root);
|
||||||
root.map = new MapView(root);
|
root.map = new MapView(root);
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* typehints:start */
|
||||||
|
import { enumHubGoalRewards } from "./tutorial_goals";
|
||||||
|
/* typehints:end */
|
||||||
|
|
||||||
|
import { GameRoot } from "./root";
|
||||||
|
|
||||||
|
/** @typedef {{
|
||||||
|
* shape: string,
|
||||||
|
* amount: number
|
||||||
|
* }} UpgradeRequirement */
|
||||||
|
|
||||||
|
/** @typedef {{
|
||||||
|
* required: Array<UpgradeRequirement>
|
||||||
|
* improvement?: number,
|
||||||
|
* excludePrevious?: boolean
|
||||||
|
* }} TierRequirement */
|
||||||
|
|
||||||
|
/** @typedef {Array<TierRequirement>} UpgradeTiers */
|
||||||
|
|
||||||
|
/** @typedef {{
|
||||||
|
* shape: string,
|
||||||
|
* required: number,
|
||||||
|
* reward: enumHubGoalRewards,
|
||||||
|
* throughputOnly?: boolean
|
||||||
|
* }} LevelDefinition */
|
||||||
|
|
||||||
|
export class GameMode {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {GameRoot} root
|
||||||
|
*/
|
||||||
|
constructor(root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return all available upgrades
|
||||||
|
* @returns {Object<string, UpgradeTiers>}
|
||||||
|
*/
|
||||||
|
getUpgrades() {
|
||||||
|
abstract;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the blueprint shape key
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getBlueprintShapeKey() {
|
||||||
|
abstract;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the goals for all levels including their reward
|
||||||
|
* @returns {Array<LevelDefinition>}
|
||||||
|
*/
|
||||||
|
getLevelDefinitions() {
|
||||||
|
abstract;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return whether free play is available or if the game stops
|
||||||
|
* after the predefined levels
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
getIsFreeplayAvailable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,13 @@
|
||||||
import { globalConfig, IS_DEMO } from "../core/config";
|
import { globalConfig, IS_DEMO } from "../core/config";
|
||||||
import { RandomNumberGenerator } from "../core/rng";
|
import { RandomNumberGenerator } from "../core/rng";
|
||||||
import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils";
|
import { clamp } from "../core/utils";
|
||||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||||
import { enumColors } from "./colors";
|
import { enumColors } from "./colors";
|
||||||
import { enumItemProcessorTypes } from "./components/item_processor";
|
import { enumItemProcessorTypes } from "./components/item_processor";
|
||||||
import { enumAnalyticsDataSource } from "./production_analytics";
|
import { enumAnalyticsDataSource } from "./production_analytics";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
||||||
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
|
import { enumHubGoalRewards } from "./tutorial_goals";
|
||||||
import { UPGRADES } from "./upgrades";
|
|
||||||
|
|
||||||
export class HubGoals extends BasicSerializableObject {
|
export class HubGoals extends BasicSerializableObject {
|
||||||
static getId() {
|
static getId() {
|
||||||
|
@ -23,27 +22,36 @@ export class HubGoals extends BasicSerializableObject {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialize(data) {
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} data
|
||||||
|
* @param {GameRoot} root
|
||||||
|
*/
|
||||||
|
deserialize(data, root) {
|
||||||
const errorCode = super.deserialize(data);
|
const errorCode = super.deserialize(data);
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_DEMO) {
|
const levels = root.gameMode.getLevelDefinitions();
|
||||||
this.level = Math.min(this.level, tutorialGoals.length);
|
|
||||||
|
// If freeplay is not available, clamp the level
|
||||||
|
if (!root.gameMode.getIsFreeplayAvailable()) {
|
||||||
|
this.level = Math.min(this.level, levels.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute gained rewards
|
// Compute gained rewards
|
||||||
for (let i = 0; i < this.level - 1; ++i) {
|
for (let i = 0; i < this.level - 1; ++i) {
|
||||||
if (i < tutorialGoals.length) {
|
if (i < levels.length) {
|
||||||
const reward = tutorialGoals[i].reward;
|
const reward = levels[i].reward;
|
||||||
this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1;
|
this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute upgrade improvements
|
// Compute upgrade improvements
|
||||||
for (const upgradeId in UPGRADES) {
|
const upgrades = this.root.gameMode.getUpgrades();
|
||||||
const tiers = UPGRADES[upgradeId];
|
for (const upgradeId in upgrades) {
|
||||||
|
const tiers = upgrades[upgradeId];
|
||||||
const level = this.upgradeLevels[upgradeId] || 0;
|
const level = this.upgradeLevels[upgradeId] || 0;
|
||||||
let totalImprovement = 1;
|
let totalImprovement = 1;
|
||||||
for (let i = 0; i < level; ++i) {
|
for (let i = 0; i < level; ++i) {
|
||||||
|
@ -84,17 +92,16 @@ export class HubGoals extends BasicSerializableObject {
|
||||||
*/
|
*/
|
||||||
this.upgradeLevels = {};
|
this.upgradeLevels = {};
|
||||||
|
|
||||||
// Reset levels
|
|
||||||
for (const key in UPGRADES) {
|
|
||||||
this.upgradeLevels[key] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the improvements for all upgrades
|
* Stores the improvements for all upgrades
|
||||||
* @type {Object<string, number>}
|
* @type {Object<string, number>}
|
||||||
*/
|
*/
|
||||||
this.upgradeImprovements = {};
|
this.upgradeImprovements = {};
|
||||||
for (const key in UPGRADES) {
|
|
||||||
|
// Reset levels first
|
||||||
|
const upgrades = this.root.gameMode.getUpgrades();
|
||||||
|
for (const key in upgrades) {
|
||||||
|
this.upgradeLevels[key] = 0;
|
||||||
this.upgradeImprovements[key] = 1;
|
this.upgradeImprovements[key] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +127,10 @@ export class HubGoals extends BasicSerializableObject {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
isEndOfDemoReached() {
|
isEndOfDemoReached() {
|
||||||
return IS_DEMO && this.level >= tutorialGoals.length;
|
return (
|
||||||
|
!this.root.gameMode.getIsFreeplayAvailable() &&
|
||||||
|
this.level >= this.root.gameMode.getLevelDefinitions().length
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -215,8 +225,9 @@ export class HubGoals extends BasicSerializableObject {
|
||||||
*/
|
*/
|
||||||
computeNextGoal() {
|
computeNextGoal() {
|
||||||
const storyIndex = this.level - 1;
|
const storyIndex = this.level - 1;
|
||||||
if (storyIndex < tutorialGoals.length) {
|
const levels = this.root.gameMode.getLevelDefinitions();
|
||||||
const { shape, required, reward, throughputOnly } = tutorialGoals[storyIndex];
|
if (storyIndex < levels.length) {
|
||||||
|
const { shape, required, reward, throughputOnly } = levels[storyIndex];
|
||||||
this.currentGoal = {
|
this.currentGoal = {
|
||||||
/** @type {ShapeDefinition} */
|
/** @type {ShapeDefinition} */
|
||||||
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape),
|
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape),
|
||||||
|
@ -254,7 +265,7 @@ export class HubGoals extends BasicSerializableObject {
|
||||||
* Returns whether we are playing in free-play
|
* Returns whether we are playing in free-play
|
||||||
*/
|
*/
|
||||||
isFreePlay() {
|
isFreePlay() {
|
||||||
return this.level >= tutorialGoals.length;
|
return this.level >= this.root.gameMode.getLevelDefinitions().length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -262,7 +273,7 @@ export class HubGoals extends BasicSerializableObject {
|
||||||
* @param {string} upgradeId
|
* @param {string} upgradeId
|
||||||
*/
|
*/
|
||||||
canUnlockUpgrade(upgradeId) {
|
canUnlockUpgrade(upgradeId) {
|
||||||
const tiers = UPGRADES[upgradeId];
|
const tiers = this.root.gameMode.getUpgrades()[upgradeId];
|
||||||
const currentLevel = this.getUpgradeLevel(upgradeId);
|
const currentLevel = this.getUpgradeLevel(upgradeId);
|
||||||
|
|
||||||
if (currentLevel >= tiers.length) {
|
if (currentLevel >= tiers.length) {
|
||||||
|
@ -296,7 +307,7 @@ export class HubGoals extends BasicSerializableObject {
|
||||||
*/
|
*/
|
||||||
getAvailableUpgradeCount() {
|
getAvailableUpgradeCount() {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (const upgradeId in UPGRADES) {
|
for (const upgradeId in this.root.gameMode.getUpgrades()) {
|
||||||
if (this.canUnlockUpgrade(upgradeId)) {
|
if (this.canUnlockUpgrade(upgradeId)) {
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
|
@ -314,7 +325,7 @@ export class HubGoals extends BasicSerializableObject {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgradeTiers = UPGRADES[upgradeId];
|
const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId];
|
||||||
const currentLevel = this.getUpgradeLevel(upgradeId);
|
const currentLevel = this.getUpgradeLevel(upgradeId);
|
||||||
|
|
||||||
const tierData = upgradeTiers[currentLevel];
|
const tierData = upgradeTiers[currentLevel];
|
||||||
|
|
|
@ -1,202 +1,203 @@
|
||||||
import { DrawParameters } from "../../../core/draw_parameters";
|
import { DrawParameters } from "../../../core/draw_parameters";
|
||||||
import { STOP_PROPAGATION } from "../../../core/signal";
|
import { STOP_PROPAGATION } from "../../../core/signal";
|
||||||
import { TrackedState } from "../../../core/tracked_state";
|
import { TrackedState } from "../../../core/tracked_state";
|
||||||
import { makeDiv } from "../../../core/utils";
|
import { makeDiv } from "../../../core/utils";
|
||||||
import { Vector } from "../../../core/vector";
|
import { Vector } from "../../../core/vector";
|
||||||
import { T } from "../../../translations";
|
import { SOUNDS } from "../../../platform/sound";
|
||||||
import { enumMouseButton } from "../../camera";
|
import { T } from "../../../translations";
|
||||||
import { KEYMAPPINGS } from "../../key_action_mapper";
|
import { Blueprint } from "../../blueprint";
|
||||||
import { blueprintShape } from "../../upgrades";
|
import { enumMouseButton } from "../../camera";
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { KEYMAPPINGS } from "../../key_action_mapper";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { Blueprint } from "../../blueprint";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
import { SOUNDS } from "../../../platform/sound";
|
|
||||||
|
export class HUDBlueprintPlacer extends BaseHUDPart {
|
||||||
export class HUDBlueprintPlacer extends BaseHUDPart {
|
createElements(parent) {
|
||||||
createElements(parent) {
|
const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey(
|
||||||
const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey(blueprintShape);
|
this.root.gameMode.getBlueprintShapeKey()
|
||||||
const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80);
|
);
|
||||||
|
const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80);
|
||||||
this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``);
|
|
||||||
|
this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``);
|
||||||
makeDiv(this.costDisplayParent, null, ["label"], T.ingame.blueprintPlacer.cost);
|
|
||||||
const costContainer = makeDiv(this.costDisplayParent, null, ["costContainer"], "");
|
makeDiv(this.costDisplayParent, null, ["label"], T.ingame.blueprintPlacer.cost);
|
||||||
this.costDisplayText = makeDiv(costContainer, null, ["costText"], "");
|
const costContainer = makeDiv(this.costDisplayParent, null, ["costContainer"], "");
|
||||||
costContainer.appendChild(blueprintCostShapeCanvas);
|
this.costDisplayText = makeDiv(costContainer, null, ["costText"], "");
|
||||||
}
|
costContainer.appendChild(blueprintCostShapeCanvas);
|
||||||
|
}
|
||||||
initialize() {
|
|
||||||
this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this);
|
initialize() {
|
||||||
|
this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this);
|
||||||
/** @type {TypedTrackedState<Blueprint?>} */
|
|
||||||
this.currentBlueprint = new TrackedState(this.onBlueprintChanged, this);
|
/** @type {TypedTrackedState<Blueprint?>} */
|
||||||
/** @type {Blueprint?} */
|
this.currentBlueprint = new TrackedState(this.onBlueprintChanged, this);
|
||||||
this.lastBlueprintUsed = null;
|
/** @type {Blueprint?} */
|
||||||
|
this.lastBlueprintUsed = null;
|
||||||
const keyActionMapper = this.root.keyMapper;
|
|
||||||
keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this);
|
const keyActionMapper = this.root.keyMapper;
|
||||||
keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.abortPlacement, this);
|
keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this);
|
||||||
keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this);
|
keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.abortPlacement, this);
|
||||||
keyActionMapper.getBinding(KEYMAPPINGS.massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this);
|
keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this);
|
||||||
|
keyActionMapper.getBinding(KEYMAPPINGS.massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this);
|
||||||
this.root.camera.downPreHandler.add(this.onMouseDown, this);
|
|
||||||
this.root.camera.movePreHandler.add(this.onMouseMove, this);
|
this.root.camera.downPreHandler.add(this.onMouseDown, this);
|
||||||
|
this.root.camera.movePreHandler.add(this.onMouseMove, this);
|
||||||
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this);
|
|
||||||
this.root.signals.editModeChanged.add(this.onEditModeChanged, this);
|
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this);
|
||||||
|
this.root.signals.editModeChanged.add(this.onEditModeChanged, this);
|
||||||
this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent);
|
|
||||||
this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this);
|
this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent);
|
||||||
}
|
this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this);
|
||||||
|
}
|
||||||
abortPlacement() {
|
|
||||||
if (this.currentBlueprint.get()) {
|
abortPlacement() {
|
||||||
this.currentBlueprint.set(null);
|
if (this.currentBlueprint.get()) {
|
||||||
|
this.currentBlueprint.set(null);
|
||||||
return STOP_PROPAGATION;
|
|
||||||
}
|
return STOP_PROPAGATION;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Called when the layer was changed
|
/**
|
||||||
* @param {Layer} layer
|
* Called when the layer was changed
|
||||||
*/
|
* @param {Layer} layer
|
||||||
onEditModeChanged(layer) {
|
*/
|
||||||
// Check if the layer of the blueprint differs and thus we have to deselect it
|
onEditModeChanged(layer) {
|
||||||
const blueprint = this.currentBlueprint.get();
|
// Check if the layer of the blueprint differs and thus we have to deselect it
|
||||||
if (blueprint) {
|
const blueprint = this.currentBlueprint.get();
|
||||||
if (blueprint.layer !== layer) {
|
if (blueprint) {
|
||||||
this.currentBlueprint.set(null);
|
if (blueprint.layer !== layer) {
|
||||||
}
|
this.currentBlueprint.set(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Called when the blueprint is now affordable or not
|
/**
|
||||||
* @param {boolean} canAfford
|
* Called when the blueprint is now affordable or not
|
||||||
*/
|
* @param {boolean} canAfford
|
||||||
onCanAffordChanged(canAfford) {
|
*/
|
||||||
this.costDisplayParent.classList.toggle("canAfford", canAfford);
|
onCanAffordChanged(canAfford) {
|
||||||
}
|
this.costDisplayParent.classList.toggle("canAfford", canAfford);
|
||||||
|
}
|
||||||
update() {
|
|
||||||
const currentBlueprint = this.currentBlueprint.get();
|
update() {
|
||||||
this.domAttach.update(currentBlueprint && currentBlueprint.getCost() > 0);
|
const currentBlueprint = this.currentBlueprint.get();
|
||||||
this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root));
|
this.domAttach.update(currentBlueprint && currentBlueprint.getCost() > 0);
|
||||||
}
|
this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root));
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Called when the blueprint was changed
|
/**
|
||||||
* @param {Blueprint} blueprint
|
* Called when the blueprint was changed
|
||||||
*/
|
* @param {Blueprint} blueprint
|
||||||
onBlueprintChanged(blueprint) {
|
*/
|
||||||
if (blueprint) {
|
onBlueprintChanged(blueprint) {
|
||||||
this.lastBlueprintUsed = blueprint;
|
if (blueprint) {
|
||||||
this.costDisplayText.innerText = "" + blueprint.getCost();
|
this.lastBlueprintUsed = blueprint;
|
||||||
}
|
this.costDisplayText.innerText = "" + blueprint.getCost();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* mouse down pre handler
|
/**
|
||||||
* @param {Vector} pos
|
* mouse down pre handler
|
||||||
* @param {enumMouseButton} button
|
* @param {Vector} pos
|
||||||
*/
|
* @param {enumMouseButton} button
|
||||||
onMouseDown(pos, button) {
|
*/
|
||||||
if (button === enumMouseButton.right) {
|
onMouseDown(pos, button) {
|
||||||
if (this.currentBlueprint.get()) {
|
if (button === enumMouseButton.right) {
|
||||||
this.abortPlacement();
|
if (this.currentBlueprint.get()) {
|
||||||
return STOP_PROPAGATION;
|
this.abortPlacement();
|
||||||
}
|
return STOP_PROPAGATION;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const blueprint = this.currentBlueprint.get();
|
|
||||||
if (!blueprint) {
|
const blueprint = this.currentBlueprint.get();
|
||||||
return;
|
if (!blueprint) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
if (!blueprint.canAfford(this.root)) {
|
|
||||||
this.root.soundProxy.playUiError();
|
if (!blueprint.canAfford(this.root)) {
|
||||||
return;
|
this.root.soundProxy.playUiError();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
const worldPos = this.root.camera.screenToWorld(pos);
|
|
||||||
const tile = worldPos.toTileSpace();
|
const worldPos = this.root.camera.screenToWorld(pos);
|
||||||
if (blueprint.tryPlace(this.root, tile)) {
|
const tile = worldPos.toTileSpace();
|
||||||
const cost = blueprint.getCost();
|
if (blueprint.tryPlace(this.root, tile)) {
|
||||||
this.root.hubGoals.takeShapeByKey(blueprintShape, cost);
|
const cost = blueprint.getCost();
|
||||||
this.root.soundProxy.playUi(SOUNDS.placeBuilding);
|
this.root.hubGoals.takeShapeByKey(this.root.gameMode.getBlueprintShapeKey(), cost);
|
||||||
}
|
this.root.soundProxy.playUi(SOUNDS.placeBuilding);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Mose move handler
|
/**
|
||||||
*/
|
* Mose move handler
|
||||||
onMouseMove() {
|
*/
|
||||||
// Prevent movement while blueprint is selected
|
onMouseMove() {
|
||||||
if (this.currentBlueprint.get()) {
|
// Prevent movement while blueprint is selected
|
||||||
return STOP_PROPAGATION;
|
if (this.currentBlueprint.get()) {
|
||||||
}
|
return STOP_PROPAGATION;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Called when an array of bulidings was selected
|
/**
|
||||||
* @param {Array<number>} uids
|
* Called when an array of bulidings was selected
|
||||||
*/
|
* @param {Array<number>} uids
|
||||||
createBlueprintFromBuildings(uids) {
|
*/
|
||||||
if (uids.length === 0) {
|
createBlueprintFromBuildings(uids) {
|
||||||
return;
|
if (uids.length === 0) {
|
||||||
}
|
return;
|
||||||
this.currentBlueprint.set(Blueprint.fromUids(this.root, uids));
|
}
|
||||||
}
|
this.currentBlueprint.set(Blueprint.fromUids(this.root, uids));
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Attempts to rotate the current blueprint
|
/**
|
||||||
*/
|
* Attempts to rotate the current blueprint
|
||||||
rotateBlueprint() {
|
*/
|
||||||
if (this.currentBlueprint.get()) {
|
rotateBlueprint() {
|
||||||
if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) {
|
if (this.currentBlueprint.get()) {
|
||||||
this.currentBlueprint.get().rotateCcw();
|
if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) {
|
||||||
} else {
|
this.currentBlueprint.get().rotateCcw();
|
||||||
this.currentBlueprint.get().rotateCw();
|
} else {
|
||||||
}
|
this.currentBlueprint.get().rotateCw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Attempts to paste the last blueprint
|
/**
|
||||||
*/
|
* Attempts to paste the last blueprint
|
||||||
pasteBlueprint() {
|
*/
|
||||||
if (this.lastBlueprintUsed !== null) {
|
pasteBlueprint() {
|
||||||
if (this.lastBlueprintUsed.layer !== this.root.currentLayer) {
|
if (this.lastBlueprintUsed !== null) {
|
||||||
// Not compatible
|
if (this.lastBlueprintUsed.layer !== this.root.currentLayer) {
|
||||||
this.root.soundProxy.playUiError();
|
// Not compatible
|
||||||
return;
|
this.root.soundProxy.playUiError();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
this.root.hud.signals.pasteBlueprintRequested.dispatch();
|
|
||||||
this.currentBlueprint.set(this.lastBlueprintUsed);
|
this.root.hud.signals.pasteBlueprintRequested.dispatch();
|
||||||
} else {
|
this.currentBlueprint.set(this.lastBlueprintUsed);
|
||||||
this.root.soundProxy.playUiError();
|
} else {
|
||||||
}
|
this.root.soundProxy.playUiError();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
*
|
/**
|
||||||
* @param {DrawParameters} parameters
|
*
|
||||||
*/
|
* @param {DrawParameters} parameters
|
||||||
draw(parameters) {
|
*/
|
||||||
const blueprint = this.currentBlueprint.get();
|
draw(parameters) {
|
||||||
if (!blueprint) {
|
const blueprint = this.currentBlueprint.get();
|
||||||
return;
|
if (!blueprint) {
|
||||||
}
|
return;
|
||||||
const mousePosition = this.root.app.mousePosition;
|
}
|
||||||
if (!mousePosition) {
|
const mousePosition = this.root.app.mousePosition;
|
||||||
// Not on screen
|
if (!mousePosition) {
|
||||||
return;
|
// Not on screen
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
const worldPos = this.root.camera.screenToWorld(mousePosition);
|
|
||||||
const tile = worldPos.toTileSpace();
|
const worldPos = this.root.camera.screenToWorld(mousePosition);
|
||||||
blueprint.draw(parameters, tile);
|
const tile = worldPos.toTileSpace();
|
||||||
}
|
blueprint.draw(parameters, tile);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
|
||||||
import { makeDiv } from "../../../core/utils";
|
import { makeDiv } from "../../../core/utils";
|
||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
import { blueprintShape, UPGRADES } from "../../upgrades";
|
|
||||||
import { enumNotificationType } from "./notifications";
|
import { enumNotificationType } from "./notifications";
|
||||||
import { tutorialGoals } from "../../tutorial_goals";
|
|
||||||
|
|
||||||
export class HUDSandboxController extends BaseHUDPart {
|
export class HUDSandboxController extends BaseHUDPart {
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
|
@ -75,10 +73,11 @@ export class HUDSandboxController extends BaseHUDPart {
|
||||||
}
|
}
|
||||||
|
|
||||||
giveBlueprints() {
|
giveBlueprints() {
|
||||||
if (!this.root.hubGoals.storedShapes[blueprintShape]) {
|
const shape = this.root.gameMode.getBlueprintShapeKey();
|
||||||
this.root.hubGoals.storedShapes[blueprintShape] = 0;
|
if (!this.root.hubGoals.storedShapes[shape]) {
|
||||||
|
this.root.hubGoals.storedShapes[shape] = 0;
|
||||||
}
|
}
|
||||||
this.root.hubGoals.storedShapes[blueprintShape] += 1e9;
|
this.root.hubGoals.storedShapes[shape] += 1e9;
|
||||||
}
|
}
|
||||||
|
|
||||||
maxOutAll() {
|
maxOutAll() {
|
||||||
|
@ -89,7 +88,7 @@ export class HUDSandboxController extends BaseHUDPart {
|
||||||
}
|
}
|
||||||
|
|
||||||
modifyUpgrade(id, amount) {
|
modifyUpgrade(id, amount) {
|
||||||
const upgradeTiers = UPGRADES[id];
|
const upgradeTiers = this.root.gameMode.getUpgrades()[id];
|
||||||
const maxLevel = upgradeTiers.length;
|
const maxLevel = upgradeTiers.length;
|
||||||
|
|
||||||
this.root.hubGoals.upgradeLevels[id] = Math.max(
|
this.root.hubGoals.upgradeLevels[id] = Math.max(
|
||||||
|
@ -122,9 +121,10 @@ export class HUDSandboxController extends BaseHUDPart {
|
||||||
|
|
||||||
// Compute gained rewards
|
// Compute gained rewards
|
||||||
hubGoals.gainedRewards = {};
|
hubGoals.gainedRewards = {};
|
||||||
|
const levels = this.root.gameMode.getLevelDefinitions();
|
||||||
for (let i = 0; i < hubGoals.level - 1; ++i) {
|
for (let i = 0; i < hubGoals.level - 1; ++i) {
|
||||||
if (i < tutorialGoals.length) {
|
if (i < levels.length) {
|
||||||
const reward = tutorialGoals[i].reward;
|
const reward = levels[i].reward;
|
||||||
hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1;
|
hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { InputReceiver } from "../../../core/input_receiver";
|
||||||
import { formatBigNumber, makeDiv } from "../../../core/utils";
|
import { formatBigNumber, makeDiv } from "../../../core/utils";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
|
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
|
||||||
import { UPGRADES } from "../../upgrades";
|
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ export class HUDShop extends BaseHUDPart {
|
||||||
this.upgradeToElements = {};
|
this.upgradeToElements = {};
|
||||||
|
|
||||||
// Upgrades
|
// Upgrades
|
||||||
for (const upgradeId in UPGRADES) {
|
for (const upgradeId in this.root.gameMode.getUpgrades()) {
|
||||||
const handle = {};
|
const handle = {};
|
||||||
handle.requireIndexToElement = [];
|
handle.requireIndexToElement = [];
|
||||||
|
|
||||||
|
@ -59,7 +58,7 @@ export class HUDShop extends BaseHUDPart {
|
||||||
rerenderFull() {
|
rerenderFull() {
|
||||||
for (const upgradeId in this.upgradeToElements) {
|
for (const upgradeId in this.upgradeToElements) {
|
||||||
const handle = this.upgradeToElements[upgradeId];
|
const handle = this.upgradeToElements[upgradeId];
|
||||||
const upgradeTiers = UPGRADES[upgradeId];
|
const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId];
|
||||||
|
|
||||||
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
|
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
|
||||||
const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId];
|
const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId];
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { globalConfig } from "../../../core/config";
|
import { globalConfig } from "../../../core/config";
|
||||||
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
||||||
|
import { InputReceiver } from "../../../core/input_receiver";
|
||||||
import { makeDiv } from "../../../core/utils";
|
import { makeDiv } from "../../../core/utils";
|
||||||
import { SOUNDS } from "../../../platform/sound";
|
import { SOUNDS } from "../../../platform/sound";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { defaultBuildingVariant } from "../../meta_building";
|
import { defaultBuildingVariant } from "../../meta_building";
|
||||||
import { enumHubGoalRewards, tutorialGoals } from "../../tutorial_goals";
|
import { enumHubGoalRewards } from "../../tutorial_goals";
|
||||||
|
import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings";
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings";
|
|
||||||
import { InputReceiver } from "../../../core/input_receiver";
|
|
||||||
import { enumNotificationType } from "./notifications";
|
import { enumNotificationType } from "./notifications";
|
||||||
|
|
||||||
export class HUDUnlockNotification extends BaseHUDPart {
|
export class HUDUnlockNotification extends BaseHUDPart {
|
||||||
|
@ -53,7 +53,9 @@ export class HUDUnlockNotification extends BaseHUDPart {
|
||||||
showForLevel(level, reward) {
|
showForLevel(level, reward) {
|
||||||
this.root.soundProxy.playUi(SOUNDS.levelComplete);
|
this.root.soundProxy.playUi(SOUNDS.levelComplete);
|
||||||
|
|
||||||
if (level > tutorialGoals.length) {
|
const levels = this.root.gameMode.getLevelDefinitions();
|
||||||
|
// Don't use getIsFreeplay() because we want the freeplay level up to show
|
||||||
|
if (level > levels.length) {
|
||||||
this.root.hud.signals.notification.dispatch(
|
this.root.hud.signals.notification.dispatch(
|
||||||
T.ingame.notifications.freeplayLevelComplete.replace("<level>", String(level)),
|
T.ingame.notifications.freeplayLevelComplete.replace("<level>", String(level)),
|
||||||
enumNotificationType.success
|
enumNotificationType.success
|
||||||
|
|
|
@ -0,0 +1,445 @@
|
||||||
|
import { IS_DEMO } from "../../core/config";
|
||||||
|
import { findNiceIntegerValue } from "../../core/utils";
|
||||||
|
import { GameMode } from "../game_mode";
|
||||||
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
|
|
||||||
|
const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
|
||||||
|
const finalGameShape = "RuCw--Cw:----Ru--";
|
||||||
|
const preparementShape = "CpRpCp--:SwSwSwSw";
|
||||||
|
const blueprintShape = "CbCbCbRb:CwCwCwCw";
|
||||||
|
|
||||||
|
const fixedImprovements = [0.5, 0.5, 1, 1, 2, 1, 1];
|
||||||
|
|
||||||
|
const numEndgameUpgrades = !IS_DEMO ? 20 - fixedImprovements.length - 1 : 0;
|
||||||
|
|
||||||
|
function generateEndgameUpgrades() {
|
||||||
|
return new Array(numEndgameUpgrades).fill(null).map((_, i) => ({
|
||||||
|
required: [
|
||||||
|
{ shape: preparementShape, amount: 30000 + i * 10000 },
|
||||||
|
{ shape: finalGameShape, amount: 20000 + i * 5000 },
|
||||||
|
{ shape: rocketShape, amount: 20000 + i * 5000 },
|
||||||
|
],
|
||||||
|
excludePrevious: true,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < numEndgameUpgrades; ++i) {
|
||||||
|
fixedImprovements.push(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Object<string, import("../game_mode").UpgradeTiers>} */
|
||||||
|
const cachedUpgrades = {
|
||||||
|
belt: [
|
||||||
|
{
|
||||||
|
required: [{ shape: "CuCuCuCu", amount: 60 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "--CuCu--", amount: 500 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "CpCpCpCp", amount: 1000 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 6000 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 25000 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: preparementShape, amount: 25000 }],
|
||||||
|
excludePrevious: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [
|
||||||
|
{ shape: preparementShape, amount: 25000 },
|
||||||
|
{ shape: finalGameShape, amount: 50000 },
|
||||||
|
],
|
||||||
|
excludePrevious: true,
|
||||||
|
},
|
||||||
|
...generateEndgameUpgrades(),
|
||||||
|
],
|
||||||
|
|
||||||
|
miner: [
|
||||||
|
{
|
||||||
|
required: [{ shape: "RuRuRuRu", amount: 300 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "Cu------", amount: 800 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "ScScScSc", amount: 3500 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 23000 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 50000 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: preparementShape, amount: 25000 }],
|
||||||
|
excludePrevious: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [
|
||||||
|
{ shape: preparementShape, amount: 25000 },
|
||||||
|
{ shape: finalGameShape, amount: 50000 },
|
||||||
|
],
|
||||||
|
excludePrevious: true,
|
||||||
|
},
|
||||||
|
...generateEndgameUpgrades(),
|
||||||
|
],
|
||||||
|
|
||||||
|
processors: [
|
||||||
|
{
|
||||||
|
required: [{ shape: "SuSuSuSu", amount: 500 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "RuRu----", amount: 600 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "CgScScCg", amount: 3500 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 25000 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 50000 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: preparementShape, amount: 25000 }],
|
||||||
|
excludePrevious: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [
|
||||||
|
{ shape: preparementShape, amount: 25000 },
|
||||||
|
{ shape: finalGameShape, amount: 50000 },
|
||||||
|
],
|
||||||
|
excludePrevious: true,
|
||||||
|
},
|
||||||
|
...generateEndgameUpgrades(),
|
||||||
|
],
|
||||||
|
|
||||||
|
painting: [
|
||||||
|
{
|
||||||
|
required: [{ shape: "RbRb----", amount: 600 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "WrWrWrWr", amount: 3800 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 6500 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 25000 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 50000 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [{ shape: preparementShape, amount: 25000 }],
|
||||||
|
excludePrevious: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: [
|
||||||
|
{ shape: preparementShape, amount: 25000 },
|
||||||
|
{ shape: finalGameShape, amount: 50000 },
|
||||||
|
],
|
||||||
|
excludePrevious: true,
|
||||||
|
},
|
||||||
|
...generateEndgameUpgrades(),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tiers need % of the previous tier as requirement too
|
||||||
|
const tierGrowth = 2.5;
|
||||||
|
|
||||||
|
// Automatically generate tier levels
|
||||||
|
for (const upgradeId in cachedUpgrades) {
|
||||||
|
const upgradeTiers = cachedUpgrades[upgradeId];
|
||||||
|
|
||||||
|
let currentTierRequirements = [];
|
||||||
|
for (let i = 0; i < upgradeTiers.length; ++i) {
|
||||||
|
const tierHandle = upgradeTiers[i];
|
||||||
|
tierHandle.improvement = fixedImprovements[i];
|
||||||
|
const originalRequired = tierHandle.required.slice();
|
||||||
|
|
||||||
|
for (let k = currentTierRequirements.length - 1; k >= 0; --k) {
|
||||||
|
const oldTierRequirement = currentTierRequirements[k];
|
||||||
|
if (!tierHandle.excludePrevious) {
|
||||||
|
tierHandle.required.unshift({
|
||||||
|
shape: oldTierRequirement.shape,
|
||||||
|
amount: oldTierRequirement.amount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentTierRequirements.push(
|
||||||
|
...originalRequired.map(req => ({
|
||||||
|
amount: req.amount,
|
||||||
|
shape: req.shape,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
currentTierRequirements.forEach(tier => {
|
||||||
|
tier.amount = findNiceIntegerValue(tier.amount * tierGrowth);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALIDATE
|
||||||
|
if (G_IS_DEV) {
|
||||||
|
for (const upgradeId in cachedUpgrades) {
|
||||||
|
cachedUpgrades[upgradeId].forEach(tier => {
|
||||||
|
tier.required.forEach(({ shape }) => {
|
||||||
|
try {
|
||||||
|
ShapeDefinition.fromShortKey(shape);
|
||||||
|
} catch (ex) {
|
||||||
|
throw new Error("Invalid upgrade goal: '" + ex + "' for shape" + shape);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const levelDefinitions = [
|
||||||
|
// 1
|
||||||
|
// Circle
|
||||||
|
{
|
||||||
|
shape: "CuCuCuCu", // belts t1
|
||||||
|
required: 30,
|
||||||
|
reward: enumHubGoalRewards.reward_cutter_and_trash,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 2
|
||||||
|
// Cutter
|
||||||
|
{
|
||||||
|
shape: "----CuCu", //
|
||||||
|
required: 40,
|
||||||
|
reward: enumHubGoalRewards.no_reward,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 3
|
||||||
|
// Rectangle
|
||||||
|
{
|
||||||
|
shape: "RuRuRuRu", // miners t1
|
||||||
|
required: 70,
|
||||||
|
reward: enumHubGoalRewards.reward_balancer,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 4
|
||||||
|
{
|
||||||
|
shape: "RuRu----", // processors t2
|
||||||
|
required: 70,
|
||||||
|
reward: enumHubGoalRewards.reward_rotater,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 5
|
||||||
|
// Rotater
|
||||||
|
{
|
||||||
|
shape: "Cu----Cu", // belts t2
|
||||||
|
required: 170,
|
||||||
|
reward: enumHubGoalRewards.reward_tunnel,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 6
|
||||||
|
{
|
||||||
|
shape: "Cu------", // miners t2
|
||||||
|
required: 270,
|
||||||
|
reward: enumHubGoalRewards.reward_painter,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 7
|
||||||
|
// Painter
|
||||||
|
{
|
||||||
|
shape: "CrCrCrCr", // unused
|
||||||
|
required: 300,
|
||||||
|
reward: enumHubGoalRewards.reward_rotater_ccw,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 8
|
||||||
|
{
|
||||||
|
shape: "RbRb----", // painter t2
|
||||||
|
required: 480,
|
||||||
|
reward: enumHubGoalRewards.reward_mixer,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 9
|
||||||
|
// Mixing (purple)
|
||||||
|
{
|
||||||
|
shape: "CpCpCpCp", // belts t3
|
||||||
|
required: 600,
|
||||||
|
reward: enumHubGoalRewards.reward_merger,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 10
|
||||||
|
// STACKER: Star shape + cyan
|
||||||
|
{
|
||||||
|
shape: "ScScScSc", // miners t3
|
||||||
|
required: 800,
|
||||||
|
reward: enumHubGoalRewards.reward_stacker,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 11
|
||||||
|
// Chainable miner
|
||||||
|
{
|
||||||
|
shape: "CgScScCg", // processors t3
|
||||||
|
required: 1000,
|
||||||
|
reward: enumHubGoalRewards.reward_miner_chainable,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 12
|
||||||
|
// Blueprints
|
||||||
|
{
|
||||||
|
shape: "CbCbCbRb:CwCwCwCw",
|
||||||
|
required: 1000,
|
||||||
|
reward: enumHubGoalRewards.reward_blueprints,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 13
|
||||||
|
// Tunnel Tier 2
|
||||||
|
{
|
||||||
|
shape: "RpRpRpRp:CwCwCwCw", // painting t3
|
||||||
|
required: 3800,
|
||||||
|
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
|
||||||
|
},
|
||||||
|
|
||||||
|
// DEMO STOPS HERE
|
||||||
|
...(IS_DEMO
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
shape: "RpRpRpRp:CwCwCwCw",
|
||||||
|
required: 0,
|
||||||
|
reward: enumHubGoalRewards.reward_demo_end,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
// 14
|
||||||
|
// Belt reader
|
||||||
|
{
|
||||||
|
shape: "--Cg----:--Cr----", // unused
|
||||||
|
required: 16, // Per second!
|
||||||
|
reward: enumHubGoalRewards.reward_belt_reader,
|
||||||
|
throughputOnly: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 15
|
||||||
|
// Storage
|
||||||
|
{
|
||||||
|
shape: "SrSrSrSr:CyCyCyCy", // unused
|
||||||
|
required: 10000,
|
||||||
|
reward: enumHubGoalRewards.reward_storage,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 16
|
||||||
|
// Quad Cutter
|
||||||
|
{
|
||||||
|
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
|
||||||
|
required: 6000,
|
||||||
|
reward: enumHubGoalRewards.reward_cutter_quad,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 17
|
||||||
|
// Double painter
|
||||||
|
{
|
||||||
|
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
|
||||||
|
required: 20000,
|
||||||
|
reward: enumHubGoalRewards.reward_painter_double,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 18
|
||||||
|
// Rotater (180deg)
|
||||||
|
{
|
||||||
|
shape: "Sg----Sg:CgCgCgCg:--CyCy--", // unused
|
||||||
|
required: 20000,
|
||||||
|
reward: enumHubGoalRewards.reward_rotater_180,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 19
|
||||||
|
// Compact splitter
|
||||||
|
{
|
||||||
|
shape: "CpRpCp--:SwSwSwSw",
|
||||||
|
required: 25000,
|
||||||
|
reward: enumHubGoalRewards.reward_splitter,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 20
|
||||||
|
// WIRES
|
||||||
|
{
|
||||||
|
shape: finalGameShape,
|
||||||
|
required: 25000,
|
||||||
|
reward: enumHubGoalRewards.reward_wires_painter_and_levers,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 21
|
||||||
|
// Filter
|
||||||
|
{
|
||||||
|
shape: "CrCwCrCw:CwCrCwCr:CrCwCrCw:CwCrCwCr",
|
||||||
|
required: 25000,
|
||||||
|
reward: enumHubGoalRewards.reward_filter,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 22
|
||||||
|
// Constant signal
|
||||||
|
{
|
||||||
|
shape: "Cg----Cr:Cw----Cw:Sy------:Cy----Cy",
|
||||||
|
required: 25000,
|
||||||
|
reward: enumHubGoalRewards.reward_constant_signal,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 23
|
||||||
|
// Display
|
||||||
|
{
|
||||||
|
shape: "CcSyCcSy:SyCcSyCc:CcSyCcSy",
|
||||||
|
required: 25000,
|
||||||
|
reward: enumHubGoalRewards.reward_display,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 24 Logic gates
|
||||||
|
{
|
||||||
|
shape: "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy",
|
||||||
|
required: 25000,
|
||||||
|
reward: enumHubGoalRewards.reward_logic_gates,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 25 Virtual Processing
|
||||||
|
{
|
||||||
|
shape: "Rg--Rg--:CwRwCwRw:--Rg--Rg",
|
||||||
|
required: 25000,
|
||||||
|
reward: enumHubGoalRewards.reward_virtual_processing,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 26 Freeplay
|
||||||
|
{
|
||||||
|
shape: "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw",
|
||||||
|
required: 50000,
|
||||||
|
reward: enumHubGoalRewards.reward_freeplay,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (G_IS_DEV) {
|
||||||
|
levelDefinitions.forEach(({ shape }) => {
|
||||||
|
try {
|
||||||
|
ShapeDefinition.fromShortKey(shape);
|
||||||
|
} catch (ex) {
|
||||||
|
throw new Error("Invalid tutorial goal: '" + ex + "' for shape" + shape);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RegularGameMode extends GameMode {
|
||||||
|
constructor(root) {
|
||||||
|
super(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUpgrades() {
|
||||||
|
return cachedUpgrades;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBlueprintShapeKey() {
|
||||||
|
return blueprintShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLevelDefinitions() {
|
||||||
|
return levelDefinitions;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,221 +1,225 @@
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
import { Signal } from "../core/signal";
|
import { Signal } from "../core/signal";
|
||||||
import { RandomNumberGenerator } from "../core/rng";
|
import { RandomNumberGenerator } from "../core/rng";
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
|
|
||||||
// Type hints
|
// Type hints
|
||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
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 { 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";
|
||||||
import { InGameState } from "../states/ingame";
|
import { InGameState } from "../states/ingame";
|
||||||
import { AutomaticSave } from "./automatic_save";
|
import { AutomaticSave } from "./automatic_save";
|
||||||
import { Application } from "../application";
|
import { Application } from "../application";
|
||||||
import { SoundProxy } from "./sound_proxy";
|
import { SoundProxy } from "./sound_proxy";
|
||||||
import { Savegame } from "../savegame/savegame";
|
import { Savegame } from "../savegame/savegame";
|
||||||
import { GameLogic } from "./logic";
|
import { GameLogic } from "./logic";
|
||||||
import { ShapeDefinitionManager } from "./shape_definition_manager";
|
import { ShapeDefinitionManager } from "./shape_definition_manager";
|
||||||
import { HubGoals } from "./hub_goals";
|
import { HubGoals } from "./hub_goals";
|
||||||
import { BufferMaintainer } from "../core/buffer_maintainer";
|
import { BufferMaintainer } from "../core/buffer_maintainer";
|
||||||
import { ProductionAnalytics } from "./production_analytics";
|
import { ProductionAnalytics } from "./production_analytics";
|
||||||
import { Entity } from "./entity";
|
import { Entity } from "./entity";
|
||||||
import { ShapeDefinition } from "./shape_definition";
|
import { ShapeDefinition } from "./shape_definition";
|
||||||
import { BaseItem } from "./base_item";
|
import { BaseItem } from "./base_item";
|
||||||
import { DynamicTickrate } from "./dynamic_tickrate";
|
import { DynamicTickrate } from "./dynamic_tickrate";
|
||||||
import { KeyActionMapper } from "./key_action_mapper";
|
import { KeyActionMapper } from "./key_action_mapper";
|
||||||
import { Vector } from "../core/vector";
|
import { Vector } from "../core/vector";
|
||||||
/* typehints:end */
|
import { GameMode } from "./game_mode";
|
||||||
|
/* typehints:end */
|
||||||
const logger = createLogger("game/root");
|
|
||||||
|
const logger = createLogger("game/root");
|
||||||
/** @type {Array<Layer>} */
|
|
||||||
export const layers = ["regular", "wires"];
|
/** @type {Array<Layer>} */
|
||||||
|
export const layers = ["regular", "wires"];
|
||||||
/**
|
|
||||||
* The game root is basically the whole game state at a given point,
|
/**
|
||||||
* combining all important classes. We don't have globals, but this
|
* The game root is basically the whole game state at a given point,
|
||||||
* class is passed to almost all game classes.
|
* combining all important classes. We don't have globals, but this
|
||||||
*/
|
* class is passed to almost all game classes.
|
||||||
export class GameRoot {
|
*/
|
||||||
/**
|
export class GameRoot {
|
||||||
* Constructs a new game root
|
/**
|
||||||
* @param {Application} app
|
* Constructs a new game root
|
||||||
*/
|
* @param {Application} app
|
||||||
constructor(app) {
|
*/
|
||||||
this.app = app;
|
constructor(app) {
|
||||||
|
this.app = app;
|
||||||
/** @type {Savegame} */
|
|
||||||
this.savegame = null;
|
/** @type {Savegame} */
|
||||||
|
this.savegame = null;
|
||||||
/** @type {InGameState} */
|
|
||||||
this.gameState = null;
|
/** @type {InGameState} */
|
||||||
|
this.gameState = null;
|
||||||
/** @type {KeyActionMapper} */
|
|
||||||
this.keyMapper = null;
|
/** @type {KeyActionMapper} */
|
||||||
|
this.keyMapper = null;
|
||||||
// Store game dimensions
|
|
||||||
this.gameWidth = 500;
|
// Store game dimensions
|
||||||
this.gameHeight = 500;
|
this.gameWidth = 500;
|
||||||
|
this.gameHeight = 500;
|
||||||
// Stores whether the current session is a fresh game (true), or was continued (false)
|
|
||||||
/** @type {boolean} */
|
// Stores whether the current session is a fresh game (true), or was continued (false)
|
||||||
this.gameIsFresh = true;
|
/** @type {boolean} */
|
||||||
|
this.gameIsFresh = true;
|
||||||
// Stores whether the logic is already initialized
|
|
||||||
/** @type {boolean} */
|
// Stores whether the logic is already initialized
|
||||||
this.logicInitialized = false;
|
/** @type {boolean} */
|
||||||
|
this.logicInitialized = false;
|
||||||
// Stores whether the game is already initialized, that is, all systems etc have been created
|
|
||||||
/** @type {boolean} */
|
// Stores whether the game is already initialized, that is, all systems etc have been created
|
||||||
this.gameInitialized = false;
|
/** @type {boolean} */
|
||||||
|
this.gameInitialized = false;
|
||||||
/**
|
|
||||||
* Whether a bulk operation is running
|
/**
|
||||||
*/
|
* Whether a bulk operation is running
|
||||||
this.bulkOperationRunning = false;
|
*/
|
||||||
|
this.bulkOperationRunning = false;
|
||||||
//////// Other properties ///////
|
|
||||||
|
//////// Other properties ///////
|
||||||
/** @type {Camera} */
|
|
||||||
this.camera = null;
|
/** @type {Camera} */
|
||||||
|
this.camera = null;
|
||||||
/** @type {HTMLCanvasElement} */
|
|
||||||
this.canvas = null;
|
/** @type {HTMLCanvasElement} */
|
||||||
|
this.canvas = null;
|
||||||
/** @type {CanvasRenderingContext2D} */
|
|
||||||
this.context = null;
|
/** @type {CanvasRenderingContext2D} */
|
||||||
|
this.context = null;
|
||||||
/** @type {MapView} */
|
|
||||||
this.map = null;
|
/** @type {MapView} */
|
||||||
|
this.map = null;
|
||||||
/** @type {GameLogic} */
|
|
||||||
this.logic = null;
|
/** @type {GameLogic} */
|
||||||
|
this.logic = null;
|
||||||
/** @type {EntityManager} */
|
|
||||||
this.entityMgr = null;
|
/** @type {EntityManager} */
|
||||||
|
this.entityMgr = null;
|
||||||
/** @type {GameHUD} */
|
|
||||||
this.hud = null;
|
/** @type {GameHUD} */
|
||||||
|
this.hud = null;
|
||||||
/** @type {GameSystemManager} */
|
|
||||||
this.systemMgr = null;
|
/** @type {GameSystemManager} */
|
||||||
|
this.systemMgr = null;
|
||||||
/** @type {GameTime} */
|
|
||||||
this.time = null;
|
/** @type {GameTime} */
|
||||||
|
this.time = null;
|
||||||
/** @type {HubGoals} */
|
|
||||||
this.hubGoals = null;
|
/** @type {HubGoals} */
|
||||||
|
this.hubGoals = null;
|
||||||
/** @type {BufferMaintainer} */
|
|
||||||
this.buffers = null;
|
/** @type {BufferMaintainer} */
|
||||||
|
this.buffers = null;
|
||||||
/** @type {AutomaticSave} */
|
|
||||||
this.automaticSave = null;
|
/** @type {AutomaticSave} */
|
||||||
|
this.automaticSave = null;
|
||||||
/** @type {SoundProxy} */
|
|
||||||
this.soundProxy = null;
|
/** @type {SoundProxy} */
|
||||||
|
this.soundProxy = null;
|
||||||
/** @type {ShapeDefinitionManager} */
|
|
||||||
this.shapeDefinitionMgr = null;
|
/** @type {ShapeDefinitionManager} */
|
||||||
|
this.shapeDefinitionMgr = null;
|
||||||
/** @type {ProductionAnalytics} */
|
|
||||||
this.productionAnalytics = null;
|
/** @type {ProductionAnalytics} */
|
||||||
|
this.productionAnalytics = null;
|
||||||
/** @type {DynamicTickrate} */
|
|
||||||
this.dynamicTickrate = null;
|
/** @type {DynamicTickrate} */
|
||||||
|
this.dynamicTickrate = null;
|
||||||
/** @type {Layer} */
|
|
||||||
this.currentLayer = "regular";
|
/** @type {Layer} */
|
||||||
|
this.currentLayer = "regular";
|
||||||
this.signals = {
|
|
||||||
// Entities
|
/** @type {GameMode} */
|
||||||
entityManuallyPlaced: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
this.gameMode = null;
|
||||||
entityAdded: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
|
||||||
entityChanged: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
this.signals = {
|
||||||
entityGotNewComponent: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
// Entities
|
||||||
entityComponentRemoved: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
entityManuallyPlaced: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||||
entityQueuedForDestroy: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
entityAdded: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||||
entityDestroyed: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
entityChanged: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||||
|
entityGotNewComponent: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||||
// Global
|
entityComponentRemoved: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||||
resized: /** @type {TypedSignal<[number, number]>} */ (new Signal()),
|
entityQueuedForDestroy: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||||
readyToRender: /** @type {TypedSignal<[]>} */ (new Signal()),
|
entityDestroyed: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||||
aboutToDestruct: /** @type {TypedSignal<[]>} */ new Signal(),
|
|
||||||
|
// Global
|
||||||
// Game Hooks
|
resized: /** @type {TypedSignal<[number, number]>} */ (new Signal()),
|
||||||
gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved
|
readyToRender: /** @type {TypedSignal<[]>} */ (new Signal()),
|
||||||
gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored
|
aboutToDestruct: /** @type {TypedSignal<[]>} */ new Signal(),
|
||||||
|
|
||||||
gameFrameStarted: /** @type {TypedSignal<[]>} */ (new Signal()), // New frame
|
// Game Hooks
|
||||||
|
gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved
|
||||||
storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()),
|
gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored
|
||||||
upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()),
|
|
||||||
|
gameFrameStarted: /** @type {TypedSignal<[]>} */ (new Signal()), // New frame
|
||||||
// Called right after game is initialized
|
|
||||||
postLoadHook: /** @type {TypedSignal<[]>} */ (new Signal()),
|
storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()),
|
||||||
|
upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()),
|
||||||
shapeDelivered: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
|
|
||||||
itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()),
|
// Called right after game is initialized
|
||||||
|
postLoadHook: /** @type {TypedSignal<[]>} */ (new Signal()),
|
||||||
bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()),
|
|
||||||
|
shapeDelivered: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
|
||||||
editModeChanged: /** @type {TypedSignal<[Layer]>} */ (new Signal()),
|
itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()),
|
||||||
|
|
||||||
// Called to check if an entity can be placed, second parameter is an additional offset.
|
bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()),
|
||||||
// Use to introduce additional placement checks
|
|
||||||
prePlacementCheck: /** @type {TypedSignal<[Entity, Vector]>} */ (new Signal()),
|
editModeChanged: /** @type {TypedSignal<[Layer]>} */ (new Signal()),
|
||||||
|
|
||||||
// Called before actually placing an entity, use to perform additional logic
|
// Called to check if an entity can be placed, second parameter is an additional offset.
|
||||||
// for freeing space before actually placing.
|
// Use to introduce additional placement checks
|
||||||
freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
prePlacementCheck: /** @type {TypedSignal<[Entity, Vector]>} */ (new Signal()),
|
||||||
};
|
|
||||||
|
// Called before actually placing an entity, use to perform additional logic
|
||||||
// RNG's
|
// for freeing space before actually placing.
|
||||||
/** @type {Object.<string, Object.<string, RandomNumberGenerator>>} */
|
freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||||
this.rngs = {};
|
};
|
||||||
|
|
||||||
// Work queue
|
// RNG's
|
||||||
this.queue = {
|
/** @type {Object.<string, Object.<string, RandomNumberGenerator>>} */
|
||||||
requireRedraw: false,
|
this.rngs = {};
|
||||||
};
|
|
||||||
}
|
// Work queue
|
||||||
|
this.queue = {
|
||||||
/**
|
requireRedraw: false,
|
||||||
* Destructs the game root
|
};
|
||||||
*/
|
}
|
||||||
destruct() {
|
|
||||||
logger.log("destructing root");
|
/**
|
||||||
this.signals.aboutToDestruct.dispatch();
|
* Destructs the game root
|
||||||
|
*/
|
||||||
this.reset();
|
destruct() {
|
||||||
}
|
logger.log("destructing root");
|
||||||
|
this.signals.aboutToDestruct.dispatch();
|
||||||
/**
|
|
||||||
* Resets the whole root and removes all properties
|
this.reset();
|
||||||
*/
|
}
|
||||||
reset() {
|
|
||||||
if (this.signals) {
|
/**
|
||||||
// Destruct all signals
|
* Resets the whole root and removes all properties
|
||||||
for (let i = 0; i < this.signals.length; ++i) {
|
*/
|
||||||
this.signals[i].removeAll();
|
reset() {
|
||||||
}
|
if (this.signals) {
|
||||||
}
|
// Destruct all signals
|
||||||
|
for (let i = 0; i < this.signals.length; ++i) {
|
||||||
if (this.hud) {
|
this.signals[i].removeAll();
|
||||||
this.hud.cleanup();
|
}
|
||||||
}
|
}
|
||||||
if (this.camera) {
|
|
||||||
this.camera.cleanup();
|
if (this.hud) {
|
||||||
}
|
this.hud.cleanup();
|
||||||
|
}
|
||||||
// Finally free all properties
|
if (this.camera) {
|
||||||
for (let prop in this) {
|
this.camera.cleanup();
|
||||||
if (this.hasOwnProperty(prop)) {
|
}
|
||||||
delete this[prop];
|
|
||||||
}
|
// Finally free all properties
|
||||||
}
|
for (let prop in this) {
|
||||||
}
|
if (this.hasOwnProperty(prop)) {
|
||||||
}
|
delete this[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
|
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
|
||||||
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
||||||
import { ShapeDefinition } from "../shape_definition";
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
import { blueprintShape } from "../upgrades";
|
|
||||||
|
|
||||||
export class ConstantSignalSystem extends GameSystemWithFilter {
|
export class ConstantSignalSystem extends GameSystemWithFilter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
|
@ -61,7 +60,9 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
|
||||||
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
|
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
|
||||||
this.root.hubGoals.currentGoal.definition
|
this.root.hubGoals.currentGoal.definition
|
||||||
),
|
),
|
||||||
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(blueprintShape),
|
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(
|
||||||
|
this.root.gameMode.getBlueprintShapeKey()
|
||||||
|
),
|
||||||
...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key =>
|
...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key =>
|
||||||
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)
|
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
import { IS_DEMO } from "../core/config";
|
|
||||||
import { ShapeDefinition } from "./shape_definition";
|
|
||||||
import { finalGameShape } from "./upgrades";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Don't forget to also update tutorial_goals_mappings.js as well as the translations!
|
* Don't forget to also update tutorial_goals_mappings.js as well as the translations!
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
|
@ -40,229 +36,3 @@ export const enumHubGoalRewards = {
|
||||||
no_reward: "no_reward",
|
no_reward: "no_reward",
|
||||||
no_reward_freeplay: "no_reward_freeplay",
|
no_reward_freeplay: "no_reward_freeplay",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const tutorialGoals = [
|
|
||||||
// 1
|
|
||||||
// Circle
|
|
||||||
{
|
|
||||||
shape: "CuCuCuCu", // belts t1
|
|
||||||
required: 30,
|
|
||||||
reward: enumHubGoalRewards.reward_cutter_and_trash,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 2
|
|
||||||
// Cutter
|
|
||||||
{
|
|
||||||
shape: "----CuCu", //
|
|
||||||
required: 40,
|
|
||||||
reward: enumHubGoalRewards.no_reward,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 3
|
|
||||||
// Rectangle
|
|
||||||
{
|
|
||||||
shape: "RuRuRuRu", // miners t1
|
|
||||||
required: 70,
|
|
||||||
reward: enumHubGoalRewards.reward_balancer,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 4
|
|
||||||
{
|
|
||||||
shape: "RuRu----", // processors t2
|
|
||||||
required: 70,
|
|
||||||
reward: enumHubGoalRewards.reward_rotater,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 5
|
|
||||||
// Rotater
|
|
||||||
{
|
|
||||||
shape: "Cu----Cu", // belts t2
|
|
||||||
required: 170,
|
|
||||||
reward: enumHubGoalRewards.reward_tunnel,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 6
|
|
||||||
{
|
|
||||||
shape: "Cu------", // miners t2
|
|
||||||
required: 270,
|
|
||||||
reward: enumHubGoalRewards.reward_painter,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 7
|
|
||||||
// Painter
|
|
||||||
{
|
|
||||||
shape: "CrCrCrCr", // unused
|
|
||||||
required: 300,
|
|
||||||
reward: enumHubGoalRewards.reward_rotater_ccw,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 8
|
|
||||||
{
|
|
||||||
shape: "RbRb----", // painter t2
|
|
||||||
required: 480,
|
|
||||||
reward: enumHubGoalRewards.reward_mixer,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 9
|
|
||||||
// Mixing (purple)
|
|
||||||
{
|
|
||||||
shape: "CpCpCpCp", // belts t3
|
|
||||||
required: 600,
|
|
||||||
reward: enumHubGoalRewards.reward_merger,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 10
|
|
||||||
// STACKER: Star shape + cyan
|
|
||||||
{
|
|
||||||
shape: "ScScScSc", // miners t3
|
|
||||||
required: 800,
|
|
||||||
reward: enumHubGoalRewards.reward_stacker,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 11
|
|
||||||
// Chainable miner
|
|
||||||
{
|
|
||||||
shape: "CgScScCg", // processors t3
|
|
||||||
required: 1000,
|
|
||||||
reward: enumHubGoalRewards.reward_miner_chainable,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 12
|
|
||||||
// Blueprints
|
|
||||||
{
|
|
||||||
shape: "CbCbCbRb:CwCwCwCw",
|
|
||||||
required: 1000,
|
|
||||||
reward: enumHubGoalRewards.reward_blueprints,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 13
|
|
||||||
// Tunnel Tier 2
|
|
||||||
{
|
|
||||||
shape: "RpRpRpRp:CwCwCwCw", // painting t3
|
|
||||||
required: 3800,
|
|
||||||
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
|
|
||||||
},
|
|
||||||
|
|
||||||
// DEMO STOPS HERE
|
|
||||||
...(IS_DEMO
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
shape: "RpRpRpRp:CwCwCwCw",
|
|
||||||
required: 0,
|
|
||||||
reward: enumHubGoalRewards.reward_demo_end,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
// 14
|
|
||||||
// Belt reader
|
|
||||||
{
|
|
||||||
shape: "--Cg----:--Cr----", // unused
|
|
||||||
required: 16, // Per second!
|
|
||||||
reward: enumHubGoalRewards.reward_belt_reader,
|
|
||||||
throughputOnly: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 15
|
|
||||||
// Storage
|
|
||||||
{
|
|
||||||
shape: "SrSrSrSr:CyCyCyCy", // unused
|
|
||||||
required: 10000,
|
|
||||||
reward: enumHubGoalRewards.reward_storage,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 16
|
|
||||||
// Quad Cutter
|
|
||||||
{
|
|
||||||
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
|
|
||||||
required: 6000,
|
|
||||||
reward: enumHubGoalRewards.reward_cutter_quad,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 17
|
|
||||||
// Double painter
|
|
||||||
{
|
|
||||||
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
|
|
||||||
required: 20000,
|
|
||||||
reward: enumHubGoalRewards.reward_painter_double,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 18
|
|
||||||
// Rotater (180deg)
|
|
||||||
{
|
|
||||||
shape: "Sg----Sg:CgCgCgCg:--CyCy--", // unused
|
|
||||||
required: 20000,
|
|
||||||
reward: enumHubGoalRewards.reward_rotater_180,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 19
|
|
||||||
// Compact splitter
|
|
||||||
{
|
|
||||||
shape: "CpRpCp--:SwSwSwSw",
|
|
||||||
required: 25000,
|
|
||||||
reward: enumHubGoalRewards.reward_splitter,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 20
|
|
||||||
// WIRES
|
|
||||||
{
|
|
||||||
shape: finalGameShape,
|
|
||||||
required: 25000,
|
|
||||||
reward: enumHubGoalRewards.reward_wires_painter_and_levers,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 21
|
|
||||||
// Filter
|
|
||||||
{
|
|
||||||
shape: "CrCwCrCw:CwCrCwCr:CrCwCrCw:CwCrCwCr",
|
|
||||||
required: 25000,
|
|
||||||
reward: enumHubGoalRewards.reward_filter,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 22
|
|
||||||
// Constant signal
|
|
||||||
{
|
|
||||||
shape: "Cg----Cr:Cw----Cw:Sy------:Cy----Cy",
|
|
||||||
required: 25000,
|
|
||||||
reward: enumHubGoalRewards.reward_constant_signal,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 23
|
|
||||||
// Display
|
|
||||||
{
|
|
||||||
shape: "CcSyCcSy:SyCcSyCc:CcSyCcSy",
|
|
||||||
required: 25000,
|
|
||||||
reward: enumHubGoalRewards.reward_display,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 24 Logic gates
|
|
||||||
{
|
|
||||||
shape: "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy",
|
|
||||||
required: 25000,
|
|
||||||
reward: enumHubGoalRewards.reward_logic_gates,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 25 Virtual Processing
|
|
||||||
{
|
|
||||||
shape: "Rg--Rg--:CwRwCwRw:--Rg--Rg",
|
|
||||||
required: 25000,
|
|
||||||
reward: enumHubGoalRewards.reward_virtual_processing,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 26 Freeplay
|
|
||||||
{
|
|
||||||
shape: "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw",
|
|
||||||
required: 50000,
|
|
||||||
reward: enumHubGoalRewards.reward_freeplay,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
|
|
||||||
if (G_IS_DEV) {
|
|
||||||
tutorialGoals.forEach(({ shape }) => {
|
|
||||||
try {
|
|
||||||
ShapeDefinition.fromShortKey(shape);
|
|
||||||
} catch (ex) {
|
|
||||||
throw new Error("Invalid tutorial goal: '" + ex + "' for shape" + shape);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,212 +0,0 @@
|
||||||
import { IS_DEMO } from "../core/config";
|
|
||||||
import { findNiceIntegerValue } from "../core/utils";
|
|
||||||
import { ShapeDefinition } from "./shape_definition";
|
|
||||||
|
|
||||||
export const preparementShape = "CpRpCp--:SwSwSwSw";
|
|
||||||
export const finalGameShape = "RuCw--Cw:----Ru--";
|
|
||||||
export const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
|
|
||||||
export const blueprintShape = "CbCbCbRb:CwCwCwCw";
|
|
||||||
|
|
||||||
const fixedImprovements = [0.5, 0.5, 1, 1, 2, 1, 1];
|
|
||||||
|
|
||||||
const numEndgameUpgrades = !IS_DEMO ? 20 - fixedImprovements.length - 1 : 0;
|
|
||||||
|
|
||||||
function generateEndgameUpgrades() {
|
|
||||||
return new Array(numEndgameUpgrades).fill(null).map((_, i) => ({
|
|
||||||
required: [
|
|
||||||
{ shape: preparementShape, amount: 30000 + i * 10000 },
|
|
||||||
{ shape: finalGameShape, amount: 20000 + i * 5000 },
|
|
||||||
{ shape: rocketShape, amount: 20000 + i * 5000 },
|
|
||||||
],
|
|
||||||
excludePrevious: true,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < numEndgameUpgrades; ++i) {
|
|
||||||
fixedImprovements.push(0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @typedef {{
|
|
||||||
* shape: string,
|
|
||||||
* amount: number
|
|
||||||
* }} UpgradeRequirement */
|
|
||||||
|
|
||||||
/** @typedef {{
|
|
||||||
* required: Array<UpgradeRequirement>
|
|
||||||
* improvement?: number,
|
|
||||||
* excludePrevious?: boolean
|
|
||||||
* }} TierRequirement */
|
|
||||||
|
|
||||||
/** @typedef {Array<TierRequirement>} UpgradeTiers */
|
|
||||||
|
|
||||||
/** @type {Object<string, UpgradeTiers>} */
|
|
||||||
export const UPGRADES = {
|
|
||||||
belt: [
|
|
||||||
{
|
|
||||||
required: [{ shape: "CuCuCuCu", amount: 60 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "--CuCu--", amount: 500 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "CpCpCpCp", amount: 1000 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 6000 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 25000 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: preparementShape, amount: 25000 }],
|
|
||||||
excludePrevious: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [
|
|
||||||
{ shape: preparementShape, amount: 25000 },
|
|
||||||
{ shape: finalGameShape, amount: 50000 },
|
|
||||||
],
|
|
||||||
excludePrevious: true,
|
|
||||||
},
|
|
||||||
...generateEndgameUpgrades(),
|
|
||||||
],
|
|
||||||
|
|
||||||
miner: [
|
|
||||||
{
|
|
||||||
required: [{ shape: "RuRuRuRu", amount: 300 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "Cu------", amount: 800 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "ScScScSc", amount: 3500 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 23000 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 50000 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: preparementShape, amount: 25000 }],
|
|
||||||
excludePrevious: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [
|
|
||||||
{ shape: preparementShape, amount: 25000 },
|
|
||||||
{ shape: finalGameShape, amount: 50000 },
|
|
||||||
],
|
|
||||||
excludePrevious: true,
|
|
||||||
},
|
|
||||||
...generateEndgameUpgrades(),
|
|
||||||
],
|
|
||||||
|
|
||||||
processors: [
|
|
||||||
{
|
|
||||||
required: [{ shape: "SuSuSuSu", amount: 500 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "RuRu----", amount: 600 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "CgScScCg", amount: 3500 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 25000 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 50000 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: preparementShape, amount: 25000 }],
|
|
||||||
excludePrevious: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [
|
|
||||||
{ shape: preparementShape, amount: 25000 },
|
|
||||||
{ shape: finalGameShape, amount: 50000 },
|
|
||||||
],
|
|
||||||
excludePrevious: true,
|
|
||||||
},
|
|
||||||
...generateEndgameUpgrades(),
|
|
||||||
],
|
|
||||||
|
|
||||||
painting: [
|
|
||||||
{
|
|
||||||
required: [{ shape: "RbRb----", amount: 600 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "WrWrWrWr", amount: 3800 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 6500 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 25000 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 50000 }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [{ shape: preparementShape, amount: 25000 }],
|
|
||||||
excludePrevious: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
required: [
|
|
||||||
{ shape: preparementShape, amount: 25000 },
|
|
||||||
{ shape: finalGameShape, amount: 50000 },
|
|
||||||
],
|
|
||||||
excludePrevious: true,
|
|
||||||
},
|
|
||||||
...generateEndgameUpgrades(),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Tiers need % of the previous tier as requirement too
|
|
||||||
const tierGrowth = 2.5;
|
|
||||||
|
|
||||||
// Automatically generate tier levels
|
|
||||||
for (const upgradeId in UPGRADES) {
|
|
||||||
const upgradeTiers = UPGRADES[upgradeId];
|
|
||||||
|
|
||||||
let currentTierRequirements = [];
|
|
||||||
for (let i = 0; i < upgradeTiers.length; ++i) {
|
|
||||||
const tierHandle = upgradeTiers[i];
|
|
||||||
tierHandle.improvement = fixedImprovements[i];
|
|
||||||
const originalRequired = tierHandle.required.slice();
|
|
||||||
|
|
||||||
for (let k = currentTierRequirements.length - 1; k >= 0; --k) {
|
|
||||||
const oldTierRequirement = currentTierRequirements[k];
|
|
||||||
if (!tierHandle.excludePrevious) {
|
|
||||||
tierHandle.required.unshift({
|
|
||||||
shape: oldTierRequirement.shape,
|
|
||||||
amount: oldTierRequirement.amount,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentTierRequirements.push(
|
|
||||||
...originalRequired.map(req => ({
|
|
||||||
amount: req.amount,
|
|
||||||
shape: req.shape,
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
currentTierRequirements.forEach(tier => {
|
|
||||||
tier.amount = findNiceIntegerValue(tier.amount * tierGrowth);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VALIDATE
|
|
||||||
if (G_IS_DEV) {
|
|
||||||
for (const upgradeId in UPGRADES) {
|
|
||||||
UPGRADES[upgradeId].forEach(tier => {
|
|
||||||
tier.required.forEach(({ shape }) => {
|
|
||||||
try {
|
|
||||||
ShapeDefinition.fromShortKey(shape);
|
|
||||||
} catch (ex) {
|
|
||||||
throw new Error("Invalid upgrade goal: '" + ex + "' for shape" + shape);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +1,12 @@
|
||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
import { createLogger } from "../../core/logging";
|
import { createLogger } from "../../core/logging";
|
||||||
|
import { queryParamOptions } from "../../core/query_parameters";
|
||||||
|
import { BeltComponent } from "../../game/components/belt";
|
||||||
|
import { StaticMapEntityComponent } from "../../game/components/static_map_entity";
|
||||||
import { GameRoot } from "../../game/root";
|
import { GameRoot } from "../../game/root";
|
||||||
import { InGameState } from "../../states/ingame";
|
import { InGameState } from "../../states/ingame";
|
||||||
import { GameAnalyticsInterface } from "../game_analytics";
|
import { GameAnalyticsInterface } from "../game_analytics";
|
||||||
import { FILE_NOT_FOUND } from "../storage";
|
import { FILE_NOT_FOUND } from "../storage";
|
||||||
import { blueprintShape, UPGRADES } from "../../game/upgrades";
|
|
||||||
import { tutorialGoals } from "../../game/tutorial_goals";
|
|
||||||
import { BeltComponent } from "../../game/components/belt";
|
|
||||||
import { StaticMapEntityComponent } from "../../game/components/static_map_entity";
|
|
||||||
import { queryParamOptions } from "../../core/query_parameters";
|
|
||||||
|
|
||||||
const logger = createLogger("game_analytics");
|
const logger = createLogger("game_analytics");
|
||||||
|
|
||||||
|
@ -190,23 +188,26 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the shape is interesting
|
* Returns true if the shape is interesting
|
||||||
|
* @param {GameRoot} root
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
*/
|
*/
|
||||||
isInterestingShape(key) {
|
isInterestingShape(root, key) {
|
||||||
if (key === blueprintShape) {
|
if (key === root.gameMode.getBlueprintShapeKey()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if its a story goal
|
// Check if its a story goal
|
||||||
for (let i = 0; i < tutorialGoals.length; ++i) {
|
const levels = root.gameMode.getLevelDefinitions();
|
||||||
if (key === tutorialGoals[i].shape) {
|
for (let i = 0; i < levels.length; ++i) {
|
||||||
|
if (key === levels[i].shape) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if its required to unlock an upgrade
|
// Check if its required to unlock an upgrade
|
||||||
for (const upgradeKey in UPGRADES) {
|
const upgrades = root.gameMode.getUpgrades();
|
||||||
const upgradeTiers = UPGRADES[upgradeKey];
|
for (const upgradeKey in upgrades) {
|
||||||
|
const upgradeTiers = upgrades[upgradeKey];
|
||||||
for (let i = 0; i < upgradeTiers.length; ++i) {
|
for (let i = 0; i < upgradeTiers.length; ++i) {
|
||||||
const tier = upgradeTiers[i];
|
const tier = upgradeTiers[i];
|
||||||
const required = tier.required;
|
const required = tier.required;
|
||||||
|
@ -226,7 +227,9 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
|
||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
generateGameDump(root) {
|
generateGameDump(root) {
|
||||||
const shapeIds = Object.keys(root.hubGoals.storedShapes).filter(this.isInterestingShape.bind(this));
|
const shapeIds = Object.keys(root.hubGoals.storedShapes).filter(key =>
|
||||||
|
this.isInterestingShape(root, key)
|
||||||
|
);
|
||||||
let shapes = {};
|
let shapes = {};
|
||||||
for (let i = 0; i < shapeIds.length; ++i) {
|
for (let i = 0; i < shapeIds.length; ++i) {
|
||||||
shapes[shapeIds[i]] = root.hubGoals.storedShapes[shapeIds[i]];
|
shapes[shapeIds[i]] = root.hubGoals.storedShapes[shapeIds[i]];
|
||||||
|
|
|
@ -130,7 +130,7 @@ export class SavegameSerializer {
|
||||||
errorReason = errorReason || root.time.deserialize(savegame.time);
|
errorReason = errorReason || root.time.deserialize(savegame.time);
|
||||||
errorReason = errorReason || root.camera.deserialize(savegame.camera);
|
errorReason = errorReason || root.camera.deserialize(savegame.camera);
|
||||||
errorReason = errorReason || root.map.deserialize(savegame.map);
|
errorReason = errorReason || root.map.deserialize(savegame.map);
|
||||||
errorReason = errorReason || root.hubGoals.deserialize(savegame.hubGoals);
|
errorReason = errorReason || root.hubGoals.deserialize(savegame.hubGoals, root);
|
||||||
errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes);
|
errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes);
|
||||||
errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints);
|
errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints);
|
||||||
errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);
|
errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);
|
||||||
|
|
|
@ -19,7 +19,6 @@ import { getCodeFromBuildingData } from "../../game/building_codes.js";
|
||||||
import { StaticMapEntityComponent } from "../../game/components/static_map_entity.js";
|
import { StaticMapEntityComponent } from "../../game/components/static_map_entity.js";
|
||||||
import { Entity } from "../../game/entity.js";
|
import { Entity } from "../../game/entity.js";
|
||||||
import { defaultBuildingVariant, MetaBuilding } from "../../game/meta_building.js";
|
import { defaultBuildingVariant, MetaBuilding } from "../../game/meta_building.js";
|
||||||
import { finalGameShape } from "../../game/upgrades.js";
|
|
||||||
import { SavegameInterface_V1005 } from "./1005.js";
|
import { SavegameInterface_V1005 } from "./1005.js";
|
||||||
|
|
||||||
const schema = require("./1006.json");
|
const schema = require("./1006.json");
|
||||||
|
@ -152,7 +151,8 @@ export class SavegameInterface_V1006 extends SavegameInterface_V1005 {
|
||||||
stored[shapeKey] = rebalance(stored[shapeKey]);
|
stored[shapeKey] = rebalance(stored[shapeKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
stored[finalGameShape] = 0;
|
// Reset final game shape
|
||||||
|
stored["RuCw--Cw:----Ru--"] = 0;
|
||||||
|
|
||||||
// Reduce goals
|
// Reduce goals
|
||||||
if (dump.hubGoals.currentGoal) {
|
if (dump.hubGoals.currentGoal) {
|
||||||
|
|
Loading…
Reference in New Issue