From 1c64d5142fa405a5a0e9ae6a808072f42d8d16ea Mon Sep 17 00:00:00 2001 From: Return Date: Wed, 7 Feb 2024 00:49:32 -0800 Subject: [PATCH] Adds Cargo shelves from Shiptest, makes Skyrat modular shelves buildable (#992) ## About The Pull Request Source Inspiration: https://github.com/shiptest-ss13/Shiptest/pull/2374 This PR add ## Why It's Good For The Game This makes it so that cargo techs can neatly stack crates on a shelf for convenience. How neat! ## Proof Of Testing https://cdn.discordapp.com/attachments/1059199070855495753/1195241690450120734/2024-01-11_23-39-46.mp4?ex=65b34691&is=65a0d191&hm=9314c27ee40abaf5ef185408c7ebd3d8336a8fc091a2b94524ec23252d4fe67f& ## Changelog :cl: ReturnToZender, GenericDM (Original Version) add: Cargo shelves are now craftable using metal! They store up to 3 crates per shelf. fix: Skyrat modular shelves (such as gun racks and tall shelves) are now buildable using metal. /:cl: --------- Co-authored-by: Waterpig <49160555+Majkl-J@users.noreply.github.com> Co-authored-by: pixelkitty286 <78038207+pixelkitty286@users.noreply.github.com> Co-authored-by: BurgerLUA <8602857+BurgerLUA@users.noreply.github.com> Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com> Co-authored-by: nevimer <77420409+nevimer@users.noreply.github.com> Co-authored-by: projectkepler-RU <99981766+projectkepler-ru@users.noreply.github.com> Co-authored-by: The Sharkening <95130227+StrangeWeirdKitten@users.noreply.github.com> Co-authored-by: Tom <8881105+tf-4@users.noreply.github.com> Co-authored-by: xXPawnStarrXx <53197594+xXPawnStarrXx@users.noreply.github.com> Co-authored-by: Bubberbot <151680451+Bubberbot@users.noreply.github.com> Co-authored-by: Nitha <132854285+NithaIsTired@users.noreply.github.com> Co-authored-by: Swift --- .../items/stacks/sheets/sheet_types.dm | 9 +- modular_zubbers/icons/obj/structures.dmi | Bin 0 -> 530 bytes modular_zubbers/modules/shelves/shelf.dm | 236 ++++++++++++++++++ tgstation.dme | 1 + 4 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 modular_zubbers/icons/obj/structures.dmi create mode 100644 modular_zubbers/modules/shelves/shelf.dm diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 309d23aac9fb1..69f5a9cb5e41a 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -68,9 +68,16 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \ new /datum/stack_recipe("White Checker King", /obj/structure/chess/checker/whiteking, 2, time = 1 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_ENTERTAINMENT), \ new /datum/stack_recipe("Black Checker Man", /obj/structure/chess/checker/blackman, 2, time = 1 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_ENTERTAINMENT), \ new /datum/stack_recipe("Black Checker King", /obj/structure/chess/checker/blackking, 2, time = 1 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_ENTERTAINMENT), \ + )), \ + //BUBBER EDIT BEGIN: ADDS CRAFTABLE SHELVES + new /datum/stack_recipe_list("Racks and Shelves", list( \ + new/datum/stack_recipe("rack parts", /obj/item/rack_parts, category = CAT_FURNITURE), \ + new/datum/stack_recipe("Cargo shelf parts", /obj/item/rack_parts/cargo_shelf, 4, category = CAT_FURNITURE), \ + new/datum/stack_recipe("Gun shelf parts", /obj/item/rack_parts/gun, 2, category = CAT_FURNITURE), \ + new/datum/stack_recipe("Shelf parts", /obj/item/rack_parts/shelf, 2, category = CAT_FURNITURE), \ )), null, \ - new/datum/stack_recipe("rack parts", /obj/item/rack_parts, category = CAT_FURNITURE), \ + //BUBBER EDIT END: ADDS CRAFTABLE SHELVES new/datum/stack_recipe("closet", /obj/structure/closet, 2, time = 1.5 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_FURNITURE), \ null, \ new/datum/stack_recipe("atmos canister", /obj/machinery/portable_atmospherics/canister, 10, time = 3 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_ATMOSPHERIC), \ diff --git a/modular_zubbers/icons/obj/structures.dmi b/modular_zubbers/icons/obj/structures.dmi new file mode 100644 index 0000000000000000000000000000000000000000..494bd3b404ac288ee87770e95b062b2ac1ca0246 GIT binary patch literal 530 zcmV+t0`2{YP)iF1Ia`T~GbOXA7|1o`;!G<_%uR)`;fjhgQghPcfn1{1CM6c95>yJZ zJH8;XsH7N|Qe_2KKNqmS0K6DLLqAy|hX4Qp7)eAyR7i>Klu-`CAPj~FT!3lfGs|?( z(@hUx*bT-GV4~hYuP= 3) // If we're at or above three, we'll be on the way to going off the tile we're on. This allows mobs to be below the shelf when this happens. + stack_layer = ABOVE_MOB_LAYER + (0.02 * i) - 0.01 + else + stack_layer = BELOW_OBJ_LAYER + (0.02 * i) - 0.01 // Make each shelf piece render above the last, but below the crate that should be on it. + stack_offset = DEFAULT_SHELF_VERTICAL_OFFSET * i // Make each shelf piece physically above the last. + overlays += image(icon = 'modular_zubbers/icons/obj/structures.dmi', icon_state = "shelf_stack", layer = stack_layer, pixel_y = stack_offset) + return + +/obj/structure/cargo_shelf/Destroy() + QDEL_LIST(shelf_contents) + return ..() + +/obj/structure/cargo_shelf/examine(mob/user) + . = ..() + . += span_notice("There are some bolts holding [src] together.") + if(shelf_contents.Find(null)) // If there's an empty space in the shelf, let the examiner know. + . += span_notice("You could drag and drop a crate into [src].") + if(contents.len) // If there are any crates in the shelf, let the examiner know. + . += span_notice("You could drag and drop a crate out of [src].") + . += span_notice("[src] contains:") + for(var/obj/structure/closet/crate/crate in shelf_contents) + . += " [icon2html(crate, user)] [crate]" + +/obj/structure/cargo_shelf/attackby(obj/item/item, mob/living/user, params) + if (item.tool_behaviour == TOOL_WRENCH && !(flags_1 & NO_DECONSTRUCTION)) + item.play_tool_sound(src) + if(do_after(user, 3 SECONDS, target = src)) + deconstruct(TRUE) + return TRUE + return ..() + +/obj/structure/cargo_shelf/relay_container_resist_act(mob/living/user, obj/structure/closet/crate) + to_chat(user, span_notice("You begin attempting to knock [crate] out of [src]")) + if(do_after(user, 30 SECONDS, target = crate)) + if(!user || user.stat != CONSCIOUS || user.loc != crate || crate.loc != src) + return // If the user is in a strange condition, return early. + visible_message(span_warning("[crate] falls off of [src]!"), + span_notice("You manage to knock [crate] free of [src]"), + span_notice("You hear a thud.")) + crate.forceMove(drop_location()) // Drop the crate onto the shelf, + step_rand(crate, 1) // Then try to push it somewhere. + crate.layer = initial(crate.layer) // Reset the crate back to having the default layer, otherwise we might get strange interactions. + crate.pixel_y = initial(crate.pixel_y) // Reset the crate back to having no offset, otherwise it will be floating. + shelf_contents[shelf_contents.Find(crate)] = null // Remove the reference to the crate from the list. + handle_visuals() + +/obj/structure/cargo_shelf/proc/handle_visuals() + vis_contents = contents // It really do be that shrimple. + return + +/obj/structure/cargo_shelf/proc/load(obj/structure/closet/crate/crate, mob/user) + var/next_free = shelf_contents.Find(null) // Find the first empty slot in the shelf. + if(!next_free) // If we don't find an empty slot, return early. + balloon_alert(user, "shelf full!") + return FALSE + if(do_after(user, use_delay, target = crate)) + if(shelf_contents[next_free] != null) + return FALSE // Something has been added to the shelf while we were waiting, abort! + if(crate.opened) // If the crate is open, try to close it. + if(!crate.close()) + return FALSE // If we fail to close it, don't load it into the shelf. + shelf_contents[next_free] = crate // Insert a reference to the crate into the free slot. + crate.forceMove(src) // Insert the crate into the shelf. + crate.pixel_y = DEFAULT_SHELF_VERTICAL_OFFSET * (next_free - 1) // Adjust the vertical offset of the crate to look like it's on the shelf. + if(next_free >= 3) // If we're at or above three, we'll be on the way to going off the tile we're on. This allows mobs to be below the crate when this happens. + crate.layer = ABOVE_MOB_LAYER + 0.02 * (next_free - 1) + else + crate.layer = BELOW_OBJ_LAYER + 0.02 * (next_free - 1) // Adjust the layer of the crate to look like it's in the shelf. + handle_visuals() + return TRUE + return FALSE // If the do_after() is interrupted, return FALSE! + +/obj/structure/cargo_shelf/proc/unload(obj/structure/closet/crate/crate, mob/user, turf/unload_turf) + if(!unload_turf) + unload_turf = get_turf(user) // If a turf somehow isn't passed into the proc, put it at the user's feet. + if(!unload_turf.Enter(crate)) // If moving the crate from the shelf to the desired turf would bump, don't do it! Thanks Kapu1178 for the help here. - Generic DM + unload_turf.balloon_alert(user, "no room!") + return FALSE + if(do_after(user, use_delay, target = crate)) + if(!shelf_contents.Find(crate)) + return FALSE // If something has happened to the crate while we were waiting, abort! + crate.layer = initial(crate.layer) // Reset the crate back to having the default layer, otherwise we might get strange interactions. + crate.pixel_y = initial(crate.pixel_y) // Reset the crate back to having no offset, otherwise it will be floating. + crate.forceMove(unload_turf) + shelf_contents[shelf_contents.Find(crate)] = null // We do this instead of removing it from the list to preserve the order of the shelf. + handle_visuals() + return TRUE + return FALSE // If the do_after() is interrupted, return FALSE! + +/obj/structure/cargo_shelf/deconstruct(disassembled = TRUE) + var/turf/dump_turf = drop_location() + for(var/obj/structure/closet/crate/crate in shelf_contents) + crate.layer = initial(crate.layer) // Reset the crates back to default visual state + crate.pixel_y = initial(crate.pixel_y) + crate.forceMove(dump_turf) + step(crate, pick(GLOB.alldirs)) // Shuffle the crates around as though they've fallen down. + crate.SpinAnimation(rand(4,7), 1) // Spin the crates around a little as they fall. Randomness is applied so it doesn't look weird. + switch(pick(1, 1, 1, 1, 2, 2, 3)) // Randomly pick whether to do nothing, open the crate, or break it open. + if(1) // Believe it or not, this does nothing. + if(2) // Open the crate! + if(crate.open()) // Break some open, cause a little chaos. + crate.visible_message(span_warning("[crate]'s lid falls open!")) + else // If we somehow fail to open the crate, just break it instead! + crate.visible_message(span_warning("[crate] falls apart!")) + crate.deconstruct() + if(3) // Break that crate! + crate.visible_message(span_warning("[crate] falls apart!")) + crate.deconstruct() + shelf_contents[shelf_contents.Find(crate)] = null + if(!(flags_1 & NO_DECONSTRUCTION)) + density = FALSE + var/obj/item/rack_parts/cargo_shelf/newparts = new(loc) + transfer_fingerprints_to(newparts) + return ..() + +/obj/structure/closet/crate/MouseDrop(atom/drop_atom, src_location, over_location) + . = ..() + var/mob/living/user = usr + if(!isliving(user)) + return // Ghosts busted. + if(!isturf(user.loc) || user.incapacitated() || user.body_position == LYING_DOWN) + return // If the user is in a weird state, don't bother trying. + if(get_dist(drop_atom, src) != 1 || get_dist(drop_atom, user) != 1) + return // Check whether the crate is exactly 1 tile from the shelf and the user. + if(istype(drop_atom, /turf/open) && istype(loc, /obj/structure/cargo_shelf) && user.Adjacent(drop_atom)) + var/obj/structure/cargo_shelf/shelf = loc + return shelf.unload(src, user, drop_atom) // If we're being dropped onto a turf, and we're inside of a crate shelf, unload. + if(istype(drop_atom, /obj/structure/cargo_shelf) && isturf(loc) && user.Adjacent(src)) + var/obj/structure/cargo_shelf/shelf = drop_atom + return shelf.load(src, user) // If we're being dropped onto a crate shelf, and we're in a turf, load. + +/obj/item/rack_parts/cargo_shelf + name = "Cargo shelf parts" + icon = 'modular_zubbers/icons/obj/structures.dmi' + icon_state = "rack_parts" + desc = "Parts of a cargo shelf, for storing crates." + +/obj/item/rack_parts/cargo_shelf/attack_self(mob/user) + if(building) + return + building = TRUE + to_chat(user, span_notice("You start constructing a cargo shelf...")) + if(do_after(user, 50, target = user, progress=TRUE)) + if(!user.temporarilyRemoveItemFromInventory(src)) + return + var/obj/structure/cargo_shelf/R = new /obj/structure/cargo_shelf(get_turf(src)) + user.visible_message("[user] assembles \a [R].\ + ", span_notice("You assemble \a [R].")) + R.add_fingerprint(user) + qdel(src) + building = FALSE + +/obj/structure/cargo_shelf/deconstruct(disassembled = TRUE) + if(!(obj_flags & NO_DECONSTRUCTION)) + set_density(FALSE) + var/obj/item/rack_parts/cargo_shelf/newparts = new(loc) + transfer_fingerprints_to(newparts) + qdel(src) + +/obj/item/rack_parts/gun + name = "gun rack parts" + desc = "Parts of a gun rack." + +/obj/item/rack_parts/gun/attack_self(mob/user) + if(building) + return + building = TRUE + to_chat(user, span_notice("You start constructing a gun rack...")) + if(do_after(user, 50, target = user, progress=TRUE)) + if(!user.temporarilyRemoveItemFromInventory(src)) + return + var/obj/structure/rack/gunrack/R = new /obj/structure/rack/gunrack(get_turf(src)) + user.visible_message("[user] assembles \a [R].\ + ", span_notice("You assemble \a [R].")) + R.add_fingerprint(user) + qdel(src) + building = FALSE + +/obj/structure/rack/gunrack/deconstruct(disassembled = TRUE) + if(!(obj_flags & NO_DECONSTRUCTION)) + set_density(FALSE) + var/obj/item/rack_parts/gun/newparts = new(loc) + transfer_fingerprints_to(newparts) + qdel(src) + +/obj/item/rack_parts/shelf + name = "shelf parts" + desc = "Parts of a standard shelf." + +/obj/item/rack_parts/shelf/attack_self(mob/user) + if(building) + return + building = TRUE + to_chat(user, span_notice("You start constructing a standard shelf...")) + if(do_after(user, 50, target = user, progress=TRUE)) + if(!user.temporarilyRemoveItemFromInventory(src)) + return + var/obj/structure/rack/shelf/R = new /obj/structure/rack/shelf(get_turf(src)) + user.visible_message("[user] assembles \a [R].\ + ", span_notice("You assemble \a [R].")) + R.add_fingerprint(user) + qdel(src) + building = FALSE + +/obj/structure/rack/shelf/deconstruct(disassembled = TRUE) + if(!(obj_flags & NO_DECONSTRUCTION)) + set_density(FALSE) + var/obj/item/rack_parts/shelf/newparts = new(loc) + transfer_fingerprints_to(newparts) + qdel(src) diff --git a/tgstation.dme b/tgstation.dme index cfa8f44457286..422bdc6240285 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -8487,6 +8487,7 @@ #include "modular_zubbers\modules\quirks\code\neutral_quirks\waddle.dm" #include "modular_zubbers\modules\security\secmed\secmed_clothes.dm" #include "modular_zubbers\modules\security\secmed\security_medic.dm" +#include "modular_zubbers\modules\shelves\shelf.dm" #include "modular_zubbers\modules\space_background\parallax.dm" #include "modular_zubbers\modules\space_background\turf_space.dm" #include "modular_zubbers\modules\synths\death_sound.dm"