Routine Cargo Departmental Deliveries (#61992)

About The Pull Request

Document: https://hackmd.io/@bazelart/HkY-SO9VF

Each department's request console is upgraded. Instead of making requests, they are only able to see crates related to their department that helps the department. They can order one for free (so not out of anyone's wallet) and it will arrive at cargo. Cargo gets the price of the ordered crate for bringing it to the department that ordered it (via an area check, of which the crate will remain locked until satisfied, emitter cracking aside).

Ordering a crate puts the console on a cooldown depending on the price of the crate ordered. The time ranges from 10 minutes at the lowest value, to capped at 20 minutes at 4x the default crate's price. the price in time follows a ease in out circular function, where the time increases slowly as the time goes up, but a lot in the middle. towards the higher end it slows down again.

Cargo will start with far less money, but this is countered by the new source of income.

Finish Mapping it
Finish tgui panel for department orders
Testmerge it for balance and feedback who cares

    Maybe give multiple destination areas, just in case one area gets obliterated who cares!

Why It's Good For The Game

Cargo starts with a budget to spend on themselves, which lets them order their department rewards before doing anything. There is no inherent reward to delivering to other departments other than social expectations of filling the job, which means cargo will simply ignore deliveries and requests if there are more self-important things going on, since those deliveries both cost cargo and do not help cargo in any way
Changelog

cl
add: replaced the request consoles in each department with department order consoles, which order for free on a cooldown. cargo gets these orders and delivers the crates, which are locked until delivery. upon delivery, cargo gets paid the value of the crate, and can then sell the crate back on the shuttle.
balance: cargo doesn't start with a budget, other departments get what their budget was split up amongst them
/cl
This commit is contained in:
tralezab
2021-11-12 14:19:33 +13:00
committed by GitHub
parent 20462105e2
commit bb7638f114
16 changed files with 613 additions and 130 deletions
@@ -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,
+12 -34
View File
@@ -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,
+10 -10
View File
@@ -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" = (
+5 -5
View File
@@ -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,
+9 -9
View File
@@ -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" = (
+12
View File
@@ -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"
+7 -3
View File
@@ -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()
+97
View File
@@ -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)
@@ -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
+178
View File
@@ -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")
+9 -4
View File
@@ -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)
+18 -11
View File
@@ -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"])
+43 -36
View File
@@ -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
+2
View File
@@ -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"
+22 -12
View File
@@ -354,18 +354,28 @@ const CargoCart = (props, context) => {
<b>[Paid Privately]</b>
)}
</Table.Cell>
<Table.Cell collapsing textAlign="right">
{formatMoney(entry.cost)} cr
</Table.Cell>
<Table.Cell collapsing>
{can_send &&(
<Button
icon="minus"
onClick={() => act('remove', {
id: entry.id,
})} />
)}
</Table.Cell>
{entry.dep_order && (
<Table.Cell collapsing textAlign="right">
{formatMoney(entry.cost)} cr earned on delivery
</Table.Cell>
) || (
<>
<Table.Cell collapsing textAlign="right">
{formatMoney(entry.cost)} cr
</Table.Cell>
<Table.Cell collapsing>
{can_send &&(
<Button
icon="minus"
onClick={() => act('remove', {
id: entry.id,
})} />
)}
</Table.Cell>
</>
)}
</Table.Row>
))}
</Table>
@@ -0,0 +1,179 @@
import { BooleanLike } from 'common/react';
import { useBackend, useLocalState } from '../backend';
import { Box, Button, Dimmer, Icon, NoticeBox, Section, Stack, Tabs, Tooltip } from '../components';
import { Window } from '../layouts';
// 15x crate value
const COST_UPPER_BOUND = 3000;
type Pack = {
name: string;
cost: number;
id: string;
desc: string;
goody: string;
}
type Category = {
name: string;
packs: Pack[];
}
type Info = {
can_override: BooleanLike;
time_left: number;
supplies: Category[];
};
const CooldownEstimate = (props) => {
const {
cost,
} = props;
const cooldownColor = cost > COST_UPPER_BOUND * 0.75 && "red"
|| cost > COST_UPPER_BOUND * 0.25 && "orange"
|| "green";
const cooldownText = cost > COST_UPPER_BOUND * 0.75 && "long"
|| cost > COST_UPPER_BOUND * 0.25 && "moderate"
|| "short";
return (
<Box as="span" textColor={cooldownColor}>
{cooldownText} cooldown.
</Box>
);
};
export const DepartmentOrders = (props, context) => {
const { data } = useBackend<Info>(context);
const {
time_left,
} = data;
return (
<Window
title="Department Orders"
width={620}
height={580} >
<Window.Content>
{!!time_left
&& <CooldownDimmer />
|| (
<Stack vertical fill>
<Stack.Item grow>
<Stack fill vertical>
<Stack.Item>
<NoticeBox info>
As employees of Nanotrasen, the selection of orders
here are completely free of charge, only incurring
a cooldown on the service. Cheaper items will make
you wait for less time before Nanotrasen allows
another purchase, to encourage tasteful spending.
</NoticeBox>
</Stack.Item>
<Stack.Item grow>
<DepartmentCatalog />
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
)}
</Window.Content>
</Window>
);
};
const CooldownDimmer = (props, context) => {
const { act, data } = useBackend<Info>(context);
const {
can_override,
time_left,
} = data;
return (
<Dimmer>
<Stack vertical>
<Stack.Item textAlign="center">
<Icon
color="orange"
name="route"
size={20}
/>
</Stack.Item>
<Stack.Item fontSize="18px" color="orange">
Ready for another order in {time_left}...
</Stack.Item>
<Stack.Item textAlign="center" color="orange">
<Button
width="300px"
lineHeight={2}
tooltip={!!can_override
&& "This action requires Head of Staff access!"
|| "Crate already shipped! No cancelling now!"}
fontSize="14px"
color="red"
disabled={!can_override}
onClick={() => act("override_order")}>
<Box fontSize="22px">
Override
</Box>
</Button>
</Stack.Item>
</Stack>
</Dimmer>
);
};
const DepartmentCatalog = (props, context) => {
const { act, data } = useBackend<Info>(context);
const {
supplies,
} = data;
const [
tabCategory,
setTabCategory,
] = useLocalState(context, 'tabName', supplies[0]);
return (
<Stack vertical fill>
<Stack.Item>
<Tabs textAlign="center" fluid>
{supplies.map(cat => (
<Tabs.Tab
key={cat}
selected={tabCategory === cat}
onClick={() => (setTabCategory(cat))}>
{cat.name}
</Tabs.Tab>
))}
</Tabs>
</Stack.Item>
<Stack.Item grow>
<Section fill scrollable>
<Stack vertical>
{tabCategory.packs.map(pack => (
<Stack.Item className="candystripe" key={pack.name}>
<Stack fill>
<Stack.Item grow>
<Tooltip content={pack.desc}>
<Box as="span" style={{
"border-bottom": "2px dotted rgba(255, 255, 255, 0.8)",
}}>
{pack.name}
</Box>
</Tooltip>
</Stack.Item>
<Stack.Item>
<CooldownEstimate cost={pack.cost} />
&ensp;
<Button
onClick={() => act("order", {
id: pack.id,
})}>
Order
</Button>
</Stack.Item>
</Stack>
</Stack.Item>
))}
</Stack>
</Section>
</Stack.Item>
</Stack>
);
};