Merge branch 'master' of github.com:tgstation/tgstation into upstream-2026-06-23

This commit is contained in:
Roxy
2026-06-23 15:51:55 -04:00
1007 changed files with 40287 additions and 33507 deletions
@@ -285,7 +285,15 @@
var/turf/T = get_turf(computer)
var/datum/supply_order/SO = new(pack, name, rank, ckey, reason, account)
SO.generateRequisition(T)
if(computer.stored_paper >= 1)
SO.generateRequisition(T)
computer.stored_paper -= 1
if(computer.stored_paper <= 4)
computer.say("Paper's storage has only [computer.stored_paper] papers. Refill please!")
if(computer.stored_paper <= 1)
computer.say("Only 1 paper has left, refill please!")
else
computer.say("Requisition cannot be printed, paper storage is empty. Please insert more paper!")
if((requestonly && !self_paid) || !(computer.stored_id?.GetID()))
SSshuttle.request_list += SO
else
@@ -0,0 +1,119 @@
/datum/computer_file/program/faxbond
filename = "faxbond"
filedesc = "FaxBond"
can_run_on_flags = PROGRAM_PDA
downloader_category = PROGRAM_CATEGORY_DEVICE
program_open_overlay = "generic"
extended_desc = "A lightweight piece of software designed to decrease fax response time. \
Will send a notification as soon as one of connected faxes receives a message. \
Recommended by 9 out of 10 CentCom officers."
size = 1
tgui_id = "NtosFaxBond"
program_icon = "fa-fax"
/// All relevant info of every connected fax, including weak ref for cleaning after ourself + name, area name and muted bool
var/list/connected_faxes = list()
/**
* Proc for subscribing to faxes. Registers needed signal and updates fax related vars for program. Includes type checking.
* Arguments:
* * target - [/datum/computer_file/program/proc/tap] proc target, can be anything
*/
/datum/computer_file/program/faxbond/proc/connect_fax(obj/machinery/fax/target)
if(!istype(target))
return FALSE
var/our_id = target.fax_id
if(!connected_faxes[our_id])
RegisterSignal(target, COMSIG_FAX_MESSAGE_RECEIVED, PROC_REF(on_fax_message_received))
var/list/fax_info = list()
var/area/our_area = get_area(target)
fax_info["ref"] = WEAKREF(target)
fax_info["name"] = target.fax_name
fax_info["area_name"] = our_area.name
fax_info["muted"] = FALSE
connected_faxes[our_id] += fax_info
return TRUE
/**
* Disconnects a fax given its ID (if it was connected before), removing it from a list and unregistering relevant signal
* Arguments:
* * fax_id - fax id to disconnect from our PDA
*/
/datum/computer_file/program/faxbond/proc/disconnect_fax(fax_id)
if(!connected_faxes[fax_id])
return
var/list/fax_info = connected_faxes[fax_id]
var/datum/weakref/fax_ref = fax_info["ref"]
var/obj/machinery/fax/our_fax = fax_ref.resolve()
if (our_fax)
UnregisterSignal(our_fax, COMSIG_FAX_MESSAGE_RECEIVED)
connected_faxes -= fax_id
/**
* Signal handler for [COMSIG_FAX_MESSAGE_RECEIVED].
* Arguments:
* * receiver - [/obj/machinery/fax] that received a message
* * message_source - name of a sender
*/
/datum/computer_file/program/faxbond/proc/on_fax_message_received(obj/machinery/fax/receiver, message_source)
SIGNAL_HANDLER
var/id = receiver.fax_id
var/list/fax_info = connected_faxes[id]
if (fax_info["muted"])
return
var/datum/computer_file/program/messenger/messenger = locate() in computer.stored_files
var/datum/signal/subspace/messaging/tablet_message/signal = new(receiver, list(
"fakename" = "Fax Notificator",
"fakejob" = "PDA Program",
"message" = "Your fax [receiver.fax_name] has received a new message from [message_source]",
"targets" = list(messenger),
"automated" = TRUE
))
INVOKE_ASYNC(signal, TYPE_PROC_REF(/datum/signal/subspace, send_to_receivers))
/datum/computer_file/program/faxbond/Destroy()
. = ..()
for(var/fax in connected_faxes)
disconnect_fax(fax)
/datum/computer_file/program/faxbond/tap(atom/tapped_atom, mob/living/user, list/modifiers)
return connect_fax(tapped_atom)
/datum/computer_file/program/faxbond/ui_data(mob/user)
var/list/data = list()
data["faxes_info"] = list()
for(var/fax_id in connected_faxes)
var/list/fax_info = connected_faxes[fax_id]
var/list/fax_data = list(
"id" = fax_id,
"name" = fax_info["name"],
"location" = fax_info["area_name"],
"muted" = fax_info["muted"],
)
data["faxes_info"] += list(fax_data)
return data
/datum/computer_file/program/faxbond/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
switch(action)
if("unsubscribe")
disconnect_fax(params["id"])
return TRUE
if("mute")
var/list/fax_info = connected_faxes[params["id"]]
if (!fax_info)
return FALSE
fax_info["muted"] = !fax_info["muted"]
return TRUE
@@ -32,32 +32,32 @@
botcount = 0
for(var/mob/living/simple_animal/bot/simple_bot as anything in GLOB.bots_list)
if(!is_valid_z_level(current_turf, get_turf(simple_bot)) || !(simple_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) //Only non-emagged bots on the same Z-level are detected!
for(var/mob/living/basic/bot/basic_bot as anything in GLOB.bots_list)
if(!is_valid_z_level(current_turf, get_turf(basic_bot)) || !(basic_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) //Only non-emagged bots on the same Z-level are detected!
continue
if(!simple_bot.allowed(user) && !simple_bot.check_access(computer.stored_id)) // Only check Bots we can access
if(!basic_bot.allowed(user) && !basic_bot.check_access(computer.stored_id)) // Only check Bots we can access
continue
var/list/newbot = list(
"name" = simple_bot.name,
"mode" = simple_bot.get_mode_ui(),
"model" = simple_bot.bot_type,
"locat" = get_area(simple_bot),
"bot_ref" = REF(simple_bot),
"name" = basic_bot.name,
"mode" = basic_bot.get_mode_ui(),
"model" = basic_bot.bot_type,
"locat" = get_area(basic_bot),
"bot_ref" = REF(basic_bot),
"mule_check" = FALSE,
)
if(simple_bot.bot_type == MULE_BOT)
var/mob/living/simple_animal/bot/mulebot/simple_mulebot = simple_bot
if(basic_bot.bot_type == MULE_BOT)
var/mob/living/basic/bot/mulebot/basic_mulebot = basic_bot
mulelist += list(list(
"name" = simple_mulebot.name,
"id" = simple_mulebot.id,
"dest" = simple_mulebot.destination,
"power" = simple_mulebot.cell ? simple_mulebot.cell.percent() : 0,
"home" = simple_mulebot.home_destination,
"autoReturn" = simple_mulebot.mulebot_delivery_flags & MULEBOT_RETURN_MODE,
"autoPickup" = simple_mulebot.mulebot_delivery_flags & MULEBOT_AUTO_PICKUP_MODE,
"reportDelivery" = simple_mulebot.mulebot_delivery_flags & MULEBOT_REPORT_DELIVERY_MODE,
"mule_ref" = REF(simple_mulebot),
"load" = simple_mulebot.get_load_name(),
"name" = basic_mulebot.name,
"id" = basic_mulebot.id,
"dest" = basic_mulebot.ai_controller.blackboard[BB_MULEBOT_DESTINATION_BEACON],
"power" = basic_mulebot.cell ? basic_mulebot.cell.percent() : 0,
"home" = basic_mulebot.ai_controller.blackboard[BB_MULEBOT_HOME_BEACON],
"autoReturn" = basic_mulebot.mulebot_delivery_flags & MULEBOT_RETURN_MODE,
"autoPickup" = basic_mulebot.mulebot_delivery_flags & MULEBOT_AUTO_PICKUP_MODE,
"reportDelivery" = basic_mulebot.mulebot_delivery_flags & MULEBOT_REPORT_DELIVERY_MODE,
"mule_ref" = REF(basic_mulebot),
"load" = basic_mulebot.get_load_name(),
))
newbot["mule_check"] = TRUE
botlist += list(newbot)
@@ -106,15 +106,15 @@
"report",
"ejectpai",
)
var/mob/living/simple_animal/bot/simple_bot = locate(params["robot"]) in GLOB.bots_list
var/mob/living/basic/bot/basic_bot = locate(params["robot"]) in GLOB.bots_list
if (action in standard_actions)
simple_bot.bot_control(action, current_user, id_card?.GetAccess())
basic_bot.bot_control(action, current_user, id_card?.GetAccess())
if (action in MULE_actions)
simple_bot.bot_control(action, current_user, id_card?.GetAccess(), TRUE)
basic_bot.bot_control(action, current_user, id_card?.GetAccess(), TRUE)
switch(action)
if("summon")
simple_bot.bot_control(action, current_user, id_card ? id_card.access : id_card?.GetAccess())
basic_bot.bot_control(action, current_user, id_card ? id_card.access : id_card?.GetAccess())
if("ejectcard")
if(!computer || !computer.stored_id)
return
@@ -39,7 +39,8 @@
/datum/computer_file/program/science/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet_batched/research_designs)
get_asset_datum(/datum/asset/spritesheet_batched/sheetmaterials),
get_asset_datum(/datum/asset/spritesheet_batched/research_designs),
)
// heavy data from this proc should be moved to static data when possible
@@ -80,7 +81,8 @@
"can_unlock" = stored_research.can_unlock_node(node),
"have_experiments_done" = stored_research.have_experiments_for_node(node),
"tier" = stored_research.tiers[node.id],
"enqueued_by_user" = enqueued_by_user
"enqueued_by_user" = enqueued_by_user,
"discount_boosted" = node.discount_boosted
))
// Get experiments and serialize them
@@ -156,6 +158,8 @@
node_cache[compressed_id]["required_experiments"] = node.required_experiments
if (LAZYLEN(node.discount_experiments))
node_cache[compressed_id]["discount_experiments"] = node.discount_experiments
if (LAZYLEN(node.discount_boosts))
node_cache[compressed_id]["discount_boosts"] = node.discount_boosts
// Build design cache
var/design_cache = list()
@@ -165,8 +169,17 @@
var/datum/design/design = SSresearch.techweb_designs[design_id] || SSresearch.error_design
var/compressed_id = "[compress_id(design.id)]"
var/size = spritesheet.icon_size_id(design.id)
var/cost = list()
var/list/materials = design.materials
for(var/datum/material/mat in materials)
cost[mat.name] = OPTIMAL_COST(materials[mat])
design_cache[compressed_id] = list(
design.name,
cost,
design.build_type,
design.departmental_flags,
"[size == size32x32 ? "" : "[size] "][design.id]"
)
@@ -175,10 +188,23 @@
for (var/id in id_cache)
flat_id_cache += id
var/list/department_flags = list()
for (var/datum/job_department/department as anything in subtypesof(/datum/job_department))
if (department::department_bitflags)
department_flags["[department::department_bitflags]"] = department::department_name
// Don't pass away flags as those are irrelevant to the station
var/list/build_types = GLOB.build_types_to_string.Copy()
build_types -= "[AWAY_IMPRINTER]"
build_types -= "[AWAY_LATHE]"
.["static_data"] = list(
"node_cache" = node_cache,
"design_cache" = design_cache,
"id_cache" = flat_id_cache
"id_cache" = flat_id_cache,
"SHEET_MATERIAL_AMOUNT" = SHEET_MATERIAL_AMOUNT,
"build_types" = build_types,
"department_flags" = department_flags,
)
/**