mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-21 11:07:12 +01:00
Logic Gates WIP Commit
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
|
||||
/obj/machinery/logic_gate
|
||||
name = "Logic Base"
|
||||
desc = "This does nothing except connect to things. Highly illogical, report to a coder at once if you see this in-game."
|
||||
icon = 'icons/obj/computer3.dmi'
|
||||
icon_state = "serverframe"
|
||||
density = 1
|
||||
anchored = 1
|
||||
|
||||
settagwhitelist = list("input1_id_tag", "input2_id_tag", "output_id_tag")
|
||||
|
||||
var/tamperproof = 0 //if set, will make the machine unable to be destroyed, adjusted, etc. via in-game interaction (USE ONLY FOR MAPPING STUFF)
|
||||
var/mono_input = 0 //if set, will ignore input2
|
||||
|
||||
var/datum/radio_frequency/radio_connection
|
||||
var/frequency = 0
|
||||
|
||||
/*
|
||||
Some notes on Input/Output:
|
||||
- Multiple things can be linked to the same input or output tag, just like how wires can connect multiple sources and receivers.
|
||||
- For inputs, only the last signal received BEFORE a process() call will be used in the logic handling.
|
||||
- Input states are updated immediately whenever an input signal is received, so it is possible to update multiple times within a single process cycle.
|
||||
- This means if you have multiple connected inputs, but the last signal received before the process() call is OFF, it won't matter if the others are both ON.
|
||||
- For this reason, please set up your logic properly. You can theoretically chain these infinitely, so there's no need to link multiple things to a single input.
|
||||
- For outputs, the signal will attempt to be sent out every process() call, to ensure newly connected things are updated within one process cycle
|
||||
- Connecting an output to multiple inputs should not cause issues, as long as you don't have multiple connections to a given input (see previous notes on inputs).
|
||||
- The output state is determined immediately preceeding the signal broadcast, using the input states at the time of the process() call, not when a signal is received.
|
||||
- Because of how the process cycle works, it is possible that it may take multiple cycles for a signal to fully propogate through a logic chain.
|
||||
- This is because machines attempt to process in the order they were added to the scheduler.
|
||||
- Building the logic gates at the end of the chain first may cause delays in signal propogation.
|
||||
If you take all this into consideration when linking and using logic machinery, you should have no unexpected issues with input/output. Your design flaws are on you though.
|
||||
*/
|
||||
|
||||
var/input1_id_tag = null
|
||||
var/input1_state = LOGIC_OFF
|
||||
var/input2_id_tag = null
|
||||
var/input2_state = LOGIC_OFF
|
||||
var/output_id_tag = null
|
||||
var/output_state = LOGIC_OFF
|
||||
|
||||
/obj/machinery/logic_gate/New()
|
||||
if(tamperproof) //doing this during New so we don't have to worry about forgetting to set these vars during editting / defining
|
||||
unacidable = 1
|
||||
..()
|
||||
if(!radio_controller)
|
||||
spawn(40)
|
||||
set_frequency(frequency)
|
||||
else
|
||||
spawn(1) // For things that set the frequency directly
|
||||
set_frequency(frequency)
|
||||
component_parts = list()
|
||||
var/obj/item/weapon/circuitboard/logic_gate/LG = new(null)
|
||||
LG.set_type(type)
|
||||
component_parts += LG
|
||||
component_parts += new /obj/item/stack/cable_coil(null, 1)
|
||||
|
||||
/obj/machinery/logic_gate/proc/set_frequency(new_frequency)
|
||||
if(!radio_controller)
|
||||
sleep(20)
|
||||
if(!radio_controller)
|
||||
return
|
||||
radio_controller.remove_object(src, frequency)
|
||||
frequency = new_frequency
|
||||
radio_connection = radio_controller.add_object(src, frequency, RADIO_LOGIC)
|
||||
return
|
||||
|
||||
/obj/machinery/logic_gate/Destroy()
|
||||
if(radio_controller)
|
||||
radio_controller.remove_object(src,frequency)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/logic_gate/process()
|
||||
handle_logic()
|
||||
handle_output() //All output will send for at least one cycle, and will attempt to send every cycle. Hopefully this won't be too taxing.
|
||||
return
|
||||
|
||||
/obj/machinery/logic_gate/proc/handle_logic()
|
||||
return
|
||||
|
||||
/obj/machinery/logic_gate/proc/handle_output()
|
||||
if(!radio_connection) //can't output without this
|
||||
return
|
||||
|
||||
if(output_id_tag == null) //Don't output to an undefined id_tag
|
||||
return
|
||||
|
||||
var/datum/signal/signal = new
|
||||
signal.transmission_method = 1 //radio signal
|
||||
signal.source = src
|
||||
|
||||
signal.data = list(
|
||||
"tag" = output_id_tag,
|
||||
"sigtype" = "logic",
|
||||
"state" = output_state,
|
||||
)
|
||||
|
||||
radio_connection.post_signal(src, signal)
|
||||
return
|
||||
|
||||
/obj/machinery/logic_gate/receive_signal(datum/signal/signal, receive_method, receive_param)
|
||||
if(!signal.data["tag"] || ((signal.data["tag"] != input1_id_tag) && (signal.data["tag"] != input2_id_tag)) || (signal.data["sigtype"] != "logic"))
|
||||
//If the signal lacks tag data, the signal's tag data doesn't match either input id tag, or is not a "logic" signal, ignore it since it's not for us
|
||||
return
|
||||
|
||||
if(signal.data["tag"] == input1_id_tag) //If the signal is for input1
|
||||
if(signal.data["state"] == input1_state) //If we already match, ignore the new signal since nothing changes
|
||||
return
|
||||
if(signal.data["state"] == LOGIC_OFF) //Shut it down and keep it off
|
||||
input1_state = LOGIC_OFF
|
||||
return
|
||||
if(signal.data["state"] == LOGIC_ON) //Turn it on and keep it on
|
||||
input1_state = LOGIC_ON
|
||||
return
|
||||
if(signal.data["state"] == LOGIC_FLICKER) //Turn it on then turn it off
|
||||
if(input1_state == LOGIC_ON) //An existing continuous ON state overrides new flicker signals
|
||||
return
|
||||
input1_state = LOGIC_FLICKER
|
||||
spawn(LOGIC_FLICKER_TIME)
|
||||
if(input1_state == LOGIC_FLICKER) //Make sure we didn't get a new continuous signal set while we waited (those take priority)
|
||||
input1_state = LOGIC_OFF
|
||||
return
|
||||
|
||||
//Now, you may be wondering why I included those returns.
|
||||
//The answer is "If you link both inputs to the same source, you're an idiot and deserve to have it break", so yeah. Deal with it.
|
||||
|
||||
if(mono_input)
|
||||
//We only care about input1, so if we didn't receive a signal for that, we're done.
|
||||
return
|
||||
|
||||
if(signal.data["tag"] == input2_id_tag) //If the signal is for input2 (reaching this point assumes mono_input is not set)
|
||||
if(signal.data["state"] == input2_state) //If we already match, ignore the new signal since nothing changes
|
||||
return
|
||||
if(signal.data["state"] == LOGIC_OFF) //Shut it down and keep it off
|
||||
input2_state = LOGIC_OFF
|
||||
return
|
||||
if(signal.data["state"] == LOGIC_ON) //Turn it on and keep it on
|
||||
input2_state = LOGIC_ON
|
||||
return
|
||||
if(signal.data["state"] == LOGIC_FLICKER) //Turn it on then turn it off
|
||||
if(input2_state == LOGIC_ON) //An existing continuous ON state overrides new flicker signals
|
||||
return
|
||||
input2_state = LOGIC_FLICKER
|
||||
spawn(LOGIC_FLICKER_TIME)
|
||||
if(input2_state == LOGIC_FLICKER) //Make sure we didn't get a new continuous signal set while we waited (those take priority)
|
||||
input2_state = LOGIC_OFF
|
||||
return
|
||||
|
||||
/obj/machinery/logic_gate/multitool_menu(var/mob/user, var/obj/item/device/multitool/P)
|
||||
var/input1_state_string
|
||||
var/input2_state_string
|
||||
var/output_state_string
|
||||
|
||||
switch(input1_state)
|
||||
if(LOGIC_OFF)
|
||||
input1_state_string = "OFF"
|
||||
if(LOGIC_ON)
|
||||
input1_state_string = "ON"
|
||||
if(LOGIC_FLICKER)
|
||||
input1_state_string = "FLICKER"
|
||||
else
|
||||
input1_state_string = "ERROR: UNKNOWN STATE"
|
||||
|
||||
switch(input2_state)
|
||||
if(LOGIC_OFF)
|
||||
input2_state_string = "OFF"
|
||||
if(LOGIC_ON)
|
||||
input2_state_string = "ON"
|
||||
if(LOGIC_FLICKER)
|
||||
input2_state_string = "FLICKER"
|
||||
else
|
||||
input2_state_string = "ERROR: UNKNOWN STATE"
|
||||
|
||||
switch(output_state)
|
||||
if(LOGIC_OFF)
|
||||
output_state_string = "OFF"
|
||||
if(LOGIC_ON)
|
||||
output_state_string = "ON"
|
||||
if(LOGIC_FLICKER)
|
||||
output_state_string = "FLICKER"
|
||||
else
|
||||
output_state_string = "ERROR: UNKNOWN STATE"
|
||||
var/menu_contents = {"
|
||||
<dl>
|
||||
<dt><b>Input:</b> [format_tag("ID Tag","input1_id_tag")]</dt>
|
||||
<dd>Input State: [input1_state_string]</dd>
|
||||
"}
|
||||
if(!mono_input)
|
||||
menu_contents = {"
|
||||
<dt><b>Input 1:</b> [format_tag("ID Tag","input1_id_tag")]</dt>
|
||||
<dd>Input 1 State: [input1_state_string]</dd>
|
||||
<dt><b>Input 2:</b> [format_tag("ID Tag","input2_id_tag")]</dt>
|
||||
<dd>Input 2 State: [input2_state_string]</dd>
|
||||
"}
|
||||
menu_contents += {"
|
||||
<dt><b>Output:</b> [format_tag("ID Tag","output_id_tag")]</dt>
|
||||
<dd>Output State: [output_state_string]</dd>
|
||||
</dl>
|
||||
"}
|
||||
return menu_contents
|
||||
|
||||
/obj/machinery/logic_gate/attackby(obj/item/O, mob/user, params)
|
||||
if(tamperproof)
|
||||
user << "<span class='warning'>The [src] appears to be tamperproofed! You can't interact with it!</span>"
|
||||
return 0
|
||||
if(istype(O, /obj/item/device/multitool))
|
||||
update_multitool_menu(user)
|
||||
return 1
|
||||
if(istype(O, /obj/item/weapon/screwdriver))
|
||||
panel_open = !panel_open
|
||||
user << "<span class='notice'>You [panel_open ? "open" : "close"] the access panel.</span>"
|
||||
return 1
|
||||
if(panel_open && istype(O, /obj/item/weapon/crowbar))
|
||||
default_deconstruction_crowbar(O)
|
||||
return 1
|
||||
|
||||
//////////////////////////////////////
|
||||
// Attack procs //
|
||||
//////////////////////////////////////
|
||||
|
||||
/obj/machinery/logic_gate/attack_ai(mob/user)
|
||||
if(tamperproof)
|
||||
user << "<span class='warning'>The [src] appears to be tamperproofed! You can't interface with it!</span>"
|
||||
return 0
|
||||
add_hiddenprint(user)
|
||||
return ui_interact(user)
|
||||
|
||||
/obj/machinery/logic_gate/attack_ghost(mob/user)
|
||||
if(tamperproof)
|
||||
user << "<span class='warning'>The [src] appears to be tamperproofed! You can't haunt it!</span>"
|
||||
return 0
|
||||
return ui_interact(user)
|
||||
|
||||
/obj/machinery/logic_gate/attack_hand(mob/user)
|
||||
if(tamperproof)
|
||||
user << "<span class='warning'>The [src] appears to be tamperproofed! You can't interact with it!</span>"
|
||||
return 0
|
||||
. = ..()
|
||||
if(.)
|
||||
return 0
|
||||
return interact(user)
|
||||
|
||||
/obj/machinery/logic_gate/attack_alien(mob/user) //No xeno logic, that's too silly.
|
||||
user << "<span class='warning'>The [src] appears to be too complex! You can't comprehend it and back off in fear!</span>"
|
||||
return 0
|
||||
|
||||
/obj/machinery/logic_gate/attack_animal(mob/user) //No animal logic either.
|
||||
user << "<span class='warning'>The [src] appears to be beyond your comprehension! You can't fathom it!</span>"
|
||||
return 0
|
||||
|
||||
/obj/machinery/logic_gate/attack_slime(mob/user) //No slime logic. Seriously.
|
||||
user << "<span class='warning'>The [src] appears to be beyond your gelatinous understanding! You ignore it!</span>"
|
||||
return 0
|
||||
|
||||
/obj/machinery/logic_gate/emp_act(severity)
|
||||
if(tamperproof)
|
||||
return 0
|
||||
..()
|
||||
|
||||
/obj/machinery/logic_gate/ex_act(severity)
|
||||
if(tamperproof)
|
||||
return 0
|
||||
..()
|
||||
|
||||
/obj/machinery/logic_gate/blob_act()
|
||||
if(tamperproof)
|
||||
return 0
|
||||
..()
|
||||
|
||||
/obj/machinery/logic_gate/singularity_act()
|
||||
if(tamperproof)
|
||||
//This is some top-level tamperproofing right here, that's for sure. It can even defy a singularity!
|
||||
return 0
|
||||
|
||||
/obj/machinery/logic_gate/bullet_act()
|
||||
if(tamperproof)
|
||||
return 0
|
||||
..()
|
||||
@@ -0,0 +1,165 @@
|
||||
|
||||
//////////////////////////////////
|
||||
// Mono-Input Gates //
|
||||
//////////////////////////////////
|
||||
|
||||
//NOT Gate
|
||||
/obj/machinery/logic_gate/not
|
||||
name = "NOT Gate"
|
||||
desc = "Accepts one input and outputs the reverse state."
|
||||
mono_input = 1 //NOT Gates are the simplest logic gate because they only utilize one input.
|
||||
output_state = LOGIC_ON //Starts with an active output, since the input will be OFF at start
|
||||
|
||||
/obj/machinery/logic_gate/not/process() //This gate only handles its logic when it receives a signal, and outputs both on the process and when it receives the signal
|
||||
handle_output()
|
||||
return
|
||||
/*
|
||||
A quick note regarding NOT Gates:
|
||||
- Connecting multiple things to the input of a NOT Gate can cause weird behaviour due to updating both when it receives a signal and when it calls process().
|
||||
- This means it will attempt to output once for every logic machine connected to its input's own process() call.
|
||||
- It will then attempt to output an additional time based on the current state when it comes to its own process() call.
|
||||
- For this reason, it is HIGHLY RECOMMENDED that you only connect a single signal source to the input of a NOT Gate to avoid signal spasms.
|
||||
- Connecting multiple things to the output of a NOT Gate should not cause this unusual behavior.
|
||||
*/
|
||||
/obj/machinery/logic_gate/not/receive_signal(datum/signal/signal, receive_method, receive_param)
|
||||
..()
|
||||
handle_logic()
|
||||
return
|
||||
|
||||
/obj/machinery/logic_gate/not/handle_logic() //Our output will always be a continuous signal, even with a FLICKER, it just will update the output when the FLICKER ends
|
||||
if(input1_state == LOGIC_ON) //Output is OFF while input is ON
|
||||
output_state = LOGIC_OFF
|
||||
else if(input1_state == LOGIC_FLICKER) //Output is OFF while input is FLICKER, then output returns to ON when input returns to OFF
|
||||
output_state = LOGIC_OFF
|
||||
spawn(LOGIC_FLICKER_TIME + 1) //Call handle_logic again after this delay (the input should update from the spawn(LOGIC_FLICKER_TIME) in receive_signal() by then)
|
||||
handle_logic()
|
||||
else //Output is ON while input is OFF
|
||||
output_state = LOGIC_ON
|
||||
handle_output()
|
||||
return
|
||||
|
||||
//STATUS Gate
|
||||
/obj/machinery/logic_gate/status
|
||||
name = "Status Gate"
|
||||
desc = "Accepts one input and outputs the same state, showing a colored light based on current state."
|
||||
mono_input = 1 //STATUS Gate doesn't actually perform logic operations, but instead acts as a testing conduit.
|
||||
|
||||
/*
|
||||
STATUS Gates are largely a diagnostics tool, but I'm sure someone will still make a logic gate rave with them anyways.
|
||||
- There is no need to actually connect an output for these to work, they just need an input to sample from.
|
||||
- STATUS Gates attempt to output both on the process cycle and whenever a signal is received.
|
||||
- This may seem a little spammy, but ensures they don't hold anything up signal-wise.
|
||||
*/
|
||||
|
||||
/obj/machinery/logic_gate/status/receive_signal(datum/signal/signal, receive_method, receive_params)
|
||||
..()
|
||||
handle_logic() //STATUS Gate calls handle_logic() when it receives a signal to update its light and output_state
|
||||
handle_output() //STATUS Gate outputs when it receives a signal, since it is just a connector piece (like a wire for power)
|
||||
|
||||
/obj/machinery/logic_gate/status/handle_logic()
|
||||
output_state = input1_state //Output is equal to input, since it is simply a connection with an attached light
|
||||
if(output_state == LOGIC_OFF) //Red light when OFF
|
||||
set_light(2,2,"#ff0000")
|
||||
return
|
||||
if(output_state == LOGIC_ON) //Green light when ON
|
||||
set_light(2,2,"#009933")
|
||||
return
|
||||
if(output_state == LOGIC_FLICKER) //Orange light when FLICKER, then update after LOGIC_FLICKER_TIME + 1 to reflect the changed state
|
||||
set_light(2,2,"#ff9900")
|
||||
spawn(LOGIC_FLICKER_TIME + 1)
|
||||
handle_logic()
|
||||
handle_output()
|
||||
return
|
||||
|
||||
//////////////////////////////////
|
||||
// Dual-Input Gates //
|
||||
//////////////////////////////////
|
||||
|
||||
|
||||
// OR Gate
|
||||
/obj/machinery/logic_gate/or
|
||||
name = "OR Gate"
|
||||
desc = "Outputs ON when at least one input is ON."
|
||||
|
||||
/obj/machinery/logic_gate/or/handle_logic()
|
||||
if(input1_state == LOGIC_ON || input1_state == LOGIC_FLICKER || input2_state == LOGIC_ON || input2_state == LOGIC_FLICKER)
|
||||
if(input1_state == LOGIC_ON || input2_state == LOGIC_ON) //continuous signal takes priority in determining what to output
|
||||
output_state = LOGIC_ON
|
||||
else
|
||||
output_state = LOGIC_FLICKER
|
||||
else //Both inputs were off, so input is off
|
||||
output_state = LOGIC_OFF
|
||||
return
|
||||
|
||||
// AND Gate
|
||||
/obj/machinery/logic_gate/and
|
||||
name = "AND Gate"
|
||||
desc = "Outputs ON only when both inputs are ON."
|
||||
|
||||
/obj/machinery/logic_gate/and/handle_logic()
|
||||
if((input1_state == LOGIC_ON || input1_state == LOGIC_FLICKER) && (input2_state == LOGIC_ON || input2_state == LOGIC_FLICKER))
|
||||
if(input1_state == LOGIC_ON && input2_state == LOGIC_ON) //only output a continuous signal when both inputs are continuous signals
|
||||
output_state = LOGIC_ON
|
||||
else
|
||||
output_state = LOGIC_FLICKER
|
||||
else //At least one input was off, so output is off
|
||||
output_state = LOGIC_OFF
|
||||
return
|
||||
|
||||
// NAND Gate
|
||||
/obj/machinery/logic_gate/nand
|
||||
name = "NAND Gate"
|
||||
desc = "Outputs OFF only when both inputs are ON."
|
||||
output_state = LOGIC_ON
|
||||
|
||||
/obj/machinery/logic_gate/nand/handle_logic()
|
||||
if((input1_state == LOGIC_ON || input1_state == LOGIC_FLICKER) && (input2_state == LOGIC_ON || input2_state == LOGIC_FLICKER))
|
||||
output_state = LOGIC_OFF //This can only output continuous signals
|
||||
else //At least one input was ON/FLICKER, so output is off
|
||||
output_state = LOGIC_OFF
|
||||
return
|
||||
|
||||
// NOR Gate
|
||||
/obj/machinery/logic_gate/nor
|
||||
name = "NOR Gate"
|
||||
desc = "Outputs OFF when at least one input is ON."
|
||||
output_state = LOGIC_ON
|
||||
|
||||
/obj/machinery/logic_gate/nor/handle_logic()
|
||||
if(input1_state == LOGIC_OFF ||input2_state == LOGIC_OFF)
|
||||
output_state = LOGIC_ON //This can only output continuous signals
|
||||
else //Both inputs are ON, so output is OFF
|
||||
output_state = LOGIC_OFF
|
||||
return
|
||||
|
||||
// XOR Gate
|
||||
/obj/machinery/logic_gate/xor
|
||||
name = "XOR Gate"
|
||||
desc = "Outputs ON when only one input is ON."
|
||||
|
||||
/obj/machinery/logic_gate/xor/handle_logic()
|
||||
if((input1_state == LOGIC_ON || input1_state == LOGIC_FLICKER) || input2_state == LOGIC_OFF) //Only input1 is ON/FLICKER, so output matches input1
|
||||
output_state = input1_state
|
||||
else if((input2_state == LOGIC_ON || input2_state == LOGIC_FLICKER) || input1_state == LOGIC_OFF) //Only input2 is ON/FLICKER, so output matches input2
|
||||
output_state = input2_state
|
||||
else //Both inputs are ON or OFF, so output is OFF
|
||||
output_state = LOGIC_OFF
|
||||
return
|
||||
|
||||
// XNOR Gate
|
||||
/obj/machinery/logic_gate/xnor
|
||||
name = "XNOR Gate"
|
||||
desc = "Outputs ON when both inputs are ON or OFF."
|
||||
output_state = LOGIC_ON
|
||||
|
||||
/obj/machinery/logic_gate/xnor/handle_logic()
|
||||
if((input1_state == LOGIC_ON || input1_state == LOGIC_FLICKER) && (input2_state == LOGIC_ON || input2_state == LOGIC_FLICKER)) //Both inputs are ON/FLICKER
|
||||
if(input1_state == LOGIC_ON && input2_state == LOGIC_ON) //Only continuous signal when both inputs are ON
|
||||
output_state = LOGIC_ON
|
||||
else //If at least one input is FLICKER, output FLICKER
|
||||
output_state = LOGIC_FLICKER
|
||||
else if(input1_state == LOGIC_OFF && input2_state == LOGIC_OFF) //Both inputs are OFF
|
||||
output_state = LOGIC_ON //Always continuous in this case
|
||||
else //Only one input is ON/FLICKER
|
||||
output_state = LOGIC_OFF
|
||||
return
|
||||
Reference in New Issue
Block a user