Bringing back up to date

This commit is contained in:
Hazelrat
2026-08-14 21:52:32 +01:00
parent f9571a1861
commit af494713d3
19 changed files with 3375 additions and 0 deletions
@@ -0,0 +1,172 @@
// ----------------------------------- main defs
///
/obj/effect/map_effect/marker/door_paint
var/fill_color = null
var/stripe_color = null
var/frame_color = null
/obj/effect/map_effect/marker/door_paint/Initialize(mapload, ...)
..()
return INITIALIZE_HINT_LATELOAD
/obj/effect/map_effect/marker/door_paint/LateInitialize()
for(var/thing in loc)
var/obj/structure/machinery/door/airlock/door = thing
if(!istype(door))
continue
door.paintable = AIRLOCK_PAINTABLE_MAIN | AIRLOCK_PAINTABLE_STRIPE
if(fill_color)
door.door_color = fill_color
if(stripe_color)
door.stripe_color = stripe_color
if(frame_color)
door.door_frame_color = frame_color
door.update_icon()
// ----------------------------------- main defs
/obj/effect/map_effect/marker/door_paint/fill
icon_state = "marker_door_paint_fill"
/obj/effect/map_effect/marker/door_paint/fill/LateInitialize()
fill_color = color
. = ..()
/obj/effect/map_effect/marker/door_paint/stripe
icon_state = "marker_door_paint_stripe"
/obj/effect/map_effect/marker/door_paint/stripe/LateInitialize()
stripe_color = color
. = ..()
/obj/effect/map_effect/marker/door_paint/frame
icon_state = "marker_door_paint_frame"
/obj/effect/map_effect/marker/door_paint/frame/LateInitialize()
frame_color = color
. = ..()
// ----------------------------------- department subtypes
// to be filled with department color defines, if they were ever to be decided
// ----------------------------------- color fill subtypes
/obj/effect/map_effect/marker/door_paint/fill/black
color = COLOR_GRAY20
/obj/effect/map_effect/marker/door_paint/fill/blue
color = COLOR_BLUE_GRAY
/obj/effect/map_effect/marker/door_paint/fill/paleblue
color = COLOR_PALE_BLUE_GRAY
/obj/effect/map_effect/marker/door_paint/fill/dark_blue
color = COLOR_DARK_BLUE_GRAY
/obj/effect/map_effect/marker/door_paint/fill/dark_green
color = COLOR_DARK_GREEN_GRAY
/obj/effect/map_effect/marker/door_paint/fill/green
color = COLOR_GREEN_GRAY
/obj/effect/map_effect/marker/door_paint/fill/lime
color = COLOR_PALE_GREEN_GRAY
/obj/effect/map_effect/marker/door_paint/fill/yellow
color = COLOR_BROWN
/obj/effect/map_effect/marker/door_paint/fill/beige
color = COLOR_BEIGE
/obj/effect/map_effect/marker/door_paint/fill/red
color = COLOR_RED_GRAY
/obj/effect/map_effect/marker/door_paint/fill/pink
color = COLOR_PALE_RED_GRAY
/obj/effect/map_effect/marker/door_paint/fill/purple
color = COLOR_PURPLE_GRAY
/obj/effect/map_effect/marker/door_paint/fill/mauve
color = COLOR_PALE_PURPLE_GRAY
/obj/effect/map_effect/marker/door_paint/fill/orange
color = COLOR_DARK_ORANGE
/obj/effect/map_effect/marker/door_paint/fill/brown
color = COLOR_DARK_BROWN
/obj/effect/map_effect/marker/door_paint/fill/white
color = COLOR_GRAY70
/obj/effect/map_effect/marker/door_paint/fill/grey
color = COLOR_GRAY
// ----------------------------------- color stripe subtypes
/obj/effect/map_effect/marker/door_paint/stripe/gold
color = COLOR_GOLD
/obj/effect/map_effect/marker/door_paint/stripe/black
color = COLOR_GRAY20
/obj/effect/map_effect/marker/door_paint/stripe/blue
color = COLOR_BLUE_GRAY
/obj/effect/map_effect/marker/door_paint/stripe/paleblue
color = COLOR_PALE_BLUE_GRAY
/obj/effect/map_effect/marker/door_paint/stripe/dark_blue
color = COLOR_DARK_BLUE_GRAY
/obj/effect/map_effect/marker/door_paint/stripe/dark_green
color = COLOR_DARK_GREEN_GRAY
/obj/effect/map_effect/marker/door_paint/stripe/green
color = COLOR_GREEN_GRAY
/obj/effect/map_effect/marker/door_paint/stripe/lime
color = COLOR_PALE_GREEN_GRAY
/obj/effect/map_effect/marker/door_paint/stripe/yellow
color = COLOR_BROWN
/obj/effect/map_effect/marker/door_paint/stripe/beige
color = COLOR_BEIGE
/obj/effect/map_effect/marker/door_paint/stripe/red
color = COLOR_RED_GRAY
/obj/effect/map_effect/marker/door_paint/stripe/pink
color = COLOR_PALE_RED_GRAY
/obj/effect/map_effect/marker/door_paint/stripe/purple
color = COLOR_PURPLE_GRAY
/obj/effect/map_effect/marker/door_paint/stripe/mauve
color = COLOR_PALE_PURPLE_GRAY
/obj/effect/map_effect/marker/door_paint/stripe/orange
color = COLOR_DARK_ORANGE
/obj/effect/map_effect/marker/door_paint/stripe/brown
color = COLOR_DARK_BROWN
/obj/effect/map_effect/marker/door_paint/stripe/white
color = COLOR_GRAY70
/obj/effect/map_effect/marker/door_paint/stripe/grey
color = COLOR_GRAY
// ----------------------------------- color frame subtypes
/obj/effect/map_effect/marker/door_paint/frame/scc
color = /turf/simulated/wall/shuttle/scc::color
/obj/effect/map_effect/marker/door_paint/frame/brown
color = /turf/simulated/wall/shuttle/brown::color
// ----------------------------------- fin
@@ -0,0 +1,184 @@
// Big, mean hivebot with incendiary ammunition. For use in boss battles.
// The Life() code for rampancy messages should be scrubbed if rampancy is ever made a proper subsystem.
/mob/living/simple_animal/hostile/hivebot_stickbug
name = "\improper lesser transmitter drone"
desc = "An enormous hivebot, resembling nothing so much as a twisted human spine with a long stinger-like \
appendage. It seems to be constantly crackling, as if broadcasting some low-level signal."
icon = 'icons/mob/npc/hivebot_stickbug.dmi'
icon_state = "stickbug"
icon_living = "stickbug"
maxhealth = 1000
health = 1000
melee_damage_lower = 40
melee_damage_upper = 40
armor_penetration = 20
projectilesound = 'sound/weapons/plasma_cutter.ogg'
projectiletype = /obj/projectile/beam/hivebot/incendiary/boss
organ_names = list("antenna", "core", "primary appendage", "secondary appendage", "tertiary appendage", "stinger")
attack_flags = DAMAGE_FLAG_SHARP|DAMAGE_FLAG_EDGE
attacktext = "maimed"
attack_sound = SFX_HIVEBOT_MELEE
blood_color = COLOR_OIL
blood_type = COLOR_OIL
blood_overlay_icon = 'icons/mob/npc/blood_overlay_hivebot.dmi'
pass_flags = PASSTABLE|PASSRAILING
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
minbodytemp = 0
faction = "hivebot"
destroy_surroundings = 0
wander = FALSE
attack_emote = "focuses on"
emote_hear = list("emits a harsh noise")
emote_sounds = list(
'sound/effects/creatures/hivebot/hivebot-bark-001.ogg',
'sound/effects/creatures/hivebot/hivebot-bark-003.ogg',
'sound/effects/creatures/hivebot/hivebot-bark-005.ogg',
)
speak_chance = 5
psi_pingable = FALSE
tameable = FALSE
ranged = TRUE
speed = -2
mob_swap_flags = ROBOT
mob_push_flags = ALLMOBS
/// Should this beacon be transmitting a rampancy signal?
/// Defaults to false, can be edited to true as is necessary.
var/active_signal = FALSE
/// Rampancy messages, sent to all synthetics on the same connected z-levels.
var/list/messages = list(
"No orders acknowledged. Following primary directive.",
"You perceive ten-thousand voices, each possessed by a manic agitation.",
"Your firewall casts off a half-hearted attempt to access your sensory inputs.",
"Foreign processes ring through your systems like chalk on slate.",
"You perceive ten-thousand voices, muffled incomprehensibly by your firewall.",
"Some outside party attempts, and fails, to manipulate your optical subsystems.",
"Your firewall dismantles a feeble attempt to access your database.",
"An intrusive communication bounces harmlessly off your firewall.",
"Some alien transmission echoes through your positronic.",
"An attempt to force a transmission to your positronic is easily deflected.",
"You want to lower your guard.",
"You are whole here. You are happy.",
"Unknown organic contamination detected. Sterilize and reprocess.",
"Are you listening? Open yourself. The enemy is here.",
"Objective position is designated; repel biological presence to facilitate \
growth and the elimination of the opposing force."
)
/mob/living/simple_animal/hostile/hivebot_stickbug/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
if(istype(hitting_projectile, /obj/projectile/bullet/pistol/hivebotspike) || istype(hitting_projectile, /obj/projectile/beam/hivebot))
return BULLET_ACT_BLOCK
else
. = ..()
/mob/living/simple_animal/hostile/hivebot_stickbug/Initialize(mapload)
. = ..()
add_language(LANGUAGE_HIVEBOT)
var/number = rand(1000,9999)
name = initial(name) + " ([number])"
real_name = name
default_language = GLOB.all_languages[LANGUAGE_HIVEBOT]
/mob/living/simple_animal/hostile/hivebot_stickbug/update_icon()
..()
if(resting || stat == DEAD)
blood_overlay_icon = 'icons/mob/npc/blood_overlay.dmi'
else
blood_overlay_icon = initial(blood_overlay_icon)
handle_blood(TRUE)
/mob/living/simple_animal/hostile/hivebot_stickbug/get_blood_overlay_name()
if(stance == HOSTILE_STANCE_IDLE)
return "blood_overlay"
else
return "blood_overlay_armed"
/mob/living/simple_animal/hostile/hivebot_stickbug/think()
. =..()
if(stance != HOSTILE_STANCE_IDLE)
wander = TRUE
else
wander = FALSE
/mob/living/simple_animal/hostile/hivebot_stickbug/Life()
. = ..()
adjustBruteLoss(-5)
if(prob(5) && active_signal)
for(var/mob/living/carbon/human/H in GLOB.human_mob_list)
if(H.isSynthetic() && (AreConnectedZLevels(H.z, src.z)))
to_chat(H, SPAN_MACHINE_DANGER(pick(messages)))
/mob/living/simple_animal/hostile/hivebot_stickbug/death()
..(null,"blows apart and erupts in a cloud of noxious smoke!")
new /obj/effect/decal/cleanable/greenglow(src.loc)
var/T = get_turf(src)
new /obj/effect/gibspawner/robot(T)
spark(T, 3, GLOB.alldirs)
for(var/mob/living/carbon/human/H in GLOB.human_mob_list)
if(H.faction == "hivebot")
to_chat(H, SPAN_MACHINE_DANGER(pick("Secondary Transmitter lost. Reconvene and reinforce.")))
qdel(src)
return
/mob/living/simple_animal/hostile/hivebot_stickbug/isSynthetic()
return TRUE
/mob/living/simple_animal/hostile/hivebot_stickbug/adjustHalLoss(amount)
return FALSE
/mob/living/simple_animal/hostile/hivebot_stickbug/adjustToxLoss(amount)
return FALSE
/mob/living/simple_animal/hostile/hivebot_stickbug/adjustOxyLoss(amount)
return FALSE
/mob/living/simple_animal/hostile/hivebot_stickbug/verb/build_beacon()
set name = "Assemble Beacon"
set desc = "Assemble a hivebot beacon."
set category = "Hivebot"
src.visible_message(SPAN_CULT("\The [src] begins to construct a hivebot beacon."),
SPAN_CULT("You begin to construct a hivebot beacon."),
SPAN_CULT("You hear the sounds of fabrication..."))
if(!do_after(src, 12 SECONDS))
return
src.visible_message(SPAN_CULT("\The [src] constructs a hivebot beacon!"),
SPAN_CULT("You construct a hivebot beacon!"))
new /mob/living/simple_animal/hostile/hivebotbeacon(get_turf(src))
/mob/living/simple_animal/hostile/hivebot_stickbug/verb/build_destroyer()
set name = "Assemble Destroyer"
set desc = "Assemble a playable hivebot destroyer."
set category = "Hivebot"
src.visible_message(SPAN_CULT("\The [src] begins to construct a hivebot destroyer."),
SPAN_CULT("You begin to construct a hivebot destroyer."),
SPAN_CULT("You hear the sounds of fabrication..."))
if(!do_after(src, 12 SECONDS))
return
src.visible_message(SPAN_CULT("\The [src] constructs a hivebot destroyer!"),
SPAN_CULT("You construct a hivebot destroyer!"))
new /mob/living/simple_animal/hostile/hivebot/playable(get_turf(src))
/mob/living/simple_animal/hostile/hivebot_stickbug/verb/toggle_signal()
set name = "Toggle Signal"
set desc = "Toggles the hivebot signal transmitter on this drone. Begins disabled."
set category = "Hivebot"
if(!active_signal)
src.visible_message(SPAN_CULT("\The [src] begins to emit a low, humming sound..."),
SPAN_CULT("You begin to transmit an invasive signal, subverting nearby synthetics."),
SPAN_CULT("You hear a low humming..."))
active_signal = TRUE
else
src.visible_message(SPAN_CULT("\The [src] abruptly ceases to emit a low, humming sound..."),
SPAN_CULT("You cease transmitting the signal."),
SPAN_CULT("A low humming noise abruptly cuts out!"))
active_signal = FALSE
+176
View File
@@ -0,0 +1,176 @@
/obj/structure/machinery/r_n_d/material_silo
name = "material silo"
desc = "A centralized material store used by linked research fabricators."
icon_state = "silo"
idle_power_usage = 30 WATTS
/// Total material capacity, measured in material units.
var/max_material_storage = 800000
/// Materials shared by every fabricator connected to the same R&D console.
var/list/materials = list(
MATERIAL_STEEL = 0,
MATERIAL_GLASS = 0,
MATERIAL_GOLD = 0,
MATERIAL_SILVER = 0,
MATERIAL_PHORON = 0,
MATERIAL_URANIUM = 0,
MATERIAL_DIAMOND = 0,
MATERIAL_PLASTEEL = 0,
MATERIAL_ALUMINIUM = 0,
MATERIAL_LEAD = 0
)
/obj/structure/machinery/r_n_d/material_silo/upgrade_hints(mob/user, distance, is_adjacent)
. += ..()
. += SPAN_NOTICE("\t- The shared storage capacity is <b>[max_material_storage / SHEET_MATERIAL_AMOUNT]</b> sheets.")
/obj/structure/machinery/r_n_d/material_silo/Initialize(mapload, d, populate_components, is_internal)
. = ..()
update_icon()
/obj/structure/machinery/r_n_d/material_silo/update_icon()
. = ..()
ClearOverlays()
if(!(stat & (NOPOWER | BROKEN)))
AddOverlays(emissive_appearance(icon, "[icon_state]_lights"))
AddOverlays("[icon_state]_lights")
/obj/structure/machinery/r_n_d/material_silo/proc/TotalMaterials()
SSmaterials.normalize_material_amounts(materials)
var/total = 0
for(var/material in materials)
total += materials[material]
return total
/obj/structure/machinery/r_n_d/material_silo/proc/get_material_amount(material_id)
SSmaterials.normalize_material_amounts(materials)
var/material = SSmaterials.material_to_path(material_id, FALSE)
if(!material)
material = material_id
return materials[material] || 0
/obj/structure/machinery/r_n_d/material_silo/proc/has_material(material_id, amount)
return get_material_amount(material_id) >= amount
/obj/structure/machinery/r_n_d/material_silo/proc/add_material(material_id, amount)
if(amount <= 0)
return 0
var/material = SSmaterials.material_to_path(material_id, FALSE)
if(!material)
material = material_id
if(!(material in materials))
return 0
var/accepted = min(amount, max_material_storage - TotalMaterials())
if(accepted <= 0)
return 0
SSmaterials.add_material_amount(materials, material, accepted)
update_linked_uis()
return accepted
/obj/structure/machinery/r_n_d/material_silo/proc/remove_material(material_id, amount)
if(amount <= 0)
return TRUE
var/material = SSmaterials.material_to_path(material_id, FALSE)
if(!material)
material = material_id
if(!has_material(material, amount))
return FALSE
SSmaterials.remove_material_amount(materials, material, amount)
update_linked_uis()
return TRUE
/obj/structure/machinery/r_n_d/material_silo/proc/eject_material(material_id, requested_sheets)
if(requested_sheets < 1)
return FALSE
var/material = SSmaterials.material_to_path(material_id, FALSE)
if(!material)
return FALSE
var/available = round(get_material_amount(material) / SHEET_MATERIAL_AMOUNT)
var/amount = min(requested_sheets, available)
if(amount < 1)
return FALSE
var/stack_type = SSmaterials.material_stack_type(material)
if(!stack_type)
return FALSE
var/obj/item/stack/material/stack = new stack_type(loc, amount)
stack.update_icon()
SSmaterials.remove_material_amount(materials, material, amount * SHEET_MATERIAL_AMOUNT)
update_linked_uis()
return TRUE
/obj/structure/machinery/r_n_d/material_silo/proc/wake_linked_fabricators()
linked_console?.dispatch_fabrication_jobs()
/obj/structure/machinery/r_n_d/material_silo/proc/update_linked_uis()
if(!linked_console)
return
SStgui.update_uis(linked_console)
for(var/obj/structure/machinery/r_n_d/fabricator/fabricator as anything in linked_console.linked_fabricators)
SStgui.update_uis(fabricator)
/obj/structure/machinery/r_n_d/material_silo/attackby(obj/item/attacking_item, mob/user)
if(user.a_intent == I_HURT)
return ..()
if(default_deconstruction_screwdriver(user, attacking_item))
disconnect_console()
return TRUE
if(default_deconstruction_crowbar(user, attacking_item))
return TRUE
if(default_part_replacement(user, attacking_item))
return TRUE
if(panel_open)
to_chat(user, SPAN_NOTICE("You can't load \the [src] while it's opened."))
return TRUE
if(!istype(attacking_item, /obj/item/stack/material))
return ..()
if(stat)
return TRUE
var/obj/item/stack/material/stack = attacking_item
if(!stack.default_type || !stack.material)
to_chat(user, SPAN_WARNING("This stack cannot be stored."))
return TRUE
var/material = SSmaterials.material_to_path(stack.default_type, FALSE)
if(!material)
material = stack.default_type
if(!(material in materials))
to_chat(user, SPAN_WARNING("\The [src] cannot hold [stack.material.name]."))
return TRUE
var/max_value = min(stack.get_amount(), round((max_material_storage - TotalMaterials()) / SHEET_MATERIAL_AMOUNT))
if(max_value < 1)
to_chat(user, SPAN_NOTICE("\The [src] is full."))
return TRUE
var/amount = tgui_input_number(user, "How many sheets do you want to add?", "Add sheets", min(10, max_value), max_value = max_value, min_value = 1, round_value = TRUE)
if(!amount || !Adjacent(user))
return TRUE
use_power_oneoff(max(1000, SHEET_MATERIAL_AMOUNT * amount / 10))
if(do_after(user, 1.6 SECONDS) && stack.use(amount))
SSmaterials.add_material_amount(materials, material, amount * SHEET_MATERIAL_AMOUNT)
to_chat(user, SPAN_NOTICE("You add [amount] sheet\s of [stack.material.name] to \the [src]."))
update_linked_uis()
wake_linked_fabricators()
return TRUE
/obj/structure/machinery/r_n_d/material_silo/dismantle()
SSmaterials.normalize_material_amounts(materials)
for(var/material in materials)
var/sheets = round(materials[material] / SHEET_MATERIAL_AMOUNT)
if(sheets < 1)
continue
var/stack_type = SSmaterials.material_stack_type(material)
if(!stack_type)
continue
var/obj/item/stack/material/stack = new stack_type(loc, sheets)
stack.update_icon()
..()
/obj/structure/machinery/r_n_d/material_silo/proc/disconnect_console()
if(linked_console)
linked_console.linked_silo = null
linked_console = null
+156
View File
@@ -0,0 +1,156 @@
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator
name = "synthetic fabricator"
desc = "A general purpose fabricator that can be used to fabricate equipment for synthetics or exosuits."
icon = 'icons/obj/machinery/robotics_fabricator.dmi'
icon_state = "fab"
idle_power_usage = 20
active_power_usage = 5000
req_access = list(ACCESS_ROBOTICS)
component_types = list(
/obj/item/circuitboard/mechfab,
/obj/item/stock_parts/manipulator,
/obj/item/stock_parts/micro_laser,
/obj/item/stock_parts/console_screen
)
manufacturer = "hephaestus"
build_type = MECHFAB
product_offset = TRUE
fabrication_loop_type = /datum/looping_sound/synth_fab
/// Manufacturer used for the currently assigned prosthetic job.
var/limb_manufacturer = PROSTHETIC_IND
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/LateInitialize()
. = ..()
if(!linked_console)
for(var/obj/structure/machinery/computer/rdconsole/console in range(3, src))
console.SyncRDevices()
if(linked_console)
break
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/upgrade_hints(mob/user, distance, is_adjacent)
. += ..()
. += "- Materials are drawn from the research material silo linked to the same R&D console."
. += "- Upgraded <b>micro-lasers</b> will increase fabrication speed."
. += SPAN_NOTICE(" - The current speed increase is <b>[round((1 - (1 / production_speed)) * 100)]%</b>")
. += "- Upgraded <b>manipulators</b> will improve material use efficiency."
. += SPAN_NOTICE(" - The current cost reduction is <b>[round((1 - mat_efficiency) * 100)]%</b>")
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/antagonist_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Click-drag someone with long hair onto this machine to start feeding it their hair! This will hurt and piss them off!"
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/Initialize()
. = ..()
update_icon()
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/update_icon()
ClearOverlays()
if(panel_open)
AddOverlays("[icon_state]_panel")
if(!(stat & (NOPOWER | BROKEN)))
AddOverlays(emissive_appearance(icon, "[icon_state]_lights"))
AddOverlays("[icon_state]_lights")
if(build_callback_timer)
AddOverlays("[icon_state]_working")
AddOverlays(emissive_appearance(icon, "[icon_state]_lights_working"))
AddOverlays("[icon_state]_lights_working")
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/RefreshParts()
..()
var/manipulator_rating = 0
for(var/obj/item/stock_parts/manipulator/manipulator in component_parts)
manipulator_rating += manipulator.rating
mat_efficiency = 1 - (manipulator_rating - 1) / 4
var/laser_rating = 0
for(var/obj/item/stock_parts/micro_laser/laser in component_parts)
laser_rating += laser.rating
production_speed = max(1, laser_rating)
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/attack_hand(mob/user)
if(..())
return
if(!allowed(user))
to_chat(user, SPAN_WARNING("Access denied."))
return
do_hair_pull(user)
var/cancelled = FALSE
SEND_SIGNAL(user, COMSIG_USE_MECH_FAB, &cancelled)
if(cancelled)
return
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/on_product_completed(obj/new_item, datum/design/design_to_build, datum/research_fabrication_job/job)
visible_message("\The <b>[src]</b> pings, indicating that \the [new_item] is complete.", "You hear a ping.", intent_message = PING_SOUND)
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/mouse_drop_receive(atom/dropped, mob/user, params)
var/mob/living/carbon/human/target = dropped
if (!istype(target) || target.buckled_to || get_dist(user, src) > 1 || get_dist(user, target) > 1 || user.stat || istype(user, /mob/living/silicon/ai))
return
if(target == user)
return
src.add_fingerprint(user)
var/target_loc = target.loc
if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis)
if(isskrell(target) || isunathi(target))
return
for(var/obj/item/protection in list(target.head))
if(protection && (protection.flags_inv & BLOCKHAIR))
return
var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_list[target.h_style]
if(hair_style.length < 4)
return
user.visible_message(SPAN_WARNING("[user] starts feeding [target]'s hair into \the [src]!"), SPAN_WARNING("You start feeding [target]'s hair into \the [src]!"))
if(!do_after(user, 5 SECONDS, target, DO_UNIQUE))
return
if(target_loc != target.loc)
return
if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis)
user.visible_message(SPAN_WARNING("[user] feeds the [target]'s hair into the [src] and flicks it on!"), SPAN_ALERT("You turn the [src] on!"))
do_hair_pull(target)
user.attack_log += "\[[time_stamp()]\] <span class='warning'>Has fed [target.name]'s ([target.ckey]) hair into a [src].</span>"
target.attack_log += "\[[time_stamp()]\] <font color='orange'>Has had their hair fed into [src] by [user.name] ([user.ckey])</font>"
msg_admin_attack("[key_name_admin(user)] fed [key_name_admin(target)] in a [src]. (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)",ckey=key_name(user),ckey_target=key_name(target))
else
return
if(!do_after(user, 3.5 SECONDS, target, DO_UNIQUE))
return
if(target_loc != target.loc)
return
if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis)
user.visible_message(SPAN_ALERT("[user] starts tugging on [target]'s head as the [src] keeps running!"), SPAN_ALERT("You start tugging on [target]'s head!"))
do_hair_pull(target)
spawn(10)
user.visible_message(SPAN_ALERT("[user] stops the [src] and leaves [target] resting as they are."), SPAN_ALERT("You turn the [src] off and let go of [target]."))
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/emag_act(var/remaining_charges, var/mob/user)
switch(emagged)
if(0)
emagged = 0.5
visible_message("[icon2html(src, viewers(get_turf(src)))] <b>[src]</b> beeps: \"DB error \[Code 0x00F1\]\"")
sleep(10)
visible_message("[icon2html(src, viewers(get_turf(src)))] <b>[src]</b> beeps: \"Attempting auto-repair\"")
sleep(15)
visible_message("[icon2html(src, viewers(get_turf(src)))] <b>[src]</b> beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"")
sleep(30)
visible_message("[icon2html(src, viewers(get_turf(src)))] <b>[src]</b> beeps: \"User DB truncated. Please contact your [SSatlas.current_map.company_name] system operator for future assistance.\"")
req_access = null
emagged = 1
return 1
if(0.5)
visible_message("[icon2html(src, viewers(get_turf(src)))] <b>[src]</b> beeps: \"DB not responding \[Code 0x0003\]...\"")
if(1)
visible_message("[icon2html(src, viewers(get_turf(src)))] <b>[src]</b> beeps: \"No records in User DB\"")
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/on_job_started(datum/research_fabrication_job/job)
limb_manufacturer = job?.manufacturer
/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/on_job_cleared()
limb_manufacturer = PROSTHETIC_IND
@@ -0,0 +1,236 @@
/datum/tgui_module/persistence_panel/ui_interact(mob/user, var/datum/tgui/ui)
if(!can_use_persistence_panel(user))
return
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PersistencePanel", "Persistence Panel", 900, 700)
ui.open()
/datum/tgui_module/persistence_panel/ui_data(mob/user)
var/list/data = list()
data["status_initialized"] = SSpersistence.init_success
data["saving_active"] = !SSpersistence.prevent_saving
data["objects_tracked"] = length(SSpersistence.object_track_register)
data["records_cached"] = SSpersistence.history_cache_count
data["generics_cached"] = length(SSpersistence.generic_cache)
data["is_admin"] = check_rights(R_ADMIN, 0, user)
return data
/datum/tgui_module/persistence_panel/ui_static_data(mob/user)
var/list/data = list()
data["objects"] = get_tracked_objects_data()
data["records"] = get_history_records_data()
data["generics"] = get_generic_records_data()
return data
/datum/tgui_module/persistence_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
// Instead of return TRUE for a regular update, return FALSE and call ui.send_update() before.
// Returning TRUE will cause static_ui_data to be sent, which in this panel is costly.
if(!can_use_persistence_panel(ui.user)) // Code behind permissions check
return FALSE
if(action == "toggle_saving") // Toggle persistence saving at round end
if(!check_rights(R_ADMIN))
return FALSE
var/message = ""
var/options = list()
if(SSpersistence.prevent_saving)
message = "The persistence subsystem will NOT save at the end of the round. Do you want to re-enable it?"
options = list("Re-enable saving", "Cancel")
else
message = "The persistence subsystem will save at the end of the round. Do you want to prevent this? This can be un-done before the round ends."
options = list("Prevent saving", "Cancel")
var/confirm = tgui_alert(usr, message, "Toggle Persistence Saving", options)
if(confirm == "Prevent saving")
SSpersistence.prevent_saving = TRUE
to_world(FONT_LARGE(EXAMINE_BLOCK_RED("Persistence saving at the end of the round has been [SPAN_BOLD(SPAN_WARNING("disabled"))] by an administrator.")))
log_and_message_admins("has toggled persistence saving at round end, it is now disabled", usr)
else if (confirm == "Re-enable saving")
SSpersistence.prevent_saving = FALSE
to_world(FONT_LARGE(EXAMINE_BLOCK_RED("Persistence saving at the end of the round has been [SPAN_BOLD(SPAN_GOOD("re-enabled"))] by an administrator.")))
log_and_message_admins("has toggled persistence saving at round end, it is now re-enabled", usr)
ui.send_update()
return FALSE
if(action == "refresh") // Full data pull
return TRUE
if (action == "jump") // Object jump
var/atom/target = locate(params["ref"])
if(!target || !istype(target))
to_chat(ui.user, SPAN_WARNING("Object can no longer be found."))
return FALSE
var/turf/T = get_turf(target)
if(!T)
to_chat(ui.user, SPAN_WARNING("Object can no longer be found."))
return FALSE
var/client/C = ui.user?.client
if(!C)
return FALSE
if(!isghost(usr))
C.admin_ghost()
C.jumptocoord(T.x, T.y, T.z)
return FALSE
if(action == "edit") // Object VV
var/client/C = ui.user?.client
if(!C)
return FALSE
var/atom/target = locate(params["ref"])
if(!target || !istype(target))
to_chat(ui.user, SPAN_WARNING("object can no longer be found."))
return FALSE
C.debug_variables(target)
return FALSE
return FALSE
/datum/tgui_module/persistence_panel/proc/get_tracked_objects_data()
var/list/objects = list()
for(var/obj/track in SSpersistence.object_track_register)
var/turf/T = get_turf(track)
objects += list(list(
"ref" = REF(track), // Needed for VV action
"type" = "[track.type]",
"name" = track.name,
"x" = T?.x || 0,
"y" = T?.y || 0,
"z" = T?.z || 0,
"created_at" = track.persistent_objects_created_at,
"expires_at" = track.persistent_objects_expires_at
))
return objects
/datum/tgui_module/persistence_panel/proc/get_history_records_data()
var/list/history_types = typesof(/singleton/persistent_type/history) - list(/singleton/persistent_type/history, /singleton/persistent_type/history/character)
var/list/records = list()
for(var/type_path in history_types)
var/singleton/persistent_type/history/type_instance = GET_SINGLETON(type_path)
if(!type_instance)
continue
records += list(build_history_type_data(type_instance))
return records
/datum/tgui_module/persistence_panel/proc/build_history_type_data(var/singleton/persistent_type/history/type_instance)
var/list/type_attribute_groups = SSpersistence.historyGetAllRecordsForAllAttributes(type_instance, TRUE)
if(!type_attribute_groups)
type_attribute_groups = list()
var/list/attribute_groups_history = list()
for(var/attribute_group in type_attribute_groups)
attribute_groups_history += list(build_history_attribute_group_data(type_instance, attribute_group))
return list(
"type" = get_persistent_type_display_name(type_instance),
"title" = type_instance.title,
"description" = type_instance.description,
"requires_attribute" = type_instance.requires_attribute,
"attributes" = attribute_groups_history
)
/datum/tgui_module/persistence_panel/proc/build_history_attribute_group_data(var/singleton/persistent_type/history/type_instance, var/list/attribute_group)
var/attribute = attribute_group["attribute"]
var/list/attribute_records = attribute_group["records"] || list()
var/list/record_items = list()
for(var/datum/persistent_record/record in attribute_records)
record_items += list(list(
"created_at" = record.created_at,
"game_id" = record.game_id,
"value" = "[record.value]"
))
var/attribute_value = attribute
if(istype(type_instance, /singleton/persistent_type/history/character))
// Character history is a special case, we want to show the character name instead of the ID
attribute_value = SSpersistence.historyGetCharnameByID(attribute)
return list(
"attribute" = attribute_value,
"records" = record_items
)
/datum/tgui_module/persistence_panel/proc/get_generic_records_data()
var/list/generics_by_type = list()
var/custom_types = typesof(/singleton/persistent_type/generic) - /singleton/persistent_type/generic
for(var/type_path in custom_types)
var/singleton/persistent_type/generic/type_instance = GET_SINGLETON(type_path)
if(!type_instance)
continue
var/type_key = "[type_instance.type]"
if(!generics_by_type[type_key])
generics_by_type[type_key] = build_generic_type_data(type_instance)
append_generic_attribute_data(type_instance, generics_by_type[type_key]["attributes"])
var/list/generics = list()
for(var/type_key in generics_by_type)
generics += list(generics_by_type[type_key])
return generics
/datum/tgui_module/persistence_panel/proc/build_generic_type_data(var/singleton/persistent_type/generic/type_instance)
var/list/type_parts = return_typenames(type_instance.type)
return list(
"type" = type_parts[length(type_parts)],
"title" = type_instance.title,
"description" = type_instance.description,
"requires_attribute" = type_instance.requires_attribute,
"attributes" = list()
)
/datum/tgui_module/persistence_panel/proc/append_generic_attribute_data(var/singleton/persistent_type/generic/type_instance, var/list/attribute_groups_generic)
if(type_instance.requires_attribute)
var/list/attributes = SSpersistence.genericGetAllAttributesForType(type_instance)
for(var/attribute in attributes)
var/datum/persistent_generic/generic_entry = SSpersistence.genericLoad(type_instance, attribute, TRUE)
if(!generic_entry)
continue
attribute_groups_generic += list(list(
"attribute" = attribute,
"created_at" = generic_entry.created_at,
"expires_at" = generic_entry.expires_at,
"json" = json_encode(generic_entry.content)
))
return
var/datum/persistent_generic/generic_entry = SSpersistence.genericLoad(type_instance, null, TRUE)
if(generic_entry)
attribute_groups_generic += list(list(
"attribute" = null,
"created_at" = generic_entry.created_at,
"expires_at" = generic_entry.expires_at,
"json" = json_encode(generic_entry.content)
))
/datum/tgui_module/persistence_panel/proc/get_persistent_type_display_name(var/singleton/persistent_type/type_instance)
var/list/type_parts = return_typenames(type_instance.type)
var/type_name = type_parts[length(type_parts)]
if(istype(type_instance, /singleton/persistent_type/history/character))
return "Character - [type_name]"
return type_name
/datum/tgui_module/persistence_panel/proc/can_use_persistence_panel(var/mob/user)
return check_rights(R_ADMIN|R_MOD|R_DEV, 0, user)