mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-03-02 03:39:21 +00:00
Merge branch 'master' of git://github.com/tobspr/shapez.io into feature/ore-q-ing
This commit is contained in:
@@ -33,6 +33,7 @@ import { MainMenuState } from "./states/main_menu";
|
||||
import { MobileWarningState } from "./states/mobile_warning";
|
||||
import { PreloadState } from "./states/preload";
|
||||
import { SettingsState } from "./states/settings";
|
||||
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
||||
|
||||
const logger = createLogger("application");
|
||||
|
||||
@@ -130,8 +131,7 @@ export class Application {
|
||||
this.adProvider = new NoAdProvider(this);
|
||||
this.sound = new SoundImplBrowser(this);
|
||||
this.analytics = new GoogleAnalyticsImpl(this);
|
||||
// this.gameAnalytics = new ShapezGameAnalytics(this);
|
||||
this.gameAnalytics = new NoGameAnalytics(this);
|
||||
this.gameAnalytics = new ShapezGameAnalytics(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
export const CHANGELOG = [
|
||||
{
|
||||
version: "1.1.18",
|
||||
date: "24.06.2020",
|
||||
entries: [
|
||||
"Preparations for the wires update",
|
||||
"Add setting (on by default) to store the last used rotation per building instead of globally storing it (by Magos)",
|
||||
"Added chinese (traditional) translation",
|
||||
"Updated translations",
|
||||
],
|
||||
},
|
||||
{
|
||||
version: "1.1.17",
|
||||
date: "22.06.2020",
|
||||
|
||||
@@ -9,7 +9,7 @@ export const IS_DEBUG =
|
||||
|
||||
export const IS_DEMO = queryParamOptions.fullVersion
|
||||
? false
|
||||
: (G_IS_PROD && !G_IS_STANDALONE) ||
|
||||
: (!G_IS_DEV && !G_IS_STANDALONE) ||
|
||||
(typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0);
|
||||
|
||||
export const SUPPORT_TOUCH = false;
|
||||
|
||||
@@ -769,7 +769,7 @@ export function quantizeFloat(value) {
|
||||
* @param {number} tickRate Interval of the timer
|
||||
*/
|
||||
export function checkTimerExpired(now, lastTick, tickRate) {
|
||||
if (!G_IS_PROD) {
|
||||
if (G_IS_DEV) {
|
||||
if (quantizeFloat(now) !== now) {
|
||||
console.error("Got non-quantizied time:" + now + " vs " + quantizeFloat(now));
|
||||
now = quantizeFloat(now);
|
||||
|
||||
@@ -3,13 +3,16 @@ import { BaseItem } from "../base_item";
|
||||
import { Component } from "../component";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { gItemRegistry } from "../../core/global_registries";
|
||||
import { Entity } from "../entity";
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* pos: Vector,
|
||||
* direction: enumDirection,
|
||||
* item: BaseItem,
|
||||
* progress: number?
|
||||
* progress: number?,
|
||||
* cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot,
|
||||
* cachedTargetEntity?: Entity
|
||||
* }} ItemEjectorSlot
|
||||
*/
|
||||
|
||||
@@ -19,6 +22,8 @@ export class ItemEjectorComponent extends Component {
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
// The cachedDestSlot, cachedTargetEntity, and cachedConnectedSlots fields
|
||||
// are not serialized.
|
||||
return {
|
||||
instantEject: types.bool,
|
||||
slots: types.array(
|
||||
@@ -61,6 +66,9 @@ export class ItemEjectorComponent extends Component {
|
||||
this.instantEject = instantEject;
|
||||
|
||||
this.setSlots(slots);
|
||||
|
||||
/** @type {ItemEjectorSlot[]} */
|
||||
this.cachedConnectedSlots = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,6 +84,8 @@ export class ItemEjectorComponent extends Component {
|
||||
direction: slot.direction,
|
||||
item: null,
|
||||
progress: 0,
|
||||
cachedDestSlot: null,
|
||||
cachedTargetEntity: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,13 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
|
||||
* The current rotation
|
||||
* @type {number}
|
||||
*/
|
||||
this.currentBaseRotation = 0;
|
||||
this.currentBaseRotationGeneral = 0;
|
||||
|
||||
/**
|
||||
* The current rotation preference for each building.
|
||||
* @type{Object.<string,number>}
|
||||
*/
|
||||
this.preferredBaseRotations = {};
|
||||
|
||||
/**
|
||||
* Whether we are currently dragging
|
||||
@@ -115,6 +121,39 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
|
||||
this.root.camera.upPostHandler.add(this.onMouseUp, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current base rotation for the current meta-building.
|
||||
* @returns {number}
|
||||
*/
|
||||
get currentBaseRotation() {
|
||||
if (!this.root.app.settings.getAllSettings().rotationByBuilding) {
|
||||
return this.currentBaseRotationGeneral;
|
||||
}
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
if (metaBuilding && this.preferredBaseRotations.hasOwnProperty(metaBuilding.getId())) {
|
||||
return this.preferredBaseRotations[metaBuilding.getId()];
|
||||
} else {
|
||||
return this.currentBaseRotationGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base rotation for the current meta-building.
|
||||
* @param {number} rotation The new rotation/angle.
|
||||
*/
|
||||
set currentBaseRotation(rotation) {
|
||||
if (!this.root.app.settings.getAllSettings().rotationByBuilding) {
|
||||
this.currentBaseRotationGeneral = rotation;
|
||||
} else {
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
if (metaBuilding) {
|
||||
this.preferredBaseRotations[metaBuilding.getId()] = rotation;
|
||||
} else {
|
||||
this.currentBaseRotationGeneral = rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the direction lock is currently active
|
||||
* @returns {boolean}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Entity } from "../entity";
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { Math_min } from "../../core/builtins";
|
||||
import { createLogger } from "../../core/logging";
|
||||
import { Rectangle } from "../../core/rectangle";
|
||||
|
||||
const logger = createLogger("systems/ejector");
|
||||
|
||||
@@ -14,23 +15,34 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
super(root, [ItemEjectorComponent]);
|
||||
|
||||
/**
|
||||
* @type {Array<{
|
||||
* targetEntity: Entity,
|
||||
* sourceSlot: import("../components/item_ejector").ItemEjectorSlot,
|
||||
* destSlot: import("../components/item_acceptor").ItemAcceptorLocatedSlot
|
||||
* }>}
|
||||
*/
|
||||
this.cache = [];
|
||||
|
||||
this.cacheNeedsUpdate = true;
|
||||
|
||||
this.root.signals.entityAdded.add(this.invalidateCache, this);
|
||||
this.root.signals.entityDestroyed.add(this.invalidateCache, this);
|
||||
|
||||
/**
|
||||
* @type {Rectangle[]}
|
||||
*/
|
||||
this.smallCacheAreas = [];
|
||||
}
|
||||
|
||||
invalidateCache() {
|
||||
/**
|
||||
*
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
invalidateCache(entity) {
|
||||
if (!entity.components.StaticMapEntity) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cacheNeedsUpdate = true;
|
||||
|
||||
// Optimize for the common case: adding or removing one building at a time. Clicking
|
||||
// and dragging can cause up to 4 add/remove signals.
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const bounds = staticComp.getTileSpaceBounds();
|
||||
const expandedBounds = bounds.expandedInAllDirections(2);
|
||||
this.smallCacheAreas.push(expandedBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,59 +51,102 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
recomputeCache() {
|
||||
logger.log("Recomputing cache");
|
||||
|
||||
const cache = [];
|
||||
|
||||
// Try to find acceptors for every ejector
|
||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||
const entity = this.allEntities[i];
|
||||
const ejectorComp = entity.components.ItemEjector;
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
// For every ejector slot, try to find an acceptor
|
||||
for (let ejectorSlotIndex = 0; ejectorSlotIndex < ejectorComp.slots.length; ++ejectorSlotIndex) {
|
||||
const ejectorSlot = ejectorComp.slots[ejectorSlotIndex];
|
||||
|
||||
// Figure out where and into which direction we eject items
|
||||
const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos);
|
||||
const ejectSlotWsDirection = staticComp.localDirectionToWorld(ejectorSlot.direction);
|
||||
const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection];
|
||||
const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector);
|
||||
|
||||
// Try to find the given acceptor component to take the item
|
||||
const targetEntity = this.root.map.getTileContent(ejectSlotTargetWsTile);
|
||||
if (!targetEntity) {
|
||||
// No consumer for item
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||
const targetStaticComp = targetEntity.components.StaticMapEntity;
|
||||
if (!targetAcceptorComp) {
|
||||
// Entity doesn't accept items
|
||||
continue;
|
||||
}
|
||||
|
||||
const matchingSlot = targetAcceptorComp.findMatchingSlot(
|
||||
targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile),
|
||||
targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection)
|
||||
);
|
||||
|
||||
if (!matchingSlot) {
|
||||
// No matching slot found
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ok we found a connection
|
||||
cache.push({
|
||||
targetEntity,
|
||||
sourceSlot: ejectorSlot,
|
||||
destSlot: matchingSlot,
|
||||
});
|
||||
let entryCount = 0;
|
||||
if (this.smallCacheAreas.length <= 4) {
|
||||
// Only recompute caches of entities inside the rectangles.
|
||||
for (let i = 0; i < this.smallCacheAreas.length; i++) {
|
||||
entryCount += this.recomputeAreaCaches(this.smallCacheAreas[i]);
|
||||
}
|
||||
} else {
|
||||
// Try to find acceptors for every ejector
|
||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||
const entity = this.allEntities[i];
|
||||
entryCount += this.recomputeSingleEntityCache(entity);
|
||||
}
|
||||
}
|
||||
logger.log("Found", entryCount, "entries to update");
|
||||
|
||||
this.cache = cache;
|
||||
logger.log("Found", cache.length, "entries to update");
|
||||
this.smallCacheAreas = [];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Rectangle} area
|
||||
*/
|
||||
recomputeAreaCaches(area) {
|
||||
let entryCount = 0;
|
||||
for (let x = area.x; x < area.right(); ++x) {
|
||||
for (let y = area.y; y < area.bottom(); ++y) {
|
||||
const entity = this.root.map.getTileContentXY(x, y);
|
||||
if (entity && entity.components.ItemEjector) {
|
||||
entryCount += this.recomputeSingleEntityCache(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
return entryCount;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
recomputeSingleEntityCache(entity) {
|
||||
const ejectorComp = entity.components.ItemEjector;
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
// Clear the old cache.
|
||||
ejectorComp.cachedConnectedSlots = null;
|
||||
|
||||
// For every ejector slot, try to find an acceptor
|
||||
let entryCount = 0;
|
||||
for (let ejectorSlotIndex = 0; ejectorSlotIndex < ejectorComp.slots.length; ++ejectorSlotIndex) {
|
||||
const ejectorSlot = ejectorComp.slots[ejectorSlotIndex];
|
||||
|
||||
// Clear the old cache.
|
||||
ejectorSlot.cachedDestSlot = null;
|
||||
ejectorSlot.cachedTargetEntity = null;
|
||||
|
||||
// Figure out where and into which direction we eject items
|
||||
const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos);
|
||||
const ejectSlotWsDirection = staticComp.localDirectionToWorld(ejectorSlot.direction);
|
||||
const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection];
|
||||
const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector);
|
||||
|
||||
// Try to find the given acceptor component to take the item
|
||||
const targetEntity = this.root.map.getTileContent(ejectSlotTargetWsTile);
|
||||
if (!targetEntity) {
|
||||
// No consumer for item
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||
const targetStaticComp = targetEntity.components.StaticMapEntity;
|
||||
if (!targetAcceptorComp) {
|
||||
// Entity doesn't accept items
|
||||
continue;
|
||||
}
|
||||
|
||||
const matchingSlot = targetAcceptorComp.findMatchingSlot(
|
||||
targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile),
|
||||
targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection)
|
||||
);
|
||||
|
||||
if (!matchingSlot) {
|
||||
// No matching slot found
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ok we found a connection
|
||||
if (ejectorComp.cachedConnectedSlots) {
|
||||
ejectorComp.cachedConnectedSlots.push(ejectorSlot);
|
||||
} else {
|
||||
ejectorComp.cachedConnectedSlots = [ejectorSlot];
|
||||
}
|
||||
ejectorSlot.cachedTargetEntity = targetEntity;
|
||||
ejectorSlot.cachedDestSlot = matchingSlot;
|
||||
entryCount += 1;
|
||||
}
|
||||
return entryCount;
|
||||
}
|
||||
|
||||
update() {
|
||||
@@ -109,35 +164,45 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
}
|
||||
|
||||
// Go over all cache entries
|
||||
for (let i = 0; i < this.cache.length; ++i) {
|
||||
const { sourceSlot, destSlot, targetEntity } = this.cache[i];
|
||||
const item = sourceSlot.item;
|
||||
|
||||
if (!item) {
|
||||
// No item available to be ejected
|
||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||
const sourceEntity = this.allEntities[i];
|
||||
const sourceEjectorComp = sourceEntity.components.ItemEjector;
|
||||
if (!sourceEjectorComp.cachedConnectedSlots) {
|
||||
continue;
|
||||
}
|
||||
for (let j = 0; j < sourceEjectorComp.cachedConnectedSlots.length; j++) {
|
||||
const sourceSlot = sourceEjectorComp.cachedConnectedSlots[j];
|
||||
const destSlot = sourceSlot.cachedDestSlot;
|
||||
const targetEntity = sourceSlot.cachedTargetEntity;
|
||||
|
||||
// Advance items on the slot
|
||||
sourceSlot.progress = Math_min(1, sourceSlot.progress + progressGrowth);
|
||||
const item = sourceSlot.item;
|
||||
|
||||
// Check if we are still in the process of ejecting, can't proceed then
|
||||
if (sourceSlot.progress < 1.0) {
|
||||
continue;
|
||||
}
|
||||
if (!item) {
|
||||
// No item available to be ejected
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the target acceptor can actually accept this item
|
||||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||
if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) {
|
||||
continue;
|
||||
}
|
||||
// Advance items on the slot
|
||||
sourceSlot.progress = Math_min(1, sourceSlot.progress + progressGrowth);
|
||||
|
||||
// Try to hand over the item
|
||||
if (this.tryPassOverItem(item, targetEntity, destSlot.index)) {
|
||||
// Handover successful, clear slot
|
||||
targetAcceptorComp.onItemAccepted(destSlot.index, destSlot.acceptedDirection, item);
|
||||
sourceSlot.item = null;
|
||||
continue;
|
||||
// Check if we are still in the process of ejecting, can't proceed then
|
||||
if (sourceSlot.progress < 1.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the target acceptor can actually accept this item
|
||||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||
if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to hand over the item
|
||||
if (this.tryPassOverItem(item, targetEntity, destSlot.index)) {
|
||||
// Handover successful, clear slot
|
||||
targetAcceptorComp.onItemAccepted(destSlot.index, destSlot.acceptedDirection, item);
|
||||
sourceSlot.item = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
src/js/globals.d.ts
vendored
1
src/js/globals.d.ts
vendored
@@ -1,7 +1,6 @@
|
||||
// Globals defined by webpack
|
||||
|
||||
declare const G_IS_DEV: boolean;
|
||||
declare const G_IS_PROD: boolean;
|
||||
declare function assert(condition: boolean | object | string, ...errorMessage: string[]): void;
|
||||
declare function assertAlways(condition: boolean | object | string, ...errorMessage: string[]): void;
|
||||
|
||||
|
||||
@@ -51,10 +51,10 @@ export const LANGUAGES = {
|
||||
region: "",
|
||||
},
|
||||
"es-419": {
|
||||
name: "Español (Latinoamérica)",
|
||||
name: "Español",
|
||||
data: require("./built-temp/base-es.json"),
|
||||
code: "es",
|
||||
region: "419",
|
||||
region: "",
|
||||
},
|
||||
"pl": {
|
||||
name: "Polski",
|
||||
@@ -80,12 +80,23 @@ export const LANGUAGES = {
|
||||
code: "no",
|
||||
region: "",
|
||||
},
|
||||
|
||||
"zh-CN": {
|
||||
name: "简体中文",
|
||||
// simplified
|
||||
name: "中文简体",
|
||||
data: require("./built-temp/base-zh-CN.json"),
|
||||
code: "zh",
|
||||
region: "CN",
|
||||
},
|
||||
|
||||
"zh-TW": {
|
||||
// traditional
|
||||
name: "中文繁體",
|
||||
data: require("./built-temp/base-zh-TW.json"),
|
||||
code: "zh",
|
||||
region: "TW",
|
||||
},
|
||||
|
||||
"sv": {
|
||||
name: "Svenska",
|
||||
data: require("./built-temp/base-sv.json"),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { GameAnalyticsInterface } from "../game_analytics";
|
||||
import { createLogger } from "../../core/logging";
|
||||
import { ShapeDefinition } from "../../game/shape_definition";
|
||||
import { Savegame } from "../../savegame/savegame";
|
||||
import { FILE_NOT_FOUND } from "../storage";
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { InGameState } from "../../states/ingame";
|
||||
import { createLogger } from "../../core/logging";
|
||||
import { GameRoot } from "../../game/root";
|
||||
import { InGameState } from "../../states/ingame";
|
||||
import { GameAnalyticsInterface } from "../game_analytics";
|
||||
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";
|
||||
|
||||
const logger = createLogger("game_analytics");
|
||||
@@ -14,16 +15,32 @@ const analyticsUrl = G_IS_DEV ? "http://localhost:8001" : "https://analytics.sha
|
||||
|
||||
// Be sure to increment the ID whenever it changes to make sure all
|
||||
// users are tracked
|
||||
const analyticsLocalFile = "analytics_token.3.bin";
|
||||
const analyticsLocalFile = "shapez_token_123.bin";
|
||||
|
||||
export class ShapezGameAnalytics extends GameAnalyticsInterface {
|
||||
get environment() {
|
||||
if (G_IS_DEV) {
|
||||
return "dev";
|
||||
}
|
||||
|
||||
if (G_IS_STANDALONE) {
|
||||
return "steam";
|
||||
}
|
||||
|
||||
if (G_IS_RELEASE) {
|
||||
return "prod";
|
||||
}
|
||||
|
||||
return "beta";
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
initialize() {
|
||||
this.syncKey = null;
|
||||
|
||||
setInterval(() => this.sendTimePoints(), 120 * 1000);
|
||||
setInterval(() => this.sendTimePoints(), 60 * 1000);
|
||||
|
||||
// Retrieve sync key from player
|
||||
return this.app.storage.readFileAsync(analyticsLocalFile).then(
|
||||
@@ -38,7 +55,7 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
|
||||
|
||||
// Perform call to get a new key from the API
|
||||
this.sendToApi("/v1/register", {
|
||||
environment: G_APP_ENVIRONMENT,
|
||||
environment: this.environment,
|
||||
})
|
||||
.then(res => {
|
||||
// Try to read and parse the key from the api
|
||||
@@ -135,10 +152,12 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
|
||||
playerKey: this.syncKey,
|
||||
gameKey: savegameId,
|
||||
ingameTime: root.time.now(),
|
||||
environment: this.environment,
|
||||
category,
|
||||
value,
|
||||
version: G_BUILD_VERSION,
|
||||
gameDump: this.generateGameDump(root, category === "sync"),
|
||||
level: root.hubGoals.level,
|
||||
gameDump: this.generateGameDump(root),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -151,52 +170,58 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a game dump
|
||||
* @param {GameRoot} root
|
||||
* @param {boolean=} metaOnly
|
||||
* Returns true if the shape is interesting
|
||||
* @param {string} key
|
||||
*/
|
||||
generateGameDump(root, metaOnly = false) {
|
||||
let staticEntities = [];
|
||||
isInterestingShape(key) {
|
||||
if (key === blueprintShape) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const entities = root.entityMgr.getAllWithComponent(StaticMapEntityComponent);
|
||||
|
||||
// Limit the entities
|
||||
if (!metaOnly && entities.length < 500) {
|
||||
for (let i = 0; i < entities.length; ++i) {
|
||||
const entity = entities[i];
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const payload = {};
|
||||
payload.origin = staticComp.origin;
|
||||
payload.tileSize = staticComp.tileSize;
|
||||
payload.rotation = staticComp.rotation;
|
||||
|
||||
if (entity.components.Belt) {
|
||||
payload.type = "belt";
|
||||
} else if (entity.components.UndergroundBelt) {
|
||||
payload.type = "tunnel";
|
||||
} else if (entity.components.ItemProcessor) {
|
||||
payload.type = entity.components.ItemProcessor.type;
|
||||
} else if (entity.components.Miner) {
|
||||
payload.type = "extractor";
|
||||
} else {
|
||||
logger.warn("Unkown entity type", entity);
|
||||
}
|
||||
staticEntities.push(payload);
|
||||
// Check if its a story goal
|
||||
for (let i = 0; i < tutorialGoals.length; ++i) {
|
||||
if (key === tutorialGoals[i].shape) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
storedShapes: root.hubGoals.storedShapes,
|
||||
gainedRewards: root.hubGoals.gainedRewards,
|
||||
upgradeLevels: root.hubGoals.upgradeLevels,
|
||||
staticEntities,
|
||||
};
|
||||
// Check if its required to unlock an upgrade
|
||||
for (const upgradeKey in UPGRADES) {
|
||||
const handle = UPGRADES[upgradeKey];
|
||||
const tiers = handle.tiers;
|
||||
for (let i = 0; i < tiers.length; ++i) {
|
||||
const tier = tiers[i];
|
||||
const required = tier.required;
|
||||
for (let k = 0; k < required.length; ++k) {
|
||||
if (required[k].shape === key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ShapeDefinition} definition
|
||||
* Generates a game dump
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
handleShapeDelivered(definition) {}
|
||||
generateGameDump(root) {
|
||||
const shapeIds = Object.keys(root.hubGoals.storedShapes).filter(this.isInterestingShape.bind(this));
|
||||
let shapes = {};
|
||||
for (let i = 0; i < shapeIds.length; ++i) {
|
||||
shapes[shapeIds[i]] = root.hubGoals.storedShapes[shapeIds[i]];
|
||||
}
|
||||
return {
|
||||
shapes,
|
||||
upgrades: root.hubGoals.upgradeLevels,
|
||||
belts: root.entityMgr.getAllWithComponent(BeltComponent).length,
|
||||
buildings:
|
||||
root.entityMgr.getAllWithComponent(StaticMapEntityComponent).length -
|
||||
root.entityMgr.getAllWithComponent(BeltComponent).length,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
@@ -204,6 +229,12 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
|
||||
this.sendGameEvent("game_start", "");
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
handleGameResumed() {
|
||||
this.sendTimePoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the given level completed
|
||||
* @param {number} level
|
||||
|
||||
@@ -25,9 +25,9 @@ export class GameAnalyticsInterface {
|
||||
handleGameStarted() {}
|
||||
|
||||
/**
|
||||
* @param {ShapeDefinition} definition
|
||||
* Handles a resumed game
|
||||
*/
|
||||
handleShapeDelivered(definition) {}
|
||||
handleGameResumed() {}
|
||||
|
||||
/**
|
||||
* Handles the given level completed
|
||||
|
||||
@@ -251,6 +251,7 @@ export const allApplicationSettings = [
|
||||
new BoolSetting("vignette", categoryGame, (app, value) => {}),
|
||||
new BoolSetting("compactBuildingInfo", categoryGame, (app, value) => {}),
|
||||
new BoolSetting("disableCutDeleteWarnings", categoryGame, (app, value) => {}),
|
||||
new BoolSetting("rotationByBuilding", categoryGame, (app, value) => {}),
|
||||
];
|
||||
|
||||
export function getApplicationSettingById(id) {
|
||||
@@ -277,6 +278,7 @@ class SettingsStorage {
|
||||
this.vignette = true;
|
||||
this.compactBuildingInfo = false;
|
||||
this.disableCutDeleteWarnings = false;
|
||||
this.rotationByBuilding = true;
|
||||
|
||||
this.enableColorBlindHelper = false;
|
||||
|
||||
@@ -479,7 +481,7 @@ export class ApplicationSettings extends ReadWriteProxy {
|
||||
}
|
||||
|
||||
getCurrentVersion() {
|
||||
return 17;
|
||||
return 18;
|
||||
}
|
||||
|
||||
/** @param {{settings: SettingsStorage, version: number}} data */
|
||||
@@ -552,6 +554,11 @@ export class ApplicationSettings extends ReadWriteProxy {
|
||||
data.version = 17;
|
||||
}
|
||||
|
||||
if (data.version < 18) {
|
||||
data.settings.rotationByBuilding = true;
|
||||
data.version = 18;
|
||||
}
|
||||
|
||||
return ExplainedResult.good();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +217,6 @@ export class InGameState extends GameState {
|
||||
this.core.initializeRoot(this, this.savegame);
|
||||
|
||||
if (this.savegame.hasGameDump()) {
|
||||
this.app.gameAnalytics.handleGameStarted();
|
||||
this.stage4bResumeGame();
|
||||
} else {
|
||||
this.app.gameAnalytics.handleGameStarted();
|
||||
@@ -245,6 +244,7 @@ export class InGameState extends GameState {
|
||||
this.onInitializationFailure("Savegame is corrupt and can not be restored.");
|
||||
return;
|
||||
}
|
||||
this.app.gameAnalytics.handleGameResumed();
|
||||
this.stage5FirstUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user