tgui backend

This commit is contained in:
LetterN
2021-10-28 12:51:31 +08:00
parent 853ff1d8ad
commit 49940c373e
110 changed files with 7353 additions and 259 deletions
+133
View File
@@ -0,0 +1,133 @@
/datum/wires/airlock/shell
holder_type = /obj/machinery/door/airlock/shell
proper_name = "Circuit Airlock"
/datum/wires/airlock/shell/on_cut(wire, mend)
// Don't allow them to re-enable autoclose.
if(wire == WIRE_TIMING)
return
return ..()
/obj/machinery/door/airlock/shell
name = "circuit airlock"
autoclose = FALSE
/obj/machinery/door/airlock/shell/Initialize()
. = ..()
AddComponent( \
/datum/component/shell, \
unremovable_circuit_components = list(new /obj/item/circuit_component/airlock), \
capacity = SHELL_CAPACITY_LARGE, \
shell_flags = SHELL_FLAG_ALLOW_FAILURE_ACTION \
)
/obj/machinery/door/airlock/shell/check_access(obj/item/I)
return FALSE
/obj/machinery/door/airlock/shell/canAIControl(mob/user)
return FALSE
/obj/machinery/door/airlock/shell/canAIHack(mob/user)
return FALSE
/obj/machinery/door/airlock/shell/set_wires()
return new /datum/wires/airlock/shell(src)
/obj/item/circuit_component/airlock
display_name = "Airlock"
desc = "The general interface with an airlock. Includes general statuses of the airlock"
/// Called when attack_hand is called on the shell.
var/obj/machinery/door/airlock/attached_airlock
/// Bolts the airlock (if possible)
var/datum/port/input/bolt
/// Unbolts the airlock (if possible)
var/datum/port/input/unbolt
/// Opens the airlock (if possible)
var/datum/port/input/open
/// Closes the airlock (if possible)
var/datum/port/input/close
/// Contains whether the airlock is open or not
var/datum/port/output/is_open
/// Contains whether the airlock is bolted or not
var/datum/port/output/is_bolted
/// Called when the airlock is opened.
var/datum/port/output/opened
/// Called when the airlock is closed
var/datum/port/output/closed
/// Called when the airlock is bolted
var/datum/port/output/bolted
/// Called when the airlock is unbolted
var/datum/port/output/unbolted
/obj/item/circuit_component/airlock/Initialize()
. = ..()
// Input Signals
bolt = add_input_port("Bolt", PORT_TYPE_SIGNAL)
unbolt = add_input_port("Unbolt", PORT_TYPE_SIGNAL)
open = add_input_port("Open", PORT_TYPE_SIGNAL)
close = add_input_port("Close", PORT_TYPE_SIGNAL)
// States
is_open = add_output_port("Is Open", PORT_TYPE_NUMBER)
is_bolted = add_output_port("Is Bolted", PORT_TYPE_NUMBER)
// Output Signals
opened = add_output_port("Opened", PORT_TYPE_SIGNAL)
closed = add_output_port("Closed", PORT_TYPE_SIGNAL)
bolted = add_output_port("Bolted", PORT_TYPE_SIGNAL)
unbolted = add_output_port("Unbolted", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/airlock/register_shell(atom/movable/shell)
. = ..()
if(istype(shell, /obj/machinery/door/airlock))
attached_airlock = shell
RegisterSignal(shell, COMSIG_AIRLOCK_SET_BOLT, .proc/on_airlock_set_bolted)
RegisterSignal(shell, COMSIG_AIRLOCK_OPEN, .proc/on_airlock_open)
RegisterSignal(shell, COMSIG_AIRLOCK_CLOSE, .proc/on_airlock_closed)
/obj/item/circuit_component/airlock/unregister_shell(atom/movable/shell)
attached_airlock = null
UnregisterSignal(shell, list(
COMSIG_AIRLOCK_SET_BOLT,
COMSIG_AIRLOCK_OPEN,
COMSIG_AIRLOCK_CLOSE,
))
return ..()
/obj/item/circuit_component/airlock/proc/on_airlock_set_bolted(datum/source, should_bolt)
SIGNAL_HANDLER
is_bolted.set_output(should_bolt)
if(should_bolt)
bolted.set_output(COMPONENT_SIGNAL)
else
unbolted.set_output(COMPONENT_SIGNAL)
/obj/item/circuit_component/airlock/proc/on_airlock_open(datum/source, force)
SIGNAL_HANDLER
is_open.set_output(TRUE)
opened.set_output(COMPONENT_SIGNAL)
/obj/item/circuit_component/airlock/proc/on_airlock_closed(datum/source, forced)
SIGNAL_HANDLER
is_open.set_output(FALSE)
closed.set_output(COMPONENT_SIGNAL)
/obj/item/circuit_component/airlock/input_received(datum/port/input/port)
. = ..()
if(.)
return
if(!attached_airlock)
return
if(COMPONENT_TRIGGERED_BY(bolt, port))
attached_airlock.bolt()
if(COMPONENT_TRIGGERED_BY(unbolt, port))
attached_airlock.unbolt()
if(COMPONENT_TRIGGERED_BY(open, port) && attached_airlock.density)
INVOKE_ASYNC(attached_airlock, /obj/machinery/door/airlock.proc/open)
if(COMPONENT_TRIGGERED_BY(close, port) && !attached_airlock.density)
INVOKE_ASYNC(attached_airlock, /obj/machinery/door/airlock.proc/close)
+55
View File
@@ -0,0 +1,55 @@
/**
* # Bot
*
* Immobile (but not dense) shells that can interact with world.
*/
/obj/structure/bot
name = "bot"
icon = 'icons/obj/wiremod.dmi'
icon_state = "setup_medium_box"
density = FALSE
light_system = MOVABLE_LIGHT
light_on = FALSE
/obj/structure/bot/Initialize()
. = ..()
AddComponent( \
/datum/component/shell, \
unremovable_circuit_components = list(new /obj/item/circuit_component/bot), \
capacity = SHELL_CAPACITY_LARGE, \
shell_flags = SHELL_FLAG_USB_PORT, \
)
/obj/item/circuit_component/bot
display_name = "Bot"
desc = "Triggers when someone interacts with the bot."
/// Called when attack_hand is called on the shell.
var/datum/port/output/signal
/// The user who used the bot
var/datum/port/output/entity
/obj/item/circuit_component/bot/Initialize()
. = ..()
entity = add_output_port("User", PORT_TYPE_ATOM)
signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/bot/Destroy()
signal = null
entity = null
return ..()
/obj/item/circuit_component/bot/register_shell(atom/movable/shell)
RegisterSignal(shell, COMSIG_ATOM_ATTACK_HAND, .proc/on_attack_hand)
/obj/item/circuit_component/bot/unregister_shell(atom/movable/shell)
UnregisterSignal(shell, COMSIG_ATOM_ATTACK_HAND)
/obj/item/circuit_component/bot/proc/on_attack_hand(atom/source, mob/user)
SIGNAL_HANDLER
source.balloon_alert(user, "pushed button")
playsound(source, get_sfx("terminal_type"), 25, FALSE)
entity.set_output(user)
signal.set_output(COMPONENT_SIGNAL)
@@ -0,0 +1,553 @@
/obj/item/organ/cyberimp/bci
name = "brain-computer interface"
desc = "An implant that can be placed in a user's head to control circuits using their brain."
icon = 'icons/obj/wiremod.dmi'
icon_state = "bci"
zone = BODY_ZONE_HEAD
w_class = WEIGHT_CLASS_TINY
/obj/item/organ/cyberimp/bci/Initialize()
. = ..()
AddComponent(/datum/component/shell, list(
new /obj/item/circuit_component/bci_core,
new /obj/item/circuit_component/bci_action(null, "One"),
new /obj/item/circuit_component/bci_action(null, "Two"),
new /obj/item/circuit_component/bci_action(null, "Three"),
), SHELL_CAPACITY_SMALL)
/obj/item/organ/cyberimp/bci/Insert(mob/living/carbon/reciever, special, drop_if_replaced)
. = ..()
// Organs are put in nullspace, but this breaks circuit interactions
forceMove(reciever)
/obj/item/organ/cyberimp/bci/say(message, bubble_type, list/spans, sanitize, datum/language/language, ignore_spam, forced)
if (owner)
// Otherwise say_dead will be called.
// It's intentional that a circuit for a dead person does not speak from the shell.
if (owner.stat == DEAD)
return
owner.say(message, forced = "circuit speech")
else
return ..()
/obj/item/circuit_component/bci_action
display_name = "BCI Action"
desc = "Represents an action the user can take when implanted with the brain-computer interface."
required_shells = list(/obj/item/organ/cyberimp/bci)
/// The icon of the button
var/datum/port/input/option/icon_options
/// The name to use for the button
var/datum/port/input/button_name
/// Called when the user presses the button
var/datum/port/output/signal
/// A reference to the action button itself
var/datum/action/innate/bci_action/bci_action
/obj/item/circuit_component/bci_action/Initialize(mapload, default_icon)
. = ..()
if (!isnull(default_icon))
icon_options.set_input(default_icon)
button_name = add_input_port("Name", PORT_TYPE_STRING)
signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/bci_action/Destroy()
QDEL_NULL(bci_action)
return ..()
/obj/item/circuit_component/bci_action/populate_options()
var/static/action_options = list(
"Blank",
"One",
"Two",
"Three",
"Four",
"Five",
"Blood",
"Bomb",
"Brain",
"Brain Damage",
"Cross",
"Electricity",
"Exclamation",
"Heart",
"Id",
"Info",
"Injection",
"Magnetism",
"Minus",
"Network",
"Plus",
"Power",
"Question",
"Radioactive",
"Reaction",
"Repair",
"Say",
"Scan",
"Shield",
"Skull",
"Sleep",
"Wireless",
)
icon_options = add_option_port("Icon", action_options)
/obj/item/circuit_component/bci_action/register_shell(atom/movable/shell)
var/obj/item/organ/cyberimp/bci/bci = shell
bci_action = new(src)
update_action()
bci.actions += list(bci_action)
/obj/item/circuit_component/bci_action/unregister_shell(atom/movable/shell)
var/obj/item/organ/cyberimp/bci/bci = shell
bci.actions -= bci_action
QDEL_NULL(bci_action)
/obj/item/circuit_component/bci_action/input_received(datum/port/input/port)
. = ..()
if (.)
return
if (!isnull(bci_action))
update_action()
/obj/item/circuit_component/bci_action/proc/update_action()
bci_action.name = button_name.value
bci_action.button_icon_state = "bci_[replacetextEx(lowertext(icon_options.value), " ", "_")]"
/datum/action/innate/bci_action
name = "Action"
icon_icon = 'icons/mob/actions/actions_items.dmi'
check_flags = AB_CHECK_CONSCIOUS
button_icon_state = "bci_power"
var/obj/item/circuit_component/bci_action/circuit_component
/datum/action/innate/bci_action/New(obj/item/circuit_component/bci_action/circuit_component)
..()
src.circuit_component = circuit_component
/datum/action/innate/bci_action/Destroy()
circuit_component.bci_action = null
circuit_component = null
return ..()
/datum/action/innate/bci_action/Activate()
circuit_component.signal.set_output(COMPONENT_SIGNAL)
/obj/item/circuit_component/bci_core
display_name = "BCI Core"
desc = "Controls the core operations of the BCI."
/// A reference to the action button to look at charge/get info
var/datum/action/innate/bci_charge_action/charge_action
var/datum/port/input/message
var/datum/port/input/send_message_signal
var/datum/port/output/user_port
var/datum/weakref/user
/obj/item/circuit_component/bci_core/Initialize()
. = ..()
message = add_input_port("Message", PORT_TYPE_STRING)
send_message_signal = add_input_port("Send Message", PORT_TYPE_SIGNAL)
user_port = add_output_port("User", PORT_TYPE_ATOM)
/obj/item/circuit_component/bci_core/Destroy()
QDEL_NULL(charge_action)
return ..()
/obj/item/circuit_component/bci_core/register_shell(atom/movable/shell)
var/obj/item/organ/cyberimp/bci/bci = shell
charge_action = new(src)
bci.actions += list(charge_action)
RegisterSignal(shell, COMSIG_ORGAN_IMPLANTED, .proc/on_organ_implanted)
RegisterSignal(shell, COMSIG_ORGAN_REMOVED, .proc/on_organ_removed)
/obj/item/circuit_component/bci_core/unregister_shell(atom/movable/shell)
var/obj/item/organ/cyberimp/bci/bci = shell
bci.actions -= charge_action
QDEL_NULL(charge_action)
UnregisterSignal(shell, list(
COMSIG_ORGAN_IMPLANTED,
COMSIG_ORGAN_REMOVED,
))
/obj/item/circuit_component/bci_core/input_received(datum/port/input/port)
. = ..()
if (.)
return .
if (COMPONENT_TRIGGERED_BY(send_message_signal, port))
var/sent_message = trim(message.value)
if (!sent_message)
return
var/mob/living/carbon/resolved_owner = user?.resolve()
if (isnull(resolved_owner))
return
if (resolved_owner.stat == DEAD)
return
to_chat(resolved_owner, "<i>You hear a strange, robotic voice in your head...</i> \"[span_robot("[html_encode(sent_message)]")]\"")
/obj/item/circuit_component/bci_core/proc/on_organ_implanted(datum/source, mob/living/carbon/owner)
SIGNAL_HANDLER
user_port.set_output(owner)
user = WEAKREF(owner)
RegisterSignal(owner, COMSIG_PARENT_EXAMINE, .proc/on_examine)
RegisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, .proc/on_borg_charge)
RegisterSignal(owner, COMSIG_LIVING_ELECTROCUTE_ACT, .proc/on_electrocute)
/obj/item/circuit_component/bci_core/proc/on_organ_removed(datum/source, mob/living/carbon/owner)
SIGNAL_HANDLER
user_port.set_output(null)
user = null
UnregisterSignal(owner, list(
COMSIG_PARENT_EXAMINE,
COMSIG_PROCESS_BORGCHARGER_OCCUPANT,
COMSIG_LIVING_ELECTROCUTE_ACT,
))
/obj/item/circuit_component/bci_core/proc/on_borg_charge(datum/source, amount)
SIGNAL_HANDLER
if (isnull(parent.cell))
return
parent.cell.give(amount)
/obj/item/circuit_component/bci_core/proc/on_electrocute(datum/source, shock_damage, siemens_coefficient, flags)
SIGNAL_HANDLER
if (isnull(parent.cell))
return
if (flags & SHOCK_ILLUSION)
return
parent.cell.give(shock_damage * 2)
to_chat(source, span_notice("You absorb some of the shock into your [parent.name]!"))
/obj/item/circuit_component/bci_core/proc/on_examine(datum/source, mob/mob, list/examine_text)
SIGNAL_HANDLER
if (isobserver(mob))
examine_text += span_notice("[source.p_they(capitalized = TRUE)] [source.p_have()] <a href='?src=[REF(src)];open_bci=1'>\a [parent] implanted in [source.p_them()]</a>.")
/obj/item/circuit_component/bci_core/Topic(href, list/href_list)
..()
if (!isobserver(usr))
return
if (href_list["open_bci"])
parent.attack_ghost(usr)
/datum/action/innate/bci_charge_action
name = "Check BCI Charge"
check_flags = NONE
icon_icon = 'icons/obj/power.dmi'
button_icon_state = "cell"
var/obj/item/circuit_component/bci_core/circuit_component
/datum/action/innate/bci_charge_action/New(obj/item/circuit_component/bci_core/circuit_component)
..()
src.circuit_component = circuit_component
button.maptext_x = 2
button.maptext_y = 0
update_maptext()
START_PROCESSING(SSobj, src)
/datum/action/innate/bci_charge_action/Destroy()
circuit_component.charge_action = null
circuit_component = null
STOP_PROCESSING(SSobj, src)
return ..()
/datum/action/innate/bci_charge_action/Trigger()
var/obj/item/stock_parts/cell/cell = circuit_component.parent.cell
if (isnull(cell))
to_chat(owner, span_boldwarning("[circuit_component.parent] has no power cell."))
else
to_chat(owner, span_info("[circuit_component.parent]'s [cell.name] has <b>[cell.percent()]%</b> charge left."))
to_chat(owner, span_info("You can recharge it by using a cyborg recharging station."))
/datum/action/innate/bci_charge_action/process(delta_time)
update_maptext()
/datum/action/innate/bci_charge_action/proc/update_maptext()
var/obj/item/stock_parts/cell/cell = circuit_component.parent.cell
button.maptext = cell ? MAPTEXT("[cell.percent()]%") : ""
/obj/machinery/bci_implanter
name = "brain-computer interface manipulation chamber"
desc = "A machine that, when given a brain-computer interface, will implant it into an occupant. Otherwise, will remove any brain-computer interfaces they already have."
circuit = /obj/item/circuitboard/machine/bci_implanter
icon = 'icons/obj/machines/bci_implanter.dmi'
icon_state = "bci_implanter"
base_icon_state = "bci_implanter"
layer = ABOVE_WINDOW_LAYER
use_power = IDLE_POWER_USE
anchored = TRUE
density = TRUE
obj_flags = NO_BUILD // Becomes undense when the door is open
idle_power_usage = 50
active_power_usage = 300
var/busy = FALSE
var/busy_icon_state
var/locked = FALSE
var/datum/weakref/bci_to_implant
COOLDOWN_DECLARE(message_cooldown)
/obj/machinery/bci_implanter/Initialize()
. = ..()
occupant_typecache = typecacheof(/mob/living/carbon)
/obj/machinery/bci_implanter/Destroy()
QDEL_NULL(bci_to_implant)
return ..()
/obj/machinery/bci_implanter/examine(mob/user)
. = ..()
if (isnull(bci_to_implant?.resolve()))
. += span_notice("There is no BCI inserted.")
else
. += span_notice("Right-click to remove current BCI.")
/obj/machinery/bci_implanter/proc/set_busy(status, working_icon)
busy = status
busy_icon_state = working_icon
update_appearance()
/obj/machinery/bci_implanter/update_icon_state()
if (occupant)
icon_state = busy ? busy_icon_state : "[base_icon_state]_occupied"
return ..()
icon_state = "[base_icon_state][state_open ? "_open" : null]"
return ..()
/obj/machinery/bci_implanter/update_overlays()
var/list/overlays = ..()
if ((machine_stat & MAINT) || panel_open)
overlays += "maint"
return overlays
if (machine_stat & (NOPOWER|BROKEN))
return overlays
if (busy || locked)
overlays += "red"
if (locked)
overlays += "bolted"
return overlays
overlays += "green"
return overlays
/obj/machinery/bci_implanter/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if (. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return .
if(!user.Adjacent(src))
return
if (locked)
balloon_alert(user, "it's locked!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
var/obj/item/organ/cyberimp/bci/bci_to_implant_resolved = bci_to_implant?.resolve()
if (isnull(bci_to_implant_resolved))
balloon_alert(user, "no bci inserted!")
else
user.put_in_hands(bci_to_implant_resolved)
balloon_alert(user, "ejected bci")
bci_to_implant = null
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/machinery/bci_implanter/attackby(obj/item/weapon, mob/user, params)
var/obj/item/organ/cyberimp/bci/new_bci = weapon
if (istype(new_bci))
if (!(locate(/obj/item/integrated_circuit) in new_bci))
balloon_alert(user, "bci has no circuit!")
return
var/obj/item/organ/cyberimp/bci/previous_bci_to_implant = bci_to_implant?.resolve()
bci_to_implant = WEAKREF(weapon)
weapon.forceMove(src)
if (isnull(previous_bci_to_implant))
balloon_alert(user, "inserted bci")
else
balloon_alert(user, "swapped bci")
user.put_in_hands(previous_bci_to_implant)
return
return ..()
/obj/machinery/bci_implanter/attackby_secondary(obj/item/weapon, mob/user, params)
if (!occupant && default_deconstruction_screwdriver(user, icon_state, icon_state, weapon))
update_appearance()
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if (default_pry_open(weapon))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if (default_deconstruction_crowbar(weapon))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/machinery/bci_implanter/proc/start_process()
if (machine_stat & (NOPOWER|BROKEN))
return
if ((machine_stat & MAINT) || panel_open)
return
if (!occupant || busy)
return
var/locked_state = locked
locked = TRUE
set_busy(TRUE, "[initial(icon_state)]_raising")
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "[initial(icon_state)]_active"), 1 SECONDS)
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "[initial(icon_state)]_falling"), 2 SECONDS)
addtimer(CALLBACK(src, .proc/complete_process, locked_state), 3 SECONDS)
/obj/machinery/bci_implanter/proc/complete_process(locked_state)
locked = locked_state
set_busy(FALSE)
var/mob/living/carbon/carbon_occupant = occupant
if (!istype(carbon_occupant))
return
playsound(loc, 'sound/machines/ping.ogg', 30, FALSE)
var/obj/item/organ/cyberimp/bci/bci_organ = carbon_occupant.getorgan(/obj/item/organ/cyberimp/bci)
var/obj/item/organ/cyberimp/bci/bci_to_implant_resolved = bci_to_implant?.resolve()
if (bci_organ)
bci_organ.Remove(carbon_occupant)
if (isnull(bci_to_implant_resolved))
say("Occupant's previous brain-computer interface has been transferred to internal storage unit.")
bci_organ.forceMove(src)
bci_to_implant = WEAKREF(bci_organ)
else
say("Occupant's previous brain-computer interface has been ejected.")
bci_organ.forceMove(drop_location())
else if (!isnull(bci_to_implant_resolved))
say("Occupant has been injected with [bci_to_implant_resolved].")
bci_to_implant_resolved.Insert(carbon_occupant)
bci_to_implant = null
/obj/machinery/bci_implanter/open_machine()
if(state_open)
return FALSE
..()
return TRUE
/obj/machinery/bci_implanter/close_machine(mob/living/carbon/user)
if(!state_open)
return FALSE
..()
var/mob/living/carbon/carbon_occupant = occupant
if (istype(occupant))
var/obj/item/organ/cyberimp/bci/existing_bci_organ = carbon_occupant.getorgan(/obj/item/organ/cyberimp/bci)
if (isnull(existing_bci_organ) && isnull(bci_to_implant?.resolve()))
say("No brain-computer interface inserted, and occupant does not have one. Insert a BCI to implant one.")
playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
return FALSE
addtimer(CALLBACK(src, .proc/start_process), 1 SECONDS)
return TRUE
/obj/machinery/bci_implanter/relaymove(mob/living/user, direction)
var/message
if (locked)
message = "it won't budge!"
else if (user.stat != CONSCIOUS)
message = "you don't have the energy!"
if (!isnull(message))
if (COOLDOWN_FINISHED(src, message_cooldown))
COOLDOWN_START(src, message_cooldown, 5 SECONDS)
balloon_alert(user, message)
return
open_machine()
/obj/machinery/bci_implanter/interact(mob/user)
if (state_open)
close_machine(null, user)
return
else if (locked)
balloon_alert(user, "it's locked!")
return
open_machine()
/obj/item/circuitboard/machine/bci_implanter
name = "Brain-Computer Interface Manipulation Chamber (Machine Board)"
greyscale_colors = CIRCUIT_COLOR_SCIENCE
build_path = /obj/machinery/bci_implanter
req_components = list(
/obj/item/stock_parts/micro_laser = 2,
/obj/item/stock_parts/manipulator = 1,
)
@@ -0,0 +1,51 @@
/**
* # Compact Remote
*
* A handheld device with one big button.
*/
/obj/item/compact_remote
name = "compact remote"
icon = 'icons/obj/wiremod.dmi'
icon_state = "setup_small_simple"
inhand_icon_state = "electronic"
worn_icon_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
light_system = MOVABLE_LIGHT_DIRECTIONAL
light_on = FALSE
/obj/item/compact_remote/Initialize()
. = ..()
AddComponent(/datum/component/shell, list(
new /obj/item/circuit_component/compact_remote()
), SHELL_CAPACITY_SMALL)
/obj/item/circuit_component/compact_remote
display_name = "Compact Remote"
desc = "Used to receive inputs from the compact remote shell. Use the shell in hand to trigger the output signal."
/// Called when attack_self is called on the shell.
var/datum/port/output/signal
/// The user who used the bot
var/datum/port/output/entity
/obj/item/circuit_component/compact_remote/Initialize()
. = ..()
entity = add_output_port("User", PORT_TYPE_ATOM)
signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/compact_remote/register_shell(atom/movable/shell)
RegisterSignal(shell, COMSIG_ITEM_ATTACK_SELF, .proc/send_trigger)
/obj/item/circuit_component/compact_remote/unregister_shell(atom/movable/shell)
UnregisterSignal(shell, COMSIG_ITEM_ATTACK_SELF)
/**
* Called when the shell item is used in hand.
*/
/obj/item/circuit_component/compact_remote/proc/send_trigger(atom/source, mob/user)
SIGNAL_HANDLER
source.balloon_alert(user, "clicked primary button")
playsound(source, get_sfx("terminal_type"), 25, FALSE)
entity.set_output(user)
signal.set_output(COMPONENT_SIGNAL)
+89
View File
@@ -0,0 +1,89 @@
/**
* # Compact Remote
*
* A handheld device with several buttons.
* In game, this translates to having different signals for normal usage, alt-clicking, and ctrl-clicking when in your hand.
*/
/obj/item/controller
name = "controller"
icon = 'icons/obj/wiremod.dmi'
icon_state = "setup_small_calc"
inhand_icon_state = "electronic"
worn_icon_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
light_system = MOVABLE_LIGHT_DIRECTIONAL
light_on = FALSE
/obj/item/controller/Initialize()
. = ..()
AddComponent(/datum/component/shell, list(
new /obj/item/circuit_component/controller()
), SHELL_CAPACITY_MEDIUM)
/obj/item/circuit_component/controller
display_name = "Controller"
desc = "Used to receive inputs from the controller shell. Use the shell in hand to trigger the output signal. Alt-click for the alternate signal. Right click for the extra signal."
/// The three separate buttons that are called in attack_hand on the shell.
var/datum/port/output/signal
var/datum/port/output/alt
var/datum/port/output/right
/// The entity output
var/datum/port/output/entity
/obj/item/circuit_component/controller/Initialize()
. = ..()
entity = add_output_port("User", PORT_TYPE_ATOM)
signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
alt = add_output_port("Alternate Signal", PORT_TYPE_SIGNAL)
right = add_output_port("Extra Signal", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/controller/register_shell(atom/movable/shell)
RegisterSignal(shell, COMSIG_ITEM_ATTACK_SELF, .proc/send_trigger)
RegisterSignal(shell, COMSIG_CLICK_ALT, .proc/send_alternate_signal)
RegisterSignal(shell, COMSIG_ITEM_ATTACK_SELF_SECONDARY, .proc/send_right_signal)
/obj/item/circuit_component/controller/unregister_shell(atom/movable/shell)
UnregisterSignal(shell, list(
COMSIG_ITEM_ATTACK_SELF,
COMSIG_ITEM_ATTACK_SELF_SECONDARY,
COMSIG_CLICK_ALT,
))
/**
* Called when the shell item is used in hand
*/
/obj/item/circuit_component/controller/proc/send_trigger(atom/source, mob/user)
SIGNAL_HANDLER
if(!user.Adjacent(source))
return
source.balloon_alert(user, "clicked primary button")
playsound(source, get_sfx("terminal_type"), 25, FALSE)
entity.set_output(user)
signal.set_output(COMPONENT_SIGNAL)
/**
* Called when the shell item is alt-clicked
*/
/obj/item/circuit_component/controller/proc/send_alternate_signal(atom/source, mob/user)
SIGNAL_HANDLER
if(!user.Adjacent(source))
return
source.balloon_alert(user, "clicked alternate button")
playsound(source, get_sfx("terminal_type"), 25, FALSE)
entity.set_output(user)
alt.set_output(COMPONENT_SIGNAL)
/**
* Called when the shell item is right-clicked in active hand
*/
/obj/item/circuit_component/controller/proc/send_right_signal(atom/source, mob/user)
SIGNAL_HANDLER
if(!user.Adjacent(source))
return
source.balloon_alert(user, "clicked extra button")
playsound(source, get_sfx("terminal_type"), 25, FALSE)
entity.set_output(user)
right.set_output(COMPONENT_SIGNAL)
+82
View File
@@ -0,0 +1,82 @@
/**
* # Drone
*
* A movable mob that can be fed inputs on which direction to travel.
*/
/mob/living/circuit_drone
name = "drone"
icon = 'icons/obj/wiremod.dmi'
icon_state = "setup_medium_med"
living_flags = 0
light_system = MOVABLE_LIGHT_DIRECTIONAL
light_on = FALSE
/mob/living/circuit_drone/Initialize()
. = ..()
AddComponent(/datum/component/shell, list(
new /obj/item/circuit_component/bot_circuit()
), SHELL_CAPACITY_LARGE)
/mob/living/circuit_drone/updatehealth()
. = ..()
if(health < 0)
gib(no_brain = TRUE, no_organs = TRUE, no_bodyparts = TRUE)
/mob/living/circuit_drone/spawn_gibs()
new /obj/effect/gibspawner/robot(drop_location(), src, get_static_viruses())
/obj/item/circuit_component/bot_circuit
display_name = "Drone"
desc = "Used to send movement output signals to the drone shell."
/// The inputs to allow for the drone to move
var/datum/port/input/north
var/datum/port/input/east
var/datum/port/input/south
var/datum/port/input/west
// Done like this so that travelling diagonally is more simple
COOLDOWN_DECLARE(north_delay)
COOLDOWN_DECLARE(east_delay)
COOLDOWN_DECLARE(south_delay)
COOLDOWN_DECLARE(west_delay)
/// Delay between each movement
var/move_delay = 0.2 SECONDS
/obj/item/circuit_component/bot_circuit/Initialize()
. = ..()
north = add_input_port("Move North", PORT_TYPE_SIGNAL)
east = add_input_port("Move East", PORT_TYPE_SIGNAL)
south = add_input_port("Move South", PORT_TYPE_SIGNAL)
west = add_input_port("Move West", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/bot_circuit/input_received(datum/port/input/port)
. = ..()
if(.)
return
var/mob/living/shell = parent.shell
if(!istype(shell) || shell.stat)
return
var/direction
if(COMPONENT_TRIGGERED_BY(north, port) && COOLDOWN_FINISHED(src, north_delay))
direction = NORTH
COOLDOWN_START(src, north_delay, move_delay)
else if(COMPONENT_TRIGGERED_BY(east, port) && COOLDOWN_FINISHED(src, east_delay))
direction = EAST
COOLDOWN_START(src, east_delay, move_delay)
else if(COMPONENT_TRIGGERED_BY(south, port) && COOLDOWN_FINISHED(src, south_delay))
direction = SOUTH
COOLDOWN_START(src, south_delay, move_delay)
else if(COMPONENT_TRIGGERED_BY(west, port) && COOLDOWN_FINISHED(src, west_delay))
direction = WEST
COOLDOWN_START(src, west_delay, move_delay)
if(!direction)
return
if(shell.Process_Spacemove(direction))
shell.Move(get_step(shell, direction), direction)
+140
View File
@@ -0,0 +1,140 @@
/**
* # Money Bot
*
* Immobile (but not dense) shell that can receive and dispense money.
*/
/obj/structure/money_bot
name = "money bot"
icon = 'icons/obj/wiremod.dmi'
icon_state = "setup_large"
density = FALSE
light_system = MOVABLE_LIGHT
light_on = FALSE
var/stored_money = 0
/obj/structure/money_bot/deconstruct(disassembled)
new /obj/item/holochip(drop_location(), stored_money)
return ..()
/obj/structure/money_bot/proc/add_money(to_add)
stored_money += to_add
SEND_SIGNAL(src, COMSIG_MONEYBOT_ADD_MONEY, to_add)
/obj/structure/money_bot/Initialize()
. = ..()
AddComponent(/datum/component/shell, list(
new /obj/item/circuit_component/money_bot(),
new /obj/item/circuit_component/money_dispenser()
), SHELL_CAPACITY_LARGE)
/obj/structure/money_bot/wrench_act(mob/living/user, obj/item/tool)
set_anchored(!anchored)
tool.play_tool_sound(src)
balloon_alert(user, "You [anchored?"secure":"unsecure"] [src].")
return TRUE
/obj/item/circuit_component/money_dispenser
display_name = "Money Dispenser"
desc = "Used to dispense money from the money bot. Money is taken from the internal storage of money."
circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL
/// The amount of money to dispense
var/datum/port/input/dispense_amount
/// Outputs a signal when it fails to output any money.
var/datum/port/output/on_fail
var/obj/structure/money_bot/attached_bot
/obj/item/circuit_component/money_dispenser/Initialize()
. = ..()
dispense_amount = add_input_port("Amount", PORT_TYPE_NUMBER)
on_fail = add_output_port("On Failed", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/money_dispenser/register_shell(atom/movable/shell)
. = ..()
if(istype(shell, /obj/structure/money_bot))
attached_bot = shell
/obj/item/circuit_component/money_dispenser/unregister_shell(atom/movable/shell)
attached_bot = null
return ..()
/obj/item/circuit_component/money_dispenser/input_received(datum/port/input/port)
. = ..()
if(.)
return
if(!attached_bot)
return
var/to_dispense = clamp(dispense_amount.value, 0, attached_bot.stored_money)
if(!to_dispense)
on_fail.set_output(COMPONENT_SIGNAL)
return
attached_bot.add_money(-to_dispense)
new /obj/item/holochip(drop_location(), to_dispense)
/obj/item/circuit_component/money_bot
display_name = "Money Bot"
var/obj/structure/money_bot/attached_bot
desc = "Used to receive input signals when money is inserted into the money bot shell and also keep track of the total money in the shell."
/// Total money in the shell
var/datum/port/output/total_money
/// Amount of the last money inputted into the shell
var/datum/port/output/money_input
/// Trigger for when money is inputted into the shell
var/datum/port/output/money_trigger
/// The person who input the money
var/datum/port/output/entity
/obj/item/circuit_component/money_bot/Initialize()
. = ..()
total_money = add_output_port("Total Money", PORT_TYPE_NUMBER)
money_input = add_output_port("Last Input Money", PORT_TYPE_NUMBER)
entity = add_output_port("User", PORT_TYPE_ATOM)
money_trigger = add_output_port("Money Input", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/money_bot/register_shell(atom/movable/shell)
. = ..()
if(istype(shell, /obj/structure/money_bot))
attached_bot = shell
total_money.set_output(attached_bot.stored_money)
RegisterSignal(shell, COMSIG_PARENT_ATTACKBY, .proc/handle_money_insert)
RegisterSignal(shell, COMSIG_MONEYBOT_ADD_MONEY, .proc/handle_money_update)
/obj/item/circuit_component/money_bot/unregister_shell(atom/movable/shell)
UnregisterSignal(shell, list(
COMSIG_PARENT_ATTACKBY,
COMSIG_MONEYBOT_ADD_MONEY,
))
total_money.set_output(null)
attached_bot = null
return ..()
/obj/item/circuit_component/money_bot/proc/handle_money_insert(atom/source, obj/item/item, mob/living/attacker)
SIGNAL_HANDLER
if(!attached_bot || !iscash(item))
return
var/amount_to_insert = item.get_item_credit_value()
if(!amount_to_insert)
balloon_alert(attacker, "this has no value!")
return
attached_bot.add_money(amount_to_insert)
balloon_alert(attacker, "inserted [amount_to_insert] credits.")
money_input.set_output(amount_to_insert)
entity.set_output(attacker)
money_trigger.set_output(COMPONENT_SIGNAL)
qdel(item)
/obj/item/circuit_component/money_bot/proc/handle_money_update(atom/source)
SIGNAL_HANDLER
if(attached_bot)
total_money.set_output(attached_bot.stored_money)
+62
View File
@@ -0,0 +1,62 @@
/**
* # Scanner
*
* A handheld device that lets you flash it over people.
*/
/obj/item/wiremod_scanner
name = "scanner"
icon = 'icons/obj/wiremod.dmi'
icon_state = "setup_small"
inhand_icon_state = "electronic"
worn_icon_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
light_system = MOVABLE_LIGHT_DIRECTIONAL
light_on = FALSE
/obj/item/wiremod_scanner/Initialize()
. = ..()
AddComponent(/datum/component/shell, list(
new /obj/item/circuit_component/wiremod_scanner()
), SHELL_CAPACITY_SMALL)
/obj/item/circuit_component/wiremod_scanner
display_name = "Scanner"
desc = "Used to receive scanned entities from the scanner."
/// Called when afterattack is called on the shell.
var/datum/port/output/signal
/// The attacker
var/datum/port/output/attacker
/// The entity being attacked
var/datum/port/output/attacking
/obj/item/circuit_component/wiremod_scanner/Initialize()
. = ..()
attacker = add_output_port("Scanner", PORT_TYPE_ATOM)
attacking = add_output_port("Scanned Entity", PORT_TYPE_ATOM)
signal = add_output_port("Scanned", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/wiremod_scanner/register_shell(atom/movable/shell)
RegisterSignal(shell, COMSIG_ITEM_AFTERATTACK, .proc/handle_afterattack)
/obj/item/circuit_component/wiremod_scanner/unregister_shell(atom/movable/shell)
UnregisterSignal(shell, COMSIG_ITEM_AFTERATTACK)
/**
* Called when the shell item attacks something
*/
/obj/item/circuit_component/wiremod_scanner/proc/handle_afterattack(atom/source, atom/target, mob/user, proximity_flag)
SIGNAL_HANDLER
if(!proximity_flag)
return
source.balloon_alert(user, "scanned object")
playsound(source, get_sfx("terminal_type"), 25, FALSE)
attacker.set_output(user)
attacking.set_output(target)
signal.set_output(COMPONENT_SIGNAL)
@@ -0,0 +1,66 @@
/obj/structure/scanner_gate_shell
name = "circuit scanner gate"
desc = "A gate able to perform mid-depth scans on any organisms who pass under it."
icon = 'icons/obj/machines/scangate.dmi'
icon_state = "scangate_black"
var/scanline_timer
/obj/structure/scanner_gate_shell/Initialize()
. = ..()
set_scanline("passive")
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = .proc/on_entered,
)
AddElement(/datum/element/connect_loc, loc_connections)
AddComponent(/datum/component/shell, list(
new /obj/item/circuit_component/scanner_gate()
), SHELL_CAPACITY_LARGE, SHELL_FLAG_REQUIRE_ANCHOR)
/obj/structure/scanner_gate_shell/wrench_act(mob/living/user, obj/item/tool)
set_anchored(!anchored)
tool.play_tool_sound(src)
balloon_alert(user, "You [anchored?"secure":"unsecure"] [src].")
return TRUE
/obj/structure/scanner_gate_shell/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER
set_scanline("scanning", 10)
SEND_SIGNAL(src, COMSIG_SCANGATE_SHELL_PASS, AM)
/obj/structure/scanner_gate_shell/proc/set_scanline(type, duration)
cut_overlays()
deltimer(scanline_timer)
add_overlay(type)
if(duration)
scanline_timer = addtimer(CALLBACK(src, .proc/set_scanline, "passive"), duration, TIMER_STOPPABLE)
/obj/item/circuit_component/scanner_gate
display_name = "Scanner Gate"
desc = "A gate able to perform mid-depth scans on any object that pass through it."
circuit_flags = CIRCUIT_FLAG_OUTPUT_SIGNAL
var/datum/port/output/scanned
var/obj/structure/scanner_gate_shell/attached_gate
/obj/item/circuit_component/scanner_gate/Initialize()
. = ..()
scanned = add_output_port("Scanned Object", PORT_TYPE_ATOM)
/obj/item/circuit_component/scanner_gate/register_shell(atom/movable/shell)
. = ..()
if(istype(shell, /obj/structure/scanner_gate_shell))
attached_gate = shell
RegisterSignal(attached_gate, COMSIG_SCANGATE_SHELL_PASS, .proc/on_trigger)
/obj/item/circuit_component/scanner_gate/unregister_shell(atom/movable/shell)
UnregisterSignal(attached_gate, COMSIG_SCANGATE_SHELL_PASS)
attached_gate = null
return ..()
/obj/item/circuit_component/scanner_gate/proc/on_trigger(datum/source, atom/movable/passed)
SIGNAL_HANDLER
scanned.set_output(passed)
trigger_output.set_output(COMPONENT_SIGNAL)
+24
View File
@@ -0,0 +1,24 @@
/**
* # Server
*
* Immobile (but not dense) shells that can interact with
* world.
*/
/obj/structure/server
name = "server"
icon = 'icons/obj/wiremod.dmi'
icon_state = "setup_stationary"
density = TRUE
light_system = MOVABLE_LIGHT
light_on = FALSE
/obj/structure/server/Initialize()
. = ..()
AddComponent(/datum/component/shell, null, SHELL_CAPACITY_VERY_LARGE, SHELL_FLAG_REQUIRE_ANCHOR|SHELL_FLAG_USB_PORT)
/obj/structure/server/wrench_act(mob/living/user, obj/item/tool)
set_anchored(!anchored)
tool.play_tool_sound(src)
balloon_alert(user, "You [anchored?"secure":"unsecure"] [src].")
return TRUE
+65
View File
@@ -0,0 +1,65 @@
/**
* # Shell Item
*
* Printed out by protolathes. Screwdriver to complete the shell.
*/
/obj/item/shell
name = "assembly"
desc = "A shell assembly that can be completed by screwdrivering it."
icon = 'icons/obj/wiremod.dmi'
var/shell_to_spawn
var/screw_delay = 3 SECONDS
/obj/item/shell/screwdriver_act(mob/living/user, obj/item/tool)
user.visible_message(span_notice("[user] begins finishing [src]."), span_notice("You begin finishing [src]."))
tool.play_tool_sound(src)
if(!do_after(user, screw_delay, src))
return
user.visible_message(span_notice("[user] finishes [src]."), span_notice("You finish [src]."))
var/turf/drop_loc = drop_location()
qdel(src)
if(drop_loc)
new shell_to_spawn(drop_loc)
return TRUE
/obj/item/shell/bot
name = "bot assembly"
icon_state = "setup_medium_box-open"
shell_to_spawn = /obj/structure/bot
/obj/item/shell/money_bot
name = "money bot assembly"
icon_state = "setup_large-open"
shell_to_spawn = /obj/structure/money_bot
/obj/item/shell/drone
name = "drone assembly"
icon_state = "setup_medium_med-open"
shell_to_spawn = /mob/living/circuit_drone
/obj/item/shell/server
name = "server assembly"
icon_state = "setup_stationary-open"
shell_to_spawn = /obj/structure/server
screw_delay = 10 SECONDS
/obj/item/shell/airlock
name = "circuit airlock assembly"
icon = 'icons/obj/doors/airlocks/station/public.dmi'
icon_state = "construction"
shell_to_spawn = /obj/machinery/door/airlock/shell
screw_delay = 10 SECONDS
/obj/item/shell/bci
name = "brain-computer interface assembly"
icon_state = "bci-open"
shell_to_spawn = /obj/item/organ/cyberimp/bci
/obj/item/shell/scanner_gate
name = "scanner gate assembly"
icon = 'icons/obj/machines/scangate.dmi'
icon_state = "scangate_black_open"
shell_to_spawn = /obj/structure/scanner_gate_shell