mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Add buffer functionality
This commit is contained in:
parent
1a6b64f914
commit
02ad9e3061
BIN
res/ui/building_icons/buffer.png
Normal file
BIN
res/ui/building_icons/buffer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
BIN
res_raw/sprites/blueprints/buffer.png
Normal file
BIN
res_raw/sprites/blueprints/buffer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
BIN
res_raw/sprites/buildings/buffer.png
Normal file
BIN
res_raw/sprites/buildings/buffer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
@ -1,6 +1,7 @@
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
import { ItemAcceptorComponent, enumItemAcceptorItemFilter } from "../components/item_acceptor";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { BufferComponent } from "../components/item_buffer";
|
||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
@ -34,9 +35,11 @@ export class MetaBufferBuilding extends MetaBuilding {
|
||||
*/
|
||||
setupEntityComponents(entity) {
|
||||
// TODO: Use custom component here to allow for smooth output
|
||||
entity.addComponent(new BufferComponent());
|
||||
entity.addComponent(
|
||||
new ItemProcessorComponent({
|
||||
inputsPerCharge: 1,
|
||||
chargeWhenBlocked: true,
|
||||
processorType: enumItemProcessorTypes.buffer,
|
||||
})
|
||||
);
|
||||
@ -47,6 +50,9 @@ export class MetaBufferBuilding extends MetaBuilding {
|
||||
})
|
||||
);
|
||||
|
||||
// We render the sprite our self
|
||||
// entity.components.StaticMapEntity.spriteKey = null;
|
||||
|
||||
// TODO: Replace item filters with custom filter to only allow one type of item to be collected.
|
||||
entity.addComponent(
|
||||
new ItemAcceptorComponent({
|
||||
|
@ -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 { BufferComponent } from "./components/item_buffer";
|
||||
|
||||
export function initComponentRegistry() {
|
||||
gComponentRegistry.register(StaticMapEntityComponent);
|
||||
@ -21,6 +22,7 @@ export function initComponentRegistry() {
|
||||
gComponentRegistry.register(UndergroundBeltComponent);
|
||||
gComponentRegistry.register(UnremovableComponent);
|
||||
gComponentRegistry.register(HubComponent);
|
||||
gComponentRegistry.register(BufferComponent);
|
||||
|
||||
// IMPORTANT ^^^^^ REGENERATE SAVEGAME SCHEMA AFTERWARDS
|
||||
// IMPORTANT ^^^^^ ALSO UPDATE ENTITY COMPONENT STORAG
|
||||
|
42
src/js/game/components/item_buffer.js
Normal file
42
src/js/game/components/item_buffer.js
Normal file
@ -0,0 +1,42 @@
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { Component } from "../component";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { ShapeDefinition } from "../shape_definition";
|
||||
import { gItemRegistry } from "../../core/global_registries";
|
||||
|
||||
|
||||
export class BufferComponent extends Component {
|
||||
static getId() {
|
||||
return "Buffer";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
itemCount: types.uint,
|
||||
storageLimit: types.uint,
|
||||
// TODO: Is this the right type for an item definition?
|
||||
definition: types.nullable(types.knownType(ShapeDefinition)),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.itemCount = 0;
|
||||
// TODO: Make buffer storage a level up reward
|
||||
this.storageLimit = 5000;
|
||||
|
||||
this.definition = null;
|
||||
}
|
||||
|
||||
// TODO: Is this function needed?
|
||||
/**
|
||||
*
|
||||
* @param {BaseItem} item
|
||||
*/
|
||||
tryAcceptChainedItem(item) {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ export class ItemProcessorComponent extends Component {
|
||||
nextOutputSlot: types.uint,
|
||||
type: types.enum(enumItemProcessorTypes),
|
||||
inputsPerCharge: types.uint,
|
||||
|
||||
chargeWhenBlocked: types.bool,
|
||||
inputSlots: types.array(
|
||||
types.structured({
|
||||
item: types.obj(gItemRegistry),
|
||||
@ -54,9 +54,10 @@ export class ItemProcessorComponent extends Component {
|
||||
* @param {object} param0
|
||||
* @param {enumItemProcessorTypes=} param0.processorType Which type of processor this is
|
||||
* @param {number=} param0.inputsPerCharge How many items this machine needs until it can start working
|
||||
* @param {boolean=} param0.chargeWhenBlocked If charges should be preformed when the output buffer is full
|
||||
*
|
||||
*/
|
||||
constructor({ processorType = enumItemProcessorTypes.splitter, inputsPerCharge = 1 }) {
|
||||
constructor({ processorType = enumItemProcessorTypes.splitter, inputsPerCharge = 1, chargeWhenBlocked = false}) {
|
||||
super();
|
||||
|
||||
// Which slot to emit next, this is only a preference and if it can't emit
|
||||
@ -70,6 +71,9 @@ export class ItemProcessorComponent extends Component {
|
||||
// How many inputs we need for one charge
|
||||
this.inputsPerCharge = inputsPerCharge;
|
||||
|
||||
// Should charges be performed when the output buffer still has items (output is blocked)
|
||||
this.chargeWhenBlocked = chargeWhenBlocked;
|
||||
|
||||
/**
|
||||
* Our current inputs
|
||||
* @type {Array<{ item: BaseItem, sourceSlot: number }>}
|
||||
|
@ -9,6 +9,8 @@ import { ReplaceableMapEntityComponent } from "./components/replaceable_map_enti
|
||||
import { UndergroundBeltComponent } from "./components/underground_belt";
|
||||
import { UnremovableComponent } from "./components/unremovable";
|
||||
import { HubComponent } from "./components/hub";
|
||||
import { BufferComponent } from "./components/item_buffer";
|
||||
|
||||
/* typehints:end */
|
||||
|
||||
/**
|
||||
@ -52,6 +54,9 @@ export class EntityComponentStorage {
|
||||
/** @type {HubComponent} */
|
||||
this.Hub;
|
||||
|
||||
/** @type {BufferComponent} */
|
||||
this.Buffer;
|
||||
|
||||
/* typehints:end */
|
||||
}
|
||||
}
|
||||
|
@ -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 { BufferSystem } from "./systems/item_buffer";
|
||||
|
||||
const logger = createLogger("game_system_manager");
|
||||
|
||||
@ -52,6 +53,9 @@ export class GameSystemManager {
|
||||
/** @type {ItemAcceptorSystem} */
|
||||
itemAcceptor: null,
|
||||
|
||||
/** @type {BufferSystem} */
|
||||
itemBuffer: null,
|
||||
|
||||
/* typehints:end */
|
||||
};
|
||||
this.systemUpdateOrder = [];
|
||||
@ -88,6 +92,8 @@ export class GameSystemManager {
|
||||
|
||||
add("itemAcceptor", ItemAcceptorSystem);
|
||||
|
||||
add("itemBuffer", BufferSystem);
|
||||
|
||||
logger.log("📦 There are", this.systemUpdateOrder.length, "game systems");
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ import { MetaSplitterBuilding } from "../../buildings/splitter";
|
||||
import { MetaStackerBuilding } from "../../buildings/stacker";
|
||||
import { MetaTrashBuilding } from "../../buildings/trash";
|
||||
import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt";
|
||||
import { MetaBufferBuilding } from "../../buildings/buffer";
|
||||
import { MetaBuilding } from "../../meta_building";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { KEYMAPPINGS } from "../../key_action_mapper";
|
||||
@ -27,6 +28,7 @@ const toolbarBuildings = [
|
||||
MetaMixerBuilding,
|
||||
MetaPainterBuilding,
|
||||
MetaTrashBuilding,
|
||||
MetaBufferBuilding,
|
||||
];
|
||||
|
||||
export class HUDBuildingsToolbar extends BaseHUDPart {
|
||||
|
@ -44,6 +44,7 @@ export const KEYMAPPINGS = {
|
||||
mixer: { keyCode: key("8") },
|
||||
painter: { keyCode: key("9") },
|
||||
trash: { keyCode: key("0") },
|
||||
buffer: { keyCode: key("-") },
|
||||
},
|
||||
|
||||
placement: {
|
||||
|
@ -10,6 +10,7 @@ import { MetaStackerBuilding } from "./buildings/stacker";
|
||||
import { MetaTrashBuilding } from "./buildings/trash";
|
||||
import { MetaUndergroundBeltBuilding } from "./buildings/underground_belt";
|
||||
import { MetaHubBuilding } from "./buildings/hub";
|
||||
import { MetaBufferBuilding } from "./buildings/buffer";
|
||||
|
||||
export function initMetaBuildingRegistry() {
|
||||
gMetaBuildingRegistry.register(MetaSplitterBuilding);
|
||||
@ -23,4 +24,5 @@ export function initMetaBuildingRegistry() {
|
||||
gMetaBuildingRegistry.register(MetaBeltBaseBuilding);
|
||||
gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding);
|
||||
gMetaBuildingRegistry.register(MetaHubBuilding);
|
||||
gMetaBuildingRegistry.register(MetaBufferBuilding);
|
||||
}
|
||||
|
57
src/js/game/systems/item_buffer.js
Normal file
57
src/js/game/systems/item_buffer.js
Normal file
@ -0,0 +1,57 @@
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { BufferComponent } from "../components/item_buffer";
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { Entity } from "../entity";
|
||||
import { formatBigNumber } from "../../core/utils";
|
||||
import { Loader } from "../../core/loader";
|
||||
|
||||
export class BufferSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
super(root, [BufferComponent]);
|
||||
|
||||
this.bufferSprite = Loader.getSprite("sprites/buildings/buffer.png");
|
||||
}
|
||||
|
||||
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;
|
||||
const bufferContents = entity.components.Buffer;
|
||||
|
||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pos = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||
|
||||
// Background
|
||||
staticComp.drawSpriteOnFullEntityBounds(parameters, this.bufferSprite, 2.2);
|
||||
|
||||
bufferContents.definition.draw(pos.x, pos.y - 5, parameters, 20);
|
||||
|
||||
const textOffsetX = 2;
|
||||
const textOffsetY = -6;
|
||||
|
||||
context.font = "bold 10px GameFont";
|
||||
context.fillStyle = "#64666e";
|
||||
context.textAlign = "left";
|
||||
context.fillText("" + formatBigNumber(bufferContents.itemCount), pos.x + textOffsetX, pos.y + textOffsetY);
|
||||
|
||||
context.font = "10px GameFont";
|
||||
context.fillStyle = "#a4a6b0";
|
||||
context.fillText(
|
||||
"/ " + formatBigNumber(bufferContents.storageLimit),
|
||||
pos.x + textOffsetX,
|
||||
pos.y + textOffsetY + 13
|
||||
);
|
||||
|
||||
context.textAlign = "left";
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
// First of all, process the current recipe
|
||||
processorComp.secondsUntilEject = Math_max(
|
||||
0,
|
||||
processorComp.secondsUntilEject - this.root.dynamicTickrate.deltaSeconds
|
||||
processorComp.secondsUntilEject - this.root.dynamicTickrate.deltaSeconds,
|
||||
);
|
||||
|
||||
// Check if we have any finished items we can eject
|
||||
@ -64,11 +64,11 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
}
|
||||
}
|
||||
|
||||
const inputs = processorComp.inputSlots.length >= processorComp.inputsPerCharge;
|
||||
const outputs = processorComp.itemsToEject.length > 0;
|
||||
// Check if we have an empty queue and can start a new charge
|
||||
if (processorComp.itemsToEject.length === 0) {
|
||||
if (processorComp.inputSlots.length >= processorComp.inputsPerCharge) {
|
||||
this.startNewCharge(entity);
|
||||
}
|
||||
if (inputs && !outputs || processorComp.chargeWhenBlocked && (inputs || !outputs)) {
|
||||
this.startNewCharge(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,7 +94,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
processorComp.secondsUntilEject = 1 / baseSpeed;
|
||||
|
||||
/** @type {Array<{item: BaseItem, requiredSlot?: number, preferredSlot?: number}>} */
|
||||
const outItems = [];
|
||||
const outItems = processorComp.itemsToEject;
|
||||
|
||||
// Whether to track the production towards the analytics
|
||||
let trackProduction = true;
|
||||
@ -196,7 +196,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
|
||||
const stackedDefinition = this.root.shapeDefinitionMgr.shapeActionStack(
|
||||
lowerItem.definition,
|
||||
upperItem.definition
|
||||
upperItem.definition,
|
||||
);
|
||||
outItems.push({
|
||||
item: new ShapeItem(stackedDefinition),
|
||||
@ -244,7 +244,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
|
||||
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem.definition,
|
||||
colorItem.color
|
||||
colorItem.color,
|
||||
);
|
||||
|
||||
outItems.push({
|
||||
@ -267,12 +267,12 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
|
||||
const colorizedDefinition1 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem1.definition,
|
||||
colorItem.color
|
||||
colorItem.color,
|
||||
);
|
||||
|
||||
const colorizedDefinition2 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem2.definition,
|
||||
colorItem.color
|
||||
colorItem.color,
|
||||
);
|
||||
outItems.push({
|
||||
item: new ShapeItem(colorizedDefinition1),
|
||||
@ -302,7 +302,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
|
||||
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith4Colors(
|
||||
shapeItem.definition,
|
||||
[colorItem2.color, colorItem3.color, colorItem4.color, colorItem1.color]
|
||||
[colorItem2.color, colorItem3.color, colorItem4.color, colorItem1.color],
|
||||
);
|
||||
|
||||
outItems.push({
|
||||
@ -328,6 +328,38 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
break;
|
||||
}
|
||||
|
||||
case enumItemProcessorTypes.buffer: {
|
||||
let buffer = entity.components.Buffer;
|
||||
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
const inputItem = /** @type {ShapeItem} */ (items[i].item);
|
||||
assert(inputItem instanceof ShapeItem, "Input is not a shape");
|
||||
// TODO: Validate item type
|
||||
|
||||
if (buffer.itemCount < buffer.storageLimit) {
|
||||
buffer.itemCount++;
|
||||
}
|
||||
|
||||
if (buffer.definition == null) {
|
||||
buffer.definition = inputItem.definition;
|
||||
}
|
||||
}
|
||||
|
||||
// const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(inputDefinition);
|
||||
|
||||
if (outItems.length === 0 && buffer.itemCount > 0) {
|
||||
buffer.itemCount--;
|
||||
outItems.push({
|
||||
item: new ShapeItem(buffer.definition),
|
||||
});
|
||||
|
||||
if (buffer.itemCount === 0) {
|
||||
buffer.definition = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assertAlways(false, "Unkown item processor type: " + processorComp.type);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user