diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm
deleted file mode 100644
index 12548b8f7d5..00000000000
--- a/code/game/objects/items/RCD.dm
+++ /dev/null
@@ -1,1549 +0,0 @@
-#define GLOW_MODE 3
-#define LIGHT_MODE 2
-#define REMOVE_MODE 1
-
-/*
-CONTAINS:
-RCD
-ARCD
-RLD
-*/
-
-/obj/item/construction
- name = "not for ingame use"
- desc = "A device used to rapidly build and deconstruct. Reload with iron, plasteel, glass or compressed matter cartridges."
- opacity = FALSE
- density = FALSE
- anchored = FALSE
- flags_1 = CONDUCT_1
- item_flags = NOBLUDGEON
- force = 0
- throwforce = 10
- throw_speed = 3
- throw_range = 5
- w_class = WEIGHT_CLASS_NORMAL
- custom_materials = list(/datum/material/iron=100000)
- req_access = list(ACCESS_ENGINE_EQUIP)
- armor_type = /datum/armor/item_construction
- resistance_flags = FIRE_PROOF
- var/datum/effect_system/spark_spread/spark_system
- var/matter = 0
- var/max_matter = 100
- var/no_ammo_message = "The \'Low Ammo\' light on the device blinks yellow."
- var/has_ammobar = FALSE //controls whether or not does update_icon apply ammo indicator overlays
- var/ammo_sections = 10 //amount of divisions in the ammo indicator overlay/number of ammo indicator states
- /// Bitflags for upgrades
- var/upgrade = NONE
- /// Bitflags for banned upgrades
- var/banned_upgrades = NONE
- var/datum/component/remote_materials/silo_mats //remote connection to the silo
- var/silo_link = FALSE //switch to use internal or remote storage
-
-/datum/armor/item_construction
- fire = 100
- acid = 50
-
-/obj/item/construction/Initialize(mapload)
- . = ..()
- spark_system = new /datum/effect_system/spark_spread
- spark_system.set_up(5, 0, src)
- spark_system.attach(src)
- if(upgrade & RCD_UPGRADE_SILO_LINK)
- silo_mats = AddComponent(/datum/component/remote_materials, "RCD", mapload, FALSE)
-
-///used for examining the RCD and for its UI
-/obj/item/construction/proc/get_silo_iron()
- if(silo_link && silo_mats.mat_container && !silo_mats.on_hold())
- return silo_mats.mat_container.get_material_amount(/datum/material/iron)/500
- return FALSE
-
-///returns local matter units available. overriden by rcd borg to return power units available
-/obj/item/construction/proc/get_matter(mob/user)
- return matter
-
-/obj/item/construction/examine(mob/user)
- . = ..()
- . += "It currently holds [get_matter(user)]/[max_matter] matter-units."
- if(upgrade & RCD_UPGRADE_SILO_LINK)
- . += "Remote storage link state: [silo_link ? "[silo_mats.on_hold() ? "ON HOLD" : "ON"]" : "OFF"]."
- var/iron = get_silo_iron()
- if(iron)
- . += "Remote connection has iron in equivalent to [iron] RCD unit\s." //1 matter for 1 floor tile, as 4 tiles are produced from 1 iron
-
-/obj/item/construction/Destroy()
- QDEL_NULL(spark_system)
- silo_mats = null
- return ..()
-
-/obj/item/construction/pre_attack(atom/target, mob/user, params)
- if(istype(target, /obj/item/rcd_upgrade))
- install_upgrade(target, user)
- return TRUE
- if(insert_matter(target, user))
- return TRUE
- return ..()
-
-/obj/item/construction/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/rcd_upgrade))
- install_upgrade(W, user)
- return TRUE
- if(insert_matter(W, user))
- return TRUE
- return ..()
-
-/// Installs an upgrade into the RCD checking if it is already installed, or if it is a banned upgrade
-/obj/item/construction/proc/install_upgrade(obj/item/rcd_upgrade/rcd_up, mob/user)
- if(rcd_up.upgrade & upgrade)
- to_chat(user, span_warning("[src] has already installed this upgrade!"))
- return
- if(rcd_up.upgrade & banned_upgrades)
- to_chat(user, span_warning("[src] can't install this upgrade!"))
- return
- upgrade |= rcd_up.upgrade
- if((rcd_up.upgrade & RCD_UPGRADE_SILO_LINK) && !silo_mats)
- silo_mats = AddComponent(/datum/component/remote_materials, "RCD", FALSE, FALSE)
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
- qdel(rcd_up)
-
-/// Inserts matter into the RCD allowing it to build
-/obj/item/construction/proc/insert_matter(obj/O, mob/user)
- if(iscyborg(user))
- return FALSE
-
- var/loaded = FALSE
- if(istype(O, /obj/item/rcd_ammo))
- var/obj/item/rcd_ammo/R = O
- var/load = min(R.ammoamt, max_matter - matter)
- if(load <= 0)
- to_chat(user, span_warning("[src] can't hold any more matter-units!"))
- return FALSE
- R.ammoamt -= load
- if(R.ammoamt <= 0)
- qdel(R)
- matter += load
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
- loaded = TRUE
- else if(isstack(O))
- loaded = loadwithsheets(O, user)
- if(loaded)
- to_chat(user, span_notice("[src] now holds [matter]/[max_matter] matter-units."))
- update_appearance() //ensures that ammo counters (if present) get updated
- return loaded
-
-/obj/item/construction/proc/loadwithsheets(obj/item/stack/S, mob/user)
- var/value = S.matter_amount
- if(value <= 0)
- to_chat(user, span_notice("You can't insert [S.name] into [src]!"))
- return FALSE
- var/maxsheets = round((max_matter-matter)/value) //calculate the max number of sheets that will fit in RCD
- if(maxsheets > 0)
- var/amount_to_use = min(S.amount, maxsheets)
- S.use(amount_to_use)
- matter += value*amount_to_use
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
- to_chat(user, span_notice("You insert [amount_to_use] [S.name] sheets into [src]. "))
- return TRUE
- to_chat(user, span_warning("You can't insert any more [S.name] sheets into [src]!"))
- return FALSE
-
-/obj/item/construction/proc/activate()
- playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
-
-/obj/item/construction/attack_self(mob/user)
- playsound(loc, 'sound/effects/pop.ogg', 50, FALSE)
- if(prob(20))
- spark_system.start()
-
-/obj/item/construction/update_overlays()
- . = ..()
- if(has_ammobar)
- var/ratio = CEILING((matter / max_matter) * ammo_sections, 1)
- if(ratio > 0)
- . += "[icon_state]_charge[ratio]"
-
-/obj/item/construction/proc/useResource(amount, mob/user)
- if(!silo_mats || !silo_link)
- if(matter < amount)
- if(user)
- to_chat(user, no_ammo_message)
- return FALSE
- matter -= amount
- update_appearance()
- return TRUE
- else
- if(silo_mats.on_hold())
- if(user)
- to_chat(user, span_alert("Mineral access is on hold, please contact the quartermaster."))
- return FALSE
- if(!silo_mats.mat_container)
- to_chat(user, span_alert("No silo link detected. Connect to silo via multitool."))
- return FALSE
- if(!silo_mats.mat_container.has_materials(list(/datum/material/iron = 500), amount))
- if(user)
- to_chat(user, no_ammo_message)
- return FALSE
-
- var/list/materials = list()
- materials[GET_MATERIAL_REF(/datum/material/iron)] = 500
- silo_mats.mat_container.use_materials(materials, amount)
- silo_mats.silo_log(src, "consume", -amount, "build", materials)
- return TRUE
-
-///shared data for rcd,rld & plumbing
-/obj/item/construction/ui_data(mob/user)
- var/list/data = list()
-
- //matter in the rcd
- var/total_matter = ((upgrade & RCD_UPGRADE_SILO_LINK) && silo_link) ? get_silo_iron() : get_matter(user)
- if(!total_matter)
- total_matter = 0
- data["matterLeft"] = total_matter
-
- //silo details
- data["silo_upgraded"] = !!(upgrade & RCD_UPGRADE_SILO_LINK)
- data["silo_enabled"] = silo_link
-
- return data
-
-///shared action for toggling silo link rcd,rld & plumbing
-/obj/item/construction/ui_act(action, list/params)
- . = ..()
- if(.)
- return
-
- if(action == "toggle_silo" && (upgrade & RCD_UPGRADE_SILO_LINK))
- if(silo_mats)
- if(!silo_mats.mat_container && !silo_link) // Allow them to turn off an invalid link.
- to_chat(usr, span_alert("No silo link detected. Connect to silo via multitool."))
- return FALSE
- silo_link = !silo_link
- to_chat(usr, span_notice("You change [src]'s storage link state: [silo_link ? "ON" : "OFF"]."))
- else
- to_chat(usr, span_warning("[src] doesn't have remote storage connection."))
- return TRUE
-
-/obj/item/construction/proc/checkResource(amount, mob/user)
- if(!silo_mats || !silo_mats.mat_container || !silo_link)
- if(silo_link)
- to_chat(user, span_alert("Connected silo link is invalid. Reconnect to silo via multitool."))
- return FALSE
- else
- . = matter >= amount
- else
- if(silo_mats.on_hold())
- if(user)
- to_chat(user, span_alert("Mineral access is on hold, please contact the quartermaster."))
- return FALSE
- . = silo_mats.mat_container.has_materials(list(/datum/material/iron = 500), amount)
- if(!. && user)
- to_chat(user, no_ammo_message)
- if(has_ammobar)
- flick("[icon_state]_empty", src) //somewhat hacky thing to make RCDs with ammo counters actually have a blinking yellow light
- return .
-
-/obj/item/construction/proc/range_check(atom/A, mob/user)
- if(A.z != user.z)
- return
- if(!(A in dview(7, get_turf(user))))
- to_chat(user, span_warning("The \'Out of Range\' light on [src] blinks red."))
- return FALSE
- else
- return TRUE
-
-/**
- * Checks if we are allowed to interact with a radial menu
- *
- * Arguments:
- * * user The living mob interacting with the menu
- * * remote_anchor The remote anchor for the menu
- */
-/obj/item/construction/proc/check_menu(mob/living/user, remote_anchor)
- if(!istype(user))
- return FALSE
- if(user.incapacitated())
- return FALSE
- if(remote_anchor && user.remote_control != remote_anchor)
- return FALSE
- return TRUE
-
-#define RCD_DESTRUCTIVE_SCAN_RANGE 10
-#define RCD_HOLOGRAM_FADE_TIME (15 SECONDS)
-#define RCD_DESTRUCTIVE_SCAN_COOLDOWN (RCD_HOLOGRAM_FADE_TIME + 1 SECONDS)
-
-///each define maps to a variable used for construction in the RCD
-#define CONSTRUCTION_MODE "construction_mode"
-#define WINDOW_TYPE "window_type"
-#define WINDOW_GLASS "window_glass"
-#define WINDOW_SIZE "window_size"
-#define COMPUTER_DIR "computer_dir"
-#define WALLFRAME_TYPE "wallframe_type"
-#define FURNISH_TYPE "furnish_type"
-#define FURNISH_COST "furnish_cost"
-#define FURNISH_DELAY "furnish_delay"
-#define AIRLOCK_TYPE "airlock_type"
-
-///flags to be sent to UI
-#define TITLE "title"
-#define ICON "icon"
-
-///flags for creating icons shared by an entire category
-#define CATEGORY_ICON_STATE "category_icon_state"
-#define CATEGORY_ICON_SUFFIX "category_icon_suffix"
-#define TITLE_ICON "ICON=TITLE"
-
-/obj/item/construction/rcd
- name = "rapid-construction-device (RCD)"
- icon = 'icons/obj/tools.dmi'
- icon_state = "rcd"
- worn_icon_state = "RCD"
- lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
- custom_premium_price = PAYCHECK_COMMAND * 10
- max_matter = 160
- slot_flags = ITEM_SLOT_BELT
- item_flags = NO_MAT_REDEMPTION | NOBLUDGEON
- has_ammobar = TRUE
- actions_types = list(/datum/action/item_action/rcd_scan)
-
- ///all stuff used by RCD for construction
- var/static/list/root_categories = list(
- //1ST ROOT CATEGORY
- "Construction" = list( //Stuff you use to make & decorate areas
- //Walls & Windows
- "Structures" = list(
- list(CONSTRUCTION_MODE = RCD_FLOORWALL, ICON = "wallfloor", TITLE = "Wall/Floor"),
- list(CONSTRUCTION_MODE = RCD_WINDOWGRILLE, WINDOW_TYPE = /obj/structure/window, WINDOW_GLASS = RCD_WINDOW_NORMAL, WINDOW_SIZE = RCD_WINDOW_DIRECTIONAL, ICON = "windowsize", TITLE = "Directional Window"),
- list(CONSTRUCTION_MODE = RCD_WINDOWGRILLE, WINDOW_TYPE = /obj/structure/window/reinforced, WINDOW_GLASS = RCD_WINDOW_REINFORCED, WINDOW_SIZE = RCD_WINDOW_DIRECTIONAL, ICON = "windowtype", TITLE = "Directional Reinforced Window"),
- list(CONSTRUCTION_MODE = RCD_WINDOWGRILLE, WINDOW_TYPE = /obj/structure/window/fulltile, WINDOW_GLASS = RCD_WINDOW_NORMAL, WINDOW_SIZE = RCD_WINDOW_FULLTILE, ICON = "window0", TITLE = "Full Tile Window"),
- list(CONSTRUCTION_MODE = RCD_WINDOWGRILLE, WINDOW_TYPE = /obj/structure/window/reinforced/fulltile, WINDOW_GLASS = RCD_WINDOW_REINFORCED, WINDOW_SIZE = RCD_WINDOW_FULLTILE, ICON = "rwindow0", TITLE = "Full Tile Reinforced Window"),
- list(CONSTRUCTION_MODE = RCD_CATWALK, ICON = "catwalk-0", TITLE = "Catwalk"),
- list(CONSTRUCTION_MODE = RCD_REFLECTOR, ICON = "reflector_base", TITLE = "Reflector"),
- ),
-
- //Computers & Machine Frames
- "Machines" = list(
- list(CONSTRUCTION_MODE = RCD_MACHINE, ICON = "box_1", TITLE = "Machine Frame"),
- list(CONSTRUCTION_MODE = RCD_COMPUTER, COMPUTER_DIR = 1, ICON = "cnorth", TITLE = "Computer North"),
- list(CONSTRUCTION_MODE = RCD_COMPUTER, COMPUTER_DIR = 2, ICON = "csouth", TITLE = "Computer South"),
- list(CONSTRUCTION_MODE = RCD_COMPUTER, COMPUTER_DIR = 4, ICON = "ceast", TITLE = "Computer East"),
- list(CONSTRUCTION_MODE = RCD_COMPUTER, COMPUTER_DIR = 8, ICON = "cwest", TITLE = "Computer West"),
- list(CONSTRUCTION_MODE = RCD_FLOODLIGHT, ICON = "floodlight_c1", TITLE = "FloodLight Frame"),
- list(CONSTRUCTION_MODE = RCD_WALLFRAME, WALLFRAME_TYPE = /obj/item/wallframe/apc, ICON = "apc", TITLE = "APC WallFrame"),
- list(CONSTRUCTION_MODE = RCD_WALLFRAME, WALLFRAME_TYPE = /obj/item/wallframe/airalarm, ICON = "alarm_bitem", TITLE = "AirAlarm WallFrame"),
- list(CONSTRUCTION_MODE = RCD_WALLFRAME, WALLFRAME_TYPE = /obj/item/wallframe/firealarm, ICON = "fire_bitem", TITLE = "FireAlarm WallFrame"),
- ),
-
- //Interior Design[construction_mode = RCD_FURNISHING is implied]
- "Furniture" = list(
- list(FURNISH_TYPE = /obj/structure/chair, FURNISH_COST = 8, FURNISH_DELAY = 10, ICON = "chair", TITLE = "Chair"),
- list(FURNISH_TYPE = /obj/structure/chair/stool, FURNISH_COST = 8, FURNISH_DELAY = 10, ICON = "stool", TITLE = "Stool"),
- list(FURNISH_TYPE = /obj/structure/chair/stool/bar, FURNISH_COST = 4, FURNISH_DELAY = 5, ICON = "bar", TITLE = "Bar Stool"),
- list(FURNISH_TYPE = /obj/structure/table, FURNISH_COST = 16, FURNISH_DELAY = 20, ICON = "table",TITLE = "Table"),
- list(FURNISH_TYPE = /obj/structure/table/glass, FURNISH_COST = 16, FURNISH_DELAY = 20, ICON = "glass_table", TITLE = "Glass Table"),
- list(FURNISH_TYPE = /obj/structure/rack, FURNISH_COST = 20, FURNISH_DELAY = 25, ICON = "rack", TITLE = "Rack"),
- list(FURNISH_TYPE = /obj/structure/bed, FURNISH_COST = 10, FURNISH_DELAY = 15, ICON = "bed", TITLE = "Bed"),
- ),
- ),
-
- //2ND ROOT CATEGORY[construction_mode = RCD_AIRLOCK is implied,"icon=closed"]
- "Airlocks" = list( //used to seal/close areas
- //Window Doors[airlock_glass = TRUE is implied]
- "Windoors" = list(
- list(AIRLOCK_TYPE = /obj/machinery/door/window, ICON = "windoor", TITLE = "Windoor"),
- list(AIRLOCK_TYPE = /obj/machinery/door/window/brigdoor, ICON = "secure_windoor", TITLE = "Secure Windoor"),
- ),
-
- //Glass Airlocks[airlock_glass = TRUE is implied,do fill_closed overlay]
- "Glass Airlocks" = list(
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/glass, TITLE = "Standard", CATEGORY_ICON_STATE = TITLE_ICON, CATEGORY_ICON_SUFFIX = "Glass"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/public/glass, TITLE = "Public"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/engineering/glass, TITLE = "Engineering"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/atmos/glass, TITLE = "Atmospherics"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/security/glass, TITLE = "Security"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/command/glass, TITLE = "Command"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/medical/glass, TITLE = "Medical"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/research/glass, TITLE = "Research"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/virology/glass, TITLE = "Virology"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/mining/glass, TITLE = "Mining"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance/glass, TITLE = "Maintenance"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/external/glass, TITLE = "External"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance/external/glass, TITLE = "External Maintenance"),
- ),
-
- //Solid Airlocks[airlock_glass = FALSE is implied,no fill_closed overlay]
- "Solid Airlocks" = list(
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock, TITLE = "Standard", CATEGORY_ICON_STATE = TITLE_ICON),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/public, TITLE = "Public"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/engineering, TITLE = "Engineering"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/atmos, TITLE = "Atmospherics"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/security, TITLE = "Security"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/command, TITLE = "Command"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/medical, TITLE = "Medical"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/research, TITLE = "Research"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/freezer, TITLE = "Freezer"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/virology, TITLE = "Virology"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/mining, TITLE = "Mining"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance, TITLE = "Maintenance"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/external, TITLE = "External"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance/external, TITLE = "External Maintenance"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/hatch, TITLE = "Airtight Hatch"),
- list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance_hatch, TITLE = "Maintenance Hatch"),
- ),
- ),
-
- //3RD CATEGORY Airlock access,empty list cause airlock_electronics UI will be displayed when this tab is selected
- "Airlock Access" = list()
- )
-
- ///english name for the design to check if it was selected or not
- var/design_title = "Wall/Floor"
- var/design_category = "Structures"
- var/root_category = "Construction"
- var/closed = FALSE
- ///owner of this rcd. It can either be an construction console or an player
- var/owner
- var/mode = RCD_FLOORWALL
- var/construction_mode = RCD_FLOORWALL
- var/ranged = FALSE
- var/computer_dir = 1
- var/airlock_type = /obj/machinery/door/airlock
- var/airlock_glass = FALSE // So the floor's rcd_act knows how much ammo to use
- var/window_type = /obj/structure/window/fulltile
- var/window_glass = RCD_WINDOW_NORMAL
- var/window_size = RCD_WINDOW_FULLTILE
- var/obj/item/wallframe/wallframe_type = /obj/item/wallframe/apc
- var/furnish_type = /obj/structure/chair
- var/furnish_cost = 8
- var/furnish_delay = 10
- var/advanced_airlock_setting = 1 //Set to 1 if you want more paintjobs available
- var/delay_mod = 1
- var/canRturf = FALSE //Variable for R walls to deconstruct them
- /// Integrated airlock electronics for setting access to a newly built airlocks
- var/obj/item/electronics/airlock/airlock_electronics
-
- COOLDOWN_DECLARE(destructive_scan_cooldown)
-
-GLOBAL_VAR_INIT(icon_holographic_wall, init_holographic_wall())
-GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window())
-
-// `initial` does not work here. Neither does instantiating a wall/whatever
-// and referencing that. I don't know why.
-/proc/init_holographic_wall()
- return getHologramIcon(
- icon('icons/turf/walls/wall.dmi', "wall-0"),
- opacity = 1,
- )
-
-/proc/init_holographic_window()
- var/icon/grille_icon = icon('icons/obj/structures.dmi', "grille")
- var/icon/window_icon = icon('icons/obj/smooth_structures/window.dmi', "window-0")
-
- grille_icon.Blend(window_icon, ICON_OVERLAY)
-
- return getHologramIcon(grille_icon)
-
-/obj/item/construction/rcd/ui_action_click(mob/user, actiontype)
- if (!COOLDOWN_FINISHED(src, destructive_scan_cooldown))
- to_chat(user, span_warning("[src] lets out a low buzz."))
- return
-
- COOLDOWN_START(src, destructive_scan_cooldown, RCD_DESTRUCTIVE_SCAN_COOLDOWN)
- rcd_scan(src)
-
-/**
- * Global proc that generates RCD hologram in a range.
- *
- * Arguments:
- * * source - The atom the scans originate from
- * * scan_range - The range of turfs we grab from the source
- * * fade_time - The time for RCD holograms to fade
- */
-/proc/rcd_scan(atom/source, scan_range = RCD_DESTRUCTIVE_SCAN_RANGE, fade_time = RCD_HOLOGRAM_FADE_TIME)
- playsound(source, 'sound/items/rcdscan.ogg', 50, vary = TRUE, pressure_affected = FALSE)
-
- var/turf/source_turf = get_turf(source)
- for(var/turf/open/surrounding_turf in RANGE_TURFS(scan_range, source_turf))
- var/rcd_memory = surrounding_turf.rcd_memory
- if(!rcd_memory)
- continue
-
- var/skip_to_next_turf = FALSE
-
- for(var/atom/content_of_turf as anything in surrounding_turf.contents)
- if (content_of_turf.density)
- skip_to_next_turf = TRUE
- break
-
- if(skip_to_next_turf)
- continue
-
- var/hologram_icon
- switch(rcd_memory)
- if(RCD_MEMORY_WALL)
- hologram_icon = GLOB.icon_holographic_wall
- if(RCD_MEMORY_WINDOWGRILLE)
- hologram_icon = GLOB.icon_holographic_window
-
- var/obj/effect/rcd_hologram/hologram = new(surrounding_turf)
- hologram.icon = hologram_icon
- animate(hologram, alpha = 0, time = fade_time, easing = CIRCULAR_EASING | EASE_IN)
-
-/obj/effect/rcd_hologram
- name = "hologram"
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
-
-/obj/effect/rcd_hologram/Initialize(mapload)
- . = ..()
- QDEL_IN(src, RCD_HOLOGRAM_FADE_TIME)
-
-#undef RCD_DESTRUCTIVE_SCAN_COOLDOWN
-#undef RCD_DESTRUCTIVE_SCAN_RANGE
-#undef RCD_HOLOGRAM_FADE_TIME
-
-/obj/item/construction/rcd/suicide_act(mob/living/user)
- var/turf/T = get_turf(user)
-
- if(!isopenturf(T)) // Oh fuck
- user.visible_message(span_suicide("[user] is beating [user.p_them()]self to death with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
- return BRUTELOSS
-
- mode = RCD_FLOORWALL
- user.visible_message(span_suicide("[user] sets the RCD to 'Wall' and points it down [user.p_their()] throat! It looks like [user.p_theyre()] trying to commit suicide!"))
- if(checkResource(16, user)) // It takes 16 resources to construct a wall
- var/success = T.rcd_act(user, src, RCD_FLOORWALL)
- T = get_turf(user)
- // If the RCD placed a floor instead of a wall, having a wall without plating under it is cursed
- // There isn't an easy programmatical way to check if rcd_act will place a floor or a wall, so just repeat using it for free
- if(success && isopenturf(T))
- T.rcd_act(user, src, RCD_FLOORWALL)
- useResource(16, user)
- activate()
- playsound(loc, 'sound/machines/click.ogg', 50, 1)
- user.gib()
- return MANUAL_SUICIDE
-
- user.visible_message(span_suicide("[user] pulls the trigger... But there is not enough ammo!"))
- return SHAME
-
-/**
- * checks if the turf has dense objects that could block construction of big structures such as walls, airlocks etc
- * Arguments:
- * * turf/the_turf - The turf we are checking
- * * ignore_mobs - should we ignore mobs when checking for dense objects. this is TRUE only for windoors
- * * ignored_atoms - ignore these object types when checking for dense objects on the turf. e.g. ignore directional windows when building windoors cause they all can exist on the same turf
- */
-/obj/item/construction/rcd/proc/is_turf_blocked(turf/the_turf, ignore_mobs, list/ignored_atoms)
- //find the structures to ignore
- var/list/ignored_content = list()
- if(length(ignored_atoms))
- for(var/atom/movable/movable_content in the_turf)
- if(is_type_in_list(movable_content, ignored_atoms))
- ignored_content += movable_content
-
- //return if the turf is blocked
- return the_turf.is_blocked_turf(exclude_mobs = ignore_mobs, source_atom = null, ignore_atoms = ignored_content)
-
-/obj/item/construction/rcd/proc/rcd_create(atom/A, mob/user)
- //does this atom allow for rcd actions?
- var/list/rcd_results = A.rcd_vals(user, src)
- if(!rcd_results)
- return FALSE
- var/turf/target_turf = get_turf(A)
-
- //start animation & check resource for the action
- var/delay = rcd_results["delay"] * delay_mod
- var/obj/effect/constructing_effect/rcd_effect = new(target_turf, delay, src.mode)
- if(!checkResource(rcd_results["cost"], user))
- qdel(rcd_effect)
- return FALSE
- /**
- *For anything that does not go an a wall we have to make sure that turf is clear for us to put the structure on it
- *If we are just trying to destory something then this check is not nessassary
- *RCD_WALLFRAME is also returned as the mode when upgrading apc, airalarm, firealarm using simple circuits upgrade
- */
- if(rcd_results["mode"] != RCD_WALLFRAME && rcd_results["mode"] != RCD_DECONSTRUCT)
- //if we are trying to build a window on top of a grill we check for specific edge cases
- if(rcd_results["mode"] == RCD_WINDOWGRILLE && istype(A, /obj/structure/grille))
- var/list/structures_to_ignore
-
- //if we are trying to build full-tile windows we only ignore the grille but other directional windows on the grill can block its construction
- if(window_type == /obj/structure/window/fulltile || window_type == /obj/structure/window/reinforced/fulltile)
- structures_to_ignore = list(/obj/structure/grille)
- //for normal directional windows we ignore the grille & other directional windows as they can be in diffrent directions on the grill. There is a later check during construction to deal with those
- else
- structures_to_ignore = list(/obj/structure/grille, /obj/structure/window)
-
- //check if we can build our window on the grill
- if(is_turf_blocked(the_turf = target_turf, ignore_mobs = FALSE, ignored_atoms = structures_to_ignore))
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
- balloon_alert(user, "something is on the grille!")
- qdel(rcd_effect)
- return FALSE
-
- /**
- * if we are trying to create plating on turf which is not a proper floor then dont check for objects on top of the turf just allow that turf to be converted into plating. e.g. create plating beneath a player or underneath a machine frame/any dense object
- * if we are trying to finish a wall girder then let it finish then make sure no one/nothing is stuck in the girder
- */
- else if(rcd_results["mode"] == RCD_FLOORWALL && (!istype(target_turf, /turf/open/floor) || istype(A, /obj/structure/girder)))
- //if a player builds a wallgirder on top of himself manually with iron sheets he can't finish the wall if he is still on the girder. Exclude the girder itself when checking for other dense objects on the turf
- if(istype(A, /obj/structure/girder) && is_turf_blocked(the_turf = target_turf, ignore_mobs = FALSE, ignored_atoms = list(/obj/structure/girder)))
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
- balloon_alert(user, "something is on the girder!")
- qdel(rcd_effect)
- return FALSE
-
- //check if turf is blocked in for dense structures
- else
- //structures which are small enough to fit on turfs containing directional windows.
- var/static/list/small_structures = list(
- RCD_AIRLOCK,
- RCD_COMPUTER,
- RCD_FLOODLIGHT,
- RCD_FURNISHING,
- RCD_MACHINE,
- RCD_REFLECTOR,
- RCD_WINDOWGRILLE,
- )
-
- //edge cases for what we can/can't ignore
- var/exclude_mobs = FALSE
- var/list/ignored_types
- if(rcd_results["mode"] in small_structures)
- ignored_types = list(/obj/structure/window)
- //if we are trying to create grills/windoors we can go ahead and further ignore other windoors on the turf
- if(rcd_results["mode"] == RCD_WINDOWGRILLE || (rcd_results["mode"] == RCD_AIRLOCK && ispath(airlock_type, /obj/machinery/door/window)))
- //only ignore mobs if we are trying to create windoors and not grills. We dont want to drop a grill on top of somebody
- exclude_mobs = rcd_results["mode"] == RCD_AIRLOCK
- ignored_types += /obj/machinery/door/window
- //if we are trying to create full airlock doors then we do the regular checks and make sure we have the full space for them. i.e. dont ignore anything dense on the turf
- else if(rcd_results["mode"] == RCD_AIRLOCK)
- ignored_types = list()
-
- //check if the structure can fit on this turf
- if(is_turf_blocked(the_turf = target_turf, ignore_mobs = exclude_mobs, ignored_atoms = ignored_types))
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
- balloon_alert(user, "something is on the tile!")
- qdel(rcd_effect)
- return FALSE
- //beam animation for arcd
- var/beam
- if(ranged)
- beam = user.Beam(A,icon_state="rped_upgrade", time = delay)
- if(!do_after(user, delay, target = A))
- qdel(rcd_effect)
- if(!isnull(beam))
- qdel(beam)
- return FALSE
- if(!checkResource(rcd_results["cost"], user))
- qdel(rcd_effect)
- if(!isnull(beam))
- qdel(beam)
- return FALSE
- if(!A.rcd_act(user, src, rcd_results["mode"]))
- qdel(rcd_effect)
- if(!isnull(beam))
- qdel(beam)
- return FALSE
- rcd_effect.end_animation()
- useResource(rcd_results["cost"], user)
- activate()
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
- return TRUE
-
-/obj/item/construction/rcd/Initialize(mapload)
- . = ..()
- airlock_electronics = new(src)
- airlock_electronics.name = "Access Control"
- airlock_electronics.holder = src
- GLOB.rcd_list += src
-
-/obj/item/construction/rcd/Destroy()
- QDEL_NULL(airlock_electronics)
- GLOB.rcd_list -= src
- . = ..()
-
-/obj/item/construction/rcd/ui_assets(mob/user)
- return list(
- get_asset_datum(/datum/asset/spritesheet/rcd),
- )
-
-/obj/item/construction/rcd/ui_host(mob/user)
- return owner || ..()
-
-/obj/item/construction/rcd/ui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "RapidConstructionDevice", name)
- ui.open()
-
-/obj/item/construction/rcd/ui_static_data(mob/user)
- return airlock_electronics.ui_static_data(user)
-
-/obj/item/construction/rcd/ui_data(mob/user)
- var/list/data = ..()
-
- //main categories
- data["selected_root"] = root_category
- data["root_categories"] = list()
- for(var/category in root_categories)
- data["root_categories"] += category
-
- //create the category list
- data["selected_category"] = design_category
- data["selected_design"] = design_title
- data["categories"] = list()
-
- var/category_icon_state
- var/category_icon_suffix
- for(var/sub_category as anything in root_categories[root_category])
- var/list/target_category = root_categories[root_category][sub_category]
- if(target_category.len == 0)
- continue
-
- //skip category if upgrades were not installed for these
- if(sub_category == "Machines" && !(upgrade & RCD_UPGRADE_FRAMES))
- continue
- if(sub_category == "Furniture" && !(upgrade & RCD_UPGRADE_FURNISHING))
- continue
- category_icon_state = ""
- category_icon_suffix = ""
-
- var/list/designs = list() //initialize all designs under this category
- for(var/i in 1 to target_category.len)
- var/list/design = target_category[i]
-
- //check for special icon flags
- if(design[CATEGORY_ICON_STATE] != null)
- category_icon_state = design[CATEGORY_ICON_STATE]
- if(design[CATEGORY_ICON_SUFFIX] != null)
- category_icon_suffix = design[CATEGORY_ICON_SUFFIX]
-
- //get icon or create it from pre defined flags
- var/icon_state
- if(design[ICON] != null)
- icon_state = design[ICON]
- else
- icon_state = category_icon_state
- if(icon_state == TITLE_ICON)
- icon_state = design[TITLE]
- icon_state = "[icon_state][category_icon_suffix]"
-
- //sanitize them so you dont go insane when icon names contain spaces in them
- icon_state = sanitize_css_class_name(icon_state)
-
- designs += list(list(TITLE = design[TITLE], ICON = icon_state))
- data["categories"] += list(list("cat_name" = sub_category, "designs" = designs))
-
- //merge airlock_electronics ui data with this
- var/list/airlock_data = airlock_electronics.ui_data(user)
- for(var/key in airlock_data)
- data[key] = airlock_data[key]
-
- return data
-
-/obj/item/construction/rcd/ui_act(action, params)
- . = ..()
- if(.)
- return
-
- switch(action)
- if("root_category")
- var/new_root = params["root_category"]
- if(root_categories[new_root] != null) //is a valid category
- root_category = new_root
-
- if("design")
- var/category_name = params["category"]
- var/index = params["index"]
-
- var/list/root = root_categories[root_category]
- if(root == null) //not a valid root
- return TRUE
- var/list/category = root[category_name]
- if(category == null) //not a valid category
- return TRUE
-
- /**
- * The advantage of organizing designs into categories is that
- * You can ignore an complete category if the design disk upgrade for that category isn't installed.
- */
- //You can't select designs from the Machines category if you dont have the frames upgrade installed.
- if(category == "Machines" && !(upgrade & RCD_UPGRADE_FRAMES))
- return TRUE
- //You can't select designs from the Furniture category if you dont have the furnishing upgrade installed.
- if(category == "Furniture" && !(upgrade & RCD_UPGRADE_FURNISHING))
- return TRUE
-
- var/list/design = category[index]
- if(design == null) //not a valid design
- return TRUE
-
- design_category = category_name
- design_title = design["title"]
-
- if(category_name == "Structures")
- construction_mode = design[CONSTRUCTION_MODE]
- if(design[WINDOW_TYPE] != null)
- window_type = design[WINDOW_TYPE]
- if(design[WINDOW_GLASS] != null)
- window_glass = design[WINDOW_GLASS]
- if(design[WINDOW_SIZE] != null)
- window_size = design[WINDOW_SIZE]
- else if(category_name == "Machines")
- construction_mode = design[CONSTRUCTION_MODE]
- if(design[COMPUTER_DIR] != null)
- computer_dir = design[COMPUTER_DIR]
- if(design[WALLFRAME_TYPE] != null)
- wallframe_type = design[WALLFRAME_TYPE]
- else if(category_name == "Furniture")
- construction_mode = RCD_FURNISHING
- furnish_type = design[FURNISH_TYPE]
- furnish_cost = design[FURNISH_COST]
- furnish_delay = design[FURNISH_DELAY]
-
- if(root_category == "Airlocks")
- construction_mode = RCD_AIRLOCK
- airlock_glass = (category_name != "Solid Airlocks")
- airlock_type = design[AIRLOCK_TYPE]
-
- else
- airlock_electronics.do_action(action, params)
-
- return TRUE
-
-/obj/item/construction/rcd/attack_self(mob/user)
- . = ..()
- ui_interact(user)
-
-/obj/item/construction/rcd/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
- . = ..()
- //proximity check for normal rcd & range check for arcd
- if((!proximity_flag && !ranged) || (ranged && !range_check(target, user)))
- return FALSE
-
- //do the work
- mode = construction_mode
- rcd_create(target, user)
-
- return . | AFTERATTACK_PROCESSED_ITEM
-
-/obj/item/construction/rcd/afterattack_secondary(atom/target, mob/user, proximity_flag, click_parameters)
- . = ..()
- //proximity check for normal rcd & range check for arcd
- if((!proximity_flag && !ranged) || (ranged && !range_check(target, user)))
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-
- //do the work
- mode = RCD_DECONSTRUCT
- rcd_create(target, user)
-
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-
-/obj/item/construction/rcd/proc/detonate_pulse()
- audible_message("[src] begins to vibrate and \
- buzz loudly!","[src] begins \
- vibrating violently!")
- // 5 seconds to get rid of it
- addtimer(CALLBACK(src, PROC_REF(detonate_pulse_explode)), 50)
-
-/obj/item/construction/rcd/proc/detonate_pulse_explode()
- explosion(src, light_impact_range = 3, flame_range = 1, flash_range = 1)
- qdel(src)
-
-/obj/item/construction/rcd/Initialize(mapload)
- . = ..()
- update_appearance()
-
-/obj/item/construction/rcd/borg
- no_ammo_message = "Insufficient charge."
- desc = "A device used to rapidly build walls and floors."
- banned_upgrades = RCD_UPGRADE_SILO_LINK
- var/energyfactor = 72
-
-/obj/item/construction/rcd/borg/get_matter(mob/user)
- if(!iscyborg(user))
- return 0
- var/mob/living/silicon/robot/borgy = user
- if(!borgy.cell)
- return 0
- max_matter = borgy.cell.maxcharge
- return borgy.cell.charge
-
-/obj/item/construction/rcd/borg/useResource(amount, mob/user)
- if(!iscyborg(user))
- return 0
- var/mob/living/silicon/robot/borgy = user
- if(!borgy.cell)
- if(user)
- to_chat(user, no_ammo_message)
- return 0
- . = borgy.cell.use(amount * energyfactor) //borgs get 1.3x the use of their RCDs
- if(!. && user)
- to_chat(user, no_ammo_message)
- return .
-
-/obj/item/construction/rcd/borg/checkResource(amount, mob/user)
- if(!iscyborg(user))
- return 0
- var/mob/living/silicon/robot/borgy = user
- if(!borgy.cell)
- if(user)
- to_chat(user, no_ammo_message)
- return 0
- . = borgy.cell.charge >= (amount * energyfactor)
- if(!. && user)
- to_chat(user, no_ammo_message)
- return .
-
-/obj/item/construction/rcd/borg/syndicate
- name = "syndicate RCD"
- desc = "A reverse-engineered RCD with black market upgrades that allow this device to deconstruct reinforced walls. Property of Donk Co."
- icon_state = "ircd"
- inhand_icon_state = "ircd"
- energyfactor = 66
- canRturf = TRUE
-
-/obj/item/construction/rcd/loaded
- matter = 160
-
-/obj/item/construction/rcd/loaded/upgraded
- upgrade = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
-
-/obj/item/construction/rcd/combat
- name = "industrial RCD"
- icon_state = "ircd"
- inhand_icon_state = "ircd"
- max_matter = 500
- matter = 500
- canRturf = TRUE
- upgrade = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
-
-#undef CONSTRUCTION_MODE
-#undef WINDOW_TYPE
-#undef WINDOW_GLASS
-#undef WINDOW_SIZE
-#undef COMPUTER_DIR
-#undef WALLFRAME_TYPE
-#undef FURNISH_TYPE
-#undef FURNISH_COST
-#undef FURNISH_DELAY
-#undef AIRLOCK_TYPE
-
-#undef TITLE
-#undef ICON
-
-#undef CATEGORY_ICON_STATE
-#undef CATEGORY_ICON_SUFFIX
-#undef TITLE_ICON
-
-/obj/item/rcd_ammo
- name = "RCD matter cartridge"
- desc = "Highly compressed matter for the RCD."
- icon = 'icons/obj/tools.dmi'
- icon_state = "rcdammo"
- w_class = WEIGHT_CLASS_TINY
- lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
- custom_materials = list(/datum/material/iron=12000, /datum/material/glass=8000)
- var/ammoamt = 40
-
-/obj/item/rcd_ammo/large
- custom_materials = list(/datum/material/iron=48000, /datum/material/glass=32000)
- ammoamt = 160
-
-/obj/item/construction/rcd/combat/admin
- name = "admin RCD"
- max_matter = INFINITY
- matter = INFINITY
- upgrade = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
-
-
-// Ranged RCD
-/obj/item/construction/rcd/arcd
- name = "advanced rapid-construction-device (ARCD)"
- desc = "A prototype RCD with ranged capability and infinite capacity."
- max_matter = INFINITY
- matter = INFINITY
- delay_mod = 0.6
- ranged = TRUE
- icon_state = "arcd"
- inhand_icon_state = "oldrcd"
- has_ammobar = FALSE
- upgrade = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
-
-// RAPID LIGHTING DEVICE
-/obj/item/construction/rld
- name = "Rapid Lighting Device (RLD)"
- desc = "A device used to rapidly provide lighting sources to an area. Reload with iron, plasteel, glass or compressed matter cartridges."
- icon = 'icons/obj/tools.dmi'
- icon_state = "rld-5"
- worn_icon_state = "RPD"
- lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
- matter = 200
- max_matter = 200
- slot_flags = ITEM_SLOT_BELT
- ///it does not make sense why any of these should be installed
- banned_upgrades = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
-
- var/matter_divisor = 35
- var/mode = LIGHT_MODE
- var/wallcost = 10
- var/floorcost = 15
- var/launchcost = 5
- var/deconcost = 10
-
- var/walldelay = 10
- var/floordelay = 10
- var/decondelay = 15
-
- ///reference to thr original icons
- var/list/original_options = list(
- "Color Pick" = icon(icon = 'icons/hud/radial.dmi', icon_state = "omni"),
- "Glow Stick" = icon(icon = 'icons/obj/lighting.dmi', icon_state = "glowstick"),
- "Deconstruct" = icon(icon = 'icons/obj/tools.dmi', icon_state = "wrench"),
- "Light Fixture" = icon(icon = 'icons/obj/lighting.dmi', icon_state = "ltube"),
- )
- ///will contain the original icons modified with the color choice
- var/list/display_options = list()
- var/color_choice = "#ffffff"
-
-/obj/item/construction/rld/Initialize(mapload)
- . = ..()
- for(var/option in original_options)
- display_options[option] = icon(original_options[option])
-
-/obj/item/construction/rld/update_icon_state()
- icon_state = "rld-[round(matter/matter_divisor)]"
- return ..()
-
-/obj/item/construction/rld/attack_self(mob/user)
- . = ..()
-
- if((upgrade & RCD_UPGRADE_SILO_LINK) && display_options["Silo Link"] == null) //silo upgrade instaled but option was not updated then update it just one
- display_options["Silo Link"] = icon(icon = 'icons/obj/mining.dmi', icon_state = "silo")
-
- var/choice = show_radial_menu(user, src, display_options, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE, tooltips = TRUE)
- if(!check_menu(user))
- return
- if(!choice)
- return
-
- switch(choice)
- if("Light Fixture")
- mode = LIGHT_MODE
- to_chat(user, span_notice("You change RLD's mode to 'Permanent Light Construction'."))
- if("Glow Stick")
- mode = GLOW_MODE
- to_chat(user, span_notice("You change RLD's mode to 'Light Launcher'."))
- if("Color Pick")
- var/new_choice = input(user,"","Choose Color",color_choice) as color
- if(new_choice == null)
- return
-
- var/list/new_rgb = ReadRGB(new_choice)
- for(var/option in original_options)
- if(option == "Color Pick" || option == "Deconstruct" || option == "Silo Link")
- continue
- var/icon/icon = icon(original_options[option])
- icon.SetIntensity(new_rgb[1]/255, new_rgb[2]/255, new_rgb[3]/255) //apply new scale
- display_options[option] = icon
-
- color_choice = new_choice
- if("Deconstruct")
- mode = REMOVE_MODE
- to_chat(user, span_notice("You change RLD's mode to 'Deconstruct'."))
- else
- ui_act("toggle_silo", list())
-
-/obj/item/construction/rld/proc/checkdupes(target)
- . = list()
- var/turf/checking = get_turf(target)
- for(var/obj/machinery/light/dupe in checking)
- if(istype(dupe, /obj/machinery/light))
- . |= dupe
-
-/obj/item/construction/rld/afterattack(atom/A, mob/user)
- . = ..()
- if(!range_check(A,user))
- return
- var/turf/start = get_turf(src)
- switch(mode)
- if(REMOVE_MODE)
- if(istype(A, /obj/machinery/light/))
- if(checkResource(deconcost, user))
- to_chat(user, span_notice("You start deconstructing [A]..."))
- user.Beam(A,icon_state="light_beam", time = 15)
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
- if(do_after(user, decondelay, target = A))
- if(!useResource(deconcost, user))
- return FALSE
- activate()
- qdel(A)
- return TRUE
- return FALSE
- if(LIGHT_MODE)
- if(iswallturf(A))
- var/turf/closed/wall/W = A
- if(checkResource(floorcost, user))
- to_chat(user, span_notice("You start building a wall light..."))
- user.Beam(A,icon_state="light_beam", time = 15)
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
- playsound(loc, 'sound/effects/light_flicker.ogg', 50, FALSE)
- if(do_after(user, floordelay, target = A))
- if(!istype(W))
- return FALSE
- var/list/candidates = list()
- var/turf/open/winner = null
- var/winning_dist = null
- for(var/direction in GLOB.cardinals)
- var/turf/C = get_step(W, direction)
- var/list/dupes = checkdupes(C)
- if((isspaceturf(C) || TURF_SHARES(C)) && !dupes.len)
- candidates += C
- if(!candidates.len)
- to_chat(user, span_warning("Valid target not found..."))
- playsound(loc, 'sound/misc/compiler-failure.ogg', 30, TRUE)
- return FALSE
- for(var/turf/open/O in candidates)
- if(istype(O))
- var/x0 = O.x
- var/y0 = O.y
- var/contender = CHEAP_HYPOTENUSE(start.x, start.y, x0, y0)
- if(!winner)
- winner = O
- winning_dist = contender
- else
- if(contender < winning_dist) // lower is better
- winner = O
- winning_dist = contender
- activate()
- if(!useResource(wallcost, user))
- return FALSE
- var/light = get_turf(winner)
- var/align = get_dir(winner, A)
- var/obj/machinery/light/L = new /obj/machinery/light(light)
- L.setDir(align)
- L.color = color_choice
- L.set_light_color(color_choice)
- return TRUE
- return FALSE
-
- if(isfloorturf(A))
- var/turf/open/floor/F = A
- if(checkResource(floorcost, user))
- to_chat(user, span_notice("You start building a floor light..."))
- user.Beam(A,icon_state="light_beam", time = 15)
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
- playsound(loc, 'sound/effects/light_flicker.ogg', 50, TRUE)
- if(do_after(user, floordelay, target = A))
- if(!istype(F))
- return FALSE
- if(!useResource(floorcost, user))
- return FALSE
- activate()
- var/destination = get_turf(A)
- var/obj/machinery/light/floor/FL = new /obj/machinery/light/floor(destination)
- FL.color = color_choice
- FL.set_light_color(color_choice)
- return TRUE
- return FALSE
-
- if(GLOW_MODE)
- if(useResource(launchcost, user))
- activate()
- to_chat(user, span_notice("You fire a glowstick!"))
- var/obj/item/flashlight/glowstick/G = new /obj/item/flashlight/glowstick(start)
- G.color = color_choice
- G.set_light_color(G.color)
- G.throw_at(A, 9, 3, user)
- G.on = TRUE
- G.update_brightness()
- return TRUE
- return FALSE
-
-/obj/item/construction/rld/mini
- name = "mini-rapid-light-device (MRLD)"
- desc = "A device used to rapidly provide lighting sources to an area. Reload with iron, plasteel, glass or compressed matter cartridges."
- icon = 'icons/obj/tools.dmi'
- icon_state = "rld-5"
- lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
- matter_divisor = 20
- matter = 100
- max_matter = 100
-
-///The plumbing RCD. All the blueprints are located in _globalvars > lists > construction.dm
-/obj/item/construction/plumbing
- name = "Plumbing Constructor"
- desc = "An expertly modified RCD outfitted to construct plumbing machinery."
- icon_state = "plumberer2"
- inhand_icon_state = "plumberer"
- lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
- worn_icon_state = "plumbing"
- icon = 'icons/obj/tools.dmi'
- slot_flags = ITEM_SLOT_BELT
- ///it does not make sense why any of these should be installed.
- banned_upgrades = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
- matter = 200
- max_matter = 200
-
- ///type of the plumbing machine
- var/obj/machinery/blueprint = null
- ///index, used in the attack self to get the type. stored here since it doesnt change
- var/list/choices = list()
- ///All info for construction
- var/list/machinery_data = list("cost" = list())
- ///This list that holds all the plumbing design types the plumberer can construct. Its purpose is to make it easy to make new plumberer subtypes with a different selection of machines.
- var/list/plumbing_design_types
- ///Current selected layer
- var/current_layer = "Default Layer"
- ///Current selected color, for ducts
- var/current_color = "omni"
- ///maps layer name to layer number value. didnt make this global cause only this class needs it
- var/static/list/name_to_number = list(
- "First Layer" = 1,
- "Second Layer" = 2,
- "Default Layer" = 3,
- "Fourth Layer" = 4,
- "Fifth Layer" = 5,
- )
-
-/obj/item/construction/plumbing/Initialize(mapload)
- . = ..()
-
- //design types supported for this plumbing rcd
- set_plumbing_designs()
-
- //set cost of each machine & initial blueprint
- for(var/obj/machinery/plumbing/plumbing_type as anything in plumbing_design_types)
- machinery_data["cost"][plumbing_type] = plumbing_design_types[plumbing_type]
- blueprint = plumbing_design_types[1]
-
-/obj/item/construction/plumbing/proc/set_plumbing_designs()
- plumbing_design_types = list(
- //category 1 Synthesizers i.e devices which creates , reacts & destroys chemicals
- /obj/machinery/plumbing/synthesizer = 15,
- /obj/machinery/plumbing/reaction_chamber/chem = 15,
- /obj/machinery/plumbing/grinder_chemical = 30,
- /obj/machinery/plumbing/growing_vat = 20,
- /obj/machinery/plumbing/fermenter = 30,
- /obj/machinery/plumbing/liquid_pump = 35, //extracting chemicals from ground is one way of creation
- /obj/machinery/plumbing/disposer = 10,
- /obj/machinery/plumbing/buffer = 10, //creates chemicals as it waits for other buffers containing other chemicals and when mixed creates new chemicals
-
- //category 2 distributors i.e devices which inject , move around , remove chemicals from the network
- /obj/machinery/duct = 1,
- /obj/machinery/plumbing/layer_manifold = 5,
- /obj/machinery/plumbing/input = 5,
- /obj/machinery/plumbing/filter = 5,
- /obj/machinery/plumbing/splitter = 5,
- /obj/machinery/plumbing/sender = 20,
- /obj/machinery/plumbing/output = 5,
-
- //category 3 Storage i.e devices which stores & makes the processed chemicals ready for consumption
- /obj/machinery/plumbing/tank = 20,
- /obj/machinery/plumbing/acclimator = 10,
- /obj/machinery/plumbing/bottler = 50,
- /obj/machinery/plumbing/pill_press = 20,
- /obj/machinery/iv_drip/plumbing = 20
- )
-
-/obj/item/construction/plumbing/equipped(mob/user, slot, initial)
- . = ..()
- if(slot & ITEM_SLOT_HANDS)
- RegisterSignal(user, COMSIG_MOUSE_SCROLL_ON, PROC_REF(mouse_wheeled))
- else
- UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
-
-/obj/item/construction/plumbing/dropped(mob/user, silent)
- UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
- return ..()
-
-/obj/item/construction/plumbing/cyborg_unequip(mob/user)
- UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
- return ..()
-
-/obj/item/construction/plumbing/attack_self(mob/user)
- . = ..()
- ui_interact(user)
-
-/obj/item/construction/plumbing/examine(mob/user)
- . = ..()
- . += "You can scroll your mouse wheel to change the piping layer."
- . += "You can right click a fluid duct to set the Plumbing RPD to its color and layer."
-
-/obj/item/construction/plumbing/ui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "PlumbingService", name)
- ui.open()
-
-/obj/item/construction/plumbing/ui_assets(mob/user)
- return list(
- get_asset_datum(/datum/asset/spritesheet/plumbing),
- )
-
-/obj/item/construction/plumbing/ui_static_data(mob/user)
- return list("paint_colors" = GLOB.pipe_paint_colors)
-
-///find which category this design belongs to
-/obj/item/construction/plumbing/proc/get_category(obj/machinery/recipe)
- if(ispath(recipe, /obj/machinery/plumbing))
- var/obj/machinery/plumbing/plumbing_design = recipe
- return initial(plumbing_design.category)
- else if(ispath(recipe , /obj/machinery/duct))
- return "Distribution"
- else
- return "Storage"
-
-/obj/item/construction/plumbing/ui_data(mob/user)
- var/list/data = ..()
-
- data["piping_layer"] = name_to_number[current_layer] //maps layer name to layer number's 1,2,3,4,5
- data["selected_color"] = current_color
- data["layer_icon"] = "plumbing_layer[GLOB.plumbing_layers[current_layer]]"
- data["selected_category"] = get_category(blueprint)
- data["selected_recipe"] = initial(blueprint.name)
-
- var/list/category_list = list()
- var/category_name = ""
- var/obj/machinery/recipe = null
-
- for(var/i in 1 to plumbing_design_types.len)
- recipe = plumbing_design_types[i]
-
- category_name = get_category(recipe) //get category of design
- if(!category_list[category_name])
- var/list/item_list = list()
- item_list["cat_name"] = category_name //used by RapidPipeDispenser.js
- item_list["recipes"] = list() //used by RapidPipeDispenser.js
- category_list[category_name] = item_list
-
- //add item to category
- category_list[category_name]["recipes"] += list(list(
- "index" = i,
- "icon" = initial(recipe.icon_state),
- "name" = initial(recipe.name),
- ))
-
- data["categories"] = list()
- for(category_name in category_list)
- data["categories"] += list(category_list[category_name])
-
- return data
-
-/obj/item/construction/plumbing/ui_act(action, params)
- . = ..()
- if(.)
- return
-
- switch(action)
- if("color")
- var/color = params["paint_color"]
- if(GLOB.pipe_paint_colors[color] != null) //validate if the color is in the allowed list of values
- current_color = color
- if("piping_layer")
- var/bitflag = text2num(params["piping_layer"]) //convert from layer number back to layer string
- bitflag = 1 << (bitflag - 1)
- var/layer = GLOB.plumbing_layer_names["[bitflag]"]
- if(layer != null) //validate if this value exists in the list
- current_layer = layer
- if("recipe")
- var/design = plumbing_design_types[text2num(params["id"])]
- if(design != null) //validate if design is valid
- blueprint = design
- playsound(src, 'sound/effects/pop.ogg', 50, vary = FALSE)
-
- return TRUE
-
-
-///pretty much rcd_create, but named differently to make myself feel less bad for copypasting from a sibling-type
-/obj/item/construction/plumbing/proc/create_machine(atom/destination, mob/user)
- if(!machinery_data || !isopenturf(destination))
- return FALSE
-
- if(!canPlace(destination))
- to_chat(user, span_notice("There is something blocking you from placing a [initial(blueprint.name)] there."))
- return
- if(checkResource(machinery_data["cost"][blueprint], user) && blueprint)
- //"cost" is relative to delay at a rate of 10 matter/second (1matter/decisecond) rather than playing with 2 different variables since everyone set it to this rate anyways.
- if(do_after(user, machinery_data["cost"][blueprint], target = destination))
- if(checkResource(machinery_data["cost"][blueprint], user) && canPlace(destination))
- useResource(machinery_data["cost"][blueprint], user)
- activate()
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
- if(ispath(blueprint, /obj/machinery/duct))
- var/is_omni = current_color == DUCT_COLOR_OMNI
- new blueprint(destination, FALSE, GLOB.pipe_paint_colors[current_color], GLOB.plumbing_layers[current_layer], null, is_omni)
- else
- new blueprint(destination, FALSE, GLOB.plumbing_layers[current_layer])
- return TRUE
-
-/obj/item/construction/plumbing/proc/canPlace(turf/destination)
- if(!isopenturf(destination))
- return FALSE
- . = TRUE
-
- var/layer_id = GLOB.plumbing_layers[current_layer]
-
- for(var/obj/content_obj in destination.contents)
- // Let's not built ontop of dense stuff, if this is also dense.
- if(initial(blueprint.density) && content_obj.density)
- return FALSE
-
- // Ducts can overlap other plumbing objects IF the layers are different
-
- // make sure plumbling isn't overlapping.
- for(var/datum/component/plumbing/plumber as anything in content_obj.GetComponents(/datum/component/plumbing))
- if(plumber.ducting_layer & layer_id)
- return FALSE
-
- if(istype(content_obj, /obj/machinery/duct))
- // Make sure ducts aren't overlapping.
- var/obj/machinery/duct/duct_machine = content_obj
- if(duct_machine.duct_layer & layer_id)
- return FALSE
-
-/obj/item/construction/plumbing/pre_attack_secondary(obj/machinery/target, mob/user, params)
- if(!istype(target, /obj/machinery/duct))
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-
- var/obj/machinery/duct/duct = target
- if(duct.duct_layer && duct.duct_color)
- current_color = GLOB.pipe_color_name[duct.duct_color]
- current_layer = GLOB.plumbing_layer_names["[duct.duct_layer]"]
- balloon_alert(user, "using [current_color], layer [current_layer]")
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-
-/obj/item/construction/plumbing/afterattack(atom/target, mob/user, proximity)
- . = ..()
- if(!proximity)
- return
- if(istype(target, /obj/machinery/plumbing))
- var/obj/machinery/machine_target = target
- if(machine_target.anchored)
- to_chat(user, span_warning("The [target.name] needs to be unanchored!"))
- return
- if(do_after(user, 20, target = target))
- machine_target.deconstruct() //Let's not substract matter
- playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) //this is just such a great sound effect
- else
- create_machine(target, user)
-
-/obj/item/construction/plumbing/AltClick(mob/user)
- ui_interact(user)
-
-/obj/item/construction/plumbing/proc/mouse_wheeled(mob/source, atom/A, delta_x, delta_y, params)
- SIGNAL_HANDLER
- if(source.incapacitated(IGNORE_RESTRAINTS|IGNORE_STASIS))
- return
- if(delta_y == 0)
- return
-
- if(delta_y < 0)
- var/current_loc = GLOB.plumbing_layers.Find(current_layer) + 1
- if(current_loc > GLOB.plumbing_layers.len)
- current_loc = 1
- current_layer = GLOB.plumbing_layers[current_loc]
- else
- var/current_loc = GLOB.plumbing_layers.Find(current_layer) - 1
- if(current_loc < 1)
- current_loc = GLOB.plumbing_layers.len
- current_layer = GLOB.plumbing_layers[current_loc]
- to_chat(source, span_notice("You set the layer to [current_layer]."))
-
-/obj/item/construction/plumbing/research
- name = "research plumbing constructor"
- desc = "A type of plumbing constructor designed to rapidly deploy the machines needed to conduct cytological research."
- icon_state = "plumberer_sci"
- inhand_icon_state = "plumberer_sci"
- lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
- has_ammobar = TRUE
-
-/obj/item/construction/plumbing/research/set_plumbing_designs()
- plumbing_design_types = list(
- //category 1 synthesizers
- /obj/machinery/plumbing/reaction_chamber = 15,
- /obj/machinery/plumbing/grinder_chemical = 30,
- /obj/machinery/plumbing/disposer = 10,
- /obj/machinery/plumbing/growing_vat = 20,
-
- //category 2 Distributors
- /obj/machinery/duct = 1,
- /obj/machinery/plumbing/input = 5,
- /obj/machinery/plumbing/filter = 5,
- /obj/machinery/plumbing/splitter = 5,
- /obj/machinery/plumbing/output = 5,
-
- //category 3 storage
- /obj/machinery/plumbing/tank = 20,
- /obj/machinery/plumbing/acclimator = 10,
- )
-
-/obj/item/construction/plumbing/service
- name = "service plumbing constructor"
- desc = "A type of plumbing constructor designed to rapidly deploy the machines needed to make a brewery."
- icon_state = "plumberer_service"
- has_ammobar = TRUE
-
-/obj/item/construction/plumbing/service/set_plumbing_designs()
- plumbing_design_types = list(
- //category 1 synthesizers
- /obj/machinery/plumbing/synthesizer/soda = 15,
- /obj/machinery/plumbing/synthesizer/beer = 15,
- /obj/machinery/plumbing/reaction_chamber = 15,
- /obj/machinery/plumbing/buffer = 10,
- /obj/machinery/plumbing/fermenter = 30,
- /obj/machinery/plumbing/grinder_chemical = 30,
- /obj/machinery/plumbing/disposer = 10,
-
-
- //category 2 distributors
- /obj/machinery/duct = 1,
- /obj/machinery/plumbing/layer_manifold = 5,
- /obj/machinery/plumbing/input = 5,
- /obj/machinery/plumbing/filter = 5,
- /obj/machinery/plumbing/splitter = 5,
- /obj/machinery/plumbing/output/tap = 5,
- /obj/machinery/plumbing/sender = 20,
-
- //category 3 storage
- /obj/machinery/plumbing/bottler = 50,
- /obj/machinery/plumbing/tank = 20,
- /obj/machinery/plumbing/acclimator = 10,
- )
-
-
-/obj/item/rcd_upgrade
- name = "RCD advanced design disk"
- desc = "It seems to be empty."
- icon = 'icons/obj/module.dmi'
- icon_state = "datadisk3"
- var/upgrade
-
-/obj/item/rcd_upgrade/frames
- desc = "It contains the design for machine frames and computer frames."
- upgrade = RCD_UPGRADE_FRAMES
-
-/obj/item/rcd_upgrade/simple_circuits
- desc = "It contains the design for firelock, air alarm, fire alarm, apc circuits and crap power cells."
- upgrade = RCD_UPGRADE_SIMPLE_CIRCUITS
-
-/obj/item/rcd_upgrade/silo_link
- desc = "It contains direct silo connection RCD upgrade."
- upgrade = RCD_UPGRADE_SILO_LINK
-
-/obj/item/rcd_upgrade/furnishing
- desc = "It contains the design for chairs, stools, tables, and glass tables."
- upgrade = RCD_UPGRADE_FURNISHING
-
-/datum/action/item_action/rcd_scan
- name = "Destruction Scan"
- desc = "Scans the surrounding area for destruction. Scanned structures will rebuild significantly faster."
-
-#undef GLOW_MODE
-#undef LIGHT_MODE
-#undef REMOVE_MODE
diff --git a/code/game/objects/items/rcd/RCD.dm b/code/game/objects/items/rcd/RCD.dm
new file mode 100644
index 00000000000..7f336cf6b97
--- /dev/null
+++ b/code/game/objects/items/rcd/RCD.dm
@@ -0,0 +1,676 @@
+#define RCD_DESTRUCTIVE_SCAN_RANGE 10
+#define RCD_HOLOGRAM_FADE_TIME (15 SECONDS)
+#define RCD_DESTRUCTIVE_SCAN_COOLDOWN (RCD_HOLOGRAM_FADE_TIME + 1 SECONDS)
+
+///each define maps to a variable used for construction in the RCD
+#define CONSTRUCTION_MODE "construction_mode"
+#define WINDOW_TYPE "window_type"
+#define COMPUTER_DIR "computer_dir"
+#define WALLFRAME_TYPE "wallframe_type"
+#define FURNISH_TYPE "furnish_type"
+#define AIRLOCK_TYPE "airlock_type"
+
+///flags to be sent to UI
+#define TITLE "title"
+#define ICON "icon"
+
+///flags for creating icons shared by an entire category
+#define CATEGORY_ICON_STATE "category_icon_state"
+#define CATEGORY_ICON_SUFFIX "category_icon_suffix"
+#define TITLE_ICON "ICON=TITLE"
+
+//RAPID CONSTRUCTION DEVICE
+
+/obj/item/construction/rcd
+ name = "rapid-construction-device (RCD)"
+ icon = 'icons/obj/tools.dmi'
+ icon_state = "rcd"
+ worn_icon_state = "RCD"
+ lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
+ custom_premium_price = PAYCHECK_COMMAND * 10
+ max_matter = 160
+ slot_flags = ITEM_SLOT_BELT
+ item_flags = NO_MAT_REDEMPTION | NOBLUDGEON
+ has_ammobar = TRUE
+ actions_types = list(/datum/action/item_action/rcd_scan)
+
+ ///all stuff used by RCD for construction
+ var/static/list/root_categories = list(
+ //1ST ROOT CATEGORY
+ "Construction" = list( //Stuff you use to make & decorate areas
+ //Walls & Windows
+ "Structures" = list(
+ list(CONSTRUCTION_MODE = RCD_FLOORWALL, ICON = "wallfloor", TITLE = "Wall/Floor"),
+ list(CONSTRUCTION_MODE = RCD_WINDOWGRILLE, WINDOW_TYPE = /obj/structure/window, ICON = "windowsize", TITLE = "Directional Window"),
+ list(CONSTRUCTION_MODE = RCD_WINDOWGRILLE, WINDOW_TYPE = /obj/structure/window/reinforced, ICON = "windowtype", TITLE = "Directional Reinforced Window"),
+ list(CONSTRUCTION_MODE = RCD_WINDOWGRILLE, WINDOW_TYPE = /obj/structure/window/fulltile, ICON = "window0", TITLE = "Full Tile Window"),
+ list(CONSTRUCTION_MODE = RCD_WINDOWGRILLE, WINDOW_TYPE = /obj/structure/window/reinforced/fulltile, ICON = "rwindow0", TITLE = "Full Tile Reinforced Window"),
+ list(CONSTRUCTION_MODE = RCD_CATWALK, ICON = "catwalk-0", TITLE = "Catwalk"),
+ list(CONSTRUCTION_MODE = RCD_REFLECTOR, ICON = "reflector_base", TITLE = "Reflector"),
+ ),
+
+ //Computers & Machine Frames
+ "Machines" = list(
+ list(CONSTRUCTION_MODE = RCD_MACHINE, ICON = "box_1", TITLE = "Machine Frame"),
+ list(CONSTRUCTION_MODE = RCD_COMPUTER, COMPUTER_DIR = NORTH, ICON = "cnorth", TITLE = "Computer North"),
+ list(CONSTRUCTION_MODE = RCD_COMPUTER, COMPUTER_DIR = SOUTH, ICON = "csouth", TITLE = "Computer South"),
+ list(CONSTRUCTION_MODE = RCD_COMPUTER, COMPUTER_DIR = EAST, ICON = "ceast", TITLE = "Computer East"),
+ list(CONSTRUCTION_MODE = RCD_COMPUTER, COMPUTER_DIR = WEST, ICON = "cwest", TITLE = "Computer West"),
+ list(CONSTRUCTION_MODE = RCD_FLOODLIGHT, ICON = "floodlight_c1", TITLE = "FloodLight Frame"),
+ list(CONSTRUCTION_MODE = RCD_WALLFRAME, WALLFRAME_TYPE = /obj/item/wallframe/apc, ICON = "apc", TITLE = "APC WallFrame"),
+ list(CONSTRUCTION_MODE = RCD_WALLFRAME, WALLFRAME_TYPE = /obj/item/wallframe/airalarm, ICON = "alarm_bitem", TITLE = "AirAlarm WallFrame"),
+ list(CONSTRUCTION_MODE = RCD_WALLFRAME, WALLFRAME_TYPE = /obj/item/wallframe/firealarm, ICON = "fire_bitem", TITLE = "FireAlarm WallFrame"),
+ ),
+
+ //Interior Design[construction_mode = RCD_FURNISHING is implied]
+ "Furniture" = list(
+ list(FURNISH_TYPE = /obj/structure/chair, ICON = "chair", TITLE = "Chair"),
+ list(FURNISH_TYPE = /obj/structure/chair/stool, ICON = "stool", TITLE = "Stool"),
+ list(FURNISH_TYPE = /obj/structure/chair/stool/bar, ICON = "bar", TITLE = "Bar Stool"),
+ list(FURNISH_TYPE = /obj/structure/table, ICON = "table",TITLE = "Table"),
+ list(FURNISH_TYPE = /obj/structure/table/glass, ICON = "glass_table", TITLE = "Glass Table"),
+ list(FURNISH_TYPE = /obj/structure/rack, ICON = "rack", TITLE = "Rack"),
+ list(FURNISH_TYPE = /obj/structure/bed, ICON = "bed", TITLE = "Bed"),
+ ),
+ ),
+
+ //2ND ROOT CATEGORY[construction_mode = RCD_AIRLOCK is implied,"icon=closed"]
+ "Airlocks" = list( //used to seal/close areas
+ //Window Doors[airlock_glass = TRUE is implied]
+ "Windoors" = list(
+ list(AIRLOCK_TYPE = /obj/machinery/door/window, ICON = "windoor", TITLE = "Windoor"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/window/brigdoor, ICON = "secure_windoor", TITLE = "Secure Windoor"),
+ ),
+
+ //Glass Airlocks[airlock_glass = TRUE is implied,do fill_closed overlay]
+ "Glass Airlocks" = list(
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/glass, TITLE = "Standard", CATEGORY_ICON_STATE = TITLE_ICON, CATEGORY_ICON_SUFFIX = "Glass"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/public/glass, TITLE = "Public"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/engineering/glass, TITLE = "Engineering"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/atmos/glass, TITLE = "Atmospherics"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/security/glass, TITLE = "Security"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/command/glass, TITLE = "Command"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/medical/glass, TITLE = "Medical"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/research/glass, TITLE = "Research"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/virology/glass, TITLE = "Virology"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/mining/glass, TITLE = "Mining"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance/glass, TITLE = "Maintenance"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/external/glass, TITLE = "External"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance/external/glass, TITLE = "External Maintenance"),
+ ),
+
+ //Solid Airlocks[airlock_glass = FALSE is implied,no fill_closed overlay]
+ "Solid Airlocks" = list(
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock, TITLE = "Standard", CATEGORY_ICON_STATE = TITLE_ICON),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/public, TITLE = "Public"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/engineering, TITLE = "Engineering"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/atmos, TITLE = "Atmospherics"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/security, TITLE = "Security"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/command, TITLE = "Command"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/medical, TITLE = "Medical"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/research, TITLE = "Research"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/freezer, TITLE = "Freezer"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/virology, TITLE = "Virology"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/mining, TITLE = "Mining"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance, TITLE = "Maintenance"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/external, TITLE = "External"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance/external, TITLE = "External Maintenance"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/hatch, TITLE = "Airtight Hatch"),
+ list(AIRLOCK_TYPE = /obj/machinery/door/airlock/maintenance_hatch, TITLE = "Maintenance Hatch"),
+ ),
+ ),
+
+ //3RD CATEGORY Airlock access,empty list cause airlock_electronics UI will be displayed when this tab is selected
+ "Airlock Access" = list()
+ )
+
+ /// name of currently selected design
+ var/design_title = "Wall/Floor"
+ /// category of currently selected design
+ var/design_category = "Structures"
+ /// main category of currently selected design[Structures, Airlocks, Airlock Access]
+ var/root_category = "Construction"
+ /// owner of this rcd. It can either be an construction console or an player
+ var/owner
+ /// type of structure being built, used to decide what variables are used to build what structure
+ var/mode = RCD_FLOORWALL
+ /// temporary holder of mode, used to restore mode original value after rcd deconstruction act
+ var/construction_mode = RCD_FLOORWALL
+ /// used by arcd, can this rcd work from a range
+ var/ranged = FALSE
+ /// direction which computer frames are constructed in
+ var/computer_dir = NORTH
+ /// type of airlock/windoor being built[mode = RCD_AIRLOCK]
+ var/airlock_type = /obj/machinery/door/airlock
+ /// are we building glass/solid airlocks
+ var/airlock_glass = FALSE
+ /// are we building normal/reinforced directional/fulltile windows
+ var/obj/structure/window/window_type = /obj/structure/window/fulltile
+ /// type of wallmount airalarm,firealarm,apc we are trying to build
+ var/obj/item/wallframe/wallframe_type = /obj/item/wallframe/apc
+ /// type of furniture tables,chairs etc we are trying to build
+ var/furnish_type = /obj/structure/chair
+ /// delay multiplier for all construction types
+ var/delay_mod = 1
+ /// variable for R walls to deconstruct them
+ var/canRturf = FALSE
+ /// integrated airlock electronics for setting access to a newly built airlocks
+ var/obj/item/electronics/airlock/airlock_electronics
+
+ COOLDOWN_DECLARE(destructive_scan_cooldown)
+
+GLOBAL_VAR_INIT(icon_holographic_wall, init_holographic_wall())
+GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window())
+
+// `initial` does not work here. Neither does instantiating a wall/whatever
+// and referencing that. I don't know why.
+/proc/init_holographic_wall()
+ return getHologramIcon(
+ icon('icons/turf/walls/wall.dmi', "wall-0"),
+ opacity = 1,
+ )
+
+/proc/init_holographic_window()
+ var/icon/grille_icon = icon('icons/obj/structures.dmi', "grille")
+ var/icon/window_icon = icon('icons/obj/smooth_structures/window.dmi', "window-0")
+
+ grille_icon.Blend(window_icon, ICON_OVERLAY)
+
+ return getHologramIcon(grille_icon)
+
+/obj/item/construction/rcd/ui_action_click(mob/user, actiontype)
+ if (!COOLDOWN_FINISHED(src, destructive_scan_cooldown))
+ to_chat(user, span_warning("[src] lets out a low buzz."))
+ return
+
+ COOLDOWN_START(src, destructive_scan_cooldown, RCD_DESTRUCTIVE_SCAN_COOLDOWN)
+ rcd_scan(src)
+
+/**
+ * Global proc that generates RCD hologram in a range.
+ *
+ * Arguments:
+ * * source - The atom the scans originate from
+ * * scan_range - The range of turfs we grab from the source
+ * * fade_time - The time for RCD holograms to fade
+ */
+/proc/rcd_scan(atom/source, scan_range = RCD_DESTRUCTIVE_SCAN_RANGE, fade_time = RCD_HOLOGRAM_FADE_TIME)
+ playsound(source, 'sound/items/rcdscan.ogg', 50, vary = TRUE, pressure_affected = FALSE)
+
+ var/turf/source_turf = get_turf(source)
+ for(var/turf/open/surrounding_turf in RANGE_TURFS(scan_range, source_turf))
+ var/rcd_memory = surrounding_turf.rcd_memory
+ if(!rcd_memory)
+ continue
+
+ var/skip_to_next_turf = FALSE
+
+ for(var/atom/content_of_turf as anything in surrounding_turf.contents)
+ if (content_of_turf.density)
+ skip_to_next_turf = TRUE
+ break
+
+ if(skip_to_next_turf)
+ continue
+
+ var/hologram_icon
+ switch(rcd_memory)
+ if(RCD_MEMORY_WALL)
+ hologram_icon = GLOB.icon_holographic_wall
+ if(RCD_MEMORY_WINDOWGRILLE)
+ hologram_icon = GLOB.icon_holographic_window
+
+ var/obj/effect/rcd_hologram/hologram = new(surrounding_turf)
+ hologram.icon = hologram_icon
+ animate(hologram, alpha = 0, time = fade_time, easing = CIRCULAR_EASING | EASE_IN)
+
+/obj/effect/rcd_hologram
+ name = "hologram"
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+
+/obj/effect/rcd_hologram/Initialize(mapload)
+ . = ..()
+ QDEL_IN(src, RCD_HOLOGRAM_FADE_TIME)
+
+#undef RCD_DESTRUCTIVE_SCAN_COOLDOWN
+#undef RCD_DESTRUCTIVE_SCAN_RANGE
+#undef RCD_HOLOGRAM_FADE_TIME
+
+/obj/item/construction/rcd/suicide_act(mob/living/user)
+ var/turf/T = get_turf(user)
+
+ if(!isopenturf(T)) // Oh fuck
+ user.visible_message(span_suicide("[user] is beating [user.p_them()]self to death with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
+ return BRUTELOSS
+
+ mode = RCD_FLOORWALL
+ user.visible_message(span_suicide("[user] sets the RCD to 'Wall' and points it down [user.p_their()] throat! It looks like [user.p_theyre()] trying to commit suicide!"))
+ if(checkResource(16, user)) // It takes 16 resources to construct a wall
+ var/success = T.rcd_act(user, src, RCD_FLOORWALL)
+ T = get_turf(user)
+ // If the RCD placed a floor instead of a wall, having a wall without plating under it is cursed
+ // There isn't an easy programmatical way to check if rcd_act will place a floor or a wall, so just repeat using it for free
+ if(success && isopenturf(T))
+ T.rcd_act(user, src, RCD_FLOORWALL)
+ useResource(16, user)
+ activate()
+ playsound(loc, 'sound/machines/click.ogg', 50, 1)
+ user.gib()
+ return MANUAL_SUICIDE
+
+ user.visible_message(span_suicide("[user] pulls the trigger... But there is not enough ammo!"))
+ return SHAME
+
+/// check can the structure be placed on the turf
+/obj/item/construction/rcd/proc/can_place(atom/A, list/rcd_results, mob/user)
+ /**
+ *For anything that does not go an a wall we have to make sure that turf is clear for us to put the structure on it
+ *If we are just trying to destory something then this check is not nessassary
+ *RCD_WALLFRAME is also returned as the mode when upgrading apc, airalarm, firealarm using simple circuits upgrade
+ */
+ if(rcd_results["mode"] != RCD_WALLFRAME && rcd_results["mode"] != RCD_DECONSTRUCT)
+ var/turf/target_turf = get_turf(A)
+ //if we are trying to build a window on top of a grill we check for specific edge cases
+ if(rcd_results["mode"] == RCD_WINDOWGRILLE && istype(A, /obj/structure/grille))
+ var/list/structures_to_ignore
+
+ //if we are trying to build full-tile windows we only ignore the grille but other directional windows on the grill can block its construction
+ if(window_type == /obj/structure/window/fulltile || window_type == /obj/structure/window/reinforced/fulltile)
+ structures_to_ignore = list(A)
+ //for normal directional windows we ignore the grille & other directional windows as they can be in diffrent directions on the grill. There is a later check during construction to deal with those
+ else
+ structures_to_ignore = list(/obj/structure/grille, /obj/structure/window)
+
+ //check if we can build our window on the grill
+ if(target_turf.is_blocked_turf(exclude_mobs = FALSE, source_atom = null, ignore_atoms = structures_to_ignore, type_list = (length(structures_to_ignore) == 2)))
+ playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ balloon_alert(user, "something is on the grille!")
+ return FALSE
+
+ /**
+ * if we are trying to create plating on turf which is not a proper floor then dont check for objects on top of the turf just allow that turf to be converted into plating. e.g. create plating beneath a player or underneath a machine frame/any dense object
+ * if we are trying to finish a wall girder then let it finish then make sure no one/nothing is stuck in the girder
+ */
+ else if(rcd_results["mode"] == RCD_FLOORWALL && (!istype(target_turf, /turf/open/floor) || istype(A, /obj/structure/girder)))
+ //if a player builds a wallgirder on top of himself manually with iron sheets he can't finish the wall if he is still on the girder. Exclude the girder itself when checking for other dense objects on the turf
+ if(istype(A, /obj/structure/girder) && target_turf.is_blocked_turf(exclude_mobs = FALSE, source_atom = null, ignore_atoms = list(A)))
+ playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ balloon_alert(user, "something is on the girder!")
+ return FALSE
+
+ //check if turf is blocked in for dense structures
+ else
+ //structures which are small enough to fit on turfs containing directional windows.
+ var/static/list/small_structures = list(
+ RCD_AIRLOCK,
+ RCD_COMPUTER,
+ RCD_FLOODLIGHT,
+ RCD_FURNISHING,
+ RCD_MACHINE,
+ RCD_REFLECTOR,
+ RCD_WINDOWGRILLE,
+ )
+
+ //edge cases for what we can/can't ignore
+ var/ignore_mobs = FALSE
+ var/list/ignored_types
+ if(rcd_results["mode"] in small_structures)
+ ignored_types = list(/obj/structure/window)
+ //if we are trying to create grills/windoors we can go ahead and further ignore other windoors on the turf
+ if(rcd_results["mode"] == RCD_WINDOWGRILLE || (rcd_results["mode"] == RCD_AIRLOCK && ispath(airlock_type, /obj/machinery/door/window)))
+ //only ignore mobs if we are trying to create windoors and not grills. We dont want to drop a grill on top of somebody
+ ignore_mobs = rcd_results["mode"] == RCD_AIRLOCK
+ ignored_types += /obj/machinery/door/window
+ //if we are trying to create full airlock doors then we do the regular checks and make sure we have the full space for them. i.e. dont ignore anything dense on the turf
+ else if(rcd_results["mode"] == RCD_AIRLOCK)
+ ignored_types = list()
+
+ //check if the structure can fit on this turf
+ if(target_turf.is_blocked_turf(exclude_mobs = ignore_mobs, source_atom = null, ignore_atoms = ignored_types, type_list = TRUE))
+ playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ balloon_alert(user, "something is on the tile!")
+ return FALSE
+
+ return TRUE
+
+/obj/item/construction/rcd/proc/rcd_create(atom/A, mob/user)
+ //does this atom allow for rcd actions?
+ var/list/rcd_results = A.rcd_vals(user, src)
+ if(!rcd_results)
+ return FALSE
+ var/delay = rcd_results["delay"] * delay_mod
+ var/obj/effect/constructing_effect/rcd_effect = new(get_turf(A), delay, src.mode)
+
+ //resource & structure placement sanity checks before & after delay along with beam effects
+ if(!checkResource(rcd_results["cost"], user) || !can_place(A, rcd_results, user))
+ qdel(rcd_effect)
+ return FALSE
+ var/beam
+ if(ranged)
+ beam = user.Beam(A,icon_state="rped_upgrade", time = delay)
+ if(!do_after(user, delay, target = A))
+ qdel(rcd_effect)
+ if(!isnull(beam))
+ qdel(beam)
+ return FALSE
+ if(!checkResource(rcd_results["cost"], user) || !can_place(A, rcd_results, user))
+ qdel(rcd_effect)
+ return FALSE
+
+ if(!useResource(rcd_results["cost"], user))
+ qdel(rcd_effect)
+ return FALSE
+ activate()
+ if(!A.rcd_act(user, src, rcd_results["mode"]))
+ qdel(rcd_effect)
+ return FALSE
+ playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ rcd_effect.end_animation()
+ return TRUE
+
+/obj/item/construction/rcd/Initialize(mapload)
+ . = ..()
+ airlock_electronics = new(src)
+ airlock_electronics.name = "Access Control"
+ airlock_electronics.holder = src
+ GLOB.rcd_list += src
+
+/obj/item/construction/rcd/Destroy()
+ QDEL_NULL(airlock_electronics)
+ GLOB.rcd_list -= src
+ . = ..()
+
+/obj/item/construction/rcd/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/rcd),
+ )
+
+/obj/item/construction/rcd/ui_host(mob/user)
+ return owner || ..()
+
+/obj/item/construction/rcd/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "RapidConstructionDevice", name)
+ ui.open()
+
+/obj/item/construction/rcd/ui_static_data(mob/user)
+ return airlock_electronics.ui_static_data(user)
+
+/obj/item/construction/rcd/ui_data(mob/user)
+ var/list/data = ..()
+
+ //main categories
+ data["selected_root"] = root_category
+ data["root_categories"] = list()
+ for(var/category in root_categories)
+ data["root_categories"] += category
+
+ //create the category list
+ data["selected_category"] = design_category
+ data["selected_design"] = design_title
+ data["categories"] = list()
+
+ var/category_icon_state
+ var/category_icon_suffix
+ for(var/sub_category as anything in root_categories[root_category])
+ var/list/target_category = root_categories[root_category][sub_category]
+ if(target_category.len == 0)
+ continue
+
+ //skip category if upgrades were not installed for these
+ if(sub_category == "Machines" && !(upgrade & RCD_UPGRADE_FRAMES))
+ continue
+ if(sub_category == "Furniture" && !(upgrade & RCD_UPGRADE_FURNISHING))
+ continue
+ category_icon_state = ""
+ category_icon_suffix = ""
+
+ var/list/designs = list() //initialize all designs under this category
+ for(var/i in 1 to target_category.len)
+ var/list/design = target_category[i]
+
+ //check for special icon flags
+ if(design[CATEGORY_ICON_STATE] != null)
+ category_icon_state = design[CATEGORY_ICON_STATE]
+ if(design[CATEGORY_ICON_SUFFIX] != null)
+ category_icon_suffix = design[CATEGORY_ICON_SUFFIX]
+
+ //get icon or create it from pre defined flags
+ var/icon_state
+ if(design[ICON] != null)
+ icon_state = design[ICON]
+ else
+ icon_state = category_icon_state
+ if(icon_state == TITLE_ICON)
+ icon_state = design[TITLE]
+ icon_state = "[icon_state][category_icon_suffix]"
+
+ //sanitize them so you dont go insane when icon names contain spaces in them
+ icon_state = sanitize_css_class_name(icon_state)
+
+ designs += list(list(TITLE = design[TITLE], ICON = icon_state))
+ data["categories"] += list(list("cat_name" = sub_category, "designs" = designs))
+
+ //merge airlock_electronics ui data with this
+ var/list/airlock_data = airlock_electronics.ui_data(user)
+ for(var/key in airlock_data)
+ data[key] = airlock_data[key]
+
+ return data
+
+/obj/item/construction/rcd/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("root_category")
+ var/new_root = params["root_category"]
+ if(root_categories[new_root] != null) //is a valid category
+ root_category = new_root
+
+ if("design")
+ var/category_name = params["category"]
+ var/index = params["index"]
+
+ var/list/root = root_categories[root_category]
+ if(root == null) //not a valid root
+ return TRUE
+ var/list/category = root[category_name]
+ if(category == null) //not a valid category
+ return TRUE
+
+ /**
+ * The advantage of organizing designs into categories is that
+ * You can ignore an complete category if the design disk upgrade for that category isn't installed.
+ */
+ //You can't select designs from the Machines category if you dont have the frames upgrade installed.
+ if(category == "Machines" && !(upgrade & RCD_UPGRADE_FRAMES))
+ return TRUE
+ //You can't select designs from the Furniture category if you dont have the furnishing upgrade installed.
+ if(category == "Furniture" && !(upgrade & RCD_UPGRADE_FURNISHING))
+ return TRUE
+
+ var/list/design = category[index]
+ if(design == null) //not a valid design
+ return TRUE
+
+ design_category = category_name
+ design_title = design["title"]
+
+ if(category_name == "Structures")
+ construction_mode = design[CONSTRUCTION_MODE]
+ if(design[WINDOW_TYPE] != null)
+ window_type = design[WINDOW_TYPE]
+ else if(category_name == "Machines")
+ construction_mode = design[CONSTRUCTION_MODE]
+ if(design[COMPUTER_DIR] != null)
+ computer_dir = design[COMPUTER_DIR]
+ if(design[WALLFRAME_TYPE] != null)
+ wallframe_type = design[WALLFRAME_TYPE]
+ else if(category_name == "Furniture")
+ construction_mode = RCD_FURNISHING
+ furnish_type = design[FURNISH_TYPE]
+
+ if(root_category == "Airlocks")
+ construction_mode = RCD_AIRLOCK
+ airlock_glass = (category_name != "Solid Airlocks")
+ airlock_type = design[AIRLOCK_TYPE]
+
+ else
+ airlock_electronics.do_action(action, params)
+
+ return TRUE
+
+/obj/item/construction/rcd/attack_self(mob/user)
+ . = ..()
+ ui_interact(user)
+
+/obj/item/construction/rcd/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ //proximity check for normal rcd & range check for arcd
+ if((!proximity_flag && !ranged) || (ranged && !range_check(target, user)))
+ return FALSE
+
+ //do the work
+ mode = construction_mode
+ rcd_create(target, user)
+
+ return . | AFTERATTACK_PROCESSED_ITEM
+
+/obj/item/construction/rcd/afterattack_secondary(atom/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ //proximity check for normal rcd & range check for arcd
+ if((!proximity_flag && !ranged) || (ranged && !range_check(target, user)))
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+ //do the work
+ mode = RCD_DECONSTRUCT
+ rcd_create(target, user)
+
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+/obj/item/construction/rcd/proc/detonate_pulse()
+ audible_message("[src] begins to vibrate and \
+ buzz loudly!","[src] begins \
+ vibrating violently!")
+ // 5 seconds to get rid of it
+ addtimer(CALLBACK(src, PROC_REF(detonate_pulse_explode)), 50)
+
+/obj/item/construction/rcd/proc/detonate_pulse_explode()
+ explosion(src, light_impact_range = 3, flame_range = 1, flash_range = 1)
+ qdel(src)
+
+/obj/item/construction/rcd/borg
+ desc = "A device used to rapidly build walls and floors."
+ banned_upgrades = RCD_UPGRADE_SILO_LINK
+ var/energyfactor = 72
+
+/obj/item/construction/rcd/borg/get_matter(mob/user)
+ if(!iscyborg(user))
+ return 0
+ var/mob/living/silicon/robot/borgy = user
+ if(!borgy.cell)
+ return 0
+ max_matter = borgy.cell.maxcharge
+ return borgy.cell.charge
+
+/obj/item/construction/rcd/borg/useResource(amount, mob/user)
+ if(!iscyborg(user))
+ return 0
+ var/mob/living/silicon/robot/borgy = user
+ if(!borgy.cell)
+ if(user)
+ balloon_alert(user, "no cell found!")
+ return 0
+ . = borgy.cell.use(amount * energyfactor) //borgs get 1.3x the use of their RCDs
+ if(!. && user)
+ balloon_alert(user, "insufficient charge!")
+ return .
+
+/obj/item/construction/rcd/borg/checkResource(amount, mob/user)
+ if(!iscyborg(user))
+ return 0
+ var/mob/living/silicon/robot/borgy = user
+ if(!borgy.cell)
+ if(user)
+ balloon_alert(user, "no cell found!")
+ return 0
+ . = borgy.cell.charge >= (amount * energyfactor)
+ if(!. && user)
+ balloon_alert(user, "insufficient charge!")
+ return .
+
+/obj/item/construction/rcd/borg/syndicate
+ name = "syndicate RCD"
+ desc = "A reverse-engineered RCD with black market upgrades that allow this device to deconstruct reinforced walls. Property of Donk Co."
+ icon_state = "ircd"
+ inhand_icon_state = "ircd"
+ energyfactor = 66
+ canRturf = TRUE
+
+/obj/item/construction/rcd/loaded
+ matter = 160
+
+/obj/item/construction/rcd/loaded/upgraded
+ upgrade = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
+
+/obj/item/construction/rcd/combat
+ name = "industrial RCD"
+ icon_state = "ircd"
+ inhand_icon_state = "ircd"
+ max_matter = 500
+ matter = 500
+ canRturf = TRUE
+ upgrade = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
+
+#undef CONSTRUCTION_MODE
+#undef WINDOW_TYPE
+#undef COMPUTER_DIR
+#undef WALLFRAME_TYPE
+#undef FURNISH_TYPE
+#undef AIRLOCK_TYPE
+
+#undef TITLE
+#undef ICON
+
+#undef CATEGORY_ICON_STATE
+#undef CATEGORY_ICON_SUFFIX
+#undef TITLE_ICON
+
+/obj/item/rcd_ammo
+ name = "RCD matter cartridge"
+ desc = "Highly compressed matter for the RCD."
+ icon = 'icons/obj/tools.dmi'
+ icon_state = "rcdammo"
+ w_class = WEIGHT_CLASS_TINY
+ lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
+ custom_materials = list(/datum/material/iron=12000, /datum/material/glass=8000)
+ var/ammoamt = 40
+
+/obj/item/rcd_ammo/large
+ custom_materials = list(/datum/material/iron=48000, /datum/material/glass=32000)
+ ammoamt = 160
+
+/obj/item/construction/rcd/combat/admin
+ name = "admin RCD"
+ max_matter = INFINITY
+ matter = INFINITY
+ upgrade = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
+
+
+// Ranged RCD
+/obj/item/construction/rcd/arcd
+ name = "advanced rapid-construction-device (ARCD)"
+ desc = "A prototype RCD with ranged capability and infinite capacity."
+ max_matter = INFINITY
+ matter = INFINITY
+ delay_mod = 0.6
+ ranged = TRUE
+ icon_state = "arcd"
+ inhand_icon_state = "oldrcd"
+ has_ammobar = FALSE
+ upgrade = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
diff --git a/code/game/objects/items/RCL.dm b/code/game/objects/items/rcd/RCL.dm
similarity index 96%
rename from code/game/objects/items/RCL.dm
rename to code/game/objects/items/rcd/RCL.dm
index 4a14966df06..2bef59d875b 100644
--- a/code/game/objects/items/RCL.dm
+++ b/code/game/objects/items/rcd/RCL.dm
@@ -1,3 +1,5 @@
+//RAPID PIPE CLEANER
+
/obj/item/rcl
name = "rapid pipe cleaner layer"
desc = "A device used to rapidly deploy pipe cleaners. It has screws on the side which can be removed to slide off the pipe cleaners. Do not use without insulation!"
@@ -80,6 +82,7 @@
return
else
loaded = W //W.loc is src at this point.
+ loaded.set_pipecleaner_color(colors[current_color_index])
loaded.max_amount = max_amount //We store a lot.
return
@@ -107,10 +110,11 @@
return ..()
/obj/item/rcl/update_icon_state()
+ . = ..()
if(!loaded)
icon_state = "rcl-0"
inhand_icon_state = "rcl-0"
- return ..()
+ return
switch(loaded.amount)
if(61 to INFINITY)
icon_state = "rcl-30"
@@ -124,7 +128,6 @@
else
icon_state = "rcl-0"
inhand_icon_state = "rcl-0"
- return ..()
/obj/item/rcl/proc/is_empty(mob/user, loud = 1)
update_appearance()
@@ -182,11 +185,11 @@
if(!isturf(user.loc))
return
if(is_empty(user, 0))
- to_chat(user, span_warning("\The [src] is empty!"))
+ balloon_alert(user, "its empty!")
return
if(prob(2) && ghetto) //Give ghetto RCLs a 2% chance to jam, requiring it to be reactviated manually.
- to_chat(user, span_warning("[src]'s wires jam!"))
+ balloon_alert(user, "wires jam!")
active = FALSE
return
else
@@ -205,8 +208,6 @@
return //If we've run out, display message and exit
else
last = null
- loaded.color = GLOB.cable_colors[colors[current_color_index]]
- loaded.update_appearance()
last = loaded.place_turf(get_turf(src), user, turn(user.dir, 180))
is_empty(user) //If we've run out, display message
update_appearance()
@@ -278,9 +279,6 @@
if(!T.can_have_cabling())
return
- loaded.color = GLOB.cable_colors[colors[current_color_index]]
- loaded.update_appearance()
-
var/obj/structure/pipe_cleaner/linkingCable = findLinkingCable(user)
if(linkingCable)
if(choice != linkingCable.d2)
@@ -312,8 +310,7 @@
var/cwname = colors[current_color_index]
to_chat(user, "Color changed to [cwname]!")
if(loaded)
- loaded.color = GLOB.cable_colors[colors[current_color_index]]
- loaded.update_appearance()
+ loaded.set_pipecleaner_color(colors[current_color_index])
if(wiring_gui_menu)
wiringGuiUpdate(user)
else if(istype(action, /datum/action/item_action/rcl_gui))
diff --git a/code/game/objects/items/rcd/RHD.dm b/code/game/objects/items/rcd/RHD.dm
new file mode 100644
index 00000000000..d9b9389b2f1
--- /dev/null
+++ b/code/game/objects/items/rcd/RHD.dm
@@ -0,0 +1,296 @@
+//RAPID HANDHELD DEVICE. the base for all rapid devices
+
+/obj/item/construction
+ name = "not for ingame use"
+ desc = "A device used to rapidly build and deconstruct. Reload with iron, plasteel, glass or compressed matter cartridges."
+ opacity = FALSE
+ density = FALSE
+ anchored = FALSE
+ flags_1 = CONDUCT_1
+ item_flags = NOBLUDGEON
+ force = 0
+ throwforce = 10
+ throw_speed = 3
+ throw_range = 5
+ w_class = WEIGHT_CLASS_NORMAL
+ custom_materials = list(/datum/material/iron=100000)
+ req_access = list(ACCESS_ENGINE_EQUIP)
+ armor_type = /datum/armor/item_construction
+ resistance_flags = FIRE_PROOF
+ /// the spark system which sparks whever the ui options are dited
+ var/datum/effect_system/spark_spread/spark_system
+ /// current local matter inside the device, not used when silo link is on
+ var/matter = 0
+ /// maximum local matter this device can hold, not used when silo link is on
+ var/max_matter = 100
+ /// controls whether or not does update_icon apply ammo indicator overlays
+ var/has_ammobar = FALSE
+ /// amount of divisions in the ammo indicator overlay/number of ammo indicator states
+ var/ammo_sections = 10
+ /// bitflags for upgrades
+ var/upgrade = NONE
+ /// bitflags for banned upgrades
+ var/banned_upgrades = NONE
+ /// remote connection to the silo
+ var/datum/component/remote_materials/silo_mats
+ /// switch to use internal or remote storage
+ var/silo_link = FALSE
+
+/datum/armor/item_construction
+ fire = 100
+ acid = 50
+
+/obj/item/construction/Initialize(mapload)
+ . = ..()
+ spark_system = new /datum/effect_system/spark_spread
+ spark_system.set_up(5, 0, src)
+ spark_system.attach(src)
+ if(upgrade & RCD_UPGRADE_SILO_LINK)
+ silo_mats = AddComponent(/datum/component/remote_materials, "RCD", mapload, FALSE)
+ update_appearance()
+
+///used for examining the RCD and for its UI
+/obj/item/construction/proc/get_silo_iron()
+ if(silo_link && silo_mats.mat_container && !silo_mats.on_hold())
+ return silo_mats.mat_container.get_material_amount(/datum/material/iron)/500
+ return FALSE
+
+///returns local matter units available. overriden by rcd borg to return power units available
+/obj/item/construction/proc/get_matter(mob/user)
+ return matter
+
+/obj/item/construction/examine(mob/user)
+ . = ..()
+ . += "It currently holds [get_matter(user)]/[max_matter] matter-units."
+ if(upgrade & RCD_UPGRADE_SILO_LINK)
+ . += "Remote storage link state: [silo_link ? "[silo_mats.on_hold() ? "ON HOLD" : "ON"]" : "OFF"]."
+ var/iron = get_silo_iron()
+ if(iron)
+ . += "Remote connection has iron in equivalent to [iron] RCD unit\s." //1 matter for 1 floor tile, as 4 tiles are produced from 1 iron
+
+/obj/item/construction/Destroy()
+ QDEL_NULL(spark_system)
+ silo_mats = null
+ return ..()
+
+/obj/item/construction/pre_attack(atom/target, mob/user, params)
+ if(istype(target, /obj/item/rcd_upgrade))
+ install_upgrade(target, user)
+ return TRUE
+ if(insert_matter(target, user))
+ return TRUE
+ return ..()
+
+/obj/item/construction/attackby(obj/item/item, mob/user, params)
+ if(istype(item, /obj/item/rcd_upgrade))
+ install_upgrade(item, user)
+ return TRUE
+ if(insert_matter(item, user))
+ return TRUE
+ return ..()
+
+/// Installs an upgrade into the RCD checking if it is already installed, or if it is a banned upgrade
+/obj/item/construction/proc/install_upgrade(obj/item/rcd_upgrade/design_disk, mob/user)
+ if(design_disk.upgrade & upgrade)
+ balloon_alert(user, "already installed!")
+ return
+ if(design_disk.upgrade & banned_upgrades)
+ balloon_alert(user, "cannot install upgrade!")
+ return
+ upgrade |= design_disk.upgrade
+ if((design_disk.upgrade & RCD_UPGRADE_SILO_LINK) && !silo_mats)
+ silo_mats = AddComponent(/datum/component/remote_materials, "RCD", FALSE, FALSE)
+ playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ qdel(design_disk)
+
+/// Inserts matter into the RCD allowing it to build
+/obj/item/construction/proc/insert_matter(obj/item, mob/user)
+ if(iscyborg(user))
+ return FALSE
+
+ var/loaded = FALSE
+ if(istype(item, /obj/item/rcd_ammo))
+ var/obj/item/rcd_ammo/ammo = item
+ var/load = min(ammo.ammoamt, max_matter - matter)
+ if(load <= 0)
+ balloon_alert(user, "storage full!")
+ return FALSE
+ ammo.ammoamt -= load
+ if(ammo.ammoamt <= 0)
+ qdel(ammo)
+ matter += load
+ playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ loaded = TRUE
+ else if(isstack(item))
+ loaded = loadwithsheets(item, user)
+ if(loaded)
+ update_appearance() //ensures that ammo counters (if present) get updated
+ return loaded
+
+/obj/item/construction/proc/loadwithsheets(obj/item/stack/the_stack, mob/user)
+ if(the_stack.matter_amount <= 0)
+ balloon_alert(user, "invalid sheets!")
+ return FALSE
+ var/maxsheets = round((max_matter-matter) / the_stack.matter_amount) //calculate the max number of sheets that will fit in RCD
+ if(maxsheets > 0)
+ var/amount_to_use = min(the_stack.amount, maxsheets)
+ the_stack.use(amount_to_use)
+ matter += the_stack.matter_amount * amount_to_use
+ playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ return TRUE
+ balloon_alert(user, "storage full!")
+ return FALSE
+
+/obj/item/construction/proc/activate()
+ playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
+
+/obj/item/construction/attack_self(mob/user)
+ playsound(loc, 'sound/effects/pop.ogg', 50, FALSE)
+ if(prob(20))
+ spark_system.start()
+
+/obj/item/construction/update_overlays()
+ . = ..()
+ if(has_ammobar)
+ var/ratio = CEILING((matter / max_matter) * ammo_sections, 1)
+ if(ratio > 0)
+ . += "[icon_state]_charge[ratio]"
+
+/obj/item/construction/proc/useResource(amount, mob/user)
+ if(!silo_mats || !silo_link)
+ if(matter < amount)
+ if(user)
+ balloon_alert(user, "not enough matter!")
+ return FALSE
+ matter -= amount
+ update_appearance()
+ return TRUE
+ else
+ if(silo_mats.on_hold())
+ if(user)
+ balloon_alert(user, "silo on hold!")
+ return FALSE
+ if(!silo_mats.mat_container)
+ balloon_alert(user, "no silo detected!")
+ return FALSE
+ if(!silo_mats.mat_container.has_materials(list(/datum/material/iron = 500), amount))
+ if(user)
+ balloon_alert(user, "not enough silo material!")
+ return FALSE
+
+ var/list/materials = list()
+ materials[GET_MATERIAL_REF(/datum/material/iron)] = 500
+ silo_mats.mat_container.use_materials(materials, amount)
+ silo_mats.silo_log(src, "consume", -amount, "build", materials)
+ return TRUE
+
+///shared data for rcd,rld & plumbing
+/obj/item/construction/ui_data(mob/user)
+ var/list/data = list()
+
+ //matter in the rcd
+ var/total_matter = ((upgrade & RCD_UPGRADE_SILO_LINK) && silo_link) ? get_silo_iron() : get_matter(user)
+ if(!total_matter)
+ total_matter = 0
+ data["matterLeft"] = total_matter
+
+ //silo details
+ data["silo_upgraded"] = !!(upgrade & RCD_UPGRADE_SILO_LINK)
+ data["silo_enabled"] = silo_link
+
+ return data
+
+/obj/item/construction/proc/toggle_silo(mob/user)
+ if(!silo_mats)
+ to_chat(user, span_warning("no remote storage connection."))
+ return FALSE
+ if(!silo_mats.mat_container && !silo_link) // Allow them to turn off an invalid link.
+ to_chat(user, span_warning("no silo link detected."))
+ return FALSE
+
+ silo_link = !silo_link
+ to_chat(user, span_notice("silo link state: [silo_link ? "on" : "off"]"))
+
+ return TRUE
+
+///shared action for toggling silo link rcd,rld & plumbing
+/obj/item/construction/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+
+ if(action == "toggle_silo" && (upgrade & RCD_UPGRADE_SILO_LINK))
+ return toggle_silo(usr)
+
+/obj/item/construction/proc/checkResource(amount, mob/user)
+ if(!silo_mats || !silo_mats.mat_container || !silo_link)
+ if(silo_link)
+ balloon_alert(user, "silo link invalid!")
+ return FALSE
+ else
+ . = matter >= amount
+ else
+ if(silo_mats.on_hold())
+ if(user)
+ balloon_alert(user, "silo on hold!")
+ return FALSE
+ . = silo_mats.mat_container.has_materials(list(/datum/material/iron = 500), amount)
+ if(!. && user)
+ balloon_alert(user, "low ammo!")
+ if(has_ammobar)
+ flick("[icon_state]_empty", src) //somewhat hacky thing to make RCDs with ammo counters actually have a blinking yellow light
+ return .
+
+/obj/item/construction/proc/range_check(atom/target, mob/user)
+ if(target.z != user.z)
+ return
+ if(!(target in dview(7, get_turf(user))))
+ balloon_alert(user, "out of range!")
+ flick("[icon_state]_empty", src)
+ return FALSE
+ else
+ return TRUE
+
+/**
+ * Checks if we are allowed to interact with a radial menu
+ *
+ * Arguments:
+ * * user The living mob interacting with the menu
+ * * remote_anchor The remote anchor for the menu
+ */
+/obj/item/construction/proc/check_menu(mob/living/user, remote_anchor)
+ if(!istype(user))
+ return FALSE
+ if(user.incapacitated())
+ return FALSE
+ if(remote_anchor && user.remote_control != remote_anchor)
+ return FALSE
+ return TRUE
+
+/obj/item/rcd_upgrade
+ name = "RCD advanced design disk"
+ desc = "It seems to be empty."
+ icon = 'icons/obj/module.dmi'
+ icon_state = "datadisk3"
+ var/upgrade
+
+/obj/item/rcd_upgrade/frames
+ desc = "It contains the design for machine frames and computer frames."
+ upgrade = RCD_UPGRADE_FRAMES
+
+/obj/item/rcd_upgrade/simple_circuits
+ desc = "It contains the design for firelock, air alarm, fire alarm, apc circuits and crap power cells."
+ upgrade = RCD_UPGRADE_SIMPLE_CIRCUITS
+
+/obj/item/rcd_upgrade/silo_link
+ desc = "It contains direct silo connection RCD upgrade."
+ upgrade = RCD_UPGRADE_SILO_LINK
+
+/obj/item/rcd_upgrade/furnishing
+ desc = "It contains the design for chairs, stools, tables, and glass tables."
+ upgrade = RCD_UPGRADE_FURNISHING
+
+/datum/action/item_action/rcd_scan
+ name = "Destruction Scan"
+ desc = "Scans the surrounding area for destruction. Scanned structures will rebuild significantly faster."
+
diff --git a/code/game/objects/items/rcd/RLD.dm b/code/game/objects/items/rcd/RLD.dm
new file mode 100644
index 00000000000..4cfc3d086c0
--- /dev/null
+++ b/code/game/objects/items/rcd/RLD.dm
@@ -0,0 +1,206 @@
+// RAPID LIGHTING DEVICE
+
+#define GLOW_MODE 3
+#define LIGHT_MODE 2
+#define REMOVE_MODE 1
+
+/obj/item/construction/rld
+ name = "Rapid Lighting Device"
+ desc = "A device used to rapidly provide lighting sources to an area. Reload with iron, plasteel, glass or compressed matter cartridges."
+ icon = 'icons/obj/tools.dmi'
+ icon_state = "rld"
+ worn_icon_state = "RPD"
+ lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
+ matter = 200
+ max_matter = 200
+ slot_flags = ITEM_SLOT_BELT
+ has_ammobar = TRUE
+ ammo_sections = 6
+ ///it does not make sense why any of these should be installed
+ banned_upgrades = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
+
+ var/mode = LIGHT_MODE
+ var/wallcost = 10
+ var/floorcost = 15
+ var/launchcost = 5
+ var/deconcost = 10
+
+ var/condelay = 10
+ var/decondelay = 15
+
+ ///reference to thr original icons
+ var/list/original_options = list(
+ "Color Pick" = icon(icon = 'icons/hud/radial.dmi', icon_state = "omni"),
+ "Glow Stick" = icon(icon = 'icons/obj/lighting.dmi', icon_state = "glowstick"),
+ "Deconstruct" = icon(icon = 'icons/obj/tools.dmi', icon_state = "wrench"),
+ "Light Fixture" = icon(icon = 'icons/obj/lighting.dmi', icon_state = "ltube"),
+ )
+ ///will contain the original icons modified with the color choice
+ var/list/display_options = list()
+ var/color_choice = "#ffffff"
+
+/obj/item/construction/rld/Initialize(mapload)
+ . = ..()
+ for(var/option in original_options)
+ display_options[option] = icon(original_options[option])
+
+/obj/item/construction/rld/attack_self(mob/user)
+ . = ..()
+
+ if((upgrade & RCD_UPGRADE_SILO_LINK) && display_options["Silo Link"] == null) //silo upgrade instaled but option was not updated then update it just one
+ display_options["Silo Link"] = icon(icon = 'icons/obj/mining.dmi', icon_state = "silo")
+
+ var/choice = show_radial_menu(user, src, display_options, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE, tooltips = TRUE)
+ if(!check_menu(user))
+ return
+ if(!choice)
+ return
+
+ switch(choice)
+ if("Light Fixture")
+ mode = LIGHT_MODE
+ to_chat(user, span_notice("You change RLD's mode to 'Permanent Light Construction'."))
+ if("Glow Stick")
+ mode = GLOW_MODE
+ to_chat(user, span_notice("You change RLD's mode to 'Light Launcher'."))
+ if("Color Pick")
+ var/new_choice = input(user,"","Choose Color",color_choice) as color
+ if(new_choice == null)
+ return
+
+ var/list/new_rgb = ReadRGB(new_choice)
+ for(var/option in original_options)
+ if(option == "Color Pick" || option == "Deconstruct" || option == "Silo Link")
+ continue
+ var/icon/icon = icon(original_options[option])
+ icon.SetIntensity(new_rgb[1]/255, new_rgb[2]/255, new_rgb[3]/255) //apply new scale
+ display_options[option] = icon
+
+ color_choice = new_choice
+ if("Deconstruct")
+ mode = REMOVE_MODE
+ to_chat(user, span_notice("You change RLD's mode to 'Deconstruct'."))
+ else
+ toggle_silo(user)
+
+/obj/item/construction/rld/afterattack(atom/A, mob/user)
+ . = ..()
+ if(!range_check(A,user))
+ return
+ var/turf/start = get_turf(src)
+ switch(mode)
+ if(REMOVE_MODE)
+ if(!istype(A, /obj/machinery/light/))
+ return FALSE
+
+ //resource sanity checks before & after delay
+ if(!checkResource(deconcost, user))
+ return FALSE
+ var/beam = user.Beam(A,icon_state="light_beam", time = 15)
+ playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ if(!do_after(user, decondelay, target = A))
+ qdel(beam)
+ return FALSE
+ if(!checkResource(deconcost, user))
+ return FALSE
+
+ if(!useResource(deconcost, user))
+ return FALSE
+ activate()
+ qdel(A)
+ return TRUE
+
+ if(LIGHT_MODE)
+ //resource sanity checks before & after delay
+ if(!checkResource(floorcost, user))
+ return FALSE
+ var/beam = user.Beam(A,icon_state="light_beam", time = condelay)
+ playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ playsound(loc, 'sound/effects/light_flicker.ogg', 50, FALSE)
+ if(!do_after(user, condelay, target = A))
+ qdel(beam)
+ return FALSE
+ if(!checkResource(floorcost, user))
+ return FALSE
+
+ if(iswallturf(A))
+ var/turf/open/winner = null
+ var/winning_dist = null
+ var/skip = FALSE
+ for(var/direction in GLOB.cardinals)
+ var/turf/C = get_step(A, direction)
+ //turf already has a light
+ skip = FALSE
+ for(var/obj/machinery/light/dupe in C)
+ if(istype(dupe, /obj/machinery/light))
+ skip = TRUE
+ break
+ if(skip)
+ continue
+ //can't put a light here
+ if(!(isspaceturf(C) || TURF_SHARES(C)))
+ continue
+ //find turf closest to our player
+ var/x0 = C.x
+ var/y0 = C.y
+ var/contender = CHEAP_HYPOTENUSE(start.x, start.y, x0, y0)
+ if(!winner)
+ winner = C
+ winning_dist = contender
+ else if(contender < winning_dist) // lower is better
+ winner = C
+ winning_dist = contender
+ if(!winner)
+ balloon_alert(user, "no valid target!")
+ return FALSE
+
+ if(!useResource(wallcost, user))
+ return FALSE
+ activate()
+ var/obj/machinery/light/L = new /obj/machinery/light(get_turf(winner))
+ L.setDir(get_dir(winner, A))
+ L.color = color_choice
+ L.set_light_color(color_choice)
+ return TRUE
+
+ if(isfloorturf(A))
+ var/turf/target = get_turf(A)
+ for(var/obj/machinery/light/floor/dupe in target)
+ if(istype(dupe))
+ return FALSE
+
+ if(!useResource(floorcost, user))
+ return FALSE
+ activate()
+ var/obj/machinery/light/floor/FL = new /obj/machinery/light/floor(target)
+ FL.color = color_choice
+ FL.set_light_color(color_choice)
+ return TRUE
+
+ if(GLOW_MODE)
+ if(!useResource(launchcost, user))
+ return FALSE
+ activate()
+ var/obj/item/flashlight/glowstick/G = new /obj/item/flashlight/glowstick(start)
+ G.color = color_choice
+ G.set_light_color(G.color)
+ G.throw_at(A, 9, 3, user)
+ G.on = TRUE
+ G.update_brightness()
+
+ return TRUE
+
+/obj/item/construction/rld/mini
+ name = "mini-rapid-light-device"
+ desc = "A device used to rapidly provide lighting sources to an area. Reload with iron, plasteel, glass or compressed matter cartridges."
+ icon = 'icons/obj/tools.dmi'
+ icon_state = "rld"
+ lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
+ matter = 100
+ max_matter = 100
+
+#undef GLOW_MODE
+#undef LIGHT_MODE
+#undef REMOVE_MODE
diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/rcd/RPD.dm
similarity index 94%
rename from code/game/objects/items/RPD.dm
rename to code/game/objects/items/rcd/RPD.dm
index 6f4475460a1..a80e842011d 100644
--- a/code/game/objects/items/RPD.dm
+++ b/code/game/objects/items/rcd/RPD.dm
@@ -1,3 +1,5 @@
+//RAPID PIPE DISPENSER
+
#define ATMOS_CATEGORY 0
#define DISPOSALS_CATEGORY 1
#define TRANSIT_CATEGORY 2
@@ -142,7 +144,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
var/flipped = ((dirtype == PIPE_TRIN_M) || (dirtype == PIPE_UNARY_FLIPPABLE)) && (ISDIAGONALDIR(numdir))
row["previews"] += list(list(
"selected" = dirtype == PIPE_ONEDIR ? TRUE : (numdir == selected_dir),
- "dir" = dir2text(numdir),
+ "dir" = dir2text(numdir),
"dir_name" = dirs[dir],
"icon_state" = icon_state,
"flipped" = flipped,
@@ -326,7 +328,6 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
if(target.pipe_color && target.piping_layer)
paint_color = GLOB.pipe_color_name[target.pipe_color]
piping_layer = target.piping_layer
- to_chat(user, span_notice("You change [src] to [paint_color] color and layer [piping_layer] pipes."))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/pipe_dispenser/attackby(obj/item/W, mob/user, params)
@@ -353,7 +354,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
*/
/obj/item/pipe_dispenser/proc/install_upgrade(obj/item/rpd_upgrade/rpd_up, mob/user)
if(rpd_up.upgrade_flags& upgrade_flags)
- to_chat(user, span_warning("[src] has already installed this upgrade!"))
+ balloon_alert(user, "already installed!")
return
upgrade_flags |= rpd_up.upgrade_flags
playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE)
@@ -509,7 +510,6 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
. = TRUE
if((mode & DESTROY_MODE) && istype(attack_target, /obj/item/pipe) || istype(attack_target, /obj/structure/disposalconstruct) || istype(attack_target, /obj/structure/c_transit_tube) || istype(attack_target, /obj/structure/c_transit_tube_pod) || istype(attack_target, /obj/item/pipe_meter) || istype(attack_target, /obj/structure/disposalpipe/broken))
- to_chat(user, span_notice("You start destroying a pipe..."))
playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
if(do_after(user, destroy_speed, target = attack_target))
activate()
@@ -521,19 +521,18 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
var/obj/machinery/atmospherics/pipe/smart/S = attack_target
if(istype(S))
if (S.dir == ALL_CARDINALS)
- to_chat(user, span_warning("\The [S] has no unconnected directions!"))
+ balloon_alert(user, "has no unconnected directions!")
return
var/old_init_dir = S.get_init_directions()
if (old_init_dir == p_init_dir)
- to_chat(user, span_warning("\The [S] is already in this configuration!"))
+ balloon_alert(user, "already configured!")
return
// Check for differences in unconnected directions
var/target_differences = (p_init_dir ^ old_init_dir) & ~S.connections
if (!target_differences)
- to_chat(user, span_warning("\The [S] is already in this configuration for its unconnected directions!"))
+ balloon_alert(user, "already configured for its directions!")
return
- to_chat(user, span_notice("You start reprogramming \the [S]..."))
playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
if(!do_after(user, reprogram_speed, target = S))
return
@@ -543,7 +542,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
// Double check to make sure that nothing has changed. If anything we were about to change was connected during do_after, abort
if (target_differences & S.connections)
- to_chat(user, span_warning("\The [src]'s screen flashes a warning: Can't configure a pipe in a currently connected direction."))
+ balloon_alert(user, "cant configure for its direction!")
return
// Grab the current initializable directions, which may differ from old_init_dir if someone else was working on the same pipe at the same time
var/current_init_dir = S.get_init_directions()
@@ -552,7 +551,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
var/new_init_dir = (current_init_dir & ~target_differences) | (p_init_dir & target_differences)
// Don't make a smart pipe with only one connection
if (ISSTUB(new_init_dir))
- to_chat(user, span_warning("\The [src]'s screen flashes a warning: Can't configure a pipe to only connect in one direction."))
+ balloon_alert(user, "no one directional pipes allowed!")
return
S.set_init_directions(new_init_dir)
// We're now reconfigured.
@@ -594,7 +593,6 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
return ..()
playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
if (recipe.type == /datum/pipe_info/meter)
- to_chat(user, span_notice("You start building a meter..."))
if(do_after(user, atmos_build_speed, target = attack_target))
activate()
var/obj/item/pipe_meter/PM = new /obj/item/pipe_meter(get_turf(attack_target))
@@ -602,7 +600,6 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
if(mode & WRENCH_MODE)
PM.wrench_act(user, src)
else if(recipe.type == /datum/pipe_info/sensor)
- balloon_alert(user, "building air sensor...")
if(do_after(user, atmos_build_speed, target = attack_target))
activate()
var/datum/pipe_info/sensor/sensor_recipe = recipe
@@ -612,12 +609,11 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
recipe = first_atmos
else
if(recipe.all_layers == FALSE && (piping_layer == 1 || piping_layer == 5))
- to_chat(user, span_notice("You can't build this object on the layer..."))
+ balloon_alert(user, "cant build on this layer!")
return ..()
- to_chat(user, span_notice("You start building a pipe..."))
if(do_after(user, atmos_build_speed, target = attack_target))
if(recipe.all_layers == FALSE && (piping_layer == 1 || piping_layer == 5))//double check to stop cheaters (and to not waste time waiting for something that can't be placed)
- to_chat(user, span_notice("You can't build this object on the layer..."))
+ balloon_alert(user, "cant build on this layer!")
return ..()
activate()
var/obj/machinery/atmospherics/path = queued_p_type
@@ -647,15 +643,14 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
return ..()
attack_target = get_turf(attack_target)
if(isclosedturf(attack_target))
- to_chat(user, span_warning("[src]'s error light flickers; there's something in the way!"))
+ balloon_alert(user, "target is blocked!")
return
- to_chat(user, span_notice("You start building a disposals pipe..."))
playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
if(do_after(user, disposal_build_speed, target = attack_target))
var/obj/structure/disposalconstruct/C = new (attack_target, queued_p_type, queued_p_dir, queued_p_flipped)
if(!C.can_place())
- to_chat(user, span_warning("There's not enough room to build that here!"))
+ balloon_alert(user, "not enough room!")
qdel(C)
return
@@ -672,15 +667,14 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
return ..()
attack_target = get_turf(attack_target)
if(isclosedturf(attack_target))
- to_chat(user, span_warning("[src]'s error light flickers; there's something in the way!"))
+ balloon_alert(user, "something in the way!")
return
var/turf/target_turf = get_turf(attack_target)
if(target_turf.is_blocked_turf(exclude_mobs = TRUE))
- to_chat(user, span_warning("[src]'s error light flickers; there's something in the way!"))
+ balloon_alert(user, "something in the way!")
return
- to_chat(user, span_notice("You start building a transit tube..."))
playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
if(do_after(user, transit_build_speed, target = attack_target))
activate()
diff --git a/code/game/objects/items/rcd/RPLD.dm b/code/game/objects/items/rcd/RPLD.dm
new file mode 100644
index 00000000000..ca871401c56
--- /dev/null
+++ b/code/game/objects/items/rcd/RPLD.dm
@@ -0,0 +1,343 @@
+///The plumbing RCD. All the blueprints are located in _globalvars > lists > construction.dm
+/obj/item/construction/plumbing
+ name = "Plumbing Constructor"
+ desc = "An expertly modified RCD outfitted to construct plumbing machinery."
+ icon_state = "plumberer2"
+ inhand_icon_state = "plumberer"
+ lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
+ worn_icon_state = "plumbing"
+ icon = 'icons/obj/tools.dmi'
+ slot_flags = ITEM_SLOT_BELT
+ ///it does not make sense why any of these should be installed.
+ banned_upgrades = RCD_UPGRADE_FRAMES | RCD_UPGRADE_SIMPLE_CIRCUITS | RCD_UPGRADE_FURNISHING
+ matter = 200
+ max_matter = 200
+
+ ///type of the plumbing machine
+ var/obj/machinery/blueprint = null
+ ///index, used in the attack self to get the type. stored here since it doesnt change
+ var/list/choices = list()
+ ///All info for construction
+ var/list/machinery_data = list("cost" = list())
+ ///This list that holds all the plumbing design types the plumberer can construct. Its purpose is to make it easy to make new plumberer subtypes with a different selection of machines.
+ var/list/plumbing_design_types
+ ///Current selected layer
+ var/current_layer = "Default Layer"
+ ///Current selected color, for ducts
+ var/current_color = "omni"
+ ///maps layer name to layer number value. didnt make this global cause only this class needs it
+ var/static/list/name_to_number = list(
+ "First Layer" = 1,
+ "Second Layer" = 2,
+ "Default Layer" = 3,
+ "Fourth Layer" = 4,
+ "Fifth Layer" = 5,
+ )
+
+/obj/item/construction/plumbing/Initialize(mapload)
+ . = ..()
+
+ //design types supported for this plumbing rcd
+ set_plumbing_designs()
+
+ //set cost of each machine & initial blueprint
+ for(var/obj/machinery/plumbing/plumbing_type as anything in plumbing_design_types)
+ machinery_data["cost"][plumbing_type] = plumbing_design_types[plumbing_type]
+ blueprint = plumbing_design_types[1]
+
+/obj/item/construction/plumbing/proc/set_plumbing_designs()
+ plumbing_design_types = list(
+ //category 1 Synthesizers i.e devices which creates , reacts & destroys chemicals
+ /obj/machinery/plumbing/synthesizer = 15,
+ /obj/machinery/plumbing/reaction_chamber/chem = 15,
+ /obj/machinery/plumbing/grinder_chemical = 30,
+ /obj/machinery/plumbing/growing_vat = 20,
+ /obj/machinery/plumbing/fermenter = 30,
+ /obj/machinery/plumbing/liquid_pump = 35, //extracting chemicals from ground is one way of creation
+ /obj/machinery/plumbing/disposer = 10,
+ /obj/machinery/plumbing/buffer = 10, //creates chemicals as it waits for other buffers containing other chemicals and when mixed creates new chemicals
+
+ //category 2 distributors i.e devices which inject , move around , remove chemicals from the network
+ /obj/machinery/duct = 1,
+ /obj/machinery/plumbing/layer_manifold = 5,
+ /obj/machinery/plumbing/input = 5,
+ /obj/machinery/plumbing/filter = 5,
+ /obj/machinery/plumbing/splitter = 5,
+ /obj/machinery/plumbing/sender = 20,
+ /obj/machinery/plumbing/output = 5,
+
+ //category 3 Storage i.e devices which stores & makes the processed chemicals ready for consumption
+ /obj/machinery/plumbing/tank = 20,
+ /obj/machinery/plumbing/acclimator = 10,
+ /obj/machinery/plumbing/bottler = 50,
+ /obj/machinery/plumbing/pill_press = 20,
+ /obj/machinery/iv_drip/plumbing = 20
+ )
+
+/obj/item/construction/plumbing/equipped(mob/user, slot, initial)
+ . = ..()
+ if(slot & ITEM_SLOT_HANDS)
+ RegisterSignal(user, COMSIG_MOUSE_SCROLL_ON, PROC_REF(mouse_wheeled))
+ else
+ UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
+
+/obj/item/construction/plumbing/dropped(mob/user, silent)
+ UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
+ return ..()
+
+/obj/item/construction/plumbing/cyborg_unequip(mob/user)
+ UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
+ return ..()
+
+/obj/item/construction/plumbing/attack_self(mob/user)
+ . = ..()
+ ui_interact(user)
+
+/obj/item/construction/plumbing/examine(mob/user)
+ . = ..()
+ . += "You can scroll your mouse wheel to change the piping layer."
+ . += "You can right click a fluid duct to set the Plumbing RPD to its color and layer."
+
+/obj/item/construction/plumbing/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "PlumbingService", name)
+ ui.open()
+
+/obj/item/construction/plumbing/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/plumbing),
+ )
+
+/obj/item/construction/plumbing/ui_static_data(mob/user)
+ return list("paint_colors" = GLOB.pipe_paint_colors)
+
+///find which category this design belongs to
+/obj/item/construction/plumbing/proc/get_category(obj/machinery/recipe)
+ if(ispath(recipe, /obj/machinery/plumbing))
+ var/obj/machinery/plumbing/plumbing_design = recipe
+ return initial(plumbing_design.category)
+ else if(ispath(recipe , /obj/machinery/duct))
+ return "Distribution"
+ else
+ return "Storage"
+
+/obj/item/construction/plumbing/ui_data(mob/user)
+ var/list/data = ..()
+
+ data["piping_layer"] = name_to_number[current_layer] //maps layer name to layer number's 1,2,3,4,5
+ data["selected_color"] = current_color
+ data["layer_icon"] = "plumbing_layer[GLOB.plumbing_layers[current_layer]]"
+ data["selected_category"] = get_category(blueprint)
+ data["selected_recipe"] = initial(blueprint.name)
+
+ var/list/category_list = list()
+ var/category_name = ""
+ var/obj/machinery/recipe = null
+
+ for(var/i in 1 to plumbing_design_types.len)
+ recipe = plumbing_design_types[i]
+
+ category_name = get_category(recipe) //get category of design
+ if(!category_list[category_name])
+ var/list/item_list = list()
+ item_list["cat_name"] = category_name //used by RapidPipeDispenser.js
+ item_list["recipes"] = list() //used by RapidPipeDispenser.js
+ category_list[category_name] = item_list
+
+ //add item to category
+ category_list[category_name]["recipes"] += list(list(
+ "index" = i,
+ "icon" = initial(recipe.icon_state),
+ "name" = initial(recipe.name),
+ ))
+
+ data["categories"] = list()
+ for(category_name in category_list)
+ data["categories"] += list(category_list[category_name])
+
+ return data
+
+/obj/item/construction/plumbing/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("color")
+ var/color = params["paint_color"]
+ if(GLOB.pipe_paint_colors[color] != null) //validate if the color is in the allowed list of values
+ current_color = color
+ if("piping_layer")
+ var/bitflag = text2num(params["piping_layer"]) //convert from layer number back to layer string
+ bitflag = 1 << (bitflag - 1)
+ var/layer = GLOB.plumbing_layer_names["[bitflag]"]
+ if(layer != null) //validate if this value exists in the list
+ current_layer = layer
+ if("recipe")
+ var/design = plumbing_design_types[text2num(params["id"])]
+ if(design != null) //validate if design is valid
+ blueprint = design
+ playsound(src, 'sound/effects/pop.ogg', 50, vary = FALSE)
+
+ return TRUE
+
+
+///pretty much rcd_create, but named differently to make myself feel less bad for copypasting from a sibling-type
+/obj/item/construction/plumbing/proc/create_machine(atom/destination, mob/user)
+ if(!machinery_data || !isopenturf(destination))
+ return FALSE
+
+ //resource & placement sanity check before & after delay
+ var/is_allowed = TRUE
+ if(!checkResource(machinery_data["cost"][blueprint], user) || !(is_allowed = canPlace(destination)))
+ if(!is_allowed)
+ balloon_alert(user, "turf is blocked!")
+ return FALSE
+ if(!do_after(user, machinery_data["cost"][blueprint], target = destination)) //"cost" is relative to delay at a rate of 10 matter/second (1matter/decisecond) rather than playing with 2 different variables since everyone set it to this rate anyways.
+ return FALSE
+ if(!checkResource(machinery_data["cost"][blueprint], user) || !(is_allowed = canPlace(destination)))
+ if(!is_allowed)
+ balloon_alert(user, "turf is blocked!")
+ return FALSE
+
+ if(!useResource(machinery_data["cost"][blueprint], user))
+ return FALSE
+ activate()
+ playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ if(ispath(blueprint, /obj/machinery/duct))
+ var/is_omni = current_color == DUCT_COLOR_OMNI
+ new blueprint(destination, FALSE, GLOB.pipe_paint_colors[current_color], GLOB.plumbing_layers[current_layer], null, is_omni)
+ else
+ new blueprint(destination, FALSE, GLOB.plumbing_layers[current_layer])
+ return TRUE
+
+/obj/item/construction/plumbing/proc/canPlace(turf/destination)
+ if(!isopenturf(destination))
+ return FALSE
+ if(initial(blueprint.density) && destination.is_blocked_turf(exclude_mobs = FALSE, source_atom = null, ignore_atoms = null))
+ return FALSE
+ . = TRUE
+
+ var/layer_id = GLOB.plumbing_layers[current_layer]
+ for(var/obj/content_obj in destination.contents)
+ // Make sure plumbling isn't overlapping.
+ for(var/datum/component/plumbing/plumber as anything in content_obj.GetComponents(/datum/component/plumbing))
+ if(plumber.ducting_layer & layer_id)
+ return FALSE
+
+ // Make sure ducts aren't overlapping.
+ if(istype(content_obj, /obj/machinery/duct))
+ var/obj/machinery/duct/duct_machine = content_obj
+ if(duct_machine.duct_layer & layer_id)
+ return FALSE
+
+/obj/item/construction/plumbing/pre_attack_secondary(obj/machinery/target, mob/user, params)
+ if(!istype(target, /obj/machinery/duct))
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+ var/obj/machinery/duct/duct = target
+ if(duct.duct_layer && duct.duct_color)
+ current_color = GLOB.pipe_color_name[duct.duct_color]
+ current_layer = GLOB.plumbing_layer_names["[duct.duct_layer]"]
+ balloon_alert(user, "using [current_color], layer [current_layer]")
+
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+/obj/item/construction/plumbing/afterattack(atom/target, mob/user, proximity)
+ . = ..()
+ if(!proximity)
+ return
+ if(istype(target, /obj/machinery/plumbing))
+ var/obj/machinery/machine_target = target
+ if(machine_target.anchored)
+ balloon_alert(user, "anchor first!")
+ return
+ if(do_after(user, 20, target = target))
+ machine_target.deconstruct() //Let's not substract matter
+ playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) //this is just such a great sound effect
+ else
+ create_machine(target, user)
+
+/obj/item/construction/plumbing/AltClick(mob/user)
+ ui_interact(user)
+
+/obj/item/construction/plumbing/proc/mouse_wheeled(mob/source, atom/A, delta_x, delta_y, params)
+ SIGNAL_HANDLER
+ if(source.incapacitated(IGNORE_RESTRAINTS|IGNORE_STASIS))
+ return
+ if(delta_y == 0)
+ return
+
+ if(delta_y < 0)
+ var/current_loc = GLOB.plumbing_layers.Find(current_layer) + 1
+ if(current_loc > GLOB.plumbing_layers.len)
+ current_loc = 1
+ current_layer = GLOB.plumbing_layers[current_loc]
+ else
+ var/current_loc = GLOB.plumbing_layers.Find(current_layer) - 1
+ if(current_loc < 1)
+ current_loc = GLOB.plumbing_layers.len
+ current_layer = GLOB.plumbing_layers[current_loc]
+ to_chat(source, span_notice("You set the layer to [current_layer]."))
+
+/obj/item/construction/plumbing/research
+ name = "research plumbing constructor"
+ desc = "A type of plumbing constructor designed to rapidly deploy the machines needed to conduct cytological research."
+ icon_state = "plumberer_sci"
+ inhand_icon_state = "plumberer_sci"
+ lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
+
+/obj/item/construction/plumbing/research/set_plumbing_designs()
+ plumbing_design_types = list(
+ //category 1 synthesizers
+ /obj/machinery/plumbing/reaction_chamber = 15,
+ /obj/machinery/plumbing/grinder_chemical = 30,
+ /obj/machinery/plumbing/disposer = 10,
+ /obj/machinery/plumbing/growing_vat = 20,
+
+ //category 2 Distributors
+ /obj/machinery/duct = 1,
+ /obj/machinery/plumbing/input = 5,
+ /obj/machinery/plumbing/filter = 5,
+ /obj/machinery/plumbing/splitter = 5,
+ /obj/machinery/plumbing/output = 5,
+
+ //category 3 storage
+ /obj/machinery/plumbing/tank = 20,
+ /obj/machinery/plumbing/acclimator = 10,
+ )
+
+/obj/item/construction/plumbing/service
+ name = "service plumbing constructor"
+ desc = "A type of plumbing constructor designed to rapidly deploy the machines needed to make a brewery."
+ icon_state = "plumberer_service"
+
+/obj/item/construction/plumbing/service/set_plumbing_designs()
+ plumbing_design_types = list(
+ //category 1 synthesizers
+ /obj/machinery/plumbing/synthesizer/soda = 15,
+ /obj/machinery/plumbing/synthesizer/beer = 15,
+ /obj/machinery/plumbing/reaction_chamber = 15,
+ /obj/machinery/plumbing/buffer = 10,
+ /obj/machinery/plumbing/fermenter = 30,
+ /obj/machinery/plumbing/grinder_chemical = 30,
+ /obj/machinery/plumbing/disposer = 10,
+
+
+ //category 2 distributors
+ /obj/machinery/duct = 1,
+ /obj/machinery/plumbing/layer_manifold = 5,
+ /obj/machinery/plumbing/input = 5,
+ /obj/machinery/plumbing/filter = 5,
+ /obj/machinery/plumbing/splitter = 5,
+ /obj/machinery/plumbing/output/tap = 5,
+ /obj/machinery/plumbing/sender = 20,
+
+ //category 3 storage
+ /obj/machinery/plumbing/bottler = 50,
+ /obj/machinery/plumbing/tank = 20,
+ /obj/machinery/plumbing/acclimator = 10,
+ )
diff --git a/code/game/objects/items/RSF.dm b/code/game/objects/items/rcd/RSF.dm
similarity index 100%
rename from code/game/objects/items/RSF.dm
rename to code/game/objects/items/rcd/RSF.dm
diff --git a/code/game/objects/items/RTD.dm b/code/game/objects/items/rcd/RTD.dm
similarity index 83%
rename from code/game/objects/items/RTD.dm
rename to code/game/objects/items/rcd/RTD.dm
index f7b814e9373..5d318ad6e11 100644
--- a/code/game/objects/items/RTD.dm
+++ b/code/game/objects/items/rcd/RTD.dm
@@ -1,7 +1,9 @@
+//RAPID TILING DEVICE
+
/// time taken to create tile
-#define CONSTRUCTION_TIME 0.4 SECONDS
+#define CONSTRUCTION_TIME(cost)((cost * 0.15) SECONDS)
/// time taken to destroy a tile
-#define DECONSTRUCTION_TIME 0.2 SECONDS
+#define DECONSTRUCTION_TIME(cost)((cost * 0.25) SECONDS)
/**
* An tool used to create, destroy, and copy & clear decals of floor tiles
@@ -139,9 +141,7 @@
. = ..()
selected_design = new
tile_design = new
-
selected_design.set_info(GLOB.floor_designs[root_category][design_category][1])
- update_appearance()
/obj/item/construction/rtd/Destroy()
QDEL_NULL(selected_design)
@@ -225,17 +225,13 @@
return TRUE
-/// RTD can lay floor tiles only on these 2 types of platings. this procs checks for that
-/obj/item/construction/rtd/proc/is_valid_plating(turf/open/floor)
- return floor.type == /turf/open/floor/plating || floor.type == /turf/open/floor/plating/reinforced
-
/obj/item/construction/rtd/afterattack(turf/open/floor/floor, mob/user)
. = ..()
if(!istype(floor) || !range_check(floor,user))
return TRUE
var/floor_designs = GLOB.floor_designs
- if(!is_valid_plating(floor)) //we infer what floor type it is if its not the usual plating
+ if(!istype(floor, /turf/open/floor/plating)) //we infer what floor type it is if its not the usual plating
user.Beam(floor, icon_state = "light_beam", time = 5)
for(var/main_root in floor_designs)
for(var/sub_category in floor_designs[main_root])
@@ -267,30 +263,38 @@
balloon_alert(user, "design not supported!")
return TRUE
+ var/delay = CONSTRUCTION_TIME(selected_design.cost)
+ var/obj/effect/constructing_effect/rcd_effect = new(floor, delay, RCD_FLOORWALL)
+
+ //resource sanity check before & after delay along with special effects
if(!checkResource(selected_design.cost, user))
+ qdel(rcd_effect)
+ return TRUE
+ var/beam = user.Beam(floor, icon_state = "light_beam", time = delay)
+ playsound(loc, 'sound/effects/light_flicker.ogg', 50, FALSE)
+ if(!do_after(user, delay, target = floor))
+ qdel(beam)
+ qdel(rcd_effect)
+ return TRUE
+ if(!checkResource(selected_design.cost, user))
+ qdel(rcd_effect)
return TRUE
- //All special effect stuff
- user.Beam(floor, icon_state = "light_beam", time = CONSTRUCTION_TIME)
- var/obj/effect/constructing_effect/rcd_effect = new(floor, CONSTRUCTION_TIME, RCD_FLOORWALL)
- if(!do_after(user, CONSTRUCTION_TIME, target = floor))
- rcd_effect.end_animation()
+ if(!useResource(selected_design.cost, user))
+ qdel(rcd_effect)
return TRUE
-
- //consume resource only if tile was placed successfully
+ activate()
+ //step 1 create tile
var/obj/item/stack/tile/final_tile = selected_design.new_tile(user.drop_location())
if(QDELETED(final_tile)) //if you were standing on a stack of tiles this newly spawned tile could get merged with it cause its spawned on your location
- rcd_effect.end_animation()
+ qdel(rcd_effect)
balloon_alert(user, "tile got merged with the stack beneath you!")
return TRUE
-
+ //step 2 lay tile
var/turf/open/new_turf = final_tile.place_tile(floor, user)
- if(new_turf)
- //apply infered overlays
+ if(new_turf) //apply infered overlays
for(var/datum/overlay_info/info in design_overlays)
info.add_decal(new_turf)
- //use material
- useResource(selected_design.cost, user)
rcd_effect.end_animation()
return TRUE
@@ -300,32 +304,51 @@
if(!istype(floor) || !range_check(floor,user))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
- if(is_valid_plating(floor)) //cant deconstruct normal plating thats the RCD's job
+ if(istype(floor, /turf/open/floor/plating)) //cant deconstruct normal plating thats the RCD's job
balloon_alert(user, "nothing to deconstruct!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
var/floor_designs = GLOB.floor_designs
//we only deconstruct floors which are supported by the RTD
- var/can_deconstruct = FALSE
- var/cost
+ var/cost = 0
for(var/main_root in floor_designs)
- if(can_deconstruct)
+ if(cost)
break
for(var/sub_category in floor_designs[main_root])
- if(can_deconstruct)
+ if(cost)
break
for(var/list/design_info in floor_designs[main_root][sub_category])
var/obj/item/stack/tile/tile_type = design_info["type"]
if(initial(tile_type.turf_type) == floor.type)
- cost = tile_design.cost
- can_deconstruct = TRUE
+ cost = design_info["tile_cost"]
break
- if(!can_deconstruct || !checkResource(cost * 0.7, user)) //no ballon alert for checkResource as it already spans an alert to chat
- if(!can_deconstruct)
- balloon_alert(user, "can't deconstruct this type!")
+ if(!cost)
+ balloon_alert(user, "can't deconstruct this type!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ var/delay = DECONSTRUCTION_TIME(cost)
+ var/obj/effect/constructing_effect/rcd_effect = new(floor, delay, RCD_FLOORWALL)
+
+ //resource sanity check before & after delay along with beam effects
+ if(!checkResource(cost * 0.7, user)) //no ballon alert for checkResource as it already spans an alert to chat
+ qdel(rcd_effect)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ var/beam = user.Beam(floor, icon_state = "light_beam", time = delay)
+ playsound(loc, 'sound/effects/light_flicker.ogg', 50, FALSE)
+ if(!do_after(user, delay, target = floor))
+ qdel(beam)
+ qdel(rcd_effect)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(!checkResource(cost * 0.7, user))
+ qdel(rcd_effect)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+ //do the tiling
+ if(!useResource(cost * 0.7, user))
+ qdel(rcd_effect)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ activate()
//find & collect all decals
var/list/all_decals = list()
for(var/obj/effect/decal in floor.contents)
@@ -334,21 +357,10 @@
for(var/obj/effect/decal in all_decals)
floor.contents -= decal
qdel(decal)
-
- //All special effect stuff
- user.Beam(floor, icon_state = "light_beam", time = DECONSTRUCTION_TIME)
- var/obj/effect/constructing_effect/rcd_effect = new(floor, DECONSTRUCTION_TIME, RCD_FLOORWALL)
- if(!do_after(user, DECONSTRUCTION_TIME, target = floor))
- rcd_effect.end_animation()
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-
- var/turf/new_turf = null
if(floor.baseturf_at_depth(1) == /turf/baseturf_bottom) //for turfs whose base is open space we put regular plating in its place else everyone dies
- new_turf = floor.ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
+ floor.ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else // for every other turf we scarp away exposing base turf underneath
- new_turf = floor.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
- if(new_turf)
- useResource(cost * 0.7, user)
+ floor.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
rcd_effect.end_animation()
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
diff --git a/code/game/objects/items/RWD.dm b/code/game/objects/items/rcd/RWD.dm
similarity index 100%
rename from code/game/objects/items/RWD.dm
rename to code/game/objects/items/rcd/RWD.dm
diff --git a/code/game/objects/items/wall_mounted.dm b/code/game/objects/items/wall_mounted.dm
index 1c14dab427f..3c8d32ac00f 100644
--- a/code/game/objects/items/wall_mounted.dm
+++ b/code/game/objects/items/wall_mounted.dm
@@ -12,20 +12,22 @@
/obj/item/wallframe/proc/try_build(turf/on_wall, mob/user)
if(get_dist(on_wall,user)>1)
+ balloon_alert(user, "you are too far!")
return
var/floor_to_wall = get_dir(user, on_wall)
if(!(floor_to_wall in GLOB.cardinals))
+ balloon_alert(user, "stand in line with wall!")
return
var/turf/T = get_turf(user)
var/area/A = get_area(T)
if(!isfloorturf(T))
- to_chat(user, span_warning("You cannot place [src] on this spot!"))
+ balloon_alert(user, "cannot place here!")
return
if(A.always_unpowered)
- to_chat(user, span_warning("You cannot place [src] in this area!"))
+ balloon_alert(user, "cannot place in this area!")
return
if(check_wall_item(T, floor_to_wall, wall_external))
- to_chat(user, span_warning("There's already an item on this wall!"))
+ balloon_alert(user, "already something here!")
return
return TRUE
diff --git a/code/game/objects/structures/construction_console/construction_console.dm b/code/game/objects/structures/construction_console/construction_console.dm
index 62b8bce221c..5cf4a9bfd85 100644
--- a/code/game/objects/structures/construction_console/construction_console.dm
+++ b/code/game/objects/structures/construction_console/construction_console.dm
@@ -126,5 +126,4 @@
/obj/item/construction/rcd/internal
name = "internal RCD"
max_matter = 600
- no_ammo_message = "Internal matter exhausted. Please add additional materials."
delay_mod = 0.5
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 1d0f76007ad..bb1ded6e214 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -61,12 +61,22 @@
if(RCD_DECONSTRUCT)
return list("mode" = RCD_DECONSTRUCT, "delay" = 20, "cost" = 5)
if(RCD_WINDOWGRILLE)
- var/cost = 8
- var/delay = 2 SECONDS
-
- if(the_rcd.window_glass == RCD_WINDOW_REINFORCED)
- delay = 4 SECONDS
+ var/cost = 0
+ var/delay = 0
+ if(the_rcd.window_type == /obj/structure/window)
+ cost = 6
+ delay = 2 SECONDS
+ else if(the_rcd.window_type == /obj/structure/window/reinforced)
+ cost = 9
+ delay = 2.5 SECONDS
+ else if(the_rcd.window_type == /obj/structure/window/fulltile)
cost = 12
+ delay = 3 SECONDS
+ else if(the_rcd.window_type == /obj/structure/window/reinforced/fulltile)
+ cost = 15
+ delay = 4 SECONDS
+ if(!cost)
+ return FALSE
return rcd_result_with_memory(
list("mode" = RCD_WINDOWGRILLE, "delay" = delay, "cost" = cost),
@@ -86,18 +96,17 @@
var/turf/T = loc
if(repair_grille())
- to_chat(user, span_notice("You rebuild the broken grille."))
+ balloon_alert(user, "grille rebuilt")
if(!clear_tile(user))
return FALSE
- if(!ispath(the_rcd.window_type, /obj/structure/window))
- CRASH("Invalid window path type in RCD: [the_rcd.window_type]")
var/obj/structure/window/window_path = the_rcd.window_type
+ if(!ispath(window_path))
+ CRASH("Invalid window path type in RCD: [window_path]")
if(!valid_window_location(T, user.dir, is_fulltile = initial(window_path.fulltile)))
- to_chat(user, span_notice("Already a window in this direction!"))
+ balloon_alert(user, "window already here!")
return FALSE
- to_chat(user, span_notice("You construct the window."))
var/obj/structure/window/WD = new the_rcd.window_type(T, user.dir)
WD.set_anchored(TRUE)
return TRUE
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index c4ec6a3d196..65898b81358 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -96,7 +96,6 @@
/obj/structure/window/rcd_act(mob/user, obj/item/construction/rcd/the_rcd)
switch(the_rcd.mode)
if(RCD_DECONSTRUCT)
- to_chat(user, span_notice("You deconstruct the window."))
qdel(src)
return TRUE
return FALSE
@@ -477,6 +476,12 @@
fire = 80
acid = 100
+/obj/structure/window/reinforced/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
+ switch(the_rcd.mode)
+ if(RCD_DECONSTRUCT)
+ return list("mode" = RCD_DECONSTRUCT, "delay" = 30, "cost" = 15)
+ return FALSE
+
/obj/structure/window/reinforced/attackby_secondary(obj/item/tool, mob/user, params)
switch(state)
if(RWINDOW_SECURE)
@@ -675,6 +680,12 @@
canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
glass_amount = 2
+/obj/structure/window/fulltile/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
+ switch(the_rcd.mode)
+ if(RCD_DECONSTRUCT)
+ return list("mode" = RCD_DECONSTRUCT, "delay" = 25, "cost" = 10)
+ return FALSE
+
/obj/structure/window/fulltile/unanchored
anchored = FALSE
@@ -723,6 +734,12 @@
canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
glass_amount = 2
+/obj/structure/window/reinforced/fulltile/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
+ switch(the_rcd.mode)
+ if(RCD_DECONSTRUCT)
+ return list("mode" = RCD_DECONSTRUCT, "delay" = 40, "cost" = 20)
+ return FALSE
+
/obj/structure/window/reinforced/fulltile/unanchored
anchored = FALSE
state = WINDOW_OUT_OF_FRAME
diff --git a/code/game/turfs/open/floor.dm b/code/game/turfs/open/floor.dm
index 0a9d77dbde3..44232b5932a 100644
--- a/code/game/turfs/open/floor.dm
+++ b/code/game/turfs/open/floor.dm
@@ -243,6 +243,7 @@
/turf/open/floor/acid_melt()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
+/// if you are updating this make to to update /turf/open/misc/rcd_vals() too
/turf/open/floor/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
switch(the_rcd.mode)
if(RCD_FLOORWALL)
@@ -274,9 +275,35 @@
if(RCD_FLOODLIGHT)
return list("mode" = RCD_FLOODLIGHT, "delay" = 30, "cost" = 35)
if(RCD_FURNISHING)
- return list("mode" = RCD_FURNISHING, "delay" = the_rcd.furnish_delay, "cost" = the_rcd.furnish_cost)
+ var/cost = 0
+ var/delay = 0
+ if(the_rcd.furnish_type == /obj/structure/chair || the_rcd.furnish_type == /obj/structure/chair/stool)
+ cost = 8
+ delay = 10
+ else if(the_rcd.furnish_type == /obj/structure/chair/stool/bar)
+ cost = 4
+ delay = 5
+ else if(the_rcd.furnish_type == /obj/structure/chair/stool/bar)
+ cost = 4
+ delay = 5
+ else if(the_rcd.furnish_type == /obj/structure/table)
+ cost = 15
+ delay = 20
+ else if(the_rcd.furnish_type == /obj/structure/table/glass)
+ cost = 12
+ delay = 15
+ else if(the_rcd.furnish_type == /obj/structure/rack)
+ cost = 20
+ delay = 25
+ else if(the_rcd.furnish_type == /obj/structure/bed)
+ cost = 10
+ delay = 15
+ if(cost == 0)
+ return FALSE
+ return list("mode" = RCD_FURNISHING, "delay" = cost, "cost" = delay)
return FALSE
+/// if you are updating this make to to update /turf/open/misc/rcd_act() too
/turf/open/floor/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
switch(passed_mode)
if(RCD_FLOORWALL)
@@ -284,7 +311,6 @@
if(girder)
return girder.rcd_act(user, the_rcd, passed_mode)
- to_chat(user, span_notice("You build a wall."))
PlaceOnTop(/turf/closed/wall)
return TRUE
if(RCD_REFLECTOR)
@@ -303,7 +329,6 @@
continue
balloon_alert(user, "there's already a door!")
return FALSE
- to_chat(user, span_notice("You build a windoor."))
var/obj/machinery/door/window/new_window = new the_rcd.airlock_type(src, user.dir, the_rcd.airlock_electronics?.unres_sides)
if(the_rcd.airlock_electronics)
new_window.name = the_rcd.airlock_electronics.passed_name || initial(new_window.name)
@@ -348,16 +373,12 @@
if(rcd_proof)
balloon_alert(user, "it's too thick!")
return FALSE
- else
- var/old_turf_name = name
- if(!ScrapeAway(flags = CHANGETURF_INHERIT_AIR))
- return FALSE
- to_chat(user, span_notice("You deconstruct the [old_turf_name]."))
- return TRUE
+ if(!ScrapeAway(flags = CHANGETURF_INHERIT_AIR))
+ return FALSE
+ return TRUE
if(RCD_WINDOWGRILLE)
if(locate(/obj/structure/grille) in src)
return FALSE
- to_chat(user, span_notice("You construct the grille."))
var/obj/structure/grille/new_grille = new(src)
new_grille.set_anchored(TRUE)
return TRUE
diff --git a/code/game/turfs/open/misc.dm b/code/game/turfs/open/misc.dm
index 29956cd332d..0cc844f4fe8 100644
--- a/code/game/turfs/open/misc.dm
+++ b/code/game/turfs/open/misc.dm
@@ -83,7 +83,8 @@
return list("mode" = RCD_FLOORWALL, "delay" = 0, "cost" = 1)
else
return list("mode" = RCD_FLOORWALL, "delay" = 0, "cost" = 3)
-
+ if(RCD_REFLECTOR)
+ return list("mode" = RCD_REFLECTOR, "delay" = 20, "cost" = 30)
if(RCD_AIRLOCK)
if(the_rcd.airlock_glass)
return list("mode" = RCD_AIRLOCK, "delay" = 50, "cost" = 20)
@@ -91,31 +92,65 @@
return list("mode" = RCD_AIRLOCK, "delay" = 50, "cost" = 16)
if(RCD_WINDOWGRILLE)
return rcd_result_with_memory(
- list("mode" = RCD_WINDOWGRILLE, "delay" = 2 SECONDS, "cost" = 6),
+ list("mode" = RCD_WINDOWGRILLE, "delay" = 1 SECONDS, "cost" = 4),
src, RCD_MEMORY_WINDOWGRILLE,
)
if(RCD_MACHINE)
- return list("mode" = RCD_MACHINE, "delay" = 2 SECONDS, "cost" = 25)
+ return list("mode" = RCD_MACHINE, "delay" = 20, "cost" = 25)
if(RCD_COMPUTER)
- return list("mode" = RCD_COMPUTER, "delay" = 2 SECONDS, "cost" = 25)
+ return list("mode" = RCD_COMPUTER, "delay" = 20, "cost" = 25)
+ if(RCD_FLOODLIGHT)
+ return list("mode" = RCD_FLOODLIGHT, "delay" = 30, "cost" = 35)
if(RCD_FURNISHING)
- return list("mode" = RCD_FURNISHING, "delay" = the_rcd.furnish_delay, "cost" = the_rcd.furnish_cost)
+ var/cost = 0
+ var/delay = 0
+ if(the_rcd.furnish_type == /obj/structure/chair || the_rcd.furnish_type == /obj/structure/chair/stool)
+ cost = 8
+ delay = 10
+ else if(the_rcd.furnish_type == /obj/structure/chair/stool/bar)
+ cost = 4
+ delay = 5
+ else if(the_rcd.furnish_type == /obj/structure/chair/stool/bar)
+ cost = 4
+ delay = 5
+ else if(the_rcd.furnish_type == /obj/structure/table)
+ cost = 15
+ delay = 20
+ else if(the_rcd.furnish_type == /obj/structure/table/glass)
+ cost = 12
+ delay = 15
+ else if(the_rcd.furnish_type == /obj/structure/rack)
+ cost = 20
+ delay = 25
+ else if(the_rcd.furnish_type == /obj/structure/bed)
+ cost = 10
+ delay = 15
+ if(!cost)
+ return FALSE
+ return list("mode" = RCD_FURNISHING, "delay" = cost, "cost" = delay)
return FALSE
/turf/open/misc/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
switch(passed_mode)
if(RCD_FLOORWALL)
- to_chat(user, span_notice("You build a floor."))
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
- if(RCD_AIRLOCK) // I'm sorry
- for(var/obj/machinery/door/door in src)
- if(door.sub_door)
- continue
- to_chat(user, span_notice("There is another door here!"))
+ if(RCD_REFLECTOR)
+ if(locate(/obj/structure/reflector) in src)
return FALSE
+ var/obj/structure/reflector/reflector_base = new(src)
+ reflector_base.set_anchored(TRUE)
+ return TRUE
+ if(RCD_AIRLOCK)
if(ispath(the_rcd.airlock_type, /obj/machinery/door/window))
- to_chat(user, span_notice("You build a windoor."))
+ if(!valid_window_location(src, user.dir, is_fulltile = FALSE))
+ balloon_alert(user, "there's already a windoor!")
+ return FALSE
+ for(var/obj/machinery/door/door in src)
+ if(istype(door, /obj/machinery/door/window))
+ continue
+ balloon_alert(user, "there's already a door!")
+ return FALSE
var/obj/machinery/door/window/new_window = new the_rcd.airlock_type(src, user.dir, the_rcd.airlock_electronics?.unres_sides)
if(the_rcd.airlock_electronics)
new_window.name = the_rcd.airlock_electronics.passed_name || initial(new_window.name)
@@ -126,7 +161,12 @@
new_window.autoclose = TRUE
new_window.update_appearance()
return TRUE
- to_chat(user, span_notice("You build an airlock."))
+
+ for(var/obj/machinery/door/door in src)
+ if(door.sub_door)
+ continue
+ balloon_alert(user, "there's already a door!")
+ return FALSE
var/obj/machinery/door/airlock/new_airlock = new the_rcd.airlock_type(src)
new_airlock.electronics = new /obj/item/electronics/airlock(new_airlock)
if(the_rcd.airlock_electronics)
@@ -135,6 +175,7 @@
new_airlock.electronics.unres_sides = the_rcd.airlock_electronics.unres_sides
new_airlock.electronics.passed_name = the_rcd.airlock_electronics.passed_name
new_airlock.electronics.passed_cycle_id = the_rcd.airlock_electronics.passed_cycle_id
+ new_airlock.electronics.shell = the_rcd.airlock_electronics.shell
if(new_airlock.electronics.one_access)
new_airlock.req_one_access = new_airlock.electronics.accesses
else
@@ -153,7 +194,6 @@
if(RCD_WINDOWGRILLE)
if(locate(/obj/structure/grille) in src)
return FALSE
- to_chat(user, span_notice("You construct the grille."))
var/obj/structure/grille/new_grille = new(src)
new_grille.set_anchored(TRUE)
return TRUE
@@ -173,6 +213,15 @@
new_computer.state = 1
new_computer.setDir(the_rcd.computer_dir)
return TRUE
+ if(RCD_FLOODLIGHT)
+ if(locate(/obj/structure/floodlight_frame) in src)
+ return FALSE
+ var/obj/structure/floodlight_frame/new_floodlight = new(src)
+ new_floodlight.name = "secured [new_floodlight.name]"
+ new_floodlight.desc = "A bare metal frame that looks like a floodlight. Requires a light tube to complete."
+ new_floodlight.icon_state = "floodlight_c3"
+ new_floodlight.state = FLOODLIGHT_NEEDS_LIGHTS
+ return TRUE
if(RCD_FURNISHING)
if(locate(the_rcd.furnish_type) in src)
return FALSE
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index c29d23a2452..72242dcef80 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -253,15 +253,23 @@ GLOBAL_LIST_EMPTY(station_turfs)
* * exclude_mobs - If TRUE, ignores dense mobs on the turf.
* * source_atom - If this is not null, will check whether any contents on the turf can block this atom specifically. Also ignores itself on the turf.
* * ignore_atoms - Check will ignore any atoms in this list. Useful to prevent an atom from blocking itself on the turf.
+ * * type_list - are we checking for types of atoms to ignore and not physical atoms
*/
-/turf/proc/is_blocked_turf(exclude_mobs = FALSE, source_atom = null, list/ignore_atoms)
+/turf/proc/is_blocked_turf(exclude_mobs = FALSE, source_atom = null, list/ignore_atoms, type_list = FALSE)
if(density)
return TRUE
for(var/atom/movable/movable_content as anything in contents)
- // We don't want to block ourselves or consider any ignored atoms.
- if((movable_content == source_atom) || (movable_content in ignore_atoms))
+ // We don't want to block ourselves
+ if((movable_content == source_atom))
continue
+ // dont consider ignored atoms or their types
+ if(length(ignore_atoms))
+ if(!type_list && (movable_content in ignore_atoms))
+ continue
+ else if(type_list && is_type_in_list(movable_content, ignore_atoms))
+ continue
+
// If the thing is dense AND we're including mobs or the thing isn't a mob AND if there's a source atom and
// it cannot pass through the thing on the turf, we consider the turf blocked.
if(movable_content.density && (!exclude_mobs || !ismob(movable_content)))
diff --git a/code/modules/power/pipecleaners.dm b/code/modules/power/pipecleaners.dm
index 8ead56c9fda..bb37f8b29a2 100644
--- a/code/modules/power/pipecleaners.dm
+++ b/code/modules/power/pipecleaners.dm
@@ -88,18 +88,12 @@ By design, d1 is the smallest direction and d2 is the highest
else
stored = new/obj/item/stack/pipe_cleaner_coil(null, 1, null, null, null)
- if(param_color)
- color = GLOB.cable_colors[param_color]
- pipecleaner_color = param_color
-
- if(!color)
- var/list/pipe_cleaner_colors = GLOB.cable_colors
- var/random_color = pick(pipe_cleaner_colors)
- color = pipe_cleaner_colors[random_color]
- pipecleaner_color = random_color
-
- update_appearance()
+ if(!param_color)
+ param_color = "white"
+ color = GLOB.cable_colors[param_color]
+ pipecleaner_color = param_color
stored?.set_pipecleaner_color(pipecleaner_color)
+ update_appearance()
if(isturf(loc))
var/turf/turf_loc = loc
diff --git a/icons/obj/tools.dmi b/icons/obj/tools.dmi
index 6ea515a37de..fab7e7e5f45 100644
Binary files a/icons/obj/tools.dmi and b/icons/obj/tools.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 5a7b42669fe..64b5733ee23 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1827,13 +1827,7 @@
#include "code\game\objects\items\pneumaticCannon.dm"
#include "code\game\objects\items\powerfist.dm"
#include "code\game\objects\items\puzzle_pieces.dm"
-#include "code\game\objects\items\RCD.dm"
-#include "code\game\objects\items\RCL.dm"
#include "code\game\objects\items\religion.dm"
-#include "code\game\objects\items\RPD.dm"
-#include "code\game\objects\items\RSF.dm"
-#include "code\game\objects\items\RTD.dm"
-#include "code\game\objects\items\RWD.dm"
#include "code\game\objects\items\scrolls.dm"
#include "code\game\objects\items\secret_documents.dm"
#include "code\game\objects\items\sharpener.dm"
@@ -2005,6 +1999,15 @@
#include "code\game\objects\items\melee\baton.dm"
#include "code\game\objects\items\melee\energy.dm"
#include "code\game\objects\items\melee\misc.dm"
+#include "code\game\objects\items\rcd\RCD.dm"
+#include "code\game\objects\items\rcd\RCL.dm"
+#include "code\game\objects\items\rcd\RHD.dm"
+#include "code\game\objects\items\rcd\RLD.dm"
+#include "code\game\objects\items\rcd\RPD.dm"
+#include "code\game\objects\items\rcd\RPLD.dm"
+#include "code\game\objects\items\rcd\RSF.dm"
+#include "code\game\objects\items\rcd\RTD.dm"
+#include "code\game\objects\items\rcd\RWD.dm"
#include "code\game\objects\items\robot\ai_upgrades.dm"
#include "code\game\objects\items\robot\robot_parts.dm"
#include "code\game\objects\items\robot\robot_upgrades.dm"