1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2026-03-02 03:39:21 +00:00

Further performance improvements, show indicator while game is saving

This commit is contained in:
tobspr
2020-09-18 20:41:40 +02:00
parent bba29b8a8b
commit 1ebfafd8de
11 changed files with 1223 additions and 1172 deletions

View File

@@ -1,83 +1,92 @@
/* typehints:start */
import { MetaBuilding } from "./meta_building";
import { AtlasSprite } from "../core/sprites";
import { Vector } from "../core/vector";
/* typehints:end */
/**
* @typedef {{
* metaClass: typeof MetaBuilding,
* metaInstance?: MetaBuilding,
* variant?: string,
* rotationVariant?: number,
* tileSize?: Vector,
* sprite?: AtlasSprite,
* blueprintSprite?: AtlasSprite,
* silhouetteColor?: string
* }} BuildingVariantIdentifier
*/
/**
* Stores a lookup table for all building variants (for better performance)
* @type {Object<number, BuildingVariantIdentifier>}
*/
export const gBuildingVariants = {
// Set later
};
/**
* Registers a new variant
* @param {number} id
* @param {typeof MetaBuilding} meta
* @param {string} variant
* @param {number} rotationVariant
*/
export function registerBuildingVariant(
id,
meta,
variant = "default" /* FIXME: Circular dependency, actually its defaultBuildingVariant */,
rotationVariant = 0
) {
assert(!gBuildingVariants[id], "Duplicate id: " + id);
gBuildingVariants[id] = {
metaClass: meta,
variant,
rotationVariant,
// @ts-ignore
tileSize: new meta().getDimensions(variant),
};
}
/**
*
* @param {number} code
* @returns {BuildingVariantIdentifier}
*/
export function getBuildingDataFromCode(code) {
assert(gBuildingVariants[code], "Invalid building code: " + code);
return gBuildingVariants[code];
}
/**
* Finds the code for a given variant
* @param {MetaBuilding} metaBuilding
* @param {string} variant
* @param {number} rotationVariant
*/
export function getCodeFromBuildingData(metaBuilding, variant, rotationVariant) {
for (const key in gBuildingVariants) {
const data = gBuildingVariants[key];
if (
data.metaInstance.getId() === metaBuilding.getId() &&
data.variant === variant &&
data.rotationVariant === rotationVariant
) {
return +key;
}
}
assertAlways(
false,
"Building not found by data: " + metaBuilding.getId() + " / " + variant + " / " + rotationVariant
);
return 0;
}
/* typehints:start */
import { MetaBuilding } from "./meta_building";
import { AtlasSprite } from "../core/sprites";
import { Vector } from "../core/vector";
/* typehints:end */
/**
* @typedef {{
* metaClass: typeof MetaBuilding,
* metaInstance?: MetaBuilding,
* variant?: string,
* rotationVariant?: number,
* tileSize?: Vector,
* sprite?: AtlasSprite,
* blueprintSprite?: AtlasSprite,
* silhouetteColor?: string
* }} BuildingVariantIdentifier
*/
/**
* Stores a lookup table for all building variants (for better performance)
* @type {Object<number, BuildingVariantIdentifier>}
*/
export const gBuildingVariants = {
// Set later
};
/**
* Mapping from 'metaBuildingId/variant/rotationVariant' to building code
* @type {Map<string, number>}
*/
const variantsCache = new Map();
/**
* Registers a new variant
* @param {number} code
* @param {typeof MetaBuilding} meta
* @param {string} variant
* @param {number} rotationVariant
*/
export function registerBuildingVariant(
code,
meta,
variant = "default" /* FIXME: Circular dependency, actually its defaultBuildingVariant */,
rotationVariant = 0
) {
assert(!gBuildingVariants[code], "Duplicate id: " + code);
gBuildingVariants[code] = {
metaClass: meta,
variant,
rotationVariant,
// @ts-ignore
tileSize: new meta().getDimensions(variant),
};
}
/**
*
* @param {number} code
* @returns {BuildingVariantIdentifier}
*/
export function getBuildingDataFromCode(code) {
assert(gBuildingVariants[code], "Invalid building code: " + code);
return gBuildingVariants[code];
}
/**
* Builds the cache for the codes
*/
export function buildBuildingCodeCache() {
for (const code in gBuildingVariants) {
const data = gBuildingVariants[code];
const hash = data.metaInstance.getId() + "/" + data.variant + "/" + data.rotationVariant;
variantsCache.set(hash, +code);
}
}
/**
* Finds the code for a given variant
* @param {MetaBuilding} metaBuilding
* @param {string} variant
* @param {number} rotationVariant
* @returns {number}
*/
export function getCodeFromBuildingData(metaBuilding, variant, rotationVariant) {
const hash = metaBuilding.getId() + "/" + variant + "/" + rotationVariant;
const result = variantsCache.get(hash);
if (G_IS_DEV) {
assertAlways(!!result, "Building not found by data: " + hash);
}
return result;
}

View File

@@ -67,11 +67,8 @@ export class EntityManager extends BasicSerializableObject {
}
assert(!entity.destroyed, `Attempting to register destroyed entity ${entity}`);
if (G_IS_DEV && uid !== null) {
if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts && uid !== null) {
assert(!this.findByUid(uid, false), "Entity uid already taken: " + uid);
}
if (uid !== null) {
assert(uid >= 0 && uid < Number.MAX_SAFE_INTEGER, "Invalid uid passed: " + uid);
}

View File

@@ -5,6 +5,7 @@ import { enumNotificationType } from "./notifications";
import { T } from "../../../translations";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { TrackedState } from "../../../core/tracked_state";
export class HUDGameMenu extends BaseHUDPart {
createElements(parent) {
@@ -97,12 +98,17 @@ export class HUDGameMenu extends BaseHUDPart {
initialize() {
this.root.signals.gameSaved.add(this.onGameSaved, this);
this.trackedIsSaving = new TrackedState(this.onIsSavingChanged, this);
}
update() {
let playSound = false;
let notifications = new Set();
// Check whether we are saving
this.trackedIsSaving.set(!!this.root.gameState.currentSavePromise);
// Update visibility of buttons
for (let i = 0; i < this.visibilityToUpdate.length; ++i) {
const { condition, domAttach } = this.visibilityToUpdate[i];
@@ -154,6 +160,10 @@ export class HUDGameMenu extends BaseHUDPart {
});
}
onIsSavingChanged(isSaving) {
this.saveButton.classList.toggle("saving", isSaving);
}
onGameSaved() {
this.saveButton.classList.toggle("animEven");
this.saveButton.classList.toggle("animOdd");

View File

@@ -1,56 +1,55 @@
import { BaseHUDPart } from "../base_hud_part";
import { makeDiv } from "../../../core/utils";
import { T } from "../../../translations";
import { IS_DEMO } from "../../../core/config";
/** @enum {string} */
export const enumNotificationType = {
saved: "saved",
upgrade: "upgrade",
success: "success",
};
const notificationDuration = 3;
export class HUDNotifications extends BaseHUDPart {
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_Notifications", [], ``);
}
initialize() {
this.root.hud.signals.notification.add(this.onNotification, this);
/** @type {Array<{ element: HTMLElement, expireAt: number}>} */
this.notificationElements = [];
// Automatic notifications
this.root.signals.gameSaved.add(() =>
this.onNotification(T.ingame.notifications.gameSaved, enumNotificationType.saved)
);
}
/**
* @param {string} message
* @param {enumNotificationType} type
*/
onNotification(message, type) {
const element = makeDiv(this.element, null, ["notification", "type-" + type], message);
element.setAttribute("data-icon", "icons/notification_" + type + ".png");
this.notificationElements.push({
element,
expireAt: this.root.time.realtimeNow() + notificationDuration,
});
}
update() {
const now = this.root.time.realtimeNow();
for (let i = 0; i < this.notificationElements.length; ++i) {
const handle = this.notificationElements[i];
if (handle.expireAt <= now) {
handle.element.remove();
this.notificationElements.splice(i, 1);
}
}
}
}
import { makeDiv } from "../../../core/utils";
import { T } from "../../../translations";
import { BaseHUDPart } from "../base_hud_part";
/** @enum {string} */
export const enumNotificationType = {
saved: "saved",
upgrade: "upgrade",
success: "success",
};
const notificationDuration = 3;
export class HUDNotifications extends BaseHUDPart {
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_Notifications", [], ``);
}
initialize() {
this.root.hud.signals.notification.add(this.onNotification, this);
/** @type {Array<{ element: HTMLElement, expireAt: number}>} */
this.notificationElements = [];
// Automatic notifications
this.root.signals.gameSaved.add(() =>
this.onNotification(T.ingame.notifications.gameSaved, enumNotificationType.saved)
);
}
/**
* @param {string} message
* @param {enumNotificationType} type
*/
onNotification(message, type) {
const element = makeDiv(this.element, null, ["notification", "type-" + type], message);
element.setAttribute("data-icon", "icons/notification_" + type + ".png");
this.notificationElements.push({
element,
expireAt: this.root.time.realtimeNow() + notificationDuration,
});
}
update() {
const now = this.root.time.realtimeNow();
for (let i = 0; i < this.notificationElements.length; ++i) {
const handle = this.notificationElements[i];
if (handle.expireAt <= now) {
handle.element.remove();
this.notificationElements.splice(i, 1);
}
}
}
}

View File

@@ -1,236 +1,236 @@
import { globalConfig } from "../core/config";
import { Vector } from "../core/vector";
import { BasicSerializableObject, types } from "../savegame/serialization";
import { BaseItem } from "./base_item";
import { Entity } from "./entity";
import { MapChunkView } from "./map_chunk_view";
import { GameRoot } from "./root";
export class BaseMap extends BasicSerializableObject {
static getId() {
return "Map";
}
static getSchema() {
return {
seed: types.uint,
};
}
/**
*
* @param {GameRoot} root
*/
constructor(root) {
super();
this.root = root;
this.seed = 0;
/**
* Mapping of 'X|Y' to chunk
* @type {Map<string, MapChunkView>} */
this.chunksById = new Map();
}
/**
* Returns the given chunk by index
* @param {number} chunkX
* @param {number} chunkY
*/
getChunk(chunkX, chunkY, createIfNotExistent = false) {
const chunkIdentifier = chunkX + "|" + chunkY;
let storedChunk;
if ((storedChunk = this.chunksById.get(chunkIdentifier))) {
return storedChunk;
}
if (createIfNotExistent) {
const instance = new MapChunkView(this.root, chunkX, chunkY);
this.chunksById.set(chunkIdentifier, instance);
return instance;
}
return null;
}
/**
* Gets or creates a new chunk if not existent for the given tile
* @param {number} tileX
* @param {number} tileY
* @returns {MapChunkView}
*/
getOrCreateChunkAtTile(tileX, tileY) {
const chunkX = Math.floor(tileX / globalConfig.mapChunkSize);
const chunkY = Math.floor(tileY / globalConfig.mapChunkSize);
return this.getChunk(chunkX, chunkY, true);
}
/**
* Gets a chunk if not existent for the given tile
* @param {number} tileX
* @param {number} tileY
* @returns {MapChunkView?}
*/
getChunkAtTileOrNull(tileX, tileY) {
const chunkX = Math.floor(tileX / globalConfig.mapChunkSize);
const chunkY = Math.floor(tileY / globalConfig.mapChunkSize);
return this.getChunk(chunkX, chunkY, false);
}
/**
* Checks if a given tile is within the map bounds
* @param {Vector} tile
* @returns {boolean}
*/
isValidTile(tile) {
if (G_IS_DEV) {
assert(tile instanceof Vector, "tile is not a vector");
}
return Number.isInteger(tile.x) && Number.isInteger(tile.y);
}
/**
* Returns the tile content of a given tile
* @param {Vector} tile
* @param {Layer} layer
* @returns {Entity} Entity or null
*/
getTileContent(tile, layer) {
if (G_IS_DEV) {
this.internalCheckTile(tile);
}
const chunk = this.getChunkAtTileOrNull(tile.x, tile.y);
return chunk && chunk.getLayerContentFromWorldCoords(tile.x, tile.y, layer);
}
/**
* Returns the lower layers content of the given tile
* @param {number} x
* @param {number} y
* @returns {BaseItem=}
*/
getLowerLayerContentXY(x, y) {
return this.getOrCreateChunkAtTile(x, y).getLowerLayerFromWorldCoords(x, y);
}
/**
* Returns the tile content of a given tile
* @param {number} x
* @param {number} y
* @param {Layer} layer
* @returns {Entity} Entity or null
*/
getLayerContentXY(x, y, layer) {
const chunk = this.getChunkAtTileOrNull(x, y);
return chunk && chunk.getLayerContentFromWorldCoords(x, y, layer);
}
/**
* Returns the tile contents of a given tile
* @param {number} x
* @param {number} y
* @returns {Array<Entity>} Entity or null
*/
getLayersContentsMultipleXY(x, y) {
const chunk = this.getChunkAtTileOrNull(x, y);
if (!chunk) {
return [];
}
return chunk.getLayersContentsMultipleFromWorldCoords(x, y);
}
/**
* Checks if the tile is used
* @param {Vector} tile
* @param {Layer} layer
* @returns {boolean}
*/
isTileUsed(tile, layer) {
if (G_IS_DEV) {
this.internalCheckTile(tile);
}
const chunk = this.getChunkAtTileOrNull(tile.x, tile.y);
return chunk && chunk.getLayerContentFromWorldCoords(tile.x, tile.y, layer) != null;
}
/**
* Checks if the tile is used
* @param {number} x
* @param {number} y
* @param {Layer} layer
* @returns {boolean}
*/
isTileUsedXY(x, y, layer) {
const chunk = this.getChunkAtTileOrNull(x, y);
return chunk && chunk.getLayerContentFromWorldCoords(x, y, layer) != null;
}
/**
* Sets the tiles content
* @param {Vector} tile
* @param {Entity} entity
*/
setTileContent(tile, entity) {
if (G_IS_DEV) {
this.internalCheckTile(tile);
}
this.getOrCreateChunkAtTile(tile.x, tile.y).setLayerContentFromWorldCords(
tile.x,
tile.y,
entity,
entity.layer
);
const staticComponent = entity.components.StaticMapEntity;
assert(staticComponent, "Can only place static map entities in tiles");
}
/**
* Places an entity with the StaticMapEntity component
* @param {Entity} entity
*/
placeStaticEntity(entity) {
assert(entity.components.StaticMapEntity, "Entity is not static");
const staticComp = entity.components.StaticMapEntity;
const rect = staticComp.getTileSpaceBounds();
for (let dx = 0; dx < rect.w; ++dx) {
for (let dy = 0; dy < rect.h; ++dy) {
const x = rect.x + dx;
const y = rect.y + dy;
this.getOrCreateChunkAtTile(x, y).setLayerContentFromWorldCords(x, y, entity, entity.layer);
}
}
}
/**
* Removes an entity with the StaticMapEntity component
* @param {Entity} entity
*/
removeStaticEntity(entity) {
assert(entity.components.StaticMapEntity, "Entity is not static");
const staticComp = entity.components.StaticMapEntity;
const rect = staticComp.getTileSpaceBounds();
for (let dx = 0; dx < rect.w; ++dx) {
for (let dy = 0; dy < rect.h; ++dy) {
const x = rect.x + dx;
const y = rect.y + dy;
this.getOrCreateChunkAtTile(x, y).setLayerContentFromWorldCords(x, y, null, entity.layer);
}
}
}
// Internal
/**
* Checks a given tile for validty
* @param {Vector} tile
*/
internalCheckTile(tile) {
assert(tile instanceof Vector, "tile is not a vector: " + tile);
assert(tile.x % 1 === 0, "Tile X is not a valid integer: " + tile.x);
assert(tile.y % 1 === 0, "Tile Y is not a valid integer: " + tile.y);
}
}
import { globalConfig } from "../core/config";
import { Vector } from "../core/vector";
import { BasicSerializableObject, types } from "../savegame/serialization";
import { BaseItem } from "./base_item";
import { Entity } from "./entity";
import { MapChunkView } from "./map_chunk_view";
import { GameRoot } from "./root";
export class BaseMap extends BasicSerializableObject {
static getId() {
return "Map";
}
static getSchema() {
return {
seed: types.uint,
};
}
/**
*
* @param {GameRoot} root
*/
constructor(root) {
super();
this.root = root;
this.seed = 0;
/**
* Mapping of 'X|Y' to chunk
* @type {Map<string, MapChunkView>} */
this.chunksById = new Map();
}
/**
* Returns the given chunk by index
* @param {number} chunkX
* @param {number} chunkY
*/
getChunk(chunkX, chunkY, createIfNotExistent = false) {
const chunkIdentifier = chunkX + "|" + chunkY;
let storedChunk;
if ((storedChunk = this.chunksById.get(chunkIdentifier))) {
return storedChunk;
}
if (createIfNotExistent) {
const instance = new MapChunkView(this.root, chunkX, chunkY);
this.chunksById.set(chunkIdentifier, instance);
return instance;
}
return null;
}
/**
* Gets or creates a new chunk if not existent for the given tile
* @param {number} tileX
* @param {number} tileY
* @returns {MapChunkView}
*/
getOrCreateChunkAtTile(tileX, tileY) {
const chunkX = Math.floor(tileX / globalConfig.mapChunkSize);
const chunkY = Math.floor(tileY / globalConfig.mapChunkSize);
return this.getChunk(chunkX, chunkY, true);
}
/**
* Gets a chunk if not existent for the given tile
* @param {number} tileX
* @param {number} tileY
* @returns {MapChunkView?}
*/
getChunkAtTileOrNull(tileX, tileY) {
const chunkX = Math.floor(tileX / globalConfig.mapChunkSize);
const chunkY = Math.floor(tileY / globalConfig.mapChunkSize);
return this.getChunk(chunkX, chunkY, false);
}
/**
* Checks if a given tile is within the map bounds
* @param {Vector} tile
* @returns {boolean}
*/
isValidTile(tile) {
if (G_IS_DEV) {
assert(tile instanceof Vector, "tile is not a vector");
}
return Number.isInteger(tile.x) && Number.isInteger(tile.y);
}
/**
* Returns the tile content of a given tile
* @param {Vector} tile
* @param {Layer} layer
* @returns {Entity} Entity or null
*/
getTileContent(tile, layer) {
if (G_IS_DEV) {
this.internalCheckTile(tile);
}
const chunk = this.getChunkAtTileOrNull(tile.x, tile.y);
return chunk && chunk.getLayerContentFromWorldCoords(tile.x, tile.y, layer);
}
/**
* Returns the lower layers content of the given tile
* @param {number} x
* @param {number} y
* @returns {BaseItem=}
*/
getLowerLayerContentXY(x, y) {
return this.getOrCreateChunkAtTile(x, y).getLowerLayerFromWorldCoords(x, y);
}
/**
* Returns the tile content of a given tile
* @param {number} x
* @param {number} y
* @param {Layer} layer
* @returns {Entity} Entity or null
*/
getLayerContentXY(x, y, layer) {
const chunk = this.getChunkAtTileOrNull(x, y);
return chunk && chunk.getLayerContentFromWorldCoords(x, y, layer);
}
/**
* Returns the tile contents of a given tile
* @param {number} x
* @param {number} y
* @returns {Array<Entity>} Entity or null
*/
getLayersContentsMultipleXY(x, y) {
const chunk = this.getChunkAtTileOrNull(x, y);
if (!chunk) {
return [];
}
return chunk.getLayersContentsMultipleFromWorldCoords(x, y);
}
/**
* Checks if the tile is used
* @param {Vector} tile
* @param {Layer} layer
* @returns {boolean}
*/
isTileUsed(tile, layer) {
if (G_IS_DEV) {
this.internalCheckTile(tile);
}
const chunk = this.getChunkAtTileOrNull(tile.x, tile.y);
return chunk && chunk.getLayerContentFromWorldCoords(tile.x, tile.y, layer) != null;
}
/**
* Checks if the tile is used
* @param {number} x
* @param {number} y
* @param {Layer} layer
* @returns {boolean}
*/
isTileUsedXY(x, y, layer) {
const chunk = this.getChunkAtTileOrNull(x, y);
return chunk && chunk.getLayerContentFromWorldCoords(x, y, layer) != null;
}
/**
* Sets the tiles content
* @param {Vector} tile
* @param {Entity} entity
*/
setTileContent(tile, entity) {
if (G_IS_DEV) {
this.internalCheckTile(tile);
}
this.getOrCreateChunkAtTile(tile.x, tile.y).setLayerContentFromWorldCords(
tile.x,
tile.y,
entity,
entity.layer
);
const staticComponent = entity.components.StaticMapEntity;
assert(staticComponent, "Can only place static map entities in tiles");
}
/**
* Places an entity with the StaticMapEntity component
* @param {Entity} entity
*/
placeStaticEntity(entity) {
assert(entity.components.StaticMapEntity, "Entity is not static");
const staticComp = entity.components.StaticMapEntity;
const rect = staticComp.getTileSpaceBounds();
for (let dx = 0; dx < rect.w; ++dx) {
for (let dy = 0; dy < rect.h; ++dy) {
const x = rect.x + dx;
const y = rect.y + dy;
this.getOrCreateChunkAtTile(x, y).setLayerContentFromWorldCords(x, y, entity, entity.layer);
}
}
}
/**
* Removes an entity with the StaticMapEntity component
* @param {Entity} entity
*/
removeStaticEntity(entity) {
assert(entity.components.StaticMapEntity, "Entity is not static");
const staticComp = entity.components.StaticMapEntity;
const rect = staticComp.getTileSpaceBounds();
for (let dx = 0; dx < rect.w; ++dx) {
for (let dy = 0; dy < rect.h; ++dy) {
const x = rect.x + dx;
const y = rect.y + dy;
this.getOrCreateChunkAtTile(x, y).setLayerContentFromWorldCords(x, y, null, entity.layer);
}
}
}
// Internal
/**
* Checks a given tile for validty
* @param {Vector} tile
*/
internalCheckTile(tile) {
assert(tile instanceof Vector, "tile is not a vector: " + tile);
assert(tile.x % 1 === 0, "Tile X is not a valid integer: " + tile.x);
assert(tile.y % 1 === 0, "Tile Y is not a valid integer: " + tile.y);
}
}

View File

@@ -66,32 +66,34 @@ export class MapView extends BaseMap {
* @param {DrawParameters} drawParameters
*/
drawStaticEntityDebugOverlays(drawParameters) {
const cullRange = drawParameters.visibleRect.toTileCullRectangle();
const top = cullRange.top();
const right = cullRange.right();
const bottom = cullRange.bottom();
const left = cullRange.left();
if (G_IS_DEV && (globalConfig.debug.showAcceptorEjectors || globalConfig.debug.showEntityBounds)) {
const cullRange = drawParameters.visibleRect.toTileCullRectangle();
const top = cullRange.top();
const right = cullRange.right();
const bottom = cullRange.bottom();
const left = cullRange.left();
const border = 1;
const border = 1;
const minY = top - border;
const maxY = bottom + border;
const minX = left - border;
const maxX = right + border - 1;
const minY = top - border;
const maxY = bottom + border;
const minX = left - border;
const maxX = right + border - 1;
// Render y from top down for proper blending
for (let y = minY; y <= maxY; ++y) {
for (let x = minX; x <= maxX; ++x) {
// const content = this.tiles[x][y];
const chunk = this.getChunkAtTileOrNull(x, y);
if (!chunk) {
continue;
}
const content = chunk.getTileContentFromWorldCoords(x, y);
if (content) {
let isBorder = x <= left - 1 || x >= right + 1 || y <= top - 1 || y >= bottom + 1;
if (!isBorder) {
content.drawDebugOverlays(drawParameters);
// Render y from top down for proper blending
for (let y = minY; y <= maxY; ++y) {
for (let x = minX; x <= maxX; ++x) {
// const content = this.tiles[x][y];
const chunk = this.getChunkAtTileOrNull(x, y);
if (!chunk) {
continue;
}
const content = chunk.getTileContentFromWorldCoords(x, y);
if (content) {
let isBorder = x <= left - 1 || x >= right + 1 || y <= top - 1 || y >= bottom + 1;
if (!isBorder) {
content.drawDebugOverlays(drawParameters);
}
}
}
}

View File

@@ -12,7 +12,7 @@ import { MetaStackerBuilding } from "./buildings/stacker";
import { enumTrashVariants, MetaTrashBuilding } from "./buildings/trash";
import { enumUndergroundBeltVariants, MetaUndergroundBeltBuilding } from "./buildings/underground_belt";
import { MetaWireBuilding } from "./buildings/wire";
import { gBuildingVariants, registerBuildingVariant } from "./building_codes";
import { buildBuildingCodeCache, gBuildingVariants, registerBuildingVariant } from "./building_codes";
import { defaultBuildingVariant } from "./meta_building";
import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
import { MetaLogicGateBuilding, enumLogicGateVariants } from "./buildings/logic_gate";
@@ -174,4 +174,7 @@ export function initBuildingCodesAfterResourcesLoaded() {
);
variant.silhouetteColor = variant.metaInstance.getSilhouetteColor();
}
// Update caches
buildBuildingCodeCache();
}

View File

@@ -1,101 +1,101 @@
import { GameSystemWithFilter } from "../game_system_with_filter";
import { StorageComponent } from "../components/storage";
import { DrawParameters } from "../../core/draw_parameters";
import { formatBigNumber, lerp } from "../../core/utils";
import { Loader } from "../../core/loader";
import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item";
import { MapChunkView } from "../map_chunk_view";
export class StorageSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [StorageComponent]);
this.storageOverlaySprite = Loader.getSprite("sprites/misc/storage_overlay.png");
/**
* Stores which uids were already drawn to avoid drawing entities twice
* @type {Set<number>}
*/
this.drawnUids = new Set();
this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this);
}
clearDrawnUids() {
this.drawnUids.clear();
}
update() {
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const storageComp = entity.components.Storage;
const pinsComp = entity.components.WiredPins;
// Eject from storage
if (storageComp.storedItem && storageComp.storedCount > 0) {
const ejectorComp = entity.components.ItemEjector;
const nextSlot = ejectorComp.getFirstFreeSlot();
if (nextSlot !== null) {
if (ejectorComp.tryEject(nextSlot, storageComp.storedItem)) {
storageComp.storedCount--;
if (storageComp.storedCount === 0) {
storageComp.storedItem = null;
}
}
}
}
let targetAlpha = storageComp.storedCount > 0 ? 1 : 0;
storageComp.overlayOpacity = lerp(storageComp.overlayOpacity, targetAlpha, 0.05);
pinsComp.slots[0].value = storageComp.storedItem;
pinsComp.slots[1].value = storageComp.getIsFull() ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
}
}
/**
* @param {DrawParameters} parameters
* @param {MapChunkView} chunk
*/
drawChunk(parameters, chunk) {
const contents = chunk.containedEntitiesByLayer.regular;
for (let i = 0; i < contents.length; ++i) {
const entity = contents[i];
const storageComp = entity.components.Storage;
if (!storageComp) {
continue;
}
const storedItem = storageComp.storedItem;
if (!storedItem) {
continue;
}
if (this.drawnUids.has(entity.uid)) {
continue;
}
this.drawnUids.add(entity.uid);
const staticComp = entity.components.StaticMapEntity;
const context = parameters.context;
context.globalAlpha = storageComp.overlayOpacity;
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
storedItem.drawItemCenteredClipped(center.x, center.y, parameters, 30);
this.storageOverlaySprite.drawCached(parameters, center.x - 15, center.y + 15, 30, 15);
if (parameters.visibleRect.containsCircle(center.x, center.y + 25, 20)) {
context.font = "bold 10px GameFont";
context.textAlign = "center";
context.fillStyle = "#64666e";
context.fillText(formatBigNumber(storageComp.storedCount), center.x, center.y + 25.5);
context.textAlign = "left";
}
context.globalAlpha = 1;
}
}
}
import { GameSystemWithFilter } from "../game_system_with_filter";
import { StorageComponent } from "../components/storage";
import { DrawParameters } from "../../core/draw_parameters";
import { formatBigNumber, lerp } from "../../core/utils";
import { Loader } from "../../core/loader";
import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item";
import { MapChunkView } from "../map_chunk_view";
export class StorageSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [StorageComponent]);
this.storageOverlaySprite = Loader.getSprite("sprites/misc/storage_overlay.png");
/**
* Stores which uids were already drawn to avoid drawing entities twice
* @type {Set<number>}
*/
this.drawnUids = new Set();
this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this);
}
clearDrawnUids() {
this.drawnUids.clear();
}
update() {
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const storageComp = entity.components.Storage;
const pinsComp = entity.components.WiredPins;
// Eject from storage
if (storageComp.storedItem && storageComp.storedCount > 0) {
const ejectorComp = entity.components.ItemEjector;
const nextSlot = ejectorComp.getFirstFreeSlot();
if (nextSlot !== null) {
if (ejectorComp.tryEject(nextSlot, storageComp.storedItem)) {
storageComp.storedCount--;
if (storageComp.storedCount === 0) {
storageComp.storedItem = null;
}
}
}
}
let targetAlpha = storageComp.storedCount > 0 ? 1 : 0;
storageComp.overlayOpacity = lerp(storageComp.overlayOpacity, targetAlpha, 0.05);
pinsComp.slots[0].value = storageComp.storedItem;
pinsComp.slots[1].value = storageComp.getIsFull() ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
}
}
/**
* @param {DrawParameters} parameters
* @param {MapChunkView} chunk
*/
drawChunk(parameters, chunk) {
const contents = chunk.containedEntitiesByLayer.regular;
for (let i = 0; i < contents.length; ++i) {
const entity = contents[i];
const storageComp = entity.components.Storage;
if (!storageComp) {
continue;
}
const storedItem = storageComp.storedItem;
if (!storedItem) {
continue;
}
if (this.drawnUids.has(entity.uid)) {
continue;
}
this.drawnUids.add(entity.uid);
const staticComp = entity.components.StaticMapEntity;
const context = parameters.context;
context.globalAlpha = storageComp.overlayOpacity;
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
storedItem.drawItemCenteredClipped(center.x, center.y, parameters, 30);
this.storageOverlaySprite.drawCached(parameters, center.x - 15, center.y + 15, 30, 15);
if (parameters.visibleRect.containsCircle(center.x, center.y + 25, 20)) {
context.font = "bold 10px GameFont";
context.textAlign = "center";
context.fillStyle = "#64666e";
context.fillText(formatBigNumber(storageComp.storedCount), center.x, center.y + 25.5);
context.textAlign = "left";
}
context.globalAlpha = 1;
}
}
}