1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-13 13:04:03 +00:00

[WIP] Add building, component, and systems for producing and

accepting user-specified items and checking goal criteria
This commit is contained in:
Greg Considine 2021-03-23 19:53:52 -04:00
parent ddab63bd2e
commit 212ea67125
33 changed files with 434 additions and 56 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -1,6 +1,6 @@
$buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, trash, underground_belt, wire,
constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor, reader, storage,
transistor, analyzer, comparator, item_producer, constant_producer;
transistor, analyzer, comparator, item_producer, constant_producer, goal_acceptor;
@each $building in $buildings {
[data-icon="building_icons/#{$building}.png"] {
@ -14,7 +14,7 @@ $buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2,
reader, rotater-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not,
logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker, item_producer,
constant_producer, virtual_processor-stacker, virtual_processor-painter, wire-second, painter,
painter-mirrored, comparator;
painter-mirrored, comparator, goal_acceptor;
@each $building in $buildingsAndVariants {
[data-icon="building_tutorials/#{$building}.png"] {
/* @load-async */

View File

@ -3,6 +3,7 @@ import { Entity } from "../entity";
/* typehints:end */
import { enumDirection, Vector } from "../../core/vector";
import { enumConstantSignalType, ConstantSignalComponent } from "../components/constant_signal";
import { ItemEjectorComponent } from "../components/item_ejector";
import { enumItemProducerType, ItemProducerComponent } from "../components/item_producer";
import { MetaBuilding } from "../meta_building";
@ -31,5 +32,10 @@ export class MetaConstantProducerBuilding extends MetaBuilding {
type: enumItemProducerType.wireless,
})
);
entity.addComponent(
new ConstantSignalComponent({
type: enumConstantSignalType.wireless,
})
);
}
}

View File

@ -0,0 +1,52 @@
/* typehints:start */
import { Entity } from "../entity";
/* typehints:end */
import { enumDirection, Vector } from "../../core/vector";
import { enumBeltReaderType, BeltReaderComponent } from "../components/belt_reader";
import { GoalAcceptorComponent } from "../components/goal_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
import { MetaBuilding } from "../meta_building";
export class MetaGoalAcceptorBuilding extends MetaBuilding {
constructor() {
super("goal_acceptor");
}
getSilhouetteColor() {
return "#ce418a";
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new ItemAcceptorComponent({
slots: [
{
pos: new Vector(0, 0),
directions: [enumDirection.top],
},
],
})
);
entity.addComponent(
new ItemProcessorComponent({
processorType: enumItemProcessorTypes.goal,
})
);
entity.addComponent(
new BeltReaderComponent({
type: enumBeltReaderType.wireless,
})
);
entity.addComponent(new GoalAcceptorComponent({}));
}
}

View File

@ -110,6 +110,6 @@ export class MetaReaderBuilding extends MetaBuilding {
})
);
entity.addComponent(new BeltReaderComponent());
entity.addComponent(new BeltReaderComponent({}));
}
}

View File

@ -19,6 +19,7 @@ import { DisplayComponent } from "./components/display";
import { BeltReaderComponent } from "./components/belt_reader";
import { FilterComponent } from "./components/filter";
import { ItemProducerComponent } from "./components/item_producer";
import { GoalAcceptorComponent } from "./components/goal_acceptor";
export function initComponentRegistry() {
gComponentRegistry.register(StaticMapEntityComponent);
@ -41,6 +42,7 @@ export function initComponentRegistry() {
gComponentRegistry.register(BeltReaderComponent);
gComponentRegistry.register(FilterComponent);
gComponentRegistry.register(ItemProducerComponent);
gComponentRegistry.register(GoalAcceptorComponent);
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS

View File

@ -3,6 +3,12 @@ import { BaseItem } from "../base_item";
import { typeItemSingleton } from "../item_resolver";
import { types } from "../../savegame/serialization";
/** @enum {string} */
export const enumBeltReaderType = {
wired: "wired",
wireless: "wireless",
};
export class BeltReaderComponent extends Component {
static getId() {
return "BeltReader";
@ -10,13 +16,20 @@ export class BeltReaderComponent extends Component {
static getSchema() {
return {
type: types.string,
lastItem: types.nullable(typeItemSingleton),
};
}
constructor() {
/**
* @param {object} param0
* @param {string=} param0.type
*/
constructor({ type = enumBeltReaderType.wired }) {
super();
this.type = type;
/**
* Which items went through the reader, we only store the time
* @type {Array<number>}
@ -41,4 +54,8 @@ export class BeltReaderComponent extends Component {
*/
this.lastThroughputComputation = 0;
}
isWireless() {
return this.type === enumBeltReaderType.wireless;
}
}

View File

@ -4,6 +4,12 @@ import { Component } from "../component";
import { BaseItem } from "../base_item";
import { typeItemSingleton } from "../item_resolver";
/** @enum {string} */
export const enumConstantSignalType = {
wired: "wired",
wireless: "wireless",
};
export class ConstantSignalComponent extends Component {
static getId() {
return "ConstantSignal";
@ -11,6 +17,7 @@ export class ConstantSignalComponent extends Component {
static getSchema() {
return {
type: types.string,
signal: types.nullable(typeItemSingleton),
};
}
@ -21,15 +28,22 @@ export class ConstantSignalComponent extends Component {
*/
copyAdditionalStateTo(otherComponent) {
otherComponent.signal = this.signal;
otherComponent.type = this.type;
}
/**
*
* @param {object} param0
* @param {string=} param0.type
* @param {BaseItem=} param0.signal The signal to store
*/
constructor({ signal = null }) {
constructor({ signal = null, type = enumConstantSignalType.wired }) {
super();
this.signal = signal;
this.type = type;
}
isWireless() {
return this.type === enumConstantSignalType.wireless;
}
}

View File

@ -0,0 +1,22 @@
import { BaseItem } from "../base_item";
import { Component } from "../component";
export class GoalAcceptorComponent extends Component {
static getId() {
return "GoalAcceptor";
}
/**
* @param {object} param0
* @param {BaseItem=} param0.item
* @param {number=} param0.rate
*/
constructor({ item = null, rate = null }) {
super();
this.item = item;
this.rate = rate;
this.achieved = false;
this.achievedOnce = false;
}
}

View File

@ -19,6 +19,7 @@ export const enumItemProcessorTypes = {
hub: "hub",
filter: "filter",
reader: "reader",
goal: "goal",
};
/** @enum {string} */
@ -104,7 +105,11 @@ export class ItemProcessorComponent extends Component {
* @param {number} sourceSlot
*/
tryTakeItem(item, sourceSlot) {
if (this.type === enumItemProcessorTypes.hub || this.type === enumItemProcessorTypes.trash) {
if (
this.type === enumItemProcessorTypes.hub ||
this.type === enumItemProcessorTypes.trash ||
this.type === enumItemProcessorTypes.goal
) {
// Hub has special logic .. not really nice but efficient.
this.inputSlots.push({ item, sourceSlot });
return true;

View File

@ -1,3 +1,4 @@
import { types } from "../../savegame/serialization";
import { Component } from "../component";
/** @enum {string} */
@ -11,12 +12,22 @@ export class ItemProducerComponent extends Component {
return "ItemProducer";
}
static getSchema() {
return {
type: types.string,
};
}
/**
* @param {object} options
* @prop {type=} options.type
* @param {object} param0
* @param {string=} param0.type
*/
constructor({ type = enumItemProducerType.wired }) {
super();
this.type = type;
}
isWireless() {
return this.type === enumItemProducerType.wireless;
}
}

View File

@ -19,6 +19,7 @@ import { DisplayComponent } from "./components/display";
import { BeltReaderComponent } from "./components/belt_reader";
import { FilterComponent } from "./components/filter";
import { ItemProducerComponent } from "./components/item_producer";
import { GoalAcceptorComponent } from "./components/goal_acceptor";
/* typehints:end */
/**
@ -89,6 +90,9 @@ export class EntityComponentStorage {
/** @type {ItemProducerComponent} */
this.ItemProducer;
/** @type {GoalAcceptorComponent} */
this.GoalAcceptor;
/* typehints:end */
}
}

View File

@ -24,6 +24,8 @@ import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays";
import { BeltReaderSystem } from "./systems/belt_reader";
import { FilterSystem } from "./systems/filter";
import { ItemProducerSystem } from "./systems/item_producer";
import { ConstantProducerSystem } from "./systems/constant_producer";
import { GoalAcceptorSystem } from "./systems/goal_acceptor";
import { ZoneSystem } from "./systems/zone";
const logger = createLogger("game_system_manager");
@ -101,6 +103,12 @@ export class GameSystemManager {
/** @type {ItemProducerSystem} */
itemProducer: null,
/** @type {ConstantProducerSystem} */
ConstantProducer: null,
/** @type {GoalAcceptorSystem} */
GoalAcceptor: null,
/** @type {ZoneSystem} */
zone: null,
@ -171,6 +179,10 @@ export class GameSystemManager {
add("itemProcessorOverlays", ItemProcessorOverlaysSystem);
add("constantProducer", ConstantProducerSystem);
add("goalAcceptor", GoalAcceptorSystem);
if (this.root.gameMode.hasZone()) {
add("zone", ZoneSystem);
}

View File

@ -500,6 +500,7 @@ export class HubGoals extends BasicSerializableObject {
switch (processorType) {
case enumItemProcessorTypes.trash:
case enumItemProcessorTypes.hub:
case enumItemProcessorTypes.goal:
return 1e30;
case enumItemProcessorTypes.balancer:
return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt * 2;

View File

@ -16,12 +16,14 @@ import { HUDBaseToolbar } from "./base_toolbar";
import { MetaStorageBuilding } from "../../buildings/storage";
import { MetaItemProducerBuilding } from "../../buildings/item_producer";
import { MetaConstantProducerBuilding } from "../../buildings/constant_producer";
import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor";
export class HUDBuildingsToolbar extends HUDBaseToolbar {
constructor(root) {
super(root, {
primaryBuildings: [
MetaConstantProducerBuilding,
MetaGoalAcceptorBuilding,
MetaBeltBuilding,
MetaBalancerBuilding,
MetaUndergroundBeltBuilding,

View File

@ -49,8 +49,9 @@ export const KEYMAPPINGS = {
},
buildings: {
// Puzzle
constant_producer: { keyCode: 192 }, // "`"
// Puzzle buildings
constant_producer: { keyCode: key("H") },
goal_acceptor: { keyCode: key("N") },
// Primary Toolbar
belt: { keyCode: key("1") },

View File

@ -76,6 +76,7 @@ export class MapChunkView extends MapChunk {
systems.lever.drawChunk(parameters, this);
systems.display.drawChunk(parameters, this);
systems.storage.drawChunk(parameters, this);
systems.constantProducer.drawChunk(parameters, this);
systems.itemProcessorOverlays.drawChunk(parameters, this);
}

View File

@ -10,6 +10,7 @@ import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter";
import { MetaDisplayBuilding } from "./buildings/display";
import { MetaFilterBuilding } from "./buildings/filter";
import { MetaGoalAcceptorBuilding } from "./buildings/goal_acceptor";
import { MetaHubBuilding } from "./buildings/hub";
import { MetaItemProducerBuilding } from "./buildings/item_producer";
import { MetaLeverBuilding } from "./buildings/lever";
@ -46,6 +47,7 @@ export function initMetaBuildingRegistry() {
gMetaBuildingRegistry.register(MetaStorageBuilding);
gMetaBuildingRegistry.register(MetaBeltBuilding);
gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding);
gMetaBuildingRegistry.register(MetaGoalAcceptorBuilding);
gMetaBuildingRegistry.register(MetaHubBuilding);
gMetaBuildingRegistry.register(MetaWireBuilding);
gMetaBuildingRegistry.register(MetaConstantSignalBuilding);
@ -170,6 +172,9 @@ export function initMetaBuildingRegistry() {
// Constant producer
registerBuildingVariant(62, MetaConstantProducerBuilding);
// Goal acceptor
registerBuildingVariant(63, MetaGoalAcceptorBuilding);
// Propagate instances
for (const key in gBuildingVariants) {
gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass(

View File

@ -117,4 +117,9 @@ export class PuzzleGameMode extends GameMode {
getMinimumZoom() {
return 1;
}
/** @returns {boolean} */
getIsFreeplayAvailable() {
return true;
}
}

View File

@ -2,7 +2,10 @@
import { GameRoot } from "../root";
/* typehints:end */
// import { MetaBeltBuilding } from "../buildings/belt";
import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor";
// import { MetaItemProducerBuilding } from "../buildings/item_producer";
import { enumGameModeIds } from "../game_mode";
import { PuzzleGameMode } from "./puzzle";
@ -17,6 +20,9 @@ export class PuzzleEditGameMode extends PuzzleGameMode {
this.setBuildings({
[MetaConstantProducerBuilding.name]: true,
// [MetaBeltBuilding.name]: true,
[MetaGoalAcceptorBuilding.name]: true,
// [MetaItemProducerBuilding.name]: true,
});
}
}

View File

@ -5,6 +5,7 @@ import { GameRoot } from "../root";
import { queryParamOptions } from "../../core/query_parameters";
import { findNiceIntegerValue } from "../../core/utils";
import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor";
import { MetaItemProducerBuilding } from "../buildings/item_producer";
import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode";
import { ShapeDefinition } from "../shape_definition";
@ -494,6 +495,7 @@ export class RegularGameMode extends GameMode {
this.setBuildings({
[MetaConstantProducerBuilding.name]: false,
[MetaGoalAcceptorBuilding.name]: false,
[MetaItemProducerBuilding.name]: queryParamOptions.sandboxMode || G_IS_DEV,
});
}

View File

@ -14,7 +14,6 @@ export class BeltReaderSystem extends GameSystemWithFilter {
const minimumTimeForThroughput = now - 1;
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const readerComp = entity.components.BeltReader;
const pinsComp = entity.components.WiredPins;
@ -23,12 +22,14 @@ export class BeltReaderSystem extends GameSystemWithFilter {
readerComp.lastItemTimes.shift();
}
pinsComp.slots[1].value = readerComp.lastItem;
pinsComp.slots[0].value =
(readerComp.lastItemTimes[readerComp.lastItemTimes.length - 1] || 0) >
minimumTimeForThroughput
? BOOL_TRUE_SINGLETON
: BOOL_FALSE_SINGLETON;
if (!entity.components.BeltReader.isWireless()) {
pinsComp.slots[1].value = readerComp.lastItem;
pinsComp.slots[0].value =
(readerComp.lastItemTimes[readerComp.lastItemTimes.length - 1] || 0) >
minimumTimeForThroughput
? BOOL_TRUE_SINGLETON
: BOOL_FALSE_SINGLETON;
}
if (now - readerComp.lastThroughputComputation > 0.5) {
// Compute throughput

View File

@ -0,0 +1,54 @@
/* typehints:start */
import { GameRoot } from "../root";
/* typehints:end */
import { globalConfig } from "../../core/config";
import { ConstantSignalComponent } from "../components/constant_signal";
import { ItemProducerComponent } from "../components/item_producer";
import { GameSystemWithFilter } from "../game_system_with_filter";
export class ConstantProducerSystem extends GameSystemWithFilter {
/** @param {GameRoot} root */
constructor(root) {
super(root, [ConstantSignalComponent, ItemProducerComponent]);
}
update() {
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const producerComp = entity.components.ItemProducer;
const signalComp = entity.components.ConstantSignal;
if (!producerComp.isWireless() || !signalComp.isWireless()) {
continue;
}
const ejectorComp = entity.components.ItemEjector;
ejectorComp.tryEject(0, signalComp.signal);
}
}
drawChunk(parameters, chunk) {
const contents = chunk.containedEntitiesByLayer.regular;
for (let i = 0; i < contents.length; ++i) {
const producerComp = contents[i].components.ItemProducer;
const signalComp = contents[i].components.ConstantSignal;
if (!producerComp || !producerComp.isWireless() || !signalComp || !signalComp.isWireless()) {
return;
}
const staticComp = contents[i].components.StaticMapEntity;
const item = signalComp.signal;
if (!item) {
return;
}
// TODO: Better looking overlay
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
item.drawItemCenteredClipped(center.x, center.y, parameters, globalConfig.tileSize);
}
}
}

View File

@ -6,9 +6,10 @@ import { fillInLinkIntoTranslation } from "../../core/utils";
import { T } from "../../translations";
import { BaseItem } from "../base_item";
import { enumColors } from "../colors";
import { ConstantSignalComponent } from "../components/constant_signal";
import { enumConstantSignalType, ConstantSignalComponent } from "../components/constant_signal";
import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter";
import { HUDPinnedShapes } from "../hud/parts/pinned_shapes";
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
import { ShapeDefinition } from "../shape_definition";
@ -26,8 +27,13 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
// Set signals
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const pinsComp = entity.components.WiredPins;
const signalComp = entity.components.ConstantSignal;
if (signalComp.isWireless()) {
continue;
}
const pinsComp = entity.components.WiredPins;
pinsComp.slots[0].value = signalComp.signal;
}
}
@ -54,23 +60,33 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
validator: val => this.parseSignalCode(val),
});
const items = [
BOOL_FALSE_SINGLETON,
BOOL_TRUE_SINGLETON,
...Object.values(COLOR_ITEM_SINGLETONS),
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(this.root.gameMode.getBlueprintShapeKey()),
];
if (this.root.gameMode.hasHub()) {
items.push(
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
this.root.hubGoals.currentGoal.definition
)
);
}
if (!this.root.gameMode.isHudPartExcluded(HUDPinnedShapes.name)) {
items.push(
...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key =>
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)
)
);
}
const itemInput = new FormElementItemChooser({
id: "signalItem",
label: null,
items: [
BOOL_FALSE_SINGLETON,
BOOL_TRUE_SINGLETON,
...Object.values(COLOR_ITEM_SINGLETONS),
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
this.root.hubGoals.currentGoal.definition
),
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(
this.root.gameMode.getBlueprintShapeKey()
),
...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key =>
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)
),
],
items,
});
const dialog = new DialogWithForm({
@ -103,7 +119,6 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
}
if (itemInput.chosenItem) {
console.log(itemInput.chosenItem);
constantComp.signal = itemInput.chosenItem;
} else {
constantComp.signal = this.parseSignalCode(signalValueInput.getValue());

View File

@ -0,0 +1,120 @@
/* typehints:start */
import { GameRoot } from "../root";
/* typehints:end */
import { THIRDPARTY_URLS, globalConfig } from "../../core/config";
import { DialogWithForm } from "../../core/modal_dialog_elements";
import { FormElementInput, FormElementItemChooser } from "../../core/modal_dialog_forms";
import { fillInLinkIntoTranslation } from "../../core/utils";
import { T } from "../../translations";
import { GoalAcceptorComponent } from "../components/goal_acceptor";
import { GameSystemWithFilter } from "../game_system_with_filter";
// import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
// import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
export class GoalAcceptorSystem extends GameSystemWithFilter {
/** @param {GameRoot} root */
constructor(root) {
super(root, [GoalAcceptorComponent]);
this.root.signals.entityManuallyPlaced.add(this.editGoal, this);
}
update() {
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const goalComp = entity.components.GoalAcceptor;
const readerComp = entity.components.BeltReader;
// Check against goals (set on placement)
}
// Check if goal criteria has been met for all goals
}
drawChunk(parameters, chunk) {
/*
*const contents = chunk.containedEntitiesByLayer.regular;
*for (let i = 0; i < contents.length; ++i) {}
*/
}
editGoal(entity) {
if (!entity.components.GoalAcceptor) {
return;
}
const uid = entity.uid;
const goalComp = entity.components.GoalAcceptor;
const itemInput = new FormElementInput({
id: "goalItemInput",
label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer),
placeholder: "CuCuCuCu",
defaultValue: "CuCuCuCu",
validator: val => this.parseItem(val),
});
const rateInput = new FormElementInput({
id: "goalRateInput",
label: "Rate:",
placeholder: "0",
defaultValue: "0",
validator: val => !isNaN(Number(val)),
});
const dialog = new DialogWithForm({
app: this.root.app,
title: "Set Goal",
desc: "",
formElements: [itemInput, rateInput],
buttons: ["cancel:bad:escape", "ok:good:enter"],
closeButton: false,
});
this.root.hud.parts.dialogs.internalShowDialog(dialog);
const closeHandler = () => {
if (this.isEntityStale(uid)) {
return;
}
goalComp.item = this.parseItem(itemInput.getValue());
goalComp.rate = this.parseRate(rateInput.getValue());
};
dialog.buttonSignals.ok.add(closeHandler);
dialog.buttonSignals.cancel.add(() => {
if (this.isEntityStale(uid)) {
return;
}
this.root.logic.tryDeleteBuilding(entity);
});
}
parseRate(value) {
return Number(value);
}
parseItem(value) {
return this.root.systemMgr.systems.constantSignal.parseSignalCode(value);
}
isEntityStale(uid) {
if (!this.root || !this.root.entityMgr) {
return true;
}
const entity = this.root.entityMgr.findByUid(uid, false);
if (!entity) {
return true;
}
const goalComp = entity.components.GoalAcceptor;
if (!goalComp) {
return true;
}
return false;
}
}

View File

@ -59,6 +59,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
[enumItemProcessorTypes.painterQuad]: this.process_PAINTER_QUAD,
[enumItemProcessorTypes.hub]: this.process_HUB,
[enumItemProcessorTypes.reader]: this.process_READER,
[enumItemProcessorTypes.goal]: this.process_GOAL,
};
// Bind all handlers
@ -562,4 +563,13 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
this.root.hubGoals.handleDefinitionDelivered(item.definition);
}
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_GOAL(payload) {
const readerComp = payload.entity.components.BeltReader;
readerComp.lastItemTimes.push(this.root.time.now());
readerComp.lastItem = payload.items[payload.items.length - 1].item;
}
}

View File

@ -2,33 +2,36 @@
import { GameRoot } from "../root";
/* typehints:end */
import { enumItemProducerType, ItemProducerComponent } from "../components/item_producer";
import { ItemProducerComponent } from "../components/item_producer";
import { GameSystemWithFilter } from "../game_system_with_filter";
export class ItemProducerSystem extends GameSystemWithFilter {
/** @param {GameRoot} root */
constructor(root) {
super(root, [ItemProducerComponent]);
this.item = null;
}
update() {
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const producerComp = entity.components.ItemProducer;
const ejectorComp = entity.components.ItemEjector;
if (entity.components.ItemProducer.type === enumItemProducerType.wired) {
const pinsComp = entity.components.WiredPins;
const pin = pinsComp.slots[0];
const network = pin.linkedNetwork;
if (!network || !network.hasValue()) {
continue;
}
const ejectorComp = entity.components.ItemEjector;
ejectorComp.tryEject(0, network.currentValue);
} else {
// TODO: entity w/ wireless item producer (e.g. ConstantProducer)
if (producerComp.isWireless()) {
continue;
}
const pinsComp = entity.components.WiredPins;
const pin = pinsComp.slots[0];
const network = pin.linkedNetwork;
if (!network || !network.hasValue()) {
continue;
}
this.item = network.currentValue;
ejectorComp.tryEject(0, this.item);
}
}
}

View File

@ -39,15 +39,16 @@ export class ZoneSystem extends GameSystem {
*/
drawChunk(parameters, chunk) {
const zone = this.root.gameMode.getZone().allScaled(globalConfig.tileSize);
const context = parameters.context;
parameters.context.globalAlpha = 0.1;
parameters.context.fillStyle = THEME.map.zone.background;
parameters.context.fillRect(zone.x, zone.y, zone.w, zone.h);
context.globalAlpha = 0.1;
context.fillStyle = THEME.map.zone.background;
context.fillRect(zone.x, zone.y, zone.w, zone.h);
parameters.context.globalAlpha = 0.9;
parameters.context.strokeStyle = THEME.map.zone.border;
parameters.context.strokeRect(zone.x, zone.y, zone.w, zone.h);
context.globalAlpha = 0.9;
context.strokeStyle = THEME.map.zone.border;
context.strokeRect(zone.x, zone.y, zone.w, zone.h);
parameters.context.globalAlpha = 1;
context.globalAlpha = 1;
}
}

View File

@ -209,7 +209,7 @@ export class MainMenuState extends GameState {
if (G_IS_DEV && globalConfig.debug.fastGameEnter) {
if (globalConfig.debug.testPuzzleMode) {
this.onPuzzlePlayButtonClicked();
this.onPuzzleEditButtonClicked();
return;
}

View File

@ -712,6 +712,11 @@ buildings:
name: &constant_producer Constant Producer
description: Outputs a shape, color or boolean (1 or 0) as specified.
goal_acceptor:
default:
name: &goal_acceptor Goal Acceptor
description: Accepts items and triggers a goal if the specified item and/or rate criteria are met.
storyRewards:
# Those are the rewards gained from completing the store
reward_cutter_and_trash:
@ -1140,6 +1145,7 @@ keybindings:
comparator: *comparator
item_producer: Item Producer (Sandbox)
constant_producer: *constant_producer
goal_acceptor: *goal_acceptor
# ---
pipette: Pipette