Merge branch 'master' of https://github.com/tgstation/tgstation into upstream-sync

This commit is contained in:
xPokee
2025-10-03 07:05:54 -04:00
517 changed files with 24339 additions and 21253 deletions
@@ -485,6 +485,15 @@
default = 6
min_val = -1
/// Freebie quirk points. Can't go negative because we have no way of enforcing a person has a quirk before they join.
/datum/config_entry/number/default_quirk_points
default = 2
min_val = 0
/// Max personalities you can have at once
/datum/config_entry/number/max_personalities
default = 5
/**
* A config that skews with the random spawners weights
* If the value is lower than 1, it'll tend to even out the odds
@@ -77,6 +77,8 @@
var/minimum_required_age = 0
/// Templates necessary for this ruleset to be executed
VAR_PROTECTED/list/ruleset_lazy_templates
/// Extra logging information can be set here, to be output into any admin messaging and dynamic logs.
VAR_FINAL/log_data
/datum/dynamic_ruleset/New(list/dynamic_config)
for(var/new_var in dynamic_config?[config_tag])
@@ -220,6 +222,7 @@
// This is (mostly) redundant, buuuut the (potential) sleep above makes it iffy, so let's just be safe
if(!can_be_selected())
log_data = "Reason: Ruleset cannot be selected."
return FALSE
var/max_candidates = get_antag_cap(population_size, max_antag_cap || min_antag_cap)
@@ -227,6 +230,7 @@
var/list/selected_candidates = select_candidates(antag_candidates, max_candidates)
if(length(selected_candidates) < min_candidates)
log_data = "Reason: Not enough eligible candidates. Have: [length(selected_candidates)], Need: [min_candidates]"
return FALSE
for(var/mob/candidate as anything in selected_candidates)
@@ -149,7 +149,7 @@ SUBSYSTEM_DEF(dynamic)
for(var/datum/dynamic_ruleset/roundstart/ruleset in queued_rulesets)
// NOTE: !! THIS CAN SLEEP !!
if(!ruleset.prepare_execution( num_real_players, antag_candidates ))
log_dynamic("Roundstart: Selected ruleset [ruleset.config_tag], but preparation failed!")
log_dynamic("Roundstart: Selected ruleset [ruleset.config_tag], but preparation failed! [ruleset.log_data]")
queued_rulesets -= ruleset
qdel(ruleset)
continue
@@ -368,7 +368,7 @@ SUBSYSTEM_DEF(dynamic)
// NOTE: !! THIS CAN SLEEP !!
if(!picked_ruleset.prepare_execution(player_count, picked_ruleset.collect_candidates()))
log_dynamic("Midround ([range]): Selected ruleset [picked_ruleset.config_tag], but preparation failed!")
log_dynamic("Midround ([range]): Selected ruleset [picked_ruleset.config_tag], but preparation failed! [picked_ruleset.log_data]")
QDEL_LIST(rulesets_weighted)
return FALSE
// Run the thing
@@ -430,8 +430,8 @@ SUBSYSTEM_DEF(dynamic)
// NOTE: !! THIS CAN SLEEP !!
if(!running.prepare_execution(get_active_player_count(afk_check = TRUE), running.collect_candidates()))
if(alert_admins_on_fail)
message_admins("Midround (forced): Forced ruleset [running.config_tag], but preparation failed!")
log_dynamic("Midround (forced): Forced ruleset [running.config_tag], but preparation failed!")
message_admins("Midround (forced): Forced ruleset [running.config_tag], but preparation failed! [running.log_data]")
log_dynamic("Midround (forced): Forced ruleset [running.config_tag], but preparation failed! [running.log_data]")
qdel(running)
return FALSE
@@ -493,7 +493,7 @@ SUBSYSTEM_DEF(dynamic)
return FALSE
// NOTE: !! THIS CAN SLEEP !!
if(!picked_ruleset.prepare_execution(player_count, list(latejoiner)))
log_dynamic("Latejoin: Selected ruleset [picked_ruleset.name] for [key_name(latejoiner)], but preparation failed! Latejoin chance has increased.")
log_dynamic("Latejoin: Selected ruleset [picked_ruleset.name] for [key_name(latejoiner)], but preparation failed! Latejoin chance has increased. [picked_ruleset.log_data]")
QDEL_LIST(rulesets_weighted)
failed_latejoins++
return FALSE
@@ -407,6 +407,9 @@
/datum/dynamic_ruleset/midround/from_ghosts/blob/false_alarm()
priority_announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", ANNOUNCER_OUTBREAK5)
// Set status displays to biohazard alert even for false alarm
send_status_display_biohazard_alert()
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph
name = "Alien Infestation"
config_tag = "Xenomorph"
+17 -7
View File
@@ -463,8 +463,9 @@ ADMIN_VERB(check_bomb_impacts, R_DEBUG, "Check Bomb Impact", "See what the effec
throwingturf[1] = max_range - dist
throwingturf[2] = epicenter
throwingturf[3] = max_range
throwingturf[4] = severity
else
explode.explosion_throw_details = list(max_range - dist, epicenter, max_range)
explode.explosion_throw_details = list(max_range - dist, epicenter, max_range, severity)
held_throwturf += explode
@@ -756,19 +757,28 @@ ADMIN_VERB(check_bomb_impacts, R_DEBUG, "Check Bomb Impact", "See what the effec
var/turf/explode = thing
var/list/details = explode.explosion_throw_details
explode.explosion_throw_details = null
if (length(details) != 3)
if (length(details) != 4)
continue
var/throw_range = details[1]
var/turf/center = details[2]
var/max_range = details[3]
for(var/atom/movable/A in explode)
if(QDELETED(A))
var/severity = details[4]
var/required_resist = 0
switch(severity)
if(EXPLODE_DEVASTATE) // The jump from "very strong" to "overpowering" is intentional, more stuff should be thrown by point-blank explosions
required_resist = MOVE_FORCE_OVERPOWERING
if(EXPLODE_HEAVY)
required_resist = MOVE_FORCE_VERY_STRONG
if(EXPLODE_LIGHT)
required_resist = MOVE_FORCE_STRONG
for(var/atom/movable/movable as anything in explode)
if(QDELETED(movable))
continue
if(!A.anchored && A.move_resist != INFINITY)
if(!movable.anchored && movable.move_resist < required_resist)
// We want to have our distance matter, but we do want to bias to a lot of throw, for the vibe
var/atom_throw_range = rand(throw_range, max_range) + max_range * 0.3
var/turf/throw_at = get_ranged_target_turf_direct(A, center, atom_throw_range, 180) // Throw 180 degrees away from the explosion source
A.throw_at(throw_at, atom_throw_range, EXPLOSION_THROW_SPEED, quickstart = FALSE)
var/turf/throw_at = get_ranged_target_turf_direct(movable, center, atom_throw_range, 180) // Throw 180 degrees away from the explosion source
movable.throw_at(throw_at, atom_throw_range, EXPLOSION_THROW_SPEED, quickstart = FALSE)
cost_throwturf = MC_AVERAGE(cost_throwturf, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
currentpart = SSEXPLOSIONS_TURFS
+7 -2
View File
@@ -31,7 +31,7 @@ SUBSYSTEM_DEF(job)
var/list/prioritized_jobs = list()
var/list/latejoin_trackers = list()
var/overflow_role = /datum/job/assistant
var/datum/job/overflow_role = /datum/job/assistant
var/list/level_order = list(JP_HIGH, JP_MEDIUM, JP_LOW)
@@ -747,7 +747,12 @@ SUBSYSTEM_DEF(job)
/atom/proc/JoinPlayerHere(mob/joining_mob, buckle)
// By default, just place the mob on the same turf as the marker or whatever.
joining_mob.forceMove(get_turf(src))
// Set joining_mob as the new mob so subtypes can use it as a proper mob.
if(ispath(joining_mob))
joining_mob = new joining_mob(get_turf(src))
else
joining_mob.forceMove(get_turf(src))
return joining_mob
/obj/structure/chair/JoinPlayerHere(mob/joining_mob, buckle)
. = ..()
@@ -0,0 +1,80 @@
PROCESSING_SUBSYSTEM_DEF(personalities)
name = "Personalities"
runlevels = RUNLEVEL_GAME
flags = SS_BACKGROUND|SS_POST_FIRE_TIMING
wait = 3 SECONDS
/// All personality singletons indexed by their type
VAR_FINAL/list/datum/personality/personalities_by_type
/// All personality singletons indexed by their savefile key
VAR_FINAL/list/datum/personality/personalities_by_key
/// Assoc list of personality group to list of personality typepaths in that group
VAR_FINAL/list/incompatibilities_by_group
/// For personalities which process, this tracks all mobs we need to process for
VAR_FINAL/list/processing_personalities
/datum/controller/subsystem/processing/personalities/Initialize()
init_personalities()
return SS_INIT_SUCCESS
/// Initialized personality singletons
/datum/controller/subsystem/processing/personalities/proc/init_personalities()
personalities_by_type = list()
personalities_by_key = list()
incompatibilities_by_group = list()
processing_personalities = list()
for(var/datum/personality/personality_type as anything in subtypesof(/datum/personality))
var/personality_key = personality_type::savefile_key
if(isnull(personality_key))
// Abstract personality, ignore
continue
if(personalities_by_key[personality_key])
stack_trace("Personality save key collision! \
key: [personality_key] - \
new: [personality_type::name] - \
old: [personalities_by_key[personality_key].name]")
continue
var/datum/personality/personality = new personality_type()
personalities_by_type[personality_type] = personality
personalities_by_key[personality.savefile_key] = personality
for(var/group in personality.groups)
incompatibilities_by_group[group] ||= list()
incompatibilities_by_group[group] += personality_type
/// Helper to check if the new personality type is incompatible with the passed list of personality types
/datum/controller/subsystem/processing/personalities/proc/is_incompatible(list/personality_types, new_personality_type)
if(!length(incompatibilities_by_group))
stack_trace("Checking personality incompatibilities before the incompatibility list was initialized?")
return FALSE
if(length(personality_types))
// No incompatibilities possible with no personalities
return FALSE
var/datum/personality/new_personality = personalities_by_type[new_personality_type]
if(!length(new_personality.groups))
// No groups, so no incompatibilities
return FALSE
// Filters all incompatibily groups against the new personality's groups
for(var/group, incompatibility_list in incompatibilities_by_group & new_personality.groups)
// Then checks if any personality type in the list is also in the group
if(length(incompatibility_list & personality_types))
return TRUE
return FALSE
/// Helper to select a random list of personalities, respecting incompatibilities. REturns a list of typepaths
/datum/controller/subsystem/processing/personalities/proc/select_random_personalities(lower_end = 1, upper_end = CONFIG_GET(number/max_personalities))
var/list/personality_pool = personalities_by_type.Copy()
var/list/selected_personalities = list()
var/num = rand(lower_end, upper_end)
var/i = 1
while(i <= num)
if(!length(personality_pool))
break
var/picked_type = pick(personality_pool)
if(is_incompatible(selected_personalities, picked_type))
continue
selected_personalities += picked_type
personality_pool -= picked_type
i += 1
return selected_personalities
@@ -8,13 +8,11 @@ GLOBAL_LIST_INIT_TYPED(quirk_blacklist, /list/datum/quirk, list(
list(/datum/quirk/item_quirk/blindness, /datum/quirk/item_quirk/scarred_eye),
list(/datum/quirk/item_quirk/blindness, /datum/quirk/item_quirk/fluoride_stare),
list(/datum/quirk/item_quirk/blindness, /datum/quirk/touchy),
list(/datum/quirk/jolly, /datum/quirk/depression, /datum/quirk/apathetic, /datum/quirk/hypersensitive),
list(/datum/quirk/jolly, /datum/quirk/depression, /datum/quirk/hypersensitive),
list(/datum/quirk/no_taste, /datum/quirk/vegetarian, /datum/quirk/deviant_tastes, /datum/quirk/gamer),
list(/datum/quirk/pineapple_liker, /datum/quirk/pineapple_hater, /datum/quirk/gamer),
list(/datum/quirk/alcohol_tolerance, /datum/quirk/light_drinker),
list(/datum/quirk/item_quirk/clown_enjoyer, /datum/quirk/item_quirk/mime_fan),
list(/datum/quirk/bad_touch, /datum/quirk/friendly),
list(/datum/quirk/extrovert, /datum/quirk/introvert),
list(/datum/quirk/prosthetic_limb, /datum/quirk/quadruple_amputee, /datum/quirk/body_purist),
list(/datum/quirk/transhumanist, /datum/quirk/body_purist),
list(/datum/quirk/prosthetic_organ, /datum/quirk/tin_man, /datum/quirk/body_purist),
@@ -140,7 +138,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
*Randomises the quirks for a specified mob
*/
/datum/controller/subsystem/processing/quirks/proc/randomise_quirks(mob/living/user)
var/bonus_quirks = max((length(user.quirks) + rand(-RANDOM_QUIRK_BONUS, RANDOM_QUIRK_BONUS)), MINIMUM_RANDOM_QUIRKS)
var/bonus_quirks = max((LAZYLEN(user.quirks) + rand(-RANDOM_QUIRK_BONUS, RANDOM_QUIRK_BONUS)), MINIMUM_RANDOM_QUIRKS)
var/added_quirk_count = 0 //How many we've added
var/list/quirks_to_add = list() //Quirks we're adding
var/good_count = 0
@@ -214,7 +212,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
var/list/positive_quirks = list()
var/points_enabled = !CONFIG_GET(flag/disable_quirk_points)
var/max_positive_quirks = CONFIG_GET(number/max_positive_quirks)
var/balance = 0
var/balance = -CONFIG_GET(number/default_quirk_points)
var/list/all_quirks = get_quirks()
+9 -4
View File
@@ -589,10 +589,15 @@ SUBSYSTEM_DEF(ticker)
var/acting_captain = !is_captain_job(player_assigned_role)
SSjob.promote_to_captain(new_player_living, acting_captain)
OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(minor_announce), player_assigned_role.get_captaincy_announcement(new_player_living)))
if((player_assigned_role.job_flags & JOB_ASSIGN_QUIRKS) && ishuman(new_player_living) && CONFIG_GET(flag/roundstart_traits))
if(new_player_mob.client?.prefs?.should_be_random_hardcore(player_assigned_role, new_player_living.mind))
new_player_mob.client.prefs.hardcore_random_setup(new_player_living)
SSquirks.AssignQuirks(new_player_living, new_player_mob.client)
if(ishuman(new_player_living))
if(player_assigned_role.job_flags & JOB_ASSIGN_QUIRKS)
if(CONFIG_GET(flag/roundstart_traits))
if(new_player_mob.client?.prefs?.should_be_random_hardcore(player_assigned_role, new_player_living.mind))
new_player_mob.client.prefs.hardcore_random_setup(new_player_living)
SSquirks.AssignQuirks(new_player_living, new_player_mob.client)
else // clear any personalities the prefs added since our job clearly does not want them
new_player_living.clear_personalities()
if(ishuman(new_player_living))
SEND_SIGNAL(new_player_living, COMSIG_HUMAN_CHARACTER_SETUP_FINISHED)
CHECK_TICK