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
+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)