diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm
index 720f880cf99..80c872bcfcc 100644
--- a/code/game/turfs/simulated/walls.dm
+++ b/code/game/turfs/simulated/walls.dm
@@ -51,7 +51,8 @@
/turf/simulated/wall/ChangeTurf(var/newtype)
for(var/obj/effect/E in src) if(E.name == "Wallrot") del E
- for(var/obj/effect/plant/plant in range(1)) plant.wake_up()
+ for(var/obj/effect/plant/plant in range(1))
+ plant.update_neighbors()
..(newtype)
//Appearance
diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm
index f81f2ba260d..b893c6dec83 100644
--- a/code/modules/hydroponics/grown.dm
+++ b/code/modules/hydroponics/grown.dm
@@ -47,10 +47,11 @@
for(var/rid in seed.chems)
var/list/reagent_data = seed.chems[rid]
- var/rtotal = reagent_data[1]
- if(reagent_data.len > 1 && potency > 0)
- rtotal += round(potency/reagent_data[2])
- reagents.add_reagent(rid,max(1,rtotal))
+ if(reagent_data && reagent_data.len)
+ var/rtotal = reagent_data[1]
+ if(reagent_data.len > 1 && potency > 0)
+ rtotal += round(potency/reagent_data[2])
+ reagents.add_reagent(rid,max(1,rtotal))
update_desc()
if(reagents.total_volume > 0)
bitesize = 1+round(reagents.total_volume / 2, 1)
@@ -119,7 +120,7 @@
desc += " mushroom"
else
desc += " fruit"
- plant_controller.product_descs[seed.uid] = desc
+ plant_controller.product_descs["[seed.uid]"] = desc
desc += ". Delicious! Probably."
/obj/item/weapon/reagent_containers/food/snacks/grown/update_icon()
@@ -184,7 +185,7 @@
del(src)
return
else if(istype(W, /obj/item/weapon/circular_saw) || istype(W, /obj/item/weapon/hatchet) || (istype(W, /obj/item/weapon/twohanded/fireaxe) && W:wielded) || istype(W, /obj/item/weapon/melee/energy))
- if(seed.get_trait(TRAIT_PRODUCT_ICON) == "mushroom7")
+ if(seed.chems && !isnull(seed.chems["woodpulp"]))
user.show_message("You make planks out of \the [src]!", 1)
for(var/i=0,i<2,i++)
var/obj/item/stack/sheet/wood/NG = new (user.loc)
diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm
index 64475a7f468..b401db24f91 100644
--- a/code/modules/hydroponics/seed.dm
+++ b/code/modules/hydroponics/seed.dm
@@ -362,6 +362,7 @@
if(additional_chems)
var/list/possible_chems = list(
+ "woodpulp",
"bicaridine",
"hyperzine",
"cryoxadone",
diff --git a/code/modules/hydroponics/seed_controller.dm b/code/modules/hydroponics/seed_controller.dm
index 7d6784fb03e..d41794e546b 100644
--- a/code/modules/hydroponics/seed_controller.dm
+++ b/code/modules/hydroponics/seed_controller.dm
@@ -1,8 +1,8 @@
// Attempts to offload processing for the spreading plants from the MC.
// Processes vines/spreading plants.
-#define PLANTS_PER_TICK 5
-#define PLANT_TICK_TIME 10
+#define PLANTS_PER_TICK 100
+#define PLANT_TICK_TIME 50
// Debug for testing seed genes.
/client/proc/show_plant_genes()
@@ -25,9 +25,8 @@ var/global/datum/controller/plants/plant_controller // Set in New().
var/plants_per_tick = PLANTS_PER_TICK
var/plant_tick_time = PLANT_TICK_TIME
-
var/list/product_descs = list() // Stores generated fruit descs.
- var/list/next_plants = list() // All queued plants.
+ var/list/plant_queue = list() // All queued plants.
var/list/seeds = list() // All seed data stored here.
var/list/gene_tag_masks = list() // Gene obfuscation for delicious trial and error goodness.
var/list/plant_icon_cache = list() // Stores images of growth, fruits and seeds.
@@ -107,6 +106,9 @@ var/global/datum/controller/plants/plant_controller // Set in New().
if(seed.consume_gasses)
seed.consume_gasses["phoron"] = null
seed.consume_gasses["carbon_dioxide"] = null
+ if(seed.chems && !isnull(seed.chems["pacid"]))
+ seed.chems["pacid"] = null // Eating through the hull will make these plants completely inviable, albeit very dangerous.
+ seed.chems -= null // Setting to null does not actually remove the entry, which is weird.
seed.set_trait(TRAIT_IDEAL_HEAT,293)
seed.set_trait(TRAIT_HEAT_TOLERANCE,20)
seed.set_trait(TRAIT_IDEAL_LIGHT,8)
@@ -125,19 +127,20 @@ var/global/datum/controller/plants/plant_controller // Set in New().
sleep(plant_tick_time)
else
processed = 0
- var/list/plants = next_plants
- next_plants = list()
- for(var/x=0;xNull or non-plant entry in plant controller queue."
+ break
+ plant_queue -= plant
+ sleep(1) // Stagger processing out so previous tick can resolve (overlapping plant segments etc)
+ plant.process()
+ processed++
+ sleep(max(1,(plant_tick_time-processed)))
/datum/controller/plants/proc/add_plant(var/obj/effect/plant/plant)
- next_plants |= plant
+ plant_queue |= plant
diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm
index 4dcd661f4e2..561210537dd 100644
--- a/code/modules/hydroponics/seed_datums.dm
+++ b/code/modules/hydroponics/seed_datums.dm
@@ -443,6 +443,7 @@
name = "towercap"
seed_name = "tower cap"
display_name = "tower caps"
+ chems = list("woodpulp" = list(10,1))
mutants = null
/datum/seed/mushroom/towercap/New()
diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm
index fc46a9a7285..45fcd0d721b 100644
--- a/code/modules/hydroponics/spreading/spreading.dm
+++ b/code/modules/hydroponics/spreading/spreading.dm
@@ -30,8 +30,8 @@
/obj/effect/dead_plant/attackby()
..()
- for(var/obj/effect/plant/neighbor in view(1,src))
- neighbor.wake_up()
+ for(var/obj/effect/plant/neighbor in range(1))
+ neighbor.update_neighbors()
del(src)
/obj/effect/plant
@@ -50,13 +50,12 @@
var/growth_type = 0
var/max_growth = 0
- var/list/children = list()
+ var/list/neighbors = list()
var/obj/effect/plant/parent
var/datum/seed/seed
var/floor = 0
var/spread_chance = 40
var/spread_distance = 3
- var/spread_into_adjacent = 60
var/evolve_chance = 2
var/last_tick = 0
var/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/plant
@@ -64,9 +63,14 @@
/obj/effect/plant/single
spread_chance = 0
-/obj/effect/plant/New(var/newloc, var/datum/seed/newseed)
+/obj/effect/plant/New(var/newloc, var/datum/seed/newseed, var/obj/effect/plant/newparent)
..()
+ if(!newparent)
+ parent = src
+ else
+ parent = newparent
+
if(!plant_controller)
sleep(250) // ugly hack, should mean roundstart plants are fine.
if(!plant_controller)
@@ -102,22 +106,27 @@
if(max_growth > 2 && prob(50))
max_growth-- //Ensure some variation in final sprite, makes the carpet of crap look less wonky.
- spread_into_adjacent = round(seed.get_trait(TRAIT_POTENCY)/2)
- spread_distance = ((growth_type>0) ? round(spread_into_adjacent/2) : round(spread_into_adjacent/15))
- spread_chance = round(spread_into_adjacent/2)
+ spread_chance = seed.get_trait(TRAIT_POTENCY)
+ spread_distance = ((growth_type>0) ? round(spread_chance*0.6) : round(spread_chance*0.3))
set_dir(calc_dir())
update_icon()
- wake_up()
- last_tick = world.timeofday
+
+ spawn(1)
+ plant_controller.add_plant(src)
+ // Some plants eat through plating.
+ if(!isnull(seed.chems["pacid"]))
+ var/turf/T = get_turf(src)
+ T.ex_act(prob(80) ? 3 : 2)
/obj/effect/plant/update_icon()
-
+ //TODO: should really be caching this.
refresh_icon()
if(growth_type == 0 && !floor)
src.transform = null
var/matrix/M = matrix()
- M.Translate(0,-(rand(12,14))) // should make the plant flush against the wall it's meant to be growing from.
+ // should make the plant flush against the wall it's meant to be growing from.
+ M.Translate(0,-(rand(12,14)))
switch(dir)
if(WEST)
M.Turn(90)
@@ -141,7 +150,13 @@
SetLuminosity(0)
/obj/effect/plant/proc/refresh_icon()
- var/growth = min(max_growth,max(1,round(health/growth_threshold)))
+ var/growth = min(max_growth,round(health/growth_threshold))
+ var/at_fringe = get_dist(src,parent)
+ if(at_fringe >= (spread_distance-3))
+ max_growth--
+ if(at_fringe >= (spread_distance-2))
+ max_growth--
+ max_growth = max(1,max_growth)
if(growth_type > 0)
switch(growth_type)
if(1)
@@ -155,24 +170,14 @@
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)
- ..()
-
-/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/proc/get_root()
- if(parent)
- return parent.get_root()
+ if(growth>2 && growth == max_growth)
+ layer = 5
+ opacity = 1
+ if(!isnull(seed.chems["woodpulp"]))
+ density = 1
else
- return src
+ layer = 3
+ density = 0
/obj/effect/plant/proc/calc_dir(turf/location = loc)
set background = 1
@@ -209,6 +214,8 @@
/obj/effect/plant/attackby(var/obj/item/weapon/W, var/mob/user)
+ plant_controller.add_plant(src)
+
if(istype(W, /obj/item/weapon/wirecutters) || istype(W, /obj/item/weapon/scalpel))
if(!seed)
user << "There is nothing to take a sample from in \the [src]."
@@ -240,7 +247,7 @@
/obj/effect/plant/proc/check_health()
if(health <= 0)
- die_off(1)
+ die_off()
/obj/effect/plant/proc/is_mature()
return (health < (max_health/3))
\ No newline at end of file
diff --git a/code/modules/hydroponics/spreading/spreading_growth.dm b/code/modules/hydroponics/spreading/spreading_growth.dm
index 213f3381abf..e671663ebae 100644
--- a/code/modules/hydroponics/spreading/spreading_growth.dm
+++ b/code/modules/hydroponics/spreading/spreading_growth.dm
@@ -1,8 +1,31 @@
+#define NEIGHBOR_REFRESH_TIME 100
+
+/obj/effect/plant/proc/update_neighbors()
+ // Update our list of valid neighboring turfs.
+ neighbors = list()
+ for(var/turf/simulated/floor/floor in range(1,src))
+ if(get_dist(parent, floor) > spread_distance)
+ continue
+ if((locate(/obj/effect/plant) in floor.contents) || (locate(/obj/effect/dead_plant) in floor.contents) )
+ continue
+ if(floor.density)
+ if(!isnull(seed.chems["pacid"]))
+ spawn(rand(5,25)) floor.ex_act(3)
+ continue
+ else if(!floor.Enter(src))
+ continue
+ neighbors |= floor
+ // Update all of our friends.
+ var/turf/T = get_turf(src)
+ for(var/obj/effect/plant/neighbor in range(1,src))
+ neighbor.neighbors -= T
+
/obj/effect/plant/process()
// Something is very wrong, kill ourselves.
if(!seed)
die_off()
+ return 0
// Handle life.
var/turf/simulated/T = get_turf(src)
@@ -10,26 +33,13 @@
health -= seed.handle_environment(T, T.return_air(),1)
if(health < max_health)
health += rand(3,5)
+ refresh_icon()
if(health > max_health)
health = max_health
- refresh_icon()
-
- if(buckled_mob)
- seed.do_sting(buckled_mob,src)
- if(seed.get_trait(TRAIT_CARNIVOROUS))
- seed.do_thorns(buckled_mob,src)
-
- var/list/possible_locs = list()
- var/failed_turfs = 0
-
- for(var/turf/simulated/floor/floor in range(1))
- if((locate(/obj/effect/plant) in floor.contents) || (locate(/obj/effect/dead_plant) in floor.contents) || floor.density)
- failed_turfs++
- if(floor.Enter(src))
- possible_locs |= floor
-
- if(health == max_health && failed_turfs > 3 && !plant)
+ else if(health == max_health && !plant)
plant = new(T,seed)
+ plant.dir = src.dir
+ plant.transform = src.transform
plant.age = seed.get_trait(TRAIT_MATURATION)-1
plant.update_icon()
if(growth_type==0) //Vines do not become invisible.
@@ -37,42 +47,44 @@
else
plant.layer = layer + 0.1
- if(possible_locs.len && prob(spread_chance))
+ if(buckled_mob)
+ seed.do_sting(buckled_mob,src)
+ if(seed.get_trait(TRAIT_CARNIVOROUS))
+ seed.do_thorns(buckled_mob,src)
+
+ if(world.time >= last_tick+NEIGHBOR_REFRESH_TIME)
+ last_tick = world.time
+ update_neighbors()
+
+ if(neighbors.len && 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
+ if(prob(spread_chance))
+ sleep(rand(3,5))
+ if(!neighbors.len)
+ break
+ var/turf/target_turf = pick(neighbors)
+ var/obj/effect/plant/child = new(get_turf(src),seed,parent)
+ spawn(1) // This should do a little bit of animation.
+ child.loc = target_turf
+ child.update_icon()
+ // Update neighboring squares.
+ for(var/obj/effect/plant/neighbor in range(1,target_turf))
+ neighbor.neighbors -= target_turf
- if(buckled_mob || health != max_health || possible_locs.len)
- wake_up() // We still need to process!
-
-/obj/effect/plant/proc/wake_up()
- if(plant_controller)
+ // We shouldn't have spawned if the controller doesn't exist.
+ check_health()
+ if(neighbors.len && health == max_health)
plant_controller.add_plant(src)
-/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 as 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.wake_up()
+/obj/effect/plant/proc/die_off()
+ // Kill off our plant.
+ if(plant) plant.die()
+ // This turf is clear now, let our buddies know.
+ var/turf/T = get_turf(src)
+ for(var/obj/effect/plant/neighbor in range(1,src))
+ neighbor.neighbors |= T
+ plant_controller.add_plant(neighbor)
+ spawn(1) if(src) del(src)
- 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)
\ No newline at end of file
+#undef NEIGHBOR_REFRESH_TIME
\ No newline at end of file
diff --git a/code/modules/hydroponics/spreading/spreading_response.dm b/code/modules/hydroponics/spreading/spreading_response.dm
index 0f7cfea948b..ccf74e4ef71 100644
--- a/code/modules/hydroponics/spreading/spreading_response.dm
+++ b/code/modules/hydroponics/spreading/spreading_response.dm
@@ -1,5 +1,5 @@
/obj/effect/plant/HasProximity(var/atom/movable/AM)
- wake_up()
+
if(!is_mature() || seed.get_trait(TRAIT_SPREAD) != 2)
return
@@ -11,11 +11,10 @@
entangle(M)
/obj/effect/plant/attack_hand(mob/user as mob)
- wake_up()
+ // Todo, cause damage.
manual_unbuckle(user)
/obj/effect/plant/proc/trodden_on(var/mob/living/victim)
- wake_up()
if(!is_mature())
return
var/mob/living/carbon/human/H = victim
diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm
index acb501b850b..85ff1188681 100644
--- a/code/modules/hydroponics/trays/tray.dm
+++ b/code/modules/hydroponics/trays/tray.dm
@@ -521,7 +521,7 @@
A.hydrotray_type = src.type
del(src)
else if(O.force && seed)
- user.visible_message("\The [src] attacks the [seed.display_name] with \the [O]!")
+ user.visible_message("\The [user] attacks the [seed.display_name] with \the [O]!")
if(!dead)
health -= O.force
check_health()
diff --git a/code/modules/hydroponics/trays/tray_soil.dm b/code/modules/hydroponics/trays/tray_soil.dm
index abcf137e39f..d3a82ba383a 100644
--- a/code/modules/hydroponics/trays/tray_soil.dm
+++ b/code/modules/hydroponics/trays/tray_soil.dm
@@ -19,7 +19,7 @@
/obj/machinery/portable_atmospherics/hydroponics/soil/CanPass()
return 1
-// This is a hack pending a proper rewrite of the plant controller.
+// Holder for vine plants.
// 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
@@ -43,7 +43,7 @@
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/harvest()
..()
- if(!seed)
+ if(!seed) // Repeat harvests are a thing.
del(src)
/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/die()
@@ -62,5 +62,4 @@
for(var/obj/effect/plant/plant in get_turf(src))
if(plant.invisibility == INVISIBILITY_MAXIMUM)
plant.invisibility = initial(plant.invisibility)
- plant.die_off()
..()
diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/Chemistry-Reagents.dm
index 8beb66777c8..aba8573ded5 100644
--- a/code/modules/reagents/Chemistry-Reagents.dm
+++ b/code/modules/reagents/Chemistry-Reagents.dm
@@ -192,6 +192,13 @@ datum
M.resistances += self.data
return
+ woodpulp
+ name = "Wood Pulp"
+ id = "woodpulp"
+ description = "A mass of wood fibers."
+ reagent_state = LIQUID
+ color = "#B97A57"
+
#define WATER_LATENT_HEAT 19000 // How much heat is removed when applied to a hot turf, in J/unit (19000 makes 120 u of water roughly equivalent to 4L)
water
name = "Water"
@@ -260,13 +267,13 @@ datum
reaction_mob(var/mob/living/M, var/method=TOUCH, var/volume)
if (istype(M, /mob/living/carbon/slime))
- var/mob/living/carbon/slime/S = M
+ var/mob/living/carbon/slime/S = M
S.apply_water(volume)
if(method == TOUCH && isliving(M))
M.adjust_fire_stacks(-(volume / 10))
if(M.fire_stacks <= 0)
M.ExtinguishMob()
- return
+ return
water/holywater
name = "Holy Water"