Files
Bubberstation/code/modules/events/spacevine.dm
RandomGamer123 69d67de582 Kudzu nerfs, Venus human trap changes (#64675)
Kudzu's mutation's "severity" now reflects its gameplay severity, which factors into its maximum severity as well as chance of appearing, which is determined by its potency.
The old severity values were moved to defines.
Kudzu now stops spreading if the tile that the vine is on has a temperature lower than 100K.
Maximum spread and spread rate of kudzu have been lowered, productivity and potency for random event kudzu have been increased to compensate.
Adds a new "cold-proof" mutation for kudzu that a) allows kudzu to spread in low temperatures, and b) gives any venus human traps that spawn from the flowering trait cold-proofness
Code-wise, instead of using process_temperature, a new flags system is used.
Readds cold damage to venus human traps (although at a lower rate than before).
Venus human traps now inherit temperature-proof traits (and those only) from their parent plant (although fire-proof + flowering should not be possible without admin intervention).
Venus human traps have had their damage reduced (from 25 to 20) and have their hardstun replaced with 85 stamina damage (contractor baton level) and a 1 second knockdown. They are still very deadly but now hopefully at least people can fight them without getting immediately stunned and killed.
The kudzu thorns and toxicity traits have been buffed to reflect that they now cost severity and that they only have a 10% chance of even affecting people to begin with.
2022-02-12 23:17:48 +00:00

705 lines
28 KiB
Plaintext

/// Determines brightness of the light emitted by kudzu with the light mutation
#define LIGHT_MUTATION_BRIGHTNESS 4
/// Determines the probability that the toxicity mutation will harm someone who passes through it
#define TOXICITY_MUTATION_PROB 10
/// Determines the impact radius of kudzu's explosive mutation
#define EXPLOSION_MUTATION_IMPACT_RADIUS 2
/// Determines the scale factor for the amount of gas removed by kudzu with a gas removal mutation, which is this scale factor * the kudzu's energy level
#define GAS_MUTATION_REMOVAL_MULTIPLIER 3
/// Determines the probability that the thorn mutation will harm someone who passes through or attacks it
#define THORN_MUTATION_CUT_PROB 10
/// Determines the probability that a kudzu plant with the flowering mutation will spawn a venus flower bud
#define FLOWERING_MUTATION_SPAWN_PROB 10
/// Temperature below which the kudzu can't spread
#define VINE_FREEZING_POINT 100
/// Kudzu severity values for traits, based on severity in terms of how severely it impacts the game, the lower the severity, the more likely it is to appear
#define SEVERITY_TRIVIAL 1
#define SEVERITY_MINOR 2
#define SEVERITY_AVERAGE 4
#define SEVERITY_ABOVE_AVERAGE 7
#define SEVERITY_MAJOR 10
/// Kudzu mutativeness is based on a scale factor * potency
#define MUTATIVENESS_SCALE_FACTOR 0.1
/// Kudzu maximum mutation severity is a linear function of potency
#define MAX_SEVERITY_LINEAR_COEFF 0.1
#define MAX_SEVERITY_CONSTANT_TERM 10
/// The maximum possible productivity value of a (normal) kudzu plant, used for calculating a plant's spread cap and multiplier
#define MAX_POSSIBLE_PRODUCTIVITY_VALUE 10
/// Kudzu spread cap is a scaled version of production speed, such that the better the production speed, ie. the lower the speed value is, the faster is spreads
#define SPREAD_CAP_SCALE_FACTOR 4
/// Kudzu spread multiplier is a reciporal function of production speed, such that the better the production speed, ie. the lower the speed value is, the faster it spreads
#define SPREAD_MULTIPLIER_MAX 50
/datum/round_event_control/spacevine
name = "Space Vines"
typepath = /datum/round_event/spacevine
weight = 15
max_occurrences = 3
min_players = 10
/datum/round_event/spacevine
fakeable = FALSE
/datum/round_event/spacevine/start()
var/list/turfs = list() //list of all the empty floor turfs in the hallway areas
var/obj/structure/spacevine/vine = new()
for(var/area/hallway/area in world)
for(var/turf/floor in area)
if(floor.Enter(vine))
turfs += floor
qdel(vine)
if(length(turfs)) //Pick a turf to spawn at if we can
var/turf/floor = pick(turfs)
new /datum/spacevine_controller(floor, list(pick(subtypesof(/datum/spacevine_mutation))), rand(50,100), rand(1,4), src) //spawn a controller at turf with randomized stats and a single random mutation
/datum/spacevine_mutation
/// Displayed name of mutation
var/name = ""
/// Severity of mutation in terms of gameplay, affects appearance chance and how many mutations can be on the same vine
var/severity = 1
var/hue
var/quality
/datum/spacevine_mutation/proc/add_mutation_to_vinepiece(obj/structure/spacevine/holder)
holder.mutations |= src
holder.add_atom_colour(hue, FIXED_COLOUR_PRIORITY)
/datum/spacevine_mutation/proc/process_mutation(obj/structure/spacevine/holder)
return
/datum/spacevine_mutation/proc/on_birth(obj/structure/spacevine/holder)
return
/datum/spacevine_mutation/proc/on_grow(obj/structure/spacevine/holder)
return
/datum/spacevine_mutation/proc/on_death(obj/structure/spacevine/holder)
return
/datum/spacevine_mutation/proc/on_hit(obj/structure/spacevine/holder, mob/hitter, obj/item/item, expected_damage)
. = expected_damage
/datum/spacevine_mutation/proc/on_cross(obj/structure/spacevine/holder, mob/crosser)
return
/datum/spacevine_mutation/proc/on_chem(obj/structure/spacevine/holder, datum/reagent/chem)
return
/datum/spacevine_mutation/proc/on_eat(obj/structure/spacevine/holder, mob/living/eater)
return
/datum/spacevine_mutation/proc/on_spread(obj/structure/spacevine/holder, turf/target)
return
/datum/spacevine_mutation/proc/on_buckle(obj/structure/spacevine/holder, mob/living/buckled)
return
/datum/spacevine_mutation/proc/on_explosion(severity, target, obj/structure/spacevine/holder)
return
/datum/spacevine_mutation/aggressive_spread/proc/aggrospread_act(obj/structure/spacevine/vine, mob/living/M)
return
/datum/spacevine_mutation/light
name = "Light"
hue = "#B2EA70"
quality = POSITIVE
severity = SEVERITY_TRIVIAL
/datum/spacevine_mutation/light/on_grow(obj/structure/spacevine/holder)
if(holder.energy)
holder.set_light(LIGHT_MUTATION_BRIGHTNESS, 0.3)
/datum/spacevine_mutation/toxicity
name = "Toxic"
hue = "#9B3675"
severity = SEVERITY_AVERAGE
quality = NEGATIVE
/datum/spacevine_mutation/toxicity/on_cross(obj/structure/spacevine/holder, mob/living/crosser)
if(issilicon(crosser))
return
if(prob(TOXICITY_MUTATION_PROB) && istype(crosser) && !isvineimmune(crosser))
to_chat(crosser, span_alert("You accidentally touch the vine and feel a strange sensation."))
crosser.adjustToxLoss(20)
/datum/spacevine_mutation/toxicity/on_eat(obj/structure/spacevine/holder, mob/living/eater)
if(!isvineimmune(eater))
eater.adjustToxLoss(20)
/datum/spacevine_mutation/explosive // JC IT'S A BOMB
name = "Explosive"
hue = "#D83A56"
quality = NEGATIVE
severity = SEVERITY_ABOVE_AVERAGE
/datum/spacevine_mutation/explosive/on_explosion(explosion_severity, target, obj/structure/spacevine/holder)
if(explosion_severity < 3)
qdel(holder)
else
. = 1
QDEL_IN(holder, 5)
/datum/spacevine_mutation/explosive/on_death(obj/structure/spacevine/holder, mob/hitter, obj/item/item)
explosion(holder, light_impact_range = EXPLOSION_MUTATION_IMPACT_RADIUS, adminlog = FALSE)
/datum/spacevine_mutation/fire_proof
name = "Fire proof"
hue = "#FF616D"
quality = MINOR_NEGATIVE
severity = SEVERITY_ABOVE_AVERAGE
/datum/spacevine_mutation/fire_proof/add_mutation_to_vinepiece(obj/structure/spacevine/holder)
. = ..()
holder.trait_flags |= SPACEVINE_HEAT_RESISTANT
/datum/spacevine_mutation/fire_proof/on_hit(obj/structure/spacevine/holder, mob/hitter, obj/item/item, expected_damage)
if(item && item.damtype == BURN)
. = 0
else
. = expected_damage
/datum/spacevine_mutation/cold_proof
name = "Cold proof"
hue = "#0BD5D9"
quality = MINOR_NEGATIVE
severity = SEVERITY_AVERAGE
/datum/spacevine_mutation/cold_proof/add_mutation_to_vinepiece(obj/structure/spacevine/holder)
. = ..()
holder.trait_flags |= SPACEVINE_COLD_RESISTANT
/datum/spacevine_mutation/vine_eating
name = "Vine eating"
hue = "#F4A442"
quality = MINOR_NEGATIVE
severity = SEVERITY_MINOR
/// Destroys any vine on spread-target's tile. The checks for if this should be done are in the spread() proc.
/datum/spacevine_mutation/vine_eating/on_spread(obj/structure/spacevine/holder, turf/target)
for(var/obj/structure/spacevine/prey in target)
qdel(prey)
/datum/spacevine_mutation/aggressive_spread //very OP, but im out of other ideas currently
name = "Aggressive spreading"
hue = "#316b2f"
severity = SEVERITY_MAJOR
quality = NEGATIVE
/// Checks mobs on spread-target's turf to see if they should be hit by a damaging proc or not.
/datum/spacevine_mutation/aggressive_spread/on_spread(obj/structure/spacevine/holder, turf/turf, mob/living)
for(var/mob/living/victim in turf)
if(!isvineimmune(victim) && victim.stat != DEAD) // Don't kill immune creatures. Dead check to prevent log spam when a corpse is trapped between vine eaters.
aggrospread_act(holder, victim)
/// What happens if an aggr spreading vine buckles a mob.
/datum/spacevine_mutation/aggressive_spread/on_buckle(obj/structure/spacevine/holder, mob/living/buckled)
aggrospread_act(holder, buckled)
/// Hurts mobs. To be used when a vine with aggressive spread mutation spreads into the mob's tile or buckles them.
/datum/spacevine_mutation/aggressive_spread/aggrospread_act(obj/structure/spacevine/vine, mob/living/living_mob)
var/mob/living/carbon/victim = living_mob //If the mob is carbon then it now also exists as a victim, and not just an living mob.
if(istype(victim)) //If the mob (M) is a carbon subtype (C) we move on to pick a more complex damage proc, with damage zones, wounds and armor mitigation.
var/obj/item/bodypart/limb = pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_HEAD, BODY_ZONE_CHEST) //Picks a random bodypart. Does not runtime even if it's missing.
var/armor = victim.run_armor_check(limb, MELEE, null, null) //armor = the armor value of that randomly chosen bodypart. Nulls to not print a message, because it would still print on pierce.
var/datum/spacevine_mutation/thorns/thorn = locate() in vine.mutations //Searches for the thorns mutation in the "mutations"-list inside obj/structure/spacevine, and defines T if it finds it.
if(thorn && (prob(40))) //If we found the thorns mutation there is now a chance to get stung instead of lashed or smashed.
victim.apply_damage(50, BRUTE, def_zone = limb, wound_bonus = rand(-20,10), sharpness = SHARP_POINTY) //This one gets a bit lower damage because it ignores armor.
victim.Stun(1 SECONDS) //Stopped in place for a moment.
playsound(living_mob, 'sound/weapons/pierce.ogg', 50, TRUE, -1)
living_mob.visible_message(span_danger("[living_mob] is nailed by a sharp thorn!"), \
span_userdanger("You are nailed by a sharp thorn!"))
log_combat(vine, living_mob, "aggressively pierced") //"Aggressively" for easy ctrl+F'ing in the attack logs.
else
if(prob(80))
victim.apply_damage(60, BRUTE, def_zone = limb, blocked = armor, wound_bonus = rand(-20,10), sharpness = SHARP_EDGED)
victim.Knockdown(2 SECONDS)
playsound(victim, 'sound/weapons/whip.ogg', 50, TRUE, -1)
living_mob.visible_message(span_danger("[living_mob] is lacerated by an outburst of vines!"), \
span_userdanger("You are lacerated by an outburst of vines!"))
log_combat(vine, living_mob, "aggressively lacerated")
else
victim.apply_damage(60, BRUTE, def_zone = limb, blocked = armor, wound_bonus = rand(-20,10), sharpness = NONE)
victim.Knockdown(3 SECONDS)
var/atom/throw_target = get_edge_target_turf(living_mob, get_dir(vine, get_step_away(living_mob, vine)))
victim.throw_at(throw_target, 3, 6)
playsound(victim, 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
living_mob.visible_message(span_danger("[living_mob] is smashed by a large vine!"), \
span_userdanger("You are smashed by a large vine!"))
log_combat(vine, living_mob, "aggressively smashed")
else //Living but not a carbon? Maybe a silicon? Can't be wounded so have a big chunk of simple bruteloss with no special effects. They can be entangled.
living_mob.adjustBruteLoss(75)
playsound(living_mob, 'sound/weapons/whip.ogg', 50, TRUE, -1)
living_mob.visible_message(span_danger("[living_mob] is brutally threshed by [vine]!"), \
span_userdanger("You are brutally threshed by [vine]!"))
log_combat(vine, living_mob, "aggressively spread into") //You aren't being attacked by the vines. You just happen to stand in their way.
/datum/spacevine_mutation/transparency
name = "transparent"
hue = ""
quality = POSITIVE
severity = SEVERITY_TRIVIAL
/datum/spacevine_mutation/transparency/on_grow(obj/structure/spacevine/holder)
holder.set_opacity(0)
holder.alpha = 125
/datum/spacevine_mutation/oxy_eater
name = "Oxygen consuming"
hue = "#28B5B5"
severity = SEVERITY_AVERAGE
quality = NEGATIVE
/datum/spacevine_mutation/oxy_eater/process_mutation(obj/structure/spacevine/holder)
var/turf/open/floor/turf = holder.loc
if(istype(turf))
var/datum/gas_mixture/gas_mix = turf.air
if(!gas_mix.gases[/datum/gas/oxygen])
return
gas_mix.gases[/datum/gas/oxygen][MOLES] = max(gas_mix.gases[/datum/gas/oxygen][MOLES] - GAS_MUTATION_REMOVAL_MULTIPLIER * holder.energy, 0)
gas_mix.garbage_collect()
/datum/spacevine_mutation/nitro_eater
name = "Nitrogen consuming"
hue = "#FF7B54"
severity = SEVERITY_AVERAGE
quality = NEGATIVE
/datum/spacevine_mutation/nitro_eater/process_mutation(obj/structure/spacevine/holder)
var/turf/open/floor/turf = holder.loc
if(istype(turf))
var/datum/gas_mixture/gas_mix = turf.air
if(!gas_mix.gases[/datum/gas/nitrogen])
return
gas_mix.gases[/datum/gas/nitrogen][MOLES] = max(gas_mix.gases[/datum/gas/nitrogen][MOLES] - GAS_MUTATION_REMOVAL_MULTIPLIER * holder.energy, 0)
gas_mix.garbage_collect()
/datum/spacevine_mutation/carbondioxide_eater
name = "CO2 consuming"
hue = "#798777"
severity = SEVERITY_MINOR
quality = POSITIVE
/datum/spacevine_mutation/carbondioxide_eater/process_mutation(obj/structure/spacevine/holder)
var/turf/open/floor/turf = holder.loc
if(istype(turf))
var/datum/gas_mixture/gas_mix = turf.air
if(!gas_mix.gases[/datum/gas/carbon_dioxide])
return
gas_mix.gases[/datum/gas/carbon_dioxide][MOLES] = max(gas_mix.gases[/datum/gas/carbon_dioxide][MOLES] - GAS_MUTATION_REMOVAL_MULTIPLIER * holder.energy, 0)
gas_mix.garbage_collect()
/datum/spacevine_mutation/plasma_eater
name = "Plasma consuming"
hue = "#9074b6"
severity = SEVERITY_AVERAGE
quality = POSITIVE
/datum/spacevine_mutation/plasma_eater/process_mutation(obj/structure/spacevine/holder)
var/turf/open/floor/turf = holder.loc
if(istype(turf))
var/datum/gas_mixture/gas_mix = turf.air
if(!gas_mix.gases[/datum/gas/plasma])
return
gas_mix.gases[/datum/gas/plasma][MOLES] = max(gas_mix.gases[/datum/gas/plasma][MOLES] - GAS_MUTATION_REMOVAL_MULTIPLIER * holder.energy, 0)
gas_mix.garbage_collect()
/datum/spacevine_mutation/thorns
name = "Thorny"
hue = "#9ECCA4"
severity = SEVERITY_AVERAGE
quality = NEGATIVE
/datum/spacevine_mutation/thorns/on_cross(obj/structure/spacevine/holder, mob/living/crosser)
if(prob(THORN_MUTATION_CUT_PROB) && istype(crosser) && !isvineimmune(crosser))
var/mob/living/victim = crosser
victim.adjustBruteLoss(15)
to_chat(victim, span_danger("You cut yourself on the thorny vines."))
/datum/spacevine_mutation/thorns/on_hit(obj/structure/spacevine/holder, mob/living/hitter, obj/item/item, expected_damage)
if(prob(THORN_MUTATION_CUT_PROB) && istype(hitter) && !isvineimmune(hitter))
var/mob/living/victim = hitter
victim.adjustBruteLoss(15)
to_chat(victim, span_danger("You cut yourself on the thorny vines."))
. = expected_damage
/datum/spacevine_mutation/woodening
name = "Hardened"
hue = "#997700"
quality = NEGATIVE
severity = SEVERITY_ABOVE_AVERAGE
/datum/spacevine_mutation/woodening/on_grow(obj/structure/spacevine/holder)
if(holder.energy)
holder.set_density(TRUE)
holder.modify_max_integrity(100)
/datum/spacevine_mutation/woodening/on_hit(obj/structure/spacevine/holder, mob/living/hitter, obj/item/item, expected_damage)
if(item?.get_sharpness())
. = expected_damage * 0.5
else
. = expected_damage
/datum/spacevine_mutation/flowering
name = "Flowering"
hue = "#66DE93"
quality = NEGATIVE
severity = SEVERITY_MAJOR
/datum/spacevine_mutation/flowering/on_grow(obj/structure/spacevine/holder)
if(holder.energy == 2 && prob(FLOWERING_MUTATION_SPAWN_PROB) && !locate(/obj/structure/alien/resin/flower_bud) in range(5,holder))
var/obj/structure/alien/resin/flower_bud/spawned_flower_bud = new/obj/structure/alien/resin/flower_bud(get_turf(holder))
spawned_flower_bud.trait_flags = holder.trait_flags
/datum/spacevine_mutation/flowering/on_cross(obj/structure/spacevine/holder, mob/living/crosser)
if(prob(25))
holder.entangle(crosser)
// SPACE VINES (Note that this code is very similar to Biomass code)
/obj/structure/spacevine
name = "space vine"
desc = "An extremely expansionistic species of vine."
icon = 'icons/effects/spacevines.dmi'
icon_state = "Light1"
anchored = TRUE
density = FALSE
layer = SPACEVINE_LAYER
plane = GAME_PLANE_UPPER
mouse_opacity = MOUSE_OPACITY_OPAQUE //Clicking anywhere on the turf is good enough
pass_flags = PASSTABLE | PASSGRILLE
max_integrity = 50
var/energy = 0
var/can_spread = TRUE //Can this kudzu spread?
var/datum/spacevine_controller/master = null
/// List of mutations for a specific vine
var/list/mutations = list()
var/trait_flags = 0
/obj/structure/spacevine/Initialize(mapload)
. = ..()
add_atom_colour("#ffffff", FIXED_COLOUR_PRIORITY)
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = .proc/on_entered,
)
AddElement(/datum/element/connect_loc, loc_connections)
AddElement(/datum/element/atmos_sensitive, mapload)
/obj/structure/spacevine/examine(mob/user)
. = ..()
if(!length(mutations))
. += "This vine has no mutations."
return
var/text = "This vine has the following mutations:\n"
for(var/datum/spacevine_mutation/mutation as anything in mutations)
if(mutation.name == "transparent") /// Transparent has no hue
text += "<font color='#346751'>Transparent</font> "
else
text += "<font color='[mutation.hue]'>[mutation.name]</font> "
. += text
/obj/structure/spacevine/Destroy()
for(var/datum/spacevine_mutation/mutation in mutations)
mutation.on_death(src)
if(master)
master.VineDestroyed(src)
mutations = list()
set_opacity(0)
if(has_buckled_mobs())
unbuckle_all_mobs(force=1)
return ..()
/obj/structure/spacevine/proc/on_chem_effect(datum/reagent/chem)
var/override = 0
for(var/datum/spacevine_mutation/mutation in mutations)
override += mutation.on_chem(src, chem)
if(!override && istype(chem, /datum/reagent/toxin/plantbgone))
if(prob(50))
qdel(src)
/obj/structure/spacevine/proc/eat(mob/eater)
var/override = 0
for(var/datum/spacevine_mutation/mutation in mutations)
override += mutation.on_eat(src, eater)
if(!override)
qdel(src)
/obj/structure/spacevine/attacked_by(obj/item/item, mob/living/user)
var/damage_dealt = item.force
if(item.get_sharpness())
damage_dealt *= 4
if(item.damtype == BURN)
damage_dealt *= 4
for(var/datum/spacevine_mutation/mutation in mutations)
damage_dealt = mutation.on_hit(src, user, item, damage_dealt) //on_hit now takes override damage as arg and returns new value for other mutations to permutate further
take_damage(damage_dealt, item.damtype, MELEE, 1)
/obj/structure/spacevine/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
if(damage_amount)
playsound(src, 'sound/weapons/slash.ogg', 50, TRUE)
else
playsound(src, 'sound/weapons/tap.ogg', 50, TRUE)
if(BURN)
playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
/obj/structure/spacevine/proc/on_entered(datum/source, atom/movable/movable)
SIGNAL_HANDLER
if(!isliving(movable))
return
for(var/datum/spacevine_mutation/mutation in mutations)
mutation.on_cross(src, movable)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/structure/spacevine/attack_hand(mob/user, list/modifiers)
for(var/datum/spacevine_mutation/mutation in mutations)
mutation.on_hit(src, user)
user_unbuckle_mob(user, user)
. = ..()
/obj/structure/spacevine/attack_paw(mob/living/user, list/modifiers)
for(var/datum/spacevine_mutation/mutation in mutations)
mutation.on_hit(src, user)
user_unbuckle_mob(user,user)
/obj/structure/spacevine/attack_alien(mob/living/user, list/modifiers)
eat(user)
/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
var/spread_multiplier = 5 // corresponds to artifical kudzu with production speed of 1, 10% of total vines will spread per second
var/spread_cap = 30 // corresponds to artifical kudzu with production speed of 3.5
var/list/vine_mutations_list
var/mutativeness = 1
///Maximum sum of mutation severities
var/max_mutation_severity = 20
/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)
vine_mutations_list = list()
init_subtypes(/datum/spacevine_mutation/, vine_mutations_list)
for(var/datum/spacevine_mutation/mutation as anything in vine_mutations_list)
vine_mutations_list[mutation] = max_mutation_severity - mutation.severity // this is intended to be before the potency check as the ideal maximum potency is used for weighting
if(potency != null)
mutativeness = potency * MUTATIVENESS_SCALE_FACTOR // If potency is 100, 10 mutativeness; if 1: 0.1 mutativeness
max_mutation_severity = round(potency * MAX_SEVERITY_LINEAR_COEFF + MAX_SEVERITY_CONSTANT_TERM) // If potency is 100, 20 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_SCALE_FACTOR * (MAX_POSSIBLE_PRODUCTIVITY_VALUE + 1 - production) //Best production speed of 1 increases spread_cap to 40, worst production speed of 10 lowers it to 4, 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
/datum/spacevine_controller/vv_get_dropdown()
. = ..()
VV_DROPDOWN_OPTION(VV_HK_SPACEVINE_PURGE, "Delete Vines")
/datum/spacevine_controller/vv_do_topic(href_list)
. = ..()
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/datum/spacevine_mutation/mutation in muts)
mutation.add_mutation_to_vinepiece(vine)
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(vine_mutations_list - vine.mutations)
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(delta_time)
var/vine_count = length(vines)
if(!vine_count)
qdel(src) //space vines exterminated. Remove the controller
return
var/spread_max = round(clamp(delta_time * 0.5 * vine_count / spread_multiplier, 1, spread_cap))
var/amount_processed = 0
for(var/obj/structure/spacevine/vine as anything 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.energy >= 2) //If tile is fully grown
vine.entangle_mob()
else if(DT_PROB(10, delta_time)) //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()
/// Updates the icon as the space vine grows
/obj/structure/spacevine/proc/grow()
if(!energy)
src.icon_state = pick("Med1", "Med2", "Med3")
energy = 1
set_opacity(1)
else
src.icon_state = pick("Hvy1", "Hvy2", "Hvy3")
energy = 2
for(var/datum/spacevine_mutation/mutation in mutations)
mutation.on_grow(src)
/// Buckles mobs trying to pass through it
/obj/structure/spacevine/proc/entangle_mob()
if(!has_buckled_mobs() && prob(25))
for(var/mob/living/victim in src.loc)
entangle(victim)
if(has_buckled_mobs())
break //only capture one mob at a time
/obj/structure/spacevine/proc/entangle(mob/living/victim)
if(!victim || isvineimmune(victim))
return
for(var/datum/spacevine_mutation/mutation in mutations)
mutation.on_buckle(src, victim)
if((victim.stat != DEAD) && (victim.buckled != src)) //not dead or captured
to_chat(victim, span_userdanger("The vines [pick("wind", "tangle", "tighten")] around you!"))
buckle_mob(victim, 1)
/// Finds a target tile to spread to. If checks pass it will spread to it and also proc on_spread on target.
/obj/structure/spacevine/proc/spread()
var/direction = pick(GLOB.cardinals)
var/turf/stepturf = get_step(src, direction)
if(!isspaceturf(stepturf) && stepturf.Enter(src))
var/obj/structure/spacevine/spot_taken = locate() in stepturf //Locates any vine on target turf. Calls that vine "spot_taken".
var/datum/spacevine_mutation/vine_eating/eating = locate() in mutations //Locates the vine eating trait in our own seed and calls it E.
if(!spot_taken || (eating && (spot_taken && !spot_taken.mutations?.Find(eating)))) //Proceed if there isn't a vine on the target turf, OR we have vine eater AND target vine is from our seed and doesn't. Vines from other seeds are eaten regardless.
if(master)
for(var/datum/spacevine_mutation/mutation in mutations)
mutation.on_spread(src, stepturf) //Only do the on_spread proc if it actually spreads.
stepturf = get_step(src,direction) //in case turf changes, to make sure no runtimes happen
var/obj/structure/spacevine/spawning_vine = master.spawn_spacevine_piece(stepturf, src) //Let's do a cool little animate
if(NSCOMPONENT(direction))
spawning_vine.pixel_y = direction == NORTH ? -32 : 32
animate(spawning_vine, pixel_y = 0, time = 1 SECONDS)
else
spawning_vine.pixel_x = direction == EAST ? -32 : 32
animate(spawning_vine, pixel_x = 0, time = 1 SECONDS)
/// Destroying an explosive vine sets off a chain reaction
/obj/structure/spacevine/ex_act(severity, target)
var/index
for(var/datum/spacevine_mutation/mutation in mutations)
index += mutation.on_explosion(severity, target, src)
if(!index && prob(34 * severity))
qdel(src)
/obj/structure/spacevine/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
return (exposed_temperature > FIRE_MINIMUM_TEMPERATURE_TO_SPREAD || exposed_temperature < VINE_FREEZING_POINT || !can_spread)//if you're room temperature you're safe
/obj/structure/spacevine/atmos_expose(datum/gas_mixture/air, exposed_temperature)
if(!can_spread && (exposed_temperature >= VINE_FREEZING_POINT || (trait_flags & SPACEVINE_COLD_RESISTANT)))
can_spread = TRUE // not returning here just in case its now a plasmafire and the kudzu should be deleted
if(exposed_temperature > FIRE_MINIMUM_TEMPERATURE_TO_SPREAD && !(trait_flags & SPACEVINE_HEAT_RESISTANT))
qdel(src)
else if (exposed_temperature < VINE_FREEZING_POINT && !(trait_flags & SPACEVINE_COLD_RESISTANT))
can_spread = FALSE
/obj/structure/spacevine/CanAllowThrough(atom/movable/mover, border_dir)
. = ..()
if(isvineimmune(mover))
return TRUE
/**
* 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(("vines" in victim.faction) || ("plants" in victim.faction))
return TRUE
return FALSE
#undef LIGHT_MUTATION_BRIGHTNESS
#undef TOXICITY_MUTATION_PROB
#undef EXPLOSION_MUTATION_IMPACT_RADIUS
#undef GAS_MUTATION_REMOVAL_MULTIPLIER
#undef THORN_MUTATION_CUT_PROB
#undef FLOWERING_MUTATION_SPAWN_PROB
#undef VINE_FREEZING_POINT
#undef SEVERITY_TRIVIAL
#undef SEVERITY_MINOR
#undef SEVERITY_AVERAGE
#undef SEVERITY_ABOVE_AVERAGE
#undef SEVERITY_MAJOR