mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-03-02 03:39:21 +00:00
Add storage building
This commit is contained in:
@@ -130,10 +130,12 @@
|
||||
&[data-tile-w="4"] {
|
||||
@include S(width, 4 * $iconSize);
|
||||
}
|
||||
|
||||
&[data-tile-h="2"] {
|
||||
@include S(height, 2 * $iconSize);
|
||||
}
|
||||
&[data-tile-h="3"] {
|
||||
@include S(height, 3 * $iconSize);
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Loader } from "../../core/loader";
|
||||
import { formatItemsPerSecond } from "../../core/utils";
|
||||
import { enumAngleToDirection, enumDirection, Vector } from "../../core/vector";
|
||||
import { SOUNDS } from "../../platform/sound";
|
||||
import { T } from "../../translations";
|
||||
import { BeltComponent } from "../components/belt";
|
||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
@@ -7,10 +10,6 @@ import { ReplaceableMapEntityComponent } from "../components/replaceable_map_ent
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
import { GameRoot } from "../root";
|
||||
import { SOUNDS } from "../../platform/sound";
|
||||
import { T } from "../../translations";
|
||||
import { round1Digit, formatItemsPerSecond } from "../../core/utils";
|
||||
import { globalConfig } from "../../core/config";
|
||||
|
||||
export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right];
|
||||
|
||||
|
||||
@@ -3,23 +3,64 @@ import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||
import { GameRoot } from "../root";
|
||||
import { StorageComponent } from "../components/storage";
|
||||
import { T } from "../../translations";
|
||||
import { formatBigNumber } from "../../core/utils";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumTrashVariants = { storage: "storage" };
|
||||
|
||||
const trashSize = 5000;
|
||||
|
||||
export class MetaTrashBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
super("trash");
|
||||
}
|
||||
|
||||
isRotateable() {
|
||||
return false;
|
||||
isRotateable(variant) {
|
||||
return variant !== defaultBuildingVariant;
|
||||
}
|
||||
|
||||
getSilhouetteColor() {
|
||||
return "#cd7d86";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
* @param {string} variant
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
if (variant === enumTrashVariants.storage) {
|
||||
return [[T.ingame.buildingPlacement.infoTexts.storage, formatBigNumber(trashSize)]];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
getDimensions(variant) {
|
||||
switch (variant) {
|
||||
case defaultBuildingVariant:
|
||||
return new Vector(1, 1);
|
||||
case enumTrashVariants.storage:
|
||||
return new Vector(2, 2);
|
||||
default:
|
||||
assertAlways(false, "Unknown trash variant: " + variant);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
getAvailableVariants(root) {
|
||||
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_storage)) {
|
||||
return [defaultBuildingVariant, enumTrashVariants.storage];
|
||||
}
|
||||
return super.getAvailableVariants(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
@@ -32,13 +73,6 @@ export class MetaTrashBuilding extends MetaBuilding {
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
setupEntityComponents(entity) {
|
||||
entity.addComponent(
|
||||
new ItemProcessorComponent({
|
||||
inputsPerCharge: 1,
|
||||
processorType: enumItemProcessorTypes.trash,
|
||||
})
|
||||
);
|
||||
|
||||
// Required, since the item processor needs this.
|
||||
entity.addComponent(
|
||||
new ItemEjectorComponent({
|
||||
@@ -62,4 +96,77 @@ export class MetaTrashBuilding extends MetaBuilding {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Entity} entity
|
||||
* @param {number} rotationVariant
|
||||
* @param {string} variant
|
||||
*/
|
||||
updateVariants(entity, rotationVariant, variant) {
|
||||
switch (variant) {
|
||||
case defaultBuildingVariant: {
|
||||
if (!entity.components.ItemProcessor) {
|
||||
entity.addComponent(
|
||||
new ItemProcessorComponent({
|
||||
inputsPerCharge: 1,
|
||||
processorType: enumItemProcessorTypes.trash,
|
||||
})
|
||||
);
|
||||
}
|
||||
if (entity.components.Storage) {
|
||||
entity.removeComponent(StorageComponent);
|
||||
}
|
||||
|
||||
entity.components.ItemAcceptor.setSlots([
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [
|
||||
enumDirection.top,
|
||||
enumDirection.right,
|
||||
enumDirection.bottom,
|
||||
enumDirection.left,
|
||||
],
|
||||
},
|
||||
]);
|
||||
entity.components.ItemEjector.setSlots([]);
|
||||
entity.components.ItemProcessor.type = enumItemProcessorTypes.trash;
|
||||
break;
|
||||
}
|
||||
case enumTrashVariants.storage: {
|
||||
if (entity.components.ItemProcessor) {
|
||||
entity.removeComponent(ItemProcessorComponent);
|
||||
}
|
||||
if (!entity.components.Storage) {
|
||||
entity.addComponent(new StorageComponent({}));
|
||||
}
|
||||
|
||||
entity.components.Storage.maximumStorage = trashSize;
|
||||
entity.components.ItemAcceptor.setSlots([
|
||||
{
|
||||
pos: new Vector(0, 1),
|
||||
directions: [enumDirection.bottom],
|
||||
},
|
||||
{
|
||||
pos: new Vector(1, 1),
|
||||
directions: [enumDirection.bottom],
|
||||
},
|
||||
]);
|
||||
|
||||
entity.components.ItemEjector.setSlots([
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
direction: enumDirection.top,
|
||||
},
|
||||
{
|
||||
pos: new Vector(1, 0),
|
||||
direction: enumDirection.top,
|
||||
},
|
||||
]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assertAlways(false, "Unknown trash variant: " + variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ReplaceableMapEntityComponent } from "./components/replaceable_map_enti
|
||||
import { UndergroundBeltComponent } from "./components/underground_belt";
|
||||
import { UnremovableComponent } from "./components/unremovable";
|
||||
import { HubComponent } from "./components/hub";
|
||||
import { StorageComponent } from "./components/storage";
|
||||
|
||||
export function initComponentRegistry() {
|
||||
gComponentRegistry.register(StaticMapEntityComponent);
|
||||
@@ -21,9 +22,9 @@ export function initComponentRegistry() {
|
||||
gComponentRegistry.register(UndergroundBeltComponent);
|
||||
gComponentRegistry.register(UnremovableComponent);
|
||||
gComponentRegistry.register(HubComponent);
|
||||
gComponentRegistry.register(StorageComponent);
|
||||
|
||||
// IMPORTANT ^^^^^ REGENERATE SAVEGAME SCHEMA AFTERWARDS
|
||||
// IMPORTANT ^^^^^ ALSO UPDATE ENTITY COMPONENT STORAG
|
||||
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS
|
||||
|
||||
// Sanity check - If this is thrown, you (=me, lol) forgot to add a new component here
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ export class BeltComponent extends Component {
|
||||
/**
|
||||
* Returns if the belt can currently accept an item from the given direction
|
||||
*/
|
||||
canAcceptNewItem(leftoverProgress = 0.0) {
|
||||
canAcceptItem(leftoverProgress = 0.0) {
|
||||
const firstItem = this.sortedItems[0];
|
||||
if (!firstItem) {
|
||||
return true;
|
||||
@@ -73,7 +73,7 @@ export class BeltComponent extends Component {
|
||||
* Pushes a new item to the belt
|
||||
* @param {BaseItem} item
|
||||
*/
|
||||
takeNewItem(item, leftoverProgress = 0.0) {
|
||||
takeItem(item, leftoverProgress = 0.0) {
|
||||
if (G_IS_DEV) {
|
||||
assert(
|
||||
this.sortedItems.length === 0 ||
|
||||
|
||||
80
src/js/game/components/storage.js
Normal file
80
src/js/game/components/storage.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Component } from "../component";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { gItemRegistry } from "../../core/global_registries";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { ColorItem } from "../items/color_item";
|
||||
import { ShapeItem } from "../items/shape_item";
|
||||
|
||||
export class StorageComponent extends Component {
|
||||
static getId() {
|
||||
return "Storage";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
maximumStorage: types.uint,
|
||||
storedCount: types.uint,
|
||||
storedItem: types.nullable(types.obj(gItemRegistry)),
|
||||
overlayOpacity: types.ufloat,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} param0
|
||||
* @param {number=} param0.maximumStorage How much this storage can hold
|
||||
*/
|
||||
constructor({ maximumStorage = 1e20 }) {
|
||||
super();
|
||||
this.maximumStorage = maximumStorage;
|
||||
|
||||
/**
|
||||
* Currently stored item
|
||||
* @type {BaseItem}
|
||||
*/
|
||||
this.storedItem = null;
|
||||
|
||||
/**
|
||||
* How many of this item we have stored
|
||||
*/
|
||||
this.storedCount = 0;
|
||||
|
||||
/**
|
||||
* We compute an opacity to make sure it doesn't flicker
|
||||
*/
|
||||
this.overlayOpacity = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this storage can accept the item
|
||||
* @param {BaseItem} item
|
||||
*/
|
||||
canAcceptItem(item) {
|
||||
if (this.storedCount >= this.maximumStorage) {
|
||||
return false;
|
||||
}
|
||||
if (!this.storedItem || this.storedCount === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item instanceof ColorItem) {
|
||||
return this.storedItem instanceof ColorItem && this.storedItem.color === item.color;
|
||||
}
|
||||
|
||||
if (item instanceof ShapeItem) {
|
||||
return (
|
||||
this.storedItem instanceof ShapeItem &&
|
||||
this.storedItem.definition.getHash() === item.definition.getHash()
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BaseItem} item
|
||||
*/
|
||||
takeItem(item) {
|
||||
this.storedItem = item;
|
||||
this.storedCount++;
|
||||
}
|
||||
}
|
||||
@@ -404,6 +404,7 @@ export class GameCore {
|
||||
root.map.drawForeground(params);
|
||||
if (!this.root.camera.getIsMapOverlayActive()) {
|
||||
systems.hub.draw(params);
|
||||
systems.storage.draw(params);
|
||||
}
|
||||
|
||||
if (G_IS_DEV) {
|
||||
|
||||
@@ -99,6 +99,10 @@ export class Entity extends BasicSerializableObject {
|
||||
* @param {boolean} force Used by the entity manager. Internal parameter, do not change
|
||||
*/
|
||||
addComponent(componentInstance, force = false) {
|
||||
if (!force && this.registered) {
|
||||
this.root.entityMgr.attachDynamicComponent(this, componentInstance);
|
||||
return;
|
||||
}
|
||||
assert(force || !this.registered, "Entity already registered, use EntityManager.addDynamicComponent");
|
||||
const id = /** @type {typeof Component} */ (componentInstance.constructor).getId();
|
||||
assert(!this.components[id], "Component already present");
|
||||
@@ -109,9 +113,17 @@ export class Entity extends BasicSerializableObject {
|
||||
* Removes a given component, only possible until the entity is registered on the entity manager,
|
||||
* after that use @see EntityManager.removeDynamicComponent
|
||||
* @param {typeof Component} componentClass
|
||||
* @param {boolean} force
|
||||
*/
|
||||
removeComponent(componentClass) {
|
||||
assert(!this.registered, "Entity already registered, use EntityManager.removeDynamicComponent");
|
||||
removeComponent(componentClass, force = false) {
|
||||
if (!force && this.registered) {
|
||||
this.root.entityMgr.removeDynamicComponent(this, componentClass);
|
||||
return;
|
||||
}
|
||||
assert(
|
||||
force || !this.registered,
|
||||
"Entity already registered, use EntityManager.removeDynamicComponent"
|
||||
);
|
||||
const id = componentClass.getId();
|
||||
assert(this.components[id], "Component does not exist on entity");
|
||||
delete this.components[id];
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ReplaceableMapEntityComponent } from "./components/replaceable_map_enti
|
||||
import { UndergroundBeltComponent } from "./components/underground_belt";
|
||||
import { UnremovableComponent } from "./components/unremovable";
|
||||
import { HubComponent } from "./components/hub";
|
||||
import { StorageComponent } from "./components/storage";
|
||||
/* typehints:end */
|
||||
|
||||
/**
|
||||
@@ -52,6 +53,9 @@ export class EntityComponentStorage {
|
||||
/** @type {HubComponent} */
|
||||
this.Hub;
|
||||
|
||||
/** @type {StorageComponent} */
|
||||
this.Storage;
|
||||
|
||||
/* typehints:end */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { arrayDeleteValue, newEmptyMap } from "../core/utils";
|
||||
import { arrayDeleteValue, newEmptyMap, fastArrayDeleteValue } from "../core/utils";
|
||||
import { Component } from "./component";
|
||||
import { GameRoot } from "./root";
|
||||
import { Entity } from "./entity";
|
||||
@@ -128,6 +128,19 @@ export class EntityManager extends BasicSerializableObject {
|
||||
this.root.signals.entityGotNewComponent.dispatch(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to remove a component after the creation of the entity
|
||||
* @param {Entity} entity
|
||||
* @param {typeof Component} component
|
||||
*/
|
||||
removeDynamicComponent(entity, component) {
|
||||
entity.removeComponent(component, true);
|
||||
const componentId = /** @type {typeof Component} */ (component.constructor).getId();
|
||||
|
||||
fastArrayDeleteValue(this.componentToEntity[componentId], entity);
|
||||
this.root.signals.entityComponentRemoved.dispatch(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an entity buy its uid, kinda slow since it loops over all entities
|
||||
* @param {number} uid
|
||||
|
||||
@@ -12,6 +12,7 @@ import { UndergroundBeltSystem } from "./systems/underground_belt";
|
||||
import { HubSystem } from "./systems/hub";
|
||||
import { StaticMapEntitySystem } from "./systems/static_map_entity";
|
||||
import { ItemAcceptorSystem } from "./systems/item_acceptor";
|
||||
import { StorageSystem } from "./systems/storage";
|
||||
|
||||
const logger = createLogger("game_system_manager");
|
||||
|
||||
@@ -52,6 +53,9 @@ export class GameSystemManager {
|
||||
/** @type {ItemAcceptorSystem} */
|
||||
itemAcceptor: null,
|
||||
|
||||
/** @type {StorageSystem} */
|
||||
storage: null,
|
||||
|
||||
/* typehints:end */
|
||||
};
|
||||
this.systemUpdateOrder = [];
|
||||
@@ -72,15 +76,17 @@ export class GameSystemManager {
|
||||
|
||||
add("belt", BeltSystem);
|
||||
|
||||
add("itemEjector", ItemEjectorSystem);
|
||||
add("undergroundBelt", UndergroundBeltSystem);
|
||||
|
||||
add("miner", MinerSystem);
|
||||
|
||||
add("mapResources", MapResourcesSystem);
|
||||
add("storage", StorageSystem);
|
||||
|
||||
add("itemProcessor", ItemProcessorSystem);
|
||||
|
||||
add("undergroundBelt", UndergroundBeltSystem);
|
||||
add("itemEjector", ItemEjectorSystem);
|
||||
|
||||
add("mapResources", MapResourcesSystem);
|
||||
|
||||
add("hub", HubSystem);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Entity } from "./entity";
|
||||
/* typehints:end */
|
||||
|
||||
import { GameSystem } from "./game_system";
|
||||
import { arrayDelete } from "../core/utils";
|
||||
import { arrayDelete, arrayDeleteValue } from "../core/utils";
|
||||
import { DrawParameters } from "../core/draw_parameters";
|
||||
import { globalConfig } from "../core/config";
|
||||
import { Math_floor, Math_ceil } from "../core/builtins";
|
||||
@@ -30,6 +30,7 @@ export class GameSystemWithFilter extends GameSystem {
|
||||
|
||||
this.root.signals.entityAdded.add(this.internalPushEntityIfMatching, this);
|
||||
this.root.signals.entityGotNewComponent.add(this.internalReconsiderEntityToAdd, this);
|
||||
this.root.signals.entityComponentRemoved.add(this.internalCheckEntityAfterComponentRemoval, this);
|
||||
this.root.signals.entityQueuedForDestroy.add(this.internalPopEntityIfMatching, this);
|
||||
|
||||
this.root.signals.postLoadHook.add(this.internalPostLoadHook, this);
|
||||
@@ -122,6 +123,24 @@ export class GameSystemWithFilter extends GameSystem {
|
||||
this.internalRegisterEntity(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
internalCheckEntityAfterComponentRemoval(entity) {
|
||||
if (this.allEntities.indexOf(entity) < 0) {
|
||||
// Entity wasn't interesting anyways
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.requiredComponentIds.length; ++i) {
|
||||
if (!entity.components[this.requiredComponentIds[i]]) {
|
||||
// Entity is not interesting anymore
|
||||
arrayDeleteValue(this.allEntities, entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Entity} entity
|
||||
|
||||
@@ -107,9 +107,10 @@ export class MetaBuilding {
|
||||
|
||||
/**
|
||||
* Returns whether this building is rotateable
|
||||
* @param {string} variant
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isRotateable() {
|
||||
isRotateable(variant) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -185,7 +186,7 @@ export class MetaBuilding {
|
||||
* @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }}
|
||||
*/
|
||||
computeOptimalDirectionAndRotationVariantAtTile(root, tile, rotation, variant) {
|
||||
if (!this.isRotateable()) {
|
||||
if (!this.isRotateable(variant)) {
|
||||
return {
|
||||
rotation: 0,
|
||||
rotationVariant: 0,
|
||||
|
||||
@@ -123,6 +123,7 @@ export class GameRoot {
|
||||
// Entities
|
||||
entityAdded: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||
entityGotNewComponent: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||
entityComponentRemoved: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||
entityQueuedForDestroy: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||
entityDestroyed: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||
|
||||
|
||||
@@ -258,8 +258,8 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
if (progressAndItem[0] >= 1.0) {
|
||||
if (followUp) {
|
||||
const followUpBelt = followUp.components.Belt;
|
||||
if (followUpBelt.canAcceptNewItem()) {
|
||||
followUpBelt.takeNewItem(progressAndItem[1], progressAndItem[0] - 1.0);
|
||||
if (followUpBelt.canAcceptItem()) {
|
||||
followUpBelt.takeItem(progressAndItem[1], progressAndItem[0] - 1.0);
|
||||
items.splice(itemIndex, 1);
|
||||
} else {
|
||||
// Well, we couldn't really take it to a follow up belt, keep it at
|
||||
|
||||
@@ -99,8 +99,17 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
const beltComp = receiver.components.Belt;
|
||||
if (beltComp) {
|
||||
// Ayy, its a belt!
|
||||
if (beltComp.canAcceptNewItem()) {
|
||||
beltComp.takeNewItem(item);
|
||||
if (beltComp.canAcceptItem()) {
|
||||
beltComp.takeItem(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const storageComp = receiver.components.Storage;
|
||||
if (storageComp) {
|
||||
// It's a storage
|
||||
if (storageComp.canAcceptItem(item)) {
|
||||
storageComp.takeItem(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,19 @@ export class StaticMapEntitySystem extends GameSystem {
|
||||
|
||||
const drawOutlinesOnly = parameters.zoomLevel < globalConfig.mapChunkOverviewMinZoom;
|
||||
|
||||
const drawnUids = new Set();
|
||||
|
||||
const contents = chunk.contents;
|
||||
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
|
||||
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
|
||||
const entity = contents[x][y];
|
||||
|
||||
if (entity) {
|
||||
if (drawnUids.has(entity.uid)) {
|
||||
continue;
|
||||
}
|
||||
drawnUids.add(entity.uid);
|
||||
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
if (drawOutlinesOnly) {
|
||||
const rect = staticComp.getTileSpaceBounds();
|
||||
|
||||
75
src/js/game/systems/storage.js
Normal file
75
src/js/game/systems/storage.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { StorageComponent } from "../components/storage";
|
||||
import { Entity } from "../entity";
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { formatBigNumber, lerp } from "../../core/utils";
|
||||
import { Loader } from "../../core/loader";
|
||||
|
||||
export class StorageSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
super(root, [StorageComponent]);
|
||||
|
||||
this.storageOverlaySprite = Loader.getSprite("sprites/misc/storage_overlay.png");
|
||||
}
|
||||
|
||||
update() {
|
||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||
const entity = this.allEntities[i];
|
||||
const storageComp = entity.components.Storage;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
draw(parameters) {
|
||||
this.forEachMatchingEntityOnScreen(parameters, this.drawEntity.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
drawEntity(parameters, entity) {
|
||||
const context = parameters.context;
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const storageComp = entity.components.Storage;
|
||||
|
||||
const storedItem = storageComp.storedItem;
|
||||
if (storedItem !== null) {
|
||||
context.globalAlpha = storageComp.overlayOpacity;
|
||||
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||
storedItem.draw(center.x, center.y, parameters, 30);
|
||||
|
||||
this.storageOverlaySprite.drawCached(parameters, center.x - 15, center.y + 15, 30, 15);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ export const enumHubGoalRewards = {
|
||||
reward_cutter_quad: "reward_cutter_quad",
|
||||
reward_painter_double: "reward_painter_double",
|
||||
reward_painter_quad: "reward_painter_quad",
|
||||
reward_storage: "reward_storage",
|
||||
|
||||
reward_freeplay: "reward_freeplay",
|
||||
|
||||
@@ -107,6 +108,12 @@ export const tutorialGoals = [
|
||||
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
|
||||
},
|
||||
|
||||
{
|
||||
shape: "SrSrSrSr:CyCyCyCy", // unused
|
||||
required: 7850,
|
||||
reward: enumHubGoalRewards.reward_storage,
|
||||
},
|
||||
|
||||
{
|
||||
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
|
||||
required: 8000,
|
||||
|
||||
Reference in New Issue
Block a user