mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-07 07:22:56 +00:00
770 lines
31 KiB
Plaintext
770 lines
31 KiB
Plaintext
/* SKYRAT EDIT REMOVAL - MOVED TO MODULAR
|
|
/// 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
|
|
/// Maximum energy used per atmos tick that the temperature stabilisation mutation will use to bring the temperature to T20C
|
|
#define TEMP_STABILISATION_MUTATION_MAXIMUM_ENERGY 40000
|
|
|
|
/// 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.2
|
|
|
|
/// Kudzu maximum mutation severity is a linear function of potency
|
|
#define MAX_SEVERITY_LINEAR_COEFF 0.15
|
|
#define MAX_SEVERITY_CONSTANT_TERM 10
|
|
|
|
/// Additional maximum mutation severity given to kudzu spawned by a random event
|
|
#define MAX_SEVERITY_EVENT_BONUS 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_LINEAR_COEFF 4
|
|
#define SPREAD_CAP_CONSTANT_TERM 20
|
|
/// 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
|
|
|
|
/// Kudzu's maximum possible maximum mutation severity (assuming ideal potency), used to balance mutation appearance chance
|
|
#define IDEAL_MAX_SEVERITY 20
|
|
|
|
|
|
/datum/round_event_control/spacevine
|
|
name = "Space Vines"
|
|
typepath = /datum/round_event/spacevine
|
|
weight = 15
|
|
max_occurrences = 3
|
|
min_players = 10
|
|
category = EVENT_CATEGORY_ENTITIES
|
|
description = "Kudzu begins to overtake the station. Might spawn man-traps."
|
|
|
|
/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/station/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/proc/additional_atmos_processes(obj/structure/spacevine/holder, datum/gas_mixture/air)
|
|
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_MAJOR
|
|
|
|
/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/temp_stabilisation
|
|
name = "Temperature stabilisation"
|
|
hue = "#B09856"
|
|
quality = POSITIVE
|
|
severity = SEVERITY_AVERAGE
|
|
|
|
/datum/spacevine_mutation/temp_stabilisation/add_mutation_to_vinepiece(obj/structure/spacevine/holder)
|
|
. = ..()
|
|
holder.always_atmos_process = TRUE
|
|
|
|
/datum/spacevine_mutation/temp_stabilisation/additional_atmos_processes(obj/structure/spacevine/holder, datum/gas_mixture/air)
|
|
var/heat_capacity = air.heat_capacity()
|
|
if(!heat_capacity) // No heating up space or vacuums
|
|
return
|
|
var/energy_used = min(abs(air.temperature - T20C) * heat_capacity, TEMP_STABILISATION_MUTATION_MAXIMUM_ENERGY)
|
|
var/delta_temperature = energy_used / heat_capacity
|
|
if(delta_temperature < 0.1)
|
|
return
|
|
if(air.temperature > T20C)
|
|
delta_temperature *= -1
|
|
air.temperature += delta_temperature
|
|
holder.air_update_turf(FALSE, FALSE)
|
|
|
|
/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 = victim.get_bodypart(victim.get_random_valid_zone(even_weights = TRUE)) //Picks a random bodypart.
|
|
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_FOV_HIDDEN
|
|
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
|
|
/// Should atmos always process this tile
|
|
var/always_atmos_process = FALSE
|
|
|
|
/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
|
|
///Spread multiplier, depends on productivity, affects how often kudzu spreads
|
|
var/spread_multiplier = 5 // corresponds to artifical 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 artifical kudzu with production speed of 3.5
|
|
var/static/list/vine_mutations_list
|
|
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(!vine_mutations_list)
|
|
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] = IDEAL_MAX_SEVERITY - mutation.severity // the ideal maximum potency is used for weighting
|
|
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(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
|
|
|
|
/// 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(delta_time * (spread_base + start_spread_bonus), max(delta_time * minimum_spread_rate, 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 (always_atmos_process || 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)
|
|
for(var/datum/spacevine_mutation/mutation in mutations)
|
|
mutation.additional_atmos_processes(src, air)
|
|
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
|
|
#undef MUTATIVENESS_SCALE_FACTOR
|
|
#undef MAX_SEVERITY_LINEAR_COEFF
|
|
#undef MAX_SEVERITY_CONSTANT_TERM
|
|
#undef MAX_SEVERITY_EVENT_BONUS
|
|
#undef MAX_POSSIBLE_PRODUCTIVITY_VALUE
|
|
#undef SPREAD_CAP_LINEAR_COEFF
|
|
#undef SPREAD_CAP_CONSTANT_TERM
|
|
#undef SPREAD_MULTIPLIER_MAX
|
|
#undef IDEAL_MAX_SEVERITY
|
|
*/
|