Merge branch 'Aurorastation:master' into boneyard

This commit is contained in:
hazelrat
2026-08-14 21:55:08 +01:00
committed by GitHub
350 changed files with 9291 additions and 6834 deletions
+9
View File
@@ -15,6 +15,15 @@ They should also be used for when you want to effect the ENTIRE mob, like having
return COMPONENT_BULLET_BLOCKED
/obj/aura/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
var/result = ..()
if(result == BULLET_ACT_BLOCK) //The bullet was blocked, don't check the remaining auras.
return AURA_FALSE|AURA_CANCEL
else if(result == BULLET_ACT_FORCE_PIERCE) //The bullet was forced to pierce, check the remaining auras to see if they block.
return AURA_FALSE
else
return
/obj/aura/Destroy()
if(user)
user.remove_aura(src)
@@ -55,13 +55,11 @@
/obj/effect/decal/cleanable/greenglow/Initialize(mapload)
. = ..()
if (!mapload) // Round-start goo should stick around.
QDEL_IN(src, 2 MINUTES)
/obj/effect/decal/cleanable/greenglow/post_sweep(var/mob/user)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.apply_radiation(3)
H.rad_act(30)
STOP_PROCESSING(SSprocessing, src)
ABSTRACT_TYPE(/obj/effect/decal/cleanable/greenglow/radioactive)
@@ -71,6 +69,13 @@ ABSTRACT_TYPE(/obj/effect/decal/cleanable/greenglow/radioactive)
light_power = 0.6
light_color = "#64C864"
/obj/effect/decal/cleanable/greenglow/radioactive/post_sweep(var/mob/user)
if(radioactivity)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.rad_act(radioactivity)
STOP_PROCESSING(SSprocessing, src)
/obj/effect/decal/cleanable/greenglow/radioactive/antagonist_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Characters directly adjacent to or on top of this object will be exposed to <b>[radioactivity] IU/s</b> of radiation. Radiation falls off (approximately) by 75% for every tile away you move."
@@ -123,7 +128,6 @@ ABSTRACT_TYPE(/obj/effect/decal/cleanable/greenglow/radioactive)
/obj/effect/decal/cleanable/greenglow/radioactive/process()
SSradiation.radiate(src, radioactivity)
/obj/effect/decal/cleanable/cobweb
name = "cobweb"
desc = "Somebody should remove that."
@@ -7,6 +7,7 @@
anchored = TRUE
density = FALSE
mouse_opacity = MOUSE_OPACITY_ICON
layer = ABOVE_DOOR_LAYER
var/obj/item/plastique/parent
/obj/effect/plastic_explosive/feedback_hints(mob/user, distance, is_adjacent)
@@ -16,8 +17,8 @@
/obj/effect/plastic_explosive/Initialize(var/atom/owner_pos, var/atom/target, var/obj/item/plastique/c4)
. = ..()
parent = c4
if(parent)
parent = c4
parent.forceMove(src)
name = parent.name
desc = parent.desc
@@ -46,8 +47,8 @@
/obj/effect/plastic_explosive/attack_hand(mob/living/user)
to_chat(user, SPAN_WARNING("\The [src] is solidly attached, it doesn't budge!"))
/obj/effect/plastic_explosive/attackby(obj/item/attacking_item, mob/user)
return parent.attackby(attacking_item, user)
/obj/effect/plastic_explosive/attackby(obj/item/attacking_item, mob/user, click_parameters)
return parent.attackby(attacking_item, user, click_parameters)
/obj/effect/plastic_explosive/big
name = "bundled plastic explosives"
+5 -2
View File
@@ -47,12 +47,12 @@
return
if(emagged)
if(insults)
user.audible_message("<B>[user]</B> broadcasts, <FONT size=5>\"[pick(insultmsg)]\"</FONT>", "<B>[user]</B> speaks into \the [src].", 14)
speak(pick(insultmsg), user)
insults--
else
to_chat(user, SPAN_WARNING("*BZZZZzzzzzt*"))
else
user.audible_message("<B>[user]</B> broadcasts, <FONT size=5>\"[message]\"</FONT>", "<B>[user]</B> speaks into \the [src].", 14)
speak(message, user)
if(activation_sound)
playsound(loc, activation_sound, 100, 0, 1)
for (var/mob/living/carbon/human/C in range(user, 2) - user)
@@ -63,6 +63,9 @@
spamcheck = world.time + 50
return
/obj/item/megaphone/proc/speak(var/msg, var/mob/living/user)
user.say(msg, user.get_default_language(), "broadcasts")
/obj/item/megaphone/emag_act(var/remaining_charges, var/mob/user)
if(!emagged)
to_chat(user, SPAN_WARNING("You overload \the [src]'s voice synthesizer."))
@@ -238,6 +238,8 @@
/obj/item/clothing/suit/space/void/atmos = /obj/item/clothing/suit/space/void/atmos/himeo/tajara,
/obj/item/clothing/head/helmet/space/void/atmos = /obj/item/clothing/head/helmet/space/void/atmos/himeo/tajara,
/obj/item/rig/industrial = /obj/item/rig/industrial/himeo/dequipped,
/obj/item/rig_assembly/industrial = /obj/item/rig_assembly/industrial/himeo
)
@@ -1,10 +1,96 @@
#define MICROPHONE_LISTEN_RANGE 5
/obj/item/radio/microphone
name = "handheld microphone"
desc = "A handheld microphone, used for on-the-fly interviewing. Pose for the fans!"
icon_state = "microphone"
item_state = "microphone"
canhear_range = MICROPHONE_LISTEN_RANGE
item_flags = ITEM_FLAG_NO_BLUDGEON
/// If we have specific people we want to be able to speak into the microphone.
var/directed_listening = TRUE
/// A lazylist of the people allowed to talk into the microphone. Essentially acts as a filter.
var/list/mob/living/listeners
/// If the messages end with these endings, bypass the listener check.
var/static/list/word_bypasses = list("!", "!!", "?!", "!?")
/obj/item/radio/microphone/Initialize()
. = ..()
set_frequency(ENT_FREQ)
internal_channels = list(num2text(ENT_FREQ) = list())
/obj/item/radio/microphone/Destroy()
LAZYNULL(listeners)
return ..()
/obj/item/radio/microphone/AltClick(mob/user)
. = ..()
directed_listening = !directed_listening
to_chat(user, SPAN_NOTICE("You are now [directed_listening ? "" : "no longer "]listening to specific people."))
/obj/item/radio/microphone/get_examine_text(mob/user, distance, is_adjacent, infix, suffix, get_extended)
. = ..()
. += SPAN_NOTICE("You are [directed_listening ? "" : SPAN_BOLD("not ")]listening to specific people with the microphone.")
if(directed_listening)
if(LAZYLEN(listeners))
. += SPAN_NOTICE("You are angling the microphone specifically towards the following people: [SPAN_BOLD(english_list(listeners))].")
else
. += SPAN_NOTICE("You are not angling the microphone towards anyone.")
/obj/item/radio/microphone/mechanics_hints(mob/user, distance, is_adjacent)
. = ..()
. += "By default, your microphone will not allow anyone other than you to speak into it. [SPAN_BOLD("Alt-Click")] to disable this functionality."
. += "You can [SPAN_BOLD("click")] someone with the microphone in hand to allow them to speak into it. [SPAN_BOLD("Click")] them again to disallow them again."
. += "If someone moves more than 5 tiles away from your microphone, they will automatically be removed from the list."
/obj/item/radio/microphone/pickup(mob/user)
. = ..()
add_listener(user)
/obj/item/radio/microphone/dropped(mob/user)
. = ..()
remove_listener(user)
/obj/item/radio/microphone/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
if(!isliving(target))
return
if(get_dist(target, get_turf(src)) < MICROPHONE_LISTEN_RANGE)
if(!(target in listeners))
to_chat(user, SPAN_NOTICE("You angle the microphone to allow [target] to speak into it."))
add_listener(target)
else
to_chat(user, SPAN_NOTICE("You angle the microphone away from [target]."))
remove_listener(target)
/**
* Add a mob to the list of listeners allowed to speak into the microphone.
*/
/obj/item/radio/microphone/proc/add_listener(mob/living/target_mob)
LAZYDISTINCTADD(listeners, target_mob)
RegisterSignal(target_mob, COMSIG_MOVABLE_MOVED, PROC_REF(check_listener_distance))
/**
* Remove a mob from the list of listeners allowed to speak into the microphone.
*/
/obj/item/radio/microphone/proc/remove_listener(mob/living/target_mob)
UnregisterSignal(target_mob, COMSIG_MOVABLE_MOVED)
LAZYREMOVE(listeners, target_mob)
/**
* Checks if the mob is too far from the microphone, at which point we stop listening to them.
**/
/obj/item/radio/microphone/proc/check_listener_distance(datum/source)
SIGNAL_HANDLER
if(get_dist(source, get_turf(src)) > MICROPHONE_LISTEN_RANGE)
LAZYREMOVE(listeners, source)
if(ismob(loc))
var/mob/M = loc
to_chat(M, SPAN_NOTICE("[source] moves away from the microphone."))
/obj/item/radio/microphone/hear_talk(mob/M, msg, var/verb = "says", datum/language/speaking)
var/message_ending = copytext(msg, length(msg) - 1, length(msg)) //copies the last 2 letters
if(directed_listening && ((M in listeners) || (message_ending in word_bypasses)))
return ..()
#undef MICROPHONE_LISTEN_RANGE
@@ -4,6 +4,7 @@
icon = 'icons/obj/item/tvcamera.dmi'
icon_state = "camcorder"
item_state = "camcorder"
contained_sprite = TRUE
w_class = WEIGHT_CLASS_BULKY
slot_flags = SLOT_BELT
var/channel = "General News Feed"
+24 -2
View File
@@ -320,12 +320,35 @@
update_icon()
/obj/item/weldingtool/use_tool(atom/target, mob/living/user, delay, amount, volume, datum/callback/extra_checks)
var/welding_blind = user.is_blind()
if(welding_blind)
delay *= 3
var/image/welding_sparks = image('icons/effects/effects.dmi', welding_state)
welding_sparks.plane = ABOVE_LIGHTING_PLANE
target.AddOverlays(welding_sparks)
. = ..()
target.CutOverlays(welding_sparks)
if(. && welding_blind && prob(80))
to_chat(user, SPAN_WARNING("Unable to see your work, you botch the weld!"))
if(prob(50))
var/burn_zone
if(ishuman(user))
switch(get_equip_slot())
if(slot_l_hand)
burn_zone = BP_L_HAND
if(slot_r_hand)
burn_zone = BP_R_HAND
else
burn_zone = pick(BP_L_HAND, BP_R_HAND)
user.apply_damage(rand(5, 10), DAMAGE_BURN, burn_zone, src)
user.visible_message(
SPAN_DANGER("[user] jerks back after burning themselves with \the [src]!"),
SPAN_DANGER("Your blind welding slips, burning your hand!")
)
return FALSE
/obj/item/weldingtool/proc/update_torch()
if(welding)
AddOverlays("[initial(icon_state)]-on")
@@ -439,7 +462,7 @@
user.visible_message(SPAN_NOTICE("\The [user] finishes repairing the physical damage on \the [target]'s [affecting.name]."))
return
if(do_mob(user, target, 30))
if(use_tool(target, user, 30, volume = 15))
if(use(0))
var/static/list/repair_messages = list(
"patches some dents",
@@ -951,4 +974,3 @@
var/mutable_appearance/handle = mutable_appearance('icons/obj/tools.dmi', "hammer_handle")
handle.color = pick(COLOR_BLUE, COLOR_RED, COLOR_PURPLE, COLOR_BROWN, COLOR_GREEN, COLOR_CYAN, COLOR_YELLOW)
AddOverlays(handle)
@@ -244,6 +244,36 @@
id_card.citizenship = SSrecords.get_citizenship_record_name(citizenship)
id_card.mob_id = WEAKREF(src)
id_card.employer_faction = employer_faction
INVOKE_ASYNC(id_card, TYPE_PROC_REF(/obj/item/card/id, load_mining_points))
/obj/item/card/id/proc/load_mining_points()
var/mob/living/carbon/human/card_owner = mob_id?.resolve()
if(!card_owner?.character_id)
return
var/datum/persistent_record/saved_balance = SSpersistence.historyGetLastRecord(
/singleton/persistent_type/history/character/mining_point_balance,
card_owner.character_id
)
if(!saved_balance)
return
mining_points = clamp(text2num(saved_balance.value), 0, MINING_POINTS_CARRYOVER_MAX)
/obj/item/card/id/proc/save_mining_points()
var/mob/living/carbon/human/card_owner = mob_id?.resolve()
if(!card_owner?.character_id)
return
SSpersistence.historyAddCharacterRecord(
/singleton/persistent_type/history/character/mining_point_balance,
card_owner.character_id,
"[clamp(mining_points, 0, MINING_POINTS_CARRYOVER_MAX)]"
)
/obj/item/card/id/proc/adjust_mining_points(amount)
mining_points = max(0, mining_points + amount)
save_mining_points()
/obj/item/card/id/proc/dat()
var/dat = ("<table><tr><td>")
@@ -231,15 +231,17 @@
return INITIALIZE_HINT_NORMAL
/obj/item/nullrod/clockworkstave
name = "\improper clockwork stave"
desc = "A long, wooden stave with a gear and triangle at the top, utilized by the clergy of the Trinary Perfection. The ornate pieces atop the stave are often delicately \
hand-crafted by synthetics from the monastic Order of Pitters and exported off the planet of Axiom."
name = "\improper stave of ecclesiastical office"
desc = "A long brass stave used as the marker of the holder of some ecclesiastical \
office in the church of the Trinary Perfection. Priests, bishops, and government \
officials often bear such a staff; all faithful Trinarists ought listen when one \
holding it makes their voice heard."
icon = 'icons/obj/trinary_stave.dmi'
icon_state = "trinary_stave"
item_state = "trinary_stave"
contained_sprite = TRUE
w_class = WEIGHT_CLASS_BULKY
slot_flags = SLOT_BACK
/obj/item/nullrod/verb/change(mob/living/user)
set name = "Reassemble Null Item"
@@ -25,6 +25,12 @@
console_networks = list("MINE")
req_access = list()
/obj/item/circuitboard/security/public
name = T_BOARD("public camera monitor")
build_path = /obj/structure/machinery/computer/security/terminal/public
console_networks = list(NETWORK_EXPEDITION, NETWORK_NEWS)
req_access = list()
/obj/item/circuitboard/security/construct(var/obj/structure/machinery/computer/security/C)
if (..(C))
C.console_networks = console_networks.Copy()
@@ -143,11 +143,23 @@
origin_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 3)
board_type = BOARD_MACHINE
req_components = list(
"/obj/item/stock_parts/capacitor" = 2,
"/obj/item/stock_parts/capacitor" = 3,
"/obj/item/stock_parts/micro_laser" = 1,
"/obj/item/stack/cable_coil" = 5
)
/obj/item/circuitboard/microwave
name = T_BOARD("microwave")
desc = "The circuitboard for a microwave."
build_path = /obj/structure/machinery/appliance/cooker/microwave
origin_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 3)
board_type = BOARD_MACHINE
req_components = list(
"/obj/item/stock_parts/capacitor" = 3,
"/obj/item/stock_parts/scanning_module" = 1,
"/obj/item/stack/cable_coil" = 5
)
/obj/item/circuitboard/candymachine
name = T_BOARD("candy machine")
desc = "The circuitboard for a candy machine."
@@ -51,32 +51,29 @@
/obj/item/circuitboard/protolathe
name = T_BOARD("protolathe")
build_path = /obj/structure/machinery/r_n_d/protolathe
build_path = /obj/structure/machinery/r_n_d/fabricator/protolathe
board_type = BOARD_MACHINE
origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2)
req_components = list(
"/obj/item/stock_parts/matter_bin" = 2,
"/obj/item/stock_parts/manipulator" = 2,
"/obj/item/reagent_containers/glass/beaker" = 2)
/obj/item/circuitboard/circuit_imprinter
name = T_BOARD("circuit imprinter")
build_path = /obj/structure/machinery/r_n_d/circuit_imprinter
build_path = /obj/structure/machinery/r_n_d/fabricator/circuit_imprinter
board_type = BOARD_MACHINE
origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2)
req_components = list(
"/obj/item/stock_parts/matter_bin" = 1,
"/obj/item/stock_parts/manipulator" = 1,
"/obj/item/reagent_containers/glass/beaker" = 2)
/obj/item/circuitboard/mechfab
name = T_BOARD("mechatronic fabricator")
build_path = /obj/structure/machinery/mecha_part_fabricator
build_path = /obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator
board_type = BOARD_MACHINE
origin_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3)
req_components = list(
"/obj/item/stock_parts/matter_bin" = 2,
"/obj/item/stock_parts/manipulator" = 1,
"/obj/item/stock_parts/micro_laser" = 1,
"/obj/item/stock_parts/console_screen" = 1)
@@ -91,11 +88,3 @@
"/obj/item/stock_parts/capacitor" = 1,
"/obj/item/stack/cable_coil" = 1,
"/obj/item/stock_parts/console_screen" = 1)
/obj/item/circuitboard/ntnet_relay
name = T_BOARD("NTNet Quantum Relay")
build_path = /obj/structure/machinery/ntnet_relay
board_type = BOARD_MACHINE
origin_tech = list(TECH_DATA = 4)
req_components = list(
"/obj/item/stack/cable_coil" = 15)
@@ -60,3 +60,18 @@
"/obj/item/stock_parts/subspace/filter" = 1,
"/obj/item/stock_parts/subspace/crystal" = 1,
"/obj/item/stock_parts/micro_laser/high" = 2)
/obj/item/circuitboard/ntnet_relay
name = T_BOARD("NTNet Bluespace Relay")
build_path = /obj/structure/machinery/ntnet_relay
board_type = BOARD_MACHINE
origin_tech = list(TECH_DATA = 4)
req_components = list(
"/obj/item/stack/cable_coil" = 15,
"/obj/item/stock_parts/subspace/filter" = 1,
"/obj/item/stock_parts/subspace/crystal" = 1,
"/obj/item/stock_parts/micro_laser/high" = 2)
/obj/item/circuitboard/ntnet_relay/field
name = T_BOARD("NTNet Field Relay")
build_path = /obj/structure/machinery/ntnet_relay/field
+33 -5
View File
@@ -48,7 +48,6 @@
/obj/item/plastique/Destroy()
qdel(wires)
wires = null
qdel(target)
target = null
return ..()
@@ -195,11 +194,40 @@
desc = "A small explosive laced with radium. The explosion is small, but the radioactive material will remain for a fair while."
timer = 30 SECONDS
/obj/item/plastique/dirty/explode(turf/location)
/obj/item/plastique/dirty/explode(turf/location) //Does not call parent because we need a different order of operations.
if(!target)
target = get_atom_on_turf(src)
if(!target)
target = src
if(target)
if (istype(target, /turf/simulated/wall))
var/turf/simulated/wall/W = target
W.dismantle_wall(1, no_product = TRUE)
else if(istype(target, /mob/living))
target.ex_act(2) // c4 can't gib mobs anymore.
target.rad_act(800) //A dirty bomb going off on top of you completely irradiates you, radsuit or not.
else
target.ex_act(1)
if(location)
SSradiation.radiate(src, 250)
new /obj/effect/decal/cleanable/greenglow/radioactive/medium(get_turf(src))
..()
explosion(location, devastation_range, heavy_impact_range, light_impact_range, 3, spreading = 0)
SSradiation.radiate(location, 250)
new /obj/effect/decal/cleanable/greenglow/radioactive/extreme(location)
for(var/turf/T in RANGE_TURFS(4, location))
if(T == location)
continue
if(T in RANGE_TURFS(1, location)) //High radioactive puddles 1 tile from the epicenter, medium up to 4 tiles away.
if(prob(75))
new /obj/effect/decal/cleanable/greenglow/radioactive/high(T)
else if(prob(25))
new /obj/effect/decal/cleanable/greenglow/radioactive/medium(T)
var/obj/effect/plastic_explosive/effect = locate(/obj/effect/plastic_explosive) in get_turf(src)
if(effect)
qdel(effect)
qdel(src)
/obj/item/plastique/strong
name = "bundled plastic explosives"
@@ -12,6 +12,7 @@
throw_speed = 4
throw_range = 20
obj_flags = OBJ_FLAG_CONDUCTABLE
pass_flags_self = PASSTABLE | PASSRAILING
slot_flags = SLOT_BELT
contained_sprite = 1
var/active = 0
@@ -30,6 +30,7 @@
if(content["material"])
set_material(content["material"])
max_butts = round(material.hardness/10)
update_icon()
var/fill_count = content["fill_count"]
if(fill_count)
if(fill_count > max_butts)
@@ -39,6 +39,13 @@
* If the object may be accessed while equipped anywhere on a character, including hands.
*/
var/equip_access = TRUE
/**
* If the object should have a delay to open, for more cumbersome bags such as duffels.
* If set to zero seconds, no delay is applied. Define a value in seconds if a delay
* should be applied and the delay will be equal to the value of this variable.
* Only applies while on a character mob, not while the bag is on the floor.
*/
var/access_delay = 0 SECONDS
/obj/item/storage/backpack/antagonist_hints(mob/user, distance, is_adjacent)
. += ..()
@@ -144,6 +151,8 @@
/obj/item/storage/backpack/open(mob/user)
if (!worn_check())
return
if(access_delay && !isturf(loc) && !do_after(user, access_delay))
return
..()
/obj/item/storage/backpack/proc/worn_check(no_message = FALSE)
@@ -153,7 +162,7 @@
return TRUE //not equipped
if(!worn_access && (slot_flags & SLOT_BACK) && M.get_equipped_item(slot_back) == src)
if(!no_message)
to_chat(M, SPAN_WARNING("Your arms are not long enough to open \the [src] while it is on your back!"))
to_chat(M, SPAN_WARNING("You cannot access the contents of \the [src] while it is on your back!"))
if(use_sound)
playsound(loc, use_sound, 50, 1, -5)
if(animated)
@@ -161,7 +170,7 @@
return FALSE
if(!equip_access && (ismob(loc)))
if(!no_message)
to_chat(M, SPAN_WARNING("\The [src] is too cumbersome to handle, you're going to have to set it down somewhere!"))
to_chat(M, SPAN_WARNING("\The [src] is too cumbersome to access in your hands, you're going to have to set it down somewhere!"))
if(use_sound)
playsound(loc, use_sound, 50, 1, -5)
if(animated)
@@ -630,10 +639,16 @@
icon_state = "duffel"
item_state = "duffel"
worn_access = FALSE
equip_access = FALSE
access_delay = 1 SECOND
max_storage_space = DEFAULT_DUFFELBAG_STORAGE
straps = TRUE
/obj/item/storage/backpack/duffel/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Duffel bags store a greater capacity than any other kind of backpack or satchel."
. += "Duffel bags cannot be accessed while on a character's back, but can be accessed while in their hand or on the floor."
. += "Duffel bags are laborious to use! There is a short delay on accessing them while in your hands."
/obj/item/storage/backpack/duffel/cap
name = "captain's duffel bag"
desc = "A rare and special duffel bag for only the most air-headed of SCC personnel."
@@ -696,7 +711,6 @@
icon_state = "duffel-syndie"
item_state = "duffel-syndie"
worn_access = TRUE
equip_access = TRUE
empty_delay = 0.8 SECOND
/obj/item/storage/backpack/duffel/cmo
@@ -574,7 +574,7 @@
var/open = 0 // Is the box open?
var/ismessy = 0 // Fancy mess on the lid
var/obj/item/reagent_containers/food/snacks/sliceable/pizza/pizza // Content pizza
var/obj/item/reagent_containers/food/snacks/pizza // Content pizza
var/pizza_type
var/list/boxes = list() // If the boxes are stacked, they come here
var/boxtag = ""
@@ -699,7 +699,7 @@
return
if(istype(attacking_item, /obj/item/reagent_containers/food/snacks/sliceable/pizza/)) // Long ass fucking object name
if(istype(attacking_item, /obj/item/reagent_containers/food/snacks/sliceable/pizza/) || istype(attacking_item, /obj/item/reagent_containers/food/snacks/variable/pizza)) // Long ass fucking object name
if(src.open)
user.drop_from_inventory(attacking_item, src)
@@ -336,6 +336,8 @@
/obj/item/storage/box/fancy/tray/machinist
name = "machinist operation tray"
desc = "A tray of various tools for use by machinists in repairing robots."
// So additional tools can be added.
max_storage_space = 23
can_hold = list(
/obj/item/surgery/cautery,
/obj/item/surgery/circular_saw,
+18 -4
View File
@@ -524,6 +524,7 @@
unbuckle()
return
capturing_mob.captured = TRUE
captured = WEAKREF(capturing_mob)
playsound(src, 'sound/weapons/beartrap_shut.ogg', 100, 1)
@@ -674,10 +675,10 @@
if(captured_mob)
captured_mob.bullet_act(arglist(args))
/obj/item/trap/animal/proc/release(var/mob/user, var/turf/target)
/obj/item/trap/animal/proc/release(var/mob/user, var/turf/target, var/transferring = FALSE)
if(!target)
target = src.loc
if(user)
if(user && !transferring)
visible_message(SPAN_NOTICE("[user] opens \the [src]."))
var/mob/captured_mob = captured ? captured.resolve() : null
@@ -690,10 +691,12 @@
var/mob/living/living_mob = captured_mob
msg = SPAN_WARNING("[living_mob] runs out of \the [src].")
captured_mob.captured = FALSE
unbuckle()
captured = null
visible_message(msg)
shake_animation()
if(!transferring)
visible_message(msg)
shake_animation()
update_icon()
layer = initial(layer)
@@ -779,6 +782,17 @@
if(.)
sync_captured_mob()
/obj/item/trap/animal/post_buckle(atom/movable/MA)
if(MA == buckled || !isliving(MA))
return
var/mob/living/released_mob = MA
released_mob.captured = FALSE
if(captured?.resolve() == released_mob)
captured = null
layer = initial(layer)
update_icon()
/obj/item/trap/animal/attack_hand(mob/user)
if(user.loc == src || captured)
return
+7
View File
@@ -106,6 +106,10 @@
var/persistent_objects_author_ckey = null
// Expiration time used when saving/updating a persistent type, this can be changed depending on the use case by assigning a new value
var/persistent_objects_expiration_time_days = PERSISTENT_DEFAULT_EXPIRATION_DAYS
// Database timestamp when the tracked object was created
var/persistent_objects_created_at = null
// Database timestamp when the tracked object will expire
var/persistent_objects_expires_at = null
/* END PERSISTENCE VARS */
/// for easy reference of talking atoms
@@ -258,7 +262,10 @@
return
/mob/proc/unset_machine()
var/was_viewing_machine_remote_view = is_viewing_camera() || is_viewing_overmap()
src.machine = null
if(was_viewing_machine_remote_view)
reset_view(null)
/mob/proc/set_machine(var/obj/O)
if(src.machine)
+10 -14
View File
@@ -255,20 +255,6 @@
/obj/item/pizzabox/pepperoni,
)
//Sometimes the chef will have spare oil in storage.
//Sometimes they wont, and will need to order it from cargo
//Variety is the spice of life!
/obj/random/cookingoil
name = "random cooking oil"
desc = "Has a 50% chance of spawning a tank of cooking oil, otherwise nothing."
icon = 'icons/obj/reagent_dispensers.dmi'
icon_state = "oiltank"
spawn_nothing_percentage = 50
spawnlist = list(
/obj/structure/reagent_dispensers/cookingoil
)
/obj/random/chocolate
name = "random chocolate"
desc = "This is a random chocolate."
@@ -287,3 +273,13 @@
/obj/item/reagent_containers/food/snacks/chocolate_bar/laughterbar = 1,
/obj/item/storage/box/fancy/readies
)
/obj/random/cookingoil
name = "random cooking oil"
desc = "Has a 50% chance of spawning a tank of cooking oil, otherwise nothing."
icon = 'icons/obj/reagent_dispensers.dmi'
icon_state = "oiltank"
spawn_nothing_percentage = 50
spawnlist = list(
/obj/structure/reagent_dispensers/cookingoil
)
+3 -2
View File
@@ -120,10 +120,11 @@
if(. != BULLET_ACT_HIT)
return .
if(hitting_projectile.get_structure_damage() > 5)
var/structure_damage = hitting_projectile.get_structure_damage()
if(structure_damage > 5)
bullet_ping(hitting_projectile)
add_damage(hitting_projectile.damage, hitting_projectile.damage_flags(), hitting_projectile.damage_type, hitting_projectile.armor_penetration, hitting_projectile)
add_damage(structure_damage, hitting_projectile.damage_flags(), hitting_projectile.damage_type, hitting_projectile.armor_penetration, hitting_projectile)
/obj/structure/proc/climb_on()
+39 -17
View File
@@ -370,6 +370,9 @@ Class Procs:
attack_hand(user)
/obj/structure/machinery/attackby(obj/item/attacking_item, mob/user)
if(default_part_replacement(user, attacking_item))
return TRUE
if(obj_flags & OBJ_FLAG_SIGNALER)
if(issignaler(attacking_item))
if(signaler)
@@ -488,23 +491,43 @@ Class Procs:
update_icon()
return TRUE
/obj/structure/machinery/proc/get_part_replacement_type(obj/item/part)
var/obj/item/circuitboard/board = locate(/obj/item/circuitboard) in component_parts
var/replacement_type
if(board?.req_components)
for(var/component_type in board.req_components)
var/board_part_type = ispath(component_type) ? component_type : text2path(component_type)
if(!ispath(board_part_type, /obj/item/stock_parts) && !ispath(board_part_type, /obj/item/reagent_containers/glass))
continue
if(istype(part, board_part_type) && (!replacement_type || ispath(board_part_type, replacement_type)))
replacement_type = board_part_type
if(replacement_type)
return replacement_type
for(var/component_type in component_types)
var/component_part_type = ispath(component_type) ? component_type : text2path(component_type)
if(!ispath(component_part_type, /obj/item/stock_parts) && !ispath(component_part_type, /obj/item/reagent_containers/glass))
continue
if(istype(part, component_part_type) && (!replacement_type || ispath(component_part_type, replacement_type)))
replacement_type = component_part_type
return replacement_type
/obj/structure/machinery/proc/default_part_replacement(var/mob/user, var/obj/item/storage/part_replacer/R)
if(!LAZYLEN(component_parts))
return FALSE
else if(istype(R))
var/parts_replaced = FALSE
if(panel_open)
var/obj/item/circuitboard/CB = locate(/obj/item/circuitboard) in component_parts
var/P
for(var/obj/item/reagent_containers/glass/G in component_parts)
for(var/D in CB.req_components)
var/T = text2path(D)
if(ispath(G.type, T))
P = T
break
var/replacement_type = get_part_replacement_type(G)
if(!replacement_type)
continue
for(var/obj/item/reagent_containers/glass/B in R.contents)
if(B.reagents && B.reagents.total_volume > 0) continue
if(istype(B, P) && istype(G, P))
if(istype(B, replacement_type))
if(B.volume > G.volume)
R.remove_from_storage(B, src)
R.handle_item_insertion(G, 1)
@@ -512,15 +535,14 @@ Class Procs:
component_parts += B
B.forceMove(src)
to_chat(user, SPAN_NOTICE("[G.name] replaced with [B.name]."))
parts_replaced = TRUE
break
for(var/obj/item/stock_parts/A in component_parts)
for(var/D in CB.req_components)
var/T = text2path(D)
if(ispath(A.type, T))
P = T
break
var/replacement_type = get_part_replacement_type(A)
if(!replacement_type)
continue
for(var/obj/item/stock_parts/B in R.contents)
if(istype(B, P) && istype(A, P))
if(istype(B, replacement_type))
if(B.rating > A.rating)
R.remove_from_storage(B, src)
R.handle_item_insertion(A, 1)
@@ -530,9 +552,9 @@ Class Procs:
to_chat(user, SPAN_NOTICE("[A.name] replaced with [B.name]."))
parts_replaced = TRUE
break
RefreshParts()
update_icon()
if(parts_replaced) //only play sound when RPED actually replaces parts
if(parts_replaced)
RefreshParts()
update_icon()
playsound(src, 'sound/items/rped.ogg', 40, TRUE)
return TRUE
else
@@ -5,6 +5,7 @@
density = TRUE
atom_flags = ATOM_FLAG_CHECKS_BORDER
maxhealth = OBJECT_HEALTH_LOW
pass_flags_self = LETPASSTHROW
/// The type of stack the barricade dropped when disassembled if any.
var/stack_type
/// The amount of stack dropped when disassembled at full health
@@ -83,11 +84,11 @@
AddOverlays(image('icons/obj/barricades.dmi', icon_state = "[src.barricade_type]_closed_wire"))
..()
/obj/structure/barricade/proc/handle_barrier_chance()
///Rolls a chance based on the percentage of the barricade's health remaining. Returns TRUE if the chance succeeds, FALSE if it fails. The chance is divided by the optional chance_divisor parameter, which defaults to 1.
/obj/structure/barricade/proc/handle_barrier_chance(chance_divisor = 1)
if(!anchored)
return FALSE
return prob(max(30,(100.0*health)/maxhealth))
return prob(max(30,(100.0*health)/maxhealth) / chance_divisor)
/obj/structure/barricade/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(mover?.movement_type & PHASING)
@@ -97,6 +98,16 @@
if(istype(mover, /obj/projectile))
return (check_cover(mover,target))
if (get_dir(loc, target) == dir)
if(mover.throwing)
var/chance = (handle_barrier_chance(3))
if(chance)
visible_message(SPAN_WARNING("\The [mover] clips the top of \the [src] and bounces off!"))
if(barricade_hitsound)
playsound(src, barricade_hitsound, 20, 1)
return !density
else
visible_message(SPAN_WARNING("\The [mover] sails over \the [src]!"))
return TRUE
return !density
else
return TRUE
@@ -211,8 +222,6 @@
return .
bullet_ping(hitting_projectile)
var/damage_to_take = hitting_projectile.damage * hitting_projectile.anti_materiel_potential
add_damage(damage_to_take, hitting_projectile.damage_flags(), hitting_projectile.damtype, hitting_projectile.armor_penetration, hitting_projectile)
/obj/structure/barricade/proc/barricade_deconstruct(deconstruct)
if(deconstruct && is_wired)
@@ -357,8 +357,6 @@
for(var/mob/living/L in contents)
hitting_projectile.Impact(L)
damage(proj_damage)
/obj/structure/closet/attackby(obj/item/attacking_item, mob/user)
if(istype(attacking_item, /obj/item/closet_teleporter))
if(linked_teleporter)
@@ -126,7 +126,6 @@
if(. != BULLET_ACT_HIT)
return .
health -= hitting_projectile.get_structure_damage()
check_health()
/obj/structure/closet/statue/attack_generic(mob/user, damage, attack_message, environment_smash, armor_penetration, attack_flags, damage_type)
@@ -797,3 +797,8 @@
new /obj/item/storage/box/unique/tea/messa(src)
new /obj/item/reagent_containers/toothpaste(src)
new /obj/item/reagent_containers/food/drinks/flask/vacuumflask/mouthwash(src)
/obj/structure/closet/crate/tool/machinist/fill()
new /obj/item/reagent_containers/weldpack(src)
new /obj/item/portable_whiteboard(src)
new /obj/item/paint_sprayer(src)
@@ -40,10 +40,6 @@
if(. != BULLET_ACT_HIT)
return .
var/damage = hitting_projectile.damage
if(damage)
add_damage(damage)
/obj/structure/displaycase/on_death()
if(!destroyed)
density = FALSE
+66 -2
View File
@@ -171,7 +171,12 @@
if(isfloor(user.loc))
user.visible_message(SPAN_NOTICE("\The [user] deploys \the [src] on \the [get_turf(loc)]."), SPAN_NOTICE("You deploy \the [src] on \the [get_turf(loc)]."))
user.drop_from_inventory(src)
new flag_structure(user.loc, user.dir, deploy = TRUE, item_flag_path = flag_path)
var/obj/structure/sign/flag/F = new flag_structure(user.loc, user.dir, deploy = TRUE, item_flag_path = flag_path)
F.name = src.name
F.desc = src.desc
if(F.linked_flag)
F.linked_flag.name = src.name
F.linked_flag.desc = src.desc
qdel(src)
/obj/item/flag/afterattack(var/atom/A, var/mob/user, var/adjacent)
@@ -192,7 +197,12 @@
user.visible_message(SPAN_NOTICE("\The [user] fastens \the [src] to \the [A]."), SPAN_NOTICE("You fasten \the [src] to \the [A]."))
user.drop_from_inventory(src)
new flag_structure(user.loc, placement_dir, deploy = TRUE, item_flag_path = flag_path)
var/obj/structure/sign/flag/F = new flag_structure(user.loc, user.dir, deploy = TRUE, item_flag_path = flag_path)
F.name = src.name
F.desc = src.desc
if(F.linked_flag)
F.linked_flag.name = src.name
F.linked_flag.desc = src.desc
qdel(src)
@@ -222,6 +232,8 @@
else
user.visible_message(SPAN_NOTICE("\The [user] unfastens \the [src] and folds it back up."), SPAN_NOTICE("You unfasten \the [src] and fold it back up."))
var/obj/item/flag/F = new flag_item(get_turf(user))
F.name = src.name
F.desc = src.desc
user.put_in_hands(F)
else
user.visible_message(SPAN_NOTICE("\The [user] unfastens the tattered remnants of \the [src]."), SPAN_NOTICE("You unfasten the tattered remains of \the [src]."))
@@ -3719,6 +3731,58 @@
/obj/structure/sign/flag/venus/large/west/Initialize(mapload)
. = ..(mapload, WEST)
// Eris
/obj/item/flag/eris
name = "\improper Erisian Banner"
desc = "The banner of the Sovereign Solarian Union of Eris and Dysnomia, a pair of distant dwarf worlds in the Sol System, dominated by the dual influence of the systems largest communications array and the Solarian Navy."
desc_extended = "The banner of Eris and Dysnomia features a bicolor of white and purple, separated by a gold stripe, with a golden ring and diamond at its center. Four purple stars are arranged along the top, while four white stars line the bottom.<br> \
The white field evokes the planetoids isolation beyond the Kuiper Belt, while the purple pays tribute to the Solarian Navy and its significant influence on Erisian history. The gold stripe represents the vital flow of interstellar communication that passes through Eris. At the center, the golden emblem symbolizes the unity of Eris and Dysnomia: the circle for Eris, the diamond for Dysnomia, and the surrounding ring for their shared infrastructure and mutual dependence.<br> \
The eight stars represent the primary habitats across the Union, four in purple for those on Eris and Dysnomia themselves, and four in white for the orbital habitats positioned among their Lagrange points."
flag_path = "eris"
flag_structure = /obj/structure/sign/flag/eris
/obj/structure/sign/flag/eris
name = "\improper Erisian Banner"
desc = "The banner of the Sovereign Solarian Union of Eris and Dysnomia, a pair of distant dwarf worlds in the Sol System, dominated by the dual influence of the systems largest communications array and the Solarian Navy."
desc_extended = "The banner of Eris and Dysnomia features a bicolor of white and purple, separated by a gold stripe, with a golden ring and diamond at its center. Four purple stars are arranged along the top, while four white stars line the bottom.<br> \
The white field evokes the planetoids isolation beyond the Kuiper Belt, while the purple pays tribute to the Solarian Navy and its significant influence on Erisian history. The gold stripe represents the vital flow of interstellar communication that passes through Eris. At the center, the golden emblem symbolizes the unity of Eris and Dysnomia: the circle for Eris, the diamond for Dysnomia, and the surrounding ring for their shared infrastructure and mutual dependence.<br> \
The eight stars represent the primary habitats across the Union, four in purple for those on Eris and Dysnomia themselves, and four in white for the orbital habitats positioned among their Lagrange points."
flag_path = "eris"
icon_state = "eris"
flag_item = /obj/item/flag/eris
/obj/item/flag/eris/l
name = "\improper Erisian Flag"
desc = "The flag of the Sovereign Solarian Union of Eris and Dysnomia, a pair of distant dwarf worlds in the Sol System, dominated by the dual influence of the systems largest communications array and the Solarian Navy."
desc_extended = "The flag of Eris and Dysnomia features a bicolor of white and purple, separated by a gold stripe, with a golden ring and diamond at its center. Four purple stars are arranged along the top, while four white stars line the bottom.<br> \
The white field evokes the planetoids isolation beyond the Kuiper Belt, while the purple pays tribute to the Solarian Navy and its significant influence on Erisian history. The gold stripe represents the vital flow of interstellar communication that passes through Eris. At the center, the golden emblem symbolizes the unity of Eris and Dysnomia: the circle for Eris, the diamond for Dysnomia, and the surrounding ring for their shared infrastructure and mutual dependence.<br> \
The eight stars represent the primary habitats across the Union, four in purple for those on Eris and Dysnomia themselves, and four in white for the orbital habitats positioned among their Lagrange points."
flag_size = TRUE
flag_structure = /obj/structure/sign/flag/eris/large
/obj/structure/sign/flag/eris/large
name = "\improper Erisian Flag"
desc = "The flag of the Sovereign Solarian Union of Eris and Dysnomia, a pair of distant dwarf worlds in the Sol System, dominated by the dual influence of the systems largest communications array and the Solarian Navy."
desc_extended = "The flag of Eris and Dysnomia features a bicolor of white and purple, separated by a gold stripe, with a golden ring and diamond at its center. Four purple stars are arranged along the top, while four white stars line the bottom.<br> \
The white field evokes the planetoids isolation beyond the Kuiper Belt, while the purple pays tribute to the Solarian Navy and its significant influence on Erisian history. The gold stripe represents the vital flow of interstellar communication that passes through Eris. At the center, the golden emblem symbolizes the unity of Eris and Dysnomia: the circle for Eris, the diamond for Dysnomia, and the surrounding ring for their shared infrastructure and mutual dependence.<br> \
The eight stars represent the primary habitats across the Union, four in purple for those on Eris and Dysnomia themselves, and four in white for the orbital habitats positioned among their Lagrange points."
icon_state = "eris_l"
flag_path = "eris"
flag_size = TRUE
flag_item = /obj/item/flag/eris/l
/obj/structure/sign/flag/eris/large/north/Initialize(mapload)
. = ..(mapload, NORTH)
/obj/structure/sign/flag/eris/large/south/Initialize(mapload)
. = ..(mapload, SOUTH)
/obj/structure/sign/flag/eris/large/east/Initialize(mapload)
. = ..(mapload, EAST)
/obj/structure/sign/flag/eris/large/west/Initialize(mapload)
. = ..(mapload, WEST)
//Hiskyn
/obj/item/flag/hiskyn
name = "\improper Hiskyn's Revanchists flag"
@@ -6,7 +6,7 @@
maxhealth = OBJECT_HEALTH_MEDIUM
color = COLOR_GRAY20
build_amt = 4
pass_flags_self = PASSRAILING
pass_flags_self = PASSRAILING | LETPASSTHROW
layer = WINDOW_FRAME_LAYER
anchored = TRUE
density = TRUE
@@ -54,6 +54,10 @@
return TRUE
if(istype(mover, /obj/structure/closet/crate))
return TRUE
if(istype(mover,/obj/projectile))
return TRUE
if(mover.throwing)
return TRUE
if(istype(mover) && mover.pass_flags & PASSTABLE)
return TRUE
if(locate(/obj/structure/window_frame) in get_turf(mover))
+1 -1
View File
@@ -73,7 +73,7 @@
/obj/structure/girder/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
//Girders only provide partial cover. There's a chance that the projectiles will just pass through. (unless you are trying to shoot the girder)
if(hitting_projectile.original != src && !prob(cover))
return BULLET_ACT_HIT //pass through
return BULLET_ACT_FORCE_PIERCE //pass through
var/damage = hitting_projectile.get_structure_damage()
if(!damage)
@@ -30,8 +30,6 @@
if(. != BULLET_ACT_HIT)
return .
add_damage(hitting_projectile.damage)
/obj/structure/gore/ex_act(severity)
switch(severity)
if(1.0)
@@ -96,8 +96,6 @@
bullet_ping(hitting_projectile)
health -= proj_damage
if(health <= 0)
deflate(TRUE)
return
@@ -6,6 +6,7 @@
density = TRUE
light_system = MOVABLE_LIGHT
maxhealth = OBJECT_HEALTH_LOW
should_use_health = FALSE
obj_flags = OBJ_FLAG_SIGNALER | OBJ_FLAG_CONDUCTABLE
w_class = WEIGHT_CLASS_HUGE
@@ -157,8 +157,6 @@
if(. != BULLET_ACT_HIT)
return .
add_damage(hitting_projectile.get_structure_damage(), hitting_projectile.damage_flags(), hitting_projectile.damage_type, hitting_projectile.armor_penetration, hitting_projectile)
/obj/structure/machinery/camera/ex_act(severity)
if(src.invuln)
return
@@ -381,14 +379,23 @@
alarm_on = 0
GLOB.camera_alarm.clearAlarm(loc, src)
//if false, then the camera is listed as DEACTIVATED and cannot be used
/// If FALSE, then the camera is listed as DEACTIVATED and cannot be used
/obj/structure/machinery/camera/proc/can_use()
if(!status)
return 0
if(stat & (EMPED|BROKEN))
return 0
if(is_wireless_jammed())
return 0
return 1
/// Only used below, but has separate proc for potential future use.
/obj/structure/machinery/camera/proc/is_wireless_camera()
return !isturf(loc)
/obj/structure/machinery/camera/proc/is_wireless_jammed()
return is_wireless_camera() && get_turf(src) && within_jamming_range(src)
/obj/structure/machinery/camera/proc/can_see()
var/list/see = null
var/turf/pos = get_turf(src)
@@ -98,6 +98,7 @@
state = 4
var/obj/structure/machinery/camera/C = new(src.loc)
QDEL_NULL(C.assembly)
src.forceMove(C)
C.assembly = src
@@ -106,6 +107,10 @@
C.replace_networks(uniquelist(tempnetwork))
C.c_tag = input
C.setPowerUsage()
if(C.isMotion() && !(SSmachinery.processing[C]))
START_PROCESSING_MACHINE(C, MACHINERY_PROCESS_SELF)
C.update_coverage()
for(var/i = 5; i >= 0; i -= 1)
var/direct = tgui_input_list(user, "Direction?", "Assembling Camera", list("Confirm", "NORTH", "EAST", "SOUTH", "WEST"))
@@ -127,20 +132,35 @@
return TRUE
// Upgrades!
if(is_type_in_list(attacking_item, possible_upgrades) && !is_type_in_list(attacking_item, upgrades)) // Is a possible upgrade and isn't in the camera already.
var/upgrade_type
for(var/possible_upgrade in possible_upgrades)
if(istype(attacking_item, possible_upgrade))
upgrade_type = possible_upgrade
break
if(upgrade_type)
if(state != 3)
to_chat(user, SPAN_WARNING("You need to wire the assembly before installing upgrades."))
return TRUE
if(locate(upgrade_type) in upgrades)
to_chat(user, SPAN_WARNING("\The [src] already has that upgrade installed."))
return TRUE
if(istype(attacking_item, /obj/item/stock_parts/scanning_module))
var/obj/item/stock_parts/scanning_module/SM = attacking_item
if(SM.rating < 2)
to_chat(user, SPAN_WARNING("That scanning module doesn't seem advanced enough."))
return
return TRUE
to_chat(user, "You attach \the [attacking_item] into the assembly inner circuits.")
upgrades += attacking_item
user.remove_from_mob(attacking_item)
attacking_item.forceMove(src)
return
return TRUE
// Taking out upgrades
else if(attacking_item.tool_behaviour == TOOL_CROWBAR && upgrades.len)
if(state != 3)
to_chat(user, SPAN_WARNING("You need access to the wired circuits before removing upgrades."))
return TRUE
var/obj/U = locate(/obj) in upgrades
if(U)
to_chat(user, "You unattach an upgrade from the assembly.")
@@ -80,6 +80,9 @@ GLOBAL_LIST_INIT(engineering_networks, list(
/obj/structure/machinery/camera/network/news
network = list(NETWORK_NEWS)
/obj/structure/machinery/camera/network/expedition
network = list(NETWORK_EXPEDITION)
// EMP
/obj/structure/machinery/camera/emp_proof/Initialize()
@@ -26,13 +26,23 @@
camera_monitor_program = new("Compless")
// Make sure that camera_monitor knows its been generated from a dedicated console; this will define its network access.
camera_monitor_program.monitored_networks = console_networks
camera_monitor_program.ntnet_endpoint = src
camera_monitor_program.ntnet_endpoint_ethernet = TRUE
/obj/structure/machinery/computer/security/Destroy()
reset_remote_viewers()
if(camera_monitor_program)
camera_monitor_program.reset_current()
camera_monitor_program = null
. = ..()
/obj/structure/machinery/computer/security/proc/reset_remote_viewers()
for(var/mob/M in GLOB.player_list)
if(M.machine == src && M.is_viewing_remote_view())
M.unset_machine()
M.reset_view(null)
/obj/structure/machinery/computer/security/attack_ai(var/mob/user as mob)
if(!ai_can_interact(user))
return
@@ -41,6 +51,8 @@
/obj/structure/machinery/computer/security/check_eye(var/mob/user as mob)
if (user.stat || user.blinded || !operable())
return -1
if(camera_monitor_program?.current_camera)
return camera_monitor_program.check_eye(user)
if(!current_camera)
return 0
var/viewflag = current_camera.check_eye(user)
@@ -51,6 +63,8 @@
/obj/structure/machinery/computer/security/grants_equipment_vision(var/mob/user as mob)
if(user.stat || user.blinded || !operable())
return FALSE
if(camera_monitor_program?.current_camera)
return camera_monitor_program.grants_equipment_vision(user)
if(!current_camera)
return FALSE
var/viewflag = current_camera.check_eye(user)
@@ -91,7 +105,7 @@
. = camera_monitor_program.ui_act(action, params, ui, state)
/obj/structure/machinery/computer/security/attack_hand(var/mob/user as mob)
if (!(src.z in GetConnectedZlevels(starting_z_level)))
if (!(src.z in GetConnectedZlevels(starting_z_level)) && !SSatlas.current_map.is_always_available_network_level(src.z))
to_chat(user, "Unable to establish a connection.")
return
if(stat & (NOPOWER|BROKEN)) return
@@ -115,9 +129,11 @@
if (user.stat || user.blinded || !operable())
return 0
if(!C.can_use())
return 0
set_current(C)
if (!(C.z in GetConnectedZlevels(starting_z_level)))
if (!(C.z in GetConnectedZlevels(starting_z_level)) && !(SSatlas.current_map.is_always_available_network_level(src.z) && C.z == src.z))
to_chat(user, SPAN_NOTICE("This camera is too far away to connect to!"))
return FALSE
@@ -311,7 +327,7 @@
req_access = list(150)
/obj/structure/machinery/computer/security/terminal
name = "camera monitor terminal"
name = "public camera monitor"
icon = 'icons/obj/modular_computers/modular_terminal.dmi'
icon_screen = "cameras"
icon_keyboard = "security_key"
@@ -320,3 +336,7 @@
has_off_keyboards = TRUE
can_pass_under = FALSE
light_power_on = 1
/obj/structure/machinery/computer/security/terminal/public
console_networks = list(NETWORK_EXPEDITION, NETWORK_NEWS)
circuit = /obj/item/circuitboard/security/public
@@ -19,7 +19,10 @@ Deployable Kits
density = TRUE
layer = ABOVE_DOOR_LAYER // above door layer so it can appear on the airlocks like a proper barricade
maxhealth = OBJECT_HEALTH_LOW
armor = list(
MELEE = ARMOR_MELEE_RESISTANT,
BULLET = ARMOR_BALLISTIC_PISTOL
)
var/force_material
/obj/structure/blocker/Initialize(mapload, var/material_name)
@@ -46,13 +49,6 @@ Deployable Kits
if(. != BULLET_ACT_HIT)
return .
var/damage_modifier = 0.4
switch(hitting_projectile.damage_type)
if(DAMAGE_BURN)
damage_modifier = 1
if(DAMAGE_BRUTE)
damage_modifier = 0.75
health -= hitting_projectile.damage * damage_modifier
if(!check_dismantle())
visible_message(SPAN_WARNING("\The [src] is hit by \the [hitting_projectile]!"))
@@ -76,11 +72,6 @@ Deployable Kits
return TRUE
else
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
switch(attacking_item.damtype)
if(DAMAGE_BURN)
src.health -= attacking_item.force * 1
if(DAMAGE_BRUTE)
src.health -= attacking_item.force * 0.75
shake_animation()
playsound(src.loc, material.hitsound, attacking_item.get_clamped_volume(), 1)
if(check_dismantle())
@@ -280,7 +271,7 @@ Deployable Kits
/obj/item/deployable_kit/attack_self(mob/user)
to_chat(user, SPAN_NOTICE("You start assembling \the [src]..."))
if(do_after(user, assembly_time, src, DO_REPAIR_CONSTRUCT))
if(do_after(user, assembly_time, src, DO_DEPLOY))
assemble_kit(user)
qdel(src)
@@ -118,8 +118,6 @@
var/door_frame_color = COLOR_GRAY20
/// Color. The color of the stripe detail.
var/stripe_color = null
/// Color. The color of the symbol detail.
var/symbol_color = null
/// Color. The color of the window.
var/window_color = null
/// String (One of `MATERIAL_*`). The material used for the door's window if `glass` is set. Used to set `window_material` during init.
@@ -523,6 +521,12 @@
explosion_resistance = 20
secured_wires = TRUE
maxhealth = OBJECT_HEALTH_EXTREMELY_HIGH
destroy_hits = 30
armor = list(
MELEE = ARMOR_MELEE_RESISTANT,
BULLET = ARMOR_BALLISTIC_PISTOL,
LASER = ARMOR_LASER_MEDIUM
)
features_powerloss_manual_override = FALSE
ai_bolting_delay = 10
ai_unbolt_delay = 5
@@ -17,6 +17,12 @@
dir = 1
closed_layer = ABOVE_DOOR_LAYER
explosion_resistance = 25
destroy_hits = 40
armor = list(
MELEE = ARMOR_MELEE_MAJOR,
BULLET = ARMOR_BALLISTIC_MEDIUM,
LASER = ARMOR_LASER_RIFLE
)
pass_flags_self = PASSDOORS
rad_resistance_modifier = 6
@@ -255,6 +261,12 @@
icon_state = "shutter1"
damage = SHUTTER_CRUSH_DAMAGE
closed_layer = CLOSED_DOOR_LAYER
destroy_hits = 20
armor = list(
MELEE = ARMOR_MELEE_RESISTANT,
BULLET = ARMOR_BALLISTIC_PISTOL,
LASER = ARMOR_LASER_MEDIUM
)
/obj/structure/machinery/door/blast/shutters/open
icon_state = "shutter0"
@@ -255,21 +255,25 @@
var/damage = hitting_projectile.get_structure_damage()
// Emitter Blasts - these will eventually completely destroy the door, given enough time.
if (damage > 90)
destroy_hits -= (1 * hitting_projectile.anti_materiel_potential)
if (damage >= 30 && health <= 0) //Midlaser damage.
if (damage > 90)
destroy_hits -= max(1, (1 * hitting_projectile.anti_materiel_potential)) //Double dip for strong projectiles, like emitters or the PEAC.
destroy_hits -= max(1, (1 * hitting_projectile.anti_materiel_potential))
if (destroy_hits <= 0)
visible_message(SPAN_DANGER("\The [src.name] disintegrates!"))
switch (hitting_projectile.damage_type)
if(DAMAGE_BRUTE)
new /obj/item/stack/material/steel(src.loc, 2)
new /obj/item/stack/rods(src.loc, 3)
for(var/i = 1 to 3)
if(prob(50))
new /obj/item/stack/material/steel(src.loc, 2)
else
new /obj/item/material/shard(src.loc, MATERIAL_STEEL)
if(DAMAGE_BURN)
new /obj/effect/decal/cleanable/ash(src.loc) // Turn it to ashes!
new /obj/item/material/shard(src.loc, MATERIAL_STEEL)
new /obj/particle_emitter/door_destruction(loc, 2 SECONDS)
qdel(src)
if(damage)
add_damage(damage)
/obj/structure/machinery/door/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
..()
var/tforce = 0
@@ -419,6 +423,8 @@
update_icon()
/obj/structure/machinery/door/on_death(damage, damage_flags, damage_type, armor_penetration, obj/weapon)
if(stat & BROKEN)
return
set_broken()
/obj/structure/machinery/door/proc/set_broken()
@@ -1,506 +0,0 @@
/obj/structure/machinery/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"
density = TRUE
anchored = TRUE
idle_power_usage = 20
active_power_usage = 5000
req_access = list(ACCESS_ROBOTICS)
component_types = list(
/obj/item/circuitboard/mechfab,
/obj/item/stock_parts/matter_bin = 2,
/obj/item/stock_parts/manipulator,
/obj/item/stock_parts/micro_laser,
/obj/item/stock_parts/console_screen
)
manufacturer = "hephaestus"
/**
* A `/list` of enqueued `/datum/design` to be printed, processed in the queue
*/
var/list/datum/design/queue = list()
/**
* How much efficient (or inefficient) the protolathe is at manufacturing things
*/
var/mat_efficiency = 1
/**
* The production speed of this specific protolathe, a factor
*/
var/production_speed = 1
var/list/materials = list(MATERIAL_STEEL = 0, MATERIAL_GLASS = 0, MATERIAL_GOLD = 0, MATERIAL_SILVER = 0, MATERIAL_DIAMOND = 0, MATERIAL_PHORON = 0, MATERIAL_URANIUM = 0, MATERIAL_PLASTEEL = 0, MATERIAL_ALUMINIUM = 0, MATERIAL_LEAD = 0)
var/res_max_amount = 200000
var/datum/research/files
/// The looping sound for production.
var/datum/looping_sound/synth_fab/fab_loop
var/list/categories = list()
var/category = null
var/sync_message = ""
var/limb_manufacturer
///The timer id for the build callback, if we're building something
var/build_callback_timer
/obj/structure/machinery/mecha_part_fabricator/upgrade_hints(mob/user, distance, is_adjacent)
. += ..()
. += "- Upgraded <b>matter bins</b> will increase material storage capacity."
. += SPAN_NOTICE(" - The current storage capacity is <b>[res_max_amount / 2000]</b> sheets")
. += "- 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/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/mecha_part_fabricator/Initialize()
. = ..()
files = new /datum/research(src) //Setup the research data holder.
fab_loop = new(src)
limb_manufacturer = GLOB.basic_robolimb.company
update_categories()
update_icon()
/// Make sure the machine starts the round properly synced.
/obj/structure/machinery/mecha_part_fabricator/LateInitialize()
. = ..()
sync()
/obj/structure/machinery/mecha_part_fabricator/update_icon()
ClearOverlays()
update_fab_audio()
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")
/// Starts and stops the necessary audio.
/obj/structure/machinery/mecha_part_fabricator/proc/update_fab_audio()
if(!fab_loop)
return
if(build_callback_timer)
fab_loop.start()
else
fab_loop.stop()
/obj/structure/machinery/mecha_part_fabricator/dismantle()
SSmaterials.normalize_material_amounts(materials)
for(var/f in materials.Copy())
eject_materials(f, materials[f])
//Stop the queue building if you're dismantling
deltimer(build_callback_timer)
..()
/obj/structure/machinery/mecha_part_fabricator/RefreshParts()
..()
res_max_amount = 0
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
res_max_amount += M.rating * 100000 // 200k -> 600k
var/T = 0
for(var/obj/item/stock_parts/manipulator/M in component_parts)
T += M.rating
mat_efficiency = 1 - (T - 1) / 4 // 1 -> 0.5
for(var/obj/item/stock_parts/micro_laser/M in component_parts) // Not resetting T is intended; speed is affected by both
T += M.rating
production_speed = T / 2 // 1 -> 3
/obj/structure/machinery/mecha_part_fabricator/attack_hand(var/mob/user)
if(..())
return
if(!allowed(user))
to_chat(user, SPAN_WARNING("Access denied."))
return
do_hair_pull(user)
// Yes I'm doing this after do_hair_pull() intentionally, you can totally fail the skill check and still get your hair stuck in a blender.
var/cancelled = FALSE
SEND_SIGNAL(user, COMSIG_USE_MECH_FAB, &cancelled)
if (cancelled)
return
ui_interact(user)
/obj/structure/machinery/mecha_part_fabricator/ui_interact(mob/user, datum/tgui/ui)
. = ..()
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "SyntheticFabricator", "Synthetic Fabricator", 1200, 800)
ui.open()
/obj/structure/machinery/mecha_part_fabricator/ui_data(mob/user)
var/list/data = list()
data["manufacturer"] = manufacturer
var/datum/design/current = queue.len ? queue[1] : null
if(current)
data["current"] = current.name
data["queue"] = get_queue_names()
data["buildable"] = get_build_options()
data["category"] = category
data["available_categories"] = list()
for(var/possible_category in categories)
data["available_categories"] += list(list("name" = possible_category))
if(GLOB.fabricator_robolimbs)
var/list/T = list()
for(var/A in GLOB.fabricator_robolimbs)
var/datum/robolimb/R = GLOB.fabricator_robolimbs[A]
T += list(list("name" = R.company, "type" = A))
data["manufacturers"] = T
data["selected_manufacturer"] = limb_manufacturer
data["materials"] = get_materials()
data["maximum_resource_amount"] = res_max_amount
data["sync"] = sync_message
//If we have something in the queue that we're trying to build, show the time left if the build is undergoing, otherwise null
//the logic of displaying it is handled client-side
if(current)
if(build_callback_timer)
data["timeleft"] = round(timeleft(build_callback_timer))
else
data["timeleft"] = null
return data
/obj/structure/machinery/mecha_part_fabricator/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
switch(action)
if("build")
var/path = text2path(params["build"])
add_to_queue(path)
. = TRUE
if("remove")
remove_from_queue(text2num(params["remove"]))
. = TRUE
if("category")
if(params["category"] in categories)
category = params["category"]
. = TRUE
if("manufacturer")
if(params["manufacturer"] in GLOB.fabricator_robolimbs)
limb_manufacturer = params["manufacturer"]
. = TRUE
if("eject")
eject_materials(params["eject"], text2num(params["amount"]))
. = TRUE
if("sync")
sync()
. = TRUE
else
sync_message = ""
if(.)
playsound(src, 'sound/machines/synthfab/synthfab_button.ogg', 50)
/obj/structure/machinery/mecha_part_fabricator/attackby(obj/item/attacking_item, mob/user)
if(build_callback_timer)
to_chat(user, SPAN_NOTICE("\The [src] is busy. Please wait for completion of previous operation."))
return TRUE
if(default_deconstruction_screwdriver(user, attacking_item))
return TRUE
if(default_deconstruction_crowbar(user, attacking_item))
return TRUE
if(default_part_replacement(user, attacking_item))
return TRUE
if(!istype(attacking_item, /obj/item/stack/material))
return ..()
var/obj/item/stack/material/M = attacking_item
if(!M.material)
return ..()
var/material = SSmaterials.material_to_path(M.material, FALSE)
if(!(material in list(MATERIAL_STEEL, MATERIAL_GLASS, MATERIAL_GOLD, MATERIAL_SILVER, MATERIAL_DIAMOND, MATERIAL_PHORON, MATERIAL_URANIUM, MATERIAL_PLASTEEL, MATERIAL_ALUMINIUM, MATERIAL_LEAD)))
to_chat(user, SPAN_WARNING("\The [src] cannot hold [M.material.name]."))
return TRUE
var/sname = "[M.name]"
SSmaterials.normalize_material_amounts(materials)
if((materials[material] || 0) + SHEET_MATERIAL_AMOUNT <= res_max_amount)
if(M.amount >= 1)
var/count = 0
var/mutable_appearance/MA = mutable_appearance(icon, "material_insertion")
MA.color = M.material.icon_colour
//first play the insertion animation
flick_overlay_view(MA, 1 SECONDS)
//now play the progress bar animation
flick_overlay_view(mutable_appearance(icon, "fab_progress"), 1 SECONDS)
while((materials[material] || 0) + SHEET_MATERIAL_AMOUNT <= res_max_amount && M.amount >= 1)
SSmaterials.add_material_amount(materials, material, M.perunit)
M.use(1)
count++
to_chat(user, SPAN_NOTICE("You insert [count] [sname] into \the [src]."))
//Wake up, we have things to do (maybe)
handle_queue()
else
to_chat(user, SPAN_NOTICE("\The [src] cannot hold more [sname]."))
return TRUE
/obj/structure/machinery/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/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\"")
/**
* Adds a design to the queue
*
* * path: The path of the design to add
*/
/obj/structure/machinery/mecha_part_fabricator/proc/add_to_queue(path)
var/datum/design/D = files.known_designs[path]
if(!D)
return
queue += D
//Wake up, we have things to do
handle_queue()
/**
* Removes a design from the queue
*
* * index: The index of the design to remove
*/
/obj/structure/machinery/mecha_part_fabricator/proc/remove_from_queue(index)
queue.Cut(index, index + 1)
//If we're removing the first thing in the queue, stop the build timer
if(index == 1)
deltimer(build_callback_timer)
build_callback_timer = null
//Wake up, we have things to do
handle_queue()
/**
* Handle the construction queue
*/
/obj/structure/machinery/mecha_part_fabricator/proc/handle_queue()
//No work to do or already busy, stop
if(!length(queue) || build_callback_timer)
//If we don't have a build in progress, revert to idle power usage
if(!build_callback_timer)
update_use_power(POWER_USE_IDLE)
update_icon()
return
//If there's no power, there's no building
if(stat & NOPOWER)
queue = list()
return
//Get the first design in the queue
var/datum/design/D = queue[1]
//If we can build it, process the request
if(can_build(D))
update_use_power(POWER_USE_ACTIVE)
//Add the callback timer for printing, and remove the design from the queue
build_callback_timer = addtimer(CALLBACK(src, PROC_REF(build), D), (D.time / production_speed), TIMER_UNIQUE|TIMER_STOPPABLE)
//No queue removal here because we show how long it takes to build in the UI
else
visible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] flashes: Insufficient materials to complete build: [D.name]."))
remove_from_queue(1)
update_icon()
/obj/structure/machinery/mecha_part_fabricator/proc/can_build(var/datum/design/D)
SSmaterials.normalize_material_amounts(materials)
for(var/M in D.materials)
var/material = SSmaterials.material_to_path(M, FALSE)
if(!material)
material = M
if((materials[material] || 0) < D.materials[M] * mat_efficiency)
return 0
return 1
/**
* Builds the given design, assuming all the necessary conditions are met
*
* * design_to_build: The design to build
*/
/obj/structure/machinery/mecha_part_fabricator/proc/build(datum/design/design_to_build)
//Consume the materials
SSmaterials.normalize_material_amounts(materials)
for(var/M in design_to_build.materials)
SSmaterials.remove_material_amount(materials, M, design_to_build.materials[M] * mat_efficiency)
intent_message(MACHINE_SOUND)
if(design_to_build.build_path)
var/loc_offset = get_step(src, dir)
var/obj/new_item = design_to_build.Fabricate(loc_offset, src)
visible_message("\The <b>[src]</b> pings, indicating that \the [new_item] is complete.", "You hear a ping.", intent_message = PING_SOUND)
if(mat_efficiency != 1)
if(new_item.matter && new_item.matter.len > 0)
for(var/i in new_item.matter)
new_item.matter[i] = new_item.matter[i] * mat_efficiency
//We finished building, clear the timer and remove the thing from the queue
build_callback_timer = null
remove_from_queue(1)
//Do the queue handling for the next item, or to stop
handle_queue()
/obj/structure/machinery/mecha_part_fabricator/proc/get_queue_names()
. = list()
for(var/i = 1 to queue.len)
var/datum/design/D = queue[i]
. += list(list("name" = D.name, "time" = get_design_time(D), "index" = i))
/obj/structure/machinery/mecha_part_fabricator/proc/get_build_options()
. = list()
for(var/path in files.known_designs)
var/datum/design/D = files.known_designs[path]
if(!D.build_path || !(D.build_type & MECHFAB) || !D.category || (D.category != category))
continue
. += list(list("name" = D.name, "desc" = D.desc, "type" = D.type, "category" = D.category, "resources" = get_design_resourses(D), "time" = get_design_time(D)))
/obj/structure/machinery/mecha_part_fabricator/proc/get_design_resourses(var/datum/design/D)
var/list/F = list()
for(var/T in D.materials)
F += "[SSmaterials.material_display_name(T)]: [D.materials[T] * mat_efficiency]"
return english_list(F, and_text = ", ")
/obj/structure/machinery/mecha_part_fabricator/proc/get_design_time(var/datum/design/D)
return time2text(round(10 * D.time / production_speed), "mm:ss")
/obj/structure/machinery/mecha_part_fabricator/proc/update_categories()
categories = list()
for(var/path in files.known_designs)
var/datum/design/D = files.known_designs[path]
if(!D.build_path || !(D.build_type & MECHFAB) || !D.category)
continue
categories |= D.category
if(!category || !(category in categories))
category = categories[1]
/obj/structure/machinery/mecha_part_fabricator/proc/get_materials()
. = list()
SSmaterials.normalize_material_amounts(materials)
for(var/T in materials)
. += list(list("name" = SSmaterials.material_display_name(T), "amount" = materials[T]))
/obj/structure/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount)
if(!amount)
return
SSmaterials.normalize_material_amounts(materials)
material = SSmaterials.material_to_path(material, FALSE)
if(!material)
return
var/stack_type = SSmaterials.material_stack_type(material)
if(!stack_type)
return
var/real_amount = min(round(amount / SHEET_MATERIAL_AMOUNT), round((materials[material] || 0) / SHEET_MATERIAL_AMOUNT))
if(real_amount < 1)
return
var/obj/item/stack/material/S = new stack_type(loc, real_amount)
S.update_icon()
SSmaterials.remove_material_amount(materials, material, real_amount * SHEET_MATERIAL_AMOUNT)
/obj/structure/machinery/mecha_part_fabricator/proc/sync()
sync_message = "Error: no console found."
for(var/obj/structure/machinery/computer/rdconsole/RDC in get_area(src))
if(!RDC.sync)
continue
for(var/id in RDC.files.known_tech)
var/datum/tech/T = RDC.files.known_tech[id]
files.AddTech2Known(T)
files.RefreshResearch()
sync_message = "Sync complete."
update_categories()
@@ -1,4 +1,7 @@
GLOBAL_VAR(bomb_set)
// Security needs time to respond to it
#define MIN_BOMB_TIMER 600
#define MAX_BOMB_TIMER 900
/obj/structure/machinery/nuclearbomb
name = "\improper Nuclear Fission Explosive"
@@ -11,7 +14,7 @@ GLOBAL_VAR(bomb_set)
var/deployable = 0
var/extended = 0
var/lighthack = 0
var/timeleft = 120
var/timeleft = MIN_BOMB_TIMER
var/timing = 0
var/alerted = 0
var/r_code = "ADMIN"
@@ -268,7 +271,7 @@ GLOBAL_VAR(bomb_set)
switch(action)
if("time")
timeleft += text2num(params["value"])
timeleft = clamp(timeleft, 120, 600)
timeleft = clamp(timeleft, MIN_BOMB_TIMER, MAX_BOMB_TIMER)
return TRUE
if("timer")
if(timing == -1)
@@ -282,6 +285,9 @@ GLOBAL_VAR(bomb_set)
if(wires.is_cut(WIRE_TIMING))
to_chat(usr, SPAN_WARNING("Nothing happens, something might be wrong with the wiring."))
return TRUE
if(!area_safety_check())
audible_message(SPAN_WARNING("\The [src] emits a beep, \"Deployment aborted: local radiation shielding prevents reliable detonation coupling.\""), "\The [src]'s deployment light blinks red once and disables.")
return TRUE
if(!timing && !safety)
timing = 1
log_and_message_admins("engaged a nuclear bomb")
@@ -290,7 +296,7 @@ GLOBAL_VAR(bomb_set)
else
secure_device()
if(alerted == 0)
set_security_level(SEC_LEVEL_DELTA)
set_security_level(SEC_LEVEL_DELTA, TRUE, get_area(src))
alerted = 1
return TRUE
if("safety")
@@ -307,7 +313,13 @@ GLOBAL_VAR(bomb_set)
anchored = 0
visible_message(SPAN_WARNING("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut."))
return TRUE
if(!isinspace())
if(isinspace())
to_chat(usr, SPAN_WARNING("There is nothing to anchor to!"))
return TRUE
if(!area_safety_check())
audible_message(SPAN_WARNING("\The [src] emits a beep, \"Deployment aborted: local radiation shielding prevents reliable detonation coupling.\""), "\The [src]'s deployment light blinks red once and disables.")
return TRUE
else
anchored = !anchored
if(anchored)
visible_message(SPAN_WARNING("With a steely snap, bolts slide out of [src] and anchor it to the flooring."))
@@ -316,8 +328,6 @@ GLOBAL_VAR(bomb_set)
secure_device()
visible_message(SPAN_WARNING("The anchoring bolts slide back into the depths of [src]."))
playsound(src, 'sound/machines/boltsup.ogg', 30, 0, extrarange = SILENCED_SOUND_EXTRARANGE)
else
to_chat(usr, SPAN_WARNING("There is nothing to anchor to!"))
return TRUE
/obj/structure/machinery/nuclearbomb/proc/secure_device()
@@ -326,12 +336,22 @@ GLOBAL_VAR(bomb_set)
GLOB.bomb_set--
timing = 0
timeleft = clamp(timeleft, 120, 600)
timeleft = clamp(timeleft, MIN_BOMB_TIMER, MAX_BOMB_TIMER)
update_icon()
/obj/structure/machinery/nuclearbomb/ex_act(severity)
return
// Is it in a valid area
/obj/structure/machinery/nuclearbomb/proc/area_safety_check()
var/area/A = get_area(src)
// It needs to be the game map and not in a rad shielded area.
// That keeps it out of maintenance and other areas we don't want it deployed in
if(A.area_flags & AREA_FLAG_RAD_SHIELDED || !A.station_area)
return FALSE
else
return TRUE
#define NUKERANGE 80
/obj/structure/machinery/nuclearbomb/proc/explode()
if (src.safety)
@@ -347,10 +367,9 @@ GLOBAL_VAR(bomb_set)
sleep(100)
var/off_station = 0
var/turf/bomb_location = get_turf(src)
if(bomb_location && is_station_level(bomb_location.z))
if( (bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)) )
off_station = 1
// This should really not be necessary, but lets do it just to be safe.
if(!area_safety_check() && isinspace())
off_station = 1
else
off_station = 2
@@ -365,7 +384,7 @@ GLOBAL_VAR(bomb_set)
if(SSticker.mode)
SSticker.mode.explosion_in_progress = 0
if(off_station == 1)
to_world("<b>A nuclear device was set off, but the explosion was out of reach of the [station_name(TRUE)]!</b>")
to_world("<b>A nuclear device was set off, but the explosion was unable to destroy the [station_name(TRUE)]!</b>")
else if(off_station == 2)
to_world("<b>A nuclear device was set off, but the device was not on the [station_name(TRUE)]!</b>")
else
@@ -469,3 +488,6 @@ GLOBAL_VAR(bomb_set)
continue
T.icon_state = target_icon_state
last_turf_state = target_icon_state
#undef MIN_BOMB_TIMER
#undef MAX_BOMB_TIMER
@@ -500,8 +500,6 @@
if(. != BULLET_ACT_HIT)
return .
add_damage(damage)
/obj/structure/machinery/porta_turret/emp_act(severity)
. = ..()
@@ -30,7 +30,8 @@
/obj/item/clothing/mask/smokable/ecig,
/obj/item/inductive_charger/handheld,
/obj/item/auto_cpr,
/obj/item/personal_shield
/obj/item/personal_shield,
/obj/item/electronic_assembly
)
var/icon_state_charged = "recharger100"
var/icon_state_charging = "recharger"
@@ -19,7 +19,7 @@
var/icon_update_tick = 0 // Used to rebuild the overlay only once every 10 ticks
///Multiplier applied to all operations of giving power to cells, represents entropy, efficiency increases with upgrades
var/charging_efficiency = 1.3
var/charging_efficiency = 2
///Watts, Power rating drawn from internal cell to recharge occupant's cell 60 kW unupgraded
var/charging_power
@@ -229,7 +229,7 @@
man_rating += P.rating
cell = locate(/obj/item/cell) in component_parts
charging_efficiency = 1.3 + 0.030 * cap_rating
charging_efficiency = 2 + 0.030 * cap_rating
charging_power = 30000 + 12000 * cap_rating
restore_power_active = 10000 + 10000 * cap_rating
restore_power_passive = 5000 + 1000 * cap_rating
@@ -69,7 +69,10 @@
return ..()
/obj/structure/machinery/stasis_cage/process(seconds_per_tick)
if (use_power || !cell || !cell.charge)
if (!use_power || !cell || !cell.charge)
if (isanimal(contained))
var/mob/living/simple_animal/SA = contained
SA.in_stasis = FALSE
return
cell.use(2 * seconds_per_tick)
if (contained)
@@ -100,7 +103,10 @@
release()
/obj/structure/machinery/stasis_cage/proc/release()
if (contained)
if(contained)
if(isanimal(contained))
var/mob/living/simple_animal/SA = contained
SA.in_stasis = FALSE
contained.dropInto(src)
contained = null
playsound(get_turf(src), 'sound/machines/airlock.ogg', 40)
@@ -249,6 +255,12 @@
playsound(src, 'sound/machines/AirlockClose.ogg', 100)
add_fingerprint(user)
if (do_after(user, 2 SECONDS, src))
if(QDELETED(target) || contained || broken || !use_power)
return
if(target.buckled_to)
target.buckled_to.unbuckle()
if(istype(target.buckled_to, /obj/item/trap/animal))
var/obj/item/trap/animal/animal_trap = target.buckled_to
animal_trap.release(transferring = TRUE)
else
target.buckled_to.unbuckle()
contain(user, target)
@@ -178,6 +178,11 @@
var/list/receive = get_hearers_in_radio_ranges(radios)
// Electronic radio headsets expose received traffic to their built-in
// command receiver independently of whether a mob can hear the message.
for(var/obj/item/radio/headset/circuitry/circuit_headset in radios)
circuit_headset.receive_circuit_radio_command(data["name"], message, get_frequency_name(frequency))
// Cut out admins which have radio chatter disabled
for (var/mob/R in receive)
if(R.client && R.client.holder && !(R.client.prefs.toggles & CHAT_RADIO))
@@ -19,15 +19,18 @@
vend_id = "robo-tools"
products = list(
/obj/item/stack/cable_coil = 4,
/obj/item/flash/synthetic = 4,
/obj/item/flash/synthetic = 10,
/obj/item/cell/high = 12,
/obj/item/assembly/prox_sensor = 8,
/obj/item/assembly/signaler = 20,
/obj/item/healthanalyzer = 3,
/obj/item/surgery/scalpel = 2,
/obj/item/surgery/hemostat = 2,
/obj/item/storage/firstaid/empty = 2,
/obj/item/surgery/circular_saw = 2,
/obj/item/screwdriver = 5,
/obj/item/crowbar = 5
/obj/item/crowbar = 5,
/obj/item/mmi = 2
)
contraband = list(
/obj/item/flash = 2
@@ -506,6 +506,14 @@
/obj/item/clothing/suit/storage/tajaran/messa = 3,
/obj/item/clothing/suit/storage/tajaran/matake = 2,
/obj/item/clothing/suit/storage/tajaran/azubarre = 2,
/obj/item/clothing/suit/storage/hooded/trinary_robes = 1,
/obj/item/clothing/suit/storage/hooded/trinary_robes/templeist = 1,
/obj/item/clothing/suit/storage/hooded/trinary_robes/habit = 1,
/obj/item/clothing/head/trinary = 1,
/obj/item/clothing/accessory/poncho/trinary/shouldercape = 1,
/obj/item/clothing/accessory/poncho/trinary/pellegrina = 1,
/obj/item/clothing/accessory/poncho/trinary = 1,
/obj/item/clothing/accessory/badge/trinary = 1,
/obj/item/clothing/head/chaplain_hood = 3,
/obj/item/clothing/head/nun_hood = 3,
/obj/item/clothing/head/hijab = 3,
@@ -515,6 +523,7 @@
/obj/item/clothing/mask/trinary_mask = 3,
/obj/item/clothing/mask/gas/unathi = 3,
/obj/item/clothing/mask/tajara = 3,
/obj/item/clothing/ears/antenna/trinary_halo = 1,
/obj/item/clothing/accessory/apron/dharmela = 2,
/obj/item/clothing/shoes/sneakers/black = 3,
/obj/item/storage/box/fancy/candle_box = 3,
@@ -202,7 +202,6 @@
if(. != BULLET_ACT_HIT)
return .
health -= hitting_projectile.damage
bullet_ping(hitting_projectile)
CheckHealth()
-2
View File
@@ -114,8 +114,6 @@
if(. != BULLET_ACT_HIT)
return .
add_damage(proj_damage, hitting_projectile.damage_flags(), hitting_projectile.damage_type, hitting_projectile.armor_penetration, hitting_projectile)
/obj/structure/window/ex_act(severity)
switch(severity)
if(1)