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)