diff --git a/code/datums/elements/bed_tucking.dm b/code/datums/elements/bed_tucking.dm
index c094e5a5b108e..e505e6efdb5a2 100644
--- a/code/datums/elements/bed_tucking.dm
+++ b/code/datums/elements/bed_tucking.dm
@@ -8,8 +8,13 @@
var/y_offset = 0
/// our rotation degree - how much the item turns when in bed (+degrees turns it more parallel)
var/rotation_degree = 0
+ /// Whether the item changes its dir to match the desired lying direction of the bed that it's tucked into.
+ var/change_dir = FALSE
+ /// Whether the item changes its layer to the layer suggested by the bed for tucked-in item.
+ /// When the item is untucked, it is returned to its initial() layer.
+ var/change_layer = FALSE
-/datum/element/bed_tuckable/Attach(obj/target, x = 0, y = 0, rotation = 0)
+/datum/element/bed_tuckable/Attach(obj/target, x = 0, y = 0, rotation = 0, _change_dir = FALSE, _change_layer = FALSE)
. = ..()
if(!isitem(target))
return ELEMENT_INCOMPATIBLE
@@ -17,6 +22,8 @@
x_offset = x
y_offset = y
rotation_degree = rotation
+ change_dir = _change_dir
+ change_layer = _change_layer
RegisterSignal(target, COMSIG_ITEM_ATTACK_OBJ, PROC_REF(tuck_into_bed))
/datum/element/bed_tuckable/Detach(obj/target)
@@ -40,11 +47,20 @@
return
to_chat(tucker, "You lay [tucked] out on [target_bed].")
- tucked.pixel_x = x_offset
- tucked.pixel_y = y_offset
+ tucked.pixel_x = x_offset + target_bed.tucked_x_shift
+ tucked.pixel_y = y_offset + target_bed.tucked_y_shift
if(rotation_degree)
tucked.transform = turn(tucked.transform, rotation_degree)
RegisterSignal(tucked, COMSIG_ITEM_PICKUP, PROC_REF(untuck))
+ // the buckle_lying value on the bed controls the direction that mobs lay down in when they're buckled into bed.
+ // some items (bedsheets) have different states to reflect those directions.
+ if(change_dir)
+ if(target_bed.buckle_lying == 270)
+ tucked.setDir(NORTH)
+ else
+ tucked.setDir(SOUTH)
+ if(target_bed.suggested_tuck_layer != null)
+ tucked.layer = target_bed.suggested_tuck_layer
return COMPONENT_NO_AFTERATTACK
@@ -57,4 +73,5 @@
SIGNAL_HANDLER
tucked.transform = turn(tucked.transform, -rotation_degree)
+ tucked.layer = initial(tucked.layer)
UnregisterSignal(tucked, COMSIG_ITEM_PICKUP)
diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm
index 63e4ffc5e6c34..4b16ee60942dd 100644
--- a/code/game/objects/items/plushes.dm
+++ b/code/game/objects/items/plushes.dm
@@ -38,7 +38,7 @@
. = ..()
if(should_squeak)
AddComponent(/datum/component/squeak, squeak_override)
- AddElement(/datum/element/bed_tuckable, 6, -5, 90)
+ AddElement(/datum/element/bed_tuckable, 6, -5, 90, FALSE, FALSE)
//have we decided if Pinocchio goes in the blue or pink aisle yet?
if(gender == NEUTER)
diff --git a/code/game/objects/items/stacks/sheets/recipes/recipes_metal.dm b/code/game/objects/items/stacks/sheets/recipes/recipes_metal.dm
index 6860f34be5c74..9819a941e5f88 100644
--- a/code/game/objects/items/stacks/sheets/recipes/recipes_metal.dm
+++ b/code/game/objects/items/stacks/sheets/recipes/recipes_metal.dm
@@ -1,9 +1,13 @@
GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("stool", /obj/structure/chair/stool, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("bar stool", /obj/structure/chair/stool/bar, one_per_turf = TRUE, on_floor = TRUE), \
- new/datum/stack_recipe("bed", /obj/structure/bed, 2, one_per_turf = TRUE, on_floor = TRUE), \
- new/datum/stack_recipe("double bed", /obj/structure/bed/double, 4, one_per_turf = TRUE, on_floor = TRUE), \
null, \
+ new/datum/stack_recipe_list("beds", list( \
+ new/datum/stack_recipe("bed", /obj/structure/bed, 2, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("double bed", /obj/structure/bed/double, 4, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("bottom bunk", /obj/structure/bed/bunk, 2, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("top bunk", /obj/structure/bed/bunk/top, 2, one_per_turf = TRUE, on_floor = TRUE), \
+ )), \
new/datum/stack_recipe_list("office chairs", list( \
new/datum/stack_recipe("gray office chair", /obj/structure/chair/office, 5, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("light office chair", /obj/structure/chair/office/light, 5, one_per_turf = TRUE, on_floor = TRUE), \
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 5653b641c99dd..f34836de156f7 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -305,7 +305,7 @@
if(!window_structure.fulltile)
continue
if(object.density)
- to_chat(usr, "There is \a [object.name] here. You cant make \a [recipe.title] here!")
+ to_chat(usr, "There is \a [object.name] here. You can't make \a [recipe.title] here!")
return FALSE
if(recipe.placement_checks)
switch(recipe.placement_checks)
diff --git a/code/game/objects/structures/beds_chairs/alien_nest.dm b/code/game/objects/structures/beds_chairs/alien_nest.dm
index 4f132b11af997..ecc050aa8f772 100644
--- a/code/game/objects/structures/beds_chairs/alien_nest.dm
+++ b/code/game/objects/structures/beds_chairs/alien_nest.dm
@@ -14,6 +14,7 @@
buildstacktype = null
flags_1 = NODECONSTRUCT_1
bolts = FALSE
+ swap_lying_with_dir = FALSE
var/static/mutable_appearance/nest_overlay = mutable_appearance('icons/mob/alien.dmi', "nestoverlay", LYING_MOB_LAYER)
/obj/structure/bed/nest/user_unbuckle_mob(mob/living/buckled_mob, mob/living/user)
diff --git a/code/game/objects/structures/beds_chairs/bed.dm b/code/game/objects/structures/beds_chairs/bed.dm
index 6c5f46e94a3b1..ec7132a67cdd1 100644
--- a/code/game/objects/structures/beds_chairs/bed.dm
+++ b/code/game/objects/structures/beds_chairs/bed.dm
@@ -18,10 +18,44 @@
resistance_flags = FLAMMABLE
max_integrity = 100
integrity_failure = 0.35
+
var/buildstacktype = /obj/item/stack/sheet/metal
var/buildstackamount = 2
var/bolts = TRUE
+ /// Whether the bed changes its buckle_lying direction
+ /// (and accordingly the direction in which mobs lie down) based on its current direction.
+ var/swap_lying_with_dir = TRUE
+ /// If non-null, some items (bedsheets) which can be tucked into beds
+ /// will set their layer to this value when they are tucked in, until they are picked up again.
+ var/suggested_tuck_layer = null
+ /// The amount added to the pixel_x value of a tucked-in item.
+ var/tucked_x_shift = 0
+ /// The amount added to the pixel_y value of a tucked-in item.
+ var/tucked_y_shift = 0
+
+/obj/structure/bed/Initialize(...)
+ . = ..()
+ if(swap_lying_with_dir)
+ buckle_lying = get_buckle_angle_from_dir(dir)
+
+/obj/structure/bed/setDir(newdir)
+ . = ..()
+ if(swap_lying_with_dir)
+ buckle_lying = get_buckle_angle_from_dir(newdir)
+ // shuttle rotation etc... ugh.
+ if(has_buckled_mobs())
+ for(var/mob/living/M as anything in buckled_mobs)
+ // this proc already checks to see if the new angle is different from the old one,
+ // so this shouldn't cause any duplicate work or unnecessary animations.
+ M.set_lying_angle(buckle_lying)
+
+/obj/structure/bed/proc/get_buckle_angle_from_dir(some_dir)
+ if(some_dir & (SOUTH|WEST))
+ return 90
+ else
+ return 270
+
/obj/structure/bed/examine(mob/user)
. = ..()
if(bolts)
@@ -52,6 +86,9 @@
icon_state = "down"
anchored = FALSE
resistance_flags = NONE
+
+ // no dir states
+ swap_lying_with_dir = FALSE
var/foldabletype = /obj/item/roller
/obj/structure/bed/roller/attackby(obj/item/W, mob/user, params)
@@ -161,8 +198,9 @@
else
to_chat(user, "The dock is empty!")
-//Dog bed
-
+/*
+ * "Dog" beds
+ */
/obj/structure/bed/dogbed
name = "dog bed"
icon_state = "dogbed"
@@ -170,6 +208,9 @@
anchored = TRUE
buildstacktype = /obj/item/stack/sheet/mineral/wood
buildstackamount = 10
+
+ // no dir states
+ swap_lying_with_dir = FALSE
var/mob/living/owner = null
/obj/structure/bed/dogbed/ian
@@ -206,7 +247,9 @@
. = ..()
update_owner(M)
-//Double Beds, for luxurious sleeping, i.e. the captain and maybe heads - no quirky refrence here. Move along
+/*
+ * Double beds, for luxurious sleeping, i.e. the captain and maybe heads - no quirky refrence here. Move along
+ */
/obj/structure/bed/double
name = "double bed"
desc = "A luxurious double bed, for those too important for small dreams."
@@ -232,3 +275,84 @@
name = "double dirty mattress"
desc = "An old grubby king sized mattress. You really try to not think about what could be the cause of those stains."
icon_state = "dirty_mattress_double"
+
+/*
+ * Bunk beds. Comes with an /obj/effect spawner that lets mappers place them down easily.
+ * The base type is the bottom bunk, with the top bunk as a derived type.
+ * Like other beds, the pillow may be on the left or right depending on the direction.
+ */
+/obj/structure/bed/bunk
+ name = "bottom bunk"
+ desc = "The oft-maligned bottom bunk of a compact bunk bed. Heavy sleepers only."
+ icon_state = "bottom_bunk"
+ // just below the top bunk's main layer
+ suggested_tuck_layer = LYING_MOB_LAYER + 0.005
+ /// The amount added to the pixel_y value of mobs lying down, relative to the default shift for that position.
+ var/mob_y_shift = -1
+ // i think it looks best without shifting the bedsheet down, even though the mob gets shifted down some
+
+// alter their pixel offset when they lie down...
+/obj/structure/bed/bunk/post_buckle_mob(mob/living/M)
+ // we shift the lying mob a little so that they line up better with the pillow, but the shift direction changes
+ // depending on the direction they lie down in, controlled by buckle_lying
+ // (which is in turn based on our direction, but we don't need to worry about that directly)
+ var/horz_offset
+ if(buckle_lying == 90)
+ horz_offset = 2
+ else
+ horz_offset = -2
+
+ M.pixel_x = M.get_standard_pixel_x_offset(M.body_position == LYING_DOWN) + horz_offset
+ M.pixel_y = M.get_standard_pixel_y_offset(M.body_position == LYING_DOWN) + mob_y_shift
+
+// ...and reset it when they get off
+/obj/structure/bed/bunk/post_unbuckle_mob(mob/living/M)
+ M.pixel_x = M.get_standard_pixel_x_offset(M.body_position == LYING_DOWN)
+ M.pixel_y = M.get_standard_pixel_y_offset(M.body_position == LYING_DOWN)
+
+
+/obj/structure/bed/bunk/top
+ name = "top bunk"
+ desc = "The top bunk of a compact bunk bed. Few other sleeping accommodations can match its luxury."
+ icon_state = "top_bunk"
+
+ // higher layer, so that it renders on top of people on the bottom bunk
+ layer = LYING_MOB_LAYER + 0.01
+ mob_y_shift = 13
+
+ // above the lying mob, but below the ladder
+ suggested_tuck_layer = LYING_MOB_LAYER + 0.025
+ tucked_y_shift = 14
+
+/obj/structure/bed/bunk/top/Initialize(...)
+ . = ..()
+ // the ladder needs to render above the mob
+ overlays += image(icon = 'icons/obj/objects.dmi', icon_state = "top_bunk_ladder", layer = LYING_MOB_LAYER + 0.03)
+ // and the posts need to render below the bottom bunk
+ overlays += image(icon = 'icons/obj/objects.dmi', icon_state = "top_bunk_posts", layer = TABLE_LAYER)
+
+/obj/structure/bed/bunk/top/post_buckle_mob(mob/living/M)
+ . = ..()
+ M.layer = LYING_MOB_LAYER + 0.02
+
+/obj/structure/bed/bunk/top/post_unbuckle_mob(mob/living/M)
+ . = ..()
+ // honestly not really confident in this, but since standing up takes a do_after
+ // (and thus happens afterwards, resetting the layer), it should be fine...
+ // i'm more worried about altering layers via + and -, since if you figured out ways
+ // of stacking those you could layer yourself under, like, the floor.
+ M.layer = LYING_MOB_LAYER
+
+
+// the spawner
+/obj/effect/spawner/bunk_bed
+ name = "bunk bed spawner"
+ icon_state = "bunk_bed_spawner"
+
+/obj/effect/spawner/bunk_bed/Initialize(...)
+ . = ..()
+ var/obj/structure/bed/bunk/bottom_bunk = new(loc)
+ var/obj/structure/bed/bunk/top/top_bunk = new(loc)
+
+ bottom_bunk.setDir(dir)
+ top_bunk.setDir(dir)
diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm
index 1a89195c646a2..31e9d9a0b8fcb 100644
--- a/code/game/objects/structures/bedsheet_bin.dm
+++ b/code/game/objects/structures/bedsheet_bin.dm
@@ -28,7 +28,7 @@ LINEN BINS
/obj/item/bedsheet/Initialize(mapload)
. = ..()
- AddElement(/datum/element/bed_tuckable, 0, 0, 0)
+ AddElement(/datum/element/bed_tuckable, 0, 0, 0, TRUE, TRUE)
/obj/item/bedsheet/attack_self(mob/user)
if(!user.CanReach(src)) //No telekenetic grabbing.
diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm
index 1be5f857e1558..c0604dd5e09b5 100644
--- a/code/game/objects/structures/tank_dispenser.dm
+++ b/code/game/objects/structures/tank_dispenser.dm
@@ -28,10 +28,10 @@
/obj/structure/tank_dispenser/update_overlays()
. = ..()
switch(oxygentanks)
- if(1 to 3)
+ if(1 to 4)
. += "oxygen-[oxygentanks]"
- if(4 to TANK_DISPENSER_CAPACITY)
- . += "oxygen-4"
+ if(5 to TANK_DISPENSER_CAPACITY)
+ . += "oxygen-5"
switch(plasmatanks)
if(1 to 4)
. += "plasma-[plasmatanks]"
diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
index 5bce8014babe8..abc69300c9c6d 100644
--- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm
+++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
@@ -761,6 +761,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
icon = 'icons/obj/abductor.dmi'
buildstacktype = /obj/item/stack/sheet/mineral/abductor
icon_state = "bed"
+ swap_lying_with_dir = FALSE
/obj/structure/table_frame/abductor
name = "alien table frame"
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index 9c65e50130cf9..c073a7c3521c0 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -608,7 +608,7 @@ This is here to make the tiles around the station mininuke change when it's arme
/obj/item/disk/nuclear/Initialize()
. = ..()
- AddElement(/datum/element/bed_tuckable, 6, -6, 0)
+ AddElement(/datum/element/bed_tuckable, 6, -6, 0, FALSE, FALSE)
if(!fake)
SSpoints_of_interest.make_point_of_interest(src)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 3ed2b86816ac4..9a19e198a5552 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -386,7 +386,7 @@
/mob/living/carbon/get_standard_pixel_y_offset(lying = 0)
if(lying)
- return -6
+ return PIXEL_Y_OFFSET_LYING
else
return initial(pixel_y)
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 0aa256c631e42..bb63eb2b7f0ed 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/obj/objects.dmi b/icons/obj/objects.dmi
index 1b156b9294f9f..8479c100f1b0a 100644
Binary files a/icons/obj/objects.dmi and b/icons/obj/objects.dmi differ