mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Merge pull request #7 from Moppler/feature/counter-building
Pull original counter code
This commit is contained in:
commit
f02fe91bed
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 { WiredPinsComponent } from "./components/wired_pins";
|
||||
import { EnergyConsumerComponent } from "./components/energy_consumer";
|
||||
import { ItemCounterComponent } from "./components/counter";
|
||||
|
||||
export function initComponentRegistry() {
|
||||
gComponentRegistry.register(StaticMapEntityComponent);
|
||||
@ -29,6 +30,7 @@ export function initComponentRegistry() {
|
||||
gComponentRegistry.register(EnergyGeneratorComponent);
|
||||
gComponentRegistry.register(WiredPinsComponent);
|
||||
gComponentRegistry.register(EnergyConsumerComponent);
|
||||
gComponentRegistry.register(ItemCounterComponent);
|
||||
|
||||
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS
|
||||
|
||||
|
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)
|
||||
systems.energyConsumer.draw(params);
|
||||
|
||||
systems.counter.draw(params);
|
||||
}
|
||||
|
||||
// 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 { WiredPinsComponent } from "./components/wired_pins";
|
||||
import { EnergyConsumerComponent } from "./components/energy_consumer";
|
||||
import { ItemCounterComponent } from "./components/counter";
|
||||
/* typehints:end */
|
||||
|
||||
/**
|
||||
@ -65,6 +66,9 @@ export class EntityComponentStorage {
|
||||
/** @type {EnergyConsumerComponent} */
|
||||
this.EnergyConsumer;
|
||||
|
||||
/** @type {ItemCounterComponent} */
|
||||
this.Counter;
|
||||
|
||||
/* typehints:end */
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import { StorageSystem } from "./systems/storage";
|
||||
import { EnergyGeneratorSystem } from "./systems/energy_generator";
|
||||
import { WiredPinsSystem } from "./systems/wired_pins";
|
||||
import { EnergyConsumerSystem } from "./systems/energy_consumer";
|
||||
import { CounterSystem } from "./systems/counter";
|
||||
|
||||
const logger = createLogger("game_system_manager");
|
||||
|
||||
@ -68,6 +69,9 @@ export class GameSystemManager {
|
||||
/** @type {EnergyConsumerSystem} */
|
||||
energyConsumer: null,
|
||||
|
||||
/** @type {CounterSystem} */
|
||||
counter: null,
|
||||
|
||||
/* typehints:end */
|
||||
};
|
||||
this.systemUpdateOrder = [];
|
||||
@ -110,6 +114,8 @@ export class GameSystemManager {
|
||||
|
||||
add("energyConsumer", EnergyConsumerSystem);
|
||||
|
||||
add("counter", CounterSystem);
|
||||
|
||||
// IMPORTANT: Must be after belt system since belt system can change the
|
||||
// orientation of an entity after it is placed -> the item acceptor cache
|
||||
// then would be invalid
|
||||
|
@ -4,6 +4,7 @@ import { MetaEnergyGenerator } from "../../buildings/energy_generator";
|
||||
import { MetaMinerBuilding } from "../../buildings/miner";
|
||||
import { MetaMixerBuilding } from "../../buildings/mixer";
|
||||
import { MetaPainterBuilding } from "../../buildings/painter";
|
||||
import { MetaCounterBuilding } from "../../buildings/counter";
|
||||
import { MetaRotaterBuilding } from "../../buildings/rotater";
|
||||
import { MetaSplitterBuilding } from "../../buildings/splitter";
|
||||
import { MetaStackerBuilding } from "../../buildings/stacker";
|
||||
@ -19,6 +20,7 @@ const supportedBuildings = [
|
||||
MetaUndergroundBeltBuilding,
|
||||
MetaMinerBuilding,
|
||||
MetaCutterBuilding,
|
||||
MetaCounterBuilding,
|
||||
MetaRotaterBuilding,
|
||||
MetaStackerBuilding,
|
||||
MetaMixerBuilding,
|
||||
|
@ -49,6 +49,7 @@ export const KEYMAPPINGS = {
|
||||
underground_belt: { keyCode: key("3") },
|
||||
miner: { keyCode: key("4") },
|
||||
cutter: { keyCode: key("5") },
|
||||
counter: { keyCode: key("~") },
|
||||
rotater: { keyCode: key("6") },
|
||||
stacker: { keyCode: key("7") },
|
||||
mixer: { keyCode: key("8") },
|
||||
|
@ -4,6 +4,7 @@ import { MetaCutterBuilding } from "./buildings/cutter";
|
||||
import { MetaMinerBuilding } from "./buildings/miner";
|
||||
import { MetaMixerBuilding } from "./buildings/mixer";
|
||||
import { MetaPainterBuilding } from "./buildings/painter";
|
||||
import { MetaCounterBuilding } from "./buildings/counter";
|
||||
import { MetaRotaterBuilding } from "./buildings/rotater";
|
||||
import { MetaSplitterBuilding } from "./buildings/splitter";
|
||||
import { MetaStackerBuilding } from "./buildings/stacker";
|
||||
@ -20,6 +21,7 @@ export function initMetaBuildingRegistry() {
|
||||
gMetaBuildingRegistry.register(MetaSplitterBuilding);
|
||||
gMetaBuildingRegistry.register(MetaMinerBuilding);
|
||||
gMetaBuildingRegistry.register(MetaCutterBuilding);
|
||||
gMetaBuildingRegistry.register(MetaCounterBuilding);
|
||||
gMetaBuildingRegistry.register(MetaRotaterBuilding);
|
||||
gMetaBuildingRegistry.register(MetaStackerBuilding);
|
||||
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;
|
||||
}
|
||||
}
|
@ -332,6 +332,16 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
return false;
|
||||
}
|
||||
|
||||
const counterComp = receiver.components.Counter;
|
||||
if (counterComp) {
|
||||
if (counterComp.tryTakeItem(item, slotIndex)) {
|
||||
// Passed it over
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -504,6 +504,11 @@ buildings:
|
||||
name: Rotate (CCW)
|
||||
description: Rotates shapes counter-clockwise by 90 degrees.
|
||||
|
||||
counter:
|
||||
default:
|
||||
name: &counter Counter
|
||||
description: COUNTER
|
||||
|
||||
stacker:
|
||||
default:
|
||||
name: &stacker Stacker
|
||||
@ -818,6 +823,7 @@ keybindings:
|
||||
miner: *miner
|
||||
cutter: *cutter
|
||||
advanced_processor: *advanced_processor
|
||||
counter: *counter
|
||||
rotater: *rotater
|
||||
stacker: *stacker
|
||||
mixer: *mixer
|
||||
|
Loading…
Reference in New Issue
Block a user