diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 19a8b03ec40..e948a8f00cc 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -3803,7 +3803,7 @@ /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 10 }, -/obj/machinery/computer/cargo/request{ +/obj/machinery/computer/department_orders/security{ dir = 8 }, /turf/open/floor/iron, @@ -28391,8 +28391,8 @@ "dFg" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/bot, -/obj/machinery/computer/cargo/request, /obj/effect/turf_decal/trimline/brown/filled/line, +/obj/machinery/computer/department_orders/science, /turf/open/floor/iron, /area/science/research) "dFs" = ( @@ -57959,10 +57959,10 @@ /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 10 }, -/obj/machinery/computer/cargo/request{ +/obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/structure/disposalpipe/segment{ +/obj/machinery/computer/department_orders/engineering{ dir = 4 }, /turf/open/floor/iron, @@ -62724,7 +62724,7 @@ /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 1 }, -/obj/machinery/computer/cargo/request{ +/obj/machinery/computer/department_orders/service{ dir = 8 }, /turf/open/floor/iron, @@ -98390,7 +98390,7 @@ /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 5 }, -/obj/machinery/computer/cargo/request{ +/obj/machinery/computer/department_orders/medical{ dir = 1 }, /turf/open/floor/iron, diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 6b3946d1aaf..2a7879ae0d4 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -10529,11 +10529,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/tcommsat/computer) -"bZw" = ( -/obj/structure/chair/stool/directional/east, -/obj/effect/landmark/start/assistant, -/turf/open/floor/iron, -/area/commons/locker) "bZC" = ( /obj/structure/chair/stool/directional/west, /obj/effect/turf_decal/tile/green{ @@ -13995,12 +13990,12 @@ }, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/computer/cargo/request{ - dir = 1 - }, /obj/effect/turf_decal/trimline/brown/filled/end{ dir = 4 }, +/obj/machinery/computer/department_orders/security{ + dir = 1 + }, /turf/open/floor/iron, /area/security/office) "dhJ" = ( @@ -29295,11 +29290,6 @@ }, /turf/open/floor/iron/white, /area/medical/medbay/central) -"lcq" = ( -/obj/structure/chair/stool/directional/east, -/obj/effect/landmark/start/assistant, -/turf/open/floor/iron, -/area/commons/dorms) "lcz" = ( /obj/effect/turf_decal/tile/red/full, /obj/effect/turf_decal/siding/white/corner, @@ -30371,10 +30361,10 @@ /turf/open/floor/iron/white, /area/science/lab) "lIj" = ( -/obj/machinery/computer/cargo/request, /obj/effect/turf_decal/trimline/brown/filled/end{ dir = 4 }, +/obj/machinery/computer/department_orders/medical, /turf/open/floor/iron, /area/medical/medbay/aft) "lIw" = ( @@ -30647,10 +30637,6 @@ }, /turf/open/floor/iron, /area/security/office) -"lPE" = ( -/obj/structure/chair/stool/directional/east, -/turf/open/floor/iron, -/area/commons/dorms) "lPM" = ( /obj/structure/chair{ dir = 1 @@ -34640,10 +34626,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/hallway/primary/central) -"nQn" = ( -/obj/structure/chair/stool/directional/north, -/turf/open/floor/iron, -/area/security/prison) "nQw" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/layer_manifold/dark, @@ -34756,10 +34738,10 @@ /turf/open/floor/iron/dark, /area/science/breakroom) "nSm" = ( -/obj/machinery/computer/chef_order{ +/obj/effect/turf_decal/stripes/line{ dir = 4 }, -/obj/effect/turf_decal/stripes/line{ +/obj/machinery/modular_computer/console/preset/cargochat/service{ dir = 4 }, /turf/open/floor/plating, @@ -44967,10 +44949,6 @@ }, /turf/open/floor/iron, /area/ai_monitored/command/storage/eva) -"tjn" = ( -/obj/structure/chair/stool/directional/east, -/turf/open/floor/iron, -/area/commons/locker) "tjs" = ( /obj/structure/table, /obj/item/paper_bin{ @@ -50412,9 +50390,6 @@ /turf/open/floor/iron, /area/science/storage) "vUo" = ( -/obj/machinery/computer/cargo/request{ - dir = 1 - }, /obj/effect/turf_decal/tile/brown{ dir = 8 }, @@ -50423,6 +50398,9 @@ }, /obj/machinery/bounty_board/directional/south, /obj/machinery/light/directional/south, +/obj/machinery/computer/department_orders/science{ + dir = 1 + }, /turf/open/floor/iron, /area/science/lab) "vUu" = ( @@ -50688,7 +50666,6 @@ /turf/open/floor/iron/freezer, /area/command/heads_quarters/captain) "wbm" = ( -/obj/machinery/computer/cargo/request, /obj/effect/turf_decal/tile/brown, /obj/effect/turf_decal/tile/brown{ dir = 4 @@ -50699,6 +50676,7 @@ /obj/effect/turf_decal/tile/brown{ dir = 8 }, +/obj/machinery/computer/department_orders/engineering, /turf/open/floor/iron/dark, /area/engineering/lobby) "wbB" = ( @@ -51925,10 +51903,10 @@ /turf/open/floor/iron, /area/service/janitor) "wIs" = ( -/obj/machinery/computer/cargo/request{ +/obj/effect/turf_decal/stripes/line{ dir = 4 }, -/obj/effect/turf_decal/stripes/line{ +/obj/machinery/computer/department_orders/service{ dir = 4 }, /turf/open/floor/plating, diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index 055fdf56a2b..2298b4d649d 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -2081,7 +2081,7 @@ /area/maintenance/starboard) "ajq" = ( /obj/effect/decal/cleanable/dirt, -/obj/machinery/computer/cargo/request{ +/obj/machinery/computer/department_orders/service{ dir = 8 }, /turf/open/floor/plating{ @@ -11887,12 +11887,12 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/machinery/computer/cargo/request{ - dir = 1 - }, /obj/effect/turf_decal/trimline/brown/filled/end{ dir = 4 }, +/obj/machinery/computer/department_orders/science{ + dir = 1 + }, /turf/open/floor/iron/dark, /area/science/research) "bgo" = ( @@ -20972,7 +20972,7 @@ /turf/open/floor/iron, /area/security/brig) "cmB" = ( -/obj/machinery/computer/cargo/request{ +/obj/machinery/computer/department_orders/engineering{ dir = 1 }, /turf/open/floor/iron/dark, @@ -32669,10 +32669,10 @@ dir = 4 }, /obj/structure/cable, -/obj/machinery/computer/cargo/request{ +/obj/structure/disposalpipe/segment, +/obj/machinery/computer/department_orders/medical{ dir = 8 }, -/obj/structure/disposalpipe/segment, /turf/open/floor/iron/showroomfloor, /area/medical/medbay/central) "fuO" = ( @@ -70007,12 +70007,12 @@ /obj/effect/turf_decal/tile/neutral{ dir = 4 }, -/obj/machinery/computer/cargo/request{ - dir = 1 - }, /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/machinery/computer/department_orders/security{ + dir = 1 + }, /turf/open/floor/iron/dark, /area/security/office) "tas" = ( diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index b14c1173422..e762ab4a4f2 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -13896,7 +13896,7 @@ /turf/open/floor/iron/white, /area/medical/surgery) "cXm" = ( -/obj/machinery/computer/cargo/request{ +/obj/machinery/computer/department_orders/science{ dir = 4 }, /turf/open/floor/iron, @@ -26632,13 +26632,13 @@ /turf/open/floor/iron, /area/commons/fitness/recreation) "hoa" = ( -/obj/machinery/computer/cargo/request, /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 4 }, /obj/effect/turf_decal/trimline/yellow/filled/warning{ dir = 4 }, +/obj/machinery/computer/department_orders/engineering, /turf/open/floor/iron, /area/engineering/break_room) "hoe" = ( @@ -27385,7 +27385,7 @@ /obj/effect/turf_decal/trimline/red/filled/warning{ dir = 8 }, -/obj/machinery/computer/cargo/request{ +/obj/machinery/computer/department_orders/security{ dir = 8 }, /turf/open/floor/iron, @@ -38223,7 +38223,7 @@ /area/science/misc_lab) "lGw" = ( /obj/effect/decal/cleanable/dirt, -/obj/machinery/computer/cargo/request{ +/obj/machinery/computer/department_orders/service{ dir = 8 }, /turf/open/floor/iron, @@ -57752,7 +57752,7 @@ /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 1 }, -/obj/machinery/computer/cargo/request{ +/obj/machinery/computer/department_orders/medical{ dir = 1 }, /turf/open/floor/iron/dark, diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index 9e0d7efc458..23c97a66751 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -8673,12 +8673,12 @@ /turf/open/floor/iron/dark, /area/ai_monitored/turret_protected/ai_upload_foyer) "bmm" = ( -/obj/machinery/computer/cargo/request, /obj/effect/turf_decal/trimline/neutral/filled/line{ dir = 5 }, /obj/machinery/airalarm/directional/north, /obj/machinery/light/directional/east, +/obj/machinery/computer/department_orders/service, /turf/open/floor/iron, /area/hallway/primary/central) "bmz" = ( @@ -10787,7 +10787,7 @@ /obj/effect/turf_decal/trimline/red/filled/warning{ dir = 6 }, -/obj/machinery/computer/cargo/request, +/obj/machinery/computer/department_orders/security, /turf/open/floor/iron, /area/security/office) "cim" = ( @@ -50287,9 +50287,6 @@ /turf/open/floor/plating, /area/ai_monitored/turret_protected/aisat/foyer) "rgq" = ( -/obj/machinery/computer/cargo/request{ - dir = 8 - }, /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 5 }, @@ -50299,6 +50296,9 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/machinery/computer/department_orders/engineering{ + dir = 8 + }, /turf/open/floor/iron, /area/engineering/break_room) "rgz" = ( @@ -54304,7 +54304,7 @@ "sMu" = ( /obj/effect/turf_decal/trimline/brown/filled/line, /obj/effect/turf_decal/trimline/blue/filled/warning, -/obj/machinery/computer/cargo/request, +/obj/machinery/computer/department_orders/medical, /turf/open/floor/iron, /area/medical/medbay/central) "sMv" = ( @@ -67928,9 +67928,6 @@ dir = 1; pixel_y = 1 }, -/obj/machinery/computer/cargo/request{ - dir = 8 - }, /obj/effect/turf_decal/tile/brown, /obj/effect/turf_decal/tile/brown{ dir = 8 @@ -67941,6 +67938,9 @@ /obj/effect/turf_decal/tile/brown{ dir = 1 }, +/obj/machinery/computer/department_orders/science{ + dir = 8 + }, /turf/open/floor/iron, /area/science/research) "xRY" = ( diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 277ff48e4be..d1d7244fdfa 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -67,6 +67,9 @@ ///from SSsecurity_level when the security level changes : (new_level) #define COMSIG_SECURITY_LEVEL_CHANGED "security_level_changed" +///from SSshuttle when the supply shuttle starts spawning orders : () +#define COMSIG_SUPPLY_SHUTTLE_BUY "supply_shuttle_buy" + ////////////////////////////////////////////////////////////////// // /datum signals @@ -940,6 +943,15 @@ ///used to interrupt insertion #define COMPONENT_CLOSET_INSERT_INTERRUPT (1<<0) +///From open: (forced) +#define COMSIG_CLOSET_PRE_OPEN "closet_pre_open" + #define BLOCK_OPEN (1<<0) +///From open: (forced) +#define COMSIG_CLOSET_POST_OPEN "closet_post_open" + +///a deliver_first element closet was successfully delivered +#define COMSIG_CLOSET_DELIVERED "crate_delivered" + ///Eigenstasium ///From base of [/datum/controller/subsystem/eigenstates/proc/use_eigenlinked_atom]: (var/target) #define COMSIG_EIGENSTATE_ACTIVATE "eigenstate_activate" diff --git a/code/controllers/subsystem/economy.dm b/code/controllers/subsystem/economy.dm index 513f03ec21b..0458b4b26ac 100644 --- a/code/controllers/subsystem/economy.dm +++ b/code/controllers/subsystem/economy.dm @@ -51,11 +51,15 @@ SUBSYSTEM_DEF(economy) var/mail_blocked = FALSE /datum/controller/subsystem/economy/Initialize(timeofday) - var/budget_to_hand_out = round(budget_pool / department_accounts.len) + //removes cargo from the split + var/budget_to_hand_out = round(budget_pool / department_accounts.len -1) if(time2text(world.timeofday, "DDD") == SUNDAY) mail_blocked = TRUE - for(var/A in department_accounts) - new /datum/bank_account/department(A, budget_to_hand_out) + for(var/dep_id in department_accounts) + if(dep_id == ACCOUNT_CAR) //cargo starts with NOTHING + new /datum/bank_account/department(dep_id, 0) + continue + new /datum/bank_account/department(dep_id, budget_to_hand_out) return ..() /datum/controller/subsystem/economy/Recover() diff --git a/code/datums/elements/deliver_first.dm b/code/datums/elements/deliver_first.dm new file mode 100644 index 00000000000..16ab14ebc8a --- /dev/null +++ b/code/datums/elements/deliver_first.dm @@ -0,0 +1,97 @@ +/** + * # deliver first element! + * + * bespoke element (1 per set of specific arguments in existence) that makes crates unable to be sold UNTIL they are opened once (to where this element, and the block, are removed) + * Used for non-cargo orders to not get turned into a quick buck + * + * for the future coders or just me: please convert this into a component to allow for more feedback on the crate's status (clicking when unlocked, overlays, etc) + */ +/datum/element/deliver_first + element_flags = ELEMENT_DETACH | ELEMENT_BESPOKE + id_arg_index = 2 + ///typepath of the area we will be allowed to be opened in + var/goal_area_type + ///how much is earned on delivery of the crate + var/payment + +/datum/element/deliver_first/Attach(datum/target, goal_area_type, payment) + . = ..() + if(!istype(target, /obj/structure/closet)) + return ELEMENT_INCOMPATIBLE + src.goal_area_type = goal_area_type + src.payment = payment + RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine) + RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/on_moved) + RegisterSignal(target, COMSIG_ATOM_EMAG_ACT, .proc/on_emag) + RegisterSignal(target, COMSIG_CLOSET_POST_OPEN, .proc/on_post_open) + ADD_TRAIT(target, TRAIT_BANNED_FROM_CARGO_SHUTTLE, src) + //registers pre_open when appropriate + area_check(target) + +/datum/element/deliver_first/Detach(datum/target) + . = ..() + REMOVE_TRAIT(target, TRAIT_BANNED_FROM_CARGO_SHUTTLE, src) + UnregisterSignal(target, list( + COMSIG_PARENT_EXAMINE, + COMSIG_MOVABLE_MOVED, + COMSIG_ATOM_EMAG_ACT, + COMSIG_CLOSET_PRE_OPEN, + COMSIG_CLOSET_POST_OPEN, + )) + +///signal sent from examining target +/datum/element/deliver_first/proc/on_examine(obj/structure/closet/target, mob/user, list/examine_list) + SIGNAL_HANDLER + examine_list += span_warning("An electronic delivery lock prevents this from opening until it reaches its destination, [GLOB.areas_by_type[goal_area_type]].") + examine_list += span_warning("This crate cannot be sold until it is opened.") + +///registers the signal that blocks target from opening when outside of the valid area, returns if it is now unlocked +/datum/element/deliver_first/proc/area_check(obj/structure/closet/target) + var/area/target_area = get_area(target) + if(target_area.type == goal_area_type) + UnregisterSignal(target, COMSIG_CLOSET_PRE_OPEN) + return TRUE + else + RegisterSignal(target, COMSIG_CLOSET_PRE_OPEN, .proc/on_pre_open, override = TRUE) //very purposefully overriding + return FALSE + +/datum/element/deliver_first/proc/on_moved(obj/structure/closet/target, atom/oldloc, direction) + SIGNAL_HANDLER + area_check(target) + +/datum/element/deliver_first/proc/on_emag(obj/structure/closet/target, mob/emagger) + SIGNAL_HANDLER + emagger.balloon_alert(emagger, "delivery lock bypassed") + remove_lock(target) + +///signal called before opening target, blocks opening +/datum/element/deliver_first/proc/on_pre_open(obj/structure/closet/target, mob/living/user, force) + SIGNAL_HANDLER + if(force) + return + if(istype(target, /obj/structure/closet/crate)) + var/obj/structure/closet/crate/opening_crate = target + if(opening_crate.manifest) //we don't want to send feedback if they're just tearing off the manifest + return BLOCK_OPEN + if(user) + target.balloon_alert(user, "access denied until delivery!") + playsound(target, 'sound/machines/buzz-two.ogg', 30, TRUE) + return BLOCK_OPEN + +///signal called by successfully opening target +/datum/element/deliver_first/proc/on_post_open(obj/structure/closet/target, force) + SIGNAL_HANDLER + if(area_check(target)) + //noice, delivered! + var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR) + cargo_account.adjust_money(payment) + remove_lock(target) + +///called to remove the element in a flavorful way, either from delivery or from emagging/breaking open the crate +/datum/element/deliver_first/proc/remove_lock(obj/structure/closet/target) + target.visible_message(span_notice("[target]'s delivery lock self destructs, spewing sparks from the mechanism!")) + var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() + spark_system.set_up(4, 0, target.loc) + spark_system.start() + playsound(src, "sparks", 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + target.RemoveElement(/datum/element/deliver_first, goal_area_type, payment) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index f60aad00eef..b8aede08a6f 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -255,6 +255,8 @@ return if(opened) return + if(SEND_SIGNAL(src, COMSIG_CLOSET_PRE_OPEN, user, force) & BLOCK_OPEN) + return welded = FALSE locked = FALSE playsound(loc, open_sound, open_sound_volume, TRUE, -3) @@ -264,7 +266,9 @@ dump_contents() animate_door(FALSE) update_appearance() + after_open(user, force) + SEND_SIGNAL(src, COMSIG_CLOSET_POST_OPEN, force) return TRUE ///Proc to override for effects after opening a door diff --git a/code/modules/cargo/department_order.dm b/code/modules/cargo/department_order.dm new file mode 100644 index 00000000000..0eaa1b3396b --- /dev/null +++ b/code/modules/cargo/department_order.dm @@ -0,0 +1,178 @@ + +///cooldown for each department, assoc type 2 cooldown. global, so rebuilding the console doesn't refresh the cd +GLOBAL_LIST_INIT(department_order_cooldowns, list( + /obj/machinery/computer/department_orders/service = 0, + /obj/machinery/computer/department_orders/engineering = 0, + /obj/machinery/computer/department_orders/science = 0, + /obj/machinery/computer/department_orders/security = 0, + /obj/machinery/computer/department_orders/medical = 0, +)) + +/obj/machinery/computer/department_orders + name = "department order console" + desc = "Used to order supplies for a department. Crates ordered this way will be locked until they reach their destination." + icon_screen = "supply" + circuit = /obj/item/circuitboard/computer/cargo + light_color = COLOR_BRIGHT_ORANGE + ///reference to the order we've made UNTIL it gets sent on the supply shuttle. this is so heads can cancel it + var/datum/supply_order/department_order + ///access required to override an order - this should be a head of staff for the department + var/override_access + ///where this computer expects deliveries to need to go, passed onto orders + var/department_delivery_area + ///which groups this computer can order from + var/list/dep_groups = list() + +/obj/machinery/computer/department_orders/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "DepartmentOrders") + ui.open() + +/obj/machinery/computer/department_orders/ui_data(mob/user) + var/list/data = list() + var/cooldown = GLOB.department_order_cooldowns[type] - world.time + if(cooldown < 0) + data["time_left"] = 0 + else + data["time_left"] = DisplayTimeText(cooldown, 1) + data["can_override"] = department_order ? TRUE : FALSE + return data + +/obj/machinery/computer/department_orders/ui_static_data(mob/user) + var/list/data = list() + var/list/supply_data = list() //each item in this needs to be a Category + for(var/pack_key in SSshuttle.supply_packs) + var/datum/supply_pack/pack = SSshuttle.supply_packs[pack_key] + //skip groups we do not offer + if(!(pack.group in dep_groups)) + continue + //find which group this belongs to, make the group if it doesn't exist + var/list/target_group + for(var/list/possible_group in supply_data) + if(possible_group["name"] == pack.group) + target_group = possible_group + break + if(!target_group) + target_group = list( + "name" = pack.group, + "packs" = list(), + ) + supply_data += list(target_group) + //skip packs we should not show, even if we should show the group + if((pack.hidden && !(obj_flags & EMAGGED)) || (pack.special && !pack.special_enabled) || pack.DropPodOnly || pack.goody) + continue + //finally the pack data itself + target_group["packs"] += list(list( + "name" = pack.name, + "cost" = pack.get_cost(), + "id" = pack.type, + "desc" = pack.desc || pack.name, // If there is a description, use it. Otherwise use the pack's name. + )) + data["supplies"] = supply_data + return data + +/obj/machinery/computer/department_orders/ui_act(action, list/params) + . = ..() + + if(!isliving(usr)) + return + var/mob/living/orderer = usr + + var/obj/item/card/id/id_card = orderer.get_idcard(hand_first = TRUE) + + //needs to come BEFORE preventing actions! + if(action == "override_order") + if(!(override_access in id_card.GetAccess())) + balloon_alert(usr, "requires head of staff access!") + playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE) + return + + if(department_order && (department_order in SSshuttle.shoppinglist)) + GLOB.department_order_cooldowns[type] = 0 + SSshuttle.shoppinglist -= department_order + department_order = null + UnregisterSignal(SSshuttle, COMSIG_SUPPLY_SHUTTLE_BUY) + return TRUE + + if(GLOB.department_order_cooldowns[type] > world.time) + return + + if(!check_access(id_card)) + balloon_alert(usr, "access denied!") + playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE) + return + + . = TRUE + var/id = text2path(params["id"]) + var/datum/supply_pack/pack = SSshuttle.supply_packs[id] + var/name = "*None Provided*" + var/rank = "*None Provided*" + var/ckey = usr.ckey + if(ishuman(usr)) + var/mob/living/carbon/human/human_orderer = usr + name = human_orderer.get_authentification_name() + rank = human_orderer.get_assignment(hand_first = TRUE) + else if(issilicon(usr)) + name = usr.real_name + rank = "Silicon" + //already have a signal to finalize the order + var/already_signalled = department_order ? TRUE : FALSE + department_order = new(pack, name, rank, ckey, "", null, department_delivery_area, null) + SSshuttle.shoppinglist += department_order + if(!already_signalled) + RegisterSignal(SSshuttle, COMSIG_SUPPLY_SHUTTLE_BUY, .proc/finalize_department_order) + say("Order processed. Cargo will deliver the crate when it comes in on their shuttle. NOTICE: Heads of staff may override the order.") + calculate_cooldown(pack.cost) + +///signal when the supply shuttle begins to spawn orders. we forget the current order preventing it from being overridden (since it's already past the point of no return on undoing the order) +/obj/machinery/computer/department_orders/proc/finalize_department_order(datum/subsystem) + SIGNAL_HANDLER + if(department_order && (department_order in SSshuttle.shoppinglist)) + department_order = null + UnregisterSignal(subsystem, COMSIG_SUPPLY_SHUTTLE_BUY) + +/obj/machinery/computer/department_orders/proc/calculate_cooldown(credits) + //minimum almost the lowest value of a crate + var/min = CARGO_CRATE_VALUE * 1.6 + //maximum fairly expensive crate at 3000 + var/max = CARGO_CRATE_VALUE * 15 + credits = clamp(credits, min, max) + var/time_y = (credits - min)/(max - min) + 1 //convert to between 1 and 2 + time_y = 10 MINUTES * time_y + GLOB.department_order_cooldowns[type] = world.time + time_y + +/obj/machinery/computer/department_orders/service + name = "service order console" + department_delivery_area = /area/hallway/secondary/service + override_access = ACCESS_HOP + req_one_access = list(ACCESS_KITCHEN, ACCESS_BAR, ACCESS_HYDROPONICS, ACCESS_JANITOR) + dep_groups = list("Service", "Food & Hydroponics", "Livestock", "Costumes & Toys") + +/obj/machinery/computer/department_orders/engineering + name = "engineering order console" + department_delivery_area = /area/engineering/main + override_access = ACCESS_CE + req_one_access = REGION_ACCESS_ENGINEERING + dep_groups = list("Engineering", "Engine Construction", "Canisters & Materials") + +/obj/machinery/computer/department_orders/science + name = "science order console" + department_delivery_area = /area/science/research + override_access = ACCESS_RD + req_one_access = REGION_ACCESS_RESEARCH + dep_groups = list("Science", "Livestock") + +/obj/machinery/computer/department_orders/security + name = "security order console" + department_delivery_area = /area/security/brig + override_access = ACCESS_HOS + req_one_access = REGION_ACCESS_SECURITY + dep_groups = list("Security", "Armory") + +/obj/machinery/computer/department_orders/medical + name = "medical order console" + department_delivery_area = /area/medical/medbay/central + override_access = ACCESS_CMO + req_one_access = REGION_ACCESS_MEDBAY + dep_groups = list("Medical") diff --git a/code/modules/cargo/order.dm b/code/modules/cargo/order.dm index a16f0965d5a..2665c276b3a 100644 --- a/code/modules/cargo/order.dm +++ b/code/modules/cargo/order.dm @@ -28,11 +28,13 @@ var/orderer_ckey var/reason var/discounted_pct + ///area this order wants to reach, if not null then it will come with the deliver_first component set to this area + var/department_destination var/datum/supply_pack/pack var/datum/bank_account/paying_account var/obj/item/coupon/applied_coupon -/datum/supply_order/New(datum/supply_pack/pack, orderer, orderer_rank, orderer_ckey, reason, paying_account, coupon) +/datum/supply_order/New(datum/supply_pack/pack, orderer, orderer_rank, orderer_ckey, reason, paying_account, department_destination, coupon) id = SSshuttle.ordernum++ src.pack = pack src.orderer = orderer @@ -40,6 +42,7 @@ src.orderer_ckey = orderer_ckey src.reason = reason src.paying_account = paying_account + src.department_destination = department_destination src.applied_coupon = coupon /datum/supply_order/proc/generateRequisition(turf/T) @@ -114,9 +117,11 @@ account_holder = paying_account.account_holder else account_holder = "Cargo" - var/obj/structure/closet/crate/C = pack.generate(A, paying_account) - generateManifest(C, account_holder, pack, pack.cost) - return C + var/obj/structure/closet/crate/crate = pack.generate(A, paying_account) + if(department_destination) + crate.AddElement(/datum/element/deliver_first, department_destination, pack.cost) + generateManifest(crate, account_holder, pack, pack.cost) + return crate /datum/supply_order/proc/generateCombo(miscbox, misc_own, misc_contents, misc_cost) for (var/I in misc_contents) diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm index 7124f621673..690874895ad 100644 --- a/code/modules/cargo/orderconsole.dm +++ b/code/modules/cargo/orderconsole.dm @@ -121,7 +121,8 @@ "cost" = SO.pack.get_cost(), "id" = SO.id, "orderer" = SO.orderer, - "paid" = !isnull(SO.paying_account) //paid by requester + "paid" = !isnull(SO.paying_account), //paid by requester + "dep_order" = SO.department_destination ? TRUE : FALSE )) data["requests"] = list() @@ -138,7 +139,6 @@ /obj/machinery/computer/cargo/ui_static_data(mob/user) var/list/data = list() - data["requestonly"] = requestonly data["supplies"] = list() for(var/pack in SSshuttle.supply_packs) var/datum/supply_pack/P = SSshuttle.supply_packs[pack] @@ -257,7 +257,7 @@ break var/turf/T = get_turf(src) - var/datum/supply_order/SO = new(pack, name, rank, ckey, reason, account, applied_coupon) + var/datum/supply_order/SO = new(pack, name, rank, ckey, reason, account, null, applied_coupon) SO.generateRequisition(T) if(requestonly && !self_paid) SSshuttle.requestlist += SO @@ -272,15 +272,22 @@ if("remove") var/id = text2num(params["id"]) for(var/datum/supply_order/SO in SSshuttle.shoppinglist) - if(SO.id == id) - if(SO.applied_coupon) - say("Coupon refunded.") - SO.applied_coupon.forceMove(get_turf(src)) - SSshuttle.shoppinglist -= SO - . = TRUE - break + if(SO.id != id) + continue + if(SO.department_destination) + say("Only the department that ordered this item may cancel it.") + return + if(SO.applied_coupon) + say("Coupon refunded.") + SO.applied_coupon.forceMove(get_turf(src)) + SSshuttle.shoppinglist -= SO + . = TRUE + break if("clear") - SSshuttle.shoppinglist.Cut() + for(var/datum/supply_order/cancelled_order in SSshuttle.shoppinglist) + if(cancelled_order.department_destination) + continue //don't cancel other department's orders + SSshuttle.shoppinglist -= cancelled_order . = TRUE if("approve") var/id = text2num(params["id"]) diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm index e445a1b090f..e62e42244aa 100644 --- a/code/modules/shuttle/supply.dm +++ b/code/modules/shuttle/supply.dm @@ -90,6 +90,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( sell() /obj/docking_port/mobile/supply/proc/buy() + SEND_SIGNAL(SSshuttle, COMSIG_SUPPLY_SHUTTLE_BUY) var/list/obj/miscboxes = list() //miscboxes are combo boxes that contain all goody orders grouped var/list/misc_order_num = list() //list of strings of order numbers, so that the manifest can show all orders in a box var/list/misc_contents = list() //list of lists of items that each box will contain @@ -122,50 +123,56 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( var/purchases = 0 var/list/goodies_by_buyer = list() // if someone orders more than GOODY_FREE_SHIPPING_MAX goodies, we upcharge to a normal crate so they can't carry around 20 combat shotties - for(var/datum/supply_order/SO in SSshuttle.shoppinglist) + for(var/datum/supply_order/spawning_order in SSshuttle.shoppinglist) if(!empty_turfs.len) break - var/price = SO.pack.get_cost() - if(SO.applied_coupon) - price *= (1 - SO.applied_coupon.discount_pct_off) + var/price = spawning_order.pack.get_cost() + if(spawning_order.applied_coupon) + price *= (1 - spawning_order.applied_coupon.discount_pct_off) - var/datum/bank_account/D - if(SO.paying_account) //Someone paid out of pocket - D = SO.paying_account - var/list/current_buyer_orders = goodies_by_buyer[SO.paying_account] // so we can access the length a few lines down - if(!SO.pack.goody) - price *= 1.1 //TODO make this customizable by the quartermaster + var/datum/bank_account/paying_for_this - // note this is before we increment, so this is the GOODY_FREE_SHIPPING_MAX + 1th goody to ship. also note we only increment off this step if they successfully pay the fee, so there's no way around it - else if(LAZYLEN(current_buyer_orders) == GOODY_FREE_SHIPPING_MAX) - price += CRATE_TAX - D.bank_card_talk("Goody order size exceeds free shipping limit: Assessing [CRATE_TAX] credit S&H fee.") - else - D = SSeconomy.get_dep_account(ACCOUNT_CAR) - if(D) - if(!D.adjust_money(-price)) - if(SO.paying_account) - D.bank_card_talk("Cargo order #[SO.id] rejected due to lack of funds. Credits required: [price]") - continue + //department orders EARN money for cargo, not the other way around + if(!spawning_order.department_destination) + if(spawning_order.paying_account) //Someone paid out of pocket + paying_for_this = spawning_order.paying_account + var/list/current_buyer_orders = goodies_by_buyer[spawning_order.paying_account] // so we can access the length a few lines down + if(!spawning_order.pack.goody) + price *= 1.1 //TODO make this customizable by the quartermaster - if(SO.paying_account) - if(SO.pack.goody) - LAZYADD(goodies_by_buyer[SO.paying_account], SO) - D.bank_card_talk("Cargo order #[SO.id] has shipped. [price] credits have been charged to your bank account.") + // note this is before we increment, so this is the GOODY_FREE_SHIPPING_MAX + 1th goody to ship. also note we only increment off this step if they successfully pay the fee, so there's no way around it + else if(LAZYLEN(current_buyer_orders) == GOODY_FREE_SHIPPING_MAX) + price += CRATE_TAX + paying_for_this.bank_card_talk("Goody order size exceeds free shipping limit: Assessing [CRATE_TAX] credit S&H fee.") + else + paying_for_this = SSeconomy.get_dep_account(ACCOUNT_CAR) + if(paying_for_this) + if(!paying_for_this.adjust_money(-price)) + if(spawning_order.paying_account) + paying_for_this.bank_card_talk("Cargo order #[spawning_order.id] rejected due to lack of funds. Credits required: [price]") + continue + + if(spawning_order.paying_account) + if(spawning_order.pack.goody) + LAZYADD(goodies_by_buyer[spawning_order.paying_account], spawning_order) + paying_for_this.bank_card_talk("Cargo order #[spawning_order.id] has shipped. [price] credits have been charged to your bank account.") var/datum/bank_account/department/cargo = SSeconomy.get_dep_account(ACCOUNT_CAR) - cargo.adjust_money(price - SO.pack.get_cost()) //Cargo gets the handling fee - value += SO.pack.get_cost() - SSshuttle.shoppinglist -= SO - SSshuttle.orderhistory += SO - QDEL_NULL(SO.applied_coupon) + cargo.adjust_money(price - spawning_order.pack.get_cost()) //Cargo gets the handling fee + value += spawning_order.pack.get_cost() + SSshuttle.shoppinglist -= spawning_order + SSshuttle.orderhistory += spawning_order + QDEL_NULL(spawning_order.applied_coupon) - if(!SO.pack.goody) //we handle goody crates below - SO.generate(pick_n_take(empty_turfs)) + if(!spawning_order.pack.goody) //we handle goody crates below + spawning_order.generate(pick_n_take(empty_turfs)) - SSblackbox.record_feedback("nested tally", "cargo_imports", 1, list("[SO.pack.get_cost()]", "[SO.pack.name]")) - investigate_log("Order #[SO.id] ([SO.pack.name], placed by [key_name(SO.orderer_ckey)]), paid by [D.account_holder] has shipped.", INVESTIGATE_CARGO) - if(SO.pack.dangerous) - message_admins("\A [SO.pack.name] ordered by [ADMIN_LOOKUPFLW(SO.orderer_ckey)], paid by [D.account_holder] has shipped.") + SSblackbox.record_feedback("nested tally", "cargo_imports", 1, list("[spawning_order.pack.get_cost()]", "[spawning_order.pack.name]")) + + var/from_whom = paying_for_this?.account_holder || "nobody (department order)" + + investigate_log("Order #[spawning_order.id] ([spawning_order.pack.name], placed by [key_name(spawning_order.orderer_ckey)]), paid by [from_whom] has shipped.", INVESTIGATE_CARGO) + if(spawning_order.pack.dangerous) + message_admins("\A [spawning_order.pack.name] ordered by [ADMIN_LOOKUPFLW(spawning_order.orderer_ckey)], paid by [from_whom] has shipped.") purchases++ // we handle packing all the goodies last, since the type of crate we use depends on how many goodies they ordered. If it's more than GOODY_FREE_SHIPPING_MAX diff --git a/tgstation.dme b/tgstation.dme index 23496312ea0..f3d390007f4 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -788,6 +788,7 @@ #include "code\datums\elements\death_drops.dm" #include "code\datums\elements\deferred_aquarium_content.dm" #include "code\datums\elements\delete_on_drop.dm" +#include "code\datums\elements\deliver_first.dm" #include "code\datums\elements\digitalcamo.dm" #include "code\datums\elements\drag_pickup.dm" #include "code\datums\elements\dryable.dm" @@ -2092,6 +2093,7 @@ #include "code\modules\cargo\bounty.dm" #include "code\modules\cargo\centcom_podlauncher.dm" #include "code\modules\cargo\coupon.dm" +#include "code\modules\cargo\department_order.dm" #include "code\modules\cargo\export_scanner.dm" #include "code\modules\cargo\exports.dm" #include "code\modules\cargo\expressconsole.dm" diff --git a/tgui/packages/tgui/interfaces/Cargo.js b/tgui/packages/tgui/interfaces/Cargo.js index 85f9c781b79..0237bfd582c 100644 --- a/tgui/packages/tgui/interfaces/Cargo.js +++ b/tgui/packages/tgui/interfaces/Cargo.js @@ -354,18 +354,28 @@ const CargoCart = (props, context) => { [Paid Privately] )} -