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:
parent
ddab63bd2e
commit
212ea67125
BIN
res/ui/building_icons/goal_acceptor.png
Normal file
BIN
res/ui/building_icons/goal_acceptor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
BIN
res/ui/building_tutorials/goal_acceptor.png
Normal file
BIN
res/ui/building_tutorials/goal_acceptor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 271 KiB |
BIN
res_raw/sprites/blueprints/goal_acceptor.png
Normal file
BIN
res_raw/sprites/blueprints/goal_acceptor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.4 KiB |
BIN
res_raw/sprites/buildings/goal_acceptor.png
Normal file
BIN
res_raw/sprites/buildings/goal_acceptor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
@ -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 */
|
||||
|
@ -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,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
52
src/js/game/buildings/goal_acceptor.js
Normal file
52
src/js/game/buildings/goal_acceptor.js
Normal 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({}));
|
||||
}
|
||||
}
|
@ -110,6 +110,6 @@ export class MetaReaderBuilding extends MetaBuilding {
|
||||
})
|
||||
);
|
||||
|
||||
entity.addComponent(new BeltReaderComponent());
|
||||
entity.addComponent(new BeltReaderComponent({}));
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
22
src/js/game/components/goal_acceptor.js
Normal file
22
src/js/game/components/goal_acceptor.js
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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") },
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -117,4 +117,9 @@ export class PuzzleGameMode extends GameMode {
|
||||
getMinimumZoom() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
getIsFreeplayAvailable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
@ -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
|
||||
|
54
src/js/game/systems/constant_producer.js
Normal file
54
src/js/game/systems/constant_producer.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
|
120
src/js/game/systems/goal_acceptor.js
Normal file
120
src/js/game/systems/goal_acceptor.js
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user