mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 18:53:06 +00:00
More work on the vine system, fixing multiple bugs.
This commit is contained in:
@@ -303,6 +303,7 @@
|
||||
#include "code\game\machinery\autolathe_datums.dm"
|
||||
#include "code\game\machinery\Beacon.dm"
|
||||
#include "code\game\machinery\bees_items.dm"
|
||||
#include "code\game\machinery\biogenerator.dm"
|
||||
#include "code\game\machinery\bioprinter.dm"
|
||||
#include "code\game\machinery\buttons.dm"
|
||||
#include "code\game\machinery\cell_charger.dm"
|
||||
@@ -957,18 +958,24 @@
|
||||
#include "code\modules\games\cards.dm"
|
||||
#include "code\modules\genetics\side_effects.dm"
|
||||
#include "code\modules\hydroponics\_hydro_setup.dm"
|
||||
#include "code\modules\hydroponics\bees_apiary.dm"
|
||||
#include "code\modules\hydroponics\biogenerator.dm"
|
||||
#include "code\modules\hydroponics\grown.dm"
|
||||
#include "code\modules\hydroponics\grown_inedible.dm"
|
||||
#include "code\modules\hydroponics\hydro_tools.dm"
|
||||
#include "code\modules\hydroponics\hydro_tray.dm"
|
||||
#include "code\modules\hydroponics\seed.dm"
|
||||
#include "code\modules\hydroponics\seed_datums.dm"
|
||||
#include "code\modules\hydroponics\seed_machines.dm"
|
||||
#include "code\modules\hydroponics\seed_mobs.dm"
|
||||
#include "code\modules\hydroponics\seed_storage.dm"
|
||||
#include "code\modules\hydroponics\seeds.dm"
|
||||
#include "code\modules\hydroponics\spread_plant.dm"
|
||||
#include "code\modules\hydroponics\seed_packets.dm"
|
||||
#include "code\modules\hydroponics\spreading\spreading.dm"
|
||||
#include "code\modules\hydroponics\spreading\spreading_growth.dm"
|
||||
#include "code\modules\hydroponics\spreading\spreading_response.dm"
|
||||
#include "code\modules\hydroponics\trays\tray.dm"
|
||||
#include "code\modules\hydroponics\trays\tray_apiary.dm"
|
||||
#include "code\modules\hydroponics\trays\tray_process.dm"
|
||||
#include "code\modules\hydroponics\trays\tray_reagents.dm"
|
||||
#include "code\modules\hydroponics\trays\tray_soil.dm"
|
||||
#include "code\modules\hydroponics\trays\tray_tools.dm"
|
||||
#include "code\modules\hydroponics\trays\tray_update_icons.dm"
|
||||
#include "code\modules\library\lib_items.dm"
|
||||
#include "code\modules\library\lib_machines.dm"
|
||||
#include "code\modules\library\lib_readme.dm"
|
||||
|
||||
@@ -25,11 +25,17 @@
|
||||
usr << "\red Movement is admin-disabled." //This is to identify lag problems
|
||||
return
|
||||
|
||||
if (istype(A,/mob/living/carbon))
|
||||
var/mob/living/carbon/M = A
|
||||
if (istype(A,/mob/living))
|
||||
var/mob/living/M = A
|
||||
if(M.lying)
|
||||
..()
|
||||
return
|
||||
|
||||
// Ugly hack :( Should never have multiple plants in the same tile.
|
||||
var/obj/effect/plant/plant = locate() in contents
|
||||
if(plant) plant.trodden_on(M)
|
||||
|
||||
// Dirt overlays.
|
||||
dirt++
|
||||
var/obj/effect/decal/cleanable/dirt/dirtoverlay = locate(/obj/effect/decal/cleanable/dirt, src)
|
||||
if (dirt >= 50)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//Misc
|
||||
#define DEAD_PLANT_COLOUR "#C2A180"
|
||||
|
||||
// Definitions for genes (trait groupings)
|
||||
#define GENE_PRODUCTS "products"
|
||||
#define GENE_CONSUMPTION "consumption"
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
return
|
||||
if(!reagents || reagents.total_volume <= 0)
|
||||
return
|
||||
reagents.remove_any(rand(1,3))
|
||||
reagents.remove_any(rand(1,3)) //Todo, make it actually remove the reagents the seed uses.
|
||||
seed.do_thorns(H,src)
|
||||
seed.do_sting(H,src,pick("r_hand","l_hand"))
|
||||
|
||||
|
||||
773
code/modules/hydroponics/seed.dm
Normal file
773
code/modules/hydroponics/seed.dm
Normal file
@@ -0,0 +1,773 @@
|
||||
// Sprite lists.
|
||||
var/global/list/plant_sprites = list() // List of all harvested product sprites.
|
||||
var/global/list/plant_product_sprites = list() // List of all growth sprites plus number of growth stages.
|
||||
|
||||
// Proc for creating a random seed type.
|
||||
/proc/create_random_seed(var/survive_on_station)
|
||||
var/datum/seed/seed = new()
|
||||
seed.randomize()
|
||||
seed.uid = seed_types.len + 1
|
||||
seed.name = "[seed.uid]"
|
||||
seed_types[seed.name] = seed
|
||||
|
||||
if(survive_on_station)
|
||||
if(seed.consume_gasses)
|
||||
seed.consume_gasses["phoron"] = null
|
||||
seed.consume_gasses["carbon_dioxide"] = null
|
||||
seed.set_trait(TRAIT_IDEAL_HEAT,293)
|
||||
seed.set_trait(TRAIT_HEAT_TOLERANCE,20)
|
||||
seed.set_trait(TRAIT_IDEAL_LIGHT,8)
|
||||
seed.set_trait(TRAIT_LIGHT_TOLERANCE,5)
|
||||
seed.set_trait(TRAIT_LOWKPA_TOLERANCE,25)
|
||||
seed.set_trait(TRAIT_HIGHKPA_TOLERANCE,200)
|
||||
|
||||
return seed
|
||||
|
||||
// Debug for testing seed genes.
|
||||
/client/proc/show_plant_genes()
|
||||
set category = "Debug"
|
||||
set name = "Show Plant Genes"
|
||||
set desc = "Prints the round's plant gene masks."
|
||||
|
||||
if(!holder) return
|
||||
|
||||
if(!gene_tag_masks)
|
||||
usr << "Gene masks not set."
|
||||
return
|
||||
|
||||
for(var/mask in gene_tag_masks)
|
||||
usr << "[mask]: [gene_tag_masks[mask]]"
|
||||
|
||||
// Predefined/roundstart varieties use a string key to make it
|
||||
// easier to grab the new variety when mutating. Post-roundstart
|
||||
// and mutant varieties use their uid converted to a string instead.
|
||||
// Looks like shit but it's sort of necessary.
|
||||
|
||||
proc/populate_seed_list()
|
||||
|
||||
// Build the icon lists.
|
||||
for(var/icostate in icon_states('icons/obj/hydroponics_growing.dmi'))
|
||||
var/split = findtext(icostate,"-")
|
||||
if(!split)
|
||||
// invalid icon_state
|
||||
continue
|
||||
|
||||
var/ikey = copytext(icostate,(split+1))
|
||||
if(ikey == "dead")
|
||||
// don't count dead icons
|
||||
continue
|
||||
ikey = text2num(ikey)
|
||||
var/base = copytext(icostate,1,split)
|
||||
|
||||
if(!(plant_sprites[base]) || (plant_sprites[base]<ikey))
|
||||
plant_sprites[base] = ikey
|
||||
|
||||
for(var/icostate in icon_states('icons/obj/hydroponics_products.dmi'))
|
||||
plant_product_sprites |= icostate
|
||||
|
||||
// Populate the global seed datum list.
|
||||
for(var/type in typesof(/datum/seed)-/datum/seed)
|
||||
var/datum/seed/S = new type
|
||||
seed_types[S.name] = S
|
||||
S.uid = "[seed_types.len]"
|
||||
S.roundstart = 1
|
||||
|
||||
// Make sure any seed packets that were mapped in are updated
|
||||
// correctly (since the seed datums did not exist a tick ago).
|
||||
for(var/obj/item/seeds/S in world)
|
||||
S.update_seed()
|
||||
|
||||
//Might as well mask the gene types while we're at it.
|
||||
var/list/used_masks = list()
|
||||
var/list/plant_traits = ALL_GENES
|
||||
while(plant_traits && plant_traits.len)
|
||||
var/gene_tag = pick(plant_traits)
|
||||
var/gene_mask = "[num2hex(rand(0,255))]"
|
||||
|
||||
while(gene_mask in used_masks)
|
||||
gene_mask = "[num2hex(rand(0,255))]"
|
||||
|
||||
used_masks += gene_mask
|
||||
plant_traits -= gene_tag
|
||||
gene_tag_masks[gene_tag] = gene_mask
|
||||
|
||||
/datum/plantgene
|
||||
var/genetype // Label used when applying trait.
|
||||
var/list/values // Values to copy into the target seed datum.
|
||||
|
||||
/datum/seed
|
||||
|
||||
//Tracking.
|
||||
var/uid // Unique identifier.
|
||||
var/name // Index for global list.
|
||||
var/seed_name // Plant name for seed packet.
|
||||
var/seed_noun = "seeds" // Descriptor for packet.
|
||||
var/display_name // Prettier name.
|
||||
var/roundstart // If set, seed will not display variety number.
|
||||
var/mysterious // Only used for the random seed packets.
|
||||
var/can_self_harvest = 0 // Mostly used for living mobs.
|
||||
var/growth_stages = 0 // Number of stages the plant passes through before it is mature.
|
||||
var/list/traits = list() // Initialized in New()
|
||||
var/list/products // Possible fruit/other product paths.
|
||||
var/list/mutants // Possible predefined mutant varieties, if any.
|
||||
var/list/chems // Chemicals that plant produces in products/injects into victim.
|
||||
var/list/consume_gasses // The plant will absorb these gasses during its life.
|
||||
var/list/exude_gasses // The plant will exude these gasses during its life.
|
||||
var/splat_type = /obj/effect/decal/cleanable/fruit_smudge // Graffiti decal.
|
||||
|
||||
/datum/seed/New()
|
||||
|
||||
set_trait(TRAIT_IMMUTABLE, 0) // If set, plant will never mutate. If -1, plant is highly mutable.
|
||||
set_trait(TRAIT_HARVEST_REPEAT, 0) // If 1, this plant will fruit repeatedly.
|
||||
set_trait(TRAIT_PRODUCES_POWER, 0) // Can be used to make a battery.
|
||||
set_trait(TRAIT_JUICY, 0) // When thrown, causes a splatter decal.
|
||||
set_trait(TRAIT_EXPLOSIVE, 0) // When thrown, acts as a grenade.
|
||||
set_trait(TRAIT_CARNIVOROUS, 0) // 0 = none, 1 = eat pests in tray, 2 = eat living things (when a vine).
|
||||
set_trait(TRAIT_PARASITE, 0) // 0 = no, 1 = gain health from weed level.
|
||||
set_trait(TRAIT_STINGS, 0) // Can cause damage/inject reagents when thrown or handled.
|
||||
set_trait(TRAIT_YIELD, 0) // Amount of product.
|
||||
set_trait(TRAIT_SPREAD, 0) // 0 limits plant to tray, 1 = creepers, 2 = vines.
|
||||
set_trait(TRAIT_MATURATION, 0) // Time taken before the plant is mature.
|
||||
set_trait(TRAIT_PRODUCTION, 0) // Time before harvesting can be undertaken again.
|
||||
set_trait(TRAIT_TELEPORTING, 0) // Uses the bluespace tomato effect.
|
||||
set_trait(TRAIT_BIOLUM, 0) // Plant is bioluminescent.
|
||||
set_trait(TRAIT_ALTER_TEMP, 0) // If set, the plant will periodically alter local temp by this amount.
|
||||
set_trait(TRAIT_PRODUCT_ICON, 0) // Icon to use for fruit coming from this plant.
|
||||
set_trait(TRAIT_PLANT_ICON, 0) // Icon to use for the plant growing in the tray.
|
||||
set_trait(TRAIT_PRODUCT_COLOUR, 0) // Colour to apply to product icon.
|
||||
set_trait(TRAIT_BIOLUM_COLOUR, 0) // The colour of the plant's radiance.
|
||||
set_trait(TRAIT_POTENCY, 1) // General purpose plant strength value.
|
||||
set_trait(TRAIT_REQUIRES_NUTRIENTS, 1) // The plant can starve.
|
||||
set_trait(TRAIT_REQUIRES_WATER, 1) // The plant can become dehydrated.
|
||||
set_trait(TRAIT_WATER_CONSUMPTION, 3) // Plant drinks this much per tick.
|
||||
set_trait(TRAIT_LIGHT_TOLERANCE, 5) // Departure from ideal that is survivable.
|
||||
set_trait(TRAIT_TOXINS_TOLERANCE, 5) // Resistance to poison.
|
||||
set_trait(TRAIT_PEST_TOLERANCE, 5) // Threshold for pests to impact health.
|
||||
set_trait(TRAIT_WEED_TOLERANCE, 5) // Threshold for weeds to impact health.
|
||||
set_trait(TRAIT_IDEAL_LIGHT, 8) // Preferred light level in luminosity.
|
||||
set_trait(TRAIT_HEAT_TOLERANCE, 20) // Departure from ideal that is survivable.
|
||||
set_trait(TRAIT_LOWKPA_TOLERANCE, 25) // Low pressure capacity.
|
||||
set_trait(TRAIT_ENDURANCE, 100) // Maximum plant HP when growing.
|
||||
set_trait(TRAIT_HIGHKPA_TOLERANCE, 200) // High pressure capacity.
|
||||
set_trait(TRAIT_IDEAL_HEAT, 293) // Preferred temperature in Kelvin.
|
||||
set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) // Plant eats this much per tick.
|
||||
set_trait(TRAIT_PLANT_COLOUR, "#6EF86A") // Colour of the plant icon.
|
||||
|
||||
spawn(5)
|
||||
sleep(-1)
|
||||
update_growth_stages()
|
||||
|
||||
/datum/seed/proc/get_trait(var/trait)
|
||||
return traits["[trait]"]
|
||||
|
||||
/datum/seed/proc/set_trait(var/trait,var/nval,var/ubound,var/lbound, var/degrade)
|
||||
if(!isnull(degrade)) nval *= degrade
|
||||
if(!isnull(ubound)) nval = min(nval,ubound)
|
||||
if(!isnull(lbound)) nval = max(nval,lbound)
|
||||
traits["[trait]"] = nval
|
||||
|
||||
// Does brute damage to a target.
|
||||
/datum/seed/proc/do_thorns(var/mob/living/carbon/human/target, var/obj/item/fruit, var/target_limb)
|
||||
|
||||
if(!istype(target) || !get_trait(TRAIT_CARNIVOROUS))
|
||||
return
|
||||
|
||||
if(!target_limb) target_limb = pick("l_foot","r_foot","l_leg","r_leg","l_hand","r_hand","l_arm", "r_arm","head","chest","groin")
|
||||
var/datum/organ/external/affecting = target.get_organ(target_limb)
|
||||
var/damage = 0
|
||||
|
||||
if(get_trait(TRAIT_CARNIVOROUS))
|
||||
if(get_trait(TRAIT_CARNIVOROUS) == 2)
|
||||
if(affecting)
|
||||
target << "<span class='danger'>\The [fruit]'s thorns pierce your [affecting.display_name] greedily!</span>"
|
||||
else
|
||||
target << "<span class='danger'>\The [fruit]'s thorns pierce your flesh greedily!</span>"
|
||||
damage = get_trait(TRAIT_POTENCY)/2
|
||||
else
|
||||
if(affecting)
|
||||
target << "<span class='danger'>\The [fruit]'s thorns dig deeply into your [affecting.display_name]!</span>"
|
||||
else
|
||||
target << "<span class='danger'>\The [fruit]'s thorns dig deeply into your flesh!</span>"
|
||||
damage = get_trait(TRAIT_POTENCY)/5
|
||||
else
|
||||
return
|
||||
|
||||
if(affecting)
|
||||
affecting.take_damage(damage, 0)
|
||||
affecting.add_autopsy_data("Thorns",damage)
|
||||
else
|
||||
target.adjustBruteLoss(damage)
|
||||
target.UpdateDamageIcon()
|
||||
target.updatehealth()
|
||||
|
||||
// Adds reagents to a target.
|
||||
/datum/seed/proc/do_sting(var/mob/living/carbon/human/target, var/obj/item/fruit)
|
||||
if(!get_trait(TRAIT_STINGS))
|
||||
return
|
||||
if(chems && chems.len)
|
||||
target << "<span class='danger'>You are stung by \the [fruit]!</span>"
|
||||
for(var/rid in chems)
|
||||
var/injecting = min(5,max(1,get_trait(TRAIT_POTENCY)/5))
|
||||
target.reagents.add_reagent(rid,injecting)
|
||||
|
||||
//Splatter a turf.
|
||||
/datum/seed/proc/splatter(var/turf/T,var/obj/item/thrown)
|
||||
if(splat_type)
|
||||
var/obj/effect/plant/splat = new splat_type(T, src)
|
||||
if(!istype(splat)) // Plants handle their own stuff.
|
||||
splat.name = "[thrown.name] [pick("smear","smudge","splatter")]"
|
||||
if(get_trait(TRAIT_BIOLUM))
|
||||
if(get_trait(TRAIT_BIOLUM_COLOUR))
|
||||
splat.l_color = get_trait(TRAIT_BIOLUM_COLOUR)
|
||||
splat.SetLuminosity(get_trait(TRAIT_BIOLUM))
|
||||
if(get_trait(TRAIT_PRODUCT_COLOUR))
|
||||
splat.color = get_trait(TRAIT_PRODUCT_COLOUR)
|
||||
|
||||
if(chems)
|
||||
for(var/mob/living/M in T.contents)
|
||||
if(!M.reagents)
|
||||
continue
|
||||
for(var/chem in chems)
|
||||
var/injecting = min(5,max(1,get_trait(TRAIT_POTENCY)/3))
|
||||
M.reagents.add_reagent(chem,injecting)
|
||||
|
||||
//Applies an effect to a target atom.
|
||||
/datum/seed/proc/thrown_at(var/obj/item/thrown,var/atom/target, var/force_explode)
|
||||
|
||||
var/splatted
|
||||
var/turf/origin_turf = get_turf(target)
|
||||
|
||||
if(force_explode || get_trait(TRAIT_EXPLOSIVE))
|
||||
|
||||
var/flood_dist = min(10,max(1,get_trait(TRAIT_POTENCY)/15))
|
||||
var/list/open_turfs = list()
|
||||
var/list/closed_turfs = list()
|
||||
var/list/valid_turfs = list()
|
||||
open_turfs |= origin_turf
|
||||
|
||||
// Flood fill to get affected turfs.
|
||||
while(open_turfs.len)
|
||||
var/turf/T = pick(open_turfs)
|
||||
open_turfs -= T
|
||||
closed_turfs |= T
|
||||
valid_turfs |= T
|
||||
|
||||
for(var/dir in alldirs)
|
||||
var/turf/neighbor = get_step(T,dir)
|
||||
if(!neighbor || (neighbor in closed_turfs) || (neighbor in open_turfs))
|
||||
continue
|
||||
if(neighbor.density || get_dist(neighbor,origin_turf) > flood_dist || istype(neighbor,/turf/space))
|
||||
closed_turfs |= neighbor
|
||||
continue
|
||||
// Check for windows.
|
||||
var/no_los
|
||||
var/turf/last_turf = origin_turf
|
||||
for(var/turf/target_turf in getline(origin_turf,neighbor))
|
||||
if(!last_turf.Enter(target_turf) || target_turf.density)
|
||||
no_los = 1
|
||||
break
|
||||
last_turf = target_turf
|
||||
if(!no_los && !origin_turf.Enter(neighbor))
|
||||
no_los = 1
|
||||
if(no_los)
|
||||
closed_turfs |= neighbor
|
||||
continue
|
||||
open_turfs |= neighbor
|
||||
|
||||
for(var/turf/T in valid_turfs)
|
||||
for(var/mob/living/M in T.contents)
|
||||
apply_special_effect(M)
|
||||
splatter(T,thrown)
|
||||
origin_turf.visible_message("<span class='danger'>The [thrown.name] explodes!</span>")
|
||||
del(thrown)
|
||||
return
|
||||
|
||||
if(istype(target,/mob/living))
|
||||
splatted = apply_special_effect(target,thrown)
|
||||
else if(istype(target,/turf))
|
||||
splatted = 1
|
||||
for(var/mob/living/M in target.contents)
|
||||
apply_special_effect(M)
|
||||
|
||||
if(get_trait(TRAIT_JUICY) && splatted)
|
||||
splatter(origin_turf,thrown)
|
||||
origin_turf.visible_message("<span class='danger'>The [thrown.name] splatters against [target]!</span>")
|
||||
del(thrown)
|
||||
|
||||
/datum/seed/proc/handle_environment(var/turf/current_turf, var/datum/gas_mixture/environment, var/check_only)
|
||||
|
||||
var/health_change = 0
|
||||
// Handle gas consumption.
|
||||
if(consume_gasses && consume_gasses.len)
|
||||
var/missing_gas = 0
|
||||
for(var/gas in consume_gasses)
|
||||
if(environment && environment.gas && environment.gas[gas] && \
|
||||
environment.gas[gas] >= consume_gasses[gas])
|
||||
if(!check_only)
|
||||
environment.adjust_gas(gas,-consume_gasses[gas],1)
|
||||
else
|
||||
missing_gas++
|
||||
|
||||
if(missing_gas > 0)
|
||||
health_change += missing_gas * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// Process it.
|
||||
var/pressure = environment.return_pressure()
|
||||
if(pressure < get_trait(TRAIT_LOWKPA_TOLERANCE)|| pressure > get_trait(TRAIT_HIGHKPA_TOLERANCE))
|
||||
health_change += rand(1,3) * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
if(abs(environment.temperature - get_trait(TRAIT_IDEAL_HEAT)) > get_trait(TRAIT_HEAT_TOLERANCE))
|
||||
health_change += rand(1,3) * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// Handle gas production.
|
||||
if(exude_gasses && exude_gasses.len && !check_only)
|
||||
for(var/gas in exude_gasses)
|
||||
environment.adjust_gas(gas, max(1,round((exude_gasses[gas]*get_trait(TRAIT_POTENCY))/exude_gasses.len)))
|
||||
|
||||
// Handle light requirements.
|
||||
var/area/A = get_area(current_turf)
|
||||
if(A)
|
||||
var/light_available
|
||||
if(A.lighting_use_dynamic)
|
||||
light_available = max(0,min(10,current_turf.lighting_lumcount)-5)
|
||||
else
|
||||
light_available = 5
|
||||
if(abs(light_available - get_trait(TRAIT_IDEAL_LIGHT)) > get_trait(TRAIT_LIGHT_TOLERANCE))
|
||||
health_change += rand(1,3) * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
return health_change
|
||||
|
||||
/datum/seed/proc/apply_special_effect(var/mob/living/target,var/obj/item/thrown)
|
||||
|
||||
var/impact = 1
|
||||
do_sting(target,thrown)
|
||||
do_thorns(target,thrown)
|
||||
|
||||
// Bluespace tomato code copied over from grown.dm.
|
||||
if(get_trait(TRAIT_TELEPORTING))
|
||||
|
||||
//Plant potency determines radius of teleport.
|
||||
var/outer_teleport_radius = get_trait(TRAIT_POTENCY)/5
|
||||
var/inner_teleport_radius = get_trait(TRAIT_POTENCY)/15
|
||||
|
||||
var/list/turfs = list()
|
||||
if(inner_teleport_radius > 0)
|
||||
for(var/turf/T in orange(target,outer_teleport_radius))
|
||||
if(get_dist(target,T) >= inner_teleport_radius)
|
||||
turfs |= T
|
||||
|
||||
if(turfs.len)
|
||||
// Moves the mob, causes sparks.
|
||||
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
||||
s.set_up(3, 1, get_turf(target))
|
||||
s.start()
|
||||
var/turf/picked = get_turf(pick(turfs)) // Just in case...
|
||||
new/obj/effect/decal/cleanable/molten_item(get_turf(target)) // Leave a pile of goo behind for dramatic effect...
|
||||
target.loc = picked // And teleport them to the chosen location.
|
||||
|
||||
impact = 1
|
||||
|
||||
return impact
|
||||
|
||||
//Creates a random seed. MAKE SURE THE LINE HAS DIVERGED BEFORE THIS IS CALLED.
|
||||
/datum/seed/proc/randomize()
|
||||
|
||||
roundstart = 0
|
||||
seed_name = "strange plant" // TODO: name generator.
|
||||
display_name = "strange plants" // TODO: name generator.
|
||||
mysterious = 1
|
||||
seed_noun = pick("spores","nodes","cuttings","seeds")
|
||||
products = list(pick(typesof(/obj/item/weapon/reagent_containers/food/snacks/grown)-/obj/item/weapon/reagent_containers/food/snacks/grown))
|
||||
|
||||
set_trait(TRAIT_POTENCY,rand(5,30),200,0)
|
||||
set_trait(TRAIT_PRODUCT_ICON,pick(plant_product_sprites))
|
||||
set_trait(TRAIT_PLANT_ICON,pick(plant_sprites))
|
||||
set_trait(TRAIT_PLANT_COLOUR,"#[get_random_colour(0,75,190)]")
|
||||
set_trait(TRAIT_PRODUCT_COLOUR,"#[get_random_colour(0,75,190)]")
|
||||
update_growth_stages()
|
||||
|
||||
if(prob(20))
|
||||
set_trait(TRAIT_HARVEST_REPEAT,1)
|
||||
|
||||
if(prob(15))
|
||||
if(prob(15))
|
||||
set_trait(TRAIT_JUICY,2)
|
||||
else
|
||||
set_trait(TRAIT_JUICY,1)
|
||||
|
||||
if(prob(5))
|
||||
set_trait(TRAIT_STINGS,1)
|
||||
|
||||
if(prob(5))
|
||||
set_trait(TRAIT_PRODUCES_POWER,1)
|
||||
|
||||
if(prob(1))
|
||||
set_trait(TRAIT_EXPLOSIVE,1)
|
||||
else if(prob(1))
|
||||
set_trait(TRAIT_TELEPORTING,1)
|
||||
|
||||
if(prob(5))
|
||||
consume_gasses = list()
|
||||
var/gas = pick("oxygen","nitrogen","phoron","carbon_dioxide")
|
||||
consume_gasses[gas] = rand(3,9)
|
||||
|
||||
if(prob(5))
|
||||
exude_gasses = list()
|
||||
var/gas = pick("oxygen","nitrogen","phoron","carbon_dioxide")
|
||||
exude_gasses[gas] = rand(3,9)
|
||||
|
||||
chems = list()
|
||||
if(prob(80))
|
||||
chems["nutriment"] = list(rand(1,10),rand(10,20))
|
||||
|
||||
var/additional_chems = rand(0,5)
|
||||
|
||||
if(additional_chems)
|
||||
var/list/possible_chems = list(
|
||||
"bicaridine",
|
||||
"hyperzine",
|
||||
"cryoxadone",
|
||||
"blood",
|
||||
"water",
|
||||
"potassium",
|
||||
"plasticide",
|
||||
"mutationtoxin",
|
||||
"amutationtoxin",
|
||||
"inaprovaline",
|
||||
"space_drugs",
|
||||
"paroxetine",
|
||||
"mercury",
|
||||
"sugar",
|
||||
"radium",
|
||||
"ryetalyn",
|
||||
"alkysine",
|
||||
"thermite",
|
||||
"tramadol",
|
||||
"cryptobiolin",
|
||||
"dermaline",
|
||||
"dexalin",
|
||||
"phoron",
|
||||
"synaptizine",
|
||||
"impedrezene",
|
||||
"hyronalin",
|
||||
"peridaxon",
|
||||
"toxin",
|
||||
"rezadone",
|
||||
"ethylredoxrazine",
|
||||
"slimejelly",
|
||||
"cyanide",
|
||||
"mindbreaker",
|
||||
"stoxin"
|
||||
)
|
||||
|
||||
for(var/x=1;x<=additional_chems;x++)
|
||||
if(!possible_chems.len)
|
||||
break
|
||||
var/new_chem = pick(possible_chems)
|
||||
possible_chems -= new_chem
|
||||
chems[new_chem] = list(rand(1,10),rand(10,20))
|
||||
|
||||
if(prob(90))
|
||||
set_trait(TRAIT_REQUIRES_NUTRIENTS,1)
|
||||
set_trait(TRAIT_NUTRIENT_CONSUMPTION,rand(100)*0.1)
|
||||
else
|
||||
set_trait(TRAIT_REQUIRES_NUTRIENTS,0)
|
||||
|
||||
if(prob(90))
|
||||
set_trait(TRAIT_REQUIRES_WATER,1)
|
||||
set_trait(TRAIT_WATER_CONSUMPTION,rand(10))
|
||||
else
|
||||
set_trait(TRAIT_REQUIRES_WATER,0)
|
||||
|
||||
set_trait(TRAIT_IDEAL_HEAT, rand(100,400))
|
||||
set_trait(TRAIT_HEAT_TOLERANCE, rand(10,30))
|
||||
set_trait(TRAIT_IDEAL_LIGHT, rand(2,10))
|
||||
set_trait(TRAIT_LIGHT_TOLERANCE, rand(2,7))
|
||||
set_trait(TRAIT_TOXINS_TOLERANCE, rand(2,7))
|
||||
set_trait(TRAIT_PEST_TOLERANCE, rand(2,7))
|
||||
set_trait(TRAIT_WEED_TOLERANCE, rand(2,7))
|
||||
set_trait(TRAIT_LOWKPA_TOLERANCE, rand(10,50))
|
||||
set_trait(TRAIT_HIGHKPA_TOLERANCE,rand(100,300))
|
||||
|
||||
if(prob(5))
|
||||
set_trait(TRAIT_ALTER_TEMP,rand(-5,5))
|
||||
|
||||
if(prob(1))
|
||||
set_trait(TRAIT_IMMUTABLE,-1)
|
||||
|
||||
var/carnivore_prob = rand(100)
|
||||
if(carnivore_prob < 5)
|
||||
set_trait(TRAIT_CARNIVOROUS,2)
|
||||
else if(carnivore_prob < 10)
|
||||
set_trait(TRAIT_CARNIVOROUS,1)
|
||||
|
||||
if(prob(10))
|
||||
set_trait(TRAIT_PARASITE,1)
|
||||
|
||||
var/vine_prob = rand(100)
|
||||
if(vine_prob < 5)
|
||||
set_trait(TRAIT_SPREAD,2)
|
||||
else if(vine_prob < 10)
|
||||
set_trait(TRAIT_SPREAD,1)
|
||||
|
||||
if(prob(5))
|
||||
set_trait(TRAIT_BIOLUM,1)
|
||||
set_trait(TRAIT_BIOLUM_COLOUR,"#[get_random_colour(0,75,190)]")
|
||||
|
||||
set_trait(TRAIT_ENDURANCE,rand(60,100))
|
||||
set_trait(TRAIT_YIELD,rand(3,15))
|
||||
set_trait(TRAIT_MATURATION,rand(5,15))
|
||||
set_trait(TRAIT_PRODUCTION,get_trait(TRAIT_MATURATION)+rand(2,5))
|
||||
|
||||
//Returns a key corresponding to an entry in the global seed list.
|
||||
/datum/seed/proc/get_mutant_variant()
|
||||
if(!mutants || !mutants.len || get_trait(TRAIT_IMMUTABLE) > 0) return 0
|
||||
return pick(mutants)
|
||||
|
||||
//Mutates the plant overall (randomly).
|
||||
/datum/seed/proc/mutate(var/degree,var/turf/source_turf)
|
||||
|
||||
if(!degree || get_trait(TRAIT_IMMUTABLE) > 0) return
|
||||
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name] quivers!</span>")
|
||||
|
||||
//This looks like shit, but it's a lot easier to read/change this way.
|
||||
var/total_mutations = rand(1,1+degree)
|
||||
for(var/i = 0;i<total_mutations;i++)
|
||||
switch(rand(0,11))
|
||||
if(0) //Plant cancer!
|
||||
set_trait(TRAIT_ENDURANCE,get_trait(TRAIT_ENDURANCE)-rand(10,20),null,0)
|
||||
source_turf.visible_message("<span class='danger'>\The [display_name] withers rapidly!</span>")
|
||||
if(1)
|
||||
set_trait(TRAIT_NUTRIENT_CONSUMPTION,get_trait(TRAIT_NUTRIENT_CONSUMPTION)+rand(-(degree*0.1),(degree*0.1)),5,0)
|
||||
set_trait(TRAIT_WATER_CONSUMPTION, get_trait(TRAIT_WATER_CONSUMPTION) +rand(-degree,degree),50,0)
|
||||
set_trait(TRAIT_JUICY, !get_trait(TRAIT_JUICY))
|
||||
set_trait(TRAIT_STINGS, !get_trait(TRAIT_STINGS))
|
||||
if(2)
|
||||
set_trait(TRAIT_IDEAL_HEAT, get_trait(TRAIT_IDEAL_HEAT) + (rand(-5,5)*degree),800,70)
|
||||
set_trait(TRAIT_HEAT_TOLERANCE, get_trait(TRAIT_HEAT_TOLERANCE) + (rand(-5,5)*degree),800,70)
|
||||
set_trait(TRAIT_LOWKPA_TOLERANCE, get_trait(TRAIT_LOWKPA_TOLERANCE)+ (rand(-5,5)*degree),80,0)
|
||||
set_trait(TRAIT_HIGHKPA_TOLERANCE, get_trait(TRAIT_HIGHKPA_TOLERANCE)+(rand(-5,5)*degree),500,110)
|
||||
set_trait(TRAIT_EXPLOSIVE,1)
|
||||
if(3)
|
||||
set_trait(TRAIT_IDEAL_LIGHT, get_trait(TRAIT_IDEAL_LIGHT)+(rand(-1,1)*degree),30,0)
|
||||
set_trait(TRAIT_LIGHT_TOLERANCE, get_trait(TRAIT_LIGHT_TOLERANCE)+(rand(-2,2)*degree),10,0)
|
||||
if(4)
|
||||
set_trait(TRAIT_TOXINS_TOLERANCE, get_trait(TRAIT_TOXINS_TOLERANCE)+(rand(-2,2)*degree),10,0)
|
||||
if(5)
|
||||
set_trait(TRAIT_WEED_TOLERANCE, get_trait(TRAIT_WEED_TOLERANCE)+(rand(-2,2)*degree),10, 0)
|
||||
if(prob(degree*5))
|
||||
set_trait(TRAIT_CARNIVOROUS, get_trait(TRAIT_CARNIVOROUS)+rand(-degree,degree),2, 0)
|
||||
if(get_trait(TRAIT_CARNIVOROUS))
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name] shudders hungrily.</span>")
|
||||
if(6)
|
||||
set_trait(TRAIT_WEED_TOLERANCE, get_trait(TRAIT_WEED_TOLERANCE)+(rand(-2,2)*degree),10, 0)
|
||||
if(prob(degree*5))
|
||||
set_trait(TRAIT_PARASITE,!get_trait(TRAIT_PARASITE))
|
||||
if(7)
|
||||
if(get_trait(TRAIT_YIELD) != -1)
|
||||
set_trait(TRAIT_YIELD, get_trait(TRAIT_YIELD)+(rand(-2,2)*degree),10,0)
|
||||
if(8)
|
||||
set_trait(TRAIT_ENDURANCE, get_trait(TRAIT_ENDURANCE)+(rand(-5,5)*degree),100,10)
|
||||
set_trait(TRAIT_PRODUCTION, get_trait(TRAIT_PRODUCTION)+(rand(-1,1)*degree),10, 1)
|
||||
set_trait(TRAIT_POTENCY, get_trait(TRAIT_POTENCY)+(rand(-20,20)*degree),200, 0)
|
||||
if(prob(degree*5))
|
||||
set_trait(TRAIT_SPREAD, get_trait(TRAIT_SPREAD)+rand(-1,1),2, 0)
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name] spasms visibly, shifting in the tray.</span>")
|
||||
if(9)
|
||||
set_trait(TRAIT_MATURATION, get_trait(TRAIT_MATURATION)+(rand(-1,1)*degree),30, 0)
|
||||
if(prob(degree*5))
|
||||
set_trait(TRAIT_HARVEST_REPEAT, !get_trait(TRAIT_HARVEST_REPEAT))
|
||||
if(10)
|
||||
if(prob(degree*2))
|
||||
set_trait(TRAIT_BIOLUM, !get_trait(TRAIT_BIOLUM))
|
||||
if(get_trait(TRAIT_BIOLUM))
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name] begins to glow!</span>")
|
||||
if(prob(degree*2))
|
||||
set_trait(TRAIT_BIOLUM_COLOUR,"#[get_random_colour(0,75,190)]")
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name]'s glow </span><font color='[get_trait(TRAIT_BIOLUM_COLOUR)]'>changes colour</font>!")
|
||||
else
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name]'s glow dims...</span>")
|
||||
if(11)
|
||||
set_trait(TRAIT_TELEPORTING,1)
|
||||
|
||||
return
|
||||
|
||||
//Mutates a specific trait/set of traits.
|
||||
/datum/seed/proc/apply_gene(var/datum/plantgene/gene)
|
||||
|
||||
if(!gene || !gene.values || get_trait(TRAIT_IMMUTABLE) > 0) return
|
||||
|
||||
// Splicing products has some detrimental effects on yield and lifespan.
|
||||
// We handle this before we do the rest of the looping, as normal traits don't really include lists.
|
||||
if(gene.genetype == GENE_PRODUCTS)
|
||||
for(var/trait in list(TRAIT_YIELD, TRAIT_ENDURANCE))
|
||||
if(get_trait(trait) > 0) set_trait(trait,get_trait(trait),null,1,0.85)
|
||||
|
||||
if(!products) products = list()
|
||||
products |= gene.values["[TRAIT_PRODUCTS]"]
|
||||
|
||||
if(!chems) chems = list()
|
||||
|
||||
var/list/gene_value = gene.values["[TRAIT_CHEMS]"]
|
||||
for(var/rid in gene_value)
|
||||
|
||||
var/list/gene_chem = gene_value[rid]
|
||||
|
||||
if(!chems[rid])
|
||||
chems[rid] = gene_chem.Copy()
|
||||
continue
|
||||
|
||||
for(var/i=1;i<=gene_chem.len;i++)
|
||||
|
||||
if(isnull(gene_chem[i])) gene_chem[i] = 0
|
||||
|
||||
if(chems[rid][i])
|
||||
chems[rid][i] = max(1,round((gene_chem[i] + chems[rid][i])/2))
|
||||
else
|
||||
chems[rid][i] = gene_chem[i]
|
||||
|
||||
var/list/new_gasses = gene.values["[TRAIT_EXUDE_GASSES]"]
|
||||
if(islist(new_gasses))
|
||||
if(!exude_gasses) exude_gasses = list()
|
||||
exude_gasses |= new_gasses
|
||||
for(var/gas in exude_gasses)
|
||||
exude_gasses[gas] = max(1,round(exude_gasses[gas]*0.8))
|
||||
|
||||
for(var/trait in gene.values)
|
||||
set_trait(trait,gene.values["[trait]"])
|
||||
|
||||
update_growth_stages()
|
||||
|
||||
//Returns a list of the desired trait values.
|
||||
/datum/seed/proc/get_gene(var/genetype)
|
||||
|
||||
if(!genetype) return 0
|
||||
|
||||
var/list/traits_to_copy
|
||||
var/datum/plantgene/P = new()
|
||||
P.genetype = genetype
|
||||
P.values = list()
|
||||
|
||||
switch(genetype)
|
||||
if(GENE_PRODUCTS)
|
||||
P.values["[TRAIT_PRODUCTS]"] = products
|
||||
P.values["[TRAIT_CHEMS]"] = chems
|
||||
P.values["[TRAIT_EXUDE_GASSES]"] = exude_gasses
|
||||
traits_to_copy = list(TRAIT_ALTER_TEMP,TRAIT_POTENCY,TRAIT_HARVEST_REPEAT,TRAIT_PRODUCES_POWER,TRAIT_JUICY,TRAIT_PRODUCT_ICON,TRAIT_PLANT_ICON)
|
||||
if(GENE_CONSUMPTION)
|
||||
P.values["[TRAIT_CONSUME_GASSES]"] = consume_gasses
|
||||
traits_to_copy = list(TRAIT_REQUIRES_NUTRIENTS,TRAIT_NUTRIENT_CONSUMPTION,TRAIT_REQUIRES_WATER,TRAIT_WATER_CONSUMPTION,TRAIT_CARNIVOROUS,TRAIT_PARASITE,TRAIT_STINGS)
|
||||
if(GENE_ENVIRONMENT)
|
||||
traits_to_copy = list(TRAIT_IDEAL_HEAT,TRAIT_HEAT_TOLERANCE,TRAIT_IDEAL_LIGHT,TRAIT_LIGHT_TOLERANCE,TRAIT_LOWKPA_TOLERANCE,TRAIT_HIGHKPA_TOLERANCE,TRAIT_EXPLOSIVE)
|
||||
if(GENE_RESISTANCE)
|
||||
traits_to_copy = list(TRAIT_TOXINS_TOLERANCE,TRAIT_PEST_TOLERANCE,TRAIT_WEED_TOLERANCE)
|
||||
if(GENE_VIGOUR)
|
||||
traits_to_copy = list(TRAIT_ENDURANCE,TRAIT_YIELD,TRAIT_SPREAD,TRAIT_MATURATION,TRAIT_PRODUCTION,TRAIT_TELEPORTING)
|
||||
if(GENE_PIGMENT)
|
||||
traits_to_copy = list(TRAIT_PLANT_COLOUR,TRAIT_PRODUCT_COLOUR,TRAIT_BIOLUM,TRAIT_BIOLUM_COLOUR)
|
||||
|
||||
for(var/trait in traits_to_copy)
|
||||
P.values["[trait]"] = get_trait(trait)
|
||||
return (P ? P : 0)
|
||||
|
||||
//Place the plant products at the feet of the user.
|
||||
/datum/seed/proc/harvest(var/mob/user,var/yield_mod,var/harvest_sample,var/force_amount)
|
||||
|
||||
if(!user)
|
||||
return
|
||||
|
||||
var/got_product
|
||||
if(!isnull(products) && products.len && get_trait(TRAIT_YIELD) > 0)
|
||||
got_product = 1
|
||||
|
||||
if(!force_amount && !got_product && !harvest_sample)
|
||||
if(istype(user)) user << "<span class='danger'>You fail to harvest anything useful.</span>"
|
||||
else
|
||||
if(istype(user)) user << "You [harvest_sample ? "take a sample" : "harvest"] from the [display_name]."
|
||||
|
||||
//This may be a new line. Update the global if it is.
|
||||
if(name == "new line" || !(name in seed_types))
|
||||
uid = seed_types.len + 1
|
||||
name = "[uid]"
|
||||
seed_types[name] = src
|
||||
|
||||
if(harvest_sample)
|
||||
var/obj/item/seeds/seeds = new(get_turf(user))
|
||||
seeds.seed_type = name
|
||||
seeds.update_seed()
|
||||
return
|
||||
|
||||
var/total_yield = 0
|
||||
if(!isnull(force_amount))
|
||||
total_yield = force_amount
|
||||
else
|
||||
if(get_trait(TRAIT_YIELD) > -1)
|
||||
if(isnull(yield_mod) || yield_mod < 1)
|
||||
yield_mod = 0
|
||||
total_yield = get_trait(TRAIT_YIELD)
|
||||
else
|
||||
total_yield = get_trait(TRAIT_YIELD) + rand(yield_mod)
|
||||
total_yield = max(1,total_yield)
|
||||
|
||||
currently_querying = list()
|
||||
for(var/i = 0;i<total_yield;i++)
|
||||
var/product_type = pick(products)
|
||||
var/obj/item/product = new product_type(get_turf(user),name)
|
||||
|
||||
if(get_trait(TRAIT_PRODUCT_COLOUR))
|
||||
product.color = get_trait(TRAIT_PRODUCT_COLOUR)
|
||||
if(istype(product,/obj/item/weapon/reagent_containers/food))
|
||||
var/obj/item/weapon/reagent_containers/food/food = product
|
||||
food.filling_color = get_trait(TRAIT_PRODUCT_COLOUR)
|
||||
|
||||
if(mysterious)
|
||||
product.name += "?"
|
||||
product.desc += " On second thought, something about this one looks strange."
|
||||
|
||||
if(get_trait(TRAIT_BIOLUM))
|
||||
if(get_trait(TRAIT_BIOLUM_COLOUR))
|
||||
product.l_color = get_trait(TRAIT_BIOLUM_COLOUR)
|
||||
product.SetLuminosity(get_trait(TRAIT_BIOLUM))
|
||||
|
||||
//Handle spawning in living, mobile products (like dionaea).
|
||||
if(istype(product,/mob/living))
|
||||
product.visible_message("<span class='notice'>The pod disgorges [product]!</span>")
|
||||
handle_living_product(product)
|
||||
if(istype(product,/mob/living/simple_animal/mushroom)) // Gross.
|
||||
var/mob/living/simple_animal/mushroom/mush = product
|
||||
mush.seed = src
|
||||
|
||||
// When the seed in this machine mutates/is modified, the tray seed value
|
||||
// is set to a new datum copied from the original. This datum won't actually
|
||||
// be put into the global datum list until the product is harvested, though.
|
||||
/datum/seed/proc/diverge(var/modified)
|
||||
|
||||
if(get_trait(TRAIT_IMMUTABLE) > 0) return
|
||||
|
||||
//Set up some basic information.
|
||||
var/datum/seed/new_seed = new
|
||||
new_seed.name = "new line"
|
||||
new_seed.uid = 0
|
||||
new_seed.roundstart = 0
|
||||
new_seed.can_self_harvest = can_self_harvest
|
||||
|
||||
//Copy over everything else.
|
||||
if(products) new_seed.products = products.Copy()
|
||||
if(mutants) new_seed.mutants = mutants.Copy()
|
||||
if(chems) new_seed.chems = chems.Copy()
|
||||
if(consume_gasses) new_seed.consume_gasses = consume_gasses.Copy()
|
||||
if(exude_gasses) new_seed.exude_gasses = exude_gasses.Copy()
|
||||
|
||||
new_seed.seed_name = "[(roundstart ? "[(modified ? "modified" : "mutant")] " : "")][seed_name]"
|
||||
new_seed.display_name = "[(roundstart ? "[(modified ? "modified" : "mutant")] " : "")][display_name]"
|
||||
new_seed.seed_noun = seed_noun
|
||||
new_seed.traits = traits.Copy()
|
||||
new_seed.update_growth_stages()
|
||||
return new_seed
|
||||
|
||||
/datum/seed/proc/update_growth_stages()
|
||||
if(get_trait(TRAIT_PLANT_ICON))
|
||||
growth_stages = plant_sprites[get_trait(TRAIT_PLANT_ICON)]
|
||||
else
|
||||
growth_stages = 0
|
||||
@@ -1,778 +1,3 @@
|
||||
// Sprite lists.
|
||||
var/global/list/plant_sprites = list() // List of all harvested product sprites.
|
||||
var/global/list/plant_product_sprites = list() // List of all growth sprites plus number of growth stages.
|
||||
|
||||
// Proc for creating a random seed type.
|
||||
/proc/create_random_seed(var/survive_on_station)
|
||||
var/datum/seed/seed = new()
|
||||
seed.randomize()
|
||||
seed.uid = seed_types.len + 1
|
||||
seed.name = "[seed.uid]"
|
||||
seed_types[seed.name] = seed
|
||||
|
||||
if(survive_on_station)
|
||||
if(seed.consume_gasses)
|
||||
seed.consume_gasses["phoron"] = null
|
||||
seed.consume_gasses["carbon_dioxide"] = null
|
||||
seed.set_trait(TRAIT_IDEAL_HEAT,293)
|
||||
seed.set_trait(TRAIT_HEAT_TOLERANCE,20)
|
||||
seed.set_trait(TRAIT_IDEAL_LIGHT,8)
|
||||
seed.set_trait(TRAIT_LIGHT_TOLERANCE,5)
|
||||
seed.set_trait(TRAIT_LOWKPA_TOLERANCE,25)
|
||||
seed.set_trait(TRAIT_HIGHKPA_TOLERANCE,200)
|
||||
|
||||
return seed
|
||||
|
||||
// Debug for testing seed genes.
|
||||
/client/proc/show_plant_genes()
|
||||
set category = "Debug"
|
||||
set name = "Show Plant Genes"
|
||||
set desc = "Prints the round's plant gene masks."
|
||||
|
||||
if(!holder) return
|
||||
|
||||
if(!gene_tag_masks)
|
||||
usr << "Gene masks not set."
|
||||
return
|
||||
|
||||
for(var/mask in gene_tag_masks)
|
||||
usr << "[mask]: [gene_tag_masks[mask]]"
|
||||
|
||||
// Predefined/roundstart varieties use a string key to make it
|
||||
// easier to grab the new variety when mutating. Post-roundstart
|
||||
// and mutant varieties use their uid converted to a string instead.
|
||||
// Looks like shit but it's sort of necessary.
|
||||
|
||||
proc/populate_seed_list()
|
||||
|
||||
// Build the icon lists.
|
||||
for(var/icostate in icon_states('icons/obj/hydroponics_growing.dmi'))
|
||||
var/split = findtext(icostate,"-")
|
||||
if(!split)
|
||||
// invalid icon_state
|
||||
continue
|
||||
|
||||
var/ikey = copytext(icostate,(split+1))
|
||||
if(ikey == "dead")
|
||||
// don't count dead icons
|
||||
continue
|
||||
ikey = text2num(ikey)
|
||||
var/base = copytext(icostate,1,split)
|
||||
|
||||
if(!(plant_sprites[base]) || (plant_sprites[base]<ikey))
|
||||
plant_sprites[base] = ikey
|
||||
|
||||
for(var/icostate in icon_states('icons/obj/hydroponics_products.dmi'))
|
||||
plant_product_sprites |= icostate
|
||||
|
||||
// Populate the global seed datum list.
|
||||
for(var/type in typesof(/datum/seed)-/datum/seed)
|
||||
var/datum/seed/S = new type
|
||||
seed_types[S.name] = S
|
||||
S.uid = "[seed_types.len]"
|
||||
S.roundstart = 1
|
||||
|
||||
// Make sure any seed packets that were mapped in are updated
|
||||
// correctly (since the seed datums did not exist a tick ago).
|
||||
for(var/obj/item/seeds/S in world)
|
||||
S.update_seed()
|
||||
|
||||
//Might as well mask the gene types while we're at it.
|
||||
var/list/used_masks = list()
|
||||
var/list/plant_traits = ALL_GENES
|
||||
while(plant_traits && plant_traits.len)
|
||||
var/gene_tag = pick(plant_traits)
|
||||
var/gene_mask = "[num2hex(rand(0,255))]"
|
||||
|
||||
while(gene_mask in used_masks)
|
||||
gene_mask = "[num2hex(rand(0,255))]"
|
||||
|
||||
used_masks += gene_mask
|
||||
plant_traits -= gene_tag
|
||||
gene_tag_masks[gene_tag] = gene_mask
|
||||
|
||||
/datum/plantgene
|
||||
var/genetype // Label used when applying trait.
|
||||
var/list/values // Values to copy into the target seed datum.
|
||||
|
||||
/datum/seed
|
||||
|
||||
//Tracking.
|
||||
var/uid // Unique identifier.
|
||||
var/name // Index for global list.
|
||||
var/seed_name // Plant name for seed packet.
|
||||
var/seed_noun = "seeds" // Descriptor for packet.
|
||||
var/display_name // Prettier name.
|
||||
var/roundstart // If set, seed will not display variety number.
|
||||
var/mysterious // Only used for the random seed packets.
|
||||
var/can_self_harvest = 0 // Mostly used for living mobs.
|
||||
var/growth_stages = 0 // Number of stages the plant passes through before it is mature.
|
||||
var/list/traits = list() // Initialized in New()
|
||||
var/list/products // Possible fruit/other product paths.
|
||||
var/list/mutants // Possible predefined mutant varieties, if any.
|
||||
var/list/chems // Chemicals that plant produces in products/injects into victim.
|
||||
var/list/consume_gasses // The plant will absorb these gasses during its life.
|
||||
var/list/exude_gasses // The plant will exude these gasses during its life.
|
||||
var/splat_type = /obj/effect/decal/cleanable/fruit_smudge // Graffiti decal.
|
||||
|
||||
/datum/seed/New()
|
||||
|
||||
set_trait(TRAIT_IMMUTABLE, 0) // If set, plant will never mutate. If -1, plant is highly mutable.
|
||||
set_trait(TRAIT_HARVEST_REPEAT, 0) // If 1, this plant will fruit repeatedly.
|
||||
set_trait(TRAIT_PRODUCES_POWER, 0) // Can be used to make a battery.
|
||||
set_trait(TRAIT_JUICY, 0) // When thrown, causes a splatter decal.
|
||||
set_trait(TRAIT_EXPLOSIVE, 0) // When thrown, acts as a grenade.
|
||||
set_trait(TRAIT_CARNIVOROUS, 0) // 0 = none, 1 = eat pests in tray, 2 = eat living things (when a vine).
|
||||
set_trait(TRAIT_PARASITE, 0) // 0 = no, 1 = gain health from weed level.
|
||||
set_trait(TRAIT_STINGS, 0) // Can cause damage/inject reagents when thrown or handled.
|
||||
set_trait(TRAIT_YIELD, 0) // Amount of product.
|
||||
set_trait(TRAIT_SPREAD, 0) // 0 limits plant to tray, 1 = creepers, 2 = vines.
|
||||
set_trait(TRAIT_MATURATION, 0) // Time taken before the plant is mature.
|
||||
set_trait(TRAIT_PRODUCTION, 0) // Time before harvesting can be undertaken again.
|
||||
set_trait(TRAIT_TELEPORTING, 0) // Uses the bluespace tomato effect.
|
||||
set_trait(TRAIT_BIOLUM, 0) // Plant is bioluminescent.
|
||||
set_trait(TRAIT_ALTER_TEMP, 0) // If set, the plant will periodically alter local temp by this amount.
|
||||
set_trait(TRAIT_PRODUCT_ICON, 0) // Icon to use for fruit coming from this plant.
|
||||
set_trait(TRAIT_PLANT_ICON, 0) // Icon to use for the plant growing in the tray.
|
||||
set_trait(TRAIT_PRODUCT_COLOUR, 0) // Colour to apply to product icon.
|
||||
set_trait(TRAIT_BIOLUM_COLOUR, 0) // The colour of the plant's radiance.
|
||||
set_trait(TRAIT_POTENCY, 1) // General purpose plant strength value.
|
||||
set_trait(TRAIT_REQUIRES_NUTRIENTS, 1) // The plant can starve.
|
||||
set_trait(TRAIT_REQUIRES_WATER, 1) // The plant can become dehydrated.
|
||||
set_trait(TRAIT_WATER_CONSUMPTION, 3) // Plant drinks this much per tick.
|
||||
set_trait(TRAIT_LIGHT_TOLERANCE, 5) // Departure from ideal that is survivable.
|
||||
set_trait(TRAIT_TOXINS_TOLERANCE, 5) // Resistance to poison.
|
||||
set_trait(TRAIT_PEST_TOLERANCE, 5) // Threshold for pests to impact health.
|
||||
set_trait(TRAIT_WEED_TOLERANCE, 5) // Threshold for weeds to impact health.
|
||||
set_trait(TRAIT_IDEAL_LIGHT, 8) // Preferred light level in luminosity.
|
||||
set_trait(TRAIT_HEAT_TOLERANCE, 20) // Departure from ideal that is survivable.
|
||||
set_trait(TRAIT_LOWKPA_TOLERANCE, 25) // Low pressure capacity.
|
||||
set_trait(TRAIT_ENDURANCE, 100) // Maximum plant HP when growing.
|
||||
set_trait(TRAIT_HIGHKPA_TOLERANCE, 200) // High pressure capacity.
|
||||
set_trait(TRAIT_IDEAL_HEAT, 293) // Preferred temperature in Kelvin.
|
||||
set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) // Plant eats this much per tick.
|
||||
set_trait(TRAIT_PLANT_COLOUR, "#6EF86A") // Colour of the plant icon.
|
||||
|
||||
spawn(5)
|
||||
sleep(-1)
|
||||
update_growth_stages()
|
||||
|
||||
/datum/seed/proc/get_trait(var/trait)
|
||||
return traits["[trait]"]
|
||||
|
||||
/datum/seed/proc/set_trait(var/trait,var/nval,var/ubound,var/lbound, var/degrade)
|
||||
if(!isnull(degrade)) nval *= degrade
|
||||
if(!isnull(ubound)) nval = min(nval,ubound)
|
||||
if(!isnull(lbound)) nval = max(nval,lbound)
|
||||
traits["[trait]"] = nval
|
||||
|
||||
// Does brute damage to a target.
|
||||
/datum/seed/proc/do_thorns(var/mob/living/carbon/human/target, var/obj/item/fruit, var/target_limb)
|
||||
|
||||
if(!istype(target) || !get_trait(TRAIT_CARNIVOROUS))
|
||||
return
|
||||
|
||||
if(!target_limb) target_limb = pick("l_foot","r_foot","l_leg","r_leg","l_hand","r_hand","l_arm", "r_arm","head","chest","groin")
|
||||
var/datum/organ/external/affecting = target.get_organ(target_limb)
|
||||
var/damage = 0
|
||||
|
||||
if(get_trait(TRAIT_CARNIVOROUS))
|
||||
if(get_trait(TRAIT_CARNIVOROUS) == 2)
|
||||
if(affecting)
|
||||
target << "<span class='danger'>\The [fruit]'s thorns pierce your [affecting.display_name] greedily!</span>"
|
||||
else
|
||||
target << "<span class='danger'>\The [fruit]'s thorns pierce your flesh greedily!</span>"
|
||||
damage = get_trait(TRAIT_POTENCY)/2
|
||||
else
|
||||
if(affecting)
|
||||
target << "<span class='danger'>\The [fruit]'s thorns dig deeply into your [affecting.display_name]!</span>"
|
||||
else
|
||||
target << "<span class='danger'>\The [fruit]'s thorns dig deeply into your flesh!</span>"
|
||||
damage = get_trait(TRAIT_POTENCY)/5
|
||||
else
|
||||
return
|
||||
|
||||
if(affecting)
|
||||
affecting.take_damage(damage, 0)
|
||||
affecting.add_autopsy_data("Thorns",damage)
|
||||
else
|
||||
target.adjustBruteLoss(damage)
|
||||
target.UpdateDamageIcon()
|
||||
target.updatehealth()
|
||||
|
||||
// Adds reagents to a target.
|
||||
/datum/seed/proc/do_sting(var/mob/living/carbon/human/target, var/obj/item/fruit)
|
||||
if(!get_trait(TRAIT_STINGS))
|
||||
return
|
||||
if(chems && chems.len)
|
||||
target << "<span class='danger'>You are stung by \the [fruit]!</span>"
|
||||
for(var/rid in chems)
|
||||
var/injecting = min(5,max(1,get_trait(TRAIT_POTENCY)/5))
|
||||
target.reagents.add_reagent(rid,injecting)
|
||||
|
||||
//Splatter a turf.
|
||||
/datum/seed/proc/splatter(var/turf/T,var/obj/item/thrown)
|
||||
if(splat_type)
|
||||
var/obj/effect/plant/splat = new splat_type(T, src)
|
||||
if(!istype(splat)) // Plants handle their own stuff.
|
||||
splat.name = "[thrown.name] [pick("smear","smudge","splatter")]"
|
||||
if(get_trait(TRAIT_BIOLUM))
|
||||
if(get_trait(TRAIT_BIOLUM_COLOUR))
|
||||
splat.l_color = get_trait(TRAIT_BIOLUM_COLOUR)
|
||||
splat.SetLuminosity(get_trait(TRAIT_BIOLUM))
|
||||
if(get_trait(TRAIT_PRODUCT_COLOUR))
|
||||
splat.color = get_trait(TRAIT_PRODUCT_COLOUR)
|
||||
|
||||
if(chems)
|
||||
for(var/mob/living/M in T.contents)
|
||||
if(!M.reagents)
|
||||
continue
|
||||
for(var/chem in chems)
|
||||
var/injecting = min(5,max(1,get_trait(TRAIT_POTENCY)/3))
|
||||
M.reagents.add_reagent(chem,injecting)
|
||||
|
||||
//Applies an effect to a target atom.
|
||||
/datum/seed/proc/thrown_at(var/obj/item/thrown,var/atom/target, var/force_explode)
|
||||
|
||||
var/splatted
|
||||
var/turf/origin_turf = get_turf(target)
|
||||
|
||||
if(force_explode || get_trait(TRAIT_EXPLOSIVE))
|
||||
|
||||
var/flood_dist = min(10,max(1,get_trait(TRAIT_POTENCY)/15))
|
||||
var/list/open_turfs = list()
|
||||
var/list/closed_turfs = list()
|
||||
var/list/valid_turfs = list()
|
||||
open_turfs |= origin_turf
|
||||
|
||||
// Flood fill to get affected turfs.
|
||||
while(open_turfs.len)
|
||||
var/turf/T = pick(open_turfs)
|
||||
open_turfs -= T
|
||||
closed_turfs |= T
|
||||
valid_turfs |= T
|
||||
|
||||
for(var/dir in alldirs)
|
||||
var/turf/neighbor = get_step(T,dir)
|
||||
if(!neighbor || (neighbor in closed_turfs) || (neighbor in open_turfs))
|
||||
continue
|
||||
if(neighbor.density || get_dist(neighbor,origin_turf) > flood_dist || istype(neighbor,/turf/space))
|
||||
closed_turfs |= neighbor
|
||||
continue
|
||||
// Check for windows.
|
||||
var/no_los
|
||||
var/turf/last_turf = origin_turf
|
||||
for(var/turf/target_turf in getline(origin_turf,neighbor))
|
||||
if(!last_turf.Enter(target_turf) || target_turf.density)
|
||||
no_los = 1
|
||||
break
|
||||
last_turf = target_turf
|
||||
if(!no_los && !origin_turf.Enter(neighbor))
|
||||
no_los = 1
|
||||
if(no_los)
|
||||
closed_turfs |= neighbor
|
||||
continue
|
||||
open_turfs |= neighbor
|
||||
|
||||
for(var/turf/T in valid_turfs)
|
||||
for(var/mob/living/M in T.contents)
|
||||
apply_special_effect(M)
|
||||
splatter(T,thrown)
|
||||
origin_turf.visible_message("<span class='danger'>The [thrown.name] explodes!</span>")
|
||||
del(thrown)
|
||||
return
|
||||
|
||||
if(istype(target,/mob/living))
|
||||
splatted = apply_special_effect(target,thrown)
|
||||
else if(istype(target,/turf))
|
||||
splatted = 1
|
||||
for(var/mob/living/M in target.contents)
|
||||
apply_special_effect(M)
|
||||
|
||||
if(get_trait(TRAIT_JUICY) && splatted)
|
||||
splatter(origin_turf,thrown)
|
||||
origin_turf.visible_message("<span class='danger'>The [thrown.name] splatters against [target]!</span>")
|
||||
del(thrown)
|
||||
|
||||
/datum/seed/proc/handle_environment(var/turf/current_turf, var/datum/gas_mixture/environment, var/check_only)
|
||||
|
||||
var/health_change = 0
|
||||
// Handle gas consumption.
|
||||
if(consume_gasses && consume_gasses.len)
|
||||
var/missing_gas = 0
|
||||
for(var/gas in consume_gasses)
|
||||
if(environment && environment.gas && environment.gas[gas] && \
|
||||
environment.gas[gas] >= consume_gasses[gas])
|
||||
if(!check_only)
|
||||
environment.adjust_gas(gas,-consume_gasses[gas],1)
|
||||
else
|
||||
missing_gas++
|
||||
|
||||
if(missing_gas > 0)
|
||||
health_change += missing_gas * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// Process it.
|
||||
var/pressure = environment.return_pressure()
|
||||
if(pressure < get_trait(TRAIT_LOWKPA_TOLERANCE)|| pressure > get_trait(TRAIT_HIGHKPA_TOLERANCE))
|
||||
health_change += rand(1,3) * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
if(abs(environment.temperature - get_trait(TRAIT_IDEAL_HEAT)) > get_trait(TRAIT_HEAT_TOLERANCE))
|
||||
health_change += rand(1,3) * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// Handle gas production.
|
||||
if(exude_gasses && exude_gasses.len && !check_only)
|
||||
for(var/gas in exude_gasses)
|
||||
environment.adjust_gas(gas, max(1,round((exude_gasses[gas]*get_trait(TRAIT_POTENCY))/exude_gasses.len)))
|
||||
|
||||
// Handle light requirements.
|
||||
var/area/A = get_area(current_turf)
|
||||
if(A)
|
||||
var/light_available
|
||||
if(A.lighting_use_dynamic)
|
||||
light_available = max(0,min(10,current_turf.lighting_lumcount)-5)
|
||||
else
|
||||
light_available = 5
|
||||
if(abs(light_available - get_trait(TRAIT_IDEAL_LIGHT)) > get_trait(TRAIT_LIGHT_TOLERANCE))
|
||||
health_change += rand(1,3) * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
return health_change
|
||||
|
||||
/datum/seed/proc/apply_special_effect(var/mob/living/target,var/obj/item/thrown)
|
||||
|
||||
var/impact = 1
|
||||
do_sting(target,thrown)
|
||||
do_thorns(target,thrown)
|
||||
|
||||
// Bluespace tomato code copied over from grown.dm.
|
||||
if(get_trait(TRAIT_TELEPORTING))
|
||||
|
||||
//Plant potency determines radius of teleport.
|
||||
var/outer_teleport_radius = get_trait(TRAIT_POTENCY)/5
|
||||
var/inner_teleport_radius = get_trait(TRAIT_POTENCY)/15
|
||||
|
||||
var/list/turfs = list()
|
||||
if(inner_teleport_radius > 0)
|
||||
for(var/turf/T in orange(target,outer_teleport_radius))
|
||||
if(get_dist(target,T) >= inner_teleport_radius)
|
||||
turfs |= T
|
||||
|
||||
if(turfs.len)
|
||||
// Moves the mob, causes sparks.
|
||||
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
||||
s.set_up(3, 1, get_turf(target))
|
||||
s.start()
|
||||
var/turf/picked = get_turf(pick(turfs)) // Just in case...
|
||||
new/obj/effect/decal/cleanable/molten_item(get_turf(target)) // Leave a pile of goo behind for dramatic effect...
|
||||
target.loc = picked // And teleport them to the chosen location.
|
||||
|
||||
impact = 1
|
||||
|
||||
return impact
|
||||
|
||||
//Creates a random seed. MAKE SURE THE LINE HAS DIVERGED BEFORE THIS IS CALLED.
|
||||
/datum/seed/proc/randomize()
|
||||
|
||||
roundstart = 0
|
||||
seed_name = "strange plant" // TODO: name generator.
|
||||
display_name = "strange plants" // TODO: name generator.
|
||||
mysterious = 1
|
||||
seed_noun = pick("spores","nodes","cuttings","seeds")
|
||||
products = list(pick(typesof(/obj/item/weapon/reagent_containers/food/snacks/grown)-/obj/item/weapon/reagent_containers/food/snacks/grown))
|
||||
|
||||
set_trait(TRAIT_POTENCY,rand(5,30),200,0)
|
||||
set_trait(TRAIT_PRODUCT_ICON,pick(plant_product_sprites))
|
||||
set_trait(TRAIT_PLANT_ICON,pick(plant_sprites))
|
||||
set_trait(TRAIT_PLANT_COLOUR,"#[get_random_colour(0,75,190)]")
|
||||
set_trait(TRAIT_PRODUCT_COLOUR,"#[get_random_colour(0,75,190)]")
|
||||
update_growth_stages()
|
||||
|
||||
if(prob(20))
|
||||
set_trait(TRAIT_HARVEST_REPEAT,1)
|
||||
|
||||
if(prob(15))
|
||||
if(prob(15))
|
||||
set_trait(TRAIT_JUICY,2)
|
||||
else
|
||||
set_trait(TRAIT_JUICY,1)
|
||||
|
||||
if(prob(5))
|
||||
set_trait(TRAIT_STINGS,1)
|
||||
|
||||
if(prob(5))
|
||||
set_trait(TRAIT_PRODUCES_POWER,1)
|
||||
|
||||
if(prob(1))
|
||||
set_trait(TRAIT_EXPLOSIVE,1)
|
||||
else if(prob(1))
|
||||
set_trait(TRAIT_TELEPORTING,1)
|
||||
|
||||
if(prob(5))
|
||||
consume_gasses = list()
|
||||
var/gas = pick("oxygen","nitrogen","phoron","carbon_dioxide")
|
||||
consume_gasses[gas] = rand(3,9)
|
||||
|
||||
if(prob(5))
|
||||
exude_gasses = list()
|
||||
var/gas = pick("oxygen","nitrogen","phoron","carbon_dioxide")
|
||||
exude_gasses[gas] = rand(3,9)
|
||||
|
||||
chems = list()
|
||||
if(prob(80))
|
||||
chems["nutriment"] = list(rand(1,10),rand(10,20))
|
||||
|
||||
var/additional_chems = rand(0,5)
|
||||
|
||||
if(additional_chems)
|
||||
var/list/possible_chems = list(
|
||||
"bicaridine",
|
||||
"hyperzine",
|
||||
"cryoxadone",
|
||||
"blood",
|
||||
"water",
|
||||
"potassium",
|
||||
"plasticide",
|
||||
"mutationtoxin",
|
||||
"amutationtoxin",
|
||||
"inaprovaline",
|
||||
"space_drugs",
|
||||
"paroxetine",
|
||||
"mercury",
|
||||
"sugar",
|
||||
"radium",
|
||||
"ryetalyn",
|
||||
"alkysine",
|
||||
"thermite",
|
||||
"tramadol",
|
||||
"cryptobiolin",
|
||||
"dermaline",
|
||||
"dexalin",
|
||||
"phoron",
|
||||
"synaptizine",
|
||||
"impedrezene",
|
||||
"hyronalin",
|
||||
"peridaxon",
|
||||
"toxin",
|
||||
"rezadone",
|
||||
"ethylredoxrazine",
|
||||
"slimejelly",
|
||||
"cyanide",
|
||||
"mindbreaker",
|
||||
"stoxin"
|
||||
)
|
||||
|
||||
for(var/x=1;x<=additional_chems;x++)
|
||||
if(!possible_chems.len)
|
||||
break
|
||||
var/new_chem = pick(possible_chems)
|
||||
possible_chems -= new_chem
|
||||
chems[new_chem] = list(rand(1,10),rand(10,20))
|
||||
|
||||
if(prob(90))
|
||||
set_trait(TRAIT_REQUIRES_NUTRIENTS,1)
|
||||
set_trait(TRAIT_NUTRIENT_CONSUMPTION,rand(100)*0.1)
|
||||
else
|
||||
set_trait(TRAIT_REQUIRES_NUTRIENTS,0)
|
||||
|
||||
if(prob(90))
|
||||
set_trait(TRAIT_REQUIRES_WATER,1)
|
||||
set_trait(TRAIT_WATER_CONSUMPTION,rand(10))
|
||||
else
|
||||
set_trait(TRAIT_REQUIRES_WATER,0)
|
||||
|
||||
set_trait(TRAIT_IDEAL_HEAT, rand(100,400))
|
||||
set_trait(TRAIT_HEAT_TOLERANCE, rand(10,30))
|
||||
set_trait(TRAIT_IDEAL_LIGHT, rand(2,10))
|
||||
set_trait(TRAIT_LIGHT_TOLERANCE, rand(2,7))
|
||||
set_trait(TRAIT_TOXINS_TOLERANCE, rand(2,7))
|
||||
set_trait(TRAIT_PEST_TOLERANCE, rand(2,7))
|
||||
set_trait(TRAIT_WEED_TOLERANCE, rand(2,7))
|
||||
set_trait(TRAIT_LOWKPA_TOLERANCE, rand(10,50))
|
||||
set_trait(TRAIT_HIGHKPA_TOLERANCE,rand(100,300))
|
||||
|
||||
if(prob(5))
|
||||
set_trait(TRAIT_ALTER_TEMP,rand(-5,5))
|
||||
|
||||
if(prob(1))
|
||||
set_trait(TRAIT_IMMUTABLE,-1)
|
||||
|
||||
var/carnivore_prob = rand(100)
|
||||
if(carnivore_prob < 5)
|
||||
set_trait(TRAIT_CARNIVOROUS,2)
|
||||
else if(carnivore_prob < 10)
|
||||
set_trait(TRAIT_CARNIVOROUS,1)
|
||||
|
||||
if(prob(10))
|
||||
set_trait(TRAIT_PARASITE,1)
|
||||
|
||||
var/vine_prob = rand(100)
|
||||
if(vine_prob < 5)
|
||||
set_trait(TRAIT_SPREAD,2)
|
||||
else if(vine_prob < 10)
|
||||
set_trait(TRAIT_SPREAD,1)
|
||||
|
||||
if(prob(5))
|
||||
set_trait(TRAIT_BIOLUM,1)
|
||||
set_trait(TRAIT_BIOLUM_COLOUR,"#[get_random_colour(0,75,190)]")
|
||||
|
||||
set_trait(TRAIT_ENDURANCE,rand(60,100))
|
||||
set_trait(TRAIT_YIELD,rand(3,15))
|
||||
set_trait(TRAIT_MATURATION,rand(5,15))
|
||||
set_trait(TRAIT_PRODUCTION,get_trait(TRAIT_MATURATION)+rand(2,5))
|
||||
|
||||
//Returns a key corresponding to an entry in the global seed list.
|
||||
/datum/seed/proc/get_mutant_variant()
|
||||
if(!mutants || !mutants.len || get_trait(TRAIT_IMMUTABLE) > 0) return 0
|
||||
return pick(mutants)
|
||||
|
||||
//Mutates the plant overall (randomly).
|
||||
/datum/seed/proc/mutate(var/degree,var/turf/source_turf)
|
||||
|
||||
if(!degree || get_trait(TRAIT_IMMUTABLE) > 0) return
|
||||
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name] quivers!</span>")
|
||||
|
||||
//This looks like shit, but it's a lot easier to read/change this way.
|
||||
var/total_mutations = rand(1,1+degree)
|
||||
for(var/i = 0;i<total_mutations;i++)
|
||||
switch(rand(0,11))
|
||||
if(0) //Plant cancer!
|
||||
set_trait(TRAIT_ENDURANCE,get_trait(TRAIT_ENDURANCE)-rand(10,20),null,0)
|
||||
source_turf.visible_message("<span class='danger'>\The [display_name] withers rapidly!</span>")
|
||||
if(1)
|
||||
set_trait(TRAIT_NUTRIENT_CONSUMPTION,get_trait(TRAIT_NUTRIENT_CONSUMPTION)+rand(-(degree*0.1),(degree*0.1)),5,0)
|
||||
set_trait(TRAIT_WATER_CONSUMPTION, get_trait(TRAIT_WATER_CONSUMPTION) +rand(-degree,degree),50,0)
|
||||
set_trait(TRAIT_JUICY, !get_trait(TRAIT_JUICY))
|
||||
set_trait(TRAIT_STINGS, !get_trait(TRAIT_STINGS))
|
||||
if(2)
|
||||
set_trait(TRAIT_IDEAL_HEAT, get_trait(TRAIT_IDEAL_HEAT) + (rand(-5,5)*degree),800,70)
|
||||
set_trait(TRAIT_HEAT_TOLERANCE, get_trait(TRAIT_HEAT_TOLERANCE) + (rand(-5,5)*degree),800,70)
|
||||
set_trait(TRAIT_LOWKPA_TOLERANCE, get_trait(TRAIT_LOWKPA_TOLERANCE)+ (rand(-5,5)*degree),80,0)
|
||||
set_trait(TRAIT_HIGHKPA_TOLERANCE, get_trait(TRAIT_HIGHKPA_TOLERANCE)+(rand(-5,5)*degree),500,110)
|
||||
set_trait(TRAIT_EXPLOSIVE,1)
|
||||
if(3)
|
||||
set_trait(TRAIT_IDEAL_LIGHT, get_trait(TRAIT_IDEAL_LIGHT)+(rand(-1,1)*degree),30,0)
|
||||
set_trait(TRAIT_LIGHT_TOLERANCE, get_trait(TRAIT_LIGHT_TOLERANCE)+(rand(-2,2)*degree),10,0)
|
||||
if(4)
|
||||
set_trait(TRAIT_TOXINS_TOLERANCE, get_trait(TRAIT_TOXINS_TOLERANCE)+(rand(-2,2)*degree),10,0)
|
||||
if(5)
|
||||
set_trait(TRAIT_WEED_TOLERANCE, get_trait(TRAIT_WEED_TOLERANCE)+(rand(-2,2)*degree),10, 0)
|
||||
if(prob(degree*5))
|
||||
set_trait(TRAIT_CARNIVOROUS, get_trait(TRAIT_CARNIVOROUS)+rand(-degree,degree),2, 0)
|
||||
if(get_trait(TRAIT_CARNIVOROUS))
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name] shudders hungrily.</span>")
|
||||
if(6)
|
||||
set_trait(TRAIT_WEED_TOLERANCE, get_trait(TRAIT_WEED_TOLERANCE)+(rand(-2,2)*degree),10, 0)
|
||||
if(prob(degree*5))
|
||||
set_trait(TRAIT_PARASITE,!get_trait(TRAIT_PARASITE))
|
||||
if(7)
|
||||
if(get_trait(TRAIT_YIELD) != -1)
|
||||
set_trait(TRAIT_YIELD, get_trait(TRAIT_YIELD)+(rand(-2,2)*degree),10,0)
|
||||
if(8)
|
||||
set_trait(TRAIT_ENDURANCE, get_trait(TRAIT_ENDURANCE)+(rand(-5,5)*degree),100,10)
|
||||
set_trait(TRAIT_PRODUCTION, get_trait(TRAIT_PRODUCTION)+(rand(-1,1)*degree),10, 1)
|
||||
set_trait(TRAIT_POTENCY, get_trait(TRAIT_POTENCY)+(rand(-20,20)*degree),200, 0)
|
||||
if(prob(degree*5))
|
||||
set_trait(TRAIT_SPREAD, get_trait(TRAIT_SPREAD)+rand(-1,1),2, 0)
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name] spasms visibly, shifting in the tray.</span>")
|
||||
if(9)
|
||||
set_trait(TRAIT_MATURATION, get_trait(TRAIT_MATURATION)+(rand(-1,1)*degree),30, 0)
|
||||
if(prob(degree*5))
|
||||
set_trait(TRAIT_HARVEST_REPEAT, !get_trait(TRAIT_HARVEST_REPEAT))
|
||||
if(10)
|
||||
if(prob(degree*2))
|
||||
set_trait(TRAIT_BIOLUM, !get_trait(TRAIT_BIOLUM))
|
||||
if(get_trait(TRAIT_BIOLUM))
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name] begins to glow!</span>")
|
||||
if(prob(degree*2))
|
||||
set_trait(TRAIT_BIOLUM_COLOUR,"#[get_random_colour(0,75,190)]")
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name]'s glow </span><font color='[get_trait(TRAIT_BIOLUM_COLOUR)]'>changes colour</font>!")
|
||||
else
|
||||
source_turf.visible_message("<span class='notice'>\The [display_name]'s glow dims...</span>")
|
||||
if(11)
|
||||
set_trait(TRAIT_TELEPORTING,1)
|
||||
|
||||
return
|
||||
|
||||
//Mutates a specific trait/set of traits.
|
||||
/datum/seed/proc/apply_gene(var/datum/plantgene/gene)
|
||||
|
||||
if(!gene || !gene.values || get_trait(TRAIT_IMMUTABLE) > 0) return
|
||||
|
||||
// Splicing products has some detrimental effects on yield and lifespan.
|
||||
// We handle this before we do the rest of the looping, as normal traits don't really include lists.
|
||||
if(gene.genetype == GENE_PRODUCTS)
|
||||
for(var/trait in list(TRAIT_YIELD, TRAIT_ENDURANCE))
|
||||
if(get_trait(trait) > 0) set_trait(trait,get_trait(trait),null,1,0.85)
|
||||
|
||||
if(!products) products = list()
|
||||
products |= gene.values["[TRAIT_PRODUCTS]"]
|
||||
|
||||
if(!chems) chems = list()
|
||||
|
||||
var/list/gene_value = gene.values["[TRAIT_CHEMS]"]
|
||||
for(var/rid in gene_value)
|
||||
|
||||
var/list/gene_chem = gene_value[rid]
|
||||
|
||||
if(!chems[rid])
|
||||
chems[rid] = gene_chem.Copy()
|
||||
continue
|
||||
|
||||
for(var/i=1;i<=gene_chem.len;i++)
|
||||
|
||||
if(isnull(gene_chem[i])) gene_chem[i] = 0
|
||||
|
||||
if(chems[rid][i])
|
||||
chems[rid][i] = max(1,round((gene_chem[i] + chems[rid][i])/2))
|
||||
else
|
||||
chems[rid][i] = gene_chem[i]
|
||||
|
||||
var/list/new_gasses = gene.values["[TRAIT_EXUDE_GASSES]"]
|
||||
if(islist(new_gasses))
|
||||
if(!exude_gasses) exude_gasses = list()
|
||||
exude_gasses |= new_gasses
|
||||
for(var/gas in exude_gasses)
|
||||
exude_gasses[gas] = max(1,round(exude_gasses[gas]*0.8))
|
||||
|
||||
for(var/trait in gene.values)
|
||||
set_trait(trait,gene.values["[trait]"])
|
||||
|
||||
update_growth_stages()
|
||||
|
||||
//Returns a list of the desired trait values.
|
||||
/datum/seed/proc/get_gene(var/genetype)
|
||||
|
||||
if(!genetype) return 0
|
||||
|
||||
var/list/traits_to_copy
|
||||
var/datum/plantgene/P = new()
|
||||
P.genetype = genetype
|
||||
P.values = list()
|
||||
|
||||
switch(genetype)
|
||||
if(GENE_PRODUCTS)
|
||||
P.values["[TRAIT_PRODUCTS]"] = products
|
||||
P.values["[TRAIT_CHEMS]"] = chems
|
||||
P.values["[TRAIT_EXUDE_GASSES]"] = exude_gasses
|
||||
traits_to_copy = list(TRAIT_ALTER_TEMP,TRAIT_POTENCY,TRAIT_HARVEST_REPEAT,TRAIT_PRODUCES_POWER,TRAIT_JUICY,TRAIT_PRODUCT_ICON,TRAIT_PLANT_ICON)
|
||||
if(GENE_CONSUMPTION)
|
||||
P.values["[TRAIT_CONSUME_GASSES]"] = consume_gasses
|
||||
traits_to_copy = list(TRAIT_REQUIRES_NUTRIENTS,TRAIT_NUTRIENT_CONSUMPTION,TRAIT_REQUIRES_WATER,TRAIT_WATER_CONSUMPTION,TRAIT_CARNIVOROUS,TRAIT_PARASITE,TRAIT_STINGS)
|
||||
if(GENE_ENVIRONMENT)
|
||||
traits_to_copy = list(TRAIT_IDEAL_HEAT,TRAIT_HEAT_TOLERANCE,TRAIT_IDEAL_LIGHT,TRAIT_LIGHT_TOLERANCE,TRAIT_LOWKPA_TOLERANCE,TRAIT_HIGHKPA_TOLERANCE,TRAIT_EXPLOSIVE)
|
||||
if(GENE_RESISTANCE)
|
||||
traits_to_copy = list(TRAIT_TOXINS_TOLERANCE,TRAIT_PEST_TOLERANCE,TRAIT_WEED_TOLERANCE)
|
||||
if(GENE_VIGOUR)
|
||||
traits_to_copy = list(TRAIT_ENDURANCE,TRAIT_YIELD,TRAIT_SPREAD,TRAIT_MATURATION,TRAIT_PRODUCTION,TRAIT_TELEPORTING)
|
||||
if(GENE_PIGMENT)
|
||||
traits_to_copy = list(TRAIT_PLANT_COLOUR,TRAIT_PRODUCT_COLOUR,TRAIT_BIOLUM,TRAIT_BIOLUM_COLOUR)
|
||||
|
||||
for(var/trait in traits_to_copy)
|
||||
P.values["[trait]"] = get_trait(trait)
|
||||
return (P ? P : 0)
|
||||
|
||||
//Place the plant products at the feet of the user.
|
||||
/datum/seed/proc/harvest(var/mob/user,var/yield_mod,var/harvest_sample,var/force_amount)
|
||||
|
||||
if(!user)
|
||||
return
|
||||
|
||||
var/got_product
|
||||
if(!isnull(products) && products.len && get_trait(TRAIT_YIELD) > 0)
|
||||
got_product = 1
|
||||
|
||||
if(!force_amount && !got_product && !harvest_sample)
|
||||
if(istype(user)) user << "<span class='danger'>You fail to harvest anything useful.</span>"
|
||||
else
|
||||
if(istype(user)) user << "You [harvest_sample ? "take a sample" : "harvest"] from the [display_name]."
|
||||
|
||||
//This may be a new line. Update the global if it is.
|
||||
if(name == "new line" || !(name in seed_types))
|
||||
uid = seed_types.len + 1
|
||||
name = "[uid]"
|
||||
seed_types[name] = src
|
||||
|
||||
if(harvest_sample)
|
||||
var/obj/item/seeds/seeds = new(get_turf(user))
|
||||
seeds.seed_type = name
|
||||
seeds.update_seed()
|
||||
return
|
||||
|
||||
var/total_yield = 0
|
||||
if(!isnull(force_amount))
|
||||
total_yield = force_amount
|
||||
else
|
||||
if(get_trait(TRAIT_YIELD) > -1)
|
||||
if(isnull(yield_mod) || yield_mod < 1)
|
||||
yield_mod = 0
|
||||
total_yield = get_trait(TRAIT_YIELD)
|
||||
else
|
||||
total_yield = get_trait(TRAIT_YIELD) + rand(yield_mod)
|
||||
total_yield = max(1,total_yield)
|
||||
|
||||
currently_querying = list()
|
||||
for(var/i = 0;i<total_yield;i++)
|
||||
var/product_type = pick(products)
|
||||
var/obj/item/product = new product_type(get_turf(user),name)
|
||||
|
||||
if(get_trait(TRAIT_PRODUCT_COLOUR))
|
||||
product.color = get_trait(TRAIT_PRODUCT_COLOUR)
|
||||
if(istype(product,/obj/item/weapon/reagent_containers/food))
|
||||
var/obj/item/weapon/reagent_containers/food/food = product
|
||||
food.filling_color = get_trait(TRAIT_PRODUCT_COLOUR)
|
||||
|
||||
if(mysterious)
|
||||
product.name += "?"
|
||||
product.desc += " On second thought, something about this one looks strange."
|
||||
|
||||
if(get_trait(TRAIT_BIOLUM))
|
||||
if(get_trait(TRAIT_BIOLUM_COLOUR))
|
||||
product.l_color = get_trait(TRAIT_BIOLUM_COLOUR)
|
||||
product.SetLuminosity(get_trait(TRAIT_BIOLUM))
|
||||
|
||||
//Handle spawning in living, mobile products (like dionaea).
|
||||
if(istype(product,/mob/living))
|
||||
product.visible_message("<span class='notice'>The pod disgorges [product]!</span>")
|
||||
handle_living_product(product)
|
||||
if(istype(product,/mob/living/simple_animal/mushroom)) // Gross.
|
||||
var/mob/living/simple_animal/mushroom/mush = product
|
||||
mush.seed = src
|
||||
|
||||
// When the seed in this machine mutates/is modified, the tray seed value
|
||||
// is set to a new datum copied from the original. This datum won't actually
|
||||
// be put into the global datum list until the product is harvested, though.
|
||||
/datum/seed/proc/diverge(var/modified)
|
||||
|
||||
if(get_trait(TRAIT_IMMUTABLE) > 0) return
|
||||
|
||||
//Set up some basic information.
|
||||
var/datum/seed/new_seed = new
|
||||
new_seed.name = "new line"
|
||||
new_seed.uid = 0
|
||||
new_seed.roundstart = 0
|
||||
new_seed.can_self_harvest = can_self_harvest
|
||||
|
||||
//Copy over everything else.
|
||||
if(products) new_seed.products = products.Copy()
|
||||
if(mutants) new_seed.mutants = mutants.Copy()
|
||||
if(chems) new_seed.chems = chems.Copy()
|
||||
if(consume_gasses) new_seed.consume_gasses = consume_gasses.Copy()
|
||||
if(exude_gasses) new_seed.exude_gasses = exude_gasses.Copy()
|
||||
|
||||
new_seed.seed_name = "[(roundstart ? "[(modified ? "modified" : "mutant")] " : "")][seed_name]"
|
||||
new_seed.display_name = "[(roundstart ? "[(modified ? "modified" : "mutant")] " : "")][display_name]"
|
||||
new_seed.seed_noun = seed_noun
|
||||
new_seed.traits = traits.Copy()
|
||||
new_seed.update_growth_stages()
|
||||
return new_seed
|
||||
|
||||
/datum/seed/proc/update_growth_stages()
|
||||
if(get_trait(TRAIT_PLANT_ICON))
|
||||
growth_stages = plant_sprites[get_trait(TRAIT_PLANT_ICON)]
|
||||
else
|
||||
growth_stages = 0
|
||||
|
||||
// Actual roundstart seed types after this point.
|
||||
// Chili plants/variants.
|
||||
/datum/seed/chili
|
||||
name = "chili"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#define DEFAULT_SEED "glowshroom"
|
||||
#define VINE_GROWTH_STAGES 4
|
||||
#define VINE_GROWTH_STAGES 5
|
||||
|
||||
/proc/spacevine_infestation()
|
||||
spawn() //to stop the secrets panel hanging
|
||||
@@ -23,6 +23,7 @@
|
||||
anchored = 1
|
||||
opacity = 0
|
||||
density = 0
|
||||
color = DEAD_PLANT_COLOUR
|
||||
|
||||
/obj/effect/dead_plant/attackby()
|
||||
..()
|
||||
@@ -37,7 +38,7 @@
|
||||
density = 0
|
||||
icon = 'icons/obj/hydroponics_growing.dmi'
|
||||
icon_state = "bush4-1"
|
||||
layer = 2
|
||||
layer = 3
|
||||
|
||||
var/health = 10
|
||||
var/max_health = 100
|
||||
@@ -47,6 +48,7 @@
|
||||
|
||||
var/list/children = list()
|
||||
var/obj/effect/plant/parent
|
||||
var/mob/living/buckled_mob
|
||||
var/datum/seed/seed
|
||||
var/floor = 0
|
||||
var/spread_chance = 40
|
||||
@@ -68,12 +70,11 @@
|
||||
del(src)
|
||||
return
|
||||
|
||||
layer = rand(3,4) // Will display over pipes/machines at all times and mobs half the time.
|
||||
name = seed.display_name
|
||||
max_health = round(seed.get_trait(TRAIT_ENDURANCE)/2)
|
||||
if(seed.get_trait(TRAIT_SPREAD)==2)
|
||||
max_growth = VINE_GROWTH_STAGES
|
||||
growth_threshold = round(max_health/VINE_GROWTH_STAGES)
|
||||
growth_threshold = max_health/VINE_GROWTH_STAGES
|
||||
icon = 'icons/obj/hydroponics_vines.dmi'
|
||||
growth_type = 2 // Vines by default.
|
||||
if(seed.get_trait(TRAIT_CARNIVOROUS) == 2)
|
||||
@@ -85,7 +86,7 @@
|
||||
growth_type = 4 // Mold
|
||||
else
|
||||
max_growth = seed.growth_stages
|
||||
growth_threshold = round(max_health/seed.growth_stages)
|
||||
growth_threshold = max_health/seed.growth_stages
|
||||
|
||||
if(max_growth > 2 && prob(50))
|
||||
max_growth-- //Ensure some variation in final sprite, makes the carpet of crap look less wonky.
|
||||
@@ -143,94 +144,20 @@
|
||||
else
|
||||
icon_state = "[seed.get_trait(TRAIT_PLANT_ICON)]-[growth]"
|
||||
|
||||
layer = (growth == max_growth ? 4 : 3)
|
||||
|
||||
/obj/effect/plant/Del()
|
||||
if(children && children.len)
|
||||
die_off(null,1)
|
||||
processing_objects -= src
|
||||
..()
|
||||
|
||||
/obj/effect/plant/proc/die_off(var/no_remains)
|
||||
// Remove ourselves from our parent.
|
||||
if(parent && parent.children)
|
||||
parent.children -= src
|
||||
// Kill off any of our children (and add an added bonus, other plants in this area)
|
||||
for(var/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/plant in get_turf(src))
|
||||
plant.dead = 1
|
||||
plant.update_icon()
|
||||
// Cause the plants around us to update.
|
||||
if(children && children.len)
|
||||
for(var/obj/effect/plant/child in children)
|
||||
child.die_off()
|
||||
for(var/obj/effect/plant/neighbor in view(1,src))
|
||||
neighbor.hibernating = 0
|
||||
if(!no_remains && !(locate(/obj/effect/dead_plant) in get_turf(src)))
|
||||
var/obj/effect/dead_plant/plant_remains = new(get_turf(src))
|
||||
plant_remains.icon = src.icon
|
||||
plant_remains.icon_state = src.icon_state
|
||||
del(src)
|
||||
|
||||
/obj/effect/plant/proc/get_dist_to_parent(var/current_count)
|
||||
if(!parent)
|
||||
return current_count
|
||||
current_count++
|
||||
return parent.get_dist_to_parent(current_count)
|
||||
|
||||
/obj/effect/plant/process()
|
||||
|
||||
// Something is very wrong, kill ourselves.
|
||||
if(!seed)
|
||||
die_off()
|
||||
|
||||
// Handle life.
|
||||
var/turf/simulated/T = get_turf(src)
|
||||
if(istype(T))
|
||||
health -= seed.handle_environment(T, T.return_air(),1)
|
||||
if(health < max_health)
|
||||
health += rand(3,5)
|
||||
if(health > max_health)
|
||||
health = max_health
|
||||
refresh_icon()
|
||||
|
||||
// Damaged, young hibernating or too far from parent, no chance of spreading.
|
||||
if(health < (max_health/2) || hibernating || (parent && (get_dist_to_parent(0) > spread_distance)))
|
||||
return
|
||||
|
||||
// Count our neighbors and possible locations for spreading.
|
||||
var/list/possible_locs = list()
|
||||
var/count = 0
|
||||
for(var/turf/simulated/floor/floor in view(1,src))
|
||||
if((locate(/obj/effect/dead_plant) in floor.contents) || !floor.Enter(src) || floor.density)
|
||||
continue
|
||||
if(locate(/obj/effect/plant) in floor.contents)
|
||||
count++
|
||||
continue
|
||||
possible_locs |= floor
|
||||
|
||||
//Entirely surrounded, try to spawn an actual plant.
|
||||
if(count>=8 && prob(5))
|
||||
if(!(locate(/obj/machinery/portable_atmospherics/hydroponics/soil/invisible) in T.contents))
|
||||
var/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/new_plant = new(T,seed)
|
||||
new_plant.age = seed.get_trait(TRAIT_MATURATION)-5
|
||||
new_plant.update_icon()
|
||||
if(growth_type==0) //Vines do not become invisible.
|
||||
invisibility = INVISIBILITY_MAXIMUM
|
||||
|
||||
if(prob(spread_chance))
|
||||
for(var/i=1,i<=seed.get_trait(TRAIT_YIELD),i++)
|
||||
if(!possible_locs.len)
|
||||
break
|
||||
if(prob(spread_into_adjacent))
|
||||
var/turf/target_turf = pick(possible_locs)
|
||||
possible_locs -= target_turf
|
||||
var/obj/effect/plant/child = new(target_turf, seed)
|
||||
child.parent = get_root()
|
||||
child.parent.children |= child
|
||||
|
||||
/*
|
||||
var/need_hibernate = 0
|
||||
if(need_hibernate)
|
||||
hibernating = 1
|
||||
world << "[src] at [x],[y] is hibernating"
|
||||
*/
|
||||
|
||||
/obj/effect/plant/proc/get_root()
|
||||
if(parent)
|
||||
return parent.get_root()
|
||||
@@ -303,4 +230,7 @@
|
||||
|
||||
/obj/effect/plant/proc/check_health()
|
||||
if(health <= 0)
|
||||
die_off()
|
||||
die_off(1)
|
||||
|
||||
/obj/effect/plant/proc/is_mature()
|
||||
return (health < (max_health/3))
|
||||
75
code/modules/hydroponics/spreading/spreading_growth.dm
Normal file
75
code/modules/hydroponics/spreading/spreading_growth.dm
Normal file
@@ -0,0 +1,75 @@
|
||||
/obj/effect/plant/process()
|
||||
|
||||
// Something is very wrong, kill ourselves.
|
||||
if(!seed)
|
||||
die_off()
|
||||
|
||||
// Handle life.
|
||||
var/turf/simulated/T = get_turf(src)
|
||||
if(istype(T))
|
||||
health -= seed.handle_environment(T, T.return_air(),1)
|
||||
if(health < max_health)
|
||||
health += rand(3,5)
|
||||
if(health > max_health)
|
||||
health = max_health
|
||||
refresh_icon()
|
||||
|
||||
// Damaged, young hibernating or too far from parent, no chance of spreading.
|
||||
if(is_mature() || hibernating || (parent && (get_dist_to_parent(0) > spread_distance)))
|
||||
return
|
||||
|
||||
// Count our neighbors and possible locations for spreading.
|
||||
var/list/possible_locs = list()
|
||||
var/count = 0
|
||||
for(var/turf/simulated/floor/floor in view(1,src))
|
||||
if((locate(/obj/effect/dead_plant) in floor.contents) || !floor.Enter(src) || floor.density)
|
||||
continue
|
||||
if(locate(/obj/effect/plant) in floor.contents)
|
||||
count++
|
||||
continue
|
||||
possible_locs |= floor
|
||||
|
||||
//Entirely surrounded, try to spawn an actual plant.
|
||||
if(count>=8)
|
||||
if(!(locate(/obj/machinery/portable_atmospherics/hydroponics/soil/invisible) in T.contents))
|
||||
var/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/new_plant = new(T,seed)
|
||||
new_plant.age = seed.get_trait(TRAIT_MATURATION)-1
|
||||
new_plant.update_icon()
|
||||
if(growth_type==0) //Vines do not become invisible.
|
||||
invisibility = INVISIBILITY_MAXIMUM
|
||||
else
|
||||
new_plant.layer = 4.1
|
||||
|
||||
if(prob(spread_chance))
|
||||
for(var/i=1,i<=seed.get_trait(TRAIT_YIELD),i++)
|
||||
if(!possible_locs.len)
|
||||
hibernating = 1
|
||||
world << "[src] at [x],[y] is hibernating"
|
||||
break
|
||||
if(prob(spread_into_adjacent))
|
||||
var/turf/target_turf = pick(possible_locs)
|
||||
possible_locs -= target_turf
|
||||
var/obj/effect/plant/child = new(target_turf, seed)
|
||||
child.parent = get_root()
|
||||
child.parent.children |= child
|
||||
|
||||
/obj/effect/plant/proc/die_off(var/no_remains, var/no_del)
|
||||
// Remove ourselves from our parent.
|
||||
if(parent && parent.children)
|
||||
parent.children -= src
|
||||
// Kill off any of our children (and add an added bonus, other plants in this area)
|
||||
for(var/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/plant in get_turf(src))
|
||||
plant.dead = 1
|
||||
plant.update_icon()
|
||||
// Cause the plants around us to update.
|
||||
if(children && children.len)
|
||||
for(var/obj/effect/plant/child in children)
|
||||
child.die_off()
|
||||
for(var/obj/effect/plant/neighbor in view(1,src))
|
||||
neighbor.hibernating = 0
|
||||
if(!no_remains && !(locate(/obj/effect/dead_plant) in get_turf(src)))
|
||||
var/obj/effect/dead_plant/plant_remains = new(get_turf(src))
|
||||
plant_remains.icon = src.icon
|
||||
plant_remains.icon_state = src.icon_state
|
||||
if(!no_del)
|
||||
del(src)
|
||||
74
code/modules/hydroponics/spreading/spreading_response.dm
Normal file
74
code/modules/hydroponics/spreading/spreading_response.dm
Normal file
@@ -0,0 +1,74 @@
|
||||
/obj/effect/plant/HasProximity(var/atom/movable/AM)
|
||||
|
||||
hibernating = 0
|
||||
|
||||
if(!is_mature() || seed.get_trait(TRAIT_SPREAD) != 2)
|
||||
return
|
||||
|
||||
var/mob/living/M = AM
|
||||
if(!istype(M))
|
||||
return
|
||||
|
||||
if(!buckled_mob && !M.buckled && !M.anchored && prob(round(seed.get_trait(TRAIT_POTENCY)/2)))
|
||||
entangle(M)
|
||||
|
||||
/obj/effect/plant/attack_hand(mob/user as mob)
|
||||
hibernating = 0
|
||||
manual_unbuckle(user)
|
||||
|
||||
/obj/effect/plant/proc/trodden_on(var/mob/living/victim)
|
||||
hibernating = 0
|
||||
world << "Blah."
|
||||
if(!is_mature())
|
||||
return
|
||||
var/mob/living/carbon/human/H = victim
|
||||
if(!istype(H) || H.shoes)
|
||||
return
|
||||
seed.do_thorns(victim,src)
|
||||
seed.do_sting(victim,src,pick("r_foot","l_foot","r_leg","l_leg"))
|
||||
|
||||
/obj/effect/plant/proc/unbuckle()
|
||||
if(buckled_mob)
|
||||
if(buckled_mob.buckled == src)
|
||||
buckled_mob.buckled = null
|
||||
buckled_mob.anchored = initial(buckled_mob.anchored)
|
||||
buckled_mob.update_canmove()
|
||||
buckled_mob = null
|
||||
return
|
||||
|
||||
/obj/effect/plant/proc/manual_unbuckle(mob/user as mob)
|
||||
if(buckled_mob)
|
||||
if(prob(seed ? min(max(0,100 - seed.get_trait(TRAIT_POTENCY)/2),100) : 50))
|
||||
if(buckled_mob.buckled == src)
|
||||
if(buckled_mob != user)
|
||||
buckled_mob.visible_message(\
|
||||
"<span class='notice'>[user.name] frees [buckled_mob.name] from \the [src].</span>",\
|
||||
"<span class='notice'>[user.name] frees you from \the [src].</span>",\
|
||||
"<span class='warning'>You hear shredding and ripping.</span>")
|
||||
else
|
||||
buckled_mob.visible_message(\
|
||||
"<span class='notice'>[buckled_mob.name] struggles free of \the [src].</span>",\
|
||||
"<span class='notice'>You untangle \the [src] from around yourself.</span>",\
|
||||
"<span class='warning'>You hear shredding and ripping.</span>")
|
||||
unbuckle()
|
||||
else
|
||||
var/text = pick("rip","tear","pull")
|
||||
user.visible_message(\
|
||||
"<span class='notice'>[user.name] [text]s at \the [src].</span>",\
|
||||
"<span class='notice'>You [text] at \the [src].</span>",\
|
||||
"<span class='warning'>You hear shredding and ripping.</span>")
|
||||
return
|
||||
|
||||
/obj/effect/plant/proc/entangle(var/mob/living/victim)
|
||||
|
||||
if(buckled_mob)
|
||||
return
|
||||
|
||||
victim.buckled = src
|
||||
victim.update_canmove()
|
||||
buckled_mob = victim
|
||||
|
||||
if(victim.loc != src.loc)
|
||||
src.visible_message("<span class='danger'>Tendrils lash out from \the [src] and drag \the [victim] in!</span>")
|
||||
victim.loc = src.loc
|
||||
victim << "<span class='danger'>Tendrils [pick("wind", "tangle", "tighten")] around you!</span>"
|
||||
@@ -174,133 +174,6 @@
|
||||
else
|
||||
return 0
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/process()
|
||||
|
||||
//Do this even if we're not ready for a plant cycle.
|
||||
process_reagents()
|
||||
|
||||
// Update values every cycle rather than every process() tick.
|
||||
if(force_update)
|
||||
force_update = 0
|
||||
else if(world.time < (lastcycle + cycledelay))
|
||||
return
|
||||
lastcycle = world.time
|
||||
|
||||
// Mutation level drops each main tick.
|
||||
mutation_level -= rand(2,4)
|
||||
|
||||
// Weeds like water and nutrients, there's a chance the weed population will increase.
|
||||
// Bonus chance if the tray is unoccupied.
|
||||
if(waterlevel > 10 && nutrilevel > 2 && prob(isnull(seed) ? 5 : 1))
|
||||
weedlevel += 1 * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// There's a chance for a weed explosion to happen if the weeds take over.
|
||||
// Plants that are themselves weeds (weed_tolerance > 10) are unaffected.
|
||||
if (weedlevel >= 10 && prob(10))
|
||||
if(!seed || weedlevel >= seed.get_trait(TRAIT_WEED_TOLERANCE))
|
||||
weed_invasion()
|
||||
|
||||
// If there is no seed data (and hence nothing planted),
|
||||
// or the plant is dead, process nothing further.
|
||||
if(!seed || dead)
|
||||
if(mechanical) update_icon() //Harvesting would fail to set alert icons properly.
|
||||
return
|
||||
|
||||
// Advance plant age.
|
||||
if(prob(30)) age += 1 * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
//Highly mutable plants have a chance of mutating every tick.
|
||||
if(seed.get_trait(TRAIT_IMMUTABLE) == -1)
|
||||
var/mut_prob = rand(1,100)
|
||||
if(mut_prob <= 5) mutate(mut_prob == 1 ? 2 : 1)
|
||||
|
||||
// Other plants also mutate if enough mutagenic compounds have been added.
|
||||
if(!seed.get_trait(TRAIT_IMMUTABLE))
|
||||
if(prob(min(mutation_level,100)))
|
||||
mutate((rand(100) < 15) ? 2 : 1)
|
||||
mutation_level = 0
|
||||
|
||||
// Maintain tray nutrient and water levels.
|
||||
if(seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) > 0 && nutrilevel > 0 && prob(25))
|
||||
nutrilevel -= max(0,seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) * HYDRO_SPEED_MULTIPLIER)
|
||||
if(seed.get_trait(TRAIT_WATER_CONSUMPTION) > 0 && waterlevel > 0 && prob(25))
|
||||
waterlevel -= max(0,seed.get_trait(TRAIT_WATER_CONSUMPTION) * HYDRO_SPEED_MULTIPLIER)
|
||||
|
||||
// Make sure the plant is not starving or thirsty. Adequate
|
||||
// water and nutrients will cause a plant to become healthier.
|
||||
var/healthmod = rand(1,3) * HYDRO_SPEED_MULTIPLIER
|
||||
if(seed.get_trait(TRAIT_REQUIRES_NUTRIENTS) && prob(35))
|
||||
health += (nutrilevel < 2 ? -healthmod : healthmod)
|
||||
if(seed.get_trait(TRAIT_REQUIRES_WATER) && prob(35))
|
||||
health += (waterlevel < 10 ? -healthmod : healthmod)
|
||||
|
||||
// Check that pressure, heat and light are all within bounds.
|
||||
// First, handle an open system or an unconnected closed system.
|
||||
|
||||
var/turf/T = loc
|
||||
var/datum/gas_mixture/environment
|
||||
// If we're closed, take from our internal sources.
|
||||
if(closed_system && (connected_port || holding))
|
||||
environment = air_contents
|
||||
// If atmos input is not there, grab from turf.
|
||||
if(!environment && istype(T)) environment = T.return_air()
|
||||
if(!environment) return
|
||||
|
||||
// Seed datum handles gasses, light and pressure.
|
||||
health -= seed.handle_environment(T,environment)
|
||||
|
||||
// If we're attached to a pipenet, then we should let the pipenet know we might have modified some gasses
|
||||
if (closed_system && connected_port)
|
||||
update_connected_network()
|
||||
|
||||
// Toxin levels beyond the plant's tolerance cause damage, but
|
||||
// toxins are sucked up each tick and slowly reduce over time.
|
||||
if(toxins > 0)
|
||||
var/toxin_uptake = max(1,round(toxins/10))
|
||||
if(toxins > seed.get_trait(TRAIT_TOXINS_TOLERANCE))
|
||||
health -= toxin_uptake
|
||||
toxins -= toxin_uptake
|
||||
|
||||
// Check for pests and weeds.
|
||||
// Some carnivorous plants happily eat pests.
|
||||
if(pestlevel > 0)
|
||||
if(seed.get_trait(TRAIT_CARNIVOROUS))
|
||||
health += HYDRO_SPEED_MULTIPLIER
|
||||
pestlevel -= HYDRO_SPEED_MULTIPLIER
|
||||
else if (pestlevel >= seed.get_trait(TRAIT_PEST_TOLERANCE))
|
||||
health -= HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// Some plants thrive and live off of weeds.
|
||||
if(weedlevel > 0)
|
||||
if(seed.get_trait(TRAIT_PARASITE))
|
||||
health += HYDRO_SPEED_MULTIPLIER
|
||||
weedlevel -= HYDRO_SPEED_MULTIPLIER
|
||||
else if (weedlevel >= seed.get_trait(TRAIT_WEED_TOLERANCE))
|
||||
health -= HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// Handle life and death.
|
||||
// When the plant dies, weeds thrive and pests die off.
|
||||
check_health()
|
||||
|
||||
// If enough time (in cycles, not ticks) has passed since the plant was harvested, we're ready to harvest again.
|
||||
if(seed.products && seed.products.len && \
|
||||
(age > seed.get_trait(TRAIT_MATURATION)) && \
|
||||
((age - lastproduce) > seed.get_trait(TRAIT_PRODUCTION)) && \
|
||||
(!harvest && !dead))
|
||||
|
||||
harvest = 1
|
||||
lastproduce = age
|
||||
|
||||
if(prob(3)) // On each tick, there's a chance the pest population will increase
|
||||
pestlevel += 0.1 * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// Some seeds will self-harvest if you don't keep a lid on them.
|
||||
if(seed && seed.can_self_harvest && harvest && !closed_system && prob(5))
|
||||
harvest()
|
||||
|
||||
check_health()
|
||||
return
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/proc/check_health()
|
||||
if(seed && !dead && health <= 0)
|
||||
die()
|
||||
@@ -415,82 +288,7 @@
|
||||
check_health()
|
||||
return
|
||||
|
||||
//Refreshes the icon and sets the luminosity
|
||||
/obj/machinery/portable_atmospherics/hydroponics/update_icon()
|
||||
|
||||
overlays.Cut()
|
||||
|
||||
// Updates the plant overlay.
|
||||
if(!isnull(seed))
|
||||
|
||||
if(mechanical && health <= (seed.get_trait(TRAIT_ENDURANCE) / 2))
|
||||
overlays += "over_lowhealth3"
|
||||
|
||||
if(dead)
|
||||
var/ikey = "[seed.get_trait(TRAIT_PLANT_ICON)]-dead"
|
||||
var/image/dead_overlay = plant_icon_cache["[ikey]"]
|
||||
if(!dead_overlay)
|
||||
dead_overlay = image('icons/obj/hydroponics_growing.dmi', "[ikey]")
|
||||
overlays |= dead_overlay
|
||||
else
|
||||
if(!seed.growth_stages)
|
||||
seed.update_growth_stages()
|
||||
if(!seed.growth_stages)
|
||||
world << "<span class='danger'>Seed type [seed.get_trait(TRAIT_PLANT_ICON)] cannot find a growth stage value.</span>"
|
||||
return
|
||||
var/overlay_stage = 1
|
||||
if(age >= seed.get_trait(TRAIT_MATURATION))
|
||||
overlay_stage = seed.growth_stages
|
||||
lastproduce = age
|
||||
else
|
||||
overlay_stage = max(1,round(seed.get_trait(TRAIT_MATURATION) / seed.growth_stages))
|
||||
|
||||
var/ikey = "[seed.get_trait(TRAIT_PLANT_ICON)]-[overlay_stage]"
|
||||
var/image/plant_overlay = plant_icon_cache["[ikey]-[seed.get_trait(TRAIT_PLANT_COLOUR)]"]
|
||||
if(!plant_overlay)
|
||||
plant_overlay = image('icons/obj/hydroponics_growing.dmi', "[ikey]")
|
||||
plant_overlay.color = seed.get_trait(TRAIT_PLANT_COLOUR)
|
||||
plant_icon_cache["[ikey]-[seed.get_trait(TRAIT_PLANT_COLOUR)]"] = plant_overlay
|
||||
overlays |= plant_overlay
|
||||
|
||||
if(harvest && overlay_stage == seed.growth_stages)
|
||||
ikey = "[seed.get_trait(TRAIT_PRODUCT_ICON)]"
|
||||
var/image/harvest_overlay = plant_icon_cache["product-[ikey]-[seed.get_trait(TRAIT_PLANT_COLOUR)]"]
|
||||
if(!harvest_overlay)
|
||||
harvest_overlay = image('icons/obj/hydroponics_products.dmi', "[ikey]")
|
||||
harvest_overlay.color = seed.get_trait(TRAIT_PRODUCT_COLOUR)
|
||||
plant_icon_cache["product-[ikey]-[seed.get_trait(TRAIT_PRODUCT_COLOUR)]"] = harvest_overlay
|
||||
overlays |= harvest_overlay
|
||||
|
||||
//Draw the cover.
|
||||
if(closed_system)
|
||||
overlays += "hydrocover"
|
||||
|
||||
//Updated the various alert icons.
|
||||
if(mechanical)
|
||||
if(waterlevel <= 10)
|
||||
overlays += "over_lowwater3"
|
||||
if(nutrilevel <= 2)
|
||||
overlays += "over_lownutri3"
|
||||
if(weedlevel >= 5 || pestlevel >= 5 || toxins >= 40)
|
||||
overlays += "over_alert3"
|
||||
if(harvest)
|
||||
overlays += "over_harvest3"
|
||||
|
||||
// Update bioluminescence.
|
||||
if(seed)
|
||||
if(seed.get_trait(TRAIT_BIOLUM))
|
||||
SetLuminosity(round(seed.get_trait(TRAIT_POTENCY)/10))
|
||||
if(seed.get_trait(TRAIT_BIOLUM_COLOUR))
|
||||
l_color = seed.get_trait(TRAIT_BIOLUM_COLOUR)
|
||||
else
|
||||
l_color = null
|
||||
return
|
||||
|
||||
SetLuminosity(0)
|
||||
return
|
||||
|
||||
// If a weed growth is sufficient, this proc is called.
|
||||
// If a weed growth is sufficient, this proc is called.
|
||||
/obj/machinery/portable_atmospherics/hydroponics/proc/weed_invasion()
|
||||
|
||||
//Remove the seed if something is already planted.
|
||||
@@ -784,72 +582,3 @@
|
||||
closed_system = !closed_system
|
||||
usr << "You [closed_system ? "close" : "open"] the tray's lid."
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil
|
||||
name = "soil"
|
||||
icon_state = "soil"
|
||||
density = 0
|
||||
use_power = 0
|
||||
mechanical = 0
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/attackby(var/obj/item/O as obj, var/mob/user as mob)
|
||||
if(istype(O,/obj/item/weapon/tank))
|
||||
return
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/New()
|
||||
..()
|
||||
verbs -= /obj/machinery/portable_atmospherics/hydroponics/verb/close_lid
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/can_label()
|
||||
return 0
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/CanPass()
|
||||
return 1
|
||||
|
||||
// This is a hack pending a proper rewrite of the plant controller.
|
||||
// Icons for plants are generated as overlays, so setting it to invisible wouldn't work.
|
||||
// Hence using a blank icon.
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible
|
||||
name = "plant"
|
||||
icon = 'icons/obj/seeds.dmi'
|
||||
icon_state = "blank"
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/New(var/newloc,var/datum/seed/newseed)
|
||||
..()
|
||||
seed = newseed
|
||||
dead = 0
|
||||
age = 1
|
||||
health = seed.get_trait(TRAIT_ENDURANCE)
|
||||
lastcycle = world.time
|
||||
pixel_y = rand(-5,5)
|
||||
check_health()
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/remove_dead()
|
||||
..()
|
||||
del(src)
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/harvest()
|
||||
..()
|
||||
if(!seed)
|
||||
del(src)
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/die()
|
||||
del(src)
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/process()
|
||||
if(!seed)
|
||||
del(src)
|
||||
return
|
||||
else if(name=="plant")
|
||||
name = seed.display_name
|
||||
..()
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/Del()
|
||||
// Check if we're masking a decal that needs to be visible again.
|
||||
for(var/obj/effect/plant/plant in get_turf(src))
|
||||
if(plant.invisibility == INVISIBILITY_MAXIMUM)
|
||||
plant.invisibility = initial(plant.invisibility)
|
||||
plant.die_off()
|
||||
..()
|
||||
125
code/modules/hydroponics/trays/tray_process.dm
Normal file
125
code/modules/hydroponics/trays/tray_process.dm
Normal file
@@ -0,0 +1,125 @@
|
||||
/obj/machinery/portable_atmospherics/hydroponics/process()
|
||||
|
||||
//Do this even if we're not ready for a plant cycle.
|
||||
process_reagents()
|
||||
|
||||
// Update values every cycle rather than every process() tick.
|
||||
if(force_update)
|
||||
force_update = 0
|
||||
else if(world.time < (lastcycle + cycledelay))
|
||||
return
|
||||
lastcycle = world.time
|
||||
|
||||
// Mutation level drops each main tick.
|
||||
mutation_level -= rand(2,4)
|
||||
|
||||
// Weeds like water and nutrients, there's a chance the weed population will increase.
|
||||
// Bonus chance if the tray is unoccupied.
|
||||
if(waterlevel > 10 && nutrilevel > 2 && prob(isnull(seed) ? 5 : 1))
|
||||
weedlevel += 1 * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// There's a chance for a weed explosion to happen if the weeds take over.
|
||||
// Plants that are themselves weeds (weed_tolerance > 10) are unaffected.
|
||||
if (weedlevel >= 10 && prob(10))
|
||||
if(!seed || weedlevel >= seed.get_trait(TRAIT_WEED_TOLERANCE))
|
||||
weed_invasion()
|
||||
|
||||
// If there is no seed data (and hence nothing planted),
|
||||
// or the plant is dead, process nothing further.
|
||||
if(!seed || dead)
|
||||
if(mechanical) update_icon() //Harvesting would fail to set alert icons properly.
|
||||
return
|
||||
|
||||
// Advance plant age.
|
||||
if(prob(30)) age += 1 * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
//Highly mutable plants have a chance of mutating every tick.
|
||||
if(seed.get_trait(TRAIT_IMMUTABLE) == -1)
|
||||
var/mut_prob = rand(1,100)
|
||||
if(mut_prob <= 5) mutate(mut_prob == 1 ? 2 : 1)
|
||||
|
||||
// Other plants also mutate if enough mutagenic compounds have been added.
|
||||
if(!seed.get_trait(TRAIT_IMMUTABLE))
|
||||
if(prob(min(mutation_level,100)))
|
||||
mutate((rand(100) < 15) ? 2 : 1)
|
||||
mutation_level = 0
|
||||
|
||||
// Maintain tray nutrient and water levels.
|
||||
if(seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) > 0 && nutrilevel > 0 && prob(25))
|
||||
nutrilevel -= max(0,seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) * HYDRO_SPEED_MULTIPLIER)
|
||||
if(seed.get_trait(TRAIT_WATER_CONSUMPTION) > 0 && waterlevel > 0 && prob(25))
|
||||
waterlevel -= max(0,seed.get_trait(TRAIT_WATER_CONSUMPTION) * HYDRO_SPEED_MULTIPLIER)
|
||||
|
||||
// Make sure the plant is not starving or thirsty. Adequate
|
||||
// water and nutrients will cause a plant to become healthier.
|
||||
var/healthmod = rand(1,3) * HYDRO_SPEED_MULTIPLIER
|
||||
if(seed.get_trait(TRAIT_REQUIRES_NUTRIENTS) && prob(35))
|
||||
health += (nutrilevel < 2 ? -healthmod : healthmod)
|
||||
if(seed.get_trait(TRAIT_REQUIRES_WATER) && prob(35))
|
||||
health += (waterlevel < 10 ? -healthmod : healthmod)
|
||||
|
||||
// Check that pressure, heat and light are all within bounds.
|
||||
// First, handle an open system or an unconnected closed system.
|
||||
|
||||
var/turf/T = loc
|
||||
var/datum/gas_mixture/environment
|
||||
// If we're closed, take from our internal sources.
|
||||
if(closed_system && (connected_port || holding))
|
||||
environment = air_contents
|
||||
// If atmos input is not there, grab from turf.
|
||||
if(!environment && istype(T)) environment = T.return_air()
|
||||
if(!environment) return
|
||||
|
||||
// Seed datum handles gasses, light and pressure.
|
||||
health -= seed.handle_environment(T,environment)
|
||||
|
||||
// If we're attached to a pipenet, then we should let the pipenet know we might have modified some gasses
|
||||
if (closed_system && connected_port)
|
||||
update_connected_network()
|
||||
|
||||
// Toxin levels beyond the plant's tolerance cause damage, but
|
||||
// toxins are sucked up each tick and slowly reduce over time.
|
||||
if(toxins > 0)
|
||||
var/toxin_uptake = max(1,round(toxins/10))
|
||||
if(toxins > seed.get_trait(TRAIT_TOXINS_TOLERANCE))
|
||||
health -= toxin_uptake
|
||||
toxins -= toxin_uptake
|
||||
|
||||
// Check for pests and weeds.
|
||||
// Some carnivorous plants happily eat pests.
|
||||
if(pestlevel > 0)
|
||||
if(seed.get_trait(TRAIT_CARNIVOROUS))
|
||||
health += HYDRO_SPEED_MULTIPLIER
|
||||
pestlevel -= HYDRO_SPEED_MULTIPLIER
|
||||
else if (pestlevel >= seed.get_trait(TRAIT_PEST_TOLERANCE))
|
||||
health -= HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// Some plants thrive and live off of weeds.
|
||||
if(weedlevel > 0)
|
||||
if(seed.get_trait(TRAIT_PARASITE))
|
||||
health += HYDRO_SPEED_MULTIPLIER
|
||||
weedlevel -= HYDRO_SPEED_MULTIPLIER
|
||||
else if (weedlevel >= seed.get_trait(TRAIT_WEED_TOLERANCE))
|
||||
health -= HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// Handle life and death.
|
||||
// When the plant dies, weeds thrive and pests die off.
|
||||
check_health()
|
||||
|
||||
// If enough time (in cycles, not ticks) has passed since the plant was harvested, we're ready to harvest again.
|
||||
if(seed.products && seed.products.len && \
|
||||
(age > seed.get_trait(TRAIT_MATURATION)) && \
|
||||
((age - lastproduce) > seed.get_trait(TRAIT_PRODUCTION)) && \
|
||||
(!harvest && !dead))
|
||||
harvest = 1
|
||||
lastproduce = age
|
||||
|
||||
if(prob(3)) // On each tick, there's a chance the pest population will increase
|
||||
pestlevel += 0.1 * HYDRO_SPEED_MULTIPLIER
|
||||
|
||||
// Some seeds will self-harvest if you don't keep a lid on them.
|
||||
if(seed && seed.can_self_harvest && harvest && !closed_system && prob(5))
|
||||
harvest()
|
||||
|
||||
check_health()
|
||||
return
|
||||
143
code/modules/hydroponics/trays/tray_reagents.dm
Normal file
143
code/modules/hydroponics/trays/tray_reagents.dm
Normal file
@@ -0,0 +1,143 @@
|
||||
|
||||
/obj/item/weapon/plantspray
|
||||
icon = 'icons/obj/hydroponics_machines.dmi'
|
||||
item_state = "spray"
|
||||
flags = TABLEPASS | FPRINT | NOBLUDGEON
|
||||
slot_flags = SLOT_BELT
|
||||
throwforce = 4
|
||||
w_class = 2.0
|
||||
throw_speed = 2
|
||||
throw_range = 10
|
||||
var/toxicity = 4
|
||||
var/pest_kill_str = 0
|
||||
var/weed_kill_str = 0
|
||||
|
||||
/obj/item/weapon/plantspray/weeds // -- Skie
|
||||
|
||||
name = "weed-spray"
|
||||
desc = "It's a toxic mixture, in spray form, to kill small weeds."
|
||||
icon_state = "weedspray"
|
||||
weed_kill_str = 6
|
||||
|
||||
/obj/item/weapon/plantspray/pests
|
||||
name = "pest-spray"
|
||||
desc = "It's some pest eliminator spray! <I>Do not inhale!</I>"
|
||||
icon_state = "pestspray"
|
||||
pest_kill_str = 6
|
||||
|
||||
/obj/item/weapon/plantspray/pests/old
|
||||
name = "bottle of pestkiller"
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle16"
|
||||
|
||||
/obj/item/weapon/plantspray/pests/old/carbaryl
|
||||
name = "bottle of carbaryl"
|
||||
icon_state = "bottle16"
|
||||
toxicity = 4
|
||||
pest_kill_str = 2
|
||||
|
||||
/obj/item/weapon/plantspray/pests/old/lindane
|
||||
name = "bottle of lindane"
|
||||
icon_state = "bottle18"
|
||||
toxicity = 6
|
||||
pest_kill_str = 4
|
||||
|
||||
/obj/item/weapon/plantspray/pests/old/phosmet
|
||||
name = "bottle of phosmet"
|
||||
icon_state = "bottle15"
|
||||
toxicity = 8
|
||||
pest_kill_str = 7
|
||||
|
||||
/obj/item/weapon/minihoe // -- Numbers
|
||||
name = "mini hoe"
|
||||
desc = "It's used for removing weeds or scratching your back."
|
||||
icon = 'icons/obj/weapons.dmi'
|
||||
icon_state = "hoe"
|
||||
item_state = "hoe"
|
||||
flags = FPRINT | TABLEPASS | CONDUCT | NOBLUDGEON
|
||||
force = 5.0
|
||||
throwforce = 7.0
|
||||
w_class = 2.0
|
||||
matter = list("metal" = 50)
|
||||
attack_verb = list("slashed", "sliced", "cut", "clawed")
|
||||
|
||||
|
||||
// *************************************
|
||||
// Weedkiller defines for hydroponics
|
||||
// *************************************
|
||||
|
||||
/obj/item/weedkiller
|
||||
name = "bottle of weedkiller"
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle16"
|
||||
flags = FPRINT | TABLEPASS
|
||||
var/toxicity = 0
|
||||
var/weed_kill_str = 0
|
||||
|
||||
/obj/item/weedkiller/triclopyr
|
||||
name = "bottle of glyphosate"
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle16"
|
||||
flags = FPRINT | TABLEPASS
|
||||
toxicity = 4
|
||||
weed_kill_str = 2
|
||||
|
||||
/obj/item/weedkiller/lindane
|
||||
name = "bottle of triclopyr"
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle18"
|
||||
flags = FPRINT | TABLEPASS
|
||||
toxicity = 6
|
||||
weed_kill_str = 4
|
||||
|
||||
/obj/item/weedkiller/D24
|
||||
name = "bottle of 2,4-D"
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle15"
|
||||
flags = FPRINT | TABLEPASS
|
||||
toxicity = 8
|
||||
weed_kill_str = 7
|
||||
|
||||
|
||||
// *************************************
|
||||
// Nutrient defines for hydroponics
|
||||
// *************************************
|
||||
|
||||
/obj/item/weapon/reagent_containers/glass/fertilizer
|
||||
name = "fertilizer bottle"
|
||||
desc = "A small glass bottle. Can hold up to 10 units."
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle16"
|
||||
flags = FPRINT | TABLEPASS | OPENCONTAINER
|
||||
possible_transfer_amounts = null
|
||||
w_class = 2.0
|
||||
|
||||
var/fertilizer //Reagent contained, if any.
|
||||
|
||||
//Like a shot glass!
|
||||
amount_per_transfer_from_this = 10
|
||||
volume = 10
|
||||
|
||||
/obj/item/weapon/reagent_containers/glass/fertilizer/New()
|
||||
..()
|
||||
|
||||
src.pixel_x = rand(-5.0, 5)
|
||||
src.pixel_y = rand(-5.0, 5)
|
||||
|
||||
if(fertilizer)
|
||||
reagents.add_reagent(fertilizer,10)
|
||||
|
||||
/obj/item/weapon/reagent_containers/glass/fertilizer/ez
|
||||
name = "bottle of E-Z-Nutrient"
|
||||
icon_state = "bottle16"
|
||||
fertilizer = "eznutrient"
|
||||
|
||||
/obj/item/weapon/reagent_containers/glass/fertilizer/l4z
|
||||
name = "bottle of Left 4 Zed"
|
||||
icon_state = "bottle18"
|
||||
fertilizer = "left4zed"
|
||||
|
||||
/obj/item/weapon/reagent_containers/glass/fertilizer/rh
|
||||
name = "bottle of Robust Harvest"
|
||||
icon_state = "bottle15"
|
||||
fertilizer = "robustharvest"
|
||||
68
code/modules/hydroponics/trays/tray_soil.dm
Normal file
68
code/modules/hydroponics/trays/tray_soil.dm
Normal file
@@ -0,0 +1,68 @@
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil
|
||||
name = "soil"
|
||||
icon_state = "soil"
|
||||
density = 0
|
||||
use_power = 0
|
||||
mechanical = 0
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/attackby(var/obj/item/O as obj, var/mob/user as mob)
|
||||
if(istype(O,/obj/item/weapon/tank))
|
||||
return
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/New()
|
||||
..()
|
||||
verbs -= /obj/machinery/portable_atmospherics/hydroponics/verb/close_lid
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/can_label()
|
||||
return 0
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/CanPass()
|
||||
return 1
|
||||
|
||||
// This is a hack pending a proper rewrite of the plant controller.
|
||||
// Icons for plants are generated as overlays, so setting it to invisible wouldn't work.
|
||||
// Hence using a blank icon.
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible
|
||||
name = "plant"
|
||||
icon = 'icons/obj/seeds.dmi'
|
||||
icon_state = "blank"
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/New(var/newloc,var/datum/seed/newseed)
|
||||
..()
|
||||
seed = newseed
|
||||
dead = 0
|
||||
age = 1
|
||||
health = seed.get_trait(TRAIT_ENDURANCE)
|
||||
lastcycle = world.time
|
||||
pixel_y = rand(-5,5)
|
||||
check_health()
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/remove_dead()
|
||||
..()
|
||||
del(src)
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/harvest()
|
||||
..()
|
||||
if(!seed)
|
||||
del(src)
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/die()
|
||||
del(src)
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/process()
|
||||
if(!seed)
|
||||
del(src)
|
||||
return
|
||||
else if(name=="plant")
|
||||
name = seed.display_name
|
||||
..()
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/Del()
|
||||
// Check if we're masking a decal that needs to be visible again.
|
||||
for(var/obj/effect/plant/plant in get_turf(src))
|
||||
if(plant.invisibility == INVISIBILITY_MAXIMUM)
|
||||
plant.invisibility = initial(plant.invisibility)
|
||||
plant.die_off()
|
||||
..()
|
||||
74
code/modules/hydroponics/trays/tray_update_icons.dm
Normal file
74
code/modules/hydroponics/trays/tray_update_icons.dm
Normal file
@@ -0,0 +1,74 @@
|
||||
//Refreshes the icon and sets the luminosity
|
||||
/obj/machinery/portable_atmospherics/hydroponics/update_icon()
|
||||
|
||||
overlays.Cut()
|
||||
|
||||
// Updates the plant overlay.
|
||||
if(!isnull(seed))
|
||||
|
||||
if(mechanical && health <= (seed.get_trait(TRAIT_ENDURANCE) / 2))
|
||||
overlays += "over_lowhealth3"
|
||||
|
||||
if(dead)
|
||||
var/ikey = "[seed.get_trait(TRAIT_PLANT_ICON)]-dead"
|
||||
var/image/dead_overlay = plant_icon_cache["[ikey]"]
|
||||
if(!dead_overlay)
|
||||
dead_overlay = image('icons/obj/hydroponics_growing.dmi', "[ikey]")
|
||||
dead_overlay.color = DEAD_PLANT_COLOUR
|
||||
overlays |= dead_overlay
|
||||
else
|
||||
if(!seed.growth_stages)
|
||||
seed.update_growth_stages()
|
||||
if(!seed.growth_stages)
|
||||
world << "<span class='danger'>Seed type [seed.get_trait(TRAIT_PLANT_ICON)] cannot find a growth stage value.</span>"
|
||||
return
|
||||
var/overlay_stage = 1
|
||||
if(age >= seed.get_trait(TRAIT_MATURATION))
|
||||
overlay_stage = seed.growth_stages
|
||||
else
|
||||
overlay_stage = max(1,round(seed.get_trait(TRAIT_MATURATION) / seed.growth_stages))
|
||||
|
||||
var/ikey = "[seed.get_trait(TRAIT_PLANT_ICON)]-[overlay_stage]"
|
||||
var/image/plant_overlay = plant_icon_cache["[ikey]-[seed.get_trait(TRAIT_PLANT_COLOUR)]"]
|
||||
if(!plant_overlay)
|
||||
plant_overlay = image('icons/obj/hydroponics_growing.dmi', "[ikey]")
|
||||
plant_overlay.color = seed.get_trait(TRAIT_PLANT_COLOUR)
|
||||
plant_icon_cache["[ikey]-[seed.get_trait(TRAIT_PLANT_COLOUR)]"] = plant_overlay
|
||||
overlays |= plant_overlay
|
||||
|
||||
if(harvest && overlay_stage == seed.growth_stages)
|
||||
ikey = "[seed.get_trait(TRAIT_PRODUCT_ICON)]"
|
||||
var/image/harvest_overlay = plant_icon_cache["product-[ikey]-[seed.get_trait(TRAIT_PLANT_COLOUR)]"]
|
||||
if(!harvest_overlay)
|
||||
harvest_overlay = image('icons/obj/hydroponics_products.dmi', "[ikey]")
|
||||
harvest_overlay.color = seed.get_trait(TRAIT_PRODUCT_COLOUR)
|
||||
plant_icon_cache["product-[ikey]-[seed.get_trait(TRAIT_PRODUCT_COLOUR)]"] = harvest_overlay
|
||||
overlays |= harvest_overlay
|
||||
|
||||
//Draw the cover.
|
||||
if(closed_system)
|
||||
overlays += "hydrocover"
|
||||
|
||||
//Updated the various alert icons.
|
||||
if(mechanical)
|
||||
if(waterlevel <= 10)
|
||||
overlays += "over_lowwater3"
|
||||
if(nutrilevel <= 2)
|
||||
overlays += "over_lownutri3"
|
||||
if(weedlevel >= 5 || pestlevel >= 5 || toxins >= 40)
|
||||
overlays += "over_alert3"
|
||||
if(harvest)
|
||||
overlays += "over_harvest3"
|
||||
|
||||
// Update bioluminescence.
|
||||
if(seed)
|
||||
if(seed.get_trait(TRAIT_BIOLUM))
|
||||
SetLuminosity(round(seed.get_trait(TRAIT_POTENCY)/10))
|
||||
if(seed.get_trait(TRAIT_BIOLUM_COLOUR))
|
||||
l_color = seed.get_trait(TRAIT_BIOLUM_COLOUR)
|
||||
else
|
||||
l_color = null
|
||||
return
|
||||
|
||||
SetLuminosity(0)
|
||||
return
|
||||
Reference in New Issue
Block a user