Add virtual shape processing buildings

This commit is contained in:
tobspr 2020-08-18 20:02:39 +02:00
parent 1ff76e0b2e
commit 296b76bf11
28 changed files with 6929 additions and 6368 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

After

Width:  |  Height:  |  Size: 254 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 567 KiB

After

Width:  |  Height:  |  Size: 601 KiB

View File

@ -277,6 +277,7 @@
<key type="filename">sprites/blueprints/underground_belt_entry.png</key>
<key type="filename">sprites/blueprints/underground_belt_exit-tier2.png</key>
<key type="filename">sprites/blueprints/underground_belt_exit.png</key>
<key type="filename">sprites/blueprints/wire_tunnel-coating.png</key>
<key type="filename">sprites/blueprints/wire_tunnel.png</key>
<key type="filename">sprites/buildings/constant_signal.png</key>
<key type="filename">sprites/buildings/display.png</key>
@ -295,6 +296,7 @@
<key type="filename">sprites/buildings/underground_belt_entry.png</key>
<key type="filename">sprites/buildings/underground_belt_exit-tier2.png</key>
<key type="filename">sprites/buildings/underground_belt_exit.png</key>
<key type="filename">sprites/buildings/wire_tunnel-coating.png</key>
<key type="filename">sprites/buildings/wire_tunnel.png</key>
<key type="filename">sprites/wires/lever_on.png</key>
<key type="filename">sprites/wires/sets/color_cross.png</key>
@ -533,6 +535,8 @@
</struct>
<key type="filename">sprites/wires/boolean_false.png</key>
<key type="filename">sprites/wires/boolean_true.png</key>
<key type="filename">sprites/wires/network_conflict.png</key>
<key type="filename">sprites/wires/network_empty.png</key>
<key type="filename">sprites/wires/wires_preview.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,38 +1,38 @@
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt, wire,
constant_signal, logic_gate, lever, filter, wire_tunnel, display;
@each $building in $buildings {
[data-icon="building_icons/#{$building}.png"] {
background-image: uiResource("res/ui/building_icons/#{$building}.png") !important;
}
}
$buildingsAndVariants: belt, splitter, splitter-compact, splitter-compact-inverse, underground_belt,
underground_belt-tier2, miner, miner-chainable, cutter, cutter-quad, rotater, rotater-ccw, rotater-fl,
stacker, mixer, painter, painter-double, painter-quad, trash, trash-storage;
@each $building in $buildingsAndVariants {
[data-icon="building_tutorials/#{$building}.png"] {
background-image: uiResource("res/ui/building_tutorials/#{$building}.png") !important;
}
}
// Special case
[data-icon="building_tutorials/painter-mirrored.png"] {
background-image: uiResource("res/ui/building_tutorials/painter.png") !important;
}
$icons: notification_saved, notification_success, notification_upgrade;
@each $icon in $icons {
[data-icon="icons/#{$icon}.png"] {
background-image: uiResource("res/ui/icons/#{$icon}.png") !important;
}
}
$languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi,
th, hu, pl, ja, kor, no, pt-PT;
@each $language in $languages {
[data-languageicon="#{$language}"] {
background-image: uiResource("languages/#{$language}.svg") !important;
}
}
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt, wire,
constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor;
@each $building in $buildings {
[data-icon="building_icons/#{$building}.png"] {
background-image: uiResource("res/ui/building_icons/#{$building}.png") !important;
}
}
$buildingsAndVariants: belt, splitter, splitter-compact, splitter-compact-inverse, underground_belt,
underground_belt-tier2, miner, miner-chainable, cutter, cutter-quad, rotater, rotater-ccw, rotater-fl,
stacker, mixer, painter, painter-double, painter-quad, trash, trash-storage;
@each $building in $buildingsAndVariants {
[data-icon="building_tutorials/#{$building}.png"] {
background-image: uiResource("res/ui/building_tutorials/#{$building}.png") !important;
}
}
// Special case
[data-icon="building_tutorials/painter-mirrored.png"] {
background-image: uiResource("res/ui/building_tutorials/painter.png") !important;
}
$icons: notification_saved, notification_success, notification_upgrade;
@each $icon in $icons {
[data-icon="icons/#{$icon}.png"] {
background-image: uiResource("res/ui/icons/#{$icon}.png") !important;
}
}
$languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi,
th, hu, pl, ja, kor, no, pt-PT;
@each $language in $languages {
[data-languageicon="#{$language}"] {
background-image: uiResource("languages/#{$language}.svg") !important;
}
}

View File

@ -0,0 +1,151 @@
import { Vector, enumDirection } from "../../core/vector";
import { LogicGateComponent, enumLogicGateType } from "../components/logic_gate";
import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins";
import { Entity } from "../entity";
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
import { GameRoot } from "../root";
/** @enum {string} */
export const enumVirtualProcessorVariants = {
analyzer: "analyzer",
rotater: "rotater",
unstacker: "unstacker",
shapecompare: "shapecompare",
};
/** @enum {string} */
export const enumVariantToGate = {
[defaultBuildingVariant]: enumLogicGateType.cutter,
[enumVirtualProcessorVariants.analyzer]: enumLogicGateType.analyzer,
[enumVirtualProcessorVariants.rotater]: enumLogicGateType.rotater,
[enumVirtualProcessorVariants.unstacker]: enumLogicGateType.unstacker,
[enumVirtualProcessorVariants.shapecompare]: enumLogicGateType.shapecompare,
};
export class MetaVirtualProcessorBuilding extends MetaBuilding {
constructor() {
super("virtual_processor");
}
getSilhouetteColor() {
return "#823cab";
}
/**
* @param {GameRoot} root
*/
getIsUnlocked(root) {
// @todo
return true;
}
/** @returns {"wires"} **/
getLayer() {
return "wires";
}
getDimensions() {
return new Vector(1, 1);
}
getAvailableVariants() {
return [
defaultBuildingVariant,
enumVirtualProcessorVariants.rotater,
enumVirtualProcessorVariants.unstacker,
enumVirtualProcessorVariants.analyzer,
enumVirtualProcessorVariants.shapecompare,
];
}
getRenderPins() {
// We already have it included
return false;
}
/**
*
* @param {Entity} entity
* @param {number} rotationVariant
*/
updateVariants(entity, rotationVariant, variant) {
const gateType = enumVariantToGate[variant];
entity.components.LogicGate.type = gateType;
const pinComp = entity.components.WiredPins;
switch (gateType) {
case enumLogicGateType.cutter:
case enumLogicGateType.analyzer:
case enumLogicGateType.unstacker: {
pinComp.setSlots([
{
pos: new Vector(0, 0),
direction: enumDirection.left,
type: enumPinSlotType.logicalEjector,
},
{
pos: new Vector(0, 0),
direction: enumDirection.right,
type: enumPinSlotType.logicalEjector,
},
{
pos: new Vector(0, 0),
direction: enumDirection.bottom,
type: enumPinSlotType.logicalAcceptor,
},
]);
break;
}
case enumLogicGateType.rotater: {
pinComp.setSlots([
{
pos: new Vector(0, 0),
direction: enumDirection.top,
type: enumPinSlotType.logicalEjector,
},
{
pos: new Vector(0, 0),
direction: enumDirection.bottom,
type: enumPinSlotType.logicalAcceptor,
},
]);
break;
}
case enumLogicGateType.shapecompare: {
pinComp.setSlots([
{
pos: new Vector(0, 0),
direction: enumDirection.top,
type: enumPinSlotType.logicalEjector,
},
{
pos: new Vector(0, 0),
direction: enumDirection.left,
type: enumPinSlotType.logicalAcceptor,
},
{
pos: new Vector(0, 0),
direction: enumDirection.right,
type: enumPinSlotType.logicalAcceptor,
},
]);
break;
}
default:
assertAlways("unknown logic gate type: " + gateType);
}
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new WiredPinsComponent({
slots: [],
})
);
entity.addComponent(new LogicGateComponent({}));
}
}

View File

@ -1,30 +1,36 @@
import { Component } from "../component";
/** @enum {string} */
export const enumLogicGateType = {
and: "and",
not: "not",
xor: "xor",
or: "or",
transistor: "transistor",
};
export class LogicGateComponent extends Component {
static getId() {
return "LogicGate";
}
duplicateWithoutContents() {
return new LogicGateComponent({ type: this.type });
}
/**
*
* @param {object} param0
* @param {enumLogicGateType=} param0.type
*/
constructor({ type = enumLogicGateType.and }) {
super();
this.type = type;
}
}
import { Component } from "../component";
/** @enum {string} */
export const enumLogicGateType = {
and: "and",
not: "not",
xor: "xor",
or: "or",
transistor: "transistor",
analyzer: "analyzer",
rotater: "rotater",
unstacker: "unstacker",
cutter: "cutter",
shapecompare: "shapecompare",
};
export class LogicGateComponent extends Component {
static getId() {
return "LogicGate";
}
duplicateWithoutContents() {
return new LogicGateComponent({ type: this.type });
}
/**
*
* @param {object} param0
* @param {enumLogicGateType=} param0.type
*/
constructor({ type = enumLogicGateType.and }) {
super();
this.type = type;
}
}

View File

@ -1,25 +1,27 @@
import { HUDBaseToolbar } from "./base_toolbar";
import { MetaWireBuilding } from "../../buildings/wire";
import { MetaConstantSignalBuilding } from "../../buildings/constant_signal";
import { MetaLogicGateBuilding } from "../../buildings/logic_gate";
import { MetaLeverBuilding } from "../../buildings/lever";
import { MetaWireTunnelBuilding } from "../../buildings/wire_tunnel";
const supportedBuildings = [
MetaWireBuilding,
MetaWireTunnelBuilding,
MetaConstantSignalBuilding,
MetaLogicGateBuilding,
MetaLeverBuilding,
];
export class HUDWiresToolbar extends HUDBaseToolbar {
constructor(root) {
super(root, {
supportedBuildings,
visibilityCondition: () =>
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires",
htmlElementId: "ingame_HUD_wires_toolbar",
});
}
}
import { HUDBaseToolbar } from "./base_toolbar";
import { MetaWireBuilding } from "../../buildings/wire";
import { MetaConstantSignalBuilding } from "../../buildings/constant_signal";
import { MetaLogicGateBuilding } from "../../buildings/logic_gate";
import { MetaLeverBuilding } from "../../buildings/lever";
import { MetaWireTunnelBuilding } from "../../buildings/wire_tunnel";
import { MetaVirtualProcessorBuilding } from "../../buildings/virtual_processor";
const supportedBuildings = [
MetaWireBuilding,
MetaWireTunnelBuilding,
MetaConstantSignalBuilding,
MetaLogicGateBuilding,
MetaLeverBuilding,
MetaVirtualProcessorBuilding,
];
export class HUDWiresToolbar extends HUDBaseToolbar {
constructor(root) {
super(root, {
supportedBuildings,
visibilityCondition: () =>
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires",
htmlElementId: "ingame_HUD_wires_toolbar",
});
}
}

View File

@ -1,456 +1,457 @@
/* typehints:start */
import { GameRoot } from "./root";
import { InputReceiver } from "../core/input_receiver";
import { Application } from "../application";
/* typehints:end */
import { Signal, STOP_PROPAGATION } from "../core/signal";
import { IS_MOBILE } from "../core/config";
import { T } from "../translations";
function key(str) {
return str.toUpperCase().charCodeAt(0);
}
export const KEYMAPPINGS = {
general: {
confirm: { keyCode: 13 }, // enter
back: { keyCode: 27, builtin: true }, // escape
},
ingame: {
menuOpenShop: { keyCode: key("F") },
menuOpenStats: { keyCode: key("G") },
menuClose: { keyCode: key("Q") },
toggleHud: { keyCode: 113 }, // F2
exportScreenshot: { keyCode: 114 }, // F3PS
toggleFPSInfo: { keyCode: 115 }, // F4
switchLayers: { keyCode: key("Y") },
},
navigation: {
mapMoveUp: { keyCode: key("W") },
mapMoveRight: { keyCode: key("D") },
mapMoveDown: { keyCode: key("S") },
mapMoveLeft: { keyCode: key("A") },
mapMoveFaster: { keyCode: 16 }, //shift
centerMap: { keyCode: 32 }, // SPACE
mapZoomIn: { keyCode: 187, repeated: true }, // "+"
mapZoomOut: { keyCode: 189, repeated: true }, // "-"
createMarker: { keyCode: key("M") },
},
buildings: {
belt: { keyCode: key("1") },
splitter: { keyCode: key("2") },
underground_belt: { keyCode: key("3") },
miner: { keyCode: key("4") },
cutter: { keyCode: key("5") },
rotater: { keyCode: key("6") },
stacker: { keyCode: key("7") },
mixer: { keyCode: key("8") },
painter: { keyCode: key("9") },
trash: { keyCode: key("0") },
lever: { keyCode: key("L") },
filter: { keyCode: key("B") },
display: { keyCode: key("N") },
wire: { keyCode: key("1") },
wire_tunnel: { keyCode: key("2") },
constant_signal: { keyCode: key("3") },
logic_gate: { keyCode: key("4") },
},
placement: {
pipette: { keyCode: key("Q") },
rotateWhilePlacing: { keyCode: key("R") },
rotateInverseModifier: { keyCode: 16 }, // SHIFT
cycleBuildingVariants: { keyCode: key("T") },
cycleBuildings: { keyCode: 9 }, // TAB
switchDirectionLockSide: { keyCode: key("R") },
},
massSelect: {
massSelectStart: { keyCode: 17 }, // CTRL
massSelectSelectMultiple: { keyCode: 16 }, // SHIFT
massSelectCopy: { keyCode: key("C") },
massSelectCut: { keyCode: key("X") },
confirmMassDelete: { keyCode: 46 }, // DEL
pasteLastBlueprint: { keyCode: key("V") },
},
placementModifiers: {
lockBeltDirection: { keyCode: 16 }, // SHIFT
placementDisableAutoOrientation: { keyCode: 17 }, // CTRL
placeMultiple: { keyCode: 16 }, // SHIFT
placeInverse: { keyCode: 18 }, // ALT
},
};
// Assign ids
for (const categoryId in KEYMAPPINGS) {
for (const mappingId in KEYMAPPINGS[categoryId]) {
KEYMAPPINGS[categoryId][mappingId].id = mappingId;
}
}
export const KEYCODE_LMB = 1;
export const KEYCODE_MMB = 2;
export const KEYCODE_RMB = 3;
/**
* Returns a keycode -> string
* @param {number} code
* @returns {string}
*/
export function getStringForKeyCode(code) {
switch (code) {
case KEYCODE_LMB:
return "LMB";
case KEYCODE_MMB:
return "MMB";
case KEYCODE_RMB:
return "RMB";
case 4:
return "MB4";
case 5:
return "MB5";
case 8:
return "⌫";
case 9:
return T.global.keys.tab;
case 13:
return "⏎";
case 16:
return "⇪";
case 17:
return T.global.keys.control;
case 18:
return T.global.keys.alt;
case 19:
return "PAUSE";
case 20:
return "CAPS";
case 27:
return T.global.keys.escape;
case 32:
return T.global.keys.space;
case 33:
return "PGUP";
case 34:
return "PGDOWN";
case 35:
return "END";
case 36:
return "HOME";
case 37:
return "⬅";
case 38:
return "⬆";
case 39:
return "➡";
case 40:
return "⬇";
case 44:
return "PRNT";
case 45:
return "INS";
case 46:
return "DEL";
case 93:
return "SEL";
case 96:
return "NUM 0";
case 97:
return "NUM 1";
case 98:
return "NUM 2";
case 99:
return "NUM 3";
case 100:
return "NUM 4";
case 101:
return "NUM 5";
case 102:
return "NUM 6";
case 103:
return "NUM 7";
case 104:
return "NUM 8";
case 105:
return "NUM 9";
case 106:
return "*";
case 107:
return "+";
case 109:
return "-";
case 110:
return ".";
case 111:
return "/";
case 112:
return "F1";
case 113:
return "F2";
case 114:
return "F3";
case 115:
return "F4";
case 116:
return "F4";
case 117:
return "F5";
case 118:
return "F6";
case 119:
return "F7";
case 120:
return "F8";
case 121:
return "F9";
case 122:
return "F10";
case 123:
return "F11";
case 124:
return "F12";
case 144:
return "NUMLOCK";
case 145:
return "SCRLOCK";
case 182:
return "COMP";
case 183:
return "CALC";
case 186:
return ";";
case 187:
return "+";
case 188:
return ",";
case 189:
return "-";
case 191:
return "/";
case 219:
return "[";
case 220:
return "\\";
case 221:
return "]";
case 222:
return "'";
}
return String.fromCharCode(code);
}
export class Keybinding {
/**
*
* @param {KeyActionMapper} keyMapper
* @param {Application} app
* @param {object} param0
* @param {number} param0.keyCode
* @param {boolean=} param0.builtin
* @param {boolean=} param0.repeated
*/
constructor(keyMapper, app, { keyCode, builtin = false, repeated = false }) {
assert(keyCode && Number.isInteger(keyCode), "Invalid key code: " + keyCode);
this.keyMapper = keyMapper;
this.app = app;
this.keyCode = keyCode;
this.builtin = builtin;
this.repeated = repeated;
this.signal = new Signal();
this.toggled = new Signal();
}
/**
* Returns whether this binding is currently pressed
* @returns {boolean}
*/
get pressed() {
// Check if the key is down
if (this.app.inputMgr.keysDown.has(this.keyCode)) {
// Check if it is the top reciever
const reciever = this.keyMapper.inputReceiver;
return this.app.inputMgr.getTopReciever() === reciever;
}
return false;
}
/**
* Adds an event listener
* @param {function() : void} receiver
* @param {object=} scope
*/
add(receiver, scope = null) {
this.signal.add(receiver, scope);
}
/**
* @param {Element} elem
* @returns {HTMLElement} the created element, or null if the keybindings are not shown
* */
appendLabelToElement(elem) {
if (IS_MOBILE) {
return null;
}
const spacer = document.createElement("code");
spacer.classList.add("keybinding");
spacer.innerHTML = getStringForKeyCode(this.keyCode);
elem.appendChild(spacer);
return spacer;
}
/**
* Returns the key code as a nice string
*/
getKeyCodeString() {
return getStringForKeyCode(this.keyCode);
}
/**
* Remvoes all signal receivers
*/
clearSignalReceivers() {
this.signal.removeAll();
}
}
export class KeyActionMapper {
/**
*
* @param {GameRoot} root
* @param {InputReceiver} inputReciever
*/
constructor(root, inputReciever) {
this.root = root;
this.inputReceiver = inputReciever;
inputReciever.keydown.add(this.handleKeydown, this);
inputReciever.keyup.add(this.handleKeyup, this);
/** @type {Object.<string, Keybinding>} */
this.keybindings = {};
const overrides = root.app.settings.getKeybindingOverrides();
for (const category in KEYMAPPINGS) {
for (const key in KEYMAPPINGS[category]) {
let payload = Object.assign({}, KEYMAPPINGS[category][key]);
if (overrides[key]) {
payload.keyCode = overrides[key];
}
this.keybindings[key] = new Keybinding(this, this.root.app, payload);
}
}
inputReciever.pageBlur.add(this.onPageBlur, this);
inputReciever.destroyed.add(this.cleanup, this);
}
/**
* Returns all keybindings starting with the given id
* @param {string} pattern
* @returns {Array<Keybinding>}
*/
getKeybindingsStartingWith(pattern) {
let result = [];
for (const key in this.keybindings) {
if (key.startsWith(pattern)) {
result.push(this.keybindings[key]);
}
}
return result;
}
/**
* Forwards the given events to the other mapper (used in tooltips)
* @param {KeyActionMapper} receiver
* @param {Array<string>} bindings
*/
forward(receiver, bindings) {
for (let i = 0; i < bindings.length; ++i) {
const key = bindings[i];
this.keybindings[key].signal.add((...args) => receiver.keybindings[key].signal.dispatch(...args));
}
}
cleanup() {
for (const key in this.keybindings) {
this.keybindings[key].signal.removeAll();
}
}
onPageBlur() {
// Reset all down states
// Find mapping
for (const key in this.keybindings) {
/** @type {Keybinding} */
const binding = this.keybindings[key];
}
}
/**
* Internal keydown handler
* @param {object} param0
* @param {number} param0.keyCode
* @param {boolean} param0.shift
* @param {boolean} param0.alt
* @param {boolean=} param0.initial
*/
handleKeydown({ keyCode, shift, alt, initial }) {
let stop = false;
// Find mapping
for (const key in this.keybindings) {
/** @type {Keybinding} */
const binding = this.keybindings[key];
if (binding.keyCode === keyCode && (initial || binding.repeated)) {
/** @type {Signal} */
const signal = this.keybindings[key].signal;
if (signal.dispatch() === STOP_PROPAGATION) {
return;
}
}
}
if (stop) {
return STOP_PROPAGATION;
}
}
/**
* Internal keyup handler
* @param {object} param0
* @param {number} param0.keyCode
* @param {boolean} param0.shift
* @param {boolean} param0.alt
*/
handleKeyup({ keyCode, shift, alt }) {
// Empty
}
/**
* Returns a given keybinding
* @param {{ keyCode: number }} binding
* @returns {Keybinding}
*/
getBinding(binding) {
// @ts-ignore
const id = binding.id;
assert(id, "Not a valid keybinding: " + JSON.stringify(binding));
assert(this.keybindings[id], "Keybinding " + id + " not known!");
return this.keybindings[id];
}
}
/* typehints:start */
import { GameRoot } from "./root";
import { InputReceiver } from "../core/input_receiver";
import { Application } from "../application";
/* typehints:end */
import { Signal, STOP_PROPAGATION } from "../core/signal";
import { IS_MOBILE } from "../core/config";
import { T } from "../translations";
function key(str) {
return str.toUpperCase().charCodeAt(0);
}
export const KEYMAPPINGS = {
general: {
confirm: { keyCode: 13 }, // enter
back: { keyCode: 27, builtin: true }, // escape
},
ingame: {
menuOpenShop: { keyCode: key("F") },
menuOpenStats: { keyCode: key("G") },
menuClose: { keyCode: key("Q") },
toggleHud: { keyCode: 113 }, // F2
exportScreenshot: { keyCode: 114 }, // F3PS
toggleFPSInfo: { keyCode: 115 }, // F4
switchLayers: { keyCode: key("Y") },
},
navigation: {
mapMoveUp: { keyCode: key("W") },
mapMoveRight: { keyCode: key("D") },
mapMoveDown: { keyCode: key("S") },
mapMoveLeft: { keyCode: key("A") },
mapMoveFaster: { keyCode: 16 }, //shift
centerMap: { keyCode: 32 }, // SPACE
mapZoomIn: { keyCode: 187, repeated: true }, // "+"
mapZoomOut: { keyCode: 189, repeated: true }, // "-"
createMarker: { keyCode: key("M") },
},
buildings: {
belt: { keyCode: key("1") },
splitter: { keyCode: key("2") },
underground_belt: { keyCode: key("3") },
miner: { keyCode: key("4") },
cutter: { keyCode: key("5") },
rotater: { keyCode: key("6") },
stacker: { keyCode: key("7") },
mixer: { keyCode: key("8") },
painter: { keyCode: key("9") },
trash: { keyCode: key("0") },
lever: { keyCode: key("L") },
filter: { keyCode: key("B") },
display: { keyCode: key("N") },
wire: { keyCode: key("1") },
wire_tunnel: { keyCode: key("2") },
constant_signal: { keyCode: key("3") },
logic_gate: { keyCode: key("4") },
virtual_processor: { keyCode: key("5") },
},
placement: {
pipette: { keyCode: key("Q") },
rotateWhilePlacing: { keyCode: key("R") },
rotateInverseModifier: { keyCode: 16 }, // SHIFT
cycleBuildingVariants: { keyCode: key("T") },
cycleBuildings: { keyCode: 9 }, // TAB
switchDirectionLockSide: { keyCode: key("R") },
},
massSelect: {
massSelectStart: { keyCode: 17 }, // CTRL
massSelectSelectMultiple: { keyCode: 16 }, // SHIFT
massSelectCopy: { keyCode: key("C") },
massSelectCut: { keyCode: key("X") },
confirmMassDelete: { keyCode: 46 }, // DEL
pasteLastBlueprint: { keyCode: key("V") },
},
placementModifiers: {
lockBeltDirection: { keyCode: 16 }, // SHIFT
placementDisableAutoOrientation: { keyCode: 17 }, // CTRL
placeMultiple: { keyCode: 16 }, // SHIFT
placeInverse: { keyCode: 18 }, // ALT
},
};
// Assign ids
for (const categoryId in KEYMAPPINGS) {
for (const mappingId in KEYMAPPINGS[categoryId]) {
KEYMAPPINGS[categoryId][mappingId].id = mappingId;
}
}
export const KEYCODE_LMB = 1;
export const KEYCODE_MMB = 2;
export const KEYCODE_RMB = 3;
/**
* Returns a keycode -> string
* @param {number} code
* @returns {string}
*/
export function getStringForKeyCode(code) {
switch (code) {
case KEYCODE_LMB:
return "LMB";
case KEYCODE_MMB:
return "MMB";
case KEYCODE_RMB:
return "RMB";
case 4:
return "MB4";
case 5:
return "MB5";
case 8:
return "⌫";
case 9:
return T.global.keys.tab;
case 13:
return "⏎";
case 16:
return "⇪";
case 17:
return T.global.keys.control;
case 18:
return T.global.keys.alt;
case 19:
return "PAUSE";
case 20:
return "CAPS";
case 27:
return T.global.keys.escape;
case 32:
return T.global.keys.space;
case 33:
return "PGUP";
case 34:
return "PGDOWN";
case 35:
return "END";
case 36:
return "HOME";
case 37:
return "⬅";
case 38:
return "⬆";
case 39:
return "➡";
case 40:
return "⬇";
case 44:
return "PRNT";
case 45:
return "INS";
case 46:
return "DEL";
case 93:
return "SEL";
case 96:
return "NUM 0";
case 97:
return "NUM 1";
case 98:
return "NUM 2";
case 99:
return "NUM 3";
case 100:
return "NUM 4";
case 101:
return "NUM 5";
case 102:
return "NUM 6";
case 103:
return "NUM 7";
case 104:
return "NUM 8";
case 105:
return "NUM 9";
case 106:
return "*";
case 107:
return "+";
case 109:
return "-";
case 110:
return ".";
case 111:
return "/";
case 112:
return "F1";
case 113:
return "F2";
case 114:
return "F3";
case 115:
return "F4";
case 116:
return "F4";
case 117:
return "F5";
case 118:
return "F6";
case 119:
return "F7";
case 120:
return "F8";
case 121:
return "F9";
case 122:
return "F10";
case 123:
return "F11";
case 124:
return "F12";
case 144:
return "NUMLOCK";
case 145:
return "SCRLOCK";
case 182:
return "COMP";
case 183:
return "CALC";
case 186:
return ";";
case 187:
return "+";
case 188:
return ",";
case 189:
return "-";
case 191:
return "/";
case 219:
return "[";
case 220:
return "\\";
case 221:
return "]";
case 222:
return "'";
}
return String.fromCharCode(code);
}
export class Keybinding {
/**
*
* @param {KeyActionMapper} keyMapper
* @param {Application} app
* @param {object} param0
* @param {number} param0.keyCode
* @param {boolean=} param0.builtin
* @param {boolean=} param0.repeated
*/
constructor(keyMapper, app, { keyCode, builtin = false, repeated = false }) {
assert(keyCode && Number.isInteger(keyCode), "Invalid key code: " + keyCode);
this.keyMapper = keyMapper;
this.app = app;
this.keyCode = keyCode;
this.builtin = builtin;
this.repeated = repeated;
this.signal = new Signal();
this.toggled = new Signal();
}
/**
* Returns whether this binding is currently pressed
* @returns {boolean}
*/
get pressed() {
// Check if the key is down
if (this.app.inputMgr.keysDown.has(this.keyCode)) {
// Check if it is the top reciever
const reciever = this.keyMapper.inputReceiver;
return this.app.inputMgr.getTopReciever() === reciever;
}
return false;
}
/**
* Adds an event listener
* @param {function() : void} receiver
* @param {object=} scope
*/
add(receiver, scope = null) {
this.signal.add(receiver, scope);
}
/**
* @param {Element} elem
* @returns {HTMLElement} the created element, or null if the keybindings are not shown
* */
appendLabelToElement(elem) {
if (IS_MOBILE) {
return null;
}
const spacer = document.createElement("code");
spacer.classList.add("keybinding");
spacer.innerHTML = getStringForKeyCode(this.keyCode);
elem.appendChild(spacer);
return spacer;
}
/**
* Returns the key code as a nice string
*/
getKeyCodeString() {
return getStringForKeyCode(this.keyCode);
}
/**
* Remvoes all signal receivers
*/
clearSignalReceivers() {
this.signal.removeAll();
}
}
export class KeyActionMapper {
/**
*
* @param {GameRoot} root
* @param {InputReceiver} inputReciever
*/
constructor(root, inputReciever) {
this.root = root;
this.inputReceiver = inputReciever;
inputReciever.keydown.add(this.handleKeydown, this);
inputReciever.keyup.add(this.handleKeyup, this);
/** @type {Object.<string, Keybinding>} */
this.keybindings = {};
const overrides = root.app.settings.getKeybindingOverrides();
for (const category in KEYMAPPINGS) {
for (const key in KEYMAPPINGS[category]) {
let payload = Object.assign({}, KEYMAPPINGS[category][key]);
if (overrides[key]) {
payload.keyCode = overrides[key];
}
this.keybindings[key] = new Keybinding(this, this.root.app, payload);
}
}
inputReciever.pageBlur.add(this.onPageBlur, this);
inputReciever.destroyed.add(this.cleanup, this);
}
/**
* Returns all keybindings starting with the given id
* @param {string} pattern
* @returns {Array<Keybinding>}
*/
getKeybindingsStartingWith(pattern) {
let result = [];
for (const key in this.keybindings) {
if (key.startsWith(pattern)) {
result.push(this.keybindings[key]);
}
}
return result;
}
/**
* Forwards the given events to the other mapper (used in tooltips)
* @param {KeyActionMapper} receiver
* @param {Array<string>} bindings
*/
forward(receiver, bindings) {
for (let i = 0; i < bindings.length; ++i) {
const key = bindings[i];
this.keybindings[key].signal.add((...args) => receiver.keybindings[key].signal.dispatch(...args));
}
}
cleanup() {
for (const key in this.keybindings) {
this.keybindings[key].signal.removeAll();
}
}
onPageBlur() {
// Reset all down states
// Find mapping
for (const key in this.keybindings) {
/** @type {Keybinding} */
const binding = this.keybindings[key];
}
}
/**
* Internal keydown handler
* @param {object} param0
* @param {number} param0.keyCode
* @param {boolean} param0.shift
* @param {boolean} param0.alt
* @param {boolean=} param0.initial
*/
handleKeydown({ keyCode, shift, alt, initial }) {
let stop = false;
// Find mapping
for (const key in this.keybindings) {
/** @type {Keybinding} */
const binding = this.keybindings[key];
if (binding.keyCode === keyCode && (initial || binding.repeated)) {
/** @type {Signal} */
const signal = this.keybindings[key].signal;
if (signal.dispatch() === STOP_PROPAGATION) {
return;
}
}
}
if (stop) {
return STOP_PROPAGATION;
}
}
/**
* Internal keyup handler
* @param {object} param0
* @param {number} param0.keyCode
* @param {boolean} param0.shift
* @param {boolean} param0.alt
*/
handleKeyup({ keyCode, shift, alt }) {
// Empty
}
/**
* Returns a given keybinding
* @param {{ keyCode: number }} binding
* @returns {Keybinding}
*/
getBinding(binding) {
// @ts-ignore
const id = binding.id;
assert(id, "Not a valid keybinding: " + JSON.stringify(binding));
assert(this.keybindings[id], "Keybinding " + id + " not known!");
return this.keybindings[id];
}
}

View File

@ -1,162 +1,171 @@
import { gMetaBuildingRegistry } from "../core/global_registries";
import { createLogger } from "../core/logging";
import { MetaBeltBuilding } from "./buildings/belt";
import { MetaBeltBaseBuilding } from "./buildings/belt_base";
import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter";
import { MetaHubBuilding } from "./buildings/hub";
import { enumMinerVariants, MetaMinerBuilding } from "./buildings/miner";
import { MetaMixerBuilding } from "./buildings/mixer";
import { enumPainterVariants, MetaPainterBuilding } from "./buildings/painter";
import { enumRotaterVariants, MetaRotaterBuilding } from "./buildings/rotater";
import { enumSplitterVariants, MetaSplitterBuilding } from "./buildings/splitter";
import { MetaStackerBuilding } from "./buildings/stacker";
import { enumTrashVariants, MetaTrashBuilding } from "./buildings/trash";
import { enumUndergroundBeltVariants, MetaUndergroundBeltBuilding } from "./buildings/underground_belt";
import { MetaWireBuilding } from "./buildings/wire";
import { gBuildingVariants, registerBuildingVariant } from "./building_codes";
import { defaultBuildingVariant } from "./meta_building";
import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
import { MetaLogicGateBuilding, enumLogicGateVariants } from "./buildings/logic_gate";
import { MetaLeverBuilding } from "./buildings/lever";
import { MetaFilterBuilding } from "./buildings/filter";
import { MetaWireTunnelBuilding, enumWireTunnelVariants } from "./buildings/wire_tunnel";
import { MetaDisplayBuilding } from "./buildings/display";
const logger = createLogger("building_registry");
export function initMetaBuildingRegistry() {
gMetaBuildingRegistry.register(MetaSplitterBuilding);
gMetaBuildingRegistry.register(MetaMinerBuilding);
gMetaBuildingRegistry.register(MetaCutterBuilding);
gMetaBuildingRegistry.register(MetaRotaterBuilding);
gMetaBuildingRegistry.register(MetaStackerBuilding);
gMetaBuildingRegistry.register(MetaMixerBuilding);
gMetaBuildingRegistry.register(MetaPainterBuilding);
gMetaBuildingRegistry.register(MetaTrashBuilding);
gMetaBuildingRegistry.register(MetaBeltBuilding);
gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding);
gMetaBuildingRegistry.register(MetaHubBuilding);
gMetaBuildingRegistry.register(MetaWireBuilding);
gMetaBuildingRegistry.register(MetaConstantSignalBuilding);
gMetaBuildingRegistry.register(MetaLogicGateBuilding);
gMetaBuildingRegistry.register(MetaLeverBuilding);
gMetaBuildingRegistry.register(MetaFilterBuilding);
gMetaBuildingRegistry.register(MetaWireTunnelBuilding);
gMetaBuildingRegistry.register(MetaDisplayBuilding);
// Belt
registerBuildingVariant(1, MetaBeltBaseBuilding, defaultBuildingVariant, 0);
registerBuildingVariant(2, MetaBeltBaseBuilding, defaultBuildingVariant, 1);
registerBuildingVariant(3, MetaBeltBaseBuilding, defaultBuildingVariant, 2);
// Splitter
registerBuildingVariant(4, MetaSplitterBuilding);
registerBuildingVariant(5, MetaSplitterBuilding, enumSplitterVariants.compact);
registerBuildingVariant(6, MetaSplitterBuilding, enumSplitterVariants.compactInverse);
// Miner
registerBuildingVariant(7, MetaMinerBuilding);
registerBuildingVariant(8, MetaMinerBuilding, enumMinerVariants.chainable);
// Cutter
registerBuildingVariant(9, MetaCutterBuilding);
registerBuildingVariant(10, MetaCutterBuilding, enumCutterVariants.quad);
// Rotater
registerBuildingVariant(11, MetaRotaterBuilding);
registerBuildingVariant(12, MetaRotaterBuilding, enumRotaterVariants.ccw);
registerBuildingVariant(13, MetaRotaterBuilding, enumRotaterVariants.fl);
// Stacker
registerBuildingVariant(14, MetaStackerBuilding);
// Mixer
registerBuildingVariant(15, MetaMixerBuilding);
// Painter
registerBuildingVariant(16, MetaPainterBuilding);
registerBuildingVariant(17, MetaPainterBuilding, enumPainterVariants.mirrored);
registerBuildingVariant(18, MetaPainterBuilding, enumPainterVariants.double);
registerBuildingVariant(19, MetaPainterBuilding, enumPainterVariants.quad);
// Trash
registerBuildingVariant(20, MetaTrashBuilding);
registerBuildingVariant(21, MetaTrashBuilding, enumTrashVariants.storage);
// Underground belt
registerBuildingVariant(22, MetaUndergroundBeltBuilding, defaultBuildingVariant, 0);
registerBuildingVariant(23, MetaUndergroundBeltBuilding, defaultBuildingVariant, 1);
registerBuildingVariant(24, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 0);
registerBuildingVariant(25, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 1);
// Hub
registerBuildingVariant(26, MetaHubBuilding);
// Wire
registerBuildingVariant(27, MetaWireBuilding, defaultBuildingVariant, 0);
registerBuildingVariant(28, MetaWireBuilding, defaultBuildingVariant, 1);
registerBuildingVariant(29, MetaWireBuilding, defaultBuildingVariant, 2);
registerBuildingVariant(30, MetaWireBuilding, defaultBuildingVariant, 3);
// Constant signal
registerBuildingVariant(31, MetaConstantSignalBuilding);
// Logic gate
registerBuildingVariant(32, MetaLogicGateBuilding);
registerBuildingVariant(34, MetaLogicGateBuilding, enumLogicGateVariants.not);
registerBuildingVariant(35, MetaLogicGateBuilding, enumLogicGateVariants.xor);
registerBuildingVariant(36, MetaLogicGateBuilding, enumLogicGateVariants.or);
registerBuildingVariant(38, MetaLogicGateBuilding, enumLogicGateVariants.transistor);
// Lever
registerBuildingVariant(33, MetaLeverBuilding);
// Filter
registerBuildingVariant(37, MetaFilterBuilding);
// Wire tunnel
registerBuildingVariant(39, MetaWireTunnelBuilding);
registerBuildingVariant(41, MetaWireTunnelBuilding, enumWireTunnelVariants.coating);
// Display
registerBuildingVariant(40, MetaDisplayBuilding);
// Propagate instances
for (const key in gBuildingVariants) {
gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass(
gBuildingVariants[key].metaClass
);
}
for (const key in gBuildingVariants) {
const variant = gBuildingVariants[key];
assert(variant.metaClass, "Variant has no meta: " + key);
if (typeof variant.rotationVariant === "undefined") {
variant.rotationVariant = 0;
}
if (typeof variant.variant === "undefined") {
variant.variant = defaultBuildingVariant;
}
}
logger.log("Registered", gMetaBuildingRegistry.getNumEntries(), "buildings");
logger.log("Registered", Object.keys(gBuildingVariants).length, "building codes");
}
/**
* Once all sprites are loaded, propagates the cache
*/
export function initBuildingCodesAfterResourcesLoaded() {
logger.log("Propagating sprite cache");
for (const key in gBuildingVariants) {
const variant = gBuildingVariants[key];
variant.sprite = variant.metaInstance.getSprite(variant.rotationVariant, variant.variant);
variant.blueprintSprite = variant.metaInstance.getBlueprintSprite(
variant.rotationVariant,
variant.variant
);
variant.silhouetteColor = variant.metaInstance.getSilhouetteColor();
}
}
import { gMetaBuildingRegistry } from "../core/global_registries";
import { createLogger } from "../core/logging";
import { MetaBeltBuilding } from "./buildings/belt";
import { MetaBeltBaseBuilding } from "./buildings/belt_base";
import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter";
import { MetaHubBuilding } from "./buildings/hub";
import { enumMinerVariants, MetaMinerBuilding } from "./buildings/miner";
import { MetaMixerBuilding } from "./buildings/mixer";
import { enumPainterVariants, MetaPainterBuilding } from "./buildings/painter";
import { enumRotaterVariants, MetaRotaterBuilding } from "./buildings/rotater";
import { enumSplitterVariants, MetaSplitterBuilding } from "./buildings/splitter";
import { MetaStackerBuilding } from "./buildings/stacker";
import { enumTrashVariants, MetaTrashBuilding } from "./buildings/trash";
import { enumUndergroundBeltVariants, MetaUndergroundBeltBuilding } from "./buildings/underground_belt";
import { MetaWireBuilding } from "./buildings/wire";
import { gBuildingVariants, registerBuildingVariant } from "./building_codes";
import { defaultBuildingVariant } from "./meta_building";
import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
import { MetaLogicGateBuilding, enumLogicGateVariants } from "./buildings/logic_gate";
import { MetaLeverBuilding } from "./buildings/lever";
import { MetaFilterBuilding } from "./buildings/filter";
import { MetaWireTunnelBuilding, enumWireTunnelVariants } from "./buildings/wire_tunnel";
import { MetaDisplayBuilding } from "./buildings/display";
import { MetaVirtualProcessorBuilding, enumVirtualProcessorVariants } from "./buildings/virtual_processor";
const logger = createLogger("building_registry");
export function initMetaBuildingRegistry() {
gMetaBuildingRegistry.register(MetaSplitterBuilding);
gMetaBuildingRegistry.register(MetaMinerBuilding);
gMetaBuildingRegistry.register(MetaCutterBuilding);
gMetaBuildingRegistry.register(MetaRotaterBuilding);
gMetaBuildingRegistry.register(MetaStackerBuilding);
gMetaBuildingRegistry.register(MetaMixerBuilding);
gMetaBuildingRegistry.register(MetaPainterBuilding);
gMetaBuildingRegistry.register(MetaTrashBuilding);
gMetaBuildingRegistry.register(MetaBeltBuilding);
gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding);
gMetaBuildingRegistry.register(MetaHubBuilding);
gMetaBuildingRegistry.register(MetaWireBuilding);
gMetaBuildingRegistry.register(MetaConstantSignalBuilding);
gMetaBuildingRegistry.register(MetaLogicGateBuilding);
gMetaBuildingRegistry.register(MetaLeverBuilding);
gMetaBuildingRegistry.register(MetaFilterBuilding);
gMetaBuildingRegistry.register(MetaWireTunnelBuilding);
gMetaBuildingRegistry.register(MetaDisplayBuilding);
gMetaBuildingRegistry.register(MetaVirtualProcessorBuilding);
// Belt
registerBuildingVariant(1, MetaBeltBaseBuilding, defaultBuildingVariant, 0);
registerBuildingVariant(2, MetaBeltBaseBuilding, defaultBuildingVariant, 1);
registerBuildingVariant(3, MetaBeltBaseBuilding, defaultBuildingVariant, 2);
// Splitter
registerBuildingVariant(4, MetaSplitterBuilding);
registerBuildingVariant(5, MetaSplitterBuilding, enumSplitterVariants.compact);
registerBuildingVariant(6, MetaSplitterBuilding, enumSplitterVariants.compactInverse);
// Miner
registerBuildingVariant(7, MetaMinerBuilding);
registerBuildingVariant(8, MetaMinerBuilding, enumMinerVariants.chainable);
// Cutter
registerBuildingVariant(9, MetaCutterBuilding);
registerBuildingVariant(10, MetaCutterBuilding, enumCutterVariants.quad);
// Rotater
registerBuildingVariant(11, MetaRotaterBuilding);
registerBuildingVariant(12, MetaRotaterBuilding, enumRotaterVariants.ccw);
registerBuildingVariant(13, MetaRotaterBuilding, enumRotaterVariants.fl);
// Stacker
registerBuildingVariant(14, MetaStackerBuilding);
// Mixer
registerBuildingVariant(15, MetaMixerBuilding);
// Painter
registerBuildingVariant(16, MetaPainterBuilding);
registerBuildingVariant(17, MetaPainterBuilding, enumPainterVariants.mirrored);
registerBuildingVariant(18, MetaPainterBuilding, enumPainterVariants.double);
registerBuildingVariant(19, MetaPainterBuilding, enumPainterVariants.quad);
// Trash
registerBuildingVariant(20, MetaTrashBuilding);
registerBuildingVariant(21, MetaTrashBuilding, enumTrashVariants.storage);
// Underground belt
registerBuildingVariant(22, MetaUndergroundBeltBuilding, defaultBuildingVariant, 0);
registerBuildingVariant(23, MetaUndergroundBeltBuilding, defaultBuildingVariant, 1);
registerBuildingVariant(24, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 0);
registerBuildingVariant(25, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 1);
// Hub
registerBuildingVariant(26, MetaHubBuilding);
// Wire
registerBuildingVariant(27, MetaWireBuilding, defaultBuildingVariant, 0);
registerBuildingVariant(28, MetaWireBuilding, defaultBuildingVariant, 1);
registerBuildingVariant(29, MetaWireBuilding, defaultBuildingVariant, 2);
registerBuildingVariant(30, MetaWireBuilding, defaultBuildingVariant, 3);
// Constant signal
registerBuildingVariant(31, MetaConstantSignalBuilding);
// Logic gate
registerBuildingVariant(32, MetaLogicGateBuilding);
registerBuildingVariant(34, MetaLogicGateBuilding, enumLogicGateVariants.not);
registerBuildingVariant(35, MetaLogicGateBuilding, enumLogicGateVariants.xor);
registerBuildingVariant(36, MetaLogicGateBuilding, enumLogicGateVariants.or);
registerBuildingVariant(38, MetaLogicGateBuilding, enumLogicGateVariants.transistor);
// Lever
registerBuildingVariant(33, MetaLeverBuilding);
// Filter
registerBuildingVariant(37, MetaFilterBuilding);
// Wire tunnel
registerBuildingVariant(39, MetaWireTunnelBuilding);
registerBuildingVariant(41, MetaWireTunnelBuilding, enumWireTunnelVariants.coating);
// Display
registerBuildingVariant(40, MetaDisplayBuilding);
// Virtual Processor
registerBuildingVariant(42, MetaVirtualProcessorBuilding);
registerBuildingVariant(43, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.analyzer);
registerBuildingVariant(44, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.rotater);
registerBuildingVariant(45, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.unstacker);
registerBuildingVariant(46, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.shapecompare);
// Propagate instances
for (const key in gBuildingVariants) {
gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass(
gBuildingVariants[key].metaClass
);
}
for (const key in gBuildingVariants) {
const variant = gBuildingVariants[key];
assert(variant.metaClass, "Variant has no meta: " + key);
if (typeof variant.rotationVariant === "undefined") {
variant.rotationVariant = 0;
}
if (typeof variant.variant === "undefined") {
variant.variant = defaultBuildingVariant;
}
}
logger.log("Registered", gMetaBuildingRegistry.getNumEntries(), "buildings");
logger.log("Registered", Object.keys(gBuildingVariants).length, "building codes");
}
/**
* Once all sprites are loaded, propagates the cache
*/
export function initBuildingCodesAfterResourcesLoaded() {
logger.log("Propagating sprite cache");
for (const key in gBuildingVariants) {
const variant = gBuildingVariants[key];
variant.sprite = variant.metaInstance.getSprite(variant.rotationVariant, variant.variant);
variant.blueprintSprite = variant.metaInstance.getBlueprintSprite(
variant.rotationVariant,
variant.variant
);
variant.silhouetteColor = variant.metaInstance.getSilhouetteColor();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,180 +1,318 @@
import { LogicGateComponent, enumLogicGateType } from "../components/logic_gate";
import { GameSystemWithFilter } from "../game_system_with_filter";
import { BaseItem } from "../base_item";
import { enumPinSlotType } from "../components/wired_pins";
import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON, BooleanItem } from "../items/boolean_item";
import { enumItemProcessorTypes } from "../components/item_processor";
export class LogicGateSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [LogicGateComponent]);
this.boundOperations = {
[enumLogicGateType.and]: this.compute_AND.bind(this),
[enumLogicGateType.not]: this.compute_NOT.bind(this),
[enumLogicGateType.xor]: this.compute_XOR.bind(this),
[enumLogicGateType.or]: this.compute_OR.bind(this),
[enumLogicGateType.transistor]: this.compute_IF.bind(this),
};
}
update() {
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const logicComp = entity.components.LogicGate;
const slotComp = entity.components.WiredPins;
const slotValues = [];
for (let i = 0; i < slotComp.slots.length; ++i) {
const slot = slotComp.slots[i];
if (slot.type !== enumPinSlotType.logicalAcceptor) {
continue;
}
if (slot.linkedNetwork) {
slotValues.push(slot.linkedNetwork.currentValue);
} else {
slotValues.push(null);
}
}
const result = this.boundOperations[logicComp.type](slotValues);
// @TODO: For now we hardcode the value to always be slot 0
assert(
slotValues.length === slotComp.slots.length - 1,
"Bad slot config, should have N acceptor slots and 1 ejector"
);
assert(slotComp.slots[0].type === enumPinSlotType.logicalEjector, "Slot 0 should be ejector");
slotComp.slots[0].value = result;
}
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_AND(parameters) {
assert(parameters.length === 2, "bad parameter count for AND");
const param1 = parameters[0];
const param2 = parameters[1];
if (!param1 || !param2) {
// Not enough params
return BOOL_FALSE_SINGLETON;
}
const itemType = param1.getItemType();
if (itemType !== param2.getItemType()) {
// Differing type
return BOOL_FALSE_SINGLETON;
}
if (itemType === "boolean") {
return /** @type {BooleanItem} */ (param1).value && /** @type {BooleanItem} */ (param2).value
? BOOL_TRUE_SINGLETON
: BOOL_FALSE_SINGLETON;
}
return BOOL_FALSE_SINGLETON;
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_NOT(parameters) {
const item = parameters[0];
if (!item) {
return BOOL_TRUE_SINGLETON;
}
if (item.getItemType() !== "boolean") {
// Not a boolean actually
return BOOL_FALSE_SINGLETON;
}
const value = /** @type {BooleanItem} */ (item).value;
return value ? BOOL_FALSE_SINGLETON : BOOL_TRUE_SINGLETON;
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_XOR(parameters) {
assert(parameters.length === 2, "bad parameter count for XOR");
const param1 = parameters[0];
const param2 = parameters[1];
if (!param1 && !param2) {
// Not enough params
return BOOL_FALSE_SINGLETON;
}
// Check for the right types
if (param1 && param1.getItemType() !== "boolean") {
return BOOL_FALSE_SINGLETON;
}
if (param2 && param2.getItemType() !== "boolean") {
return BOOL_FALSE_SINGLETON;
}
const valueParam1 = param1 ? /** @type {BooleanItem} */ (param1).value : 0;
const valueParam2 = param2 ? /** @type {BooleanItem} */ (param2).value : 0;
return valueParam1 ^ valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_OR(parameters) {
assert(parameters.length === 2, "bad parameter count for OR");
const param1 = parameters[0];
const param2 = parameters[1];
if (!param1 && !param2) {
// Not enough params
return BOOL_FALSE_SINGLETON;
}
const valueParam1 =
param1 && param1.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param1).value : 0;
const valueParam2 =
param2 && param2.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param2).value : 0;
return valueParam1 || valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_IF(parameters) {
assert(parameters.length === 2, "bad parameter count for IF");
const flag = parameters[0];
const value = parameters[1];
if (!flag || !value) {
// Not enough params
return null;
}
if (flag.getItemType() !== "boolean") {
// Flag is not a boolean
return null;
}
// pass through item
if (/** @type {BooleanItem} */ (flag).value) {
return value;
}
return null;
}
}
import { BaseItem } from "../base_item";
import { enumColors } from "../colors";
import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate";
import { enumPinSlotType } from "../components/wired_pins";
import { GameSystemWithFilter } from "../game_system_with_filter";
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, BooleanItem } from "../items/boolean_item";
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
import { ShapeDefinition } from "../shape_definition";
import { ShapeItem } from "../items/shape_item";
export class LogicGateSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [LogicGateComponent]);
this.boundOperations = {
[enumLogicGateType.and]: this.compute_AND.bind(this),
[enumLogicGateType.not]: this.compute_NOT.bind(this),
[enumLogicGateType.xor]: this.compute_XOR.bind(this),
[enumLogicGateType.or]: this.compute_OR.bind(this),
[enumLogicGateType.transistor]: this.compute_IF.bind(this),
[enumLogicGateType.rotater]: this.compute_ROTATE.bind(this),
[enumLogicGateType.analyzer]: this.compute_ANALYZE.bind(this),
[enumLogicGateType.cutter]: this.compute_CUT.bind(this),
[enumLogicGateType.unstacker]: this.compute_UNSTACK.bind(this),
[enumLogicGateType.shapecompare]: this.compute_SHAPECOMPARE.bind(this),
};
}
update() {
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const logicComp = entity.components.LogicGate;
const slotComp = entity.components.WiredPins;
const slotValues = [];
for (let i = 0; i < slotComp.slots.length; ++i) {
const slot = slotComp.slots[i];
if (slot.type !== enumPinSlotType.logicalAcceptor) {
continue;
}
if (slot.linkedNetwork) {
slotValues.push(slot.linkedNetwork.currentValue);
} else {
slotValues.push(null);
}
}
const result = this.boundOperations[logicComp.type](slotValues);
if (Array.isArray(result)) {
let resultIndex = 0;
for (let i = 0; i < slotComp.slots.length; ++i) {
const slot = slotComp.slots[i];
if (slot.type !== enumPinSlotType.logicalEjector) {
continue;
}
slot.value = result[resultIndex++];
}
} else {
// @TODO: For now we hardcode the value to always be slot 0
assert(
slotValues.length === slotComp.slots.length - 1,
"Bad slot config, should have N acceptor slots and 1 ejector"
);
assert(slotComp.slots[0].type === enumPinSlotType.logicalEjector, "Slot 0 should be ejector");
slotComp.slots[0].value = result;
}
}
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_AND(parameters) {
assert(parameters.length === 2, "bad parameter count for AND");
const param1 = parameters[0];
const param2 = parameters[1];
if (!param1 || !param2) {
// Not enough params
return BOOL_FALSE_SINGLETON;
}
const itemType = param1.getItemType();
if (itemType !== param2.getItemType()) {
// Differing type
return BOOL_FALSE_SINGLETON;
}
if (itemType === "boolean") {
return /** @type {BooleanItem} */ (param1).value && /** @type {BooleanItem} */ (param2).value
? BOOL_TRUE_SINGLETON
: BOOL_FALSE_SINGLETON;
}
return BOOL_FALSE_SINGLETON;
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_NOT(parameters) {
const item = parameters[0];
if (!item) {
return BOOL_TRUE_SINGLETON;
}
if (item.getItemType() !== "boolean") {
// Not a boolean actually
return BOOL_FALSE_SINGLETON;
}
const value = /** @type {BooleanItem} */ (item).value;
return value ? BOOL_FALSE_SINGLETON : BOOL_TRUE_SINGLETON;
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_XOR(parameters) {
assert(parameters.length === 2, "bad parameter count for XOR");
const param1 = parameters[0];
const param2 = parameters[1];
if (!param1 && !param2) {
// Not enough params
return BOOL_FALSE_SINGLETON;
}
// Check for the right types
if (param1 && param1.getItemType() !== "boolean") {
return BOOL_FALSE_SINGLETON;
}
if (param2 && param2.getItemType() !== "boolean") {
return BOOL_FALSE_SINGLETON;
}
const valueParam1 = param1 ? /** @type {BooleanItem} */ (param1).value : 0;
const valueParam2 = param2 ? /** @type {BooleanItem} */ (param2).value : 0;
return valueParam1 ^ valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_OR(parameters) {
assert(parameters.length === 2, "bad parameter count for OR");
const param1 = parameters[0];
const param2 = parameters[1];
if (!param1 && !param2) {
// Not enough params
return BOOL_FALSE_SINGLETON;
}
const valueParam1 =
param1 && param1.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param1).value : 0;
const valueParam2 =
param2 && param2.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param2).value : 0;
return valueParam1 || valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_IF(parameters) {
assert(parameters.length === 2, "bad parameter count for IF");
const flag = parameters[0];
const value = parameters[1];
if (!flag || !value) {
// Not enough params
return null;
}
if (flag.getItemType() !== "boolean") {
// Flag is not a boolean
return null;
}
// pass through item
if (/** @type {BooleanItem} */ (flag).value) {
return value;
}
return null;
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_ROTATE(parameters) {
const item = parameters[0];
if (!item || item.getItemType() !== "shape") {
// Not a shape
return null;
}
const definition = /** @type {ShapeItem} */ (item).definition;
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(definition);
return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition);
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {[BaseItem, BaseItem]}
*/
compute_ANALYZE(parameters) {
const item = parameters[0];
if (!item || item.getItemType() !== "shape") {
// Not a shape
return [null, null];
}
const definition = /** @type {ShapeItem} */ (item).definition;
const lowerLayer = /** @type {import("../shape_definition").ShapeLayer} */ (definition.layers[0]);
const topRightContent = lowerLayer[0];
if (!topRightContent) {
return [null, null];
}
const newDefinition = new ShapeDefinition({
layers: [
[
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
],
],
});
return [
COLOR_ITEM_SINGLETONS[topRightContent.color],
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(newDefinition),
];
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {[BaseItem, BaseItem]}
*/
compute_CUT(parameters) {
const item = parameters[0];
if (!item || item.getItemType() !== "shape") {
// Not a shape
return [null, null];
}
const definition = /** @type {ShapeItem} */ (item).definition;
const result = this.root.shapeDefinitionMgr.shapeActionCutHalf(definition);
return [
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[0]),
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[1]),
];
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {[BaseItem, BaseItem]}
*/
compute_UNSTACK(parameters) {
const item = parameters[0];
if (!item || item.getItemType() !== "shape") {
// Not a shape
return [null, null];
}
const definition = /** @type {ShapeItem} */ (item).definition;
const layers = /** @type {Array<import("../shape_definition").ShapeLayer>} */ (definition.layers);
const upperLayerDefinition = new ShapeDefinition({
layers: [layers[layers.length - 1]],
});
const lowerLayers = layers.slice(0, layers.length - 1);
const lowerLayerDefinition =
lowerLayers.length > 0 ? new ShapeDefinition({ layers: lowerLayers }) : null;
return [
lowerLayerDefinition
? this.root.shapeDefinitionMgr.getShapeItemFromDefinition(lowerLayerDefinition)
: null,
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(upperLayerDefinition),
];
}
/**
* @param {Array<BaseItem|null>} parameters
* @returns {BaseItem}
*/
compute_SHAPECOMPARE(parameters) {
const itemA = parameters[0];
const itemB = parameters[1];
return itemA &&
itemB &&
itemA.getItemType() === "shape" &&
itemB.getItemType() === "shape" &&
/** @type {ShapeItem} */ (itemA).definition.getHash() ===
/** @type {ShapeItem} */ (itemB).definition.getHash()
? BOOL_TRUE_SINGLETON
: BOOL_FALSE_SINGLETON;
}
}

View File

@ -312,7 +312,6 @@ ingame:
black: Schwarz
uncolored: Farblos
# Everything related to placing buildings (I.e. as soon as you selected a building
# from the toolbar)
buildingPlacement:
@ -424,7 +423,6 @@ ingame:
1_3_expand: >-
Dies ist <strong>KEIN</strong> Idle-Game! Baue mehr Extrahierer und Förderbänder, um das Ziel schneller zu erreichen.<br><br>Tipp: Halte <strong>UMSCH</strong>, um mehrere Gebäude zu platzieren und nutze <strong>R</strong> um sie zu rotieren.
# All shop upgrades
shopUpgrades:
belt:
@ -564,7 +562,6 @@ buildings:
name: Kabelverbinder
description: Verbindet zwei Energiekabel zu einem.
storyRewards:
# Those are the rewards gained from completing the store
reward_cutter_and_trash:
@ -837,7 +834,6 @@ keybindings:
Modifikator: stattdessen gegen den UZS rotieren
cycleBuildingVariants: Variante wählen
confirmMassDelete: Massenlöschung bestätigen
pasteLastBlueprint: Letzte Blaupause einfügen
cycleBuildings: Gebäude rotieren
lockBeltDirection: Bandplaner aktivieren
switchDirectionLockSide: >-
@ -852,14 +848,6 @@ keybindings:
placeMultiple: Im Platziermodus bleiben
placeInverse: Automatische Förderbandorientierung invertieren
pasteLastBlueprint: Letzte Blaupause einfügen
massSelectCut: Areal ausschneiden
exportScreenshot: Ganze Fabrik als Foto exportieren
mapMoveFaster: Schneller bewegen
lockBeltDirection: Bandplaner aktivieren
switchDirectionLockSide: "Planer: Seite wechseln"
pipette: Pipette
menuClose: Close Menu
switchLayers: Switch layers
advanced_processor: Farbinvertierer
energy_generator: Energiegenerator
wire: Energiekabel

File diff suppressed because it is too large Load Diff