From 9a9e25910446c4c42b159e02c40986969ca86912 Mon Sep 17 00:00:00 2001 From: Casper3667 <8396443+Casper3667@users.noreply.github.com> Date: Wed, 10 Sep 2025 21:05:19 +0200 Subject: [PATCH] Adds the parcel cart to operations. (#21288) This adds a parcel cart to the mail room in operations, which can be loaded with up to 17 packages. These packages can be any that can be held in the hand, so not crates but anything smaller effectively yes as long as it can be wrapped in package wrapper. When it is loaded with 5 packages, it gives a slowdown to the person pushing it, and further slowdown at 11 packages. The cart can have a total of 17 packages at the moment, as that is the amount of different sprites I have for it. The code is based on the engineering cart, and also contain a small bugfix so the engineering cart give back steel when taken apart, as that is what is used to build it. https://github.com/user-attachments/assets/b8613c9e-55e1-4f76-926a-60f4acd6269e ### Asset Licenses The following assets that **have not** been created by myself are included in this PR: | Path | Original Author | License | | --- | --- | --- | | icons/obj/parcelcart.dmi | Tomixcomics (Aurora Station) | CC-BY | --- aurorastation.dme | 6 +- code/game/machinery/doors/door.dm | 4 +- code/game/objects/structures/carts/carts.dm | 141 ++++++++++++++++ .../structures/{ => carts}/engicart.dm | 151 ++---------------- .../structures/{ => carts}/janicart.dm | 147 ++--------------- .../objects/structures/carts/parcelcart.dm | 123 ++++++++++++++ code/game/objects/structures/mop_bucket.dm | 4 +- code/modules/cargo/items/custodial.dm | 2 +- code/modules/cargo/items/engineering.dm | 2 +- code/modules/cargo/items/operations.dm | 2 +- code/modules/item_worth/worths_list.dm | 2 +- code/modules/materials/material_recipes.dm | 5 +- code/modules/mob/mob_movement.dm | 2 +- .../file_system/programs/civilian/janitor.dm | 4 +- html/changelogs/ParcelCart.yml | 7 + icons/obj/parcelcart.dmi | Bin 0 -> 1296 bytes .../abandoned_diner/abandoned_diner.dmm | 2 +- .../abandoned_industrial_station.dmm | 4 +- .../away_site/hivebot_hub/hivebot_hub.dmm | 2 +- .../konyang/point_verdant/point_verdant.dmm | 4 +- maps/away/away_site/magshield/magshield.dmm | 2 +- .../solarian_prewar/sensor_relay.dmm | 2 +- .../uueoaesa/reclamation/ihss_reclamation.dmm | 2 +- ...nviro_testing_facility_submaps_layouts.dmm | 6 +- .../enviro_testing_facility_submaps_main.dmm | 2 +- .../biesel/tcaf_corvette/tcaf_corvette.dmm | 2 +- maps/away/ships/coc/coc_ranger/coc_ship.dmm | 2 +- .../ships/coc/coc_surveyor/coc_surveyor.dmm | 2 +- .../dominian_corvette/dominian_corvette.dmm | 2 +- .../dominian_unathi_privateer.dmm | 2 +- maps/away/ships/golden_deep/golden_deep.dmm | 2 +- .../hegemony_corvette/hegemony_corvette.dmm | 2 +- .../heph/heph_security/heph_security.dmm | 2 +- maps/away/ships/himeo/himeo_patrol_ship.dmm | 2 +- maps/away/ships/iac/iac_rescue_ship.dmm | 2 +- maps/away/ships/idris/idris_cruiser.dmm | 2 +- maps/away/ships/orion/orion_express_ship.dmm | 2 +- .../ships/pra/headmaster/headmaster_ship.dmm | 2 +- maps/away/ships/sol/sol_splf/splf_raider.dmm | 2 +- maps/away/ships/sol/sol_ssrm/ssrm_ship.dmm | 2 +- .../tirakqi_smuggler/tirakqi_smuggler.dmm | 2 +- maps/away/ships/xanu/xanu_frigate.dmm | 2 +- maps/away/ships/yacht/yacht.dmm | 2 +- maps/event/generic_dock/generic_dock-1.dmm | 2 +- maps/event/idris_cruise/idris_cruise-1.dmm | 4 +- maps/event/rooftop/rooftop-1.dmm | 2 +- .../exoplanets/moghes/moghes_bar.dmm | 2 +- .../exoplanets/ouerea/ouerea_bar.dmm | 2 +- maps/sccv_horizon/sccv_horizon.dmm | 15 +- .../submaps/ops_warehouse_small_storage.dmm | 4 +- 50 files changed, 367 insertions(+), 328 deletions(-) create mode 100644 code/game/objects/structures/carts/carts.dm rename code/game/objects/structures/{ => carts}/engicart.dm (69%) rename code/game/objects/structures/{ => carts}/janicart.dm (70%) create mode 100644 code/game/objects/structures/carts/parcelcart.dm create mode 100644 html/changelogs/ParcelCart.yml create mode 100644 icons/obj/parcelcart.dmi diff --git a/aurorastation.dme b/aurorastation.dme index f535063139d..1b1bcbf5fe8 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -1429,7 +1429,6 @@ #include "code\game\objects\structures\displaycase.dm" #include "code\game\objects\structures\door_assembly.dm" #include "code\game\objects\structures\ECD.dm" -#include "code\game\objects\structures\engicart.dm" #include "code\game\objects\structures\extinguisher.dm" #include "code\game\objects\structures\fireaxe_cabinet.dm" #include "code\game\objects\structures\flags_banners.dm" @@ -1440,7 +1439,6 @@ #include "code\game\objects\structures\hadii_statue.dm" #include "code\game\objects\structures\hivebot_head.dm" #include "code\game\objects\structures\inflatable.dm" -#include "code\game\objects\structures\janicart.dm" #include "code\game\objects\structures\kitchen_spike.dm" #include "code\game\objects\structures\large.dm" #include "code\game\objects\structures\lattice.dm" @@ -1477,6 +1475,10 @@ #include "code\game\objects\structures\barricades\metal.dm" #include "code\game\objects\structures\barricades\plasteel.dm" #include "code\game\objects\structures\barricades\wood.dm" +#include "code\game\objects\structures\carts\carts.dm" +#include "code\game\objects\structures\carts\engicart.dm" +#include "code\game\objects\structures\carts\janicart.dm" +#include "code\game\objects\structures\carts\parcelcart.dm" #include "code\game\objects\structures\crates_lockers\closets.dm" #include "code\game\objects\structures\crates_lockers\crates.dm" #include "code\game\objects\structures\crates_lockers\largecrate.dm" diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 38e6a429f1f..4624f885319 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -194,8 +194,8 @@ else do_animate("deny") return - if(istype(bumped_atom, /obj/structure/janitorialcart) || istype(bumped_atom, /obj/structure/engineeringcart)) - var/obj/structure/janitorialcart/cart = bumped_atom + if(istype(bumped_atom, /obj/structure/cart)) + var/obj/structure/cart/cart = bumped_atom if(density) if(cart.pulling && (src.allowed(cart.pulling))) open() diff --git a/code/game/objects/structures/carts/carts.dm b/code/game/objects/structures/carts/carts.dm new file mode 100644 index 00000000000..18acf44e6bf --- /dev/null +++ b/code/game/objects/structures/carts/carts.dm @@ -0,0 +1,141 @@ +ABSTRACT_TYPE(/obj/structure/cart) + name = "cart parent item" + desc = DESC_PARENT + icon_state = "cart" + anchored = FALSE + density = TRUE + climbable = TRUE + build_amt = 15 + material = DEFAULT_WALL_MATERIAL + slowdown = 0 + var/movesound = 'sound/effects/roll.ogg' + var/driving + var/mob/living/pulling + +/obj/structure/cart/mechanics_hints(mob/user, distance, is_adjacent) + . += ..() + . += "\ + You can CTRL-Click to start dragging this cart. This object has a special dragging behaviour: when dragged, character's movement \ + directs the cart and the character is subsequently pulled by it. \ + " + +/obj/structure/cart/disassembly_hints(mob/user, distance, is_adjacent) + . += ..() + . += "An empty cart can be taken apart with a wrench or a welder. Or a plasma cutter, if you're that hardcore." + +/obj/structure/cart/proc/take_apart(var/mob/user = null, var/obj/I) + if(user) + if(iswelder(I)) + var/obj/item/welder = I + welder.play_tool_sound(get_turf(src), 50) + + user.visible_message("[user] starts taking apart the [src]...", SPAN_NOTICE("You start disassembling the [src]...")) + if (!do_after(user, 30, do_flags = DO_DEFAULT & ~DO_USER_SAME_HAND)) + return + + dismantle() + +/obj/structure/cart/ex_act(severity) + spill(100 / severity) + ..() + +/obj/structure/cart/proc/spill(var/chance = 100) + +/obj/structure/cart/proc/update_slowdown() + +/obj/structure/cart/relaymove(mob/living/user, direction) + . = ..() + + if(user.stat || user.stunned || user.weakened || user.paralysis || user.lying || user.restrained()) + if(user==pulling) + pulling = null + user.pulledby = null + to_chat(user, SPAN_WARNING("You lost your grip!")) + return + if(user.pulling && (user == pulling)) + pulling = null + user.pulledby = null + return + if(pulling && (get_dist(src, pulling) > 1)) + pulling = null + user.pulledby = null + if(user==pulling) + return + if(pulling && (get_dir(src.loc, pulling.loc) == direction)) + to_chat(user, SPAN_WARNING("You cannot go there.")) + return + + driving = 1 + var/turf/T = null + if(pulling) + T = pulling.loc + if(get_dist(src, pulling) >= 1) + step(pulling, get_dir(pulling.loc, src.loc)) + step(src, direction) + set_dir(direction) + if(pulling) + if(pulling.loc == src.loc) + pulling.forceMove(T) + else + spawn(0) + if(get_dist(src, pulling) > 1) + pulling = null + user.pulledby = null + pulling.set_dir(get_dir(pulling, src)) + driving = 0 + + +/obj/structure/cart/Move() + . = ..() + if (pulling && (get_dist(src, pulling) > 1)) + pulling.pulledby = null + to_chat(pulling, SPAN_WARNING("You lost your grip!")) + pulling = null + if(has_gravity()) + playsound(src, movesound, 50, 1) + +/obj/structure/cart/CtrlClick(var/mob/user) + if(in_range(src, user)) + if(!ishuman(user)) return + if(!pulling) + pulling = user + user.pulledby = src + if(user.pulling) + user.stop_pulling() + user.set_dir(get_dir(user, src)) + to_chat(user, SPAN_NOTICE("You grip \the [name]'s handles.")) + else + to_chat(user, SPAN_NOTICE("You let go of \the [name]'s handles.")) + pulling.pulledby = null + pulling = null + return + +/obj/structure/cart/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(air_group || (height==0)) + return TRUE + if(mover?.movement_type & PHASING) + return TRUE + if(istype(mover) && mover.pass_flags & PASSTABLE) + return TRUE + if(istype(mover, /mob/living) && mover == pulling) + return TRUE + else + if(istype(mover, /obj/projectile)) + return prob(20) + else + return !density + +ABSTRACT_TYPE(/obj/structure/cart/storage) + var/has_items = FALSE + + /// Used for displaying and handling radial menu. Collective list of every single item this object contains. + var/list/storage_contents = list() + +/obj/structure/cart/storage/proc/handle_storing(var/attacking_item, var/mob/user, var/should_store, var/storage_is_full) + +/obj/structure/cart/storage/proc/get_storage_contents_list() + +/obj/structure/cart/storage/take_apart(var/mob/user = null, var/obj/I) + if(has_items) + spill() + . = ..() diff --git a/code/game/objects/structures/engicart.dm b/code/game/objects/structures/carts/engicart.dm similarity index 69% rename from code/game/objects/structures/engicart.dm rename to code/game/objects/structures/carts/engicart.dm index 70b76778cc1..7a10a2e75bc 100644 --- a/code/game/objects/structures/engicart.dm +++ b/code/game/objects/structures/carts/engicart.dm @@ -1,27 +1,16 @@ -/obj/structure/engineeringcart +/obj/structure/cart/storage/engineeringcart name = "engineering cart" desc = "A cart for your engineering-related storage needs." icon = 'icons/obj/engicart.dmi' icon_state = "cart" - anchored = FALSE - density = TRUE - climbable = TRUE - build_amt = 15 - material = DEFAULT_TABLE_REINF_MATERIAL - slowdown = 0 - var/has_items = FALSE var/list/my_glass = list() var/list/my_metal = list() var/list/my_plasteel = list() - /// Used for displaying and handling radial menu. Collective list of every single item this object contains. - var/list/storage_contents = list() var/obj/item/device/lightreplacer/my_lightreplacer = null var/obj/item/storage/toolbox/mechanical/my_blue_toolbox = null var/obj/item/storage/toolbox/electrical/my_yellow_toolbox = null var/obj/item/storage/toolbox/emergency/my_red_toolbox = null - var/driving - var/mob/living/pulling /// Amount of stacks the cart is capable to store. var/stack_capacity = 2 @@ -35,19 +24,11 @@ /obj/item/storage/toolbox/emergency )) -/obj/structure/engineeringcart/mechanics_hints(mob/user, distance, is_adjacent) +/obj/structure/cart/storage/engineeringcart/mechanics_hints(mob/user, distance, is_adjacent) . += ..() . += "You can use steel, plasteel, glass sheets, toolboxes and light replacers on the cart to store them." - . += "\ - You can CTRL-Click to start dragging this cart. This object has a special dragging behaviour: when dragged, character's movement \ - directs the cart and the character is subsequently pulled by it. \ - " -/obj/structure/engineeringcart/disassembly_hints(mob/user, distance, is_adjacent) - . += ..() - . += "An empty engineering cart can be taken apart with a wrench or a welder. Or a plasma cutter, if you're that hardcore." - -/obj/structure/engineeringcart/feedback_hints(mob/user, distance, is_adjacent) +/obj/structure/cart/storage/engineeringcart/feedback_hints(mob/user, distance, is_adjacent) . += ..() if(distance <= 1) if(locate(/obj/item/stack/material) in storage_contents) @@ -75,7 +56,7 @@ else . += "[icon2html(src, user)] There is no material in this cart!" -/obj/structure/engineeringcart/proc/get_storage_contents_list() +/obj/structure/cart/storage/engineeringcart/get_storage_contents_list() storage_contents.Cut() var/list/lists_to_check = list( my_glass, my_metal, my_plasteel @@ -90,7 +71,7 @@ if(O) storage_contents += O -/obj/structure/engineeringcart/Destroy() +/obj/structure/cart/storage/engineeringcart/Destroy() QDEL_NULL(my_glass) QDEL_NULL(my_metal) QDEL_NULL(my_plasteel) @@ -100,7 +81,7 @@ QDEL_NULL(my_red_toolbox) return ..() -/obj/structure/engineeringcart/attackby(obj/item/attacking_item, mob/user) +/obj/structure/cart/storage/engineeringcart/attackby(obj/item/attacking_item, mob/user) if(is_type_in_typecache(attacking_item, allowed_types)) var/should_store = FALSE var/storage_is_full = FALSE @@ -171,28 +152,7 @@ return ..() -// copied from janicart code -/obj/structure/engineeringcart/proc/take_apart(var/mob/user = null, var/obj/I) - if(has_items) - spill() - - if(user) - - if(iswelder(I)) - var/obj/item/welder = I - welder.play_tool_sound(get_turf(src), 50) - - user.visible_message("[user] starts taking apart the [src]...", SPAN_NOTICE("You start disassembling the [src]...")) - if (!do_after(user, 30, do_flags = DO_DEFAULT & ~DO_USER_SAME_HAND)) - return - - dismantle() - -/obj/structure/engineeringcart/ex_act(severity) - spill(100 / severity) - ..() - -/obj/structure/engineeringcart/proc/spill(var/chance = 100) +/obj/structure/cart/storage/engineeringcart/spill(var/chance = 100) var/turf/dropspot = get_turf(src) if(LAZYLEN(my_glass) && prob(chance)) var/obj/item/stack/material/glass/G @@ -243,7 +203,7 @@ update_icon() -/obj/structure/engineeringcart/proc/handle_storing(var/attacking_item, var/mob/user, var/should_store, var/storage_is_full) +/obj/structure/cart/storage/engineeringcart/handle_storing(var/attacking_item, var/mob/user, var/should_store, var/storage_is_full) if(should_store) user.drop_from_inventory(attacking_item, src) get_storage_contents_list() @@ -254,7 +214,7 @@ else to_chat(user, SPAN_WARNING("You can't store this here!")) -/obj/structure/engineeringcart/attack_hand(mob/user) +/obj/structure/cart/storage/engineeringcart/attack_hand(mob/user) if(!isliving(user)) return @@ -309,7 +269,7 @@ else to_chat(user, SPAN_WARNING("\The [chosen_item] is not in the cart anymore!")) -/obj/structure/engineeringcart/update_icon() +/obj/structure/cart/storage/engineeringcart/update_icon() ClearOverlays() has_items = FALSE if(my_plasteel.len) @@ -334,92 +294,9 @@ AddOverlays("cart_redtoolbox") has_items = TRUE +/obj/structure/cart/storage/engineeringcart/half_filled -/// For future reference, this kind of pulling functions are dependant on a type-check in `mob_movement.dm` line:332 -/obj/structure/engineeringcart/relaymove(mob/living/user, direction) - . = ..() - - if(user.stat || user.stunned || user.weakened || user.paralysis || user.lying || user.restrained()) - if(user==pulling) - pulling = null - user.pulledby = null - to_chat(user, SPAN_WARNING("You lost your grip!")) - return - if(user.pulling && (user == pulling)) - pulling = null - user.pulledby = null - return - if(pulling && (get_dist(src, pulling) > 1)) - pulling = null - user.pulledby = null - if(user==pulling) - return - if(pulling && (get_dir(src.loc, pulling.loc) == direction)) - to_chat(user, SPAN_WARNING("You cannot go there.")) - return - - driving = 1 - var/turf/T = null - if(pulling) - T = pulling.loc - if(get_dist(src, pulling) >= 1) - step(pulling, get_dir(pulling.loc, src.loc)) - step(src, direction) - set_dir(direction) - if(pulling) - if(pulling.loc == src.loc) - pulling.forceMove(T) - else - spawn(0) - if(get_dist(src, pulling) > 1) - pulling = null - user.pulledby = null - pulling.set_dir(get_dir(pulling, src)) - driving = 0 - -/obj/structure/engineeringcart/Move() - . = ..() - if (pulling && (get_dist(src, pulling) > 1)) - pulling.pulledby = null - to_chat(pulling, SPAN_WARNING("You lost your grip!")) - pulling = null - if(has_gravity()) - playsound(src, 'sound/effects/roll.ogg', 50, 1) - -/obj/structure/engineeringcart/CtrlClick(var/mob/user) - if(in_range(src, user)) - if(!ishuman(user)) return - if(!pulling) - pulling = user - user.pulledby = src - if(user.pulling) - user.stop_pulling() - user.set_dir(get_dir(user, src)) - to_chat(user, "You grip \the [name]'s handles.") - else - to_chat(usr, "You let go of \the [name]'s handles.") - pulling.pulledby = null - pulling = null - return - -/obj/structure/engineeringcart/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(air_group || (height==0)) - return TRUE - if(mover?.movement_type & PHASING) - return TRUE - if(istype(mover) && mover.pass_flags & PASSTABLE) - return TRUE - if(istype(mover, /mob/living) && mover == pulling) - return TRUE - else - if(istype(mover, /obj/projectile)) - return prob(30) - else - return !density - -/obj/structure/engineeringcart/half_filled - -/obj/structure/engineeringcart/half_filled/Initialize() +/obj/structure/cart/storage/engineeringcart/half_filled/Initialize() . = ..() my_blue_toolbox = new /obj/item/storage/toolbox/mechanical(src) my_yellow_toolbox = new /obj/item/storage/toolbox/electrical(src) @@ -427,9 +304,9 @@ get_storage_contents_list() update_icon() -/obj/structure/engineeringcart/full +/obj/structure/cart/storage/engineeringcart/full -/obj/structure/engineeringcart/full/Initialize() +/obj/structure/cart/storage/engineeringcart/full/Initialize() . = ..() my_lightreplacer = new /obj/item/device/lightreplacer(src) my_blue_toolbox = new /obj/item/storage/toolbox/mechanical(src) diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/carts/janicart.dm similarity index 70% rename from code/game/objects/structures/janicart.dm rename to code/game/objects/structures/carts/janicart.dm index 4e597edb0fb..620f03e73e0 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/carts/janicart.dm @@ -1,14 +1,8 @@ -/obj/structure/janitorialcart +/obj/structure/cart/storage/janitorialcart name = "custodial cart" desc = "The ultimate in custodial carts. Has space for water, mops, signs, trash bags, and more." icon = 'icons/obj/janitor.dmi' icon_state = "cart" - anchored = FALSE - density = TRUE - climbable = TRUE - atom_flags = ATOM_FLAG_OPEN_CONTAINER - build_amt = 15 - slowdown = 0 var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite var/obj/item/storage/bag/trash/mybag = null @@ -17,22 +11,15 @@ var/obj/item/device/lightreplacer/myreplacer = null var/obj/structure/mopbucket/mybucket = null var/signs = 0 //maximum capacity hardcoded below - var/has_items = FALSE //This is set true whenever the cart has anything loaded/mounted on it - var/driving - var/mob/living/pulling -/obj/structure/janitorialcart/mechanics_hints(mob/user, distance, is_adjacent) +/obj/structure/cart/storage/janitorialcart/mechanics_hints(mob/user, distance, is_adjacent) . += ..() . += "Click and drag a mop bucket onto the cart to mount it." . += "ALT-Click with a mop to put it away; a normal click will wet it in the bucket." . += "ALT-Click with a container, such as a bucket, to pour its contents into the mounted bucket. A normal click will toss it into the trash." . += "You can use a light replacer, spraybottle (of space cleaner) and four wet-floor signs on the cart to store them." -/obj/structure/janitorialcart/disassembly_hints(mob/user, distance, is_adjacent) - . += ..() - . += "An empty custodial cart can be taken apart with a wrench or a welder. Or a plasma cutter, if you're that hardcore." - -/obj/structure/janitorialcart/feedback_hints(mob/user, distance, is_adjacent) +/obj/structure/cart/storage/janitorialcart/feedback_hints(mob/user, distance, is_adjacent) . += ..() if(distance <= 1) if (mybucket) @@ -44,7 +31,7 @@ // Regular Variant // No trashbag and no light replacer, this is inside the custodian's locker. -/obj/structure/janitorialcart/Initialize() +/obj/structure/cart/storage/janitorialcart/Initialize() . = ..() mymop = new /obj/item/mop(src) myspray = new /obj/item/reagent_containers/spray/cleaner(src) @@ -58,7 +45,7 @@ // Full Variant // Has everything. -/obj/structure/janitorialcart/full/Initialize() +/obj/structure/cart/storage/janitorialcart/full/Initialize() . = ..() mybag = new /obj/item/storage/bag/trash(src) mymop = new /obj/item/mop(src) @@ -74,7 +61,7 @@ // Full with Water Variant // Has everything as well as water in the mop bucket. -/obj/structure/janitorialcart/full/water/Initialize() +/obj/structure/cart/storage/janitorialcart/full/water/Initialize() . = ..() mybag = new /obj/item/storage/bag/trash(src) mymop = new /obj/item/mop(src) @@ -89,12 +76,12 @@ update_icon() -/obj/structure/janitorialcart/New() +/obj/structure/cart/storage/janitorialcart/New() ..() if(is_station_turf(get_turf(src))) GLOB.janitorial_supplies |= src -/obj/structure/janitorialcart/Destroy() +/obj/structure/cart/storage/janitorialcart/Destroy() if(src in GLOB.janitorial_supplies) GLOB.janitorial_supplies -= src QDEL_NULL(mybag) @@ -104,10 +91,10 @@ QDEL_NULL(mybucket) return ..() -/obj/structure/janitorialcart/proc/get_short_status() +/obj/structure/cart/storage/janitorialcart/proc/get_short_status() return "Contents: [english_list(contents)]" -/obj/structure/janitorialcart/mouse_drop_receive(atom/dropped, mob/user, params) +/obj/structure/cart/storage/janitorialcart/mouse_drop_receive(atom/dropped, mob/user, params) var/atom/movable/O = dropped if (istype(O, /obj/structure/mopbucket) && !mybucket) O.forceMove(src) @@ -120,7 +107,7 @@ //New Altclick functionality! //Altclick the cart with a mop to stow the mop away //Altclick the cart with a reagent container to pour things into the bucket without putting the bottle in trash -/obj/structure/janitorialcart/AltClick() +/obj/structure/cart/storage/janitorialcart/AltClick() if(!usr || usr.stat || usr.lying || usr.restrained() || !Adjacent(usr)) return var/obj/I = usr.get_active_hand() if(istype(I, /obj/item/mop)) @@ -141,7 +128,7 @@ if (LR.store_broken) return mybag.attackby(I, usr) -/obj/structure/janitorialcart/attackby(obj/item/attacking_item, mob/user) +/obj/structure/cart/storage/janitorialcart/attackby(obj/item/attacking_item, mob/user) if(istype(attacking_item, /obj/item/mop) || istype(attacking_item, /obj/item/reagent_containers/glass/rag) || istype(attacking_item, /obj/item/soap)) if (mybucket) if(attacking_item.reagents.total_volume < attacking_item.reagents.maximum_volume) @@ -205,28 +192,8 @@ return ..() -/obj/structure/janitorialcart/proc/take_apart(var/mob/user = null, var/obj/I) - if(has_items) - spill() - - if(user) - - if(iswelder(I)) - var/obj/item/welder = I - welder.play_tool_sound(get_turf(src), 50) - - user.visible_message("[user] starts taking apart the [src]...", SPAN_NOTICE("You start disassembling the [src]...")) - if (!do_after(user, 30, do_flags = DO_DEFAULT & ~DO_USER_SAME_HAND)) - return - - dismantle() - -/obj/structure/janitorialcart/ex_act(severity) - spill(100 / severity) - ..() - //This is called if the cart is caught in an explosion, or destroyed by weapon fire -/obj/structure/janitorialcart/proc/spill(var/chance = 100) +/obj/structure/cart/storage/janitorialcart/spill(var/chance = 100) var/turf/dropspot = get_turf(src) if (mymop && prob(chance)) mymop.forceMove(dropspot) @@ -267,11 +234,11 @@ update_icon() -/obj/structure/janitorialcart/attack_hand(mob/user) +/obj/structure/cart/storage/janitorialcart/attack_hand(mob/user) ui_interact(user) return -/obj/structure/janitorialcart/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/structure/cart/storage/janitorialcart/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] data["name"] = capitalize(name) data["bag"] = mybag ? capitalize(mybag.name) : null @@ -287,7 +254,7 @@ ui.set_initial_data(data) ui.open() -/obj/structure/janitorialcart/Topic(href, href_list) +/obj/structure/cart/storage/janitorialcart/Topic(href, href_list) if(!in_range(src, usr)) return if(!isliving(usr)) @@ -335,7 +302,7 @@ update_icon() updateUsrDialog() -/obj/structure/janitorialcart/update_icon() +/obj/structure/cart/storage/janitorialcart/update_icon() ClearOverlays() has_items = 0 if(mybucket) @@ -361,83 +328,3 @@ if(signs) AddOverlays("cart_sign[signs]") has_items = 1 - -//Shamelessly copied from wheelchair code -/obj/structure/janitorialcart/relaymove(mob/living/user, direction) - . = ..() - - if(user.stat || user.stunned || user.weakened || user.paralysis || user.lying || user.restrained()) - if(user==pulling) - pulling = null - user.pulledby = null - to_chat(user, SPAN_WARNING("You lost your grip!")) - return - if(user.pulling && (user == pulling)) - pulling = null - user.pulledby = null - return - if(pulling && (get_dist(src, pulling) > 1)) - pulling = null - user.pulledby = null - if(user==pulling) - return - if(pulling && (get_dir(src.loc, pulling.loc) == direction)) - to_chat(user, SPAN_WARNING("You cannot go there.")) - return - - driving = 1 - var/turf/T = null - if(pulling) - T = pulling.loc - if(get_dist(src, pulling) >= 1) - step(pulling, get_dir(pulling.loc, src.loc)) - step(src, direction) - set_dir(direction) - if(pulling) - if(pulling.loc == src.loc) - pulling.forceMove(T) - else - spawn(0) - if(get_dist(src, pulling) > 1) - pulling = null - user.pulledby = null - pulling.set_dir(get_dir(pulling, src)) - driving = 0 - -/obj/structure/janitorialcart/Move() - . = ..() - if (pulling && (get_dist(src, pulling) > 1)) - pulling.pulledby = null - to_chat(pulling, SPAN_WARNING("You lost your grip!")) - pulling = null - -/obj/structure/janitorialcart/CtrlClick(var/mob/user) - if(in_range(src, user)) - if(!ishuman(user)) return - if(!pulling) - pulling = user - user.pulledby = src - if(user.pulling) - user.stop_pulling() - user.set_dir(get_dir(user, src)) - to_chat(user, "You grip \the [name]'s handles.") - else - to_chat(usr, "You let go of \the [name]'s handles.") - pulling.pulledby = null - pulling = null - return - -/obj/structure/janitorialcart/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(air_group || (height==0)) - return TRUE - if(mover?.movement_type & PHASING) - return TRUE - if(istype(mover) && mover.pass_flags & PASSTABLE) - return TRUE - if(istype(mover, /mob/living) && mover == pulling) - return TRUE - else - if(istype(mover, /obj/projectile)) - return prob(30) - else - return !density diff --git a/code/game/objects/structures/carts/parcelcart.dm b/code/game/objects/structures/carts/parcelcart.dm new file mode 100644 index 00000000000..21113e621dd --- /dev/null +++ b/code/game/objects/structures/carts/parcelcart.dm @@ -0,0 +1,123 @@ +/obj/structure/cart/storage/parcelcart + name = "parcel cart" + desc = "A cart for moving parcels and packages around." + icon = 'icons/obj/parcelcart.dmi' + icon_state = "cart" + + var/list/my_parcels = list() + + /// Amount of parcels the cart is capable to store. + var/parcel_capacity = 17 + + var/static/list/allowed_types = typecacheof(list( + /obj/item/smallDelivery + )) + +/obj/structure/cart/storage/parcelcart/mechanics_hints(mob/user, distance, is_adjacent) + . += ..() + . += "You can use hand held parcels on the cart to store them." + . += "It slows down at 5 packages, and becomes even slower when there is 11 packages or more loaded." + +/obj/structure/cart/storage/parcelcart/get_storage_contents_list() + storage_contents.Cut() + var/list/lists_to_check = list( + my_parcels + ) + for(var/list/list_to_check in lists_to_check) + if(list_to_check?.len) //null check + storage_contents += list_to_check + + var/list/non_sheet_objects = list(my_parcels) + + for(var/obj/O in non_sheet_objects) + if(O) + storage_contents += O + +/obj/structure/cart/storage/parcelcart/Destroy() + QDEL_NULL(my_parcels) + return ..() + +/obj/structure/cart/storage/parcelcart/attackby(obj/item/attacking_item, mob/user) + if(is_type_in_typecache(attacking_item, allowed_types)) + var/should_store = FALSE + var/storage_is_full = FALSE + if(my_parcels.len < parcel_capacity) + my_parcels += attacking_item + should_store = TRUE + else + storage_is_full = TRUE + + handle_storing(attacking_item, user, should_store, storage_is_full) + return TRUE + + else if (!has_items && (attacking_item.iswrench() || attacking_item.iswelder() || istype(attacking_item, /obj/item/gun/energy/plasmacutter))) + take_apart(user, attacking_item) + return + ..() + +/obj/structure/cart/storage/parcelcart/spill(var/chance = 100) + var/turf/dropspot = get_turf(src) + + if(LAZYLEN(my_parcels) && prob(chance)) + var/obj/item/smallDelivery/M + for(var/I in my_parcels) + M = I + M.forceMove(dropspot) + M.tumble(1) + my_parcels -= M + my_parcels.Cut() + update_icon() + +/obj/structure/cart/storage/parcelcart/handle_storing(var/attacking_item, var/mob/user, var/should_store, var/storage_is_full) + if(should_store) + user.drop_from_inventory(attacking_item, src) + get_storage_contents_list() + update_slowdown() + update_icon() + to_chat(user, SPAN_NOTICE("You put [attacking_item] into [src].")) + else if(storage_is_full) + to_chat(user, SPAN_WARNING("There isn't any space to store [attacking_item] in [src]!")) + else + to_chat(user, SPAN_WARNING("You can't store this here!")) + +/obj/structure/cart/storage/parcelcart/update_slowdown() + if(my_parcels.len <= 4) + slowdown = 0 + else if(my_parcels.len > 4 && my_parcels.len < 11) + slowdown = 1 + else if(my_parcels.len >= 11) + slowdown = 2 + +/obj/structure/cart/storage/parcelcart/attack_hand(mob/user) + if(!isliving(user)) + return + + if(LAZYLEN(storage_contents)) + for(var/obj/O in storage_contents) + storage_contents[O] = image(O.icon, O.icon_state) + + var/obj/item/chosen_item = show_radial_menu(user, src, storage_contents, require_near = TRUE, tooltips = TRUE) + + if(isnull(chosen_item)) + return + if(chosen_item in storage_contents) + switch(chosen_item.type) + if(/obj/item/smallDelivery) + if(my_parcels.len) + user.put_in_hands(chosen_item) + to_chat(user, SPAN_NOTICE("You take [my_parcels[chosen_item]] from [src].")) + my_parcels -= chosen_item + + get_storage_contents_list() + update_slowdown() + update_icon() + else + to_chat(user, SPAN_WARNING("\The [chosen_item] is not in the cart anymore!")) + +/obj/structure/cart/storage/parcelcart/update_icon() + ClearOverlays() + has_items = FALSE + if(my_parcels.len) + var/amount_to_show = min(length(my_parcels), parcel_capacity) + AddOverlays("pack_[amount_to_show]") + has_items = TRUE diff --git a/code/game/objects/structures/mop_bucket.dm b/code/game/objects/structures/mop_bucket.dm index 22c38ee95f0..c0e242a34f5 100644 --- a/code/game/objects/structures/mop_bucket.dm +++ b/code/game/objects/structures/mop_bucket.dm @@ -44,8 +44,8 @@ /obj/structure/mopbucket/on_reagent_change() . = ..() - if(istype(loc,/obj/structure/janitorialcart)) - var/obj/structure/janitorialcart/cart = loc + if(istype(loc,/obj/structure/cart/storage/janitorialcart)) + var/obj/structure/cart/storage/janitorialcart/cart = loc cart.update_icon() else update_icon() diff --git a/code/modules/cargo/items/custodial.dm b/code/modules/cargo/items/custodial.dm index 16b4c7479c8..20d6e15835d 100644 --- a/code/modules/cargo/items/custodial.dm +++ b/code/modules/cargo/items/custodial.dm @@ -145,7 +145,7 @@ description = "The ultimate in custodial carts. Has space for water, mops, signs, trash bags, and more." price = 190 items = list( - /obj/structure/janitorialcart + /obj/structure/cart/storage/janitorialcart ) access = ACCESS_JANITOR container_type = "crate" diff --git a/code/modules/cargo/items/engineering.dm b/code/modules/cargo/items/engineering.dm index 793d8ff94b2..a506f5395db 100644 --- a/code/modules/cargo/items/engineering.dm +++ b/code/modules/cargo/items/engineering.dm @@ -903,7 +903,7 @@ description = "A cart for your engineering-related storage needs." price = 100 items = list( - /obj/structure/engineeringcart + /obj/structure/cart/storage/engineeringcart ) access = ACCESS_ENGINE container_type = "crate" diff --git a/code/modules/cargo/items/operations.dm b/code/modules/cargo/items/operations.dm index edc03323285..18b15942094 100644 --- a/code/modules/cargo/items/operations.dm +++ b/code/modules/cargo/items/operations.dm @@ -185,7 +185,7 @@ description = "A set of items to restock the janitors closet." price = 2000 items = list( - /obj/structure/janitorialcart, + /obj/structure/cart/storage/janitorialcart, /obj/structure/mopbucket, /obj/item/mop, /obj/item/storage/bag/trash, diff --git a/code/modules/item_worth/worths_list.dm b/code/modules/item_worth/worths_list.dm index 445d8911368..a1a99f08924 100644 --- a/code/modules/item_worth/worths_list.dm +++ b/code/modules/item_worth/worths_list.dm @@ -689,7 +689,7 @@ var/list/worths = list( /obj/structure/girder = 15, /obj/structure/grille = 5, /obj/structure/inflatable = 1, - /obj/structure/janitorialcart = 12, + /obj/structure/cart/storage/janitorialcart = 12, /obj/structure/kitchenspike = 35, /obj/structure/lattice = 1, /obj/structure/morgue = 10, diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm index 26c452f42f3..3cc43ed85a9 100644 --- a/code/modules/materials/material_recipes.dm +++ b/code/modules/materials/material_recipes.dm @@ -71,8 +71,9 @@ recipes += new /datum/stack_recipe_list("miscellaneous construction", list( new /datum/stack_recipe("key", /obj/item/key, 1, time = 10, one_per_turf = 0, on_floor = 1), - new /datum/stack_recipe("custodial cart", /obj/structure/janitorialcart, BUILD_AMT, time = 120, one_per_turf = 1, on_floor = 1), - new /datum/stack_recipe("engineering cart", /obj/structure/engineeringcart, BUILD_AMT, time = 120, one_per_turf = 1, on_floor = 1), + new /datum/stack_recipe("custodial cart", /obj/structure/cart/storage/janitorialcart, BUILD_AMT, time = 120, one_per_turf = 1, on_floor = 1), + new /datum/stack_recipe("engineering cart", /obj/structure/cart/storage/engineeringcart, BUILD_AMT, time = 120, one_per_turf = 1, on_floor = 1), + new /datum/stack_recipe("parcel cart", /obj/structure/cart/storage/parcelcart, BUILD_AMT, time = 120, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("steel closet", /obj/structure/closet, BUILD_AMT, time = 15, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("canister", /obj/machinery/portable_atmospherics/canister, 10, time = 15, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("target stake", /obj/structure/target_stake, BUILD_AMT, time = 15, one_per_turf = 1, on_floor = 1), diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 59fb6e6773c..fe2a1d965f6 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -329,7 +329,7 @@ //Wheelchair pushing goes here for now. //TODO: Fuck wheelchairs. - if(istype(mob.pulledby, /obj/structure/bed/stool/chair/office/wheelchair) || istype(mob.pulledby, /obj/structure/janitorialcart) || istype(mob.pulledby, /obj/structure/engineeringcart)) + if(istype(mob.pulledby, /obj/structure/bed/stool/chair/office/wheelchair) || istype(mob.pulledby, /obj/structure/cart)) var/obj/structure/S = mob.pulledby move_delay += S.slowdown return mob.pulledby.relaymove(mob, direct) diff --git a/code/modules/modular_computers/file_system/programs/civilian/janitor.dm b/code/modules/modular_computers/file_system/programs/civilian/janitor.dm index eb6c521040b..3520b22ad3b 100644 --- a/code/modules/modular_computers/file_system/programs/civilian/janitor.dm +++ b/code/modules/modular_computers/file_system/programs/civilian/janitor.dm @@ -48,8 +48,8 @@ var/mob/living/bot/cleanbot/C = A status = C.on ? "Online" : "Offline" supply_type = "Cleanbots" - else if(istype(A, /obj/structure/janitorialcart)) - var/obj/structure/janitorialcart/J = A + else if(istype(A, /obj/structure/cart/storage/janitorialcart)) + var/obj/structure/cart/storage/janitorialcart/J = A status = J.get_short_status() supply_type = "Janicarts" else diff --git a/html/changelogs/ParcelCart.yml b/html/changelogs/ParcelCart.yml new file mode 100644 index 00000000000..b98c1a007c3 --- /dev/null +++ b/html/changelogs/ParcelCart.yml @@ -0,0 +1,7 @@ +author: TheGreyWolf & Tomixcomics + +delete-after: True + +changes: + - rscadd: "Added a parcel cart to the operations mail room. It can carry up to 17 handheld packages regardless of size." + - bugfix: "Fixed the engineering cart giving plasteel when taken apart." diff --git a/icons/obj/parcelcart.dmi b/icons/obj/parcelcart.dmi new file mode 100644 index 0000000000000000000000000000000000000000..aa89a986ce70f6cf68225943c312ddd792052b5e GIT binary patch literal 1296 zcmV+r1@HQaP)004jp0{{R3yS#y#xQok}VxXX)si~=(Y&y7wPL!0Crguc*+}w9} zcchJAIYCb(BqY$Ye3NZN0jjY100001bW%=J06^y0W&i*Hr+QRabVOxyV{&P5bZKvH z004NLjncge!Y~wu;W@dAL%T~)TmJ?ZX^{^079_OM1hJLmptoUwIQ$lD+mGVXs7kGz^*1NWh+@R1GaF2hOsmm{m?mp=4d!8&`-h%p zxL+7!CB`a@)fii2Y=f~a#&#IngU?%l4(z~S2nI_qn1aC;494KUHF9eE=KJYC6YJ9w zYYpH5000BSNklHjD-t6IP;wdh^vBbL%X03V=Pec!Nr$Yjb-FL z^4!GYHm+w-qc30T8PtI$H5K$e#W@SA$+4RE#vJ^7Jj_{8O^!4V^fU{86)H#*1vL+x zM{f@5`^%vw;+;50wE6I>T8eiy@1 z{h;o8f3~jm$o2O9^StT%_PlAsTyM8)defGsnaSIR0t z69pAdeRL_9Q^e~$c;ur?Y5%9)ONvK6x)k<*gpwsZ@GHMwprGHA1yqt;={Mi;%lf^p z<3L{6@5utoxYBQ4QCigRW#BFE3Efk5yX`3*hy|vU;!oa-xE4%rk#9%y?PeWd+E$D| z`8hSw1`n4%sAj2%bpWnYj6eA~>b_@BTpVB+G$wE5wx9F7v7e?9DkrGM{H@&fbLtb{`X`Tw(GafjCaxVpW3ot1^ zXK*%p{^z$eH~pO0!F?GRoHNf0Il>8$>ll7c>~L|-gTeVLoXEAvdVXKP@N=Re^Shhr zqXU0|6T(%2wS3;q^mCLup3~&?G`_$&Cm?=b!0>bGJR<(?C9VpP^?csV^mFPwBL40* z&gD}`*7JFRxSxY?E=vHoPFcSv3IsW&alUz?f00x}2wkvKX}=faa!@Dz;B*6R;{W)# zzz>%8doj-W3x06i%Kt6&gWLJPg??~5|F_T&Zs-4+A8cu9X<3}+_kh{j??v(l{a)oo z$GlgvfZxkj5bEzjobxPys|fXX5uU3c%-=2ad+W2>-|=NUPr)_*j_Wy+;yD_w^LKNB z6wlCboxi&mNO5RVelEoMbepC)I4M6D;i^E2gOl=eP!3MY&p|mjDL)70@Pa`