Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 269 KiB |
Before Width: | Height: | Size: 636 KiB After Width: | Height: | Size: 653 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
@ -1,86 +1,91 @@
|
|||||||
import { enumDirection, Vector } from "../../core/vector";
|
import { enumDirection, Vector } from "../../core/vector";
|
||||||
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { MetaBuilding } from "../meta_building";
|
import { MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
import { LeverComponent } from "../components/lever";
|
import { LeverComponent } from "../components/lever";
|
||||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||||
import { ItemProcessorComponent, enumItemProcessorTypes } from "../components/item_processor";
|
import {
|
||||||
|
ItemProcessorComponent,
|
||||||
export class MetaFilterBuilding extends MetaBuilding {
|
enumItemProcessorTypes,
|
||||||
constructor() {
|
enumItemProcessorRequirements,
|
||||||
super("filter");
|
} from "../components/item_processor";
|
||||||
}
|
|
||||||
|
export class MetaFilterBuilding extends MetaBuilding {
|
||||||
getSilhouetteColor() {
|
constructor() {
|
||||||
return "#c45c2e";
|
super("filter");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
getSilhouetteColor() {
|
||||||
* @param {GameRoot} root
|
return "#c45c2e";
|
||||||
*/
|
}
|
||||||
getIsUnlocked(root) {
|
|
||||||
// @todo
|
/**
|
||||||
return true;
|
* @param {GameRoot} root
|
||||||
}
|
*/
|
||||||
|
getIsUnlocked(root) {
|
||||||
getDimensions() {
|
// @todo
|
||||||
return new Vector(2, 1);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getShowWiresLayerPreview() {
|
getDimensions() {
|
||||||
return true;
|
return new Vector(2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
getShowWiresLayerPreview() {
|
||||||
* Creates the entity at the given location
|
return true;
|
||||||
* @param {Entity} entity
|
}
|
||||||
*/
|
|
||||||
setupEntityComponents(entity) {
|
/**
|
||||||
entity.addComponent(
|
* Creates the entity at the given location
|
||||||
new WiredPinsComponent({
|
* @param {Entity} entity
|
||||||
slots: [
|
*/
|
||||||
{
|
setupEntityComponents(entity) {
|
||||||
pos: new Vector(0, 0),
|
entity.addComponent(
|
||||||
direction: enumDirection.left,
|
new WiredPinsComponent({
|
||||||
type: enumPinSlotType.logicalAcceptor,
|
slots: [
|
||||||
},
|
{
|
||||||
],
|
pos: new Vector(0, 0),
|
||||||
})
|
direction: enumDirection.left,
|
||||||
);
|
type: enumPinSlotType.logicalAcceptor,
|
||||||
|
},
|
||||||
entity.addComponent(
|
],
|
||||||
new ItemAcceptorComponent({
|
})
|
||||||
slots: [
|
);
|
||||||
{
|
|
||||||
pos: new Vector(0, 0),
|
entity.addComponent(
|
||||||
directions: [enumDirection.bottom],
|
new ItemAcceptorComponent({
|
||||||
},
|
slots: [
|
||||||
],
|
{
|
||||||
})
|
pos: new Vector(0, 0),
|
||||||
);
|
directions: [enumDirection.bottom],
|
||||||
|
},
|
||||||
entity.addComponent(
|
],
|
||||||
new ItemEjectorComponent({
|
})
|
||||||
slots: [
|
);
|
||||||
{
|
|
||||||
pos: new Vector(0, 0),
|
entity.addComponent(
|
||||||
direction: enumDirection.top,
|
new ItemEjectorComponent({
|
||||||
},
|
slots: [
|
||||||
{
|
{
|
||||||
pos: new Vector(1, 0),
|
pos: new Vector(0, 0),
|
||||||
direction: enumDirection.right,
|
direction: enumDirection.top,
|
||||||
},
|
},
|
||||||
],
|
{
|
||||||
})
|
pos: new Vector(1, 0),
|
||||||
);
|
direction: enumDirection.right,
|
||||||
|
},
|
||||||
entity.addComponent(
|
],
|
||||||
new ItemProcessorComponent({
|
})
|
||||||
processorType: enumItemProcessorTypes.filter,
|
);
|
||||||
inputsPerCharge: 1,
|
|
||||||
})
|
entity.addComponent(
|
||||||
);
|
new ItemProcessorComponent({
|
||||||
}
|
processorType: enumItemProcessorTypes.filter,
|
||||||
}
|
inputsPerCharge: 1,
|
||||||
|
processingRequirement: enumItemProcessorRequirements.filter,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,165 +1,171 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
import { BeltSystem } from "./systems/belt";
|
import { BeltSystem } from "./systems/belt";
|
||||||
import { ItemEjectorSystem } from "./systems/item_ejector";
|
import { ItemEjectorSystem } from "./systems/item_ejector";
|
||||||
import { MapResourcesSystem } from "./systems/map_resources";
|
import { MapResourcesSystem } from "./systems/map_resources";
|
||||||
import { MinerSystem } from "./systems/miner";
|
import { MinerSystem } from "./systems/miner";
|
||||||
import { ItemProcessorSystem } from "./systems/item_processor";
|
import { ItemProcessorSystem } from "./systems/item_processor";
|
||||||
import { UndergroundBeltSystem } from "./systems/underground_belt";
|
import { UndergroundBeltSystem } from "./systems/underground_belt";
|
||||||
import { HubSystem } from "./systems/hub";
|
import { HubSystem } from "./systems/hub";
|
||||||
import { StaticMapEntitySystem } from "./systems/static_map_entity";
|
import { StaticMapEntitySystem } from "./systems/static_map_entity";
|
||||||
import { ItemAcceptorSystem } from "./systems/item_acceptor";
|
import { ItemAcceptorSystem } from "./systems/item_acceptor";
|
||||||
import { StorageSystem } from "./systems/storage";
|
import { StorageSystem } from "./systems/storage";
|
||||||
import { WiredPinsSystem } from "./systems/wired_pins";
|
import { WiredPinsSystem } from "./systems/wired_pins";
|
||||||
import { BeltUnderlaysSystem } from "./systems/belt_underlays";
|
import { BeltUnderlaysSystem } from "./systems/belt_underlays";
|
||||||
import { WireSystem } from "./systems/wire";
|
import { WireSystem } from "./systems/wire";
|
||||||
import { ConstantSignalSystem } from "./systems/constant_signal";
|
import { ConstantSignalSystem } from "./systems/constant_signal";
|
||||||
import { LogicGateSystem } from "./systems/logic_gate";
|
import { LogicGateSystem } from "./systems/logic_gate";
|
||||||
import { LeverSystem } from "./systems/lever";
|
import { LeverSystem } from "./systems/lever";
|
||||||
import { DisplaySystem } from "./systems/display";
|
import { DisplaySystem } from "./systems/display";
|
||||||
|
import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays";
|
||||||
const logger = createLogger("game_system_manager");
|
|
||||||
|
const logger = createLogger("game_system_manager");
|
||||||
export class GameSystemManager {
|
|
||||||
/**
|
export class GameSystemManager {
|
||||||
*
|
/**
|
||||||
* @param {GameRoot} root
|
*
|
||||||
*/
|
* @param {GameRoot} root
|
||||||
constructor(root) {
|
*/
|
||||||
this.root = root;
|
constructor(root) {
|
||||||
|
this.root = root;
|
||||||
this.systems = {
|
|
||||||
/* typehints:start */
|
this.systems = {
|
||||||
/** @type {BeltSystem} */
|
/* typehints:start */
|
||||||
belt: null,
|
/** @type {BeltSystem} */
|
||||||
|
belt: null,
|
||||||
/** @type {ItemEjectorSystem} */
|
|
||||||
itemEjector: null,
|
/** @type {ItemEjectorSystem} */
|
||||||
|
itemEjector: null,
|
||||||
/** @type {MapResourcesSystem} */
|
|
||||||
mapResources: null,
|
/** @type {MapResourcesSystem} */
|
||||||
|
mapResources: null,
|
||||||
/** @type {MinerSystem} */
|
|
||||||
miner: null,
|
/** @type {MinerSystem} */
|
||||||
|
miner: null,
|
||||||
/** @type {ItemProcessorSystem} */
|
|
||||||
itemProcessor: null,
|
/** @type {ItemProcessorSystem} */
|
||||||
|
itemProcessor: null,
|
||||||
/** @type {UndergroundBeltSystem} */
|
|
||||||
undergroundBelt: null,
|
/** @type {UndergroundBeltSystem} */
|
||||||
|
undergroundBelt: null,
|
||||||
/** @type {HubSystem} */
|
|
||||||
hub: null,
|
/** @type {HubSystem} */
|
||||||
|
hub: null,
|
||||||
/** @type {StaticMapEntitySystem} */
|
|
||||||
staticMapEntities: null,
|
/** @type {StaticMapEntitySystem} */
|
||||||
|
staticMapEntities: null,
|
||||||
/** @type {ItemAcceptorSystem} */
|
|
||||||
itemAcceptor: null,
|
/** @type {ItemAcceptorSystem} */
|
||||||
|
itemAcceptor: null,
|
||||||
/** @type {StorageSystem} */
|
|
||||||
storage: null,
|
/** @type {StorageSystem} */
|
||||||
|
storage: null,
|
||||||
/** @type {WiredPinsSystem} */
|
|
||||||
wiredPins: null,
|
/** @type {WiredPinsSystem} */
|
||||||
|
wiredPins: null,
|
||||||
/** @type {BeltUnderlaysSystem} */
|
|
||||||
beltUnderlays: null,
|
/** @type {BeltUnderlaysSystem} */
|
||||||
|
beltUnderlays: null,
|
||||||
/** @type {WireSystem} */
|
|
||||||
wire: null,
|
/** @type {WireSystem} */
|
||||||
|
wire: null,
|
||||||
/** @type {ConstantSignalSystem} */
|
|
||||||
constantSignal: null,
|
/** @type {ConstantSignalSystem} */
|
||||||
|
constantSignal: null,
|
||||||
/** @type {LogicGateSystem} */
|
|
||||||
logicGate: null,
|
/** @type {LogicGateSystem} */
|
||||||
|
logicGate: null,
|
||||||
/** @type {LeverSystem} */
|
|
||||||
lever: null,
|
/** @type {LeverSystem} */
|
||||||
|
lever: null,
|
||||||
/** @type {DisplaySystem} */
|
|
||||||
display: null,
|
/** @type {DisplaySystem} */
|
||||||
|
display: null,
|
||||||
/* typehints:end */
|
|
||||||
};
|
/** @type {ItemProcessorOverlaysSystem} */
|
||||||
this.systemUpdateOrder = [];
|
itemProcessorOverlays: null,
|
||||||
|
|
||||||
this.internalInitSystems();
|
/* typehints:end */
|
||||||
}
|
};
|
||||||
|
this.systemUpdateOrder = [];
|
||||||
/**
|
|
||||||
* Initializes all systems
|
this.internalInitSystems();
|
||||||
*/
|
}
|
||||||
internalInitSystems() {
|
|
||||||
const add = (id, systemClass) => {
|
/**
|
||||||
this.systems[id] = new systemClass(this.root);
|
* Initializes all systems
|
||||||
this.systemUpdateOrder.push(id);
|
*/
|
||||||
};
|
internalInitSystems() {
|
||||||
|
const add = (id, systemClass) => {
|
||||||
// Order is important!
|
this.systems[id] = new systemClass(this.root);
|
||||||
|
this.systemUpdateOrder.push(id);
|
||||||
add("belt", BeltSystem);
|
};
|
||||||
|
|
||||||
add("undergroundBelt", UndergroundBeltSystem);
|
// Order is important!
|
||||||
|
|
||||||
add("miner", MinerSystem);
|
add("belt", BeltSystem);
|
||||||
|
|
||||||
add("storage", StorageSystem);
|
add("undergroundBelt", UndergroundBeltSystem);
|
||||||
|
|
||||||
add("itemProcessor", ItemProcessorSystem);
|
add("miner", MinerSystem);
|
||||||
|
|
||||||
add("itemEjector", ItemEjectorSystem);
|
add("storage", StorageSystem);
|
||||||
|
|
||||||
add("mapResources", MapResourcesSystem);
|
add("itemProcessor", ItemProcessorSystem);
|
||||||
|
|
||||||
add("hub", HubSystem);
|
add("itemEjector", ItemEjectorSystem);
|
||||||
|
|
||||||
add("staticMapEntities", StaticMapEntitySystem);
|
add("mapResources", MapResourcesSystem);
|
||||||
|
|
||||||
add("wiredPins", WiredPinsSystem);
|
add("hub", HubSystem);
|
||||||
|
|
||||||
add("beltUnderlays", BeltUnderlaysSystem);
|
add("staticMapEntities", StaticMapEntitySystem);
|
||||||
|
|
||||||
add("constantSignal", ConstantSignalSystem);
|
add("wiredPins", WiredPinsSystem);
|
||||||
|
|
||||||
// IMPORTANT: Must be after belt system since belt system can change the
|
add("beltUnderlays", BeltUnderlaysSystem);
|
||||||
// orientation of an entity after it is placed -> the item acceptor cache
|
|
||||||
// then would be invalid
|
add("constantSignal", ConstantSignalSystem);
|
||||||
add("itemAcceptor", ItemAcceptorSystem);
|
|
||||||
|
// IMPORTANT: Must be after belt system since belt system can change the
|
||||||
// WIRES section
|
// orientation of an entity after it is placed -> the item acceptor cache
|
||||||
add("lever", LeverSystem);
|
// then would be invalid
|
||||||
|
add("itemAcceptor", ItemAcceptorSystem);
|
||||||
// IMPORTANT: We have 2 phases: In phase 1 we compute the output values of all gates,
|
|
||||||
// processors etc. In phase 2 we propagate it through the wires network
|
// WIRES section
|
||||||
add("logicGate", LogicGateSystem);
|
add("lever", LeverSystem);
|
||||||
|
|
||||||
// Wires must be after all gate, signal etc logic!
|
// IMPORTANT: We have 2 phases: In phase 1 we compute the output values of all gates,
|
||||||
add("wire", WireSystem);
|
// processors etc. In phase 2 we propagate it through the wires network
|
||||||
|
add("logicGate", LogicGateSystem);
|
||||||
add("display", DisplaySystem);
|
|
||||||
|
// Wires must be after all gate, signal etc logic!
|
||||||
logger.log("📦 There are", this.systemUpdateOrder.length, "game systems");
|
add("wire", WireSystem);
|
||||||
}
|
|
||||||
|
add("display", DisplaySystem);
|
||||||
/**
|
|
||||||
* Updates all systems
|
add("itemProcessorOverlays", ItemProcessorOverlaysSystem);
|
||||||
*/
|
|
||||||
update() {
|
logger.log("📦 There are", this.systemUpdateOrder.length, "game systems");
|
||||||
for (let i = 0; i < this.systemUpdateOrder.length; ++i) {
|
}
|
||||||
const system = this.systems[this.systemUpdateOrder[i]];
|
|
||||||
system.update();
|
/**
|
||||||
}
|
* Updates all systems
|
||||||
}
|
*/
|
||||||
|
update() {
|
||||||
refreshCaches() {
|
for (let i = 0; i < this.systemUpdateOrder.length; ++i) {
|
||||||
for (let i = 0; i < this.systemUpdateOrder.length; ++i) {
|
const system = this.systems[this.systemUpdateOrder[i]];
|
||||||
const system = this.systems[this.systemUpdateOrder[i]];
|
system.update();
|
||||||
system.refreshCaches();
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
refreshCaches() {
|
||||||
|
for (let i = 0; i < this.systemUpdateOrder.length; ++i) {
|
||||||
|
const system = this.systems[this.systemUpdateOrder[i]];
|
||||||
|
system.refreshCaches();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,62 +1,70 @@
|
|||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
import { Loader } from "../../core/loader";
|
import { Loader } from "../../core/loader";
|
||||||
import { types } from "../../savegame/serialization";
|
import { types } from "../../savegame/serialization";
|
||||||
import { BaseItem } from "../base_item";
|
import { BaseItem } from "../base_item";
|
||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
|
|
||||||
export class BooleanItem extends BaseItem {
|
export class BooleanItem extends BaseItem {
|
||||||
static getId() {
|
static getId() {
|
||||||
return "boolean_item";
|
return "boolean_item";
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSchema() {
|
static getSchema() {
|
||||||
return types.uint;
|
return types.uint;
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize() {
|
serialize() {
|
||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialize(data) {
|
deserialize(data) {
|
||||||
this.value = data;
|
this.value = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {"boolean"} **/
|
/** @returns {"boolean"} **/
|
||||||
getItemType() {
|
getItemType() {
|
||||||
return "boolean";
|
return "boolean";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} value
|
* @param {number} value
|
||||||
*/
|
*/
|
||||||
constructor(value) {
|
constructor(value) {
|
||||||
super();
|
super();
|
||||||
this.value = value ? 1 : 0;
|
this.value = value ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {BaseItem} other
|
* @param {BaseItem} other
|
||||||
*/
|
*/
|
||||||
equalsImpl(other) {
|
equalsImpl(other) {
|
||||||
return this.value === /** @type {BooleanItem} */ (other).value;
|
return this.value === /** @type {BooleanItem} */ (other).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} x
|
* @param {number} x
|
||||||
* @param {number} y
|
* @param {number} y
|
||||||
* @param {number} diameter
|
* @param {number} diameter
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
*/
|
*/
|
||||||
drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
||||||
let sprite;
|
let sprite;
|
||||||
if (this.value) {
|
if (this.value) {
|
||||||
sprite = Loader.getSprite("sprites/wires/boolean_true.png");
|
sprite = Loader.getSprite("sprites/wires/boolean_true.png");
|
||||||
} else {
|
} else {
|
||||||
sprite = Loader.getSprite("sprites/wires/boolean_false.png");
|
sprite = Loader.getSprite("sprites/wires/boolean_false.png");
|
||||||
}
|
}
|
||||||
sprite.drawCachedCentered(parameters, x, y, diameter);
|
sprite.drawCachedCentered(parameters, x, y, diameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BOOL_FALSE_SINGLETON = new BooleanItem(0);
|
export const BOOL_FALSE_SINGLETON = new BooleanItem(0);
|
||||||
export const BOOL_TRUE_SINGLETON = new BooleanItem(1);
|
export const BOOL_TRUE_SINGLETON = new BooleanItem(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {BaseItem} item
|
||||||
|
*/
|
||||||
|
export function isTrueItem(item) {
|
||||||
|
return item && item.getItemType() === "boolean" && /** @type {BooleanItem} */ (item).value;
|
||||||
|
}
|
||||||
|
@ -1,99 +1,97 @@
|
|||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
import { Loader } from "../../core/loader";
|
import { Loader } from "../../core/loader";
|
||||||
import { BaseItem } from "../base_item";
|
import { BaseItem } from "../base_item";
|
||||||
import { enumColors } from "../colors";
|
import { enumColors } from "../colors";
|
||||||
import { DisplayComponent } from "../components/display";
|
import { DisplayComponent } from "../components/display";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
import { isTrueItem } from "../items/boolean_item";
|
||||||
import { MapChunkView } from "../map_chunk_view";
|
import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
||||||
import { BooleanItem } from "../items/boolean_item";
|
import { MapChunkView } from "../map_chunk_view";
|
||||||
|
|
||||||
export class DisplaySystem extends GameSystemWithFilter {
|
export class DisplaySystem extends GameSystemWithFilter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root, [DisplayComponent]);
|
super(root, [DisplayComponent]);
|
||||||
|
|
||||||
/** @type {Object<string, import("../../core/draw_utils").AtlasSprite>} */
|
/** @type {Object<string, import("../../core/draw_utils").AtlasSprite>} */
|
||||||
this.displaySprites = {};
|
this.displaySprites = {};
|
||||||
|
|
||||||
for (const colorId in enumColors) {
|
for (const colorId in enumColors) {
|
||||||
if (colorId === enumColors.uncolored) {
|
if (colorId === enumColors.uncolored) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.displaySprites[colorId] = Loader.getSprite("sprites/wires/display/" + colorId + ".png");
|
this.displaySprites[colorId] = Loader.getSprite("sprites/wires/display/" + colorId + ".png");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the color / value a display should show
|
* Returns the color / value a display should show
|
||||||
* @param {BaseItem} value
|
* @param {BaseItem} value
|
||||||
* @returns {BaseItem}
|
* @returns {BaseItem}
|
||||||
*/
|
*/
|
||||||
getDisplayItem(value) {
|
getDisplayItem(value) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (value.getItemType()) {
|
switch (value.getItemType()) {
|
||||||
case "boolean": {
|
case "boolean": {
|
||||||
return /** @type {BooleanItem} */ (value).value
|
return isTrueItem(value) ? COLOR_ITEM_SINGLETONS[enumColors.white] : null;
|
||||||
? COLOR_ITEM_SINGLETONS[enumColors.white]
|
}
|
||||||
: null;
|
|
||||||
}
|
case "color": {
|
||||||
|
const item = /**@type {ColorItem} */ (value);
|
||||||
case "color": {
|
return item.color === enumColors.uncolored ? null : item;
|
||||||
const item = /**@type {ColorItem} */ (value);
|
}
|
||||||
return item.color === enumColors.uncolored ? null : item;
|
|
||||||
}
|
case "shape": {
|
||||||
|
return value;
|
||||||
case "shape": {
|
}
|
||||||
return value;
|
|
||||||
}
|
default:
|
||||||
|
assertAlways(false, "Unknown item type: " + value.getItemType());
|
||||||
default:
|
}
|
||||||
assertAlways(false, "Unknown item type: " + value.getItemType());
|
}
|
||||||
}
|
|
||||||
}
|
/**
|
||||||
|
* Draws a given chunk
|
||||||
/**
|
* @param {import("../../core/draw_utils").DrawParameters} parameters
|
||||||
* Draws a given chunk
|
* @param {MapChunkView} chunk
|
||||||
* @param {import("../../core/draw_utils").DrawParameters} parameters
|
*/
|
||||||
* @param {MapChunkView} chunk
|
drawChunk(parameters, chunk) {
|
||||||
*/
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
drawChunk(parameters, chunk) {
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
const contents = chunk.containedEntitiesByLayer.regular;
|
const entity = contents[i];
|
||||||
for (let i = 0; i < contents.length; ++i) {
|
if (entity && entity.components.Display) {
|
||||||
const entity = contents[i];
|
const pinsComp = entity.components.WiredPins;
|
||||||
if (entity && entity.components.Display) {
|
const network = pinsComp.slots[0].linkedNetwork;
|
||||||
const pinsComp = entity.components.WiredPins;
|
|
||||||
const network = pinsComp.slots[0].linkedNetwork;
|
if (!network || !network.currentValue) {
|
||||||
|
continue;
|
||||||
if (!network || !network.currentValue) {
|
}
|
||||||
continue;
|
|
||||||
}
|
const value = this.getDisplayItem(network.currentValue);
|
||||||
|
|
||||||
const value = this.getDisplayItem(network.currentValue);
|
if (!value) {
|
||||||
|
continue;
|
||||||
if (!value) {
|
}
|
||||||
continue;
|
|
||||||
}
|
const origin = entity.components.StaticMapEntity.origin;
|
||||||
|
if (value.getItemType() === "color") {
|
||||||
const origin = entity.components.StaticMapEntity.origin;
|
this.displaySprites[/** @type {ColorItem} */ (value).color].drawCachedCentered(
|
||||||
if (value.getItemType() === "color") {
|
parameters,
|
||||||
this.displaySprites[/** @type {ColorItem} */ (value).color].drawCachedCentered(
|
(origin.x + 0.5) * globalConfig.tileSize,
|
||||||
parameters,
|
(origin.y + 0.5) * globalConfig.tileSize,
|
||||||
(origin.x + 0.5) * globalConfig.tileSize,
|
globalConfig.tileSize
|
||||||
(origin.y + 0.5) * globalConfig.tileSize,
|
);
|
||||||
globalConfig.tileSize
|
} else if (value.getItemType() === "shape") {
|
||||||
);
|
value.drawItemCenteredClipped(
|
||||||
} else if (value.getItemType() === "shape") {
|
(origin.x + 0.5) * globalConfig.tileSize,
|
||||||
value.drawItemCenteredClipped(
|
(origin.y + 0.5) * globalConfig.tileSize,
|
||||||
(origin.x + 0.5) * globalConfig.tileSize,
|
parameters,
|
||||||
(origin.y + 0.5) * globalConfig.tileSize,
|
30
|
||||||
parameters,
|
);
|
||||||
30
|
}
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,388 +1,381 @@
|
|||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
import { createLogger } from "../../core/logging";
|
import { createLogger } from "../../core/logging";
|
||||||
import { Rectangle } from "../../core/rectangle";
|
import { Rectangle } from "../../core/rectangle";
|
||||||
import { enumDirection, enumDirectionToVector, Vector } from "../../core/vector";
|
import { enumDirection, enumDirectionToVector, Vector } from "../../core/vector";
|
||||||
import { BaseItem } from "../base_item";
|
import { BaseItem } from "../base_item";
|
||||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { enumItemProcessorTypes } from "../components/item_processor";
|
import { enumItemProcessorTypes } from "../components/item_processor";
|
||||||
import { MapChunkView } from "../map_chunk_view";
|
import { MapChunkView } from "../map_chunk_view";
|
||||||
|
|
||||||
const logger = createLogger("systems/ejector");
|
const logger = createLogger("systems/ejector");
|
||||||
|
|
||||||
export class ItemEjectorSystem extends GameSystemWithFilter {
|
export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root, [ItemEjectorComponent]);
|
super(root, [ItemEjectorComponent]);
|
||||||
|
|
||||||
this.root.signals.entityAdded.add(this.checkForCacheInvalidation, this);
|
this.root.signals.entityAdded.add(this.checkForCacheInvalidation, this);
|
||||||
this.root.signals.entityDestroyed.add(this.checkForCacheInvalidation, this);
|
this.root.signals.entityDestroyed.add(this.checkForCacheInvalidation, this);
|
||||||
this.root.signals.postLoadHook.add(this.recomputeCache, this);
|
this.root.signals.postLoadHook.add(this.recomputeCache, this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Rectangle}
|
* @type {Rectangle}
|
||||||
*/
|
*/
|
||||||
this.areaToRecompute = null;
|
this.areaToRecompute = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
*/
|
*/
|
||||||
checkForCacheInvalidation(entity) {
|
checkForCacheInvalidation(entity) {
|
||||||
if (!this.root.gameInitialized) {
|
if (!this.root.gameInitialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!entity.components.StaticMapEntity) {
|
if (!entity.components.StaticMapEntity) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimize for the common case: adding or removing one building at a time. Clicking
|
// Optimize for the common case: adding or removing one building at a time. Clicking
|
||||||
// and dragging can cause up to 4 add/remove signals.
|
// and dragging can cause up to 4 add/remove signals.
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
const bounds = staticComp.getTileSpaceBounds();
|
const bounds = staticComp.getTileSpaceBounds();
|
||||||
const expandedBounds = bounds.expandedInAllDirections(2);
|
const expandedBounds = bounds.expandedInAllDirections(2);
|
||||||
|
|
||||||
if (this.areaToRecompute) {
|
if (this.areaToRecompute) {
|
||||||
this.areaToRecompute = this.areaToRecompute.getUnion(expandedBounds);
|
this.areaToRecompute = this.areaToRecompute.getUnion(expandedBounds);
|
||||||
} else {
|
} else {
|
||||||
this.areaToRecompute = expandedBounds;
|
this.areaToRecompute = expandedBounds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Precomputes the cache, which makes up for a huge performance improvement
|
* Precomputes the cache, which makes up for a huge performance improvement
|
||||||
*/
|
*/
|
||||||
recomputeCache() {
|
recomputeCache() {
|
||||||
if (this.areaToRecompute) {
|
if (this.areaToRecompute) {
|
||||||
logger.log("Recomputing cache using rectangle");
|
logger.log("Recomputing cache using rectangle");
|
||||||
if (G_IS_DEV && globalConfig.debug.renderChanges) {
|
if (G_IS_DEV && globalConfig.debug.renderChanges) {
|
||||||
this.root.hud.parts.changesDebugger.renderChange(
|
this.root.hud.parts.changesDebugger.renderChange(
|
||||||
"ejector-area",
|
"ejector-area",
|
||||||
this.areaToRecompute,
|
this.areaToRecompute,
|
||||||
"#fe50a6"
|
"#fe50a6"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.recomputeAreaCache();
|
this.recomputeAreaCache();
|
||||||
this.areaToRecompute = null;
|
this.areaToRecompute = null;
|
||||||
} else {
|
} else {
|
||||||
logger.log("Full cache recompute");
|
logger.log("Full cache recompute");
|
||||||
if (G_IS_DEV && globalConfig.debug.renderChanges) {
|
if (G_IS_DEV && globalConfig.debug.renderChanges) {
|
||||||
this.root.hud.parts.changesDebugger.renderChange(
|
this.root.hud.parts.changesDebugger.renderChange(
|
||||||
"ejector-full",
|
"ejector-full",
|
||||||
new Rectangle(-1000, -1000, 2000, 2000),
|
new Rectangle(-1000, -1000, 2000, 2000),
|
||||||
"#fe50a6"
|
"#fe50a6"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to find acceptors for every ejector
|
// Try to find acceptors for every ejector
|
||||||
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];
|
||||||
this.recomputeSingleEntityCache(entity);
|
this.recomputeSingleEntityCache(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recomputes the cache in the given area
|
* Recomputes the cache in the given area
|
||||||
*/
|
*/
|
||||||
recomputeAreaCache() {
|
recomputeAreaCache() {
|
||||||
const area = this.areaToRecompute;
|
const area = this.areaToRecompute;
|
||||||
let entryCount = 0;
|
let entryCount = 0;
|
||||||
|
|
||||||
logger.log("Recomputing area:", area.x, area.y, "/", area.w, area.h);
|
logger.log("Recomputing area:", area.x, area.y, "/", area.w, area.h);
|
||||||
|
|
||||||
// Store the entities we already recomputed, so we don't do work twice
|
// Store the entities we already recomputed, so we don't do work twice
|
||||||
const recomputedEntities = new Set();
|
const recomputedEntities = new Set();
|
||||||
|
|
||||||
for (let x = area.x; x < area.right(); ++x) {
|
for (let x = area.x; x < area.right(); ++x) {
|
||||||
for (let y = area.y; y < area.bottom(); ++y) {
|
for (let y = area.y; y < area.bottom(); ++y) {
|
||||||
const entities = this.root.map.getLayersContentsMultipleXY(x, y);
|
const entities = this.root.map.getLayersContentsMultipleXY(x, y);
|
||||||
for (let i = 0; i < entities.length; ++i) {
|
for (let i = 0; i < entities.length; ++i) {
|
||||||
const entity = entities[i];
|
const entity = entities[i];
|
||||||
|
|
||||||
// Recompute the entity in case its relevant for this system and it
|
// Recompute the entity in case its relevant for this system and it
|
||||||
// hasn't already been computed
|
// hasn't already been computed
|
||||||
if (!recomputedEntities.has(entity.uid) && entity.components.ItemEjector) {
|
if (!recomputedEntities.has(entity.uid) && entity.components.ItemEjector) {
|
||||||
recomputedEntities.add(entity.uid);
|
recomputedEntities.add(entity.uid);
|
||||||
this.recomputeSingleEntityCache(entity);
|
this.recomputeSingleEntityCache(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return entryCount;
|
return entryCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
*/
|
*/
|
||||||
recomputeSingleEntityCache(entity) {
|
recomputeSingleEntityCache(entity) {
|
||||||
const ejectorComp = entity.components.ItemEjector;
|
const ejectorComp = entity.components.ItemEjector;
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
|
|
||||||
for (let slotIndex = 0; slotIndex < ejectorComp.slots.length; ++slotIndex) {
|
for (let slotIndex = 0; slotIndex < ejectorComp.slots.length; ++slotIndex) {
|
||||||
const ejectorSlot = ejectorComp.slots[slotIndex];
|
const ejectorSlot = ejectorComp.slots[slotIndex];
|
||||||
|
|
||||||
// Clear the old cache.
|
// Clear the old cache.
|
||||||
ejectorSlot.cachedDestSlot = null;
|
ejectorSlot.cachedDestSlot = null;
|
||||||
ejectorSlot.cachedTargetEntity = null;
|
ejectorSlot.cachedTargetEntity = null;
|
||||||
ejectorSlot.cachedBeltPath = null;
|
ejectorSlot.cachedBeltPath = null;
|
||||||
|
|
||||||
// Figure out where and into which direction we eject items
|
// Figure out where and into which direction we eject items
|
||||||
const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos);
|
const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos);
|
||||||
const ejectSlotWsDirection = staticComp.localDirectionToWorld(ejectorSlot.direction);
|
const ejectSlotWsDirection = staticComp.localDirectionToWorld(ejectorSlot.direction);
|
||||||
const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection];
|
const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection];
|
||||||
const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector);
|
const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector);
|
||||||
|
|
||||||
// Try to find the given acceptor component to take the item
|
// Try to find the given acceptor component to take the item
|
||||||
// Since there can be cross layer dependencies, check on all layers
|
// Since there can be cross layer dependencies, check on all layers
|
||||||
const targetEntities = this.root.map.getLayersContentsMultipleXY(
|
const targetEntities = this.root.map.getLayersContentsMultipleXY(
|
||||||
ejectSlotTargetWsTile.x,
|
ejectSlotTargetWsTile.x,
|
||||||
ejectSlotTargetWsTile.y
|
ejectSlotTargetWsTile.y
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let i = 0; i < targetEntities.length; ++i) {
|
for (let i = 0; i < targetEntities.length; ++i) {
|
||||||
const targetEntity = targetEntities[i];
|
const targetEntity = targetEntities[i];
|
||||||
|
|
||||||
const targetStaticComp = targetEntity.components.StaticMapEntity;
|
const targetStaticComp = targetEntity.components.StaticMapEntity;
|
||||||
const targetBeltComp = targetEntity.components.Belt;
|
const targetBeltComp = targetEntity.components.Belt;
|
||||||
|
|
||||||
// Check for belts (special case)
|
// Check for belts (special case)
|
||||||
if (targetBeltComp) {
|
if (targetBeltComp) {
|
||||||
const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(enumDirection.top);
|
const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(enumDirection.top);
|
||||||
if (ejectSlotWsDirection === beltAcceptingDirection) {
|
if (ejectSlotWsDirection === beltAcceptingDirection) {
|
||||||
ejectorSlot.cachedTargetEntity = targetEntity;
|
ejectorSlot.cachedTargetEntity = targetEntity;
|
||||||
ejectorSlot.cachedBeltPath = targetBeltComp.assignedPath;
|
ejectorSlot.cachedBeltPath = targetBeltComp.assignedPath;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for item acceptors
|
// Check for item acceptors
|
||||||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||||
if (!targetAcceptorComp) {
|
if (!targetAcceptorComp) {
|
||||||
// Entity doesn't accept items
|
// Entity doesn't accept items
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const matchingSlot = targetAcceptorComp.findMatchingSlot(
|
const matchingSlot = targetAcceptorComp.findMatchingSlot(
|
||||||
targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile),
|
targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile),
|
||||||
targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection)
|
targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!matchingSlot) {
|
if (!matchingSlot) {
|
||||||
// No matching slot found
|
// No matching slot found
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A slot can always be connected to one other slot only
|
// A slot can always be connected to one other slot only
|
||||||
ejectorSlot.cachedTargetEntity = targetEntity;
|
ejectorSlot.cachedTargetEntity = targetEntity;
|
||||||
ejectorSlot.cachedDestSlot = matchingSlot;
|
ejectorSlot.cachedDestSlot = matchingSlot;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
if (this.areaToRecompute) {
|
if (this.areaToRecompute) {
|
||||||
this.recomputeCache();
|
this.recomputeCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Precompute effective belt speed
|
// Precompute effective belt speed
|
||||||
let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds;
|
let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds;
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.instantBelts) {
|
if (G_IS_DEV && globalConfig.debug.instantBelts) {
|
||||||
progressGrowth = 1;
|
progressGrowth = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go over all cache entries
|
// Go over all cache entries
|
||||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||||
const sourceEntity = this.allEntities[i];
|
const sourceEntity = this.allEntities[i];
|
||||||
const sourceEjectorComp = sourceEntity.components.ItemEjector;
|
const sourceEjectorComp = sourceEntity.components.ItemEjector;
|
||||||
if (!sourceEjectorComp.enabled) {
|
if (!sourceEjectorComp.enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const slots = sourceEjectorComp.slots;
|
const slots = sourceEjectorComp.slots;
|
||||||
for (let j = 0; j < slots.length; ++j) {
|
for (let j = 0; j < slots.length; ++j) {
|
||||||
const sourceSlot = slots[j];
|
const sourceSlot = slots[j];
|
||||||
const item = sourceSlot.item;
|
const item = sourceSlot.item;
|
||||||
if (!item) {
|
if (!item) {
|
||||||
// No item available to be ejected
|
// No item available to be ejected
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetEntity = sourceSlot.cachedTargetEntity;
|
const targetEntity = sourceSlot.cachedTargetEntity;
|
||||||
|
|
||||||
// Advance items on the slot
|
// Advance items on the slot
|
||||||
sourceSlot.progress = Math.min(
|
sourceSlot.progress = Math.min(
|
||||||
1,
|
1,
|
||||||
sourceSlot.progress +
|
sourceSlot.progress +
|
||||||
progressGrowth *
|
progressGrowth *
|
||||||
this.root.hubGoals.getBeltBaseSpeed() *
|
this.root.hubGoals.getBeltBaseSpeed() *
|
||||||
globalConfig.itemSpacingOnBelts
|
globalConfig.itemSpacingOnBelts
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check if we are still in the process of ejecting, can't proceed then
|
// Check if we are still in the process of ejecting, can't proceed then
|
||||||
if (sourceSlot.progress < 1.0) {
|
if (sourceSlot.progress < 1.0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we are ejecting to a belt path
|
// Check if we are ejecting to a belt path
|
||||||
const destPath = sourceSlot.cachedBeltPath;
|
const destPath = sourceSlot.cachedBeltPath;
|
||||||
if (destPath) {
|
if (destPath) {
|
||||||
// Try passing the item over
|
// Try passing the item over
|
||||||
if (destPath.tryAcceptItem(item)) {
|
if (destPath.tryAcceptItem(item)) {
|
||||||
sourceSlot.item = null;
|
sourceSlot.item = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always stop here, since there can *either* be a belt path *or*
|
// Always stop here, since there can *either* be a belt path *or*
|
||||||
// a slot
|
// a slot
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the target acceptor can actually accept this item
|
// Check if the target acceptor can actually accept this item
|
||||||
const destSlot = sourceSlot.cachedDestSlot;
|
const destSlot = sourceSlot.cachedDestSlot;
|
||||||
if (destSlot) {
|
if (destSlot) {
|
||||||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||||
if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) {
|
if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to hand over the item
|
// Try to hand over the item
|
||||||
if (this.tryPassOverItem(item, targetEntity, destSlot.index)) {
|
if (this.tryPassOverItem(item, targetEntity, destSlot.index)) {
|
||||||
// Handover successful, clear slot
|
// Handover successful, clear slot
|
||||||
targetAcceptorComp.onItemAccepted(destSlot.index, destSlot.acceptedDirection, item);
|
targetAcceptorComp.onItemAccepted(destSlot.index, destSlot.acceptedDirection, item);
|
||||||
sourceSlot.item = null;
|
sourceSlot.item = null;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {BaseItem} item
|
* @param {BaseItem} item
|
||||||
* @param {Entity} receiver
|
* @param {Entity} receiver
|
||||||
* @param {number} slotIndex
|
* @param {number} slotIndex
|
||||||
*/
|
*/
|
||||||
tryPassOverItem(item, receiver, slotIndex) {
|
tryPassOverItem(item, receiver, slotIndex) {
|
||||||
// Try figuring out how what to do with the item
|
// Try figuring out how what to do with the item
|
||||||
// TODO: Kinda hacky. How to solve this properly? Don't want to go through inheritance hell.
|
// TODO: Kinda hacky. How to solve this properly? Don't want to go through inheritance hell.
|
||||||
// Also its just a few cases (hope it stays like this .. :x).
|
// Also its just a few cases (hope it stays like this .. :x).
|
||||||
|
|
||||||
const beltComp = receiver.components.Belt;
|
const beltComp = receiver.components.Belt;
|
||||||
if (beltComp) {
|
if (beltComp) {
|
||||||
const path = beltComp.assignedPath;
|
const path = beltComp.assignedPath;
|
||||||
assert(path, "belt has no path");
|
assert(path, "belt has no path");
|
||||||
if (path.tryAcceptItem(item)) {
|
if (path.tryAcceptItem(item)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Belt can have nothing else
|
// Belt can have nothing else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemProcessorComp = receiver.components.ItemProcessor;
|
const itemProcessorComp = receiver.components.ItemProcessor;
|
||||||
if (itemProcessorComp) {
|
if (itemProcessorComp) {
|
||||||
// @todo HACK
|
// Check for potential filters
|
||||||
// Check if there are pins, and if so if they are connected
|
if (!this.root.systemMgr.systems.itemProcessor.checkRequirements(receiver, item, slotIndex)) {
|
||||||
if (itemProcessorComp.type === enumItemProcessorTypes.filter) {
|
return false;
|
||||||
const pinsComp = receiver.components.WiredPins;
|
}
|
||||||
if (pinsComp && pinsComp.slots.length === 1) {
|
|
||||||
const network = pinsComp.slots[0].linkedNetwork;
|
// Its an item processor ..
|
||||||
if (!network || !network.currentValue) {
|
if (itemProcessorComp.tryTakeItem(item, slotIndex)) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
// Item processor can have nothing else
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
// Its an item processor ..
|
|
||||||
if (itemProcessorComp.tryTakeItem(item, slotIndex)) {
|
const undergroundBeltComp = receiver.components.UndergroundBelt;
|
||||||
return true;
|
if (undergroundBeltComp) {
|
||||||
}
|
// Its an underground belt. yay.
|
||||||
// Item processor can have nothing else
|
if (
|
||||||
return false;
|
undergroundBeltComp.tryAcceptExternalItem(
|
||||||
}
|
item,
|
||||||
|
this.root.hubGoals.getUndergroundBeltBaseSpeed()
|
||||||
const undergroundBeltComp = receiver.components.UndergroundBelt;
|
)
|
||||||
if (undergroundBeltComp) {
|
) {
|
||||||
// Its an underground belt. yay.
|
return true;
|
||||||
if (
|
}
|
||||||
undergroundBeltComp.tryAcceptExternalItem(
|
|
||||||
item,
|
// Underground belt can have nothing else
|
||||||
this.root.hubGoals.getUndergroundBeltBaseSpeed()
|
return false;
|
||||||
)
|
}
|
||||||
) {
|
|
||||||
return true;
|
const storageComp = receiver.components.Storage;
|
||||||
}
|
if (storageComp) {
|
||||||
|
// It's a storage
|
||||||
// Underground belt can have nothing else
|
if (storageComp.canAcceptItem(item)) {
|
||||||
return false;
|
storageComp.takeItem(item);
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
const storageComp = receiver.components.Storage;
|
|
||||||
if (storageComp) {
|
// Storage can't have anything else
|
||||||
// It's a storage
|
return false;
|
||||||
if (storageComp.canAcceptItem(item)) {
|
}
|
||||||
storageComp.takeItem(item);
|
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Storage can't have anything else
|
/**
|
||||||
return false;
|
* @param {DrawParameters} parameters
|
||||||
}
|
* @param {MapChunkView} chunk
|
||||||
|
*/
|
||||||
return false;
|
drawChunk(parameters, chunk) {
|
||||||
}
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
|
|
||||||
/**
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
* @param {DrawParameters} parameters
|
const entity = contents[i];
|
||||||
* @param {MapChunkView} chunk
|
const ejectorComp = entity.components.ItemEjector;
|
||||||
*/
|
if (!ejectorComp) {
|
||||||
drawChunk(parameters, chunk) {
|
continue;
|
||||||
const contents = chunk.containedEntitiesByLayer.regular;
|
}
|
||||||
|
|
||||||
for (let i = 0; i < contents.length; ++i) {
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
const entity = contents[i];
|
|
||||||
const ejectorComp = entity.components.ItemEjector;
|
for (let i = 0; i < ejectorComp.slots.length; ++i) {
|
||||||
if (!ejectorComp) {
|
const slot = ejectorComp.slots[i];
|
||||||
continue;
|
const ejectedItem = slot.item;
|
||||||
}
|
|
||||||
|
if (!ejectedItem) {
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
// No item
|
||||||
|
continue;
|
||||||
for (let i = 0; i < ejectorComp.slots.length; ++i) {
|
}
|
||||||
const slot = ejectorComp.slots[i];
|
|
||||||
const ejectedItem = slot.item;
|
const realPosition = staticComp.localTileToWorld(slot.pos);
|
||||||
|
if (!chunk.tileSpaceRectangle.containsPoint(realPosition.x, realPosition.y)) {
|
||||||
if (!ejectedItem) {
|
// Not within this chunk
|
||||||
// No item
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
const realDirection = staticComp.localDirectionToWorld(slot.direction);
|
||||||
const realPosition = staticComp.localTileToWorld(slot.pos);
|
const realDirectionVector = enumDirectionToVector[realDirection];
|
||||||
if (!chunk.tileSpaceRectangle.containsPoint(realPosition.x, realPosition.y)) {
|
|
||||||
// Not within this chunk
|
const tileX = realPosition.x + 0.5 + realDirectionVector.x * 0.5 * slot.progress;
|
||||||
continue;
|
const tileY = realPosition.y + 0.5 + realDirectionVector.y * 0.5 * slot.progress;
|
||||||
}
|
|
||||||
|
const worldX = tileX * globalConfig.tileSize;
|
||||||
const realDirection = staticComp.localDirectionToWorld(slot.direction);
|
const worldY = tileY * globalConfig.tileSize;
|
||||||
const realDirectionVector = enumDirectionToVector[realDirection];
|
|
||||||
|
ejectedItem.drawItemCenteredClipped(
|
||||||
const tileX = realPosition.x + 0.5 + realDirectionVector.x * 0.5 * slot.progress;
|
worldX,
|
||||||
const tileY = realPosition.y + 0.5 + realDirectionVector.y * 0.5 * slot.progress;
|
worldY,
|
||||||
|
parameters,
|
||||||
const worldX = tileX * globalConfig.tileSize;
|
globalConfig.defaultItemDiameter
|
||||||
const worldY = tileY * globalConfig.tileSize;
|
);
|
||||||
|
}
|
||||||
ejectedItem.drawItemCenteredClipped(
|
}
|
||||||
worldX,
|
}
|
||||||
worldY,
|
}
|
||||||
parameters,
|
|
||||||
globalConfig.defaultItemDiameter
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
import { GameSystem } from "../game_system";
|
||||||
|
import { MapChunkView } from "../map_chunk_view";
|
||||||
|
import { enumItemProcessorRequirements } from "../components/item_processor";
|
||||||
|
import { Entity } from "../entity";
|
||||||
|
import { isTrueItem } from "../items/boolean_item";
|
||||||
|
import { globalConfig } from "../../core/config";
|
||||||
|
import { Loader } from "../../core/loader";
|
||||||
|
import { smoothPulse } from "../../core/utils";
|
||||||
|
|
||||||
|
export class ItemProcessorOverlaysSystem extends GameSystem {
|
||||||
|
constructor(root) {
|
||||||
|
super(root);
|
||||||
|
|
||||||
|
this.spriteDisabled = Loader.getSprite("sprites/misc/processor_disabled.png");
|
||||||
|
this.spriteDisconnected = Loader.getSprite("sprites/misc/processor_disconnected.png");
|
||||||
|
|
||||||
|
this.drawnUids = new Set();
|
||||||
|
|
||||||
|
this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearDrawnUids() {
|
||||||
|
this.drawnUids.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import("../../core/draw_utils").DrawParameters} parameters
|
||||||
|
* @param {MapChunkView} chunk
|
||||||
|
*/
|
||||||
|
drawChunk(parameters, chunk) {
|
||||||
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
|
const entity = contents[i];
|
||||||
|
const processorComp = entity.components.ItemProcessor;
|
||||||
|
if (!processorComp) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requirement = processorComp.processingRequirement;
|
||||||
|
if (!requirement) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.drawnUids.has(entity.uid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.drawnUids.add(entity.uid);
|
||||||
|
|
||||||
|
switch (requirement) {
|
||||||
|
case enumItemProcessorRequirements.painterQuad: {
|
||||||
|
this.drawConnectedSlotRequirement(parameters, entity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case enumItemProcessorRequirements.filter: {
|
||||||
|
this.drawConnectedSlotRequirement(parameters, entity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import("../../core/draw_utils").DrawParameters} parameters
|
||||||
|
* @param {Entity} entity
|
||||||
|
*/
|
||||||
|
drawConnectedSlotRequirement(parameters, entity) {
|
||||||
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
|
const pinsComp = entity.components.WiredPins;
|
||||||
|
|
||||||
|
let anySlotConnected = false;
|
||||||
|
|
||||||
|
// Check if any slot has a value
|
||||||
|
for (let i = 0; i < pinsComp.slots.length; ++i) {
|
||||||
|
const slot = pinsComp.slots[i];
|
||||||
|
const network = slot.linkedNetwork;
|
||||||
|
if (network && network.currentValue) {
|
||||||
|
anySlotConnected = true;
|
||||||
|
|
||||||
|
if (isTrueItem(network.currentValue)) {
|
||||||
|
// No need to draw anything
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pulse = smoothPulse(this.root.time.now());
|
||||||
|
parameters.context.globalAlpha = 0.6 + 0.4 * pulse;
|
||||||
|
const sprite = anySlotConnected ? this.spriteDisabled : this.spriteDisconnected;
|
||||||
|
sprite.drawCachedCentered(
|
||||||
|
parameters,
|
||||||
|
(staticComp.origin.x + 0.5) * globalConfig.tileSize,
|
||||||
|
(staticComp.origin.y + 0.5) * globalConfig.tileSize,
|
||||||
|
globalConfig.tileSize * (0.7 + 0.2 * pulse)
|
||||||
|
);
|
||||||
|
|
||||||
|
parameters.context.globalAlpha = 1;
|
||||||
|
}
|
||||||
|
}
|