mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Initial attempt at couter building
This commit is contained in:
parent
2ccf39f1bf
commit
dd4de1cdea
62
src/js/game/buildings/counter.js
Normal file
62
src/js/game/buildings/counter.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { formatItemsPerSecond } from "../../core/utils";
|
||||||
|
import { enumDirection, Vector } from "../../core/vector";
|
||||||
|
import { T } from "../../translations";
|
||||||
|
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||||
|
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||||
|
import { Entity } from "../entity";
|
||||||
|
import { MetaBuilding } from "../meta_building";
|
||||||
|
import { GameRoot } from "../root";
|
||||||
|
import { enumItemType } from "../base_item";
|
||||||
|
import { ItemCounterComponent } from "../components/counter";
|
||||||
|
|
||||||
|
export class MetaCounterBuilding extends MetaBuilding {
|
||||||
|
constructor() {
|
||||||
|
super("counter");
|
||||||
|
}
|
||||||
|
|
||||||
|
getSilhouetteColor() {
|
||||||
|
return "#7dc6cd";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GameRoot} root
|
||||||
|
* @param {string} variant
|
||||||
|
* @returns {Array<[string, string]>}
|
||||||
|
*/
|
||||||
|
getAdditionalStatistics(root, variant) {
|
||||||
|
const speed = 2;
|
||||||
|
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GameRoot} root
|
||||||
|
*/
|
||||||
|
getIsUnlocked(root) {
|
||||||
|
return true; //root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotater);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the entity at the given location
|
||||||
|
* @param {Entity} entity
|
||||||
|
*/
|
||||||
|
setupEntityComponents(entity) {
|
||||||
|
entity.addComponent(new ItemCounterComponent());
|
||||||
|
|
||||||
|
entity.addComponent(
|
||||||
|
new ItemEjectorComponent({
|
||||||
|
slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
entity.addComponent(
|
||||||
|
new ItemAcceptorComponent({
|
||||||
|
slots: [
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
directions: [enumDirection.bottom],
|
||||||
|
filter: enumItemType.shape,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ import { StorageComponent } from "./components/storage";
|
|||||||
import { EnergyGeneratorComponent } from "./components/energy_generator";
|
import { EnergyGeneratorComponent } from "./components/energy_generator";
|
||||||
import { WiredPinsComponent } from "./components/wired_pins";
|
import { WiredPinsComponent } from "./components/wired_pins";
|
||||||
import { EnergyConsumerComponent } from "./components/energy_consumer";
|
import { EnergyConsumerComponent } from "./components/energy_consumer";
|
||||||
|
import { ItemCounterComponent } from "./components/counter";
|
||||||
|
|
||||||
export function initComponentRegistry() {
|
export function initComponentRegistry() {
|
||||||
gComponentRegistry.register(StaticMapEntityComponent);
|
gComponentRegistry.register(StaticMapEntityComponent);
|
||||||
@ -29,6 +30,7 @@ export function initComponentRegistry() {
|
|||||||
gComponentRegistry.register(EnergyGeneratorComponent);
|
gComponentRegistry.register(EnergyGeneratorComponent);
|
||||||
gComponentRegistry.register(WiredPinsComponent);
|
gComponentRegistry.register(WiredPinsComponent);
|
||||||
gComponentRegistry.register(EnergyConsumerComponent);
|
gComponentRegistry.register(EnergyConsumerComponent);
|
||||||
|
gComponentRegistry.register(ItemCounterComponent);
|
||||||
|
|
||||||
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS
|
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS
|
||||||
|
|
||||||
@ -37,7 +39,7 @@ export function initComponentRegistry() {
|
|||||||
assert(
|
assert(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
require.context("./components", false, /.*\.js/i).keys().length ===
|
require.context("./components", false, /.*\.js/i).keys().length ===
|
||||||
gComponentRegistry.getNumEntries(),
|
gComponentRegistry.getNumEntries(),
|
||||||
"Not all components are registered"
|
"Not all components are registered"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
98
src/js/game/components/counter.js
Normal file
98
src/js/game/components/counter.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { types } from "../../savegame/serialization";
|
||||||
|
import { Component } from "../component";
|
||||||
|
import { BaseItem } from "../base_item";
|
||||||
|
import { gItemRegistry } from "../../core/global_registries";
|
||||||
|
import { GameTime } from "../time/game_time";
|
||||||
|
|
||||||
|
export class ItemCounterComponent extends Component {
|
||||||
|
static getId() {
|
||||||
|
return "Counter";
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSchema() {
|
||||||
|
return {
|
||||||
|
inputSlots: types.array(
|
||||||
|
types.structured({
|
||||||
|
item: types.obj(gItemRegistry),
|
||||||
|
sourceSlot: types.uint,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicateWithoutContents() {
|
||||||
|
return new ItemCounterComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our current inputs
|
||||||
|
* @type {Array<{ item: BaseItem, sourceSlot: number }>}
|
||||||
|
*/
|
||||||
|
this.inputSlots = [];
|
||||||
|
|
||||||
|
this.currentCount = 0;
|
||||||
|
|
||||||
|
this.lastResetTime = 0;
|
||||||
|
|
||||||
|
/** @typedef {object} TickCount
|
||||||
|
* @property {number} gameTimeSeconds
|
||||||
|
* @property {number} count
|
||||||
|
*/
|
||||||
|
/** @type {TickCount[]} */
|
||||||
|
this.tickHistory = [];
|
||||||
|
|
||||||
|
this.averageItemsPerSecond = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called every time an item leaves the counter
|
||||||
|
*/
|
||||||
|
countNewItem() {
|
||||||
|
this.currentCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on every counter entity .update() call
|
||||||
|
* @param {GameTime} gameTime
|
||||||
|
*/
|
||||||
|
tick(gameTime) {
|
||||||
|
const count = this.currentCount;
|
||||||
|
this.currentCount = 0;
|
||||||
|
|
||||||
|
this.tickHistory.push({
|
||||||
|
gameTimeSeconds: gameTime.timeSeconds,
|
||||||
|
count: count,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only keep history for the last second.
|
||||||
|
this.tickHistory = this.tickHistory.filter(tick => gameTime.timeSeconds - tick.gameTimeSeconds <= 1);
|
||||||
|
|
||||||
|
const delta = gameTime.timeSeconds - this.lastResetTime;
|
||||||
|
if (delta > 1) {
|
||||||
|
const sum = this.tickHistory.reduce((a, b) => a + b.count, 0);
|
||||||
|
this.averageItemsPerSecond = sum;
|
||||||
|
this.lastResetTime = gameTime.timeSeconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to take the item
|
||||||
|
* @param {BaseItem} item
|
||||||
|
* @param {number} sourceSlot
|
||||||
|
*/
|
||||||
|
tryTakeItem(item, sourceSlot) {
|
||||||
|
// Check that we only take one item per slot
|
||||||
|
for (let i = 0; i < this.inputSlots.length; ++i) {
|
||||||
|
const slot = this.inputSlots[i];
|
||||||
|
if (slot.sourceSlot === sourceSlot) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inputSlots.push({ item, sourceSlot });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -423,6 +423,8 @@ export class GameCore {
|
|||||||
|
|
||||||
// Energy consumer (Battery icons)
|
// Energy consumer (Battery icons)
|
||||||
systems.energyConsumer.draw(params);
|
systems.energyConsumer.draw(params);
|
||||||
|
|
||||||
|
systems.counter.draw(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Green wires overlay (not within the if because it can fade)
|
// Green wires overlay (not within the if because it can fade)
|
||||||
|
@ -13,6 +13,7 @@ import { StorageComponent } from "./components/storage";
|
|||||||
import { EnergyGeneratorComponent } from "./components/energy_generator";
|
import { EnergyGeneratorComponent } from "./components/energy_generator";
|
||||||
import { WiredPinsComponent } from "./components/wired_pins";
|
import { WiredPinsComponent } from "./components/wired_pins";
|
||||||
import { EnergyConsumerComponent } from "./components/energy_consumer";
|
import { EnergyConsumerComponent } from "./components/energy_consumer";
|
||||||
|
import { ItemCounterComponent } from "./components/counter";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,6 +66,9 @@ export class EntityComponentStorage {
|
|||||||
/** @type {EnergyConsumerComponent} */
|
/** @type {EnergyConsumerComponent} */
|
||||||
this.EnergyConsumer;
|
this.EnergyConsumer;
|
||||||
|
|
||||||
|
/** @type {ItemCounterComponent} */
|
||||||
|
this.Counter;
|
||||||
|
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import { StorageSystem } from "./systems/storage";
|
|||||||
import { EnergyGeneratorSystem } from "./systems/energy_generator";
|
import { EnergyGeneratorSystem } from "./systems/energy_generator";
|
||||||
import { WiredPinsSystem } from "./systems/wired_pins";
|
import { WiredPinsSystem } from "./systems/wired_pins";
|
||||||
import { EnergyConsumerSystem } from "./systems/energy_consumer";
|
import { EnergyConsumerSystem } from "./systems/energy_consumer";
|
||||||
|
import { CounterSystem } from "./systems/counter";
|
||||||
|
|
||||||
const logger = createLogger("game_system_manager");
|
const logger = createLogger("game_system_manager");
|
||||||
|
|
||||||
@ -68,6 +69,9 @@ export class GameSystemManager {
|
|||||||
/** @type {EnergyConsumerSystem} */
|
/** @type {EnergyConsumerSystem} */
|
||||||
energyConsumer: null,
|
energyConsumer: null,
|
||||||
|
|
||||||
|
/** @type {CounterSystem} */
|
||||||
|
counter: null,
|
||||||
|
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
};
|
};
|
||||||
this.systemUpdateOrder = [];
|
this.systemUpdateOrder = [];
|
||||||
@ -110,6 +114,8 @@ export class GameSystemManager {
|
|||||||
|
|
||||||
add("energyConsumer", EnergyConsumerSystem);
|
add("energyConsumer", EnergyConsumerSystem);
|
||||||
|
|
||||||
|
add("counter", CounterSystem);
|
||||||
|
|
||||||
// IMPORTANT: Must be after belt system since belt system can change the
|
// IMPORTANT: Must be after belt system since belt system can change the
|
||||||
// orientation of an entity after it is placed -> the item acceptor cache
|
// orientation of an entity after it is placed -> the item acceptor cache
|
||||||
// then would be invalid
|
// then would be invalid
|
||||||
|
@ -4,6 +4,7 @@ import { MetaEnergyGenerator } from "../../buildings/energy_generator";
|
|||||||
import { MetaMinerBuilding } from "../../buildings/miner";
|
import { MetaMinerBuilding } from "../../buildings/miner";
|
||||||
import { MetaMixerBuilding } from "../../buildings/mixer";
|
import { MetaMixerBuilding } from "../../buildings/mixer";
|
||||||
import { MetaPainterBuilding } from "../../buildings/painter";
|
import { MetaPainterBuilding } from "../../buildings/painter";
|
||||||
|
import { MetaCounterBuilding } from "../../buildings/counter";
|
||||||
import { MetaRotaterBuilding } from "../../buildings/rotater";
|
import { MetaRotaterBuilding } from "../../buildings/rotater";
|
||||||
import { MetaSplitterBuilding } from "../../buildings/splitter";
|
import { MetaSplitterBuilding } from "../../buildings/splitter";
|
||||||
import { MetaStackerBuilding } from "../../buildings/stacker";
|
import { MetaStackerBuilding } from "../../buildings/stacker";
|
||||||
@ -19,6 +20,7 @@ const supportedBuildings = [
|
|||||||
MetaUndergroundBeltBuilding,
|
MetaUndergroundBeltBuilding,
|
||||||
MetaMinerBuilding,
|
MetaMinerBuilding,
|
||||||
MetaCutterBuilding,
|
MetaCutterBuilding,
|
||||||
|
MetaCounterBuilding,
|
||||||
MetaRotaterBuilding,
|
MetaRotaterBuilding,
|
||||||
MetaStackerBuilding,
|
MetaStackerBuilding,
|
||||||
MetaMixerBuilding,
|
MetaMixerBuilding,
|
||||||
|
@ -49,6 +49,7 @@ export const KEYMAPPINGS = {
|
|||||||
underground_belt: { keyCode: key("3") },
|
underground_belt: { keyCode: key("3") },
|
||||||
miner: { keyCode: key("4") },
|
miner: { keyCode: key("4") },
|
||||||
cutter: { keyCode: key("5") },
|
cutter: { keyCode: key("5") },
|
||||||
|
counter: { keyCode: key("~") },
|
||||||
rotater: { keyCode: key("6") },
|
rotater: { keyCode: key("6") },
|
||||||
stacker: { keyCode: key("7") },
|
stacker: { keyCode: key("7") },
|
||||||
mixer: { keyCode: key("8") },
|
mixer: { keyCode: key("8") },
|
||||||
|
@ -4,6 +4,7 @@ import { MetaCutterBuilding } from "./buildings/cutter";
|
|||||||
import { MetaMinerBuilding } from "./buildings/miner";
|
import { MetaMinerBuilding } from "./buildings/miner";
|
||||||
import { MetaMixerBuilding } from "./buildings/mixer";
|
import { MetaMixerBuilding } from "./buildings/mixer";
|
||||||
import { MetaPainterBuilding } from "./buildings/painter";
|
import { MetaPainterBuilding } from "./buildings/painter";
|
||||||
|
import { MetaCounterBuilding } from "./buildings/counter";
|
||||||
import { MetaRotaterBuilding } from "./buildings/rotater";
|
import { MetaRotaterBuilding } from "./buildings/rotater";
|
||||||
import { MetaSplitterBuilding } from "./buildings/splitter";
|
import { MetaSplitterBuilding } from "./buildings/splitter";
|
||||||
import { MetaStackerBuilding } from "./buildings/stacker";
|
import { MetaStackerBuilding } from "./buildings/stacker";
|
||||||
@ -20,6 +21,7 @@ export function initMetaBuildingRegistry() {
|
|||||||
gMetaBuildingRegistry.register(MetaSplitterBuilding);
|
gMetaBuildingRegistry.register(MetaSplitterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaMinerBuilding);
|
gMetaBuildingRegistry.register(MetaMinerBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaCutterBuilding);
|
gMetaBuildingRegistry.register(MetaCutterBuilding);
|
||||||
|
gMetaBuildingRegistry.register(MetaCounterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaRotaterBuilding);
|
gMetaBuildingRegistry.register(MetaRotaterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaStackerBuilding);
|
gMetaBuildingRegistry.register(MetaStackerBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaMixerBuilding);
|
gMetaBuildingRegistry.register(MetaMixerBuilding);
|
||||||
|
77
src/js/game/systems/counter.js
Normal file
77
src/js/game/systems/counter.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
|
import { ItemCounterComponent } from "../components/counter";
|
||||||
|
import { Entity } from "../entity";
|
||||||
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
|
import { ShapeItem } from "../items/shape_item";
|
||||||
|
import { Loader } from "../../core/loader";
|
||||||
|
|
||||||
|
export class CounterSystem extends GameSystemWithFilter {
|
||||||
|
constructor(root) {
|
||||||
|
super(root, [ItemCounterComponent]);
|
||||||
|
|
||||||
|
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 counterComp = entity.components.Counter;
|
||||||
|
const ejectorComp = entity.components.ItemEjector;
|
||||||
|
|
||||||
|
const items = counterComp.inputSlots;
|
||||||
|
|
||||||
|
let outItem = null;
|
||||||
|
|
||||||
|
if (items.length > 0) {
|
||||||
|
const inputItem = /** @type {ShapeItem} */ (items[0].item);
|
||||||
|
assert(inputItem instanceof ShapeItem, "Input for counting is not a shape");
|
||||||
|
|
||||||
|
outItem = inputItem;
|
||||||
|
let slot = ejectorComp.getFirstFreeSlot(entity.layer);
|
||||||
|
|
||||||
|
if (slot !== null) {
|
||||||
|
if (!ejectorComp.tryEject(slot, outItem)) {
|
||||||
|
assert(false, "Failed to eject");
|
||||||
|
} else {
|
||||||
|
counterComp.countNewItem();
|
||||||
|
counterComp.inputSlots = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
counterComp.tick(this.root.time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 counterComp = entity.components.Counter;
|
||||||
|
|
||||||
|
context.globalAlpha = 1;
|
||||||
|
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||||
|
|
||||||
|
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(counterComp.averageItemsPerSecond, center.x, center.y + 25.5);
|
||||||
|
|
||||||
|
context.textAlign = "left";
|
||||||
|
context.globalAlpha = 1;
|
||||||
|
}
|
||||||
|
}
|
@ -219,9 +219,9 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
sourceSlot.progress = Math.min(
|
sourceSlot.progress = Math.min(
|
||||||
1,
|
1,
|
||||||
sourceSlot.progress +
|
sourceSlot.progress +
|
||||||
progressGrowth *
|
progressGrowth *
|
||||||
this.root.hubGoals.getBeltBaseSpeed(sourceSlot.layer) *
|
this.root.hubGoals.getBeltBaseSpeed(sourceSlot.layer) *
|
||||||
globalConfig.beltItemSpacingByLayer[sourceSlot.layer]
|
globalConfig.beltItemSpacingByLayer[sourceSlot.layer]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check if we are still in the process of ejecting, can't proceed then
|
// Check if we are still in the process of ejecting, can't proceed then
|
||||||
@ -332,6 +332,16 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const counterComp = receiver.components.Counter;
|
||||||
|
if (counterComp) {
|
||||||
|
if (counterComp.tryTakeItem(item, slotIndex)) {
|
||||||
|
// Passed it over
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,6 +504,11 @@ buildings:
|
|||||||
name: Rotate (CCW)
|
name: Rotate (CCW)
|
||||||
description: Rotates shapes counter-clockwise by 90 degrees.
|
description: Rotates shapes counter-clockwise by 90 degrees.
|
||||||
|
|
||||||
|
counter:
|
||||||
|
default:
|
||||||
|
name: &counter Counter
|
||||||
|
description: COUNTER
|
||||||
|
|
||||||
stacker:
|
stacker:
|
||||||
default:
|
default:
|
||||||
name: &stacker Stacker
|
name: &stacker Stacker
|
||||||
@ -818,6 +823,7 @@ keybindings:
|
|||||||
miner: *miner
|
miner: *miner
|
||||||
cutter: *cutter
|
cutter: *cutter
|
||||||
advanced_processor: *advanced_processor
|
advanced_processor: *advanced_processor
|
||||||
|
counter: *counter
|
||||||
rotater: *rotater
|
rotater: *rotater
|
||||||
stacker: *stacker
|
stacker: *stacker
|
||||||
mixer: *mixer
|
mixer: *mixer
|
||||||
|
Loading…
Reference in New Issue
Block a user