diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index c5dad7e1334..f19bada643a 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -816,8 +816,12 @@
//Plants / Plant Traits
-///called when an action causes a plant to be squashed - slipping, throwing, attack_self.
-#define COMSIG_PLANT_SQUASH "plant_squash"
+///called when a plant with slippery skin is slipped on (mob/victim)
+#define COMSIG_PLANT_ON_SLIP "plant_on_slip"
+///called when a plant with liquid contents is squashed on (atom/target)
+#define COMSIG_PLANT_ON_SQUASH "plant_on_squash"
+///called when a plant grows in a tray (obj/machinery/hydroponics)
+#define COMSIG_PLANT_ON_GROW "plant_on_grow"
//Gibs
diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm
index f388a9329bf..e69969de0fd 100644
--- a/code/modules/hydroponics/grown.dm
+++ b/code/modules/hydroponics/grown.dm
@@ -45,8 +45,9 @@
make_dryable()
- for(var/datum/plant_gene/trait/T in seed.genes)
- T.on_new(src, loc)
+ // Go through all traits in their genes and call on_new_plant from them.
+ for(var/datum/plant_gene/trait/trait in seed.genes)
+ trait.on_new_plant(src, loc)
. = ..() //Only call it here because we want all the genes and shit to be applied before we add edibility. God this code is a mess.
@@ -64,53 +65,22 @@
eatverbs = eatverbs,\
bite_consumption = bite_consumption_mod ? 1 + round(max_volume / bite_consumption_mod) : bite_consumption,\
microwaved_type = microwaved_type,\
- junkiness = junkiness,\
- on_consume = CALLBACK(src, .proc/OnConsume))
-
+ junkiness = junkiness)
/obj/item/food/grown/proc/make_dryable()
AddElement(/datum/element/dryable, type)
-/obj/item/food/grown/examine(user)
- . = ..()
- if(seed)
- for(var/datum/plant_gene/trait/T in seed.genes)
- if(T.examine_line)
- . += T.examine_line
-
/obj/item/food/grown/attackby(obj/item/O, mob/user, params)
..()
if (istype(O, /obj/item/plant_analyzer))
var/obj/item/plant_analyzer/plant_analyzer = O
to_chat(user, plant_analyzer.scan_plant(src))
- else
- if(seed)
- for(var/datum/plant_gene/trait/T in seed.genes)
- T.on_attackby(src, O, user)
-
/obj/item/food/grown/MakeLeaveTrash()
if(trash_type)
AddElement(/datum/element/food_trash, trash_type, FOOD_TRASH_OPENABLE, /obj/item/food/grown/.proc/generate_trash)
return
-// Various gene procs
-/obj/item/food/grown/attack_self(mob/user)
- SEND_SIGNAL(src, COMSIG_PLANT_SQUASH, user)
- ..()
-
-/obj/item/food/grown/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
- if(!..()) //was it caught by a mob?
- if(seed)
- for(var/datum/plant_gene/trait/T in seed.genes)
- T.on_throw_impact(src, hit_atom)
-
-/obj/item/food/grown/proc/OnConsume(mob/living/eater, mob/living/feeder)
- if(iscarbon(usr))
- if(seed)
- for(var/datum/plant_gene/trait/T in seed.genes)
- T.on_consume(src, usr)
-
///Callback for bonus behavior for generating trash of grown food.
/obj/item/food/grown/proc/generate_trash(atom/location)
return new trash_type(location, seed)
diff --git a/code/modules/hydroponics/growninedible.dm b/code/modules/hydroponics/growninedible.dm
index cd8e9eadae3..62baeb2a3d1 100644
--- a/code/modules/hydroponics/growninedible.dm
+++ b/code/modules/hydroponics/growninedible.dm
@@ -23,8 +23,9 @@
pixel_y = base_pixel_y + rand(-5, 5)
if(seed)
- for(var/datum/plant_gene/trait/T in seed.genes)
- T.on_new(src, loc)
+ // Go through all traits in their genes and call on_new_plant from them.
+ for(var/datum/plant_gene/trait/trait in seed.genes)
+ trait.on_new_plant(src, loc)
if(istype(src, seed.product)) // no adding reagents if it is just a trash item
seed.prepare_result(src)
@@ -38,21 +39,11 @@
to_chat(user, plant_analyzer.scan_plant(src))
return
-/obj/item/grown/attack_self(mob/user)
- SEND_SIGNAL(src, COMSIG_PLANT_SQUASH, user)
- ..()
-
/obj/item/grown/proc/add_juice()
if(reagents)
return TRUE
return FALSE
-/obj/item/grown/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
- if(!..()) //was it caught by a mob?
- if(seed)
- for(var/datum/plant_gene/trait/T in seed.genes)
- T.on_throw_impact(src, hit_atom)
-
/obj/item/grown/microwave_act(obj/machinery/microwave/M)
return
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index bcfaf16f6b6..08714ae174a 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -288,10 +288,8 @@
update_icon()
if(myseed && prob(5 * (11-myseed.production)))
- for(var/g in myseed.genes)
- if(istype(g, /datum/plant_gene/trait))
- var/datum/plant_gene/trait/selectedtrait = g
- selectedtrait.on_grow(src)
+ SEND_SIGNAL(myseed, COMSIG_PLANT_ON_GROW, src)
+
return
/obj/machinery/hydroponics/update_icon()
diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm
index 3cdc3b4925b..1d1a3c3e8d1 100644
--- a/code/modules/hydroponics/plant_genes.dm
+++ b/code/modules/hydroponics/plant_genes.dm
@@ -33,9 +33,6 @@
G.mutability_flags = mutability_flags
return G
-/datum/plant_gene/proc/apply_vars(obj/item/seeds/S) // currently used for fire resist, can prob. be further refactored
- return
-
// Core plant genes store 5 main variables: lifespan, endurance, production, yield, potency
/datum/plant_gene/core
var/value
@@ -200,71 +197,90 @@
/// Traits that affect the grown product.
/datum/plant_gene/trait
+ /// The rate at which this trait affects something.
var/rate = 0.05
+ /// Bonus lines displayed on examine.
var/examine_line = ""
- /// Must be set and equal for any two traits of the same type
+ /// Traits that share this ID cannot be placed on the same plant.
var/trait_id
/// Flags that modify the final product.
var/trait_flags
+ /// A blacklist of seeds that a trait cannot be attached to.
+ var/list/obj/item/seeds/seed_blacklist
/datum/plant_gene/trait/Copy()
var/datum/plant_gene/trait/G = ..()
G.rate = rate
return G
-/datum/plant_gene/trait/can_add(obj/item/seeds/S)
+/*
+ * Checks if we can add the trait to the seed in question.
+ *
+ * source_seed - the seed genes we're adding the trait too
+ */
+/datum/plant_gene/trait/can_add(obj/item/seeds/source_seed)
if(!..())
return FALSE
- for(var/datum/plant_gene/trait/R in S.genes)
- if(trait_id && R.trait_id == trait_id)
+ for(var/obj/item/seeds/found_seed as anything in seed_blacklist)
+ if(istype(source_seed, found_seed))
return FALSE
- if(type == R.type)
+
+ for(var/datum/plant_gene/trait/trait in source_seed.genes)
+ if(trait_id && trait.trait_id == trait_id)
return FALSE
+ if(type == trait.type)
+ return FALSE
+
return TRUE
-/datum/plant_gene/trait/proc/on_new(obj/item/food/grown/G, newloc)
- return
+/*
+ * on_new_plant is called for every plant trait on an /obj/item/grown or /obj/item/food/grown when initialized.
+ *
+ * our_plant - the source plant being created
+ * newloc - the loc of the plant
+ */
+/datum/plant_gene/trait/proc/on_new_plant(obj/item/our_plant, newloc)
+ // Plants should always have seeds, but if a non-plant sneaks in or a plant with nulled seed, cut it out
+ if(isnull(our_plant.get_plant_seed()))
+ stack_trace("[our_plant] ([our_plant.type]) has a nulled seed value")
+ return FALSE
-/datum/plant_gene/trait/proc/on_consume(obj/item/food/grown/G, mob/living/carbon/target)
- return
+ // Add on any bonus lines on examine
+ if(examine_line)
+ RegisterSignal(our_plant, COMSIG_PARENT_EXAMINE, .proc/examine)
-/datum/plant_gene/trait/proc/on_slip(obj/item/food/grown/G, mob/living/carbon/target)
- return
+ return TRUE
-/datum/plant_gene/trait/proc/on_squash(obj/item/food/grown/G, atom/target)
- return
+/*
+ * on_new_seed is called when seed genes are initialized on the /obj/seed.
+ *
+ * new_seed - the seed being created
+ */
+/datum/plant_gene/trait/proc/on_new_seed(obj/item/seeds/new_seed)
+ return TRUE
-/datum/plant_gene/trait/proc/on_attackby(obj/item/food/grown/G, obj/item/I, mob/user)
- return
+/// Add on any unique examine text to the plant's examine text.
+/datum/plant_gene/trait/proc/examine(obj/item/our_plant, mob/examiner, list/examine_list)
+ SIGNAL_HANDLER
-/datum/plant_gene/trait/proc/on_throw_impact(obj/item/food/grown/G, atom/target)
- return
-
-///This proc triggers when the tray processes and a roll is sucessful, the success chance scales with production.
-/datum/plant_gene/trait/proc/on_grow(obj/machinery/hydroponics/H)
- return
+ examine_list += examine_line
+/// Allows the plant to be squashed when thrown or slipped on, leaving a colored mess and trash type item behind.
/datum/plant_gene/trait/squash
- // Allows the plant to be squashed when thrown or slipped on, leaving a colored mess and trash type item behind.
- // Also splashes everything in target turf with reagents and applies other trait effects (teleporting, etc) to the target by on_squash.
- // For code, see grown.dm
name = "Liquid Contents"
examine_line = "It has a lot of liquid contents inside."
trait_id = THROW_IMPACT_ID
// Register a signal that our plant can be squashed on add.
-/datum/plant_gene/trait/squash/on_new(obj/item/food/grown/our_plant, newloc)
+/datum/plant_gene/trait/squash/on_new_plant(obj/item/food/grown/our_plant, newloc)
. = ..()
- RegisterSignal(our_plant, COMSIG_PLANT_SQUASH, .proc/squash_plant)
+ if(!.)
+ return
-// Squash the plant on slip.
-/datum/plant_gene/trait/squash/on_slip(obj/item/food/grown/our_plant, mob/living/carbon/target)
- SEND_SIGNAL(our_plant, COMSIG_PLANT_SQUASH, target)
-
-// Squash the plant on thrown impact.
-/datum/plant_gene/trait/squash/on_throw_impact(obj/item/food/grown/our_plant, atom/target)
- SEND_SIGNAL(our_plant, COMSIG_PLANT_SQUASH, target)
+ RegisterSignal(our_plant, COMSIG_PLANT_ON_SLIP, .proc/squash_plant)
+ RegisterSignal(our_plant, COMSIG_MOVABLE_IMPACT, .proc/squash_plant)
+ RegisterSignal(our_plant, COMSIG_ITEM_ATTACK_SELF, .proc/squash_plant)
/*
* Signal proc to squash the plant this trait belongs to, causing a smudge, exposing the target to reagents, and deleting it,
@@ -292,10 +308,7 @@
misc_smudge.color = "#82b900"
our_plant.visible_message("[our_plant] is squashed.","You hear a smack.")
- var/obj/item/seeds/seed = our_plant.get_plant_seed()
- if(seed)
- for(var/datum/plant_gene/trait/trait in seed.genes)
- trait.on_squash(our_plant, target)
+ SEND_SIGNAL(our_plant, COMSIG_PLANT_ON_SQUASH, target)
our_plant.reagents?.expose(our_turf)
for(var/things in our_turf)
@@ -303,278 +316,450 @@
qdel(our_plant)
+/*
+ * Makes plant slippery, unless it has a grown-type trash. Then the trash gets slippery.
+ * Applies other trait effects (teleporting, etc) to the target by signal.
+ */
/datum/plant_gene/trait/slip
- // Makes plant slippery, unless it has a grown-type trash. Then the trash gets slippery.
- // Applies other trait effects (teleporting, etc) to the target by on_slip.
name = "Slippery Skin"
rate = 1.6
examine_line = "It has a very slippery skin."
-/datum/plant_gene/trait/slip/on_new(obj/item/food/grown/G, newloc)
- ..()
- if(istype(G) && ispath(G.trash_type, /obj/item/grown))
+/datum/plant_gene/trait/slip/on_new_plant(obj/item/our_plant, newloc)
+ . = ..()
+ if(!.)
return
- var/obj/item/seeds/seed = G.get_plant_seed()
- var/stun_len = seed.potency * rate
- if(!istype(G, /obj/item/grown/bananapeel) && (!G.reagents || !G.reagents.has_reagent(/datum/reagent/lube)))
+ var/obj/item/food/grown/grown_plant = our_plant
+ if(istype(grown_plant) && ispath(grown_plant.trash_type, /obj/item/grown))
+ return
+
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ var/stun_len = our_seed.potency * rate
+
+ if(!istype(our_plant, /obj/item/grown/bananapeel) && (!our_plant.reagents || !our_plant.reagents.has_reagent(/datum/reagent/lube)))
stun_len /= 3
- G.AddComponent(/datum/component/slippery, min(stun_len,140), NONE, CALLBACK(src, .proc/handle_slip, G))
+ our_plant.AddComponent(/datum/component/slippery, min(stun_len, 140), NONE, CALLBACK(src, .proc/handle_slip, our_plant))
-/datum/plant_gene/trait/slip/proc/handle_slip(obj/item/food/grown/G, mob/M)
- for(var/datum/plant_gene/trait/T in G.seed.genes)
- T.on_slip(G, M)
+/// On slip, sends a signal that our plant was slipped on out.
+/datum/plant_gene/trait/slip/proc/handle_slip(obj/item/food/grown/our_plant, mob/slipped_target)
+ SEND_SIGNAL(our_plant, COMSIG_PLANT_ON_SLIP, slipped_target)
+/*
+ * Cell recharging trait. Charges all mob's power cells to (potency*rate)% mark when eaten.
+ * Generates sparks on squash.
+ * Small (potency * rate) chance to shock squish or slip target for (potency * rate) damage.
+ * Also affects plant batteries see capatative cell production datum
+ */
/datum/plant_gene/trait/cell_charge
- // Cell recharging trait. Charges all mob's power cells to (potency*rate)% mark when eaten.
- // Generates sparks on squash.
- // Small (potency*rate*5) chance to shock squish or slip target for (potency*rate*5) damage.
- // Also affects plant batteries see capatative cell production datum
name = "Electrical Activity"
rate = 0.2
-/datum/plant_gene/trait/cell_charge/on_slip(obj/item/food/grown/G, mob/living/carbon/C)
- var/power = G.seed.potency*rate
- if(prob(power))
- C.electrocute_act(round(power), G, 1, SHOCK_NOGLOVES)
+/datum/plant_gene/trait/cell_charge/on_new_plant(obj/item/our_plant, newloc)
+ . = ..()
+ if(!.)
+ return
+
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ if(our_seed.get_gene(/datum/plant_gene/trait/squash))
+ // If we have the squash gene, let that handle slipping
+ RegisterSignal(our_plant, COMSIG_PLANT_ON_SQUASH, .proc/zap_target)
+ else
+ RegisterSignal(our_plant, COMSIG_PLANT_ON_SLIP, .proc/zap_target)
+
+ RegisterSignal(our_plant, COMSIG_FOOD_EATEN, .proc/recharge_cells)
+
+/*
+ * Zaps the target with a stunning shock.
+ *
+ * our_plant - our source plant, shocking the target
+ * target - the atom being zapped by our plant
+ */
+/datum/plant_gene/trait/cell_charge/proc/zap_target(obj/item/our_plant, atom/target)
+ SIGNAL_HANDLER
-/datum/plant_gene/trait/cell_charge/on_squash(obj/item/food/grown/G, atom/target)
if(iscarbon(target))
- var/mob/living/carbon/C = target
- var/power = G.seed.potency*rate
+ var/mob/living/carbon/target_carbon = target
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ var/power = our_seed.potency * rate
if(prob(power))
- C.electrocute_act(round(power), G, 1, SHOCK_NOGLOVES)
-
-/datum/plant_gene/trait/cell_charge/on_consume(obj/item/food/grown/G, mob/living/carbon/target)
- if(!G.reagents.total_volume)
- var/batteries_recharged = 0
- for(var/obj/item/stock_parts/cell/C in target.GetAllContents())
- var/newcharge = min(G.seed.potency*0.01*C.maxcharge, C.maxcharge)
- if(C.charge < newcharge)
- C.charge = newcharge
- if(isobj(C.loc))
- var/obj/O = C.loc
- O.update_icon() //update power meters and such
- C.update_icon()
- batteries_recharged = 1
- if(batteries_recharged)
- to_chat(target, "Your batteries are recharged!")
+ target_carbon.electrocute_act(round(power), our_plant, 1, SHOCK_NOGLOVES)
+/*
+ * Recharges every cell the person is holding for a bit based on plant potency.
+ *
+ * our_plant - our source plant, that we consumed to charge the cells
+ * eater - the mob that bit the plant
+ * feeder - the mob that feed the eater the plant
+ */
+/datum/plant_gene/trait/cell_charge/proc/recharge_cells(obj/item/our_plant, mob/living/eater, mob/feeder)
+ SIGNAL_HANDLER
+ to_chat(eater, "You feel energized as you bite into [our_plant].")
+ var/batteries_recharged = FALSE
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ for(var/obj/item/stock_parts/cell/found_cell in eater.GetAllContents())
+ var/newcharge = min(our_seed.potency * 0.01 * found_cell.maxcharge, found_cell.maxcharge)
+ if(found_cell.charge < newcharge)
+ found_cell.charge = newcharge
+ if(isobj(found_cell.loc))
+ var/obj/cell_location = found_cell.loc
+ cell_location.update_icon() //update power meters and such
+ found_cell.update_icon()
+ batteries_recharged = TRUE
+ if(batteries_recharged)
+ to_chat(eater, "Your batteries are recharged!")
+/*
+ * Makes the plant glow. Makes the plant in tray glow, too.
+ * Adds (1.4 + potency * rate) light range and (potency * (rate + 0.01)) light_power to products.
+ */
/datum/plant_gene/trait/glow
- // Makes plant glow. Makes plant in tray glow too.
- // Adds 1 + potency*rate light range and potency*(rate + 0.01) light_power to products.
+
name = "Bioluminescence"
rate = 0.03
examine_line = "It emits a soft glow."
trait_id = GLOW_ID
var/glow_color = "#C3E381"
-/datum/plant_gene/trait/glow/proc/glow_range(obj/item/seeds/S)
- return 1.4 + S.potency*rate
+/datum/plant_gene/trait/glow/proc/glow_range(obj/item/seeds/seed)
+ return 1.4 + seed.potency * rate
-/datum/plant_gene/trait/glow/proc/glow_power(obj/item/seeds/S)
- return max(S.potency*(rate + 0.01), 0.1)
+/datum/plant_gene/trait/glow/proc/glow_power(obj/item/seeds/seed)
+ return max(seed.potency * (rate + 0.01), 0.1)
-/datum/plant_gene/trait/glow/on_new(obj/item/food/grown/G, newloc)
+/datum/plant_gene/trait/glow/on_new_plant(obj/item/our_plant, newloc)
. = ..()
- G.light_system = MOVABLE_LIGHT
- G.AddComponent(/datum/component/overlay_lighting, glow_range(G.seed), glow_power(G.seed), glow_color)
+ if(!.)
+ return
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ our_plant.light_system = MOVABLE_LIGHT
+ our_plant.AddComponent(/datum/component/overlay_lighting, glow_range(our_seed), glow_power(our_seed), glow_color)
+
+/*
+ * Makes plant emit darkness. (Purple-ish shadows)
+ * Adds - (potency * (rate * 0.2)) light power to products.
+ */
/datum/plant_gene/trait/glow/shadow
- //makes plant emit slightly purple shadows
- //adds -potency*(rate*0.2) light power to products
+
name = "Shadow Emission"
rate = 0.04
glow_color = "#AAD84B"
-/datum/plant_gene/trait/glow/shadow/glow_power(obj/item/seeds/S)
- return -max(S.potency*(rate*0.2), 0.2)
+/datum/plant_gene/trait/glow/shadow/glow_power(obj/item/seeds/seed)
+ return -max(seed.potency*(rate*0.2), 0.2)
+/// Colored versions of bioluminescence.
+
+/// White
/datum/plant_gene/trait/glow/white
name = "White Bioluminescence"
glow_color = "#FFFFFF"
+/// Red
/datum/plant_gene/trait/glow/red
- //Colored versions of bioluminescence.
name = "Red Bioluminescence"
glow_color = "#FF3333"
+/// Yellow (not the disgusting glowshroom yellow hopefully)
/datum/plant_gene/trait/glow/yellow
- //not the disgusting glowshroom yellow hopefully
name = "Yellow Bioluminescence"
glow_color = "#FFFF66"
+/// Green (oh no, now i'm radioactive)
/datum/plant_gene/trait/glow/green
- //oh no, now i'm radioactive
name = "Green Bioluminescence"
glow_color = "#99FF99"
+/// Blue (the best one)
/datum/plant_gene/trait/glow/blue
- //the best one
name = "Blue Bioluminescence"
glow_color = "#6699FF"
+/// Purple (did you know that notepad++ doesnt think bioluminescence is a word) (was the person who wrote this using notepad++ for dm?)
/datum/plant_gene/trait/glow/purple
- //did you know that notepad++ doesnt think bioluminescence is a word
name = "Purple Bioluminescence"
glow_color = "#D966FF"
+// Pink (gay tide station pride)
/datum/plant_gene/trait/glow/pink
- //gay tide station pride
name = "Pink Bioluminescence"
glow_color = "#FFB3DA"
-
-
+/*
+ * Makes plant teleport people when squashed or slipped on.
+ * Teleport radius is roughly potency / 10.
+ */
/datum/plant_gene/trait/teleport
- // Makes plant teleport people when squashed or slipped on.
- // Teleport radius is calculated as max(round(potency*rate), 1)
name = "Bluespace Activity"
rate = 0.1
-/datum/plant_gene/trait/teleport/on_squash(obj/item/food/grown/G, atom/target)
+/datum/plant_gene/trait/teleport/on_new_plant(obj/item/our_plant, newloc)
+ . = ..()
+ if(!.)
+ return
+
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ if(our_seed.get_gene(/datum/plant_gene/trait/squash))
+ // If we have the squash gene, let that handle slipping
+ RegisterSignal(our_plant, COMSIG_PLANT_ON_SQUASH, .proc/squash_teleport)
+ else
+ RegisterSignal(our_plant, COMSIG_PLANT_ON_SLIP, .proc/slip_teleport)
+
+/*
+ * When squashed, makes the target teleport.
+ *
+ * our_plant - our plant, being squashed, and teleporting the target
+ * target - the atom targeted by the squash
+ */
+/datum/plant_gene/trait/teleport/proc/squash_teleport(obj/item/our_plant, atom/target)
+ SIGNAL_HANDLER
+
if(isliving(target))
- var/teleport_radius = max(round(G.seed.potency / 10), 1)
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ var/teleport_radius = max(round(our_seed.potency / 10), 1)
var/turf/T = get_turf(target)
new /obj/effect/decal/cleanable/molten_object(T) //Leave a pile of goo behind for dramatic effect...
do_teleport(target, T, teleport_radius, channel = TELEPORT_CHANNEL_BLUESPACE)
-/datum/plant_gene/trait/teleport/on_slip(obj/item/food/grown/G, mob/living/carbon/C)
- var/teleport_radius = max(round(G.seed.potency / 10), 1)
- var/turf/T = get_turf(C)
- to_chat(C, "You slip through spacetime!")
- do_teleport(C, T, teleport_radius, channel = TELEPORT_CHANNEL_BLUESPACE)
+/*
+ * When slipped on, makes the target teleport and either teleport the source again or delete it.
+ *
+ * our_plant - our plant being slipped on
+ * target - the carbon targeted that was slipped and was teleported
+ */
+/datum/plant_gene/trait/teleport/proc/slip_teleport(obj/item/our_plant, mob/living/carbon/target)
+ SIGNAL_HANDLER
+
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ var/teleport_radius = max(round(our_seed.potency / 10), 1)
+ var/turf/T = get_turf(target)
+ to_chat(target, "You slip through spacetime!")
+ do_teleport(target, T, teleport_radius, channel = TELEPORT_CHANNEL_BLUESPACE)
if(prob(50))
- do_teleport(G, T, teleport_radius, channel = TELEPORT_CHANNEL_BLUESPACE)
+ do_teleport(our_plant, T, teleport_radius, channel = TELEPORT_CHANNEL_BLUESPACE)
else
new /obj/effect/decal/cleanable/molten_object(T) //Leave a pile of goo behind for dramatic effect...
- qdel(G)
+ qdel(our_plant)
/**
* A plant trait that causes the plant's capacity to double.
*
* When harvested, the plant's individual capacity is set to double it's default.
- * However, the plant is also going to be limited to half as many products from yield, so 2 yield will only produce 1 plant as a result.
+ * However, the plant's maximum yield is also halved, only up to 5.
*/
/datum/plant_gene/trait/maxchem
- // 2x to max reagents volume.
name = "Densified Chemicals"
rate = 2
trait_flags = TRAIT_HALVES_YIELD
-/datum/plant_gene/trait/maxchem/on_new(obj/item/food/grown/G, newloc)
- ..()
- G.max_volume *= rate
+/datum/plant_gene/trait/maxchem/on_new_plant(obj/item/our_plant, newloc)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/food/grown/grown_plant = our_plant
+ if(istype(grown_plant, /obj/item/food/grown))
+ //Grown foods use the edible component so we need to change their max_volume var
+ grown_plant.max_volume *= rate
+ else
+ //Grown inedibles however just use a reagents holder, so.
+ our_plant.reagents?.maximum_volume *= rate
+
+/// Allows a plant to be harvested multiple times.
/datum/plant_gene/trait/repeated_harvest
name = "Perennial Growth"
+ /// Don't allow replica pods to be multi harvested, please.
+ seed_blacklist = list(/obj/item/seeds/replicapod)
-/datum/plant_gene/trait/repeated_harvest/can_add(obj/item/seeds/S)
- if(!..())
- return FALSE
- if(istype(S, /obj/item/seeds/replicapod))
- return FALSE
- return TRUE
-
+/*
+ * Allows a plant to be turned into a battery when cabling is applied.
+ * 100 potency plants are made into 2 mj batteries.
+ * Plants with electrical activity has their capacities massively increased (up to 40 mj at 100 potency)
+ */
/datum/plant_gene/trait/battery
name = "Capacitive Cell Production"
-/datum/plant_gene/trait/battery/on_attackby(obj/item/food/grown/G, obj/item/I, mob/user)
- if(istype(I, /obj/item/stack/cable_coil))
- var/obj/item/stack/cable_coil/C = I
- if(C.use(5))
- to_chat(user, "You add some cable to [G] and slide it inside the battery encasing.")
- var/obj/item/stock_parts/cell/potato/pocell = new /obj/item/stock_parts/cell/potato(user.loc)
- pocell.icon_state = G.icon_state
- pocell.maxcharge = G.seed.potency * 20
+/datum/plant_gene/trait/battery/on_new_plant(obj/item/our_plant, newloc)
+ . = ..()
+ if(!.)
+ return
- // The secret of potato supercells!
- var/datum/plant_gene/trait/cell_charge/CG = G.seed.get_gene(/datum/plant_gene/trait/cell_charge)
- if(CG) // Cell charge max is now 40MJ or otherwise known as 400KJ (Same as bluespace power cells)
- pocell.maxcharge *= CG.rate*100
- pocell.charge = pocell.maxcharge
- pocell.name = "[G.name] battery"
- pocell.desc = "A rechargeable plant-based power cell. This one has a rating of [DisplayEnergy(pocell.maxcharge)], and you should not swallow it."
+ RegisterSignal(our_plant, COMSIG_PARENT_ATTACKBY, .proc/make_battery)
- if(G.reagents.has_reagent(/datum/reagent/toxin/plasma, 2))
- pocell.rigged = TRUE
+/*
+ * When a plant with this gene is hit (attackby) with cables, we turn it into a battery.
+ *
+ * our_plant - our plant being hit
+ * hit_item - the item we're hitting the plant with
+ * user - the person hitting the plant with an item
+ */
+/datum/plant_gene/trait/battery/proc/make_battery(obj/item/our_plant, obj/item/hit_item, mob/user)
+ SIGNAL_HANDLER
- qdel(G)
- else
- to_chat(user, "You need five lengths of cable to make a [G] battery!")
+ if(!istype(hit_item, /obj/item/stack/cable_coil))
+ return
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ var/obj/item/stack/cable_coil/cabling = hit_item
+ if(!cabling.use(5))
+ to_chat(user, "You need five lengths of cable to make a [our_plant] battery!")
+ return
+ to_chat(user, "You add some cable to [our_plant] and slide it inside the battery encasing.")
+ var/obj/item/stock_parts/cell/potato/pocell = new /obj/item/stock_parts/cell/potato(user.loc)
+ pocell.icon_state = our_plant.icon_state
+ pocell.maxcharge = our_seed.potency * 20
+
+ // The secret of potato supercells!
+ var/datum/plant_gene/trait/cell_charge/electrical_gene = our_seed.get_gene(/datum/plant_gene/trait/cell_charge)
+ if(electrical_gene) // Cell charge max is now 40MJ or otherwise known as 400KJ (Same as bluespace power cells)
+ pocell.maxcharge *= (electrical_gene.rate * 100)
+ pocell.charge = pocell.maxcharge
+ pocell.name = "[our_plant.name] battery"
+ pocell.desc = "A rechargeable plant-based power cell. This one has a rating of [DisplayEnergy(pocell.maxcharge)], and you should not swallow it."
+
+ if(our_plant.reagents.has_reagent(/datum/reagent/toxin/plasma, 2))
+ pocell.rigged = TRUE
+
+ qdel(our_plant)
+
+/*
+ * Injects a number of chemicals from the plant when you throw it at someone or they slip on it.
+ * At 0 potency it can inject 1 unit of its chemicals, while at 100 potency it can inject 20 units.
+ */
/datum/plant_gene/trait/stinging
name = "Hypodermic Prickles"
+ examine_line = "It's quite prickley."
-/datum/plant_gene/trait/stinging/on_slip(obj/item/food/grown/G, atom/target)
- on_throw_impact(G, target)
+/datum/plant_gene/trait/stinging/on_new_plant(obj/item/our_plant, newloc)
+ . = ..()
+ if(!.)
+ return
-/datum/plant_gene/trait/stinging/on_throw_impact(obj/item/food/grown/G, atom/target)
- if(isliving(target) && G.reagents && G.reagents.total_volume)
- var/mob/living/L = target
- if(L.reagents && L.can_inject())
- var/injecting_amount = max(1, G.seed.potency*0.2) // Minimum of 1, max of 20
- G.reagents.trans_to(L, injecting_amount, methods = INJECT)
- to_chat(target, "You are pricked by [G]!")
- log_combat(G, L, "pricked and attempted to inject reagents from [G] to [L]. Last touched by: [G.fingerprintslast].")
+ RegisterSignal(our_plant, COMSIG_PLANT_ON_SLIP, .proc/prickles_inject)
+ RegisterSignal(our_plant, COMSIG_MOVABLE_IMPACT, .proc/prickles_inject)
+/*
+ * Injects a target with a number of reagents from our plant.
+ *
+ * our_plant - our plant that's injecting someone
+ * target - the atom being hit on thrown or slipping on our plant
+ */
+/datum/plant_gene/trait/stinging/proc/prickles_inject(obj/item/our_plant, atom/target)
+ if(isliving(target) && our_plant.reagents?.total_volume)
+ var/mob/living/living_target = target
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ if(living_target.reagents && living_target.can_inject())
+ var/injecting_amount = max(1, our_seed.potency * 0.2) // Minimum of 1, max of 20
+ our_plant.reagents.trans_to(living_target, injecting_amount, methods = INJECT)
+ to_chat(target, "You are pricked by [our_plant]!")
+ log_combat(our_plant, living_target, "pricked and attempted to inject reagents from [our_plant] to [living_target]. Last touched by: [our_plant.fingerprintslast].")
+
+/// Explodes into reagent-filled smoke when squashed.
/datum/plant_gene/trait/smoke
name = "Gaseous Decomposition"
-/datum/plant_gene/trait/smoke/on_squash(obj/item/food/grown/G, atom/target)
- var/datum/effect_system/smoke_spread/chem/S = new
- var/splat_location = get_turf(target)
- var/smoke_amount = round(sqrt(G.seed.potency * 0.1), 1)
- S.attach(splat_location)
- S.set_up(G.reagents, smoke_amount, splat_location, 0)
- S.start()
- G.reagents.clear_reagents()
+/datum/plant_gene/trait/smoke/on_new_plant(obj/item/our_plant, newloc)
+ . = ..()
+ if(!.)
+ return
-/datum/plant_gene/trait/fire_resistance // Lavaland
+ RegisterSignal(our_plant, COMSIG_PLANT_ON_SQUASH, .proc/make_smoke)
+
+/*
+ * Makes a cloud of reagent smoke.
+ *
+ * our_plant - our plant being squashed and smoked
+ * target - the atom the plant was squashed on
+ */
+/datum/plant_gene/trait/smoke/proc/make_smoke(obj/item/our_plant, atom/target)
+ SIGNAL_HANDLER
+
+ var/datum/effect_system/smoke_spread/chem/smoke = new ()
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ var/splat_location = get_turf(target)
+ var/smoke_amount = round(sqrt(our_seed.potency * 0.1), 1)
+ smoke.attach(splat_location)
+ smoke.set_up(our_plant.reagents, smoke_amount, splat_location, 0)
+ smoke.start()
+ our_plant.reagents.clear_reagents()
+
+/// Makes the plant and its seeds fireproof. From lavaland plants.
+/datum/plant_gene/trait/fire_resistance
name = "Fire Resistance"
-/datum/plant_gene/trait/fire_resistance/apply_vars(obj/item/seeds/S)
- if(!(S.resistance_flags & FIRE_PROOF))
- S.resistance_flags |= FIRE_PROOF
+/datum/plant_gene/trait/fire_resistance/on_new_seed(obj/item/seeds/new_seed)
+ if(!(new_seed.resistance_flags & FIRE_PROOF))
+ new_seed.resistance_flags |= FIRE_PROOF
-/datum/plant_gene/trait/fire_resistance/on_new(obj/item/food/grown/G, newloc)
- if(!(G.resistance_flags & FIRE_PROOF))
- G.resistance_flags |= FIRE_PROOF
+/datum/plant_gene/trait/fire_resistance/on_new_plant(obj/item/our_plant, newloc)
+ . = ..()
+ if(!.)
+ return
-///Invasive spreading lets the plant jump to other trays, the spreading plant won't replace plants of the same type.
+ if(!(our_plant.resistance_flags & FIRE_PROOF))
+ our_plant.resistance_flags |= FIRE_PROOF
+
+/// Invasive spreading lets the plant jump to other trays, and the spreading plant won't replace plants of the same type.
/datum/plant_gene/trait/invasive
name = "Invasive Spreading"
-/datum/plant_gene/trait/invasive/on_grow(obj/machinery/hydroponics/our_tray)
+/datum/plant_gene/trait/invasive/on_new_seed(obj/item/seeds/new_seed)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ RegisterSignal(new_seed, COMSIG_PLANT_ON_GROW, .proc/try_spread)
+
+ return TRUE
+/*
+ * Attempt to find an adjacent tray we can spread to.
+ *
+ * our_seed - our plant's seed, what spreads to other trays
+ * our_tray - the hydroponics tray we're currently in
+ */
+/datum/plant_gene/trait/invasive/proc/try_spread(obj/item/seeds/our_seed, obj/machinery/hydroponics/our_tray)
+ SIGNAL_HANDLER
+
for(var/step_dir in GLOB.alldirs)
var/obj/machinery/hydroponics/spread_tray = locate() in get_step(our_tray, step_dir)
if(spread_tray && prob(15))
if(!our_tray.Adjacent(spread_tray))
continue //Don't spread through things we can't go through.
- if(spread_tray.myseed) // Check if there's another seed in the next tray.
- if(spread_tray.myseed.type == our_tray.myseed.type && !spread_tray.dead)
- continue // It should not destroy its own kind.
- spread_tray.visible_message("The [spread_tray.myseed.plantname] is overtaken by [our_tray.myseed.plantname]!")
- QDEL_NULL(spread_tray.myseed)
- spread_tray.myseed = our_tray.myseed.Copy()
- spread_tray.age = 0
- spread_tray.dead = FALSE
- spread_tray.plant_health = spread_tray.myseed.endurance
- spread_tray.lastcycle = world.time
- spread_tray.harvest = FALSE
- spread_tray.weedlevel = 0 // Reset
- spread_tray.pestlevel = 0 // Reset
- spread_tray.update_icon()
- spread_tray.visible_message("The [our_tray.myseed.plantname] spreads!")
- if(spread_tray.myseed)
- spread_tray.name = "[initial(spread_tray.name)] ([spread_tray.myseed.plantname])"
- else
- spread_tray.name = initial(spread_tray.name)
+ spread_seed(spread_tray, our_tray)
+
+/*
+ * Actually spread the plant to the tray we found in try_spread.
+ *
+ * target_tray - the tray we're spreading to
+ * origin_tray - the tray we're currently in
+ */
+/datum/plant_gene/trait/invasive/proc/spread_seed(obj/machinery/hydroponics/target_tray, obj/machinery/hydroponics/origin_tray)
+ if(target_tray.myseed) // Check if there's another seed in the next tray.
+ if(target_tray.myseed.type == origin_tray.myseed.type && !target_tray.dead)
+ return FALSE // It should not destroy its own kind.
+ target_tray.visible_message("The [target_tray.myseed.plantname] is overtaken by [origin_tray.myseed.plantname]!")
+ QDEL_NULL(target_tray.myseed)
+ target_tray.myseed = origin_tray.myseed.Copy()
+ target_tray.age = 0
+ target_tray.dead = FALSE
+ target_tray.plant_health = target_tray.myseed.endurance
+ target_tray.lastcycle = world.time
+ target_tray.harvest = FALSE
+ target_tray.weedlevel = 0 // Reset
+ target_tray.pestlevel = 0 // Reset
+ target_tray.update_icon()
+ target_tray.visible_message("The [origin_tray.myseed.plantname] spreads!")
+ if(target_tray.myseed)
+ target_tray.name = "[initial(target_tray.name)] ([target_tray.myseed.plantname])"
+ else
+ target_tray.name = initial(target_tray.name)
+
+ return TRUE
/**
* A plant trait that causes the plant's food reagents to ferment instead.
@@ -606,20 +791,27 @@
name = "Hallucinatory Feedback"
/// Sounds that play when this trait triggers
var/list/sounds = list('sound/items/SitcomLaugh1.ogg', 'sound/items/SitcomLaugh2.ogg', 'sound/items/SitcomLaugh3.ogg')
- /// Whether or not we can trigger. (If we have a trash type it'll trigger on that instead)
- var/can_trigger = TRUE
-/datum/plant_gene/trait/plant_laughter/on_new(obj/item/food/grown/G, newloc)
- ..()
- if(istype(G) && ispath(G.trash_type, /obj/item/grown))
- can_trigger = FALSE
-
-/datum/plant_gene/trait/plant_laughter/on_slip(obj/item/food/grown/G, atom/target)
- if(!can_trigger)
+/datum/plant_gene/trait/plant_laughter/on_new_plant(obj/item/our_plant, newloc)
+ . = ..()
+ if(!.)
return
- G.audible_message("[G] lets out burst of laughter.")
- playsound(G, pick(sounds), 100, FALSE, SHORT_RANGE_SOUND_EXTRARANGE)
+ var/obj/item/food/grown/grown_plant = our_plant
+ if(istype(grown_plant) && ispath(grown_plant.trash_type, /obj/item/grown))
+ return
+
+ RegisterSignal(our_plant, COMSIG_PLANT_ON_SLIP, .proc/laughter)
+
+/*
+ * Play a sound effect from our plant.
+ *
+ * our_plant - the source plant that was slipped on
+ * target - the atom that slipped on the plant
+ */
+/datum/plant_gene/trait/plant_laughter/proc/laughter(obj/item/our_plant, atom/target)
+ our_plant.audible_message("[our_plant] lets out burst of laughter.")
+ playsound(our_plant, pick(sounds), 100, FALSE, SHORT_RANGE_SOUND_EXTRARANGE)
/**
* A plant trait that causes the plant to gain aesthetic googly eyes.
@@ -630,24 +822,33 @@
name = "Oculary Mimicry"
var/mutable_appearance/googly
-/datum/plant_gene/trait/eyes/on_new(obj/item/food/grown/G, newloc)
+/datum/plant_gene/trait/eyes/on_new_plant(obj/item/our_plant, newloc)
. = ..()
+ if(!.)
+ return
+
googly = mutable_appearance('icons/obj/hydroponics/harvest.dmi', "eyes")
googly.appearance_flags = RESET_COLOR
- G.add_overlay(googly)
+ our_plant.add_overlay(googly)
+/// Makes the plant embed on thrown impact.
/datum/plant_gene/trait/sticky
name = "Prickly Adhesion"
+ examine_line = "It's quite sticky."
trait_id = THROW_IMPACT_ID
-/datum/plant_gene/trait/sticky/on_new(obj/item/food/grown/G, newloc)
+/datum/plant_gene/trait/sticky/on_new_plant(obj/item/our_plant, newloc)
. = ..()
- if(G.seed.get_gene(/datum/plant_gene/trait/stinging))
- G.embedding = EMBED_POINTY
+ if(!.)
+ return
+
+ var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
+ if(our_seed.get_gene(/datum/plant_gene/trait/stinging))
+ our_plant.embedding = EMBED_POINTY
else
- G.embedding = EMBED_HARMLESS
- G.updateEmbedding()
- G.throwforce = (G.seed.potency/20)
+ our_plant.embedding = EMBED_HARMLESS
+ our_plant.updateEmbedding()
+ our_plant.throwforce = (our_seed.potency/20)
/**
* This trait automatically heats up the plant's chemical contents when harvested.
@@ -667,19 +868,24 @@
trait_id = TEMP_CHANGE_ID
trait_flags = TRAIT_HALVES_YIELD
+/// Plant type traits. Incompatible with one another.
/datum/plant_gene/trait/plant_type // Parent type
name = "you shouldn't see this"
trait_id = PLANT_TYPE_ID
+/// Weeds don't get annoyed by weeds in their tray.
/datum/plant_gene/trait/plant_type/weed_hardy
name = "Weed Adaptation"
+/// Mushrooms need less light and have a minimum yield.
/datum/plant_gene/trait/plant_type/fungal_metabolism
name = "Fungal Vitality"
+/// Currently unused and does nothing. Appears in strange seeds.
/datum/plant_gene/trait/plant_type/alien_properties
name ="?????"
+/// Plant doesn't get annoyed by pests in their tray.
/datum/plant_gene/trait/plant_type/carnivory
name = "Obligate Carnivory"
diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm
index 9424af64ed1..bd2f9e9e265 100644
--- a/code/modules/hydroponics/seeds.dm
+++ b/code/modules/hydroponics/seeds.dm
@@ -62,7 +62,7 @@
///Determines if the plant should be allowed to mutate early at 30+ instability.
var/seed_flags = MUTATE_EARLY
-/obj/item/seeds/Initialize(mapload, nogenes = 0)
+/obj/item/seeds/Initialize(mapload, nogenes = FALSE)
. = ..()
pixel_x = base_pixel_x + rand(-8, 8)
pixel_y = base_pixel_y + rand(-8, 8)
@@ -88,10 +88,14 @@
genes += new /datum/plant_gene/core/potency(potency)
genes += new /datum/plant_gene/core/instability(instability)
- for(var/p in genes)
- if(ispath(p))
- genes -= p
- genes += new p
+ for(var/plant_gene in genes)
+ if(ispath(plant_gene))
+ genes -= plant_gene
+ genes += new plant_gene
+
+ // Go through all traits in their genes and call on_new_seed from them.
+ for(var/datum/plant_gene/trait/traits in genes)
+ traits.on_new_seed(src)
for(var/reag_id in reagents_add)
genes += new /datum/plant_gene/reagent(reag_id, reagents_add[reag_id])
@@ -106,27 +110,29 @@
. += "- [G.get_name()] -"
/obj/item/seeds/proc/Copy()
- var/obj/item/seeds/S = new type(null, 1)
+ var/obj/item/seeds/copy_seed = new type(null, TRUE)
// Copy all the stats
- S.lifespan = lifespan
- S.endurance = endurance
- S.maturation = maturation
- S.production = production
- S.yield = yield
- S.potency = potency
- S.instability = instability
- S.weed_rate = weed_rate
- S.weed_chance = weed_chance
- S.name = name
- S.plantname = plantname
- S.desc = desc
- S.productdesc = productdesc
- S.genes = list()
- for(var/g in genes)
- var/datum/plant_gene/G = g
- S.genes += G.Copy()
- S.reagents_add = reagents_add.Copy() // Faster than grabbing the list from genes.
- return S
+ copy_seed.lifespan = lifespan
+ copy_seed.endurance = endurance
+ copy_seed.maturation = maturation
+ copy_seed.production = production
+ copy_seed.yield = yield
+ copy_seed.potency = potency
+ copy_seed.instability = instability
+ copy_seed.weed_rate = weed_rate
+ copy_seed.weed_chance = weed_chance
+ copy_seed.name = name
+ copy_seed.plantname = plantname
+ copy_seed.desc = desc
+ copy_seed.productdesc = productdesc
+ copy_seed.genes = list()
+ for(var/datum/plant_gene/plant_genes in genes)
+ copy_seed.genes += plant_genes.Copy()
+ for(var/datum/plant_gene/trait/traits in genes)
+ traits.on_new_seed(copy_seed)
+
+ copy_seed.reagents_add = reagents_add.Copy() // Faster than grabbing the list from genes.
+ return copy_seed
/obj/item/seeds/proc/get_gene(typepath)
return (locate(typepath) in genes)