diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 811937deb13..210b3e8cacb 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -284,12 +284,6 @@
/datum/config_entry/flag/maprotation
-/datum/config_entry/number/maprotatechancedelta
- config_entry_value = 0.75
- min_val = 0
- max_val = 1
- integer = FALSE
-
/datum/config_entry/number/soft_popcap
config_entry_value = null
min_val = 0
diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm
index 2bcd2ad4f3e..62ff1f78950 100644
--- a/code/modules/photography/camera/camera.dm
+++ b/code/modules/photography/camera/camera.dm
@@ -180,8 +180,14 @@
var/list/mobs = list()
var/blueprints = FALSE
var/clone_area = SSmapping.RequestBlockReservation(size_x * 2 + 1, size_y * 2 + 1)
- for(var/turf/T in block(locate(target_turf.x - size_x, target_turf.y - size_y, target_turf.z), locate(target_turf.x + size_x, target_turf.y + size_y, target_turf.z)))
- if((ai_user && GLOB.cameranet.checkTurfVis(T)) || (T in seen))
+ for(var/turf/placeholder in block(locate(target_turf.x - size_x, target_turf.y - size_y, target_turf.z), locate(target_turf.x + size_x, target_turf.y + size_y, target_turf.z)))
+ var/turf/T = placeholder
+ while(istype(T, /turf/open/openspace)) //Multi-z photography
+ T = SSmapping.get_turf_below(T)
+ if(!T)
+ break
+
+ if(T && ((ai_user && GLOB.cameranet.checkTurfVis(placeholder)) || (placeholder in seen)))
turfs += T
for(var/mob/M in T)
mobs += M
diff --git a/code/modules/power/multiz.dm b/code/modules/power/multiz.dm
index d5f28462930..6ad239d3b88 100644
--- a/code/modules/power/multiz.dm
+++ b/code/modules/power/multiz.dm
@@ -1,42 +1,106 @@
+#define RELAY_OK 1
+#define RELAY_ADD_CABLE 2
+#define RELAY_ADD_METAL 3
+
/obj/machinery/power/deck_relay //This bridges powernets
name = "Multi-deck power adapter"
desc = "A huge bundle of double insulated cabling which seems to run up into the ceiling."
icon = 'icons/obj/power.dmi'
icon_state = "cablerelay-off"
+ max_integrity = 350
+ integrity_failure = 0.25
+ var/broken_status = RELAY_OK
var/obj/machinery/power/deck_relay/below ///The relay that's below us (for bridging powernets)
var/obj/machinery/power/deck_relay/above ///The relay that's above us (for bridging powernets)
anchored = TRUE
density = FALSE
-/obj/machinery/power/deck_relay/attackby(obj/item/I,mob/user)
+/obj/machinery/power/deck_relay/examine(mob/user)
+ . += ..()
+ if(!anchored)
+ . += "The securing bolts are undone."
+ if(broken_status == RELAY_ADD_CABLE)
+ . += "The cable insulation is torn apart and the wires are frayed beyond use."
+ if(broken_status == RELAY_ADD_METAL)
+ . += "The cable insulation is torn apart and the wiring is exposed."
+
+/obj/machinery/power/deck_relay/attackby(obj/item/I, mob/user, params)
if(default_unfasten_wrench(user, I))
+ if(!anchored && broken_status == RELAY_OK)
+ break_connections()
+ return
return FALSE
+ . = ..()
+ if(istype(I, /obj/item/stack/cable_coil) && broken_status == RELAY_ADD_CABLE)
+ var/obj/item/stack/C = I
+ if(C.use(15))
+ to_chat(user, "You fix the frayed wires inside [src].")
+ icon_state = "cablerelay-broken-cable"
+ broken_status = RELAY_ADD_METAL
+ return
+ else
+ to_chat(user, "You need 15 cables to rewire [src].")
+ return
+ if(istype(I, /obj/item/stack/sheet/metal) && broken_status == RELAY_ADD_METAL)
+ var/obj/item/stack/S = I
+ if(S.use(10))
+ to_chat(user, "You reseal the insulation for [src].")
+ icon_state = "cablerelay"
+ broken_status = RELAY_OK
+ obj_integrity = max_integrity
+ return
+ else
+ to_chat(user, "You need 10 metal to mend [src].")
+ return
. = ..()
-/obj/machinery/power/deck_relay/process()
- if(!anchored)
- icon_state = "cablerelay-off"
- if(above) //Lose connections
- above.below = null
- if(below)
- below.above = null
- return
- refresh() //Sometimes the powernets get lost, so we need to keep checking.
- if(powernet && (powernet.avail <= 0)) // is it powered?
- icon_state = "cablerelay-off"
- else
- icon_state = "cablerelay-on"
- if(!below || QDELETED(below) || !above || QDELETED(above))
- icon_state = "cablerelay-off"
- find_relays()
+/obj/machinery/power/deck_relay/obj_break()
+ ..()
+ if(broken_status == RELAY_OK)
+ break_connections()
+ visible_message("[src]'s insulation breaks, fraying and severing the cable bundle!")
+ playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE)
+ icon_state = "cablerelay-broken"
+ broken_status = RELAY_ADD_CABLE
-///Allows you to scan the relay with a multitool to see stats.
+/obj/machinery/power/deck_relay/obj_destruction()
+ return //this shouldn't break under usual means
+
+/obj/machinery/power/deck_relay/Destroy()
+ break_connections()
+ return ..()
+
+///Lose connections and reset the merged powernet so it makes 2 new seperated ones
+/obj/machinery/power/deck_relay/proc/break_connections()
+ if(above)
+ var/turf/above_deck_relay = get_turf(above)
+ var/obj/structure/cable/above_cable = above_deck_relay.get_cable_node()
+ if(above_cable)
+ var/datum/powernet/above_powernet = new()
+ propagate_network(above_cable, above_powernet)
+ above.below = null
+ above = null
+ if(below)
+ var/turf/below_deck_relay = get_turf(below)
+ var/obj/structure/cable/below_cable = below_deck_relay.get_cable_node()
+ if(below_cable)
+ var/datum/powernet/below_powernet = new()
+ propagate_network(below_cable, below_powernet)
+ below.above = null
+ below = null
+
+///Allows you to scan the relay with a multitool to see stats/reconnect relays
/obj/machinery/power/deck_relay/multitool_act(mob/user, obj/item/I)
- if(powernet && (powernet.avail > 0)) // is it powered?
+ if(!anchored)
+ to_chat(user, "You need to wrench this into place before getting a reading!")
+ return TRUE
+ if(broken_status == RELAY_ADD_CABLE || broken_status == RELAY_ADD_METAL)
+ to_chat(user, "The [src] isn't in proper shape to get a reading!")
+ return TRUE
+ if(powernet && (above || below))//we have a powernet and at least one connected relay
to_chat(user, "Total power: [DisplayPower(powernet.avail)]\nLoad: [DisplayPower(powernet.load)]\nExcess power: [DisplayPower(surplus())]")
- if(!powernet || below.powernet != powernet)
- icon_state = "cablerelay-off"
- to_chat(user, "Powernet connection lost. Attempting to re-establish. Ensure the relays below this one are connected too.")
+ if(!above && !below)
+ to_chat(user, "Cannot access valid powernet. Attempting to re-establish. Ensure any relays above and below are aligned properly and on cable nodes.")
find_relays()
addtimer(CALLBACK(src, .proc/refresh), 20) //Wait a bit so we can find the one below, then get powering
return TRUE
@@ -53,6 +117,7 @@
if(below)
below.merge(src)
+///Merges the two powernets connected to the deck relays
/obj/machinery/power/deck_relay/proc/merge(var/obj/machinery/power/deck_relay/DR)
if(!DR)
return
@@ -69,6 +134,7 @@
if(!T || !istype(T))
return FALSE
below = null //in case we're re-establishing
+ above = null
var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked
if(C && C.powernet)
C.powernet.add_machine(src) //Nice we're in.
diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm
index 5b75c55f492..f0ef67dd92b 100644
--- a/code/modules/power/solar.dm
+++ b/code/modules/power/solar.dm
@@ -213,6 +213,16 @@
anchored = FALSE
var/tracker = 0
var/glass_type = null
+ var/random_offset = 6 //amount in pixels an unanchored assembly may be offset by
+
+/obj/item/solar_assembly/Initialize(mapload)
+ . = ..()
+ if(!anchored && !pixel_x && !pixel_y)
+ randomise_offset(random_offset)
+
+/obj/item/solar_assembly/proc/randomise_offset(amount)
+ pixel_x = rand(-amount,amount)
+ pixel_y = rand(-amount,amount)
// Give back the glass type we were supplied with
/obj/item/solar_assembly/proc/give_glass(device_broken)
@@ -234,9 +244,12 @@
if(anchored)
user.visible_message("[user] wrenches the solar assembly into place.", "You wrench the solar assembly into place.")
W.play_tool_sound(src, 75)
+ pixel_x = 0
+ pixel_y = 0
else
user.visible_message("[user] unwrenches the solar assembly from its place.", "You unwrench the solar assembly from its place.")
W.play_tool_sound(src, 75)
+ randomise_offset(random_offset)
return 1
if(istype(W, /obj/item/stack/sheet/glass) || istype(W, /obj/item/stack/sheet/rglass))
diff --git a/config/config.txt b/config/config.txt
index 4cd17022a6a..d61d9b0f4a6 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -381,12 +381,6 @@ MAPROTATION
## When it's set to zero, the map will be randomly picked each round
PREFERENCE_MAP_VOTING 1
-## Map rotate chance delta
-## This is the chance of map rotation factored to the round length.
-## A value of 1 would mean the map rotation chance is the round length in minutes (hour long round == 60% rotation chance)
-## A value of 0.5 would mean the map rotation chance is half of the round length in minutes (hour long round == 30% rotation chance)
-#MAPROTATIONCHANCEDELTA 0.75
-
## AUTOADMIN
## The default admin rank
AUTOADMIN_RANK Game Master
diff --git a/html/changelogs/AutoChangeLog-pr-49053.yml b/html/changelogs/AutoChangeLog-pr-49053.yml
new file mode 100644
index 00000000000..bfaf79b6e63
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-49053.yml
@@ -0,0 +1,4 @@
+author: "MMMiracles"
+delete-after: True
+changes:
+ - tweak: "Multi-Z power relays can repaired but must be manually updated with a multi-tool when reinstalling/moving around."
diff --git a/html/changelogs/AutoChangeLog-pr-49287.yml b/html/changelogs/AutoChangeLog-pr-49287.yml
new file mode 100644
index 00000000000..e1899959cd5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-49287.yml
@@ -0,0 +1,4 @@
+author: "ike709"
+delete-after: True
+changes:
+ - bugfix: "Taking pictures of openspace turfs works now."
diff --git a/html/changelogs/AutoChangeLog-pr-49293.yml b/html/changelogs/AutoChangeLog-pr-49293.yml
new file mode 100644
index 00000000000..2b5cdd1543e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-49293.yml
@@ -0,0 +1,4 @@
+author: "Skoglol"
+delete-after: True
+changes:
+ - bugfix: "Contractor tablet should now properly let you shop for rep."
diff --git a/html/changelogs/AutoChangeLog-pr-49303.yml b/html/changelogs/AutoChangeLog-pr-49303.yml
new file mode 100644
index 00000000000..3d9494094e3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-49303.yml
@@ -0,0 +1,4 @@
+author: "cacogen"
+delete-after: True
+changes:
+ - tweak: "Unanchored solar assemblies go off-centre to indicate they need securing. Secured ones centre themselves"
diff --git a/icons/obj/power.dmi b/icons/obj/power.dmi
index 9175252cd39..615621fcce3 100644
Binary files a/icons/obj/power.dmi and b/icons/obj/power.dmi differ
diff --git a/tgui-next/packages/tgui/interfaces/SyndContractor.js b/tgui-next/packages/tgui/interfaces/SyndContractor.js
index 23f152a8319..2c9a9b866ce 100644
--- a/tgui-next/packages/tgui/interfaces/SyndContractor.js
+++ b/tgui-next/packages/tgui/interfaces/SyndContractor.js
@@ -350,7 +350,7 @@ export const SyndPane = props => {
)}