1
0
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:
MrYawnie 2020-07-12 00:59:15 +10:00 committed by GitHub
commit f02fe91bed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 276 additions and 4 deletions

View 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,
},
],
})
);
}
}

View File

@ -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
@ -37,7 +39,7 @@ export function initComponentRegistry() {
assert(
// @ts-ignore
require.context("./components", false, /.*\.js/i).keys().length ===
gComponentRegistry.getNumEntries(),
gComponentRegistry.getNumEntries(),
"Not all components are registered"
);

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@ -219,9 +219,9 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
sourceSlot.progress = Math.min(
1,
sourceSlot.progress +
progressGrowth *
this.root.hubGoals.getBeltBaseSpeed(sourceSlot.layer) *
globalConfig.beltItemSpacingByLayer[sourceSlot.layer]
progressGrowth *
this.root.hubGoals.getBeltBaseSpeed(sourceSlot.layer) *
globalConfig.beltItemSpacingByLayer[sourceSlot.layer]
);
// 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;
}
const counterComp = receiver.components.Counter;
if (counterComp) {
if (counterComp.tryTakeItem(item, slotIndex)) {
// Passed it over
return true;
}
return false;
}
return false;
}

View File

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