Ports mod link system and scryers (#28479)
* commiting * and this * the rest of the fucking owl * name moment * in my defense it printed * Apply suggestions from code review Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Signed-off-by: Qwertytoforty <52090703+Qwertytoforty@users.noreply.github.com> * woe old code removey * and tgui / comment * Apply suggestions from code review Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> Signed-off-by: Qwertytoforty <52090703+Qwertytoforty@users.noreply.github.com> --------- Signed-off-by: Qwertytoforty <52090703+Qwertytoforty@users.noreply.github.com> Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
@@ -1777,6 +1777,16 @@
|
||||
/obj/effect/decal/cleanable/dirt,
|
||||
/turf/simulated/floor/plasteel,
|
||||
/area/ruin/ancientstation)
|
||||
"fw" = (
|
||||
/obj/effect/decal/cleanable/dirt,
|
||||
/obj/structure/table,
|
||||
/obj/item/reagent_containers/cooking/pot{
|
||||
pixel_y = 3
|
||||
},
|
||||
/turf/simulated/floor/plasteel{
|
||||
icon_state = "cafeteria"
|
||||
},
|
||||
/area/ruin/ancientstation/kitchen)
|
||||
"fx" = (
|
||||
/obj/effect/decal/cleanable/dirt,
|
||||
/obj/effect/decal/cleanable/dirt,
|
||||
@@ -2266,13 +2276,6 @@
|
||||
icon_state = "cafeteria"
|
||||
},
|
||||
/area/ruin/ancientstation/kitchen)
|
||||
"gL" = (
|
||||
/obj/effect/decal/cleanable/dirt,
|
||||
/obj/structure/table,
|
||||
/turf/simulated/floor/plasteel{
|
||||
icon_state = "cafeteria"
|
||||
},
|
||||
/area/ruin/ancientstation/kitchen)
|
||||
"gM" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
|
||||
/obj/machinery/light/small{
|
||||
@@ -6889,11 +6892,19 @@
|
||||
/area/ruin/ancientstation/powered)
|
||||
"UW" = (
|
||||
/obj/structure/table/reinforced,
|
||||
/obj/item/paper/fluff/ruins/oldstation/protosuit,
|
||||
/obj/item/paper/fluff/ruins/oldstation/protosuit{
|
||||
pixel_x = -12
|
||||
},
|
||||
/obj/machinery/door/window/reinforced/normal{
|
||||
name = "prototype display case";
|
||||
req_access = list(301)
|
||||
},
|
||||
/obj/item/clothing/neck/link_scryer/loaded/theta{
|
||||
pixel_x = -1
|
||||
},
|
||||
/obj/item/clothing/neck/link_scryer/loaded/theta{
|
||||
pixel_x = 12
|
||||
},
|
||||
/turf/simulated/floor/engine,
|
||||
/area/ruin/ancientstation/proto)
|
||||
"Va" = (
|
||||
@@ -6970,9 +6981,6 @@
|
||||
"VR" = (
|
||||
/obj/effect/decal/cleanable/dirt,
|
||||
/obj/structure/table,
|
||||
/obj/item/reagent_containers/cooking/pot{
|
||||
pixel_y = 3
|
||||
},
|
||||
/turf/simulated/floor/plasteel{
|
||||
icon_state = "cafeteria"
|
||||
},
|
||||
@@ -9220,7 +9228,7 @@ gs
|
||||
Iw
|
||||
gG
|
||||
gG
|
||||
VR
|
||||
fw
|
||||
gG
|
||||
hZ
|
||||
ha
|
||||
@@ -9316,7 +9324,7 @@ gs
|
||||
zp
|
||||
kx
|
||||
gG
|
||||
gL
|
||||
VR
|
||||
gG
|
||||
hY
|
||||
ha
|
||||
|
||||
@@ -157,6 +157,12 @@
|
||||
//sent from a mob when they set themselves to DNR
|
||||
#define COMSIG_LIVING_SET_DNR "set_dnr"
|
||||
|
||||
// /mob/living/carbon
|
||||
/// Called from apply_overlay(cache_index, overlay)
|
||||
#define COMSIG_CARBON_APPLY_OVERLAY "carbon_apply_overlay"
|
||||
/// Called from remove_overlay(cache_index, overlay)
|
||||
#define COMSIG_CARBON_REMOVE_OVERLAY "carbon_remove_overlay"
|
||||
|
||||
//ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS!
|
||||
// none of these are called as of right now, as there is nothing listening for them.
|
||||
///from base of mob/living/Stun() (amount, ignore_canstun)
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
. = new_angle - old_angle
|
||||
Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT
|
||||
|
||||
/**
|
||||
* Shear the transform on either or both axes.
|
||||
* * x - X axis shearing
|
||||
* * y - Y axis shearing
|
||||
*/
|
||||
/matrix/proc/Shear(x, y)
|
||||
return Multiply(matrix(1, x, 0, y, 1, 0))
|
||||
|
||||
/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3, parallel = TRUE)
|
||||
if(!segments)
|
||||
|
||||
@@ -258,6 +258,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
#define TRAIT_UNKNOWN "unknown" // The person with this trait always appears as 'unknown'.
|
||||
#define TRAIT_CRYO_DESPAWNING "cryo_despawning" // dont adminbus this please
|
||||
#define TRAIT_EXAMINE_HALLUCINATING "examine_hallucinating"
|
||||
|
||||
/// Whether or not the user is in a MODlink call, prevents making more calls
|
||||
#define TRAIT_IN_CALL "in_call"
|
||||
|
||||
/// Trait given when a mob has been tipped
|
||||
#define TRAIT_MOB_TIPPED "mob_tipped"
|
||||
/// Trait given to mobs that have the basic eating element
|
||||
|
||||
@@ -45,9 +45,18 @@ GLOBAL_VAR_INIT(gametime_offset, 432000) // 12:00 in seconds
|
||||
|
||||
GLOBAL_DATUM_INIT(data_core, /datum/datacore, new) // Station datacore, manifest, etc
|
||||
|
||||
//Defines for MODlink frequencies
|
||||
#define MODLINK_FREQ_NANOTRASEN "NT"
|
||||
#define MODLINK_FREQ_SYNDICATE "SYND"
|
||||
#define MODLINK_FREQ_THETA "THET"
|
||||
#define MODLINK_FREQ_CENTCOM "CC"
|
||||
|
||||
/// Global list of all /datum/mod_theme
|
||||
GLOBAL_LIST_INIT(mod_themes, setup_mod_themes())
|
||||
|
||||
/// Global list of all ids associated to a /datum/mod_link instance
|
||||
GLOBAL_LIST_EMPTY(mod_link_ids)
|
||||
|
||||
GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/atmospherics/supermatter_crystal)
|
||||
|
||||
///Global list for descriptors
|
||||
|
||||
@@ -468,7 +468,7 @@
|
||||
else
|
||||
if(dx > 0) direction = EAST
|
||||
else direction = WEST
|
||||
dir = direction
|
||||
setDir(direction)
|
||||
|
||||
/atom/movable/screen/click_catcher
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
|
||||
@@ -646,7 +646,8 @@
|
||||
/datum/action/item_action/accessory/storage
|
||||
name = "View Storage"
|
||||
|
||||
|
||||
/datum/action/item_action/call_link
|
||||
name = "Call MODlink"
|
||||
|
||||
//Preset for spells
|
||||
/datum/action/spell_action
|
||||
|
||||
@@ -100,3 +100,32 @@
|
||||
light.color = new_color
|
||||
light.brightness_color = new_color
|
||||
light.update(FALSE, TRUE, FALSE)
|
||||
|
||||
/datum/station_trait/scryers
|
||||
name = "Scryers"
|
||||
trait_type = STATION_TRAIT_NEUTRAL
|
||||
weight = 5
|
||||
show_in_report = TRUE
|
||||
report_message = "Nanotrasen has chosen your station for an experiment - everyone has free scryers! Use these to talk to other people easily and privately."
|
||||
|
||||
/datum/station_trait/scryers/New()
|
||||
. = ..()
|
||||
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, PROC_REF(on_job_after_spawn))
|
||||
|
||||
/datum/station_trait/scryers/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned, client/player_client)
|
||||
SIGNAL_HANDLER // COMSIG_GLOB_JOB_AFTER_SPAWN
|
||||
if(!ishuman(spawned))
|
||||
return
|
||||
var/mob/living/carbon/human/humanspawned = spawned
|
||||
// Put their silly little scarf or necktie somewhere else
|
||||
var/obj/item/silly_little_scarf = humanspawned.neck
|
||||
if(silly_little_scarf)
|
||||
humanspawned.unequip(silly_little_scarf)
|
||||
silly_little_scarf.forceMove(get_turf(humanspawned))
|
||||
humanspawned.equip_in_one_of_slots(silly_little_scarf, ITEM_SLOT_IN_BACKPACK, ITEM_SLOT_LEFT_POCKET, ITEM_SLOT_RIGHT_POCKET)
|
||||
|
||||
var/obj/item/clothing/neck/link_scryer/loaded/new_scryer = new(spawned)
|
||||
new_scryer.label = spawned.name
|
||||
new_scryer.update_appearance(UPDATE_NAME)
|
||||
|
||||
spawned.equip_to_slot_or_del(new_scryer, ITEM_SLOT_NECK, initial = FALSE)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
active_power_consumption = 200
|
||||
pass_flags = PASSTABLE
|
||||
|
||||
var/list/allowed_devices = list(/obj/item/gun/energy, /obj/item/melee/baton, /obj/item/rcs, /obj/item/bodyanalyzer, /obj/item/handheld_chem_dispenser, /obj/item/clothing/suit/armor/reactive, /obj/item/wormhole_jaunter/wormhole_weaver)
|
||||
var/list/allowed_devices = list(/obj/item/gun/energy, /obj/item/melee/baton, /obj/item/rcs, /obj/item/bodyanalyzer, /obj/item/handheld_chem_dispenser, /obj/item/clothing/suit/armor/reactive, /obj/item/wormhole_jaunter/wormhole_weaver, /obj/item/clothing/neck/link_scryer)
|
||||
var/recharge_coeff = 1
|
||||
|
||||
var/obj/item/charging = null // The item that is being charged
|
||||
@@ -207,6 +207,9 @@
|
||||
if(istype(I, /obj/item/wormhole_jaunter/wormhole_weaver))
|
||||
var/obj/item/wormhole_jaunter/wormhole_weaver/W = I
|
||||
return W.wcell
|
||||
if(istype(I, /obj/item/clothing/neck/link_scryer))
|
||||
var/obj/item/clothing/neck/link_scryer/LS = I
|
||||
return LS.cell
|
||||
|
||||
return null
|
||||
|
||||
|
||||
@@ -107,12 +107,14 @@ Please contact me on #coderbus IRC. ~Carn x
|
||||
. = overlays_standing[cache_index]
|
||||
if(.)
|
||||
add_overlay(.)
|
||||
SEND_SIGNAL(src, COMSIG_CARBON_APPLY_OVERLAY, cache_index, .)
|
||||
|
||||
/mob/living/carbon/human/proc/remove_overlay(cache_index)
|
||||
var/I = overlays_standing[cache_index]
|
||||
if(I)
|
||||
cut_overlay(I)
|
||||
overlays_standing[cache_index] = null
|
||||
SEND_SIGNAL(src, COMSIG_CARBON_REMOVE_OVERLAY, cache_index, I)
|
||||
|
||||
|
||||
GLOBAL_LIST_EMPTY(damage_icon_parts)
|
||||
|
||||
@@ -154,6 +154,7 @@
|
||||
if(!module.active || (module.allow_flags & MODULE_ALLOW_INACTIVE))
|
||||
continue
|
||||
module.on_deactivation(display_message = FALSE)
|
||||
mod_link.end_call()
|
||||
activating = TRUE
|
||||
to_chat(wearer, "<span class='notice'>MODsuit [active ? "shutting down" : "starting up"].</span>")
|
||||
if(do_after(wearer, activation_step_time, FALSE, target = src, allow_moving = TRUE, use_default_checks = FALSE))
|
||||
@@ -210,17 +211,15 @@
|
||||
var/mob/living/carbon/human/H = wearer
|
||||
H.regenerate_icons()
|
||||
|
||||
/// Finishes the suit's activation, starts processing
|
||||
/// Finishes the suit's activation
|
||||
/obj/item/mod/control/proc/finish_activation(on)
|
||||
active = on
|
||||
if(active)
|
||||
for(var/obj/item/mod/module/module as anything in modules)
|
||||
module.on_suit_activation()
|
||||
START_PROCESSING(SSobj, src)
|
||||
else
|
||||
for(var/obj/item/mod/module/module as anything in modules)
|
||||
module.on_suit_deactivation()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
update_speed()
|
||||
update_icon(UPDATE_ICON_STATE)
|
||||
wearer.regenerate_icons()
|
||||
|
||||
@@ -91,6 +91,10 @@
|
||||
var/datum/action/item_action/chameleon_change/modsuit/chameleon_action
|
||||
/// Is the control unit disquised?
|
||||
var/current_disguise = FALSE
|
||||
/// The MODlink datum, letting us call people from the suit.
|
||||
var/datum/mod_link/mod_link
|
||||
/// The starting MODlink frequency, overridden on subtypes that want it to be something.
|
||||
var/starting_frequency = null
|
||||
|
||||
/obj/item/mod/control/serialize()
|
||||
var/list/data = ..()
|
||||
@@ -157,10 +161,19 @@
|
||||
module = new module(src)
|
||||
install(module)
|
||||
ADD_TRAIT(src, TRAIT_ADJACENCY_TRANSPARENT, ROUNDSTART_TRAIT)
|
||||
START_PROCESSING(SSobj, src)
|
||||
mod_link = new(
|
||||
src,
|
||||
starting_frequency,
|
||||
CALLBACK(src, PROC_REF(get_wearer)),
|
||||
CALLBACK(src, PROC_REF(can_call)),
|
||||
CALLBACK(src, PROC_REF(make_link_visual)),
|
||||
CALLBACK(src, PROC_REF(get_link_visual)),
|
||||
CALLBACK(src, PROC_REF(delete_link_visual))
|
||||
)
|
||||
|
||||
/obj/item/mod/control/Destroy()
|
||||
if(active)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
for(var/obj/item/mod/module/module as anything in modules)
|
||||
uninstall(module, deleting = TRUE)
|
||||
for(var/obj/item/part as anything in mod_parts)
|
||||
@@ -179,6 +192,7 @@
|
||||
QDEL_NULL(boots)
|
||||
if(core)
|
||||
QDEL_NULL(core)
|
||||
QDEL_NULL(mod_link)
|
||||
QDEL_NULL(wires)
|
||||
wearer = null
|
||||
selected_module = null
|
||||
@@ -206,6 +220,7 @@
|
||||
. += "You could remove [core] with a <b>wrench</b>."
|
||||
else
|
||||
. += "You could use a <b>MOD core</b> on it to install one."
|
||||
. += "You could copy/set link frequency with a <b>multitool</b>."
|
||||
|
||||
/obj/item/mod/control/examine_more(mob/user)
|
||||
. = ..()
|
||||
@@ -214,9 +229,13 @@
|
||||
/obj/item/mod/control/process()
|
||||
if(seconds_electrified > 0)
|
||||
seconds_electrified--
|
||||
if(mod_link.link_call)
|
||||
subtract_charge((DEFAULT_CHARGE_DRAIN * 0.25))
|
||||
if(!active)
|
||||
return
|
||||
if(get_charge() <= 10 && active && !activating) //Sometimes we get power being funky, this should fix it.
|
||||
power_off()
|
||||
return PROCESS_KILL
|
||||
return
|
||||
var/malfunctioning_charge_drain = 0
|
||||
if(malfunctioning)
|
||||
malfunctioning_charge_drain = rand(1, 20)
|
||||
@@ -294,6 +313,49 @@
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/control/multitool_act(mob/living/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
|
||||
return
|
||||
if(!I.multitool_check_buffer(user))
|
||||
return
|
||||
var/obj/item/multitool/M = I
|
||||
var/obj/item/linked_thing = locateUID(M.buffer_uid)
|
||||
|
||||
if(!linked_thing)
|
||||
to_chat(user, "<span class='notice'>You save the frequency of [src] to the buffer.</span>")
|
||||
M.buffer_uid = UID()
|
||||
return TRUE
|
||||
if(ismodcontrol(linked_thing))
|
||||
var/obj/item/mod/control/chosen_control = linked_thing
|
||||
var/response = tgui_alert(user, "Would you like to copy the frequency to the multitool or imprint the frequency to [src]?", "MODlink Frequency", list("Copy", "Imprint"))
|
||||
if(!user.is_holding(I))
|
||||
return FALSE
|
||||
switch(response)
|
||||
if("Copy")
|
||||
to_chat(user, "<span class='notice'>You save the frequency of [src] to the buffer.</span>")
|
||||
M.buffer_uid = UID()
|
||||
return TRUE
|
||||
if("Imprint")
|
||||
mod_link.frequency = chosen_control.mod_link.frequency
|
||||
to_chat(user, "<span class='notice'>You imprint the frequency to [src].</span>")
|
||||
return TRUE
|
||||
else
|
||||
var/obj/item/clothing/neck/link_scryer/chosen_scryer = linked_thing
|
||||
var/response = tgui_alert(user, "Would you like to copy or imprint the frequency?", "MODlink Frequency", list("Copy", "Imprint"))
|
||||
if(!user.is_holding(I))
|
||||
return FALSE
|
||||
switch(response)
|
||||
if("Copy")
|
||||
mod_link.frequency = chosen_scryer.mod_link.frequency
|
||||
to_chat(user, "<span class='notice'>You imprint the frequency to [src].</span>")
|
||||
return TRUE
|
||||
if("Imprint")
|
||||
to_chat(user, "<span class='notice'>You save the frequency of [src] to the buffer.</span>")
|
||||
M.buffer_uid = UID()
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/item/mod/control/screwdriver_act(mob/living/user, obj/item/screwdriver)
|
||||
if(..())
|
||||
return TRUE
|
||||
@@ -522,6 +584,7 @@
|
||||
retract(null, part)
|
||||
if(active)
|
||||
finish_activation(on = FALSE)
|
||||
mod_link?.end_call()
|
||||
var/mob/old_wearer = wearer
|
||||
unset_wearer()
|
||||
old_wearer.drop_item()
|
||||
@@ -605,6 +668,10 @@
|
||||
QDEL_LIST_ASSOC_VAL(old_module.pinned_to)
|
||||
old_module.mod = null
|
||||
|
||||
/// Intended for callbacks, don't use normally, just get wearer by itself.
|
||||
/obj/item/mod/control/proc/get_wearer()
|
||||
return wearer
|
||||
|
||||
/obj/item/mod/control/proc/update_access(mob/user, obj/item/card/id/card)
|
||||
if(!allowed(user))
|
||||
to_chat(user, "<span class='warning'>Insufficient access!")
|
||||
|
||||
@@ -0,0 +1,557 @@
|
||||
/proc/make_link_visual_generic(datum/mod_link/mod_link, proc_path)
|
||||
var/mob/living/user = mod_link.get_user_callback.Invoke()
|
||||
var/obj/effect/overlay/link_visual = new()
|
||||
link_visual.name = "holocall ([mod_link.id])"
|
||||
link_visual.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
link_visual.appearance_flags |= KEEP_TOGETHER
|
||||
link_visual.makeHologram(0.5)
|
||||
mod_link.visual_overlays = user.overlays
|
||||
link_visual.add_overlay(mod_link.visual_overlays)
|
||||
mod_link.visual = link_visual
|
||||
mod_link.holder.RegisterSignals(user, list(COMSIG_CARBON_APPLY_OVERLAY, COMSIG_CARBON_REMOVE_OVERLAY), proc_path)
|
||||
return link_visual
|
||||
|
||||
/// Makes this atom look like a "hologram"
|
||||
/// So transparent, blue, with a scanline and an emissive glow
|
||||
/// This is acomplished using a combination of filters and render steps/overlays
|
||||
/// The degree of the opacity is optional, based off the opacity arg (0 -> 1)
|
||||
/atom/proc/makeHologram(opacity = 0.5)
|
||||
// First, we'll make things blue (roughly) and sorta transparent
|
||||
add_filter("HOLO: Color and Transparent", 1, color_matrix_filter(rgb(125, 180, 225, opacity * 255)))
|
||||
// Now we're gonna do a scanline effect
|
||||
// Gonna take this atom and give it a render target, then use it as a source for a filter
|
||||
// (We use an atom because it seems as if setting render_target on an MA is just invalid. I hate this engine)
|
||||
var/atom/movable/scanline = new(null)
|
||||
scanline.icon = 'icons/effects/effects.dmi'
|
||||
scanline.icon_state = "scanline"
|
||||
scanline.appearance_flags |= RESET_TRANSFORM
|
||||
// * so it doesn't render
|
||||
var/static/uid_scan = 0
|
||||
scanline.render_target = "*HoloScanline [uid_scan]"
|
||||
uid_scan++
|
||||
// Now we add it as a filter, and overlay the appearance so the render source is always around
|
||||
add_filter("HOLO: Scanline", 2, alpha_mask_filter(render_source = scanline.render_target))
|
||||
add_overlay(scanline)
|
||||
qdel(scanline)
|
||||
// And let's make the sucker emissive, so it glows in the dark
|
||||
if(!render_target)
|
||||
var/static/uid = 0
|
||||
render_target = "HOLOGRAM [uid]"
|
||||
uid++
|
||||
|
||||
/proc/get_link_visual_generic(datum/mod_link/mod_link, atom/movable/visuals, proc_path)
|
||||
var/mob/living/user = mod_link.get_user_callback.Invoke()
|
||||
playsound(mod_link.holder, 'sound/machines/terminal_processing.ogg', 50, TRUE)
|
||||
visuals.add_overlay(mutable_appearance('icons/effects/effects.dmi', "static_base", ABOVE_NORMAL_TURF_LAYER))
|
||||
visuals.add_overlay(mutable_appearance('icons/effects/effects.dmi', "modlink", ABOVE_ALL_MOB_LAYER))
|
||||
visuals.add_filter("crop_square", 1, alpha_mask_filter(icon = icon('icons/effects/effects.dmi', "modlink_filter")))
|
||||
visuals.maptext_height = 6
|
||||
visuals.alpha = 0
|
||||
user.vis_contents += visuals
|
||||
visuals.forceMove(user)
|
||||
animate(visuals, 0.5 SECONDS, alpha = 255)
|
||||
var/datum/callback/setdir_callback = CALLBACK(mod_link.holder, proc_path)
|
||||
setdir_callback.Invoke(user, user.dir, user.dir)
|
||||
mod_link.holder.RegisterSignal(mod_link.holder.loc, COMSIG_ATOM_DIR_CHANGE, proc_path)
|
||||
|
||||
/proc/delete_link_visual_generic(datum/mod_link/mod_link)
|
||||
var/mob/living/user = mod_link.get_user_callback.Invoke()
|
||||
playsound(mod_link.get_other().holder, 'sound/machines/terminal_processing.ogg', 50, TRUE, -1)
|
||||
mod_link.holder.UnregisterSignal(user, list(COMSIG_CARBON_APPLY_OVERLAY, COMSIG_CARBON_REMOVE_OVERLAY, COMSIG_ATOM_DIR_CHANGE))
|
||||
QDEL_NULL(mod_link.visual)
|
||||
|
||||
/proc/on_user_set_dir_generic(datum/mod_link/mod_link, newdir)
|
||||
var/atom/other_visual = mod_link.get_other().visual
|
||||
if(!newdir) // can sometimes be null or 0
|
||||
return
|
||||
other_visual.setDir(SOUTH)
|
||||
other_visual.pixel_x = 0
|
||||
other_visual.pixel_y = 0
|
||||
var/matrix/new_transform = matrix()
|
||||
if(newdir & NORTH)
|
||||
other_visual.pixel_y = 13
|
||||
other_visual.layer = BELOW_MOB_LAYER
|
||||
if(newdir & SOUTH)
|
||||
other_visual.pixel_y = -24
|
||||
other_visual.layer = ABOVE_ALL_MOB_LAYER
|
||||
new_transform.Scale(-1, 1)
|
||||
new_transform.Translate(-1, 0)
|
||||
if(newdir & EAST)
|
||||
other_visual.pixel_x = 14
|
||||
other_visual.layer = BELOW_MOB_LAYER
|
||||
new_transform.Shear(0.5, 0)
|
||||
new_transform.Scale(0.65, 1)
|
||||
if(newdir & WEST)
|
||||
other_visual.pixel_x = -14
|
||||
other_visual.layer = BELOW_MOB_LAYER
|
||||
new_transform.Shear(-0.5, 0)
|
||||
new_transform.Scale(0.65, 1)
|
||||
other_visual.transform = new_transform
|
||||
|
||||
/obj/item/mod/control/proc/can_call()
|
||||
return get_charge() && wearer && wearer.stat < DEAD
|
||||
|
||||
/obj/item/mod/control/proc/make_link_visual()
|
||||
return make_link_visual_generic(mod_link, PROC_REF(on_overlay_change))
|
||||
|
||||
/obj/item/mod/control/proc/get_link_visual(atom/movable/visuals)
|
||||
return get_link_visual_generic(mod_link, visuals, PROC_REF(on_wearer_set_dir))
|
||||
|
||||
/obj/item/mod/control/proc/delete_link_visual()
|
||||
return delete_link_visual_generic(mod_link)
|
||||
|
||||
/obj/item/mod/control/hear_talk(mob/living/M, list/message_pieces)
|
||||
. = ..()
|
||||
if(M != wearer || !mod_link.visual)
|
||||
return
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(H.voice != M.real_name)
|
||||
mod_link.visual.name = H.voice
|
||||
else
|
||||
mod_link.visual.name = M.name
|
||||
else
|
||||
mod_link.visual.name = M.name
|
||||
mod_link.visual.atom_say(capitalize(multilingual_to_message(message_pieces)))
|
||||
|
||||
/obj/item/mod/control/proc/on_overlay_change(atom/source, cache_index, overlay)
|
||||
SIGNAL_HANDLER // COMSIG_CARBON_APPLY_OVERLAY, COMSIG_CARBON_REMOVE_OVERLAY
|
||||
addtimer(CALLBACK(src, PROC_REF(update_link_visual)), 0.2 SECONDS, TIMER_UNIQUE)
|
||||
|
||||
/obj/item/mod/control/proc/update_link_visual()
|
||||
if(QDELETED(mod_link.link_call))
|
||||
return
|
||||
mod_link.visual.cut_overlay(mod_link.visual_overlays)
|
||||
mod_link.visual_overlays = wearer.overlays
|
||||
mod_link.visual.add_overlay(mod_link.visual_overlays)
|
||||
|
||||
/obj/item/mod/control/proc/on_wearer_set_dir(atom/source, dir, newdir)
|
||||
SIGNAL_HANDLER // COMSIG_ATOM_DIR_CHANGE
|
||||
on_user_set_dir_generic(mod_link, newdir || SOUTH)
|
||||
|
||||
/obj/item/clothing/neck/link_scryer
|
||||
name = "\improper MODlink scryer"
|
||||
desc = "An intricate piece of machinery that creates a holographic video call with another MODlink-compatible device. Essentially a video necklace."
|
||||
icon_state = "modlink"
|
||||
actions_types = list(/datum/action/item_action/call_link)
|
||||
/// The installed power cell.
|
||||
var/obj/item/stock_parts/cell/cell
|
||||
/// The MODlink datum we operate.
|
||||
var/datum/mod_link/mod_link
|
||||
/// Initial frequency of the MODlink.
|
||||
var/starting_frequency
|
||||
/// An additional name tag for the scryer, seen as "MODlink scryer - [label]"
|
||||
var/label
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/Initialize(mapload)
|
||||
. = ..()
|
||||
mod_link = new(
|
||||
src,
|
||||
starting_frequency,
|
||||
CALLBACK(src, PROC_REF(get_user)),
|
||||
CALLBACK(src, PROC_REF(can_call)),
|
||||
CALLBACK(src, PROC_REF(make_link_visual)),
|
||||
CALLBACK(src, PROC_REF(get_link_visual)),
|
||||
CALLBACK(src, PROC_REF(delete_link_visual))
|
||||
)
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/Destroy()
|
||||
QDEL_NULL(cell)
|
||||
QDEL_NULL(mod_link)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/examine(mob/user)
|
||||
. = ..()
|
||||
if(cell)
|
||||
. += "<span class='notice'>The battery charge reads [cell.percent()]%. Use a <b>Screwdriver</b> to remove it.</span>"
|
||||
else
|
||||
. += "<span class='notice'>It is missing a battery, one can be installed by clicking with a power cell on it.</span>"
|
||||
. += "<span class='notice'>The MODlink ID is [mod_link.id], frequency is [mod_link.frequency || "unset"]. <b>Use</b> a multitool to copy/imprint frequency.</span>"
|
||||
. += "<span class='notice'>Use in hand to set name.</span>"
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/equipped(mob/living/user, slot)
|
||||
. = ..()
|
||||
if(slot != ITEM_SLOT_NECK)
|
||||
mod_link?.end_call()
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/dropped(mob/living/user)
|
||||
. = ..()
|
||||
mod_link?.end_call()
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/attack_self__legacy__attackchain(mob/user)
|
||||
var/new_label = reject_bad_text(tgui_input_text(user, "Change the visible name", "Set Name", label, MAX_NAME_LEN))
|
||||
if(!user.is_holding(src))
|
||||
return
|
||||
if(!new_label)
|
||||
to_chat(user, "<span class='warning'>That name is invalid!</span>")
|
||||
return
|
||||
label = new_label
|
||||
to_chat(user, "<span class='notice'>You set the name as [label].</span>")
|
||||
update_appearance(UPDATE_NAME)
|
||||
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/multitool_act(mob/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
|
||||
return
|
||||
if(!I.multitool_check_buffer(user))
|
||||
return
|
||||
var/obj/item/multitool/M = I
|
||||
var/obj/item/linked_thing = locateUID(M.buffer_uid)
|
||||
|
||||
if(!linked_thing)
|
||||
to_chat(user, "<span class='notice'>You save the frequency of [src] to the buffer.</span>")
|
||||
M.buffer_uid = UID()
|
||||
return TRUE
|
||||
if(ismodcontrol(linked_thing))
|
||||
var/obj/item/mod/control/chosen_control = linked_thing
|
||||
var/response = tgui_alert(user, "Would you like to copy the frequency to the multitool or imprint the frequency to [src]?", "MODlink Frequency", list("Copy", "Imprint"))
|
||||
if(!user.is_holding(I))
|
||||
return FALSE
|
||||
switch(response)
|
||||
if("Copy")
|
||||
to_chat(user, "<span class='notice'>You save the frequency of [src] to the buffer.</span>")
|
||||
M.buffer_uid = UID()
|
||||
return TRUE
|
||||
if("Imprint")
|
||||
mod_link.frequency = chosen_control.mod_link.frequency
|
||||
to_chat(user, "<span class='notice'>You imprint the frequency to [src].</span>")
|
||||
return TRUE
|
||||
else
|
||||
var/obj/item/clothing/neck/link_scryer/chosen_scryer = linked_thing
|
||||
var/response = tgui_alert(user, "Would you like to copy or imprint the frequency?", "MODlink Frequency", list("Copy", "Imprint"))
|
||||
if(!user.is_holding(I))
|
||||
return FALSE
|
||||
switch(response)
|
||||
if("Copy")
|
||||
mod_link.frequency = chosen_scryer.mod_link.frequency
|
||||
to_chat(user, "<span class='notice'>You imprint the frequency to [src].</span>")
|
||||
return TRUE
|
||||
if("Imprint")
|
||||
to_chat(user, "<span class='notice'>You save the frequency of [src] to the buffer.</span>")
|
||||
M.buffer_uid = UID()
|
||||
return TRUE
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/get_cell()
|
||||
return cell
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/process()
|
||||
if(!mod_link.link_call)
|
||||
return
|
||||
cell.use(200)
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/attackby__legacy__attackchain(obj/item/O, mob/user, params)
|
||||
. = ..()
|
||||
if(cell || !istype(O, /obj/item/stock_parts/cell))
|
||||
return
|
||||
if(!user.drop_item())
|
||||
to_chat(user, "<span class='warning'>[O] is stuck to your hand!</span>")
|
||||
return
|
||||
O.forceMove(src)
|
||||
cell = O
|
||||
to_chat(user, "<span class='notice'>You load [O] into [src].</span>")
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/update_name(updates)
|
||||
. = ..()
|
||||
name = "[initial(name)][label ? " - [label]" : ""]"
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/Exited(atom/movable/gone, direction)
|
||||
. = ..()
|
||||
if(gone == cell)
|
||||
cell = null
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/screwdriver_act(mob/living/user, obj/item/I)
|
||||
. = ..()
|
||||
if(!cell)
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You remove [cell] from [src].</span>")
|
||||
user.put_in_hands(cell)
|
||||
return
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/ui_action_click(mob/user)
|
||||
if(mod_link.link_call)
|
||||
mod_link.end_call()
|
||||
else
|
||||
call_link(user, mod_link)
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/proc/get_user()
|
||||
var/mob/living/carbon/human/user = loc
|
||||
return istype(user) && user.neck == src ? user : null
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/proc/can_call()
|
||||
var/mob/living/user = loc
|
||||
return istype(user) && cell?.charge && user.stat < DEAD
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/proc/make_link_visual()
|
||||
var/mob/living/user = mod_link.get_user_callback.Invoke()
|
||||
user.update_inv_neck()
|
||||
return make_link_visual_generic(mod_link, PROC_REF(on_overlay_change))
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/proc/get_link_visual(atom/movable/visuals)
|
||||
return get_link_visual_generic(mod_link, visuals, PROC_REF(on_user_set_dir))
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/proc/delete_link_visual()
|
||||
var/mob/living/user = mod_link.get_user_callback.Invoke()
|
||||
if(!QDELETED(user))
|
||||
user.update_inv_neck()
|
||||
return delete_link_visual_generic(mod_link)
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/hear_talk(mob/living/M, list/message_pieces)
|
||||
. = ..()
|
||||
if(M != loc || !mod_link.visual)
|
||||
return
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(H.voice != M.real_name)
|
||||
mod_link.visual.name = H.voice
|
||||
else
|
||||
mod_link.visual.name = M.name
|
||||
mod_link.visual.atom_say(capitalize(multilingual_to_message(message_pieces)))
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/proc/on_overlay_change(atom/source, cache_index, overlay)
|
||||
SIGNAL_HANDLER // COMSIG_CARBON_APPLY_OVERLAY, COMSIG_CARBON_REMOVE_OVERLAY
|
||||
addtimer(CALLBACK(src, PROC_REF(update_link_visual)), 0.2 SECONDS, TIMER_UNIQUE)
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/proc/update_link_visual()
|
||||
if(QDELETED(mod_link.link_call))
|
||||
return
|
||||
var/mob/living/user = loc
|
||||
mod_link.visual.cut_overlay(mod_link.visual_overlays)
|
||||
mod_link.visual_overlays = user.overlays
|
||||
mod_link.visual.add_overlay(mod_link.visual_overlays)
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/proc/on_user_set_dir(atom/source, dir, newdir)
|
||||
SIGNAL_HANDLER // COMSIG_ATOM_DIR_CHANGE
|
||||
on_user_set_dir_generic(mod_link, newdir || SOUTH)
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/loaded
|
||||
starting_frequency = MODLINK_FREQ_NANOTRASEN
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/loaded/Initialize(mapload)
|
||||
. = ..()
|
||||
cell = new /obj/item/stock_parts/cell/high(src)
|
||||
|
||||
/obj/item/clothing/neck/link_scryer/loaded/theta
|
||||
starting_frequency = MODLINK_FREQ_THETA
|
||||
|
||||
/// A MODlink datum, used to handle unique functions that will be used in the MODlink call.
|
||||
/datum/mod_link
|
||||
/// Generic name for multitool buffers.
|
||||
var/name = "MODlink"
|
||||
/// The frequency of the MODlink. You can only call other MODlinks on the same frequency.
|
||||
var/frequency
|
||||
/// The unique ID of the MODlink.
|
||||
var/id = ""
|
||||
/// The atom that holds the MODlink.
|
||||
var/atom/movable/holder
|
||||
/// A reference to the visuals generated by the MODlink.
|
||||
var/atom/movable/visual
|
||||
/// A list of all overlays of the user, copied everytime they have an overlay change.
|
||||
var/list/visual_overlays = list()
|
||||
/// A reference to the call between two MODlinks.
|
||||
var/datum/mod_link_call/link_call
|
||||
/// A callback that returns the user of the MODlink.
|
||||
var/datum/callback/get_user_callback
|
||||
/// A callback that returns whether the MODlink can currently call.
|
||||
var/datum/callback/can_call_callback
|
||||
/// A callback that returns the visuals of the MODlink.
|
||||
var/datum/callback/make_visual_callback
|
||||
/// A callback that receives the visuals of the other MODlink.
|
||||
var/datum/callback/get_visual_callback
|
||||
/// A callback that deletes the visuals of the MODlink.
|
||||
var/datum/callback/delete_visual_callback
|
||||
|
||||
/datum/mod_link/New(
|
||||
atom/holder,
|
||||
frequency,
|
||||
datum/callback/get_user_callback,
|
||||
datum/callback/can_call_callback,
|
||||
datum/callback/make_visual_callback,
|
||||
datum/callback/get_visual_callback,
|
||||
datum/callback/delete_visual_callback
|
||||
)
|
||||
var/attempts = 0
|
||||
var/digits_to_make = 3
|
||||
do
|
||||
if(attempts == 10)
|
||||
attempts = 0
|
||||
digits_to_make++
|
||||
id = ""
|
||||
for(var/i in 1 to digits_to_make)
|
||||
id += num2text(rand(0, 9))
|
||||
attempts++
|
||||
while(GLOB.mod_link_ids[id])
|
||||
GLOB.mod_link_ids[id] = src
|
||||
src.frequency = frequency
|
||||
src.holder = holder
|
||||
src.get_user_callback = get_user_callback
|
||||
src.can_call_callback = can_call_callback
|
||||
src.make_visual_callback = make_visual_callback
|
||||
src.get_visual_callback = get_visual_callback
|
||||
src.delete_visual_callback = delete_visual_callback
|
||||
RegisterSignal(holder, COMSIG_PARENT_QDELETING, PROC_REF(on_holder_delete))
|
||||
|
||||
/datum/mod_link/Destroy()
|
||||
GLOB.mod_link_ids -= id
|
||||
if(link_call)
|
||||
end_call()
|
||||
get_user_callback = null
|
||||
make_visual_callback = null
|
||||
get_visual_callback = null
|
||||
delete_visual_callback = null
|
||||
return ..()
|
||||
|
||||
/datum/mod_link/proc/get_other()
|
||||
RETURN_TYPE(/datum/mod_link)
|
||||
if(!link_call)
|
||||
return
|
||||
return link_call.link_caller == src ? link_call.link_receiver : link_call.link_caller
|
||||
|
||||
/datum/mod_link/proc/call_link(datum/mod_link/called, mob/user)
|
||||
if(!frequency)
|
||||
return
|
||||
if(!istype(called))
|
||||
to_chat(user, "<span class='warning'>That target is invalid!</span>")
|
||||
return
|
||||
var/mob/living/link_user = get_user_callback.Invoke()
|
||||
if(!link_user)
|
||||
return
|
||||
if(HAS_TRAIT(link_user, TRAIT_IN_CALL))
|
||||
to_chat(user, "<span class='warning'>You are already calling someone!</span>")
|
||||
return
|
||||
var/mob/living/link_target = called.get_user_callback.Invoke()
|
||||
if(!link_target)
|
||||
to_chat(user, "<span class='warning'>That target is invalid!</span>")
|
||||
return
|
||||
if(HAS_TRAIT(link_target, TRAIT_IN_CALL))
|
||||
to_chat(user, "<span class='warning'>The person you are calling is already in a call!</span>")
|
||||
return
|
||||
if(!can_call_callback.Invoke() || !called.can_call_callback.Invoke())
|
||||
to_chat(user, "<span class='warning'>You are unable to call at this time!</span>")
|
||||
return
|
||||
link_target.playsound_local(get_turf(called.holder), 'sound/weapons/ring.ogg', 15, TRUE)
|
||||
var/atom/movable/screen/alert/modlink_call/alert = link_target.throw_alert("[UID()]_modlink", /atom/movable/screen/alert/modlink_call)
|
||||
alert.desc = "[holder] ([id]) is calling you! Left-click this to accept the call. Right-click to deny it."
|
||||
alert.link_caller_uid = UID()
|
||||
alert.link_receiver_uid = called.UID()
|
||||
alert.user_uid = user.UID()
|
||||
|
||||
/datum/mod_link/proc/end_call()
|
||||
QDEL_NULL(link_call)
|
||||
|
||||
/datum/mod_link/proc/on_holder_delete(atom/source)
|
||||
SIGNAL_HANDLER
|
||||
qdel(src)
|
||||
|
||||
/// A MODlink call datum, used to handle the call between two MODlinks.
|
||||
/datum/mod_link_call
|
||||
/// The MODlink that is calling.
|
||||
var/datum/mod_link/link_caller
|
||||
/// The MODlink that is being called.
|
||||
var/datum/mod_link/link_receiver
|
||||
|
||||
/datum/mod_link_call/New(datum/mod_link/link_caller, datum/mod_link/link_receiver)
|
||||
link_caller.link_call = src
|
||||
link_receiver.link_call = src
|
||||
src.link_caller = link_caller
|
||||
src.link_receiver = link_receiver
|
||||
var/mob/living/caller_mob = link_caller.get_user_callback.Invoke()
|
||||
ADD_TRAIT(caller_mob, TRAIT_IN_CALL, UID())
|
||||
var/mob/living/receiver_mob = link_receiver.get_user_callback.Invoke()
|
||||
ADD_TRAIT(receiver_mob, TRAIT_IN_CALL, UID())
|
||||
make_visuals()
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
|
||||
/datum/mod_link_call/Destroy()
|
||||
var/mob/living/caller_mob = link_caller.get_user_callback.Invoke()
|
||||
if(!QDELETED(caller_mob))
|
||||
REMOVE_TRAIT(caller_mob, TRAIT_IN_CALL, UID())
|
||||
var/mob/living/receiver_mob = link_receiver.get_user_callback.Invoke()
|
||||
if(!QDELETED(receiver_mob))
|
||||
REMOVE_TRAIT(receiver_mob, TRAIT_IN_CALL, UID())
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
clear_visuals()
|
||||
link_caller.link_call = null
|
||||
link_receiver.link_call = null
|
||||
return ..()
|
||||
|
||||
/datum/mod_link_call/process(seconds_per_tick)
|
||||
if(can_continue_call())
|
||||
return
|
||||
qdel(src)
|
||||
|
||||
/datum/mod_link_call/proc/can_continue_call()
|
||||
return link_caller.frequency == link_receiver.frequency && link_caller.can_call_callback.Invoke() && link_receiver.can_call_callback.Invoke()
|
||||
|
||||
/datum/mod_link_call/proc/make_visuals()
|
||||
var/caller_visual = link_caller.make_visual_callback.Invoke()
|
||||
var/receiver_visual = link_receiver.make_visual_callback.Invoke()
|
||||
link_caller.get_visual_callback.Invoke(receiver_visual)
|
||||
link_receiver.get_visual_callback.Invoke(caller_visual)
|
||||
|
||||
/datum/mod_link_call/proc/clear_visuals()
|
||||
link_caller.delete_visual_callback.Invoke()
|
||||
link_receiver.delete_visual_callback.Invoke()
|
||||
|
||||
/proc/call_link(mob/user, datum/mod_link/calling_link)
|
||||
if(!calling_link.frequency)
|
||||
return
|
||||
var/list/callers = list()
|
||||
for(var/id in GLOB.mod_link_ids)
|
||||
var/datum/mod_link/link = GLOB.mod_link_ids[id]
|
||||
if(link.frequency != calling_link.frequency)
|
||||
continue
|
||||
if(link == calling_link)
|
||||
continue
|
||||
if(!link.can_call_callback.Invoke())
|
||||
continue
|
||||
callers["[link.holder] ([id])"] = id
|
||||
if(!length(callers))
|
||||
to_chat(user, "<span class='notice'>There are no targets on freq [calling_link.frequency].</span>")
|
||||
return
|
||||
var/chosen_link = tgui_input_list(user, "Choose ID to call from [calling_link.frequency] frequency", "MODlink", callers)
|
||||
if(!chosen_link)
|
||||
return
|
||||
calling_link.call_link(GLOB.mod_link_ids[callers[chosen_link]], user)
|
||||
|
||||
/atom/movable/screen/alert/modlink_call
|
||||
name = "MODlink Call Incoming"
|
||||
desc = "Someone is calling you! Left-click this to accept the call. Alt-click to deny it."
|
||||
icon_state = "called"
|
||||
timeout = 10 SECONDS
|
||||
var/end_message = "call timed out!"
|
||||
/// A UID to the MODlink that is calling.
|
||||
var/link_caller_uid
|
||||
/// A UID to the MODlink that is being called.
|
||||
var/link_receiver_uid
|
||||
/// A UID to the mob that is calling.
|
||||
var/user_uid
|
||||
|
||||
/atom/movable/screen/alert/modlink_call/Click(location, control, params)
|
||||
. = ..()
|
||||
if(usr != owner)
|
||||
return
|
||||
var/datum/mod_link/link_caller = locateUID(link_caller_uid)
|
||||
var/datum/mod_link/link_receiver = locateUID(link_receiver_uid)
|
||||
if(!link_caller || !link_receiver)
|
||||
return
|
||||
if(link_caller.link_call || link_receiver.link_call)
|
||||
return
|
||||
var/list/modifiers = params2list(params)
|
||||
if(LAZYACCESS(modifiers, ALT_CLICK))
|
||||
end_message = "The call was denied!"
|
||||
owner.clear_alert("[link_caller.UID()]_modlink")
|
||||
return
|
||||
end_message = "The call was accepted!"
|
||||
new /datum/mod_link_call(link_caller, link_receiver)
|
||||
owner.clear_alert("[link_caller.UID()]_modlink")
|
||||
|
||||
/atom/movable/screen/alert/modlink_call/Destroy()
|
||||
var/mob/living/user = locateUID(user_uid)
|
||||
var/datum/mod_link/link_caller = locateUID(link_caller_uid)
|
||||
if(!user || !link_caller)
|
||||
return ..()
|
||||
to_chat(user, "<span class='notice'>[end_message]</span>")
|
||||
return ..()
|
||||
@@ -1,4 +1,5 @@
|
||||
/obj/item/mod/control/pre_equipped
|
||||
starting_frequency = MODLINK_FREQ_NANOTRASEN
|
||||
/// The skin we apply to the suit, defaults to the default_skin of the theme.
|
||||
var/applied_skin
|
||||
/// The MOD core we apply to the suit.
|
||||
@@ -252,6 +253,7 @@
|
||||
)
|
||||
|
||||
/obj/item/mod/control/pre_equipped/traitor
|
||||
starting_frequency = MODLINK_FREQ_SYNDICATE
|
||||
theme = /datum/mod_theme/syndicate
|
||||
applied_cell = /obj/item/stock_parts/cell/super
|
||||
applied_modules = list(
|
||||
@@ -272,6 +274,7 @@
|
||||
new /obj/item/tank/internals/emergency_oxygen/engi/syndi(bag)
|
||||
|
||||
/obj/item/mod/control/pre_equipped/traitor_elite
|
||||
starting_frequency = MODLINK_FREQ_SYNDICATE
|
||||
theme = /datum/mod_theme/elite
|
||||
applied_cell = /obj/item/stock_parts/cell/bluespace
|
||||
applied_modules = list(
|
||||
@@ -293,6 +296,7 @@
|
||||
new /obj/item/tank/internals/emergency_oxygen/engi/syndi(bag)
|
||||
|
||||
/obj/item/mod/control/pre_equipped/nuclear
|
||||
starting_frequency = MODLINK_FREQ_SYNDICATE
|
||||
theme = /datum/mod_theme/syndicate
|
||||
applied_cell = /obj/item/stock_parts/cell/hyper
|
||||
req_access = list(ACCESS_SYNDICATE)
|
||||
@@ -313,6 +317,7 @@
|
||||
ADD_TRAIT(chestplate, TRAIT_HYPOSPRAY_IMMUNE, ROUNDSTART_TRAIT)
|
||||
|
||||
/obj/item/mod/control/pre_equipped/elite
|
||||
starting_frequency = MODLINK_FREQ_SYNDICATE
|
||||
theme = /datum/mod_theme/elite
|
||||
applied_cell = /obj/item/stock_parts/cell/bluespace
|
||||
req_access = list(ACCESS_SYNDICATE)
|
||||
@@ -334,6 +339,7 @@
|
||||
ADD_TRAIT(chestplate, TRAIT_HYPOSPRAY_IMMUNE, ROUNDSTART_TRAIT)
|
||||
|
||||
/obj/item/mod/control/pre_equipped/prototype
|
||||
starting_frequency = MODLINK_FREQ_THETA
|
||||
theme = /datum/mod_theme/prototype
|
||||
req_access = list()
|
||||
applied_modules = list(
|
||||
@@ -349,6 +355,7 @@
|
||||
)
|
||||
|
||||
/obj/item/mod/control/pre_equipped/responsory
|
||||
starting_frequency = MODLINK_FREQ_CENTCOM
|
||||
theme = /datum/mod_theme/responsory
|
||||
applied_cell = /obj/item/stock_parts/cell/bluespace
|
||||
req_access = list(ACCESS_CENT_GENERAL)
|
||||
@@ -437,6 +444,7 @@
|
||||
additional_module = /obj/item/mod/module/power_kick //JUDGEMENT
|
||||
|
||||
/obj/item/mod/control/pre_equipped/apocryphal
|
||||
starting_frequency = MODLINK_FREQ_CENTCOM
|
||||
theme = /datum/mod_theme/apocryphal
|
||||
applied_cell = /obj/item/stock_parts/cell/bluespace
|
||||
req_access = list(ACCESS_CENT_SPECOPS)
|
||||
@@ -474,6 +482,7 @@
|
||||
)
|
||||
|
||||
/obj/item/mod/control/pre_equipped/corporate
|
||||
starting_frequency = MODLINK_FREQ_CENTCOM
|
||||
theme = /datum/mod_theme/corporate
|
||||
applied_core = /obj/item/mod/core/infinite
|
||||
req_access = list(ACCESS_CENT_SPECOPS)
|
||||
@@ -490,6 +499,7 @@
|
||||
)
|
||||
|
||||
/obj/item/mod/control/pre_equipped/debug
|
||||
starting_frequency = null
|
||||
theme = /datum/mod_theme/debug
|
||||
applied_core = /obj/item/mod/core/infinite
|
||||
/// One of every type of module, for testing if they all work correctly // boy this isn't even 25% the modules
|
||||
@@ -511,6 +521,7 @@
|
||||
activation_step_time = 0.1 SECONDS // coders are cooler than admins
|
||||
|
||||
/obj/item/mod/control/pre_equipped/administrative
|
||||
starting_frequency = null
|
||||
theme = /datum/mod_theme/administrative
|
||||
applied_core = /obj/item/mod/core/infinite
|
||||
applied_modules = list(
|
||||
@@ -529,6 +540,7 @@
|
||||
|
||||
//these exist for the prefs menu
|
||||
/obj/item/mod/control/pre_equipped/empty
|
||||
starting_frequency = null
|
||||
|
||||
/obj/item/mod/control/pre_equipped/empty/syndicate
|
||||
theme = /datum/mod_theme/syndicate
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
data["malfunctioning"] = malfunctioning
|
||||
data["open"] = open
|
||||
data["active"] = active
|
||||
data["link_id"] = mod_link.id
|
||||
data["link_freq"] = mod_link.frequency
|
||||
data["link_call"] = mod_link.get_other()?.id
|
||||
data["locked"] = locked
|
||||
data["complexity"] = complexity
|
||||
data["selected_module"] = selected_module?.name
|
||||
@@ -65,6 +68,11 @@
|
||||
to_chat(usr, "<span class='warning'>ERROR!</span>")
|
||||
return
|
||||
switch(action)
|
||||
if("call")
|
||||
if(!mod_link.link_call)
|
||||
call_link(ui.user, mod_link)
|
||||
else
|
||||
mod_link.end_call()
|
||||
if("lock")
|
||||
locked = !locked
|
||||
to_chat(usr, "<span class='notice'>ID [locked ? "locked" : "unlocked"].</span>")
|
||||
|
||||
@@ -52,6 +52,16 @@
|
||||
build_path = /obj/item/mod/construction/boots
|
||||
category = list("MODsuit Construction")
|
||||
|
||||
/datum/design/mod_scryer
|
||||
name = "MODlink Scryer"
|
||||
desc = "A neck-worn piece of gear that can call with another MODlink-compatible device."
|
||||
id = "mod_scryer"
|
||||
build_type = PROTOLATHE | MECHFAB
|
||||
materials = list(MAT_METAL = 2000, MAT_GLASS = 1000, MAT_GOLD = 500)
|
||||
construction_time = 2 SECONDS
|
||||
build_path = /obj/item/clothing/neck/link_scryer
|
||||
category = list("MODsuit Construction", "Equipment")
|
||||
|
||||
/datum/design/mod_plating
|
||||
name = "MOD External Plating"
|
||||
desc = "External plating for a MODsuit."
|
||||
|
||||
|
Before Width: | Height: | Size: 470 KiB After Width: | Height: | Size: 490 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@@ -2674,6 +2674,7 @@
|
||||
#include "code\modules\mod\mod_construction.dm"
|
||||
#include "code\modules\mod\mod_control.dm"
|
||||
#include "code\modules\mod\mod_core.dm"
|
||||
#include "code\modules\mod\mod_link.dm"
|
||||
#include "code\modules\mod\mod_paint.dm"
|
||||
#include "code\modules\mod\mod_theme.dm"
|
||||
#include "code\modules\mod\mod_types.dm"
|
||||
|
||||
@@ -438,8 +438,19 @@ const displayText = (param) => {
|
||||
|
||||
const ParametersSection = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { active, malfunctioning, locked, open, selected_module, complexity, complexity_max, wearer_name, wearer_job } =
|
||||
data;
|
||||
const {
|
||||
active,
|
||||
malfunctioning,
|
||||
locked,
|
||||
open,
|
||||
selected_module,
|
||||
complexity,
|
||||
complexity_max,
|
||||
wearer_name,
|
||||
wearer_job,
|
||||
link_call,
|
||||
link_id,
|
||||
} = data;
|
||||
const status = malfunctioning ? 'Malfunctioning' : active ? 'Active' : 'Inactive';
|
||||
return (
|
||||
<Section title="Parameters">
|
||||
@@ -464,6 +475,20 @@ const ParametersSection = (props, context) => {
|
||||
>
|
||||
{locked ? 'Locked' : 'Unlocked'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="MODLink"
|
||||
buttons={
|
||||
<Button
|
||||
icon={'wifi'}
|
||||
color={link_call ? 'good' : 'default'}
|
||||
disabled={!link_id}
|
||||
content={link_call ? 'Calling (' + link_call + ')' : 'Call (' + link_id + ')'}
|
||||
onClick={() => act('call')}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{link_call ? 'Call Active' : 'No Active Call'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Cover">{open ? 'Open' : 'Closed'}</LabeledList.Item>
|
||||
<LabeledList.Item label="Selected Module">{selected_module || 'None'}</LabeledList.Item>
|
||||
<LabeledList.Item label="Complexity">
|
||||
|
||||