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, $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, 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 { @each $building in $buildings {
[data-icon="building_icons/#{$building}.png"] { [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, 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, logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker, item_producer,
constant_producer, virtual_processor-stacker, virtual_processor-painter, wire-second, painter, constant_producer, virtual_processor-stacker, virtual_processor-painter, wire-second, painter,
painter-mirrored, comparator; painter-mirrored, comparator, goal_acceptor;
@each $building in $buildingsAndVariants { @each $building in $buildingsAndVariants {
[data-icon="building_tutorials/#{$building}.png"] { [data-icon="building_tutorials/#{$building}.png"] {
/* @load-async */ /* @load-async */

View File

@ -3,6 +3,7 @@ import { Entity } from "../entity";
/* typehints:end */ /* typehints:end */
import { enumDirection, Vector } from "../../core/vector"; import { enumDirection, Vector } from "../../core/vector";
import { enumConstantSignalType, ConstantSignalComponent } from "../components/constant_signal";
import { ItemEjectorComponent } from "../components/item_ejector"; import { ItemEjectorComponent } from "../components/item_ejector";
import { enumItemProducerType, ItemProducerComponent } from "../components/item_producer"; import { enumItemProducerType, ItemProducerComponent } from "../components/item_producer";
import { MetaBuilding } from "../meta_building"; import { MetaBuilding } from "../meta_building";
@ -31,5 +32,10 @@ export class MetaConstantProducerBuilding extends MetaBuilding {
type: enumItemProducerType.wireless, 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 { BeltReaderComponent } from "./components/belt_reader";
import { FilterComponent } from "./components/filter"; import { FilterComponent } from "./components/filter";
import { ItemProducerComponent } from "./components/item_producer"; import { ItemProducerComponent } from "./components/item_producer";
import { GoalAcceptorComponent } from "./components/goal_acceptor";
export function initComponentRegistry() { export function initComponentRegistry() {
gComponentRegistry.register(StaticMapEntityComponent); gComponentRegistry.register(StaticMapEntityComponent);
@ -41,6 +42,7 @@ export function initComponentRegistry() {
gComponentRegistry.register(BeltReaderComponent); gComponentRegistry.register(BeltReaderComponent);
gComponentRegistry.register(FilterComponent); gComponentRegistry.register(FilterComponent);
gComponentRegistry.register(ItemProducerComponent); gComponentRegistry.register(ItemProducerComponent);
gComponentRegistry.register(GoalAcceptorComponent);
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS // IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS

View File

@ -3,6 +3,12 @@ import { BaseItem } from "../base_item";
import { typeItemSingleton } from "../item_resolver"; import { typeItemSingleton } from "../item_resolver";
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
/** @enum {string} */
export const enumBeltReaderType = {
wired: "wired",
wireless: "wireless",
};
export class BeltReaderComponent extends Component { export class BeltReaderComponent extends Component {
static getId() { static getId() {
return "BeltReader"; return "BeltReader";
@ -10,13 +16,20 @@ export class BeltReaderComponent extends Component {
static getSchema() { static getSchema() {
return { return {
type: types.string,
lastItem: types.nullable(typeItemSingleton), lastItem: types.nullable(typeItemSingleton),
}; };
} }
constructor() { /**
* @param {object} param0
* @param {string=} param0.type
*/
constructor({ type = enumBeltReaderType.wired }) {
super(); super();
this.type = type;
/** /**
* Which items went through the reader, we only store the time * Which items went through the reader, we only store the time
* @type {Array<number>} * @type {Array<number>}
@ -41,4 +54,8 @@ export class BeltReaderComponent extends Component {
*/ */
this.lastThroughputComputation = 0; 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 { BaseItem } from "../base_item";
import { typeItemSingleton } from "../item_resolver"; import { typeItemSingleton } from "../item_resolver";
/** @enum {string} */
export const enumConstantSignalType = {
wired: "wired",
wireless: "wireless",
};
export class ConstantSignalComponent extends Component { export class ConstantSignalComponent extends Component {
static getId() { static getId() {
return "ConstantSignal"; return "ConstantSignal";
@ -11,6 +17,7 @@ export class ConstantSignalComponent extends Component {
static getSchema() { static getSchema() {
return { return {
type: types.string,
signal: types.nullable(typeItemSingleton), signal: types.nullable(typeItemSingleton),
}; };
} }
@ -21,15 +28,22 @@ export class ConstantSignalComponent extends Component {
*/ */
copyAdditionalStateTo(otherComponent) { copyAdditionalStateTo(otherComponent) {
otherComponent.signal = this.signal; otherComponent.signal = this.signal;
otherComponent.type = this.type;
} }
/** /**
* *
* @param {object} param0 * @param {object} param0
* @param {string=} param0.type
* @param {BaseItem=} param0.signal The signal to store * @param {BaseItem=} param0.signal The signal to store
*/ */
constructor({ signal = null }) { constructor({ signal = null, type = enumConstantSignalType.wired }) {
super(); super();
this.signal = signal; 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", hub: "hub",
filter: "filter", filter: "filter",
reader: "reader", reader: "reader",
goal: "goal",
}; };
/** @enum {string} */ /** @enum {string} */
@ -104,7 +105,11 @@ export class ItemProcessorComponent extends Component {
* @param {number} sourceSlot * @param {number} sourceSlot
*/ */
tryTakeItem(item, 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. // Hub has special logic .. not really nice but efficient.
this.inputSlots.push({ item, sourceSlot }); this.inputSlots.push({ item, sourceSlot });
return true; return true;

View File

@ -1,3 +1,4 @@
import { types } from "../../savegame/serialization";
import { Component } from "../component"; import { Component } from "../component";
/** @enum {string} */ /** @enum {string} */
@ -11,12 +12,22 @@ export class ItemProducerComponent extends Component {
return "ItemProducer"; return "ItemProducer";
} }
static getSchema() {
return {
type: types.string,
};
}
/** /**
* @param {object} options * @param {object} param0
* @prop {type=} options.type * @param {string=} param0.type
*/ */
constructor({ type = enumItemProducerType.wired }) { constructor({ type = enumItemProducerType.wired }) {
super(); super();
this.type = type; 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 { BeltReaderComponent } from "./components/belt_reader";
import { FilterComponent } from "./components/filter"; import { FilterComponent } from "./components/filter";
import { ItemProducerComponent } from "./components/item_producer"; import { ItemProducerComponent } from "./components/item_producer";
import { GoalAcceptorComponent } from "./components/goal_acceptor";
/* typehints:end */ /* typehints:end */
/** /**
@ -89,6 +90,9 @@ export class EntityComponentStorage {
/** @type {ItemProducerComponent} */ /** @type {ItemProducerComponent} */
this.ItemProducer; this.ItemProducer;
/** @type {GoalAcceptorComponent} */
this.GoalAcceptor;
/* typehints:end */ /* typehints:end */
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -76,6 +76,7 @@ export class MapChunkView extends MapChunk {
systems.lever.drawChunk(parameters, this); systems.lever.drawChunk(parameters, this);
systems.display.drawChunk(parameters, this); systems.display.drawChunk(parameters, this);
systems.storage.drawChunk(parameters, this); systems.storage.drawChunk(parameters, this);
systems.constantProducer.drawChunk(parameters, this);
systems.itemProcessorOverlays.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 { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter";
import { MetaDisplayBuilding } from "./buildings/display"; import { MetaDisplayBuilding } from "./buildings/display";
import { MetaFilterBuilding } from "./buildings/filter"; import { MetaFilterBuilding } from "./buildings/filter";
import { MetaGoalAcceptorBuilding } from "./buildings/goal_acceptor";
import { MetaHubBuilding } from "./buildings/hub"; import { MetaHubBuilding } from "./buildings/hub";
import { MetaItemProducerBuilding } from "./buildings/item_producer"; import { MetaItemProducerBuilding } from "./buildings/item_producer";
import { MetaLeverBuilding } from "./buildings/lever"; import { MetaLeverBuilding } from "./buildings/lever";
@ -46,6 +47,7 @@ export function initMetaBuildingRegistry() {
gMetaBuildingRegistry.register(MetaStorageBuilding); gMetaBuildingRegistry.register(MetaStorageBuilding);
gMetaBuildingRegistry.register(MetaBeltBuilding); gMetaBuildingRegistry.register(MetaBeltBuilding);
gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding); gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding);
gMetaBuildingRegistry.register(MetaGoalAcceptorBuilding);
gMetaBuildingRegistry.register(MetaHubBuilding); gMetaBuildingRegistry.register(MetaHubBuilding);
gMetaBuildingRegistry.register(MetaWireBuilding); gMetaBuildingRegistry.register(MetaWireBuilding);
gMetaBuildingRegistry.register(MetaConstantSignalBuilding); gMetaBuildingRegistry.register(MetaConstantSignalBuilding);
@ -170,6 +172,9 @@ export function initMetaBuildingRegistry() {
// Constant producer // Constant producer
registerBuildingVariant(62, MetaConstantProducerBuilding); registerBuildingVariant(62, MetaConstantProducerBuilding);
// Goal acceptor
registerBuildingVariant(63, MetaGoalAcceptorBuilding);
// Propagate instances // Propagate instances
for (const key in gBuildingVariants) { for (const key in gBuildingVariants) {
gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass( gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass(

View File

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

View File

@ -2,7 +2,10 @@
import { GameRoot } from "../root"; import { GameRoot } from "../root";
/* typehints:end */ /* typehints:end */
// import { MetaBeltBuilding } from "../buildings/belt";
import { MetaConstantProducerBuilding } from "../buildings/constant_producer"; import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor";
// import { MetaItemProducerBuilding } from "../buildings/item_producer";
import { enumGameModeIds } from "../game_mode"; import { enumGameModeIds } from "../game_mode";
import { PuzzleGameMode } from "./puzzle"; import { PuzzleGameMode } from "./puzzle";
@ -17,6 +20,9 @@ export class PuzzleEditGameMode extends PuzzleGameMode {
this.setBuildings({ this.setBuildings({
[MetaConstantProducerBuilding.name]: true, [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 { queryParamOptions } from "../../core/query_parameters";
import { findNiceIntegerValue } from "../../core/utils"; import { findNiceIntegerValue } from "../../core/utils";
import { MetaConstantProducerBuilding } from "../buildings/constant_producer"; import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor";
import { MetaItemProducerBuilding } from "../buildings/item_producer"; import { MetaItemProducerBuilding } from "../buildings/item_producer";
import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode"; import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode";
import { ShapeDefinition } from "../shape_definition"; import { ShapeDefinition } from "../shape_definition";
@ -494,6 +495,7 @@ export class RegularGameMode extends GameMode {
this.setBuildings({ this.setBuildings({
[MetaConstantProducerBuilding.name]: false, [MetaConstantProducerBuilding.name]: false,
[MetaGoalAcceptorBuilding.name]: false,
[MetaItemProducerBuilding.name]: queryParamOptions.sandboxMode || G_IS_DEV, [MetaItemProducerBuilding.name]: queryParamOptions.sandboxMode || G_IS_DEV,
}); });
} }

View File

@ -14,7 +14,6 @@ export class BeltReaderSystem extends GameSystemWithFilter {
const minimumTimeForThroughput = now - 1; const minimumTimeForThroughput = now - 1;
for (let i = 0; i < this.allEntities.length; ++i) { for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i]; const entity = this.allEntities[i];
const readerComp = entity.components.BeltReader; const readerComp = entity.components.BeltReader;
const pinsComp = entity.components.WiredPins; const pinsComp = entity.components.WiredPins;
@ -23,12 +22,14 @@ export class BeltReaderSystem extends GameSystemWithFilter {
readerComp.lastItemTimes.shift(); readerComp.lastItemTimes.shift();
} }
pinsComp.slots[1].value = readerComp.lastItem; if (!entity.components.BeltReader.isWireless()) {
pinsComp.slots[0].value = pinsComp.slots[1].value = readerComp.lastItem;
(readerComp.lastItemTimes[readerComp.lastItemTimes.length - 1] || 0) > pinsComp.slots[0].value =
minimumTimeForThroughput (readerComp.lastItemTimes[readerComp.lastItemTimes.length - 1] || 0) >
? BOOL_TRUE_SINGLETON minimumTimeForThroughput
: BOOL_FALSE_SINGLETON; ? BOOL_TRUE_SINGLETON
: BOOL_FALSE_SINGLETON;
}
if (now - readerComp.lastThroughputComputation > 0.5) { if (now - readerComp.lastThroughputComputation > 0.5) {
// Compute throughput // 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 { T } from "../../translations";
import { BaseItem } from "../base_item"; import { BaseItem } from "../base_item";
import { enumColors } from "../colors"; import { enumColors } from "../colors";
import { ConstantSignalComponent } from "../components/constant_signal"; import { enumConstantSignalType, ConstantSignalComponent } from "../components/constant_signal";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter"; 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 { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
import { COLOR_ITEM_SINGLETONS } from "../items/color_item"; import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
import { ShapeDefinition } from "../shape_definition"; import { ShapeDefinition } from "../shape_definition";
@ -26,8 +27,13 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
// Set signals // Set signals
for (let i = 0; i < this.allEntities.length; ++i) { for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i]; const entity = this.allEntities[i];
const pinsComp = entity.components.WiredPins;
const signalComp = entity.components.ConstantSignal; const signalComp = entity.components.ConstantSignal;
if (signalComp.isWireless()) {
continue;
}
const pinsComp = entity.components.WiredPins;
pinsComp.slots[0].value = signalComp.signal; pinsComp.slots[0].value = signalComp.signal;
} }
} }
@ -54,23 +60,33 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
validator: val => this.parseSignalCode(val), 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({ const itemInput = new FormElementItemChooser({
id: "signalItem", id: "signalItem",
label: null, label: null,
items: [ 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)
),
],
}); });
const dialog = new DialogWithForm({ const dialog = new DialogWithForm({
@ -103,7 +119,6 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
} }
if (itemInput.chosenItem) { if (itemInput.chosenItem) {
console.log(itemInput.chosenItem);
constantComp.signal = itemInput.chosenItem; constantComp.signal = itemInput.chosenItem;
} else { } else {
constantComp.signal = this.parseSignalCode(signalValueInput.getValue()); 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.painterQuad]: this.process_PAINTER_QUAD,
[enumItemProcessorTypes.hub]: this.process_HUB, [enumItemProcessorTypes.hub]: this.process_HUB,
[enumItemProcessorTypes.reader]: this.process_READER, [enumItemProcessorTypes.reader]: this.process_READER,
[enumItemProcessorTypes.goal]: this.process_GOAL,
}; };
// Bind all handlers // Bind all handlers
@ -562,4 +563,13 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
this.root.hubGoals.handleDefinitionDelivered(item.definition); 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"; import { GameRoot } from "../root";
/* typehints:end */ /* typehints:end */
import { enumItemProducerType, ItemProducerComponent } from "../components/item_producer"; import { ItemProducerComponent } from "../components/item_producer";
import { GameSystemWithFilter } from "../game_system_with_filter"; import { GameSystemWithFilter } from "../game_system_with_filter";
export class ItemProducerSystem extends GameSystemWithFilter { export class ItemProducerSystem extends GameSystemWithFilter {
/** @param {GameRoot} root */ /** @param {GameRoot} root */
constructor(root) { constructor(root) {
super(root, [ItemProducerComponent]); super(root, [ItemProducerComponent]);
this.item = null;
} }
update() { update() {
for (let i = 0; i < this.allEntities.length; ++i) { for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i]; const entity = this.allEntities[i];
const producerComp = entity.components.ItemProducer;
const ejectorComp = entity.components.ItemEjector;
if (entity.components.ItemProducer.type === enumItemProducerType.wired) { if (producerComp.isWireless()) {
const pinsComp = entity.components.WiredPins; continue;
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)
} }
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) { drawChunk(parameters, chunk) {
const zone = this.root.gameMode.getZone().allScaled(globalConfig.tileSize); const zone = this.root.gameMode.getZone().allScaled(globalConfig.tileSize);
const context = parameters.context;
parameters.context.globalAlpha = 0.1; context.globalAlpha = 0.1;
parameters.context.fillStyle = THEME.map.zone.background; context.fillStyle = THEME.map.zone.background;
parameters.context.fillRect(zone.x, zone.y, zone.w, zone.h); context.fillRect(zone.x, zone.y, zone.w, zone.h);
parameters.context.globalAlpha = 0.9; context.globalAlpha = 0.9;
parameters.context.strokeStyle = THEME.map.zone.border; context.strokeStyle = THEME.map.zone.border;
parameters.context.strokeRect(zone.x, zone.y, zone.w, zone.h); 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 (G_IS_DEV && globalConfig.debug.fastGameEnter) {
if (globalConfig.debug.testPuzzleMode) { if (globalConfig.debug.testPuzzleMode) {
this.onPuzzlePlayButtonClicked(); this.onPuzzleEditButtonClicked();
return; return;
} }

View File

@ -712,6 +712,11 @@ buildings:
name: &constant_producer Constant Producer name: &constant_producer Constant Producer
description: Outputs a shape, color or boolean (1 or 0) as specified. 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: storyRewards:
# Those are the rewards gained from completing the store # Those are the rewards gained from completing the store
reward_cutter_and_trash: reward_cutter_and_trash:
@ -1140,6 +1145,7 @@ keybindings:
comparator: *comparator comparator: *comparator
item_producer: Item Producer (Sandbox) item_producer: Item Producer (Sandbox)
constant_producer: *constant_producer constant_producer: *constant_producer
goal_acceptor: *goal_acceptor
# --- # ---
pipette: Pipette pipette: Pipette