Files
Paradise/code/modules/hydroponics/plant_genes.dm
T
MigratingcocofruitandGitHub 6db06b1eb8 Plant DNA Manipulator update (#25370)
* WIP

* changes

* more changes

* chnges

* update

* changes

* I suck at TGUI if you see this please help

* UI a bit better still figuring it out

* More things

* Eject Select, Select empty and autoselect empty implemented. can insert replaace stat/all stats from disk list

* Ready to make a PR

Disks can be loaded into the machine from boxed, an empty disk is automatically selected once an extraction is performed if one is available. All insert and replace buttons moved to the disk lists. Window size and scaling changed to fit LSD properly without wrapping.

* minor ui tweak, fixes core stat disk being auto selected unintentionally. Loading via box now makes a sound.

* rebuild TGUI

* rebuild tgui

* implemented RO button/indicator in UI, Added null checks for can_add

* slightly shrink some buttons rebuild TGUI

* Rebuild TGUI

* use istype for type checks

* removes the disk compartmentalizer from maps

* removes Disk Compartmentalizer

* actually removes the disk compartmentalizer

* rebuild tgui and remove all remaining disk compartmentalizer sprites

* fixes a message being output to world instead of the user

* auto doc and readability improvements.

* some fixes

adjusts display name to display the new doubled percentages. Stored seed no longer counts as a disk for the purpose of reaching the disk capacity.

* Rebuild TGUI
2024-06-28 18:57:32 +00:00

484 lines
15 KiB
Plaintext

/datum/plant_gene
var/name
/// Used to determine if the trait should be logged when the holder is used
var/dangerous = FALSE
/datum/plant_gene/proc/get_name() // Used for manipulator display and gene disk name.
return name
/datum/plant_gene/proc/can_add(obj/item/seeds/S)
return !istype(S, /obj/item/seeds/sample) // Samples can't accept new genes
/datum/plant_gene/proc/Copy()
return new type
/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
var/use_max = FALSE
/datum/plant_gene/core/get_name()
return "[name] [value]"
/datum/plant_gene/core/proc/apply_stat(obj/item/seeds/S)
return
/datum/plant_gene/core/New(i = null)
..()
if(!isnull(i))
value = i
/datum/plant_gene/core/Copy()
var/datum/plant_gene/core/C = ..()
C.value = value
return C
/datum/plant_gene/core/can_add(obj/item/seeds/S)
if(!..())
return FALSE
return S.get_gene(type)
/datum/plant_gene/core/proc/get_genemod_variable(obj/machinery/plantgenes/modder)
if(!modder)
stack_trace("A plant gene ([get_name()]) tried to get a genemod variable without being in a genemodder.")
return
stack_trace("A plant gene ([get_name()]) tried to get a genemod variable, but had no override.")
/datum/plant_gene/core/lifespan
name = "Lifespan"
value = 25
/datum/plant_gene/core/lifespan/apply_stat(obj/item/seeds/S)
S.lifespan = value
/datum/plant_gene/core/lifespan/get_genemod_variable(obj/machinery/plantgenes/modder)
if(!modder) // Let the parent handle it
return ..()
return modder.max_endurance // Yes, this is intended. It is used for both lifespan and endurance
/datum/plant_gene/core/endurance
name = "Endurance"
value = 15
/datum/plant_gene/core/endurance/apply_stat(obj/item/seeds/S)
S.endurance = value
/datum/plant_gene/core/endurance/get_genemod_variable(obj/machinery/plantgenes/modder)
if(!modder) // Let the parent handle it
return ..()
return modder.max_endurance
/datum/plant_gene/core/production
name = "Production Speed"
value = 6
use_max = TRUE
/datum/plant_gene/core/production/apply_stat(obj/item/seeds/S)
S.production = value
/datum/plant_gene/core/production/get_genemod_variable(obj/machinery/plantgenes/modder)
if(!modder) // Let the parent handle it
return ..()
return modder.min_production
/datum/plant_gene/core/yield
name = "Yield"
value = 3
/datum/plant_gene/core/yield/apply_stat(obj/item/seeds/S)
S.yield = value
/datum/plant_gene/core/yield/get_genemod_variable(obj/machinery/plantgenes/modder)
if(!modder) // Let the parent handle it
return ..()
return modder.max_yield
/datum/plant_gene/core/potency
name = "Potency"
value = 10
/datum/plant_gene/core/potency/apply_stat(obj/item/seeds/S)
S.potency = value
/datum/plant_gene/core/potency/get_genemod_variable(obj/machinery/plantgenes/modder)
if(!modder) // Let the parent handle it
return ..()
return modder.max_potency
/datum/plant_gene/core/weed_rate
name = "Weed Growth Rate"
value = 1
use_max = TRUE
/datum/plant_gene/core/weed_rate/apply_stat(obj/item/seeds/S)
S.weed_rate = value
/datum/plant_gene/core/weed_rate/get_genemod_variable(obj/machinery/plantgenes/modder)
if(!modder) // Let the parent handle it
return ..()
return modder.min_weed_rate
/datum/plant_gene/core/weed_chance
name = "Weed Vulnerability"
value = 5
use_max = TRUE
/datum/plant_gene/core/weed_chance/apply_stat(obj/item/seeds/S)
S.weed_chance = value
/datum/plant_gene/core/weed_chance/get_genemod_variable(obj/machinery/plantgenes/modder)
if(!modder) // Let the parent handle it
return ..()
return modder.min_weed_chance
// Reagent genes store reagent ID and reagent ratio. Amount of reagent in the plant = 1 + (potency * rate)
/datum/plant_gene/reagent
name = "Nutriment"
var/reagent_id = "nutriment"
var/rate = 0.04
/datum/plant_gene/reagent/get_name()
return "[name] production [rate * 200]%" // Yes 200 is correct
/datum/plant_gene/reagent/proc/set_reagent(reag_id)
reagent_id = reag_id
name = "UNKNOWN"
var/datum/reagent/R = GLOB.chemical_reagents_list[reag_id]
if(R && R.id == reagent_id)
if(reagent_id == "holywater")
name = "Holy Water"
else
name = R.name
/datum/plant_gene/reagent/New(reag_id = null, reag_rate = 0)
..()
if(reag_id && reag_rate)
set_reagent(reag_id)
rate = reag_rate
/datum/plant_gene/reagent/Copy()
var/datum/plant_gene/reagent/G = ..()
G.name = name
G.reagent_id = reagent_id
G.rate = rate
return G
/datum/plant_gene/reagent/can_add(obj/item/seeds/S)
if(!..())
return FALSE
if(!S)
return FALSE
for(var/datum/plant_gene/reagent/R in S.genes)
if(R.reagent_id == reagent_id)
return FALSE
return TRUE
// Various traits affecting the product. Each must be somehow useful.
/datum/plant_gene/trait
var/rate = 0.05
var/examine_line = ""
var/list/origin_tech = null
var/trait_id // must be set and equal for any two traits of the same type
/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)
if(!..())
return FALSE
if(!S)
return FALSE
for(var/datum/plant_gene/trait/R in S.genes)
if(trait_id && R.trait_id == trait_id)
return FALSE
if(type == R.type)
return FALSE
return TRUE
/datum/plant_gene/trait/proc/on_new(obj/item/food/snacks/grown/G)
if(!origin_tech) // This ugly code segment adds RnD tech levels to resulting plants.
return
if(G.origin_tech)
var/list/tech = params2list(G.origin_tech)
for(var/t in origin_tech)
if(t in tech)
tech[t] = max(text2num(tech[t]), origin_tech[t])
else
tech[t] = origin_tech[t]
G.origin_tech = list2params(tech)
else
G.origin_tech = list2params(origin_tech)
/datum/plant_gene/trait/proc/on_consume(obj/item/food/snacks/grown/G, mob/living/carbon/target)
return
/datum/plant_gene/trait/proc/on_slip(obj/item/food/snacks/grown/G, mob/living/carbon/target)
return
/datum/plant_gene/trait/proc/on_squash(obj/item/food/snacks/grown/G, atom/target)
return
/datum/plant_gene/trait/proc/on_attackby(obj/item/food/snacks/grown/G, obj/item/I, mob/user)
return
/datum/plant_gene/trait/proc/on_throw_impact(obj/item/food/snacks/grown/G, atom/target)
return
/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 = "<span class='info'>It has a lot of liquid contents inside.</span>"
origin_tech = list("biotech" = 5)
dangerous = TRUE
/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 = 0.1
examine_line = "<span class='info'>It has a very slippery skin.</span>"
dangerous = TRUE
/datum/plant_gene/trait/slip/on_new(obj/item/food/snacks/grown/G)
. = ..()
if(istype(G) && ispath(G.trash, /obj/item/grown))
return
var/stun_len = G.seed.potency * rate * 1.6 SECONDS
if(!istype(G, /obj/item/grown/bananapeel) && (!G.reagents || !G.reagents.has_reagent("lube")))
stun_len /= 3
stun_len = min(stun_len, 14 SECONDS)// No fun allowed
G.AddComponent(/datum/component/slippery, G, stun_len, 100, 0, FALSE)
/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 damage (potency*rate*5).
// Multiplies max charge by (rate*100) when used in potato power cells.
name = "Electrical Activity"
rate = 0.2
origin_tech = list("powerstorage" = 5)
dangerous = TRUE
/datum/plant_gene/trait/cell_charge/on_slip(obj/item/food/snacks/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_squash(obj/item/food/snacks/grown/G, atom/target)
if(isliving(target))
var/mob/living/carbon/C = target
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_consume(obj/item/food/snacks/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)
target.reagents.add_reagent("teslium", 2)
to_chat(target, "<span class='notice'>Your batteries are recharged!</span>")
/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 = "<span class='info'>It emits a soft glow.</span>"
trait_id = "glow"
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_power(obj/item/seeds/S)
return max(S.potency*(rate + 0.01), 0.1)
/datum/plant_gene/trait/glow/on_new(obj/item/food/snacks/grown/G)
..()
G.set_light(glow_range(G.seed), glow_power(G.seed), glow_color)
/datum/plant_gene/trait/glow/shadow
//makes plant emit slightly purple shadows
//adds -potency*(rate*0.05) 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.05), 0.075)
/datum/plant_gene/trait/glow/red
name = "Red Electrical Glow"
glow_color = LIGHT_COLOR_RED
/datum/plant_gene/trait/glow/berry
name = "Strong Bioluminescence"
rate = 0.05
glow_color = null
/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
origin_tech = list("bluespace" = 5)
dangerous = TRUE
/datum/plant_gene/trait/teleport/on_squash(obj/item/food/snacks/grown/G, atom/target)
if(isliving(target))
var/mob/living/L = target
var/teleport_radius = max(round(G.seed.potency / 10), 1)
var/turf/T = get_turf(L)
new /obj/effect/decal/cleanable/molten_object(T) //Leave a pile of goo behind for dramatic effect...
do_teleport(L, T, teleport_radius)
L.apply_status_effect(STATUS_EFFECT_TELEPORTSICK)
/datum/plant_gene/trait/teleport/on_slip(obj/item/food/snacks/grown/G, mob/living/carbon/C)
var/teleport_radius = max(round(G.seed.potency / 10), 1)
var/turf/T = get_turf(C)
if(do_teleport(C, T, teleport_radius))
to_chat(C, "<span class='warning'>You slip through spacetime!</span>")
if(prob(50))
do_teleport(G, T, teleport_radius)
C.apply_status_effect(STATUS_EFFECT_TELEPORTSICK)
else
new /obj/effect/decal/cleanable/molten_object(T) //Leave a pile of goo behind for dramatic effect...
qdel(G)
else
to_chat(C, "<span class='warning'>[src] sparks, and burns up!</span>")
new /obj/effect/decal/cleanable/molten_object(T)
qdel(G)
/datum/plant_gene/trait/maxchem
// 2x to max reagents volume.
name = "Densified Chemicals"
rate = 2
/datum/plant_gene/trait/maxchem/on_new(obj/item/food/snacks/grown/G)
..()
G.reagents.maximum_volume *= rate
/datum/plant_gene/trait/repeated_harvest
name = "Perennial Growth"
/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
/datum/plant_gene/trait/battery
name = "Capacitive Cell Production"
/datum/plant_gene/trait/battery/on_attackby(obj/item/food/snacks/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, "<span class='notice'>You add some cable to [G] and slide it inside the battery encasing.</span>")
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 = max(G.seed.potency * 20, 1)
// 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) // 20x charge for deafult cell charge gene - 40 000 with 100 potency.
pocell.maxcharge *= CG.rate*100
pocell.charge = pocell.maxcharge
pocell.name = "[G] battery"
pocell.desc = "A rechargable plant based power cell. This one has a power rating of [pocell.maxcharge], and you should not swallow it."
if(G.reagents.has_reagent("plasma", 2))
pocell.rigged = 1
qdel(G)
else
to_chat(user, "<span class='warning'>You need five lengths of cable to make a [G] battery!</span>")
/datum/plant_gene/trait/stinging
name = "Hypodermic Prickles"
dangerous = TRUE
/datum/plant_gene/trait/stinging/on_throw_impact(obj/item/food/snacks/grown/G, atom/target)
if(isliving(target) && G.reagents && G.reagents.total_volume)
var/mob/living/L = target
// It would be nice to inject the body part the original thrower aimed at,
// but we don't have this kind of information here. So pick something at random.
var/target_zone = pick("chest", "chest", "chest", "l_leg", "r_leg", "l_arm", "r_arm", "head")
if(L.reagents && L.can_inject(null, FALSE, target_zone))
var/injecting_amount = max(1, G.seed.potency*0.2) // Minimum of 1, max of 20
var/fraction = min(injecting_amount/G.reagents.total_volume, 1)
G.reagents.reaction(L, REAGENT_INGEST, fraction)
G.reagents.trans_to(L, injecting_amount)
to_chat(target, "<span class='danger'>You are pricked by [G]!</span>")
/datum/plant_gene/trait/smoke
name = "Gaseous Decomposition"
dangerous = TRUE
/datum/plant_gene/trait/smoke/on_squash(obj/item/food/snacks/grown/G, atom/target)
var/datum/effect_system/smoke_spread/chem/plant/S = new()
var/splat_location = get_turf(target)
var/smoke_amount = round(sqrt(G.seed.potency * 0.1), 1)
S.set_up(G.reagents, splat_location)
S.start(smoke_amount)
/// Lavaland
/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(obj/item/food/snacks/grown/G)
if(!(G.resistance_flags & FIRE_PROOF))
G.resistance_flags |= FIRE_PROOF
/// Parent type
/datum/plant_gene/trait/plant_type
name = "you shouldn't see this"
trait_id = "plant_type"
/datum/plant_gene/trait/plant_type/weed_hardy
name = "Weed Adaptation"
/datum/plant_gene/trait/plant_type/fungal_metabolism
name = "Fungal Vitality"
/datum/plant_gene/trait/plant_type/alien_properties
name ="?????"