diff --git a/code/__DEFINES/dcs/signals/signals_cytology.dm b/code/__DEFINES/dcs/signals/signals_cytology.dm index 70fbe29c4e0..fce8e8a2714 100644 --- a/code/__DEFINES/dcs/signals/signals_cytology.dm +++ b/code/__DEFINES/dcs/signals/signals_cytology.dm @@ -1,4 +1,5 @@ //Cytology signals ///Sent from /datum/biological_sample/proc/reset_sample #define COMSIG_SAMPLE_GROWTH_COMPLETED "sample_growth_completed" - +///Sent from /datum/biological_sample/proc/succeed_growing : (atom/grown_thing) +#define COMSIG_SAMPLE_DEPOSITED "sample_deposited" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 0e966709a14..8c22b65f724 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -922,6 +922,8 @@ GLOBAL_LIST_INIT(layers_to_offset, list( #define VOMIT_CATEGORY_DEFAULT (MOB_VOMIT_MESSAGE | MOB_VOMIT_HARM | MOB_VOMIT_STUN) /// The vomit you've all come to know and love, but with a little extra "spice" (blood) #define VOMIT_CATEGORY_BLOOD (VOMIT_CATEGORY_DEFAULT | MOB_VOMIT_BLOOD) +/// The bloody vomit, but without the stunning +#define VOMIT_CATEGORY_BLOOD_STUNLESS (VOMIT_CATEGORY_BLOOD & ~MOB_VOMIT_STUN) /// Another vomit variant that causes you to get knocked down instead of just only getting a stun. Standard otherwise. #define VOMIT_CATEGORY_KNOCKDOWN (MOB_VOMIT_MESSAGE | MOB_VOMIT_HARM | MOB_VOMIT_KNOCKDOWN) diff --git a/code/__DEFINES/research.dm b/code/__DEFINES/research.dm index f56ab891fa7..f3b2cf81948 100644 --- a/code/__DEFINES/research.dm +++ b/code/__DEFINES/research.dm @@ -56,6 +56,14 @@ #define CELL_LINE_TABLE_BUTTERFLY "cell_line_butterfly_table" #define CELL_LINE_TABLE_MEGA_ARACHNID "cell_line_table_mega_arachnid" +//! Biopsy cell line organ types +#define CELL_LINE_ORGAN_HEART "cell_line_organ_heart" +#define CELL_LINE_ORGAN_LUNGS "cell_line_organ_lungs" +#define CELL_LINE_ORGAN_LIVER "cell_line_organ_liver" +#define CELL_LINE_ORGAN_STOMACH "cell_line_organ_stomach" + +#define CELL_LINE_ORGAN_HEART_CURSED "cell_line_organ_heart_cursed" + //! All cell virus types #define CELL_VIRUS_TABLE_GENERIC "cell_virus_generic_table" #define CELL_VIRUS_TABLE_GENERIC_MOB "cell_virus_generic_mob_table" diff --git a/code/_globalvars/lists/xenobiology.dm b/code/_globalvars/lists/xenobiology.dm index 0e587e891b4..5b61e4e054c 100644 --- a/code/_globalvars/lists/xenobiology.dm +++ b/code/_globalvars/lists/xenobiology.dm @@ -85,7 +85,29 @@ GLOBAL_LIST_INIT_TYPED(cell_line_tables, /list, list( /datum/micro_organism/cell_line/snake = 1, /datum/micro_organism/cell_line/walking_mushroom = 2, /datum/micro_organism/cell_line/axolotl = 1, - ) + ), + CELL_LINE_ORGAN_HEART = list( + /datum/micro_organism/cell_line/organs/heart = 3, + /datum/micro_organism/cell_line/organs/heart/evolved = 1, + ), + CELL_LINE_ORGAN_HEART_CURSED = list( + /datum/micro_organism/cell_line/organs/heart/corrupt = 1, + /datum/micro_organism/cell_line/organs/heart/sacred = 1, + ), + CELL_LINE_ORGAN_LUNGS = list( + /datum/micro_organism/cell_line/organs/lungs = 3, + /datum/micro_organism/cell_line/organs/lungs/evolved = 1, + ), + CELL_LINE_ORGAN_LIVER = list( + /datum/micro_organism/cell_line/organs/liver = 3, + /datum/micro_organism/cell_line/organs/liver/evolved = 1, + /datum/micro_organism/cell_line/organs/liver/bloody = 1, + /datum/micro_organism/cell_line/organs/liver/distillery = 1, + ), + CELL_LINE_ORGAN_STOMACH = list( + /datum/micro_organism/cell_line/organs/stomach = 3, + /datum/micro_organism/cell_line/organs/stomach/evolved = 1, + ), )) ///Assoc list of cell virus define | assoc list of datum | cell_virus diff --git a/code/datums/components/anti_magic.dm b/code/datums/components/anti_magic.dm index a96ad73e93d..29855ac6c47 100644 --- a/code/datums/components/anti_magic.dm +++ b/code/datums/components/anti_magic.dm @@ -7,10 +7,12 @@ var/charges /// The inventory slot the object must be located at in order to activate var/inventory_flags - /// The callback invoked when we have been drained a antimagic charge - var/datum/callback/drain_antimagic + /// The callback invoked when we block magic + var/datum/callback/block_magic /// The callback invoked when twe have been depleted of all charges var/datum/callback/expiration + /// Callback to invoke to see if we can block magic + var/datum/callback/check_blocking /// Whether we should, on equipping, alert the caster that this item can block any of their spells /// This changes between true and false on equip and drop, don't set it outright to something var/alert_caster_on_equip = TRUE @@ -25,8 +27,9 @@ * * antimagic_flags (optional) A bitflag with the types of magic resistance on the object * * charges (optional) The amount of times the object can protect the user from magic * * inventory_flags (optional) The inventory slot the object must be located at in order to activate - * * drain_antimagic (optional) The proc that is triggered when an object has been drained a antimagic charge + * * block_magic (optional) The proc that is triggered when an object blocks magic * * expiration (optional) The proc that is triggered when the object is depleted of charges + * * check_blocking (optional) The proc that is triggered to check if we can block magic * * * antimagic bitflags: (see code/__DEFINES/magic.dm) * * MAGIC_RESISTANCE - Default magic resistance that blocks normal magic (wizard, spells, staffs) @@ -37,8 +40,9 @@ antimagic_flags = MAGIC_RESISTANCE, charges = INFINITY, inventory_flags = ALL, - datum/callback/drain_antimagic, + datum/callback/block_magic, datum/callback/expiration, + datum/callback/check_blocking, ) @@ -67,11 +71,12 @@ src.antimagic_flags = antimagic_flags src.charges = charges src.inventory_flags = inventory_flags - src.drain_antimagic = drain_antimagic + src.block_magic = block_magic src.expiration = expiration + src.check_blocking = check_blocking /datum/component/anti_magic/Destroy(force) - drain_antimagic = null + block_magic = null expiration = null return ..() @@ -124,6 +129,9 @@ /datum/component/anti_magic/proc/block_receiving_magic(mob/living/carbon/source, casted_magic_flags, charge_cost, list/antimagic_sources) SIGNAL_HANDLER + if(check_blocking && !check_blocking.Invoke()) + return NONE + // We do not block this type of magic, good day if(!(casted_magic_flags & antimagic_flags)) return NONE @@ -135,8 +143,8 @@ // Block success! Add this parent to the list of antimagic sources antimagic_sources += parent + block_magic?.Invoke(source, parent) if((charges != INFINITY) && charge_cost > 0) - drain_antimagic?.Invoke(source, parent) charges -= charge_cost if(charges <= 0) expiration?.Invoke(source, parent) diff --git a/code/modules/antagonists/heretic/items/corrupted_organs.dm b/code/modules/antagonists/heretic/items/corrupted_organs.dm index 1035a006a9c..5ffe055de80 100644 --- a/code/modules/antagonists/heretic/items/corrupted_organs.dm +++ b/code/modules/antagonists/heretic/items/corrupted_organs.dm @@ -191,13 +191,14 @@ return ..() - /// Occasionally bombards you with spooky hands and lets everyone hear your pulse. /obj/item/organ/heart/corrupt name = "corrupt heart" desc = "What corruption is this spreading along with the blood?" beat_noise = "THE THUMPTHUMPTHUMPING OF THE CHISEL ON THE GLASS. OPEN THE FUTURE SHATTER THE-" organ_flags = parent_type::organ_flags | ORGAN_HAZARDOUS + cell_line = CELL_LINE_ORGAN_HEART_CURSED + cells_minimum = 2 //guarantees we always get sacred heart and corrupted heart cells /// How long until the next heart? COOLDOWN_DECLARE(hand_cooldown) diff --git a/code/modules/cargo/packs/medical.dm b/code/modules/cargo/packs/medical.dm index 53b46385065..2cae5797fa2 100644 --- a/code/modules/cargo/packs/medical.dm +++ b/code/modules/cargo/packs/medical.dm @@ -212,3 +212,17 @@ crate_name = "body freezer" crate_type = /obj/structure/closet/crate/secure/freezer +/datum/supply_pack/medical/organ_growing + name = "Organ Growing Kit" + desc = "All the tools you need to grow organs at home! Besides letting you grow standard organs from tissue samples, \ + you might find mutant cells which can be further proliferated to grow strange properties." + cost = CARGO_CRATE_VALUE * 5 + contains = list( + /obj/machinery/vatgrower/small/unanchored, + /obj/item/biopsy_tool/organ, + /obj/item/storage/box/petridish, + /obj/structure/microscope/unanchored, + /obj/item/reagent_containers/cup/mortar, + /obj/item/pestle, + ) + crate_name = "organ growing kit" diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index d6776a0ea4d..b7530505ed7 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -710,7 +710,7 @@ antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY, \ inventory_flags = ITEM_SLOT_OCLOTHING, \ charges = 1, \ - drain_antimagic = CALLBACK(src, PROC_REF(drain_antimagic)), \ + block_magic = CALLBACK(src, PROC_REF(drain_antimagic)), \ expiration = CALLBACK(src, PROC_REF(decay)) \ ) diff --git a/code/modules/clothing/head/tinfoilhat.dm b/code/modules/clothing/head/tinfoilhat.dm index febc379ac0a..daaf90378c9 100644 --- a/code/modules/clothing/head/tinfoilhat.dm +++ b/code/modules/clothing/head/tinfoilhat.dm @@ -25,7 +25,7 @@ antimagic_flags = MAGIC_RESISTANCE_MIND, \ inventory_flags = ITEM_SLOT_HEAD, \ charges = 6, \ - drain_antimagic = CALLBACK(src, PROC_REF(drain_antimagic)), \ + block_magic = CALLBACK(src, PROC_REF(drain_antimagic)), \ expiration = CALLBACK(src, PROC_REF(warp_up)) \ ) diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index a52e72af7b9..4472408716d 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -816,7 +816,7 @@ antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY, \ inventory_flags = ITEM_SLOT_OCLOTHING, \ charges = 1, \ - drain_antimagic = CALLBACK(src, PROC_REF(drain_antimagic)), \ + block_magic = CALLBACK(src, PROC_REF(drain_antimagic)), \ expiration = CALLBACK(src, PROC_REF(decay)) \ ) diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index 1dfe052a982..1ca10a290f6 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -467,8 +467,8 @@ GLOBAL_LIST_INIT(fish_compatible_fluid_types, list( . = ..() if(!reagents) return - reagents.convert_reagent(/datum/reagent/consumable/nutriment/protein, /datum/reagent/consumable/liquidgibs, 0.4, include_source_subtypes = TRUE) - reagents.convert_reagent(/datum/reagent/consumable/nutriment/protein, /datum/reagent/blood, 0.2, include_source_subtypes = TRUE) + reagents.convert_reagent(/datum/reagent/consumable/nutriment/protein, /datum/reagent/consumable/liquidgibs, multiplier = 0.4, include_source_subtypes = TRUE) + reagents.convert_reagent(/datum/reagent/consumable/nutriment/protein, /datum/reagent/blood, multiplier = 0.2, include_source_subtypes = TRUE) ///When processed, the reagents inside this fish will be passed to the created atoms. /obj/item/fish/UsedforProcessing(mob/living/user, obj/item/used_item, list/chosen_option, list/created_atoms) diff --git a/code/modules/fishing/fish/types/rift.dm b/code/modules/fishing/fish/types/rift.dm index c716c336e11..eb72c82ac44 100644 --- a/code/modules/fishing/fish/types/rift.dm +++ b/code/modules/fishing/fish/types/rift.dm @@ -774,7 +774,7 @@ antimagic_flags = MAGIC_RESISTANCE_MIND, \ inventory_flags = null, \ charges = maxHealth * 0.1, \ - drain_antimagic = CALLBACK(src, PROC_REF(on_drain_magic)), \ + block_magic = CALLBACK(src, PROC_REF(on_drain_magic)), \ expiration = CALLBACK(src, PROC_REF(on_expire)), \ ) diff --git a/code/modules/hydroponics/unique_plant_genes.dm b/code/modules/hydroponics/unique_plant_genes.dm index c35bf6e7a0e..1691dbecfb1 100644 --- a/code/modules/hydroponics/unique_plant_genes.dm +++ b/code/modules/hydroponics/unique_plant_genes.dm @@ -21,7 +21,7 @@ antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY, \ inventory_flags = ITEM_SLOT_HANDS, \ charges = shield_uses, \ - drain_antimagic = CALLBACK(src, PROC_REF(drain_antimagic)), \ + block_magic = CALLBACK(src, PROC_REF(drain_antimagic)), \ expiration = CALLBACK(src, PROC_REF(expire)), \ ) diff --git a/code/modules/reagents/chemistry/holder/holder.dm b/code/modules/reagents/chemistry/holder/holder.dm index 3e505c830b3..179e328928b 100644 --- a/code/modules/reagents/chemistry/holder/holder.dm +++ b/code/modules/reagents/chemistry/holder/holder.dm @@ -325,12 +325,14 @@ * * * [source_reagent_typepath][/datum/reagent] - the typepath of the reagent you are trying to convert * * [target_reagent_typepath][/datum/reagent] - the final typepath the source_reagent_typepath will be converted into + * * conversion_volume - how much of the reagent volume to convert. -1 for all * * multiplier - the multiplier applied on the source_reagent_typepath volume before converting * * include_source_subtypes- if TRUE will convert all subtypes of source_reagent_typepath into target_reagent_typepath as well */ /datum/reagents/proc/convert_reagent( datum/reagent/source_reagent_typepath, datum/reagent/target_reagent_typepath, + conversion_volume = -1, multiplier = 1, include_source_subtypes = FALSE, keep_data = FALSE, @@ -358,6 +360,17 @@ else if(!istype(cached_reagent, source_reagent_typepath)) continue + if(conversion_volume != -1) + if(cached_reagent.volume > conversion_volume) + remove_reagent(cached_reagent.type, conversion_volume) + weighted_volume += conversion_volume + break + + weighted_volume += cached_reagent.volume + conversion_volume -= cached_reagent.volume + del_reagent(cached_reagent.type) + continue + //compute average of everything reagent_volume = cached_reagent.volume weighted_purity += cached_reagent.purity * reagent_volume diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 21b76d4f22c..6c9ae29d4c3 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -74,7 +74,7 @@ mytray.mutation_roll(user) mytray.adjust_toxic(3) //It is still toxic, mind you, but not to the same degree. -/datum/reagent/mutagen/used_on_fish(obj/item/fish/fish) +/datum/reagent/toxin/mutagen/used_on_fish(obj/item/fish/fish) ADD_TRAIT(fish, TRAIT_FISH_MUTAGENIC, type) addtimer(TRAIT_CALLBACK_REMOVE(fish, TRAIT_FISH_MUTAGENIC, type), fish.feeding_frequency * 0.8, TIMER_UNIQUE|TIMER_OVERRIDE) return TRUE diff --git a/code/modules/research/xenobiology/vatgrowing/biopsy_tool.dm b/code/modules/research/xenobiology/vatgrowing/biopsy_tool.dm index d04a7f852da..cfc3e56bc5b 100644 --- a/code/modules/research/xenobiology/vatgrowing/biopsy_tool.dm +++ b/code/modules/research/xenobiology/vatgrowing/biopsy_tool.dm @@ -5,15 +5,26 @@ icon = 'icons/obj/science/vatgrowing.dmi' icon_state = "biopsy" worn_icon_state = "biopsy" + base_icon_state = "biopsy" + /// Whether or not we can swab objects + var/can_swap_objects = FALSE ///Adds the swabbing component to the biopsy tool /obj/item/biopsy_tool/Initialize(mapload) . = ..() - AddComponent(/datum/component/swabbing, FALSE, FALSE, TRUE, CALLBACK(src, PROC_REF(update_swab_icon)), max_items = 1) - + AddComponent(/datum/component/swabbing, can_swap_objects, FALSE, TRUE, CALLBACK(src, PROC_REF(update_swab_icon)), max_items = 1) /obj/item/biopsy_tool/proc/update_swab_icon(list/swabbed_items) if(LAZYLEN(swabbed_items)) - icon_state = "biopsy_full" + icon_state = base_icon_state + "_full" else - icon_state = "biopsy" + icon_state = base_icon_state + +/obj/item/biopsy_tool/organ + name = "tissue biopsy tool" + desc = "Used to retrieve cell lines from organ tissue and organisms. Don't worry, it won't sting." + icon_state = "biopsy_organ" + base_icon_state = "biopsy_organ" + + can_swap_objects = TRUE + diff --git a/code/modules/research/xenobiology/vatgrowing/microscope.dm b/code/modules/research/xenobiology/vatgrowing/microscope.dm index c3c7369981e..7be87c0d1e9 100644 --- a/code/modules/research/xenobiology/vatgrowing/microscope.dm +++ b/code/modules/research/xenobiology/vatgrowing/microscope.dm @@ -120,6 +120,9 @@ balloon_alert(user, "dish removed") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN +/obj/structure/microscope/unanchored + anchored = FALSE + /datum/crafting_recipe/microscope name = "Microscope" result = /obj/structure/microscope diff --git a/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm b/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm index 72cb3031841..4a564b58b13 100644 --- a/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm +++ b/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm @@ -112,6 +112,7 @@ if(vat_creature.mutate()) return mutate_color(thing) + SEND_SIGNAL(vat.biological_sample, COMSIG_SAMPLE_DEPOSITED, thing) ///Overriden to show more info like needs, supplementary and supressive reagents and also growth. /datum/micro_organism/cell_line/get_details(show_details) @@ -137,23 +138,30 @@ var/hue_shift = 1 //This affix is added to the name of the atom, to help players understand and converse about the different rarity levels. var/rarity_affix = "glitched" + // for adding some unique effects for subtypes, should you wish to do so + var/strength_factor = 1 switch(rand(1, 100)) if(1 to 35) //35% chance : painted hue_shift = 0.15 rarity_affix = "painted" + strength_factor = 1.2 if(36 to 70) //35% chance : mutant hue_shift = 0.85 //this value is equivalent in distance as the painted tier, just in the other direction. rarity_affix = "mutant" + strength_factor = 1.4 if(71 to 83) //13% chance : rare hue_shift = 0.3 rarity_affix = "rare" + strength_factor = 1.6 if(84 to 95) //12% chance : shiny hue_shift = 0.7 rarity_affix = "shiny" + strength_factor = 2 if(96 to 100) //5% chance : mutant king hue_shift = 0.5 //Best in show, most extreme color change. rarity_affix = "mutant king" //The name is up to debate, since cyto is all about freaky creatures I thought it was good choice over something like "cosmic" or "regal". + strength_factor = 2.5 var/list/mutant_shift_matrix = list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, hue_shift,0,0,0) //matrix for our colour shifting. @@ -163,3 +171,5 @@ if(isliving(beautiful_mutant)) //update the real name var if it's actually a living mob var/mob/living/living_mutie = beautiful_mutant living_mutie.real_name = living_mutie.name + + return strength_factor diff --git a/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/organs.dm b/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/organs.dm new file mode 100644 index 00000000000..2685885644c --- /dev/null +++ b/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/organs.dm @@ -0,0 +1,197 @@ +/// Cell lines for organ cytology, letting you duplicate or grow new mutant strains! +/datum/micro_organism/cell_line/organs + desc = "dense tissue" + growth_rate = 1 + consumption_rate = REAGENTS_METABOLISM + +/datum/micro_organism/cell_line/organs/mutate_color(atom/beautiful_mutant) + . = ..() + + if(isorgan(beautiful_mutant)) + var/obj/item/organ/organ = beautiful_mutant + // Rare affix organs get more health + organ.maxHealth *= . + +/datum/micro_organism/cell_line/organs/heart + desc = "dense heart tissue" + + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue) + + supplementary_reagents = list( + /datum/reagent/love = 6, + /datum/reagent/blood = 3, + /datum/reagent/consumable/nutriment = 1, + ) + + suppressive_reagents = list( + /datum/reagent/toxin/mutagen = -2, + ) + + resulting_atom = /obj/item/organ/heart + +/datum/micro_organism/cell_line/organs/heart/evolved + desc = "dense evolved heart tissue" + + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue) + + supplementary_reagents = list( + /datum/reagent/love = 6, + /datum/reagent/toxin/mutagen = 4, + /datum/reagent/blood = 3, + /datum/reagent/consumable/nutriment = 1, + ) + + resulting_atom = /obj/item/organ/heart/evolved + +/datum/micro_organism/cell_line/organs/heart/sacred + desc = "dense sacred heart tissue" + growth_rate = parent_type::growth_rate * 0.5 + + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue, /datum/reagent/water/holywater) + + supplementary_reagents = list( + /datum/reagent/love = 6, + /datum/reagent/blood = 3, + /datum/reagent/consumable/nutriment = 1, + ) + + resulting_atom = /obj/item/organ/heart/evolved/sacred + +/datum/micro_organism/cell_line/organs/heart/corrupt + desc = "dense corrupted heart tissue" + + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue) + + supplementary_reagents = list( + /datum/reagent/hellwater = 6, + /datum/reagent/blood = 3, + /datum/reagent/consumable/nutriment = 1, + ) + + suppressive_reagents = list( + /datum/reagent/water/holywater = -3, + ) + + resulting_atom = /obj/item/organ/heart/corrupt + +/datum/micro_organism/cell_line/organs/lungs + desc = "dense lung tissue" + + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue) + + supplementary_reagents = list( + /datum/reagent/medicine/salbutamol = 6, + /datum/reagent/blood = 3, + /datum/reagent/consumable/nutriment = 1, + ) + + suppressive_reagents = list( + /datum/reagent/toxin/mutagen = -2, + ) + + resulting_atom = /obj/item/organ/lungs + +/datum/micro_organism/cell_line/organs/lungs/evolved + desc = "dense evolved lung tissue" + + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue) + + supplementary_reagents = list( + /datum/reagent/medicine/salbutamol = 6, + /datum/reagent/toxin/mutagen = 4, + /datum/reagent/blood = 3, + /datum/reagent/consumable/nutriment = 1, + ) + + resulting_atom = /obj/item/organ/lungs/evolved + +/datum/micro_organism/cell_line/organs/liver + desc = "dense liver tissue" + + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue) + + supplementary_reagents = list( + /datum/reagent/iron = 6, + /datum/reagent/blood = 3, + /datum/reagent/consumable/nutriment = 1, + ) + + suppressive_reagents = list( + /datum/reagent/toxin/mutagen = -2, + ) + + resulting_atom = /obj/item/organ/liver + +/datum/micro_organism/cell_line/organs/liver/evolved + desc = "dense evolved liver tissue" + + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue) + + supplementary_reagents = list( + /datum/reagent/iron = 6, + /datum/reagent/toxin/mutagen = 4, + /datum/reagent/blood = 3, + /datum/reagent/consumable/nutriment = 1, + ) + + resulting_atom = /obj/item/organ/liver/evolved + +/datum/micro_organism/cell_line/organs/liver/bloody + desc = "spongy liver tissue" + + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue, /datum/reagent/blood) + + supplementary_reagents = list( + /datum/reagent/iron = 6, + /datum/reagent/blood = 3, + /datum/reagent/consumable/nutriment = 1, + ) + + resulting_atom = /obj/item/organ/liver/bloody + +/datum/micro_organism/cell_line/organs/liver/distillery + desc = "alcoholic liver tissue" + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue, /datum/reagent/consumable/ethanol) + + supplementary_reagents = list( + /datum/reagent/iron = 6, + /datum/reagent/blood = 3, + /datum/reagent/consumable/nutriment = 1, + ) + + suppressive_reagents = list( + /datum/reagent/water = -6, //are you trying to poison me or something? + ) + + resulting_atom = /obj/item/organ/liver/distillery + +/datum/micro_organism/cell_line/organs/stomach + desc = "dense stomach tissue" + + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue/stomach_lining) + + supplementary_reagents = list( + /datum/reagent/consumable/nutriment/organ_tissue = 3, + /datum/reagent/blood = 3, + /datum/reagent/consumable/nutriment = 1, + ) + + suppressive_reagents = list( + /datum/reagent/toxin/mutagen = -2, + ) + + resulting_atom = /obj/item/organ/stomach + +/datum/micro_organism/cell_line/organs/stomach/evolved + desc = "dense evolved stomach tissue" + + required_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue/stomach_lining) + + supplementary_reagents = list( + /datum/reagent/toxin/mutagen = 4, + /datum/reagent/consumable/nutriment/organ_tissue = 3, + /datum/reagent/consumable/nutriment = 3, + /datum/reagent/blood = 3, + ) + + resulting_atom = /obj/item/organ/stomach/evolved diff --git a/code/modules/research/xenobiology/vatgrowing/vatgrower.dm b/code/modules/research/xenobiology/vatgrowing/vatgrower.dm index 621f72f61f0..4f65a3d9240 100644 --- a/code/modules/research/xenobiology/vatgrowing/vatgrower.dm +++ b/code/modules/research/xenobiology/vatgrowing/vatgrower.dm @@ -15,13 +15,16 @@ var/datum/biological_sample/biological_sample ///If the vat will restart the sample upon completion var/resampler_active = FALSE + /// Whether or not to add the plumbing component and related stuff + var/use_plumbing = TRUE /obj/machinery/vatgrower/Initialize(mapload, bolt, layer) . = ..() create_reagents(reagent_volume, reagent_flags) - AddComponent(/datum/component/simple_rotation) - AddComponent(/datum/component/plumbing/simple_demand) + if(use_plumbing) + AddComponent(/datum/component/simple_rotation) + AddComponent(/datum/component/plumbing/simple_demand) var/static/list/hovering_item_typechecks = list( /obj/item/petri_dish = list( @@ -136,9 +139,9 @@ var/static/image/off_overlay var/static/image/emissive_overlay if(isnull(on_overlay)) - on_overlay = iconstate2appearance(icon, "growing_vat_on") - off_overlay = iconstate2appearance(icon, "growing_vat_off") - emissive_overlay = emissive_appearance(icon, "growing_vat_glow", src, alpha = src.alpha) + on_overlay = iconstate2appearance(icon, "[icon_state]_on") + off_overlay = iconstate2appearance(icon, "[icon_state]_off") + emissive_overlay = emissive_appearance(icon, "[icon_state]_glow", src, alpha = src.alpha) . += emissive_overlay if(is_operational) if(resampler_active) @@ -148,11 +151,11 @@ if(!reagents.total_volume) return var/reagentcolor = mix_color_from_reagents(reagents.reagent_list) - var/mutable_appearance/base_overlay = mutable_appearance(icon, "vat_reagent", appearance_flags = RESET_COLOR|KEEP_APART) + var/mutable_appearance/base_overlay = mutable_appearance(icon, "[icon_state]_reagent", appearance_flags = RESET_COLOR|KEEP_APART) base_overlay.color = reagentcolor . += base_overlay if(biological_sample && is_operational) - var/mutable_appearance/bubbles_overlay = mutable_appearance(icon, "vat_bubbles") + var/mutable_appearance/bubbles_overlay = mutable_appearance(icon, "[icon_state]_bubbles") . += bubbles_overlay /obj/machinery/vatgrower/emag_act(mob/user, obj/item/card/emag/emag_card) @@ -170,6 +173,43 @@ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), get_turf(src), 'sound/effects/servostep.ogg', 100, 1), 1.5 SECONDS) biological_sample.reset_sample() else - UnregisterSignal(biological_sample, COMSIG_SAMPLE_GROWTH_COMPLETED) QDEL_NULL(biological_sample) update_appearance() + +/obj/machinery/vatgrower/small + name = "small growing vat" + desc = "Tastes just like the chef's soup. Fit for growing organ tissue samples." + icon_state = "growing_vat_small" + density = FALSE + pass_flags_self = PASSMACHINE | LETPASSTHROW + circuit = null + + anchored_tabletop_offset = 4 + pass_flags = PASSTABLE + use_plumbing = FALSE + + reagent_volume = 50 + +/obj/machinery/vatgrower/small/emag_act(mob/user, obj/item/card/emag/emag_card) + return + +/obj/machinery/vatgrower/small/deposit_sample(mob/user, obj/item/petri_dish/petri) + . = ..() + + if(. != ITEM_INTERACT_SUCCESS) + return + + RegisterSignal(biological_sample, COMSIG_SAMPLE_DEPOSITED, PROC_REF(on_sample_deposited)) + +/obj/machinery/vatgrower/small/proc/on_sample_deposited(datum/biological_sample/sample, atom/thing) + SIGNAL_HANDLER + + if(isliving(thing)) + var/mob/living/living_type = thing + if(living_type.mob_size > MOB_SIZE_SMALL) + explosion(src, light_impact_range = 2, explosion_cause = "growing vat too small") + qdel(src) + return + +/obj/machinery/vatgrower/small/unanchored + anchored = FALSE diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index 534146d4d99..ac602413eee 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -68,6 +68,12 @@ var/failing_desc = "has decayed for too long, and has turned a sickly color. It probably won't work without repairs." /// Assoc list of alternate zones where this can organ be slotted to organ slot for that zone var/list/valid_zones = null + /// The cell line we can spawn on us + var/cell_line = null + /// The minimum cells we can spawn + var/cells_minimum = 0 + /// The maximum cells we can spawn + var/cells_maximum = 0 // Players can look at prefs before atoms SS init, and without this // they would not be able to see external organs, such as moth wings. @@ -90,6 +96,10 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) if(bodypart_overlay) setup_bodypart_overlay() + + if(cell_line && (organ_flags & ORGAN_ORGANIC)) + AddElement(/datum/element/swabable, cell_line, cell_line_amount = rand(cells_minimum, cells_maximum)) + START_PROCESSING(SSobj, src) /obj/item/organ/Destroy() diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm index 5a70e385258..0e00dce883a 100644 --- a/code/modules/surgery/organs/internal/heart/_heart.dm +++ b/code/modules/surgery/organs/internal/heart/_heart.dm @@ -21,6 +21,10 @@ // Love is stored in the heart. food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5, /datum/reagent/love = 2.5) + cell_line = CELL_LINE_ORGAN_HEART + cells_minimum = 1 + cells_maximum = 2 + // Heart attack code is in code/modules/mob/living/carbon/human/life.dm /// Whether the heart is currently beating. @@ -335,3 +339,69 @@ beat_noise = "the power of the podperson" // makes sense foodtype_flags = PODPERSON_ORGAN_FOODTYPES color = COLOR_LIME + +/// An improved version of the organic heart, with more health and more "keeping you alive" potential +/obj/item/organ/heart/evolved + name = "evolved heart" + desc = "It beats ever strong." + icon_state = "heart-evolved-on" + base_icon_state = "heart-evolved" + + maxHealth = STANDARD_ORGAN_THRESHOLD * 1.2 + + /// Chance to heal per on_life + var/healing_probability = 10 + /// Base healing we receive per tick at 0 damage and for standard versions + var/base_healing = 1 + +/obj/item/organ/heart/evolved/on_life(seconds_per_tick, times_fired) + . = ..() + + if(prob(healing_probability * seconds_per_tick)) + var/damage_to_heal = base_healing * ((maxHealth - damage) / initial(maxHealth)) * seconds_per_tick + owner.heal_overall_damage(damage_to_heal, damage_to_heal, required_bodytype = BODYTYPE_ORGANIC) + + if(owner.stat == HARD_CRIT && !owner.has_reagent(/datum/reagent/medicine/atropine, 5)) + owner.reagents.add_reagent(/datum/reagent/medicine/atropine, 1 * seconds_per_tick) + +/// A weaker evolved heart, but can block magic in exchange for our organs health! +/obj/item/organ/heart/evolved/sacred + name = "sacred heart" + desc = "Your foul magics stand no chance against the power of LOVE!!!" + + icon_state = "heart-sacred-on" + base_icon_state = "heart-sacred" + + healing_probability = 5 + base_healing = 0.5 + + // How much damage each magic block deals to us + var/damage_per_block = 50 + +/obj/item/organ/heart/evolved/sacred/on_life(seconds_per_tick, times_fired) + . = ..() + + if(IS_CULTIST(owner)) + owner.reagents.add_reagent(/datum/reagent/water/holywater, 5 * seconds_per_tick) + +/obj/item/organ/heart/evolved/sacred/on_mob_insert(mob/living/carbon/receiver, special, movement_flags) + . = ..() + + receiver.AddComponent(/datum/component/anti_magic, block_magic = CALLBACK(src, PROC_REF(on_blocked)), check_blocking = CALLBACK(src, PROC_REF(check_block))) + +/obj/item/organ/heart/evolved/sacred/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) + . = ..() + + qdel(organ_owner.GetComponent(/datum/component/anti_magic)) + +/// When we blocked damage, do PAIN on us +/obj/item/organ/heart/evolved/sacred/proc/on_blocked() + apply_organ_damage(damage_per_block) + owner.vomit(VOMIT_CATEGORY_BLOOD) + playsound(owner, 'sound/effects/health/slowbeat.ogg', 80) + +/// We don't block magic if it would kill our heart +/obj/item/organ/heart/evolved/sacred/proc/check_block() + if(maxHealth - damage <= damage_per_block) + return FALSE + return TRUE diff --git a/code/modules/surgery/organs/internal/liver/_liver.dm b/code/modules/surgery/organs/internal/liver/_liver.dm index 6e6444edfb7..f6995237c69 100755 --- a/code/modules/surgery/organs/internal/liver/_liver.dm +++ b/code/modules/surgery/organs/internal/liver/_liver.dm @@ -18,6 +18,10 @@ food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5, /datum/reagent/iron = 5) grind_results = list(/datum/reagent/consumable/nutriment/peptides = 5) + cell_line = CELL_LINE_ORGAN_LIVER + cells_minimum = 1 + cells_maximum = 2 + /// Affects how much damage the liver takes from alcohol var/alcohol_tolerance = ALCOHOL_RATE /// The maximum volume of toxins the liver will ignore @@ -328,6 +332,60 @@ organ_owner.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM * seconds_per_tick) return COMSIG_MOB_STOP_REAGENT_TICK +/obj/item/organ/liver/evolved + name = "evolved liver" + desc = "A more robust liver, better at everything." + + icon_state = "evolved-liver" + + alcohol_tolerance = ALCOHOL_RATE * 0.5 + maxHealth = 1.2 * STANDARD_ORGAN_THRESHOLD + toxTolerance = 6 //can shrug off up to 6u of toxins + liver_resistance = 1.5 * LIVER_DEFAULT_TOX_RESISTANCE + +/obj/item/organ/liver/bloody + name = "leaky liver" + desc = "An extra spongy liver, only slightly better than a normal liver, but with an increased ability to replenish blood." + + icon_state = "leaky-liver" + + maxHealth = 1.1 * STANDARD_ORGAN_THRESHOLD + alcohol_tolerance = ALCOHOL_RATE * 0.8 + toxTolerance = LIVER_DEFAULT_TOX_TOLERANCE + 1 + liver_resistance = 1.1 * LIVER_DEFAULT_TOX_RESISTANCE + +/obj/item/organ/liver/bloody/on_life(seconds_per_tick, times_fired) + . = ..() + + if(owner.blood_volume < BLOOD_VOLUME_NORMAL) + owner.blood_volume += 4 * seconds_per_tick + +/// Convert all non-alcoholic drinks into alcohol +/obj/item/organ/liver/distillery + name = "alcoholics delight" + desc = "The perfect liver, distilling non-alcoholic reagents into alcohol whenever possible." + + icon_state = "liver-distillery" + + organ_traits = list(TRAIT_ALCOHOL_TOLERANCE) + + alcohol_tolerance = ALCOHOL_RATE * 0.1 + + /// Volume that is converted per second + var/ethanol_conversion = 0.2 + /// What to convert stuff into + var/convert_into = /datum/reagent/consumable/ethanol + +/obj/item/organ/liver/distillery/on_life(seconds_per_tick, times_fired) + . = ..() + + for(var/datum/reagent/reagent as anything in owner.reagents.reagent_list) + // Already alcohol + if(istype(reagent, convert_into)) + continue + + owner.reagents.convert_reagent(reagent.type, convert_into, ethanol_conversion) + #undef LIVER_DEFAULT_TOX_TOLERANCE #undef LIVER_DEFAULT_TOX_RESISTANCE #undef LIVER_FAILURE_STAGE_SECONDS diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm index 91933262ac5..27a88a6d94e 100644 --- a/code/modules/surgery/organs/internal/lungs/_lungs.dm +++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm @@ -18,6 +18,10 @@ low_threshold_cleared = span_info("You can breathe normally again.") high_threshold_cleared = span_info("The constriction around your chest loosens as your breathing calms down.") + cell_line = CELL_LINE_ORGAN_LUNGS + cells_minimum = 1 + cells_maximum = 2 + var/failed = FALSE var/operated = FALSE //whether we can still have our damages fixed through surgery @@ -1077,6 +1081,17 @@ foodtype_flags = PODPERSON_ORGAN_FOODTYPES color = COLOR_LIME +/obj/item/organ/lungs/evolved + name = "evolved lungs" + desc = "A pair of lungs, with an organic filtering system and a stronger musculature." + + icon_state = "lungs-evolved" + + safe_plasma_max = 8 + safe_co2_max = 8 + maxHealth = 1.2 * STANDARD_ORGAN_THRESHOLD + safe_oxygen_min = 8 + #undef BREATH_RELATIONSHIP_INITIAL_GAS #undef BREATH_RELATIONSHIP_CONVERT #undef BREATH_RELATIONSHIP_MULTIPLIER diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm index 12f76c6befd..658598ee7d7 100644 --- a/code/modules/surgery/organs/internal/stomach/_stomach.dm +++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm @@ -24,6 +24,10 @@ //This is a reagent user and needs more then the 10u from edible component reagent_vol = 1000 + cell_line = CELL_LINE_ORGAN_STOMACH + cells_minimum = 1 + cells_maximum = 2 + ///The rate that disgust decays var/disgust_metabolism = 1 @@ -565,4 +569,13 @@ movement_type = PHASING organ_flags = parent_type::organ_flags | ORGAN_GHOST +/obj/item/organ/stomach/evolved + name = "evolved stomach" + desc = "It can draw nutrients from your food even harder!" + icon_state = "stomach-evolved" + + maxHealth = 1.2 * STANDARD_ORGAN_THRESHOLD + disgust_metabolism = 2.5 + metabolism_efficiency = 0.08 + #undef STOMACH_METABOLISM_CONSTANT diff --git a/icons/obj/medical/organs/organs.dmi b/icons/obj/medical/organs/organs.dmi index 5f045ac4f9b..cadfac4b992 100644 Binary files a/icons/obj/medical/organs/organs.dmi and b/icons/obj/medical/organs/organs.dmi differ diff --git a/icons/obj/science/vatgrowing.dmi b/icons/obj/science/vatgrowing.dmi index ec8b5dcb6ac..4d25046ea76 100644 Binary files a/icons/obj/science/vatgrowing.dmi and b/icons/obj/science/vatgrowing.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 71739f8c613..d16849ff205 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -6095,6 +6095,7 @@ #include "code\modules\research\xenobiology\vatgrowing\samples\_micro_organism.dm" #include "code\modules\research\xenobiology\vatgrowing\samples\_sample.dm" #include "code\modules\research\xenobiology\vatgrowing\samples\cell_lines\common.dm" +#include "code\modules\research\xenobiology\vatgrowing\samples\cell_lines\organs.dm" #include "code\modules\research\xenobiology\vatgrowing\samples\viruses\_virus.dm" #include "code\modules\security_levels\keycard_authentication.dm" #include "code\modules\security_levels\security_level_datums.dm"