Files
Alexis 21b4095dfd [MDB IGNORE] [IDB IGNORE] Upstream Sync - 04/17/2026 (#5453)
Upstream 04/17/2026

fixes https://github.com/Bubberstation/Bubberstation/issues/5549

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>
Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com>
Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com>
Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com>
Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com>
Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com>
Co-authored-by: loganuk <fakeemail123@aol.com>
Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com>
Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com>
Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com>
Co-authored-by: Lucy <lucy@absolucy.moe>
Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com>
Co-authored-by: Isratosh <Isratosh@hotmail.com>
Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com>
Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com>
Co-authored-by: Alexander V. <volas@ya.ru>
Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com>
Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Tim <timothymtorres@gmail.com>
Co-authored-by: Iamgoofball <iamgoofball@gmail.com>
Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com>
Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com>
Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com>
Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com>
Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com>
Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com>
Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com>
Co-authored-by: Josh <josh.adam.powell@gmail.com>
Co-authored-by: Josh Powell <josh.powell@softwire.com>
Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com>
Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com>
Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com>
Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
2026-05-16 00:56:00 +02:00

168 lines
7.1 KiB
Plaintext

///A list of the possible mutations for a vine
GLOBAL_LIST_INIT(vine_mutations_list, init_vine_mutation_list())
/proc/init_vine_mutation_list()
var/list/mutation_list = list()
for(var/datum/spacevine_mutation/subtype as anything in valid_subtypesof(/datum/spacevine_mutation))
var/datum/spacevine_mutation/mutation = new subtype
mutation_list[mutation] = IDEAL_MAX_SEVERITY - mutation.severity // the ideal maximum potency is used for weighting
return mutation_list
/datum/spacevine_controller
///Canonical list of all the vines we "own"
var/list/obj/structure/spacevine/vines
///Queue of vines to process
var/list/growth_queue
//List of currently processed vines, on this level to prevent runtime tomfoolery
var/list/obj/structure/spacevine/queue_end
///Spread multiplier, depends on productivity, affects how often kudzu spreads
var/spread_multiplier = 5 // corresponds to artificial kudzu with production speed of 1, approaches 10% of total vines will spread per second
///Maximum spreading limit (ie. how many kudzu can there be) for this controller
var/spread_cap = 30 // corresponds to artificial kudzu with production speed of 3.5
///The chance that we will develop a new mutation
var/mutativeness = 1
///Maximum sum of mutation severities
var/max_mutation_severity = 20
///Minimum spread rate per second
var/minimum_spread_rate = 1
/datum/spacevine_controller/New(turf/location, list/muts, potency, production, datum/round_event/event = null)
vines = list()
growth_queue = list()
queue_end = list()
var/obj/structure/spacevine/vine = spawn_spacevine_piece(location, null, muts)
if(event)
event.announce_to_ghosts(vine)
START_PROCESSING(SSobj, src)
if(potency != null)
mutativeness = potency * MUTATIVENESS_SCALE_FACTOR // If potency is 100, 20 mutativeness; if 1: 0.2 mutativeness
max_mutation_severity = round(potency * MAX_SEVERITY_LINEAR_COEFF + MAX_SEVERITY_CONSTANT_TERM) // If potency is 100, 25 max mutation severity; if 1, 10 max mutation severity
if(production != null && production <= MAX_POSSIBLE_PRODUCTIVITY_VALUE) //Prevents runtime in case production is set to 11.
spread_cap = SPREAD_CAP_LINEAR_COEFF * (MAX_POSSIBLE_PRODUCTIVITY_VALUE + 1 - production) + SPREAD_CAP_CONSTANT_TERM //Best production speed of 1 increases spread_cap to 60, worst production speed of 10 lowers it to 24, even distribution
spread_multiplier = SPREAD_MULTIPLIER_MAX / (MAX_POSSIBLE_PRODUCTIVITY_VALUE + 1 - production) // Best production speed of 1: 10% of total vines will spread per second, worst production speed of 10: 1% of total vines (with minimum of 1) will spread per second
if(event != null) // spawned by space vine event
max_mutation_severity += MAX_SEVERITY_EVENT_BONUS
minimum_spread_rate = 3
/datum/spacevine_controller/vv_get_dropdown()
. = ..()
VV_DROPDOWN_OPTION("", "--- /spacevine_controller ---")
VV_DROPDOWN_OPTION(VV_HK_SPACEVINE_PURGE, "Delete Vines")
/datum/spacevine_controller/vv_do_topic(href_list)
. = ..()
if(!.)
return
if(href_list[VV_HK_SPACEVINE_PURGE])
if(tgui_alert(usr, "Are you sure you want to delete this spacevine cluster?", "Delete Vines", list("Yes", "No")) == "Yes")
DeleteVines()
/datum/spacevine_controller/proc/DeleteVines() //this is kill
QDEL_LIST(vines) //this will also qdel us
/datum/spacevine_controller/Destroy()
STOP_PROCESSING(SSobj, src)
vines.Cut()
growth_queue.Cut()
queue_end.Cut()
return ..()
/datum/spacevine_controller/proc/spawn_spacevine_piece(turf/location, obj/structure/spacevine/parent, list/muts)
var/obj/structure/spacevine/vine = new(location)
growth_queue += vine
vines += vine
vine.master = src
for(var/mutation_type in muts)
for(var/datum/spacevine_mutation/mutation in GLOB.vine_mutations_list)
if(istype(mutation, mutation_type))
mutation.add_mutation_to_vinepiece(vine)
break
if(parent)
vine.mutations |= parent.mutations
vine.trait_flags |= parent.trait_flags
var/parentcolor = parent.atom_colours[FIXED_COLOUR_PRIORITY]
vine.add_atom_colour(parentcolor, FIXED_COLOUR_PRIORITY)
if(prob(mutativeness))
var/datum/spacevine_mutation/random_mutate = pick_weight(GLOB.vine_mutations_list - vine.mutations)
if(!isnull(random_mutate)) //If this vine has every single mutation don't attempt to add a null mutation.
var/total_severity = random_mutate.severity
for(var/datum/spacevine_mutation/mutation as anything in vine.mutations)
total_severity += mutation.severity
if(total_severity <= max_mutation_severity)
random_mutate.add_mutation_to_vinepiece(vine)
for(var/datum/spacevine_mutation/mutation in vine.mutations)
mutation.on_birth(vine)
location.Entered(vine, null)
return vine
/datum/spacevine_controller/proc/VineDestroyed(obj/structure/spacevine/vine)
vine.master = null
vines -= vine
growth_queue -= vine
queue_end -= vine
if(length(vines))
return
var/obj/item/seeds/kudzu/seed = new(vine.loc)
seed.mutations |= vine.mutations
seed.set_potency(mutativeness / MUTATIVENESS_SCALE_FACTOR)
// Mathematical notes:
// The formula for spread_multiplier is SPREAD_MULTIPLIER_MAX / (MAX_POSSIBLE_PRODUCTIVITY_VALUE + 1 - production)
// So (MAX_POSSIBLE_PRODUCTIVITY_VALUE + 1 - production) = SPREAD_MULTIPLIER_MAX / spread_multiplier
// ie. production = MAX_POSSIBLE_PRODUCTIVITY_VALUE + 1 - SPREAD_MULTIPLIER_MAX / spread_multiplier
seed.set_production(MAX_POSSIBLE_PRODUCTIVITY_VALUE + 1 - (SPREAD_MULTIPLIER_MAX / spread_multiplier)) //Reverts spread_multiplier formula so resulting seed gets original production stat or equivalent back.
qdel(src)
/// Life cycle of a space vine
/datum/spacevine_controller/process(seconds_per_tick)
var/vine_count = length(vines)
if(!vine_count)
qdel(src) //space vines exterminated. Remove the controller
return
/// Bonus spread for kudzu that has just started out (ie. with low vine count)
var/start_spread_bonus = max(5 - spread_multiplier * (vine_count ** 2) / 400, 0)
/// Base spread rate, depends solely on spread multiplier and vine count
var/spread_base = 0.5 * vine_count / spread_multiplier
/// Actual maximum spread rate for this process tick
var/spread_max = round(clamp(seconds_per_tick * (spread_base + start_spread_bonus), max(seconds_per_tick * minimum_spread_rate, 1), spread_cap))
var/amount_processed = 0
for(var/obj/structure/spacevine/vine in growth_queue)
if(!vine.can_spread)
continue
growth_queue -= vine
queue_end += vine
for(var/datum/spacevine_mutation/mutation in vine.mutations)
mutation.process_mutation(vine)
if(vine.growth_stage >= 2) //If tile is fully grown
vine.entangle_mob()
else if(SPT_PROB(10, seconds_per_tick)) //If tile isn't fully grown
vine.grow()
vine.spread()
amount_processed++
if(amount_processed >= spread_max)
break
//We can only do so much work per process, but we still want to process everything at some point
//So we shift the queue a bit
growth_queue += queue_end
queue_end = list()
/**
* Used to determine whether the mob is immune to actions by the vine.
* Use cases: Stops vine from attacking itself, other plants.
*/
/proc/isvineimmune(atom/target)
if(isliving(target))
var/mob/living/victim = target
if(victim.has_faction(FACTION_VINES, FACTION_PLANTS))
return TRUE
return FALSE