1
0
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:
Jasper Meggitt 2020-05-19 16:28:46 -07:00
parent 1a6b64f914
commit 02ad9e3061
14 changed files with 172 additions and 13 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -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({

View File

@ -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

View 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;
}
}

View File

@ -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 }>}

View File

@ -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 */
}
}

View File

@ -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");
}

View File

@ -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 {

View File

@ -44,6 +44,7 @@ export const KEYMAPPINGS = {
mixer: { keyCode: key("8") },
painter: { keyCode: key("9") },
trash: { keyCode: key("0") },
buffer: { keyCode: key("-") },
},
placement: {

View File

@ -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);
}

View 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";
}
}

View File

@ -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,14 +64,14 @@ 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) {
if (inputs && !outputs || processorComp.chargeWhenBlocked && (inputs || !outputs)) {
this.startNewCharge(entity);
}
}
}
}
/**
* Starts a new charge for the 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);
}