/obj/machinery/computer/cargo name = "supply console" desc = "Used to order supplies, approve requests, and control the shuttle." icon_screen = "supply" circuit = /obj/item/circuitboard/computer/cargo light_color = COLOR_BRIGHT_ORANGE ///Can the supply console send the shuttle back and forth? Used in the UI backend. var/can_send = TRUE ///Can this console only send requests? Typically used at the cargo front desk for pedestrians. var/requestonly = FALSE ///Can you approve requests placed for cargo? Works differently between the app and the computer. var/can_approve_requests = TRUE var/contraband = FALSE var/self_paid = FALSE var/safety_warning = "For safety and ethical reasons, the automated supply shuttle cannot transport live organisms, \ human remains, classified nuclear weaponry, mail, undelivered departmental order crates, syndicate bombs, \ homing beacons, unstable eigenstates, fax machines, or machinery housing any form of artificial intelligence." var/blockade_warning = "Bluespace instability detected. Shuttle movement impossible." /// var that tracks message cooldown var/message_cooldown var/list/loaded_coupons /// var that makes express console use rockets var/is_express = FALSE ///The name of the shuttle template being used as the cargo shuttle. 'cargo' is default and contains critical code. Don't change this unless you know what you're doing. var/cargo_shuttle = "cargo" ///The docking port called when returning to the station. var/docking_home = "cargo_home" ///The docking port called when leaving the station. var/docking_away = "cargo_away" ///If this console can loan the cargo shuttle. Set to false to disable. var/stationcargo = TRUE ///The account this console processes and displays. Independent from the account the shuttle processes. var/cargo_account = ACCOUNT_CAR ///Interface name for the ui_interact call for different subtypes. var/interface_type = "Cargo" // BUBBER EDIT START /// Can this console toggle and use private account payments. var/can_private_purchases = TRUE var/console_flags = NONE // BUBBER EDIT END /obj/machinery/computer/cargo/request name = "supply request console" desc = "Used to request supplies from cargo." icon_screen = "request" circuit = /obj/item/circuitboard/computer/cargo/request can_send = FALSE can_approve_requests = FALSE requestonly = TRUE /obj/machinery/computer/cargo/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(!istype(tool, /obj/item/trade_chip)) return NONE var/obj/item/trade_chip/contract = tool contract.try_to_unlock_contract(user) return ITEM_INTERACT_SUCCESS /obj/machinery/computer/cargo/emag_act(mob/user, obj/item/card/emag/emag_card) if(obj_flags & EMAGGED) return FALSE if(user) if (emag_card) user.visible_message(span_warning("[user] swipes [emag_card] through [src]!")) to_chat(user, span_notice("You adjust [src]'s routing and receiver spectrum, unlocking special supplies and contraband.")) obj_flags |= EMAGGED contraband = TRUE // This also permanently sets this on the circuit board var/obj/item/circuitboard/computer/cargo/board = circuit board.contraband = TRUE board.obj_flags |= EMAGGED update_static_data(user) return TRUE /obj/machinery/computer/cargo/on_construction(mob/user) . = ..() circuit.configure_machine(src) /obj/machinery/computer/cargo/ui_interact(mob/user, datum/tgui/ui) . = ..() ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, interface_type, name) ui.open() /obj/machinery/computer/cargo/ui_data() var/list/data = list() data["department"] = "Cargo" // Hardcoded here, for customization in budgetordering.dm AKA NT IRN data["location"] = SSshuttle.supply.getStatusText() var/datum/bank_account/bank = SSeconomy.get_dep_account(cargo_account) if(bank) data["points"] = bank.account_balance data["grocery"] = SSshuttle.chef_groceries.len data["away"] = SSshuttle.supply.getDockedId() == docking_away data["self_paid"] = self_paid data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE data["loan"] = !!SSshuttle.shuttle_loan data["loan_dispatched"] = SSshuttle.shuttle_loan && SSshuttle.shuttle_loan.dispatched data["can_send"] = can_send data["can_approve_requests"] = can_approve_requests data["requestonly"] = requestonly // BUBBER EDIT START - allow UI to hide private toggle on unsupported consoles data["allow_private_purchases"] = can_private_purchases // BUBBER EDIT END var/message = "Remember to stamp and send back the supply manifests." if(SSshuttle.centcom_message) message = SSshuttle.centcom_message if(SSshuttle.supply_blocked) message = blockade_warning data["message"] = message var/cart_list = list() for(var/datum/supply_order/order in SSshuttle.shopping_list) if(cart_list[order.pack.name]) cart_list[order.pack.name][1]["amount"]++ cart_list[order.pack.name][1]["cost"] += order.get_final_cost() if(order.department_destination) cart_list[order.pack.name][1]["dep_order"]++ if(!isnull(order.paying_account)) cart_list[order.pack.name][1]["paid"]++ continue cart_list[order.pack.name] = list(list( "cost_type" = order.cost_type, "object" = order.pack.name, "cost" = order.get_final_cost(), "id" = order.id, "amount" = 1, "orderer" = order.orderer, "paid" = !isnull(order.paying_account), //number of orders purchased privatly "dep_order" = !!order.department_destination, //number of orders purchased by a department "can_be_cancelled" = order.can_be_cancelled, )) data["cart"] = list() for(var/item_id in cart_list) data["cart"] += cart_list[item_id] data["requests"] = list() for(var/datum/supply_order/order in SSshuttle.request_list) var/datum/supply_pack/pack = order.pack data["requests"] += list(list( "object" = pack.name, "cost" = pack.get_cost(), "orderer" = order.orderer, "reason" = order.reason, "id" = order.id, "account" = order.paying_account ? order.paying_account.account_holder : "Cargo Department" )) return data /obj/machinery/computer/cargo/ui_static_data(mob/user) var/list/data = list() data["max_order"] = CARGO_MAX_ORDER data["supplies"] = list() for(var/pack_id in SSshuttle.supply_packs) var/datum/supply_pack/pack = SSshuttle.supply_packs[pack_id] if(!data["supplies"][pack.group]) data["supplies"][pack.group] = list( "name" = pack.group, "packs" = get_packs_data(pack.group), ) data["displayed_currency_full_name"] = " [MONEY_NAME]" data["displayed_currency_name"] = " [MONEY_SYMBOL]" return data /** * returns a list of supply packs for a certain group * * group - the group of packs to return */ /obj/machinery/computer/cargo/proc/get_packs_data(group) var/list/packs = list() for(var/pack_id in SSshuttle.supply_packs) var/datum/supply_pack/pack = SSshuttle.supply_packs[pack_id] if(pack.group != group) continue if(pack.order_flags & ORDER_INVISIBLE) continue if((pack.order_flags & ORDER_EMAG_ONLY) && !(obj_flags & EMAGGED)) continue if((pack.order_flags & ORDER_SPECIAL) && !(pack.order_flags & ORDER_SPECIAL_ENABLED)) continue // BUBBER EDIT START - Interdyne-only items check if((pack.order_flags & ORDER_INTERDYNE_ONLY) && !(console_flags & CARGO_CONSOLE_INTERDYNE)) continue // BUBBER EDIT END if((pack.order_flags & ORDER_CONTRABAND) && !contraband) continue if(!is_express && (pack.order_flags & ORDER_POD_ONLY)) continue var/obj/item/first_item = length(pack.contains) > 0 ? pack.contains[1] : null packs += list(list( "name" = pack.name, "cost" = pack.get_cost() * get_discount(), "id" = pack_id, "desc" = pack.desc || pack.name, // If there is a description, use it. Otherwise use the pack's name. "first_item_icon" = first_item?.icon, "first_item_icon_state" = first_item?.icon_state, "goody" = (pack.order_flags & ORDER_GOODY), "access" = pack.access, "contraband" = (pack.order_flags & ORDER_CONTRABAND), "contains" = pack.get_contents_ui_data(), "subcategory" = pack.subcategory, // BUBBER EDIT )) return packs /** * returns the discount multiplier applied to all supply packs, * the discount is calculated as follows: pack_cost * get_discount() */ /obj/machinery/computer/cargo/proc/get_discount() return 1 /** * adds an supply pack to the checkout cart * * user - the mobe doing this order * * id - the type of pack to order * * amount - the amount to order. You may not order more then 10 things at once */ /obj/machinery/computer/cargo/proc/add_item(mob/user, id, amount = 1) if(is_express) return id = text2path(id) || id var/datum/supply_pack/pack = SSshuttle.supply_packs[id] if(!istype(pack)) CRASH("Unknown supply pack id given by order console ui. ID: [id]") if(amount > CARGO_MAX_ORDER || amount < 1) // Holy shit fuck off CRASH("Invalid amount passed into add_item") if(((pack.order_flags & ORDER_EMAG_ONLY) && !(obj_flags & EMAGGED)) || ((pack.order_flags & ORDER_CONTRABAND) && !contraband) || (pack.order_flags & ORDER_POD_ONLY) || ((pack.order_flags & ORDER_SPECIAL) && !(pack.order_flags & ORDER_SPECIAL_ENABLED)) || ((pack.order_flags & ORDER_INTERDYNE_ONLY) && !(console_flags & CARGO_CONSOLE_INTERDYNE))) // BUBBER EDIT return var/name = "*None Provided*" var/rank = "*None Provided*" var/ckey = user.ckey if(ishuman(user)) var/mob/living/carbon/human/human = user name = human.get_authentification_name() rank = human.get_assignment(hand_first = TRUE) else if(HAS_SILICON_ACCESS(user)) name = user.real_name rank = "Silicon" var/datum/bank_account/account if(isliving(user)) var/mob/living/living_user = user var/obj/item/card/id/id_card = living_user.get_idcard(TRUE) var/bypass = FALSE if(istype(id_card, /obj/item/card/id/advanced/chameleon)) //We'll bypass access restrictions bypass = TRUE account = id_card?.registered_account // We can still assign an account for request department purposes. if(self_paid) if(!istype(id_card)) say("No ID card detected.") return if(IS_DEPARTMENTAL_CARD(id_card)) say("The [src] rejects [id_card].") return if(!istype(account)) say("Invalid bank account.") return var/list/access = id_card.GetAccess() if((pack.access_view && !(pack.access_view in access)) && !bypass) say("[id_card] lacks the requisite access for this purchase.") return // The list we are operating on right now var/list/working_list = SSshuttle.shopping_list var/reason = "" var/datum/bank_account/personal_department if(requestonly && !self_paid && !(pack.order_flags & ORDER_GOODY)) working_list = SSshuttle.request_list reason = tgui_input_text(user, "Reason", name, max_length = MAX_MESSAGE_LEN) if(isnull(reason)) return name = account?.account_holder if(account?.account_job) personal_department = SSeconomy.get_dep_account(account.account_job.paycheck_department) if(!(personal_department.account_holder == "Cargo Budget")) var/dept_choice = tgui_alert(user, "Which department are you requesting this for?", "Choose department to request from", list("Cargo Budget", "[personal_department.account_holder]")) if(!dept_choice) return if(dept_choice == "Cargo Budget") personal_department = null if(isliving(user)) var/mob/living/living_user = user var/obj/item/card/id/id_card = living_user.get_idcard(TRUE) var/list/access = id_card?.GetAccess() if(!id_card || !living_user || !access) living_user = user id_card = living_user.get_idcard(TRUE) if(pack.access_view && !(pack.access_view in access) && personal_department) // We want to block cargo requests when a player is requesting a restricted pack that they don't have access to. // BUT only when it's requested with non-cargo funds, as cargo had direct oversight over their own purchases with their own budget. // HOWEVER, this shouldn't prevent someone from buying something using their own personal funds. say("ERROR: User lacks the requisite access for this purchase request.") return if((pack.order_flags & ORDER_GOODY) && !self_paid && !pack.allow_non_private_purchase) // BUBBER EDIT playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE) say("ERROR: Small crates may only be purchased by private accounts.") return var/similar_count = SSshuttle.supply.get_order_count(pack) if(similar_count == OVER_ORDER_LIMIT) playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE) say("ERROR: No more then [CARGO_MAX_ORDER] of any pack may be ordered at once") return if(!self_paid) account = personal_department amount = clamp(amount, 1, CARGO_MAX_ORDER - similar_count) for(var/count in 1 to amount) var/obj/item/coupon/applied_coupon for(var/obj/item/coupon/coupon_check in loaded_coupons) if(pack.type == coupon_check.discounted_pack) say("Coupon found! [round(coupon_check.discount_pct_off * 100)]% off applied!") coupon_check.moveToNullspace() applied_coupon = coupon_check break var/datum/supply_order/order = new( pack = pack, orderer = name, orderer_rank = rank, orderer_ckey = ckey, reason = reason, paying_account = account, coupon = applied_coupon, charge_on_purchase = TRUE, // SKYRAT EDIT ADDITION ) working_list += order if(self_paid) say("Order processed. The price will be charged to [account.account_holder]'s bank account on delivery.") if(requestonly && message_cooldown < world.time) aas_config_announce(/datum/aas_config_entry/cargo_orders_announcement, list("AMOUNT" = amount), src, list(RADIO_CHANNEL_SUPPLY), amount == 1 ? "Single Order" : "Multiple Orders") message_cooldown = world.time + 30 SECONDS . = TRUE /** * removes an item from the checkout cart * * id - the id of the cart item to remove */ /obj/machinery/computer/cargo/proc/remove_item(id) for(var/datum/supply_order/order in SSshuttle.shopping_list) if(order.id != id) continue if(order.department_destination) say("Only the department that ordered this item may cancel it.") return FALSE if(order.applied_coupon) say("Coupon refunded.") order.applied_coupon.forceMove(get_turf(src)) SSshuttle.shopping_list -= order qdel(order) return TRUE return FALSE /** * maps the ordename displayed on the ui to its supply pack id * * order_name - the name of the order */ /obj/machinery/computer/cargo/proc/name_to_id(order_name) for(var/pack in SSshuttle.supply_packs) var/datum/supply_pack/supply = SSshuttle.supply_packs[pack] if(order_name == supply.name) return pack return null /obj/machinery/computer/cargo/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) return switch(action) if("send") if(!SSshuttle.supply.canMove()) say(safety_warning) return if(SSshuttle.supply_blocked) say(blockade_warning) return if(SSshuttle.supply.getDockedId() == docking_home) SSshuttle.moveShuttle(cargo_shuttle, docking_away, TRUE) say("The supply shuttle is departing.") ui.user.investigate_log("sent the supply shuttle away.", INVESTIGATE_CARGO) else //create the paper from the SSshuttle.shopping_list if(length(SSshuttle.shopping_list)) var/obj/item/paper/requisition/requisition_paper = new(get_turf(src)) requisition_paper.name = "requisition form - [server_timestamp(ic_time = TRUE)] (PT: [round_timestamp()])" var/requisition_text = "

[station_name()] Supply Requisition

" requisition_text += "
" requisition_text += "Time of Order: [UNDERLINED_HTML_TEXT("[server_timestamp(ic_time = TRUE)]", "Shift Time: [round_timestamp()]")]

" for(var/datum/supply_order/order as anything in SSshuttle.shopping_list) requisition_text += "[order.pack.name]
" requisition_text += "- Order ID: [order.id]
" var/restrictions = SSid_access.get_access_desc(order.pack.access) if(restrictions) requisition_text += "- Access Restrictions: [restrictions]
" requisition_text += "- Ordered by: [order.orderer] ([order.orderer_rank])
" var/paying_account = order.paying_account if(paying_account) requisition_text += "- Paid Privately by: [order.paying_account.account_holder]
" var/reason = order.reason if(reason) requisition_text += "- Reason Given: [reason]
" requisition_text += "

" requisition_paper.add_raw_text(requisition_text, advanced_html = TRUE) requisition_paper.color = "#9ef5ff" requisition_paper.update_appearance() ui.user.investigate_log("called the supply shuttle.", INVESTIGATE_CARGO) say("The supply shuttle has been called and will arrive in [SSshuttle.supply.timeLeft(600)] minute\s.") SSshuttle.moveShuttle(cargo_shuttle, docking_home, TRUE) . = TRUE if("loan") if(!SSshuttle.shuttle_loan) return if(SSshuttle.supply_blocked) say(blockade_warning) return else if(SSshuttle.supply.mode != SHUTTLE_IDLE) return else if(SSshuttle.supply.getDockedId() != docking_away) return else if(stationcargo != TRUE) return else SSshuttle.shuttle_loan.loan_shuttle() say("The supply shuttle has been loaned to CentCom.") ui.user.investigate_log("accepted a shuttle loan event.", INVESTIGATE_CARGO) ui.user.log_message("accepted a shuttle loan event.", LOG_GAME) . = TRUE if("add") return add_item(ui.user, params["id"]) if("add_by_name") var/supply_pack_id = name_to_id(params["order_name"]) if(!supply_pack_id) return return add_item(ui.user, supply_pack_id) if("remove") var/order_name = params["order_name"] //try removing at least one item with the specified name. An order may not be removed if it was from the department for(var/datum/supply_order/order in SSshuttle.shopping_list) if(order.pack.name != order_name) continue if(remove_item(order.id)) return TRUE return TRUE if("modify") var/order_name = params["order_name"] //clear out all orders with the above mentioned order_name name to make space for the new amount for(var/datum/supply_order/order in SSshuttle.shopping_list) //find corresponding order id for the order name if(order.pack.name == order_name) remove_item(order.id) //now add the new amount stuff var/amount = text2num(params["amount"]) if(amount == 0) return TRUE if(amount > CARGO_MAX_ORDER) return var/supply_pack_id = name_to_id(order_name) //map order name to supply pack id for adding if(!supply_pack_id) return return add_item(ui.user, supply_pack_id, amount) if("clear") //create copy of list else we will get runtimes when iterating & removing items on the same list SSshuttle.shopping_list var/list/shopping_cart = SSshuttle.shopping_list.Copy() for(var/datum/supply_order/cancelled_order in shopping_cart) if(cancelled_order.department_destination || !cancelled_order.can_be_cancelled) continue //don't cancel other department's orders or orders that can't be cancelled remove_item(cancelled_order.id) //remove & properly refund any coupons attached with this order if("approve") var/id = text2num(params["id"]) for(var/datum/supply_order/SO in SSshuttle.request_list) if(SO.id == id) SSshuttle.request_list -= SO SSshuttle.shopping_list += SO . = TRUE break if("deny") var/id = text2num(params["id"]) for(var/datum/supply_order/SO in SSshuttle.request_list) if(SO.id == id) SSshuttle.request_list -= SO . = TRUE break if("denyall") SSshuttle.request_list.Cut() . = TRUE if("toggleprivate") // BUBBER EDIT ADDITION - enforce private toggle capability server-side if(!can_private_purchases) return self_paid = !self_paid . = TRUE if(.) post_signal(cargo_shuttle) /obj/machinery/computer/cargo/proc/post_signal(command) var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS) if(!frequency) return var/datum/signal/status_signal = new(list("command" = command)) frequency.post_signal(src, status_signal) /datum/aas_config_entry/cargo_orders_announcement name = "Cargo Alert: New Orders" announcement_lines_map = list( "Single Order" = "A new order has been requested.", "Multiple Orders" = "%AMOUNT orders have been requested.", ) vars_and_tooltips_map = list( "AMOUNT" = "will be replaced wuth number of orders.", )