diff --git a/code/modules/wiremod/core/integrated_circuit.dm b/code/modules/wiremod/core/integrated_circuit.dm index 9315db39c51..ffc74054dfc 100644 --- a/code/modules/wiremod/core/integrated_circuit.dm +++ b/code/modules/wiremod/core/integrated_circuit.dm @@ -570,8 +570,17 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit) if(!add_component(component, usr)) qdel(component) return + if(params["is_setter"]) + var/obj/item/circuit_component/setter/setter = component + setter.variable_name.set_input(params["variable"]) + else + var/obj/item/circuit_component/getter/getter = component + getter.variable_name.set_input(params["variable"]) + component.rel_x = text2num(params["rel_x"]) + component.rel_y = text2num(params["rel_y"]) RegisterSignal(component, COMSIG_CIRCUIT_COMPONENT_REMOVED, .proc/clear_setter_or_getter) setter_and_getter_count++ + return TRUE if("move_screen") screen_x = text2num(params["screen_x"]) screen_y = text2num(params["screen_y"]) diff --git a/code/modules/wiremod/core/port.dm b/code/modules/wiremod/core/port.dm index 78b5ad846fb..61058bd5dab 100644 --- a/code/modules/wiremod/core/port.dm +++ b/code/modules/wiremod/core/port.dm @@ -168,10 +168,15 @@ src.connected_ports = list() /** - * Introduces two ports to one another. + * Connects an input port to an output port. + * + * Arguments: + * * output - The output port to connect to. */ /datum/port/input/proc/connect(datum/port/output/output) - connected_ports |= output + if(output in connected_ports) + return + connected_ports += output RegisterSignal(output, COMSIG_PORT_SET_VALUE, .proc/receive_value) RegisterSignal(output, COMSIG_PORT_SET_TYPE, .proc/check_type) RegisterSignal(output, COMSIG_PORT_DISCONNECT, .proc/disconnect) diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.js index 03951705de5..f76efc68292 100644 --- a/tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.js +++ b/tgui/packages/tgui/interfaces/IntegratedCircuit/VariableMenu.js @@ -1,4 +1,3 @@ -import { useLocalState } from '../../backend'; import { Box, Stack, @@ -6,109 +5,191 @@ import { Button, Input, Dropdown, + Icon, } from '../../components'; +import { Component } from 'inferno'; +import { shallowDiffers } from 'common/react'; -export const VariableMenu = (props, context) => { - const { - variables, - onAddVariable, - onRemoveVariable, - handleAddSetter, - handleAddGetter, - types, - ...rest - } = props; +export class VariableMenu extends Component { + constructor() { + super(); + this.state = { + variable_name: "", + variable_type: "any", + }; + } - const [name, setName] = useLocalState(context, "variable_name", null); - const [type, setType] = useLocalState(context, "variable_type", types[1]); + shouldComponentUpdate(nextProps, nextState) { + if (shallowDiffers(this.state, nextState)) { + return true; + } - return ( -
- - -
- - {variables.map(val => ( - - - - - - {val.name} - - - - - - - + + +
+
+ + + + + this.setState({ + variable_name: nameVal, + })} + /> - ))} - -
- - -
- - - setName(nameVal)} - /> - - - setType(selectedVal)} - /> - - -
-
- - + + + + this.setState({ + variable_type: selectedVal, + })} + /> + + + + + + + + + + + + ); + } +} + +const PlusIconButton = (props, context) => { + return ( + + + + + ); }; diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/constants.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/constants.js index 6f6a7c72932..0c13b0fa660 100644 --- a/tgui/packages/tgui/interfaces/IntegratedCircuit/constants.js +++ b/tgui/packages/tgui/interfaces/IntegratedCircuit/constants.js @@ -4,3 +4,5 @@ export const SVG_CURVE_INTENSITY = 64; export const MOUSE_BUTTON_LEFT = 0; export const OPTION_DROPDOWN_LARGE_CHAR_AMOUNT = 12; +// in milliseconds +export const TIME_UNTIL_PORT_RELEASE_WORKS = 100; diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/index.js b/tgui/packages/tgui/interfaces/IntegratedCircuit/index.js index c24c4a15b20..89d86e2d6d6 100644 --- a/tgui/packages/tgui/interfaces/IntegratedCircuit/index.js +++ b/tgui/packages/tgui/interfaces/IntegratedCircuit/index.js @@ -10,7 +10,7 @@ import { Component } from 'inferno'; import { Window } from '../../layouts'; import { resolveAsset } from '../../assets'; import { CircuitInfo } from './CircuitInfo'; -import { ABSOLUTE_Y_OFFSET, MOUSE_BUTTON_LEFT } from './constants'; +import { ABSOLUTE_Y_OFFSET, MOUSE_BUTTON_LEFT, TIME_UNTIL_PORT_RELEASE_WORKS } from './constants'; import { Connections } from './Connections'; import { ObjectComponent } from './ObjectComponent'; import { VariableMenu } from './VariableMenu'; @@ -39,6 +39,10 @@ export class IntegratedCircuit extends Component { this.handlePortRelease = this.handlePortRelease.bind(this); this.handleZoomChange = this.handleZoomChange.bind(this); this.handleBackgroundMoved = this.handleBackgroundMoved.bind(this); + + this.onVarClickedSetter = this.onVarClickedSetter.bind(this); + this.onVarClickedGetter = this.onVarClickedGetter.bind(this); + this.handleVarDropped = this.handleVarDropped.bind(this); } // Helper function to get an element's exact position @@ -82,6 +86,11 @@ export class IntegratedCircuit extends Component { } handlePortClick(portIndex, componentId, port, isOutput, event) { + if (this.state.selectedPort) { + this.handlePortUp(portIndex, componentId, port, isOutput, event); + return; + } + if (event.button !== MOUSE_BUTTON_LEFT) { return; } @@ -98,6 +107,9 @@ export class IntegratedCircuit extends Component { this.handlePortDrag(event); + this.timeUntilPortReleaseTimesOut + = Date.now() + TIME_UNTIL_PORT_RELEASE_WORKS; + window.addEventListener('mousemove', this.handlePortDrag); window.addEventListener('mouseup', this.handlePortRelease); } @@ -105,7 +117,7 @@ export class IntegratedCircuit extends Component { // mouse up called whilst over a port. This means we can check if selectedPort // exists and do perform some actions if it does. handlePortUp(portIndex, componentId, port, isOutput, event) { - const { act } = useBackend(this.context); + const { act, data: uiData } = useBackend(this.context); const { selectedPort, } = this.state; @@ -115,6 +127,9 @@ export class IntegratedCircuit extends Component { if (selectedPort.is_output === isOutput) { return; } + this.setState({ + selectedPort: null, + }); let data; if (isOutput) { data = { @@ -132,6 +147,26 @@ export class IntegratedCircuit extends Component { }; } act("add_connection", data); + + const { components } = uiData; + const { + input_component_id, + input_port_id, + output_component_id, + output_port_id, + } = data; + + const input_comp = components[input_component_id-1]; + const input_port = input_comp.input_ports[input_port_id-1]; + const output_comp = components[output_component_id-1]; + const output_port = output_comp.output_ports[output_port_id-1]; + // Do not predict ports that do not match because there is no guarantee + // that they will properly match. + // TODO: Implement proper prediction for this + if (!input_port || input_port.type !== output_port.type) { + return; + } + input_port.connected_to.push(isOutput? port.ref : selectedPort.ref); } handlePortDrag(event) { @@ -144,12 +179,20 @@ export class IntegratedCircuit extends Component { } handlePortRelease(event) { + window.removeEventListener('mouseup', this.handlePortRelease); + + // This will let players release their mouse when dragging + // to stop connecting the port, whilst letting players + // click on the port to click and connect. + if (this.timeUntilPortReleaseTimesOut > Date.now()) { + return; + } + this.setState({ selectedPort: null, }); window.removeEventListener('mousemove', this.handlePortDrag); - window.removeEventListener('mouseup', this.handlePortRelease); } handlePortRightClick(portIndex, componentId, port, isOutput, event) { @@ -174,11 +217,6 @@ export class IntegratedCircuit extends Component { backgroundX: newX, backgroundY: newY, }); - if (this.state.menuOpen) { - this.setState({ - menuOpen: false, - }); - } } componentDidMount() { @@ -197,6 +235,10 @@ export class IntegratedCircuit extends Component { if (examined_name) { act('remove_examined_component'); } + + if (this.state.selectedPort) { + this.handlePortRelease(event); + } } handleMouseUp(event) { @@ -210,6 +252,55 @@ export class IntegratedCircuit extends Component { } } + onVarClickedSetter(event, variable) { + this.handleVarClicked(event, variable, true); + } + + onVarClickedGetter(event, variable) { + this.handleVarClicked(event, variable, false); + } + + handleVarClicked(event, variable, is_setter) { + this.setState({ + draggingVariable: variable, + variableIsSetter: is_setter, + }); + + window.addEventListener('mouseup', this.handleVarDropped); + } + + handleVarDropped(event) { + const { data, act } = useBackend(this.context); + const { + draggingVariable, + variableIsSetter, + backgroundX, + backgroundY, + zoom, + } = this.state; + const { + screen_x, + screen_y, + } = data; + + const xPos = (event.clientX - (backgroundX || screen_x)); + const yPos = (event.clientY - (backgroundY || screen_y)); + + act("add_setter_or_getter", { + variable: draggingVariable, + is_setter: variableIsSetter, + rel_x: xPos*Math.pow(zoom, -1), + rel_y: (yPos + ABSOLUTE_Y_OFFSET)*Math.pow(zoom, -1), + }); + + this.setState({ + draggingVariable: null, + variableIsSetter: null, + }); + + window.removeEventListener('mouseup', this.handleVarDropped); + } + render() { const { act, data } = useBackend(this.context); const { @@ -263,8 +354,8 @@ export class IntegratedCircuit extends Component { return ( this.setState({ menuOpen: false })} onAddVariable={(name, type, event) => act("add_variable", { variable_name: name, variable_datatype: type, @@ -370,12 +468,11 @@ export class IntegratedCircuit extends Component { onRemoveVariable={(name, event) => act("remove_variable", { variable_name: name, })} - handleAddSetter={(e) => act("add_setter_or_getter", { - is_setter: true, - })} - handleAddGetter={(e) => act("add_setter_or_getter", { - is_setter: false, - })} + handleMouseDownSetter={this.onVarClickedSetter} + handleMouseDownGetter={this.onVarClickedGetter} + style={{ + "border-radius": "0px 32px 0px 0px", + }} /> )}