Merge branch 'master' of https://github.com/tgstation/tgstation into pupstream-2025-09-07

# Conflicts:
#	README.md
#	code/__DEFINES/admin.dm
#	code/__DEFINES/melee.dm
#	code/_globalvars/traits/_traits.dm
#	code/controllers/subsystem/economy.dm
#	code/datums/components/crafting/crafting.dm
#	code/datums/elements/crusher_loot.dm
#	code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
#	code/modules/clothing/suits/_suits.dm
#	code/modules/escape_menu/leave_body.dm
#	code/modules/jobs/job_types/_job.dm
#	code/modules/mining/equipment/mineral_scanner.dm
#	code/modules/mob/living/living.dm
#	code/modules/plumbing/plumbers/pill_press.dm
#	tgui/packages/tgui/interfaces/Vending.tsx
This commit is contained in:
nevimer
2025-09-07 00:37:52 -04:00
362 changed files with 82084 additions and 106117 deletions
@@ -641,6 +641,13 @@
/datum/config_entry/flag/picture_logging_camera
/datum/config_entry/number/roundstart_logout_report_time_average
default = 10 MINUTES
min_val = 0
/datum/config_entry/number/roundstart_logout_report_time_variance
default = 3 MINUTES
min_val = 0
/datum/config_entry/flag/reopen_roundstart_suicide_roles
@@ -101,3 +101,36 @@ SUBSYSTEM_DEF(achievements)
if(to_update.len)
SSdbcore.MassInsert(format_table_name("achievement_metadata"),to_update,duplicate_key = TRUE)
var/list/orphaned_keys = get_orphaned_keys(FALSE)
if(orphaned_keys.len)
message_admins("Achievement metadata found without matching achievement, use Achievements-Admin-Panel verb to cleanup if necessary")
/// returns list of metadata keys and versions in db with no matching achievement datum, either deleted achievements, or from server with code ahead of us.
/datum/controller/subsystem/achievements/proc/get_orphaned_keys(include_archived = TRUE)
. = list()
var/list/current_metadata = list()
// Fetch all keys from the db
var/datum/db_query/Q = SSdbcore.NewQuery("SELECT achievement_key,achievement_version FROM [format_table_name("achievement_metadata")]")
if(!Q.Execute(async = TRUE))
qdel(Q)
return
else
while(Q.NextRow())
current_metadata[Q.item[1]] = Q.item[2]
qdel(Q)
var/list/achievements_by_db_id = list()
for(var/datum/award/award as anything in subtypesof(/datum/award))
if(!initial(award.database_id)) // abstract type
continue
achievements_by_db_id[award.database_id] = TRUE
for(var/key in current_metadata)
if(achievements_by_db_id[key])
continue
if(!include_archived && current_metadata[key] == ACHIEVEMENT_ARCHIVED_VERSION)
continue
.[key] = current_metadata[key]
@@ -132,6 +132,8 @@ SUBSYSTEM_DEF(admin_verbs)
// refresh their verbs
admin_visibility_flags[admin.ckey] ||= list()
if(admin.is_localhost())
admin_visibility_flags[admin.ckey] |= list(ADMIN_VERB_VISIBLITY_FLAG_LOCALHOST)
for(var/datum/admin_verb/verb_singleton as anything in get_valid_verbs_for_admin(admin))
verb_singleton.assign_to_client(admin)
admin.init_verbs()
+4 -2
View File
@@ -390,9 +390,11 @@ SUBSYSTEM_DEF(air)
while(currentrun.len)
var/datum/excited_group/EG = currentrun[currentrun.len]
currentrun.len--
var/volatile_reaction = EG.turf_reactions & VOLATILE_REACTION
EG.breakdown_cooldown++
EG.dismantle_cooldown++
if(EG.breakdown_cooldown >= EXCITED_GROUP_BREAKDOWN_CYCLES)
if(!volatile_reaction)
EG.dismantle_cooldown++
if(EG.breakdown_cooldown >= EXCITED_GROUP_BREAKDOWN_CYCLES && !volatile_reaction)
EG.self_breakdown(poke_turfs = TRUE)
else if(EG.dismantle_cooldown >= EXCITED_GROUP_DISMANTLE_CYCLES && !(EG.turf_reactions & (REACTING | STOP_REACTIONS)))
EG.dismantle()
@@ -830,12 +830,12 @@
HUNTER_PACK_MI13,
)
. = ..()
addtimer(CALLBACK(src, PROC_REF(check_spawn_hunters), hunter_backstory, 10 MINUTES), 1 MINUTES)
addtimer(CALLBACK(src, PROC_REF(check_spawn_hunters), 10 MINUTES), 1 MINUTES)
/datum/dynamic_ruleset/midround/from_ghosts/fugitives/assign_role(datum/mind/candidate, datum/team/fugitive/team, turf/team_spawn)
candidate.current.forceMove(team_spawn)
equip_fugitive(candidate.current, team)
if(length(selected_minds) > 1 && candidate == selected_minds[1])
if(candidate == selected_minds[1])
equip_fugitive_leader(candidate.current)
playsound(candidate.current, 'sound/items/weapons/emitter.ogg', 50, TRUE)
@@ -38,7 +38,7 @@
pref_flag = ROLE_TRAITOR
weight = 10
min_pop = 3
max_antag_cap = list("denominator" = 38)
max_antag_cap = list("denominator" = 24)
/datum/dynamic_ruleset/roundstart/traitor/assign_role(datum/mind/candidate)
candidate.add_antag_datum(/datum/antagonist/traitor)
+4 -3
View File
@@ -160,7 +160,7 @@ SUBSYSTEM_DEF(economy)
var/datum/bank_account/bank_account = cached_processing[cached_processing[i]]
if(bank_account?.account_job && !ispath(bank_account.account_job))
temporary_total += (bank_account.account_job.paycheck * STARTING_PAYCHECKS)
bank_account.payday(1)
bank_account.payday(1, skippable = TRUE)
SEND_SIGNAL(bank_account, COMSIG_ON_BANK_ACCOUNT_PAYOUT) // BUBBER EDIT
station_total += bank_account.account_balance
if(MC_TICK_CHECK)
@@ -218,14 +218,15 @@ SUBSYSTEM_DEF(economy)
* * price_to_use: The cost of the purchase made for this transaction.
* * vendor: The object or structure medium that is charging the user. For Vending machines that's the machine, for payment component that's the parent, cargo that's the crate, etc.
*/
/datum/controller/subsystem/economy/proc/track_purchase(datum/bank_account/account, price_to_use, vendor)
if(!account || isnull(price_to_use) || !vendor)
/datum/controller/subsystem/economy/proc/add_audit_entry(datum/bank_account/account, price_to_use, vendor)
if(isnull(account) || isnull(price_to_use) || !vendor)
CRASH("Track purchases was missing an argument! (Account, Price, or Vendor.)")
audit_log += list(list(
"account" = "[account.account_holder]",
"cost" = price_to_use,
"vendor" = "[vendor]",
"stationtime" = station_time_timestamp("hh:mm"),
))
/**
@@ -154,7 +154,7 @@
///Resume our loop after being paused by pause_loop()
/datum/move_loop/proc/resume_loop()
if(!controller || (status & MOVELOOP_STATUS_RUNNING|MOVELOOP_STATUS_PAUSED) != (MOVELOOP_STATUS_RUNNING|MOVELOOP_STATUS_PAUSED))
if(!controller || (status & (MOVELOOP_STATUS_RUNNING|MOVELOOP_STATUS_PAUSED)) != (MOVELOOP_STATUS_RUNNING|MOVELOOP_STATUS_PAUSED))
return
timer = world.time
+9 -2
View File
@@ -348,7 +348,11 @@ SUBSYSTEM_DEF(ticker)
GLOB.communications_controller.queue_roundstart_report()
*/// BUBBER EDIT REMOVAL END - Storyteller
// Queue admin logout report
addtimer(CALLBACK(src, PROC_REF(display_roundstart_logout_report)), ROUNDSTART_LOGOUT_REPORT_TIME)
var/roundstart_logout_timer = CONFIG_GET(number/roundstart_logout_report_time_average)
var/roundstart_report_variance = CONFIG_GET(number/roundstart_logout_report_time_variance)
var/randomized_callback_timer = rand((roundstart_logout_timer - roundstart_report_variance), (roundstart_logout_timer + roundstart_report_variance))
addtimer(CALLBACK(src, PROC_REF(display_roundstart_logout_report)), randomized_callback_timer)
GLOB.logout_timer_set = randomized_callback_timer
// Queue suicide slot handling
if(CONFIG_GET(flag/reopen_roundstart_suicide_roles))
var/delay = (CONFIG_GET(number/reopen_roundstart_suicide_roles_delay) * 1 SECONDS) || 4 MINUTES
@@ -405,6 +409,7 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/proc/display_roundstart_logout_report()
var/list/msg = list("[span_boldnotice("Roundstart logout report")]\n\n")
for(var/i in GLOB.mob_living_list)
var/mob/living/L = i
var/mob/living/carbon/C = L
@@ -417,7 +422,7 @@ SUBSYSTEM_DEF(ticker)
if(L.ckey && L.client)
var/failed = FALSE
if(L.client.inactivity >= ROUNDSTART_LOGOUT_AFK_THRESHOLD) //Connected, but inactive (alt+tabbed or something)
if(L.client.inactivity >= GLOB.logout_timer_set) //Connected, but inactive (alt+tabbed or something)
msg += "<b>[L.name]</b> ([L.key]), the [L.job] (<font color='#ffcc00'><b>Connected, Inactive</b></font>)\n"
failed = TRUE //AFK client
if(!failed && L.stat)
@@ -448,6 +453,8 @@ SUBSYSTEM_DEF(ticker)
msg += "<b>[L.name]</b> ([ckey(D.mind.key)]), the [L.job] ([span_bolddanger("Ghosted")])\n"
continue //Ghosted while alive
msg += "[span_boldnotice("Roundstart logout reported at: [DisplayTimeText(GLOB.logout_timer_set)]")]\n"
var/concatenated_message = msg.Join()
log_admin(concatenated_message)
to_chat(GLOB.admins, concatenated_message)