Skip to content

Commit

Permalink
a bedtime update for beddy-bye boys (Bunk beds!) (#3345)
Browse files Browse the repository at this point in the history
## About The Pull Request

Adds bunk beds, in the form of two objects, a "bottom bunk" structure
and a "top bunk" structure, each with appropriate layering and
sprite-shifting, including for bedsheets. Sprites are courtesy of @thgvr
. They can be crafted with metal or placed by mappers as a single object
using a spawner effect.

Additionally, beds, double beds, and bunk beds have had their
alternate-direction behavior fixed. These beds have a flipped set of
sprites on the NORTH and EAST directions, but neither bedsheets nor
buckled players correctly respected the alternate sprites. This has been
fixed.

oh also i fixed a random rendering error with tank storage units that
imaginos notified me of. they weren't showing the final 5-tank overlay
state correctly. they do now


![image](https://github.com/user-attachments/assets/bbebeb5e-27db-4703-a7db-c639b8ec1502)

## Why It's Good For The Game

1. bunkbeds give mappers another tool to make ships and ruins feel
appropriately-decorated
2. mappers being effectively locked to a single bed rotation is dumb
currently, there's one bug: when crafting a bunk bed, you need to make
the bottom one first. if you make the top one first, you won't be able
to place the bottom bunk there without just deconstructing the top bunk.
i know exactly what causes this, it's mostly just annoying and i've got
a few ideas for (slightly hacky) fixes. but i'm also lazy

## Changelog

:cl: tmtmtl30, Thgvr
add: Added bunkbeds, which can now be crafted with metal or placed by
mappers.
fix: Beds facing alternate directions now correctly support people
buckling to them and bedsheets being placed on top of them.
/:cl:
  • Loading branch information
tmtmtl30 authored Sep 30, 2024
1 parent f3df7cc commit 5dfdef7
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 16 deletions.
23 changes: 20 additions & 3 deletions code/datums/elements/bed_tucking.dm
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,22 @@
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

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)
Expand All @@ -40,11 +47,20 @@
return

to_chat(tucker, "<span class='notice'>You lay [tucked] out on [target_bed].</span>")
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

Expand All @@ -57,4 +73,5 @@
SIGNAL_HANDLER

tucked.transform = turn(tucked.transform, -rotation_degree)
tucked.layer = initial(tucked.layer)
UnregisterSignal(tucked, COMSIG_ITEM_PICKUP)
2 changes: 1 addition & 1 deletion code/game/objects/items/plushes.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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), \
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/stacks/stack.dm
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@
if(!window_structure.fulltile)
continue
if(object.density)
to_chat(usr, "<span class='warning'>There is \a [object.name] here. You cant make \a [recipe.title] here!</span>")
to_chat(usr, "<span class='warning'>There is \a [object.name] here. You can't make \a [recipe.title] here!</span>")
return FALSE
if(recipe.placement_checks)
switch(recipe.placement_checks)
Expand Down
1 change: 1 addition & 0 deletions code/game/objects/structures/beds_chairs/alien_nest.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
130 changes: 127 additions & 3 deletions code/game/objects/structures/beds_chairs/bed.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -161,15 +198,19 @@
else
to_chat(user, "<span class='warning'>The dock is empty!</span>")

//Dog bed

/*
* "Dog" beds
*/
/obj/structure/bed/dogbed
name = "dog bed"
icon_state = "dogbed"
desc = "A comfy-looking dog bed. You can even strap your pet in, in case the gravity turns off."
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
Expand Down Expand Up @@ -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."
Expand All @@ -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)
2 changes: 1 addition & 1 deletion code/game/objects/structures/bedsheet_bin.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions code/game/objects/structures/tank_dispenser.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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]"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/living/carbon/carbon.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Binary file modified icons/effects/effects.dmi
Binary file not shown.
Binary file modified icons/obj/objects.dmi
Binary file not shown.

0 comments on commit 5dfdef7

Please sign in to comment.