diff --git a/res/ui/building_icons/wireless_display.png b/res/ui/building_icons/wireless_display.png new file mode 100644 index 00000000..56548fde Binary files /dev/null and b/res/ui/building_icons/wireless_display.png differ diff --git a/res/ui/building_tutorials/wireless_display-remote_control.png b/res/ui/building_tutorials/wireless_display-remote_control.png new file mode 100644 index 00000000..3048c9e3 Binary files /dev/null and b/res/ui/building_tutorials/wireless_display-remote_control.png differ diff --git a/res/ui/building_tutorials/wireless_display.png b/res/ui/building_tutorials/wireless_display.png new file mode 100644 index 00000000..3048c9e3 Binary files /dev/null and b/res/ui/building_tutorials/wireless_display.png differ diff --git a/res_raw/sprites/blueprints/wireless_display-remote_control.png b/res_raw/sprites/blueprints/wireless_display-remote_control.png new file mode 100644 index 00000000..f07d0276 Binary files /dev/null and b/res_raw/sprites/blueprints/wireless_display-remote_control.png differ diff --git a/res_raw/sprites/blueprints/wireless_display.png b/res_raw/sprites/blueprints/wireless_display.png new file mode 100644 index 00000000..bb0319f2 Binary files /dev/null and b/res_raw/sprites/blueprints/wireless_display.png differ diff --git a/res_raw/sprites/buildings/wireless_display-remote_control.png b/res_raw/sprites/buildings/wireless_display-remote_control.png new file mode 100644 index 00000000..86f15008 Binary files /dev/null and b/res_raw/sprites/buildings/wireless_display-remote_control.png differ diff --git a/res_raw/sprites/buildings/wireless_display.png b/res_raw/sprites/buildings/wireless_display.png new file mode 100644 index 00000000..ec7b95d9 Binary files /dev/null and b/res_raw/sprites/buildings/wireless_display.png differ diff --git a/src/css/resources.scss b/src/css/resources.scss index 3fa0bdf6..a40a5283 100644 --- a/src/css/resources.scss +++ b/src/css/resources.scss @@ -1,5 +1,5 @@ $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, wireless_display, virtual_processor, reader, storage, transistor, analyzer, comparator, item_producer; @each $building in $buildings { @@ -11,7 +11,7 @@ $buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, tra $buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2, miner, miner-chainable, cutter, cutter-quad, rotater, rotater-ccw, stacker, mixer, painter-double, painter-quad, trash, storage, - reader, rotater-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not, + reader, rotater-rotate180, display, wireless_display, wireless_display-remote_control, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not, logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker, item_producer, virtual_processor-stacker, virtual_processor-painter, wire-second, wire-third, painter, painter-mirrored, comparator; @each $building in $buildingsAndVariants { diff --git a/src/js/game/buildings/wireless_display.js b/src/js/game/buildings/wireless_display.js new file mode 100644 index 00000000..a360ff1d --- /dev/null +++ b/src/js/game/buildings/wireless_display.js @@ -0,0 +1,92 @@ +import { enumDirection, Vector } from "../../core/vector"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { WirelessDisplayComponent } from "../components/wireless_display"; +import { enumHubGoalRewards } from "../tutorial_goals"; +import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; +import { T } from "../../translations"; +import { BeltUnderlaysComponent } from "../components/belt_underlays"; + + +/** @enum {string} */ +export const enumWirelessDisplayVariants = { + remote_control: "remote_control", +}; + +const overlayMatrices = { + [defaultBuildingVariant]: null, + [enumWirelessDisplayVariants.remote_control]: generateMatrixRotations([0, 1, 0, 0, 1, 1, 0, 1, 0]), +}; + +export class MetaWirelessDisplayBuilding extends MetaBuilding { + constructor() { + super("wireless_display"); + } + + getSilhouetteColor() { + return "#aaaaaa"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_display); + } + + /** + * @param {GameRoot} root + */ + getAvailableVariants(root) { + let available = [defaultBuildingVariant]; + + if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_display)) { + available.push(enumWirelessDisplayVariants.remote_control); + } + + return available; + } + + getDimensions() { + return new Vector(1, 1); + } + + getShowWiresLayerPreview() { + return true; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + * @param {string} variant + */ + updateVariants(entity, rotationVariant, variant) { + if (variant == enumWirelessDisplayVariants.remote_control && !entity.components.WiredPins) { + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + type: enumPinSlotType.logicalAcceptor, + }, + ], + }) + ); + } + } +} diff --git a/src/js/game/component_registry.js b/src/js/game/component_registry.js index f094e60d..d15660bb 100644 --- a/src/js/game/component_registry.js +++ b/src/js/game/component_registry.js @@ -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 { WirelessDisplayComponent } from "./components/wireless_display"; export function initComponentRegistry() { gComponentRegistry.register(StaticMapEntityComponent); @@ -38,6 +39,7 @@ export function initComponentRegistry() { gComponentRegistry.register(LeverComponent); gComponentRegistry.register(WireTunnelComponent); gComponentRegistry.register(DisplayComponent); + gComponentRegistry.register(WirelessDisplayComponent); gComponentRegistry.register(BeltReaderComponent); gComponentRegistry.register(FilterComponent); gComponentRegistry.register(ItemProducerComponent); diff --git a/src/js/game/components/wireless_display.js b/src/js/game/components/wireless_display.js new file mode 100644 index 00000000..e129d2e9 --- /dev/null +++ b/src/js/game/components/wireless_display.js @@ -0,0 +1,7 @@ +import { Component } from "../component"; + +export class WirelessDisplayComponent extends Component { + static getId() { + return "Wiresless_Display"; + } +} diff --git a/src/js/game/entity_components.js b/src/js/game/entity_components.js index 7dee590a..8bbcc55b 100644 --- a/src/js/game/entity_components.js +++ b/src/js/game/entity_components.js @@ -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 { WirelessDisplayComponent } from "./components/wireless_display"; /* typehints:end */ /** @@ -80,6 +81,9 @@ export class EntityComponentStorage { /** @type {DisplayComponent} */ this.Display; + /** @type {WirelessDisplayComponent} */ + this.WirelessDisplay; + /** @type {BeltReaderComponent} */ this.BeltReader; diff --git a/src/js/game/game_system_manager.js b/src/js/game/game_system_manager.js index 74ba798f..1047ae98 100644 --- a/src/js/game/game_system_manager.js +++ b/src/js/game/game_system_manager.js @@ -20,6 +20,7 @@ import { ConstantSignalSystem } from "./systems/constant_signal"; import { LogicGateSystem } from "./systems/logic_gate"; import { LeverSystem } from "./systems/lever"; import { DisplaySystem } from "./systems/display"; +import { WirelessDisplaySystem } from "./systems/wireless_display"; import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays"; import { BeltReaderSystem } from "./systems/belt_reader"; import { FilterSystem } from "./systems/filter"; diff --git a/src/js/game/hud/parts/buildings_toolbar.js b/src/js/game/hud/parts/buildings_toolbar.js index 05ffc795..bed5c80f 100644 --- a/src/js/game/hud/parts/buildings_toolbar.js +++ b/src/js/game/hud/parts/buildings_toolbar.js @@ -1,6 +1,7 @@ import { MetaBeltBuilding } from "../../buildings/belt"; import { MetaCutterBuilding } from "../../buildings/cutter"; import { MetaDisplayBuilding } from "../../buildings/display"; +import { MetaWirelessDisplayBuilding } from "../../buildings/wireless_display"; import { MetaFilterBuilding } from "../../buildings/filter"; import { MetaLeverBuilding } from "../../buildings/lever"; import { MetaMinerBuilding } from "../../buildings/miner"; @@ -39,6 +40,7 @@ export class HUDBuildingsToolbar extends HUDBaseToolbar { MetaLeverBuilding, MetaFilterBuilding, MetaDisplayBuilding, + MetaWirelessDisplayBuilding, ], visibilityCondition: () => !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "regular", diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index 9fa4ffe1..0c8bbac8 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -65,6 +65,7 @@ export const KEYMAPPINGS = { lever: { keyCode: key("I") }, filter: { keyCode: key("O") }, display: { keyCode: key("P") }, + wireless_display: { keyCode: key("K") }, // Wires toolbar wire: { keyCode: key("1") }, diff --git a/src/js/game/meta_building_registry.js b/src/js/game/meta_building_registry.js index 668d8a37..ecb928d6 100644 --- a/src/js/game/meta_building_registry.js +++ b/src/js/game/meta_building_registry.js @@ -8,6 +8,7 @@ import { MetaComparatorBuilding } from "./buildings/comparator"; import { MetaConstantSignalBuilding } from "./buildings/constant_signal"; import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter"; import { MetaDisplayBuilding } from "./buildings/display"; +import { enumWirelessDisplayVariants, MetaWirelessDisplayBuilding } from "./buildings/wireless_display"; import { MetaFilterBuilding } from "./buildings/filter"; import { MetaHubBuilding } from "./buildings/hub"; import { MetaItemProducerBuilding } from "./buildings/item_producer"; @@ -53,6 +54,7 @@ export function initMetaBuildingRegistry() { gMetaBuildingRegistry.register(MetaFilterBuilding); gMetaBuildingRegistry.register(MetaWireTunnelBuilding); gMetaBuildingRegistry.register(MetaDisplayBuilding); + gMetaBuildingRegistry.register(MetaWirelessDisplayBuilding); gMetaBuildingRegistry.register(MetaVirtualProcessorBuilding); gMetaBuildingRegistry.register(MetaReaderBuilding); gMetaBuildingRegistry.register(MetaTransistorBuilding); @@ -174,6 +176,10 @@ export function initMetaBuildingRegistry() { // Item producer registerBuildingVariant(61, MetaItemProducerBuilding); + // Wireless Display + registerBuildingVariant(62, MetaWirelessDisplayBuilding); + registerBuildingVariant(63, MetaWirelessDisplayBuilding, enumWirelessDisplayVariants.remote_control); + // Propagate instances for (const key in gBuildingVariants) { gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass( diff --git a/src/js/game/systems/wireless_display.js b/src/js/game/systems/wireless_display.js new file mode 100644 index 00000000..fcf765e1 --- /dev/null +++ b/src/js/game/systems/wireless_display.js @@ -0,0 +1,97 @@ +import { globalConfig } from "../../core/config"; +import { Loader } from "../../core/loader"; +import { BaseItem } from "../base_item"; +import { enumColors } from "../colors"; +import { WirelessDisplayComponent } from "../components/wireless_display"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { isTrueItem } from "../items/boolean_item"; +import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item"; +import { MapChunkView } from "../map_chunk_view"; + +export class WirelessDisplaySystem extends GameSystemWithFilter { + constructor(root) { + super(root, [WirelessDisplayComponent]); + + /** @type {Object} */ + this.displaySprites = {}; + + for (const colorId in enumColors) { + if (colorId === enumColors.uncolored) { + continue; + } + this.displaySprites[colorId] = Loader.getSprite("sprites/wires/display/" + colorId + ".png"); + } + } + + /** + * Returns the color / value a display should show + * @param {BaseItem} value + * @returns {BaseItem} + */ + getDisplayItem(value) { + if (!value) { + return null; + } + + switch (value.getItemType()) { + case "boolean": { + return isTrueItem(value) ? COLOR_ITEM_SINGLETONS[enumColors.white] : null; + } + + case "color": { + const item = /**@type {ColorItem} */ (value); + return item.color === enumColors.uncolored ? null : item; + } + + case "shape": { + return value; + } + + default: + assertAlways(false, "Unknown item type: " + value.getItemType()); + } + } + + /** + * Draws a given chunk + * @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]; + if (entity && entity.components.Display) { + const pinsComp = entity.components.WiredPins; + const network = pinsComp.slots[0].linkedNetwork; + + if (!network || !network.hasValue()) { + continue; + } + + const value = this.getDisplayItem(network.currentValue); + + if (!value) { + continue; + } + + const origin = entity.components.StaticMapEntity.origin; + if (value.getItemType() === "color") { + this.displaySprites[/** @type {ColorItem} */ (value).color].drawCachedCentered( + parameters, + (origin.x + 0.5) * globalConfig.tileSize, + (origin.y + 0.5) * globalConfig.tileSize, + globalConfig.tileSize + ); + } else if (value.getItemType() === "shape") { + value.drawItemCenteredClipped( + (origin.x + 0.5) * globalConfig.tileSize, + (origin.y + 0.5) * globalConfig.tileSize, + parameters, + 30 + ); + } + } + } + } +} diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 06c11b96..89b491af 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -697,6 +697,14 @@ buildings: name: &display Display description: Connect a signal to show it on the display - It can be a shape, color or boolean. + wireless_display: + default: + name: &wireless_display Wireless Display + description: Connect a signal to show it on the display - It can be a shape, color or boolean. + remote_control: + name: OR Gate + description: Emits a boolean "1" if one of the inputs is truthy. (Truthy means shape, color or boolean "1") + reader: default: name: &reader Belt Reader @@ -1145,6 +1153,7 @@ keybindings: filter: *filter wire_tunnel: *wire_tunnel display: *display + wireless_display: *wireless_display reader: *reader virtual_processor: *virtual_processor transistor: *transistor