diff --git a/code/__SPLURTCODE/DEFINES/signals.dm b/code/__SPLURTCODE/DEFINES/signals.dm index c5e4f45aaca8..2ed2dabaca9e 100644 --- a/code/__SPLURTCODE/DEFINES/signals.dm +++ b/code/__SPLURTCODE/DEFINES/signals.dm @@ -3,6 +3,7 @@ #define COMSIG_ORGAN_REMOVED "organ_removed" #define COMSIG_MOB_CLIMAX "mob_coomed" #define COMSIG_OBJ_WRITTEN_ON "written_on" +#define COMSIG_MACHINERY_INCUBATOR_PROCESS "incubator_proc" #define COMSIG_MOB_GENITAL_TRY_INSERTING "mob_genital_try_inserting" //Handles external restrictions to the inserting diff --git a/modular_splurt/code/datums/components/pregnancy.dm b/modular_splurt/code/datums/components/pregnancy.dm index 8ebde1b15990..0cf9399242ae 100644 --- a/modular_splurt/code/datums/components/pregnancy.dm +++ b/modular_splurt/code/datums/components/pregnancy.dm @@ -8,12 +8,10 @@ var/obj/item/organ/container var/mob/living/carrier + var/obj/machinery/incubator/incubator_carrier - var/datum/dna/father_dna - var/datum/dna/mother_dna - - var/list/mother_features - var/list/father_features + var/datum/dna/egg_dna + var/list/egg_features = list() var/mother_name @@ -56,36 +54,11 @@ if(nads.owner) carrier = nads.owner - if(iscarbon(_father)) - var/mob/living/carbon/cardad = _father - father_dna = new - cardad.dna.copy_dna(father_dna) - - if(iscarbon(_mother)) - var/mob/living/carbon/carmom = _mother - mother_dna = new - carmom.dna.copy_dna(mother_dna) + determine_egg_dna(_mother, _father) + determine_egg_features(_mother, _father) mother_name = _mother.real_name - if(ishuman(_father)) - var/mob/living/carbon/human/cardad = _father - LAZYINITLIST(father_features) - father_features["skin_tone"] = cardad.skin_tone - father_features["hair_color"] = cardad.hair_color - father_features["facial_hair_color"] = cardad.facial_hair_color - father_features["left_eye_color"] = cardad.left_eye_color - father_features["right_eye_color"] = cardad.right_eye_color - - if(ishuman(_mother)) - var/mob/living/carbon/human/carmom = _mother - LAZYINITLIST(mother_features) - mother_features["skin_tone"] = carmom.skin_tone - mother_features["hair_color"] = carmom.hair_color - mother_features["facial_hair_color"] = carmom.facial_hair_color - mother_features["left_eye_color"] = carmom.left_eye_color - mother_features["right_eye_color"] = carmom.right_eye_color - pregnancy_inflation = carrier?.client?.prefs?.pregnancy_inflation pregnancy_breast_growth = carrier?.client?.prefs?.pregnancy_breast_growth @@ -120,6 +93,12 @@ if(oviposition) RegisterSignal(carrier, COMSIG_MOB_CLIMAX, .proc/on_climax) +/datum/component/pregnancy/proc/register_machine() + RegisterSignal(incubator_carrier, COMSIG_MACHINERY_INCUBATOR_PROCESS, .proc/handle_machine_incubator) + +/datum/component/pregnancy/proc/unregister_machine() + UnregisterSignal(incubator_carrier, COMSIG_MACHINERY_INCUBATOR_PROCESS, .proc/handle_machine_incubator) + /datum/component/pregnancy/proc/unregister_carrier() UnregisterSignal(carrier, COMSIG_MOB_DEATH) UnregisterSignal(carrier, COMSIG_LIVING_BIOLOGICAL_LIFE) @@ -132,6 +111,60 @@ generic_pragency_end() return ..() + +/datum/component/pregnancy/proc/determine_egg_dna(mob/living/_mother, mob/living/_father) + var/mob/living/carbon/father = _father + var/mob/living/carbon/mother = _mother + if(iscarbon(father) && iscarbon(_mother)) + egg_dna = mother.dna.transfer_identity_random_dna_only(father.dna) + else if(iscarbon(father)) + egg_dna = new + egg_dna.initialize_dna() + egg_dna = father.dna.transfer_identity_random_dna_only(egg_dna) + else if(iscarbon(mother)) + egg_dna = new + egg_dna.initialize_dna() + egg_dna = mother.dna.transfer_identity_random_dna_only(egg_dna) + +/datum/component/pregnancy/proc/determine_egg_features(mob/living/_mother, mob/living/_father) + if(ishuman(_father) && ishuman(_mother)) + var/mob/living/carbon/human/cardad = _father + var/mob/living/carbon/human/carmom = _mother + var/list/father_features = list() + var/list/mother_features = list() + + father_features["skin_tone"] = cardad.skin_tone + father_features["hair_color"] = cardad.hair_color + father_features["facial_hair_color"] = cardad.facial_hair_color + father_features["left_eye_color"] = cardad.left_eye_color + father_features["right_eye_color"] = cardad.right_eye_color + + mother_features["skin_tone"] = carmom.skin_tone + mother_features["hair_color"] = carmom.hair_color + mother_features["facial_hair_color"] = carmom.facial_hair_color + mother_features["left_eye_color"] = carmom.left_eye_color + mother_features["right_eye_color"] = carmom.right_eye_color + + transfer_randomized_list(egg_features, mother_features, father_features) + + else if(ishuman(_father)) + var/mob/living/carbon/human/cardad = _father + + egg_features["skin_tone"] = cardad.skin_tone + egg_features["hair_color"] = cardad.hair_color + egg_features["facial_hair_color"] = cardad.facial_hair_color + egg_features["left_eye_color"] = cardad.left_eye_color + egg_features["right_eye_color"] = cardad.right_eye_color + + else if(ishuman(_mother)) + var/mob/living/carbon/human/carmom = _mother + + egg_features["skin_tone"] = carmom.skin_tone + egg_features["hair_color"] = carmom.hair_color + egg_features["facial_hair_color"] = carmom.facial_hair_color + egg_features["left_eye_color"] = carmom.left_eye_color + egg_features["right_eye_color"] = carmom.right_eye_color + /datum/component/pregnancy/proc/name_egg(datum/source, name) SIGNAL_HANDLER @@ -172,11 +205,25 @@ pregnancy_breast_growth = carrier.client?.prefs?.pregnancy_breast_growth register_carrier() generic_pragency_start() + else if(istype(destination, /obj/machinery/incubator)) + var/obj/machinery/incubator/inc = destination + incubator_carrier = inc + register_machine() else if(carrier) generic_pragency_end() unregister_carrier() + unregister_machine() carrier = null container = null + incubator_carrier = null + +/datum/component/pregnancy/proc/handle_machine_incubator() + SIGNAL_HANDLER + + if(COOLDOWN_FINISHED(src, stage_time)) + stage += 1 + stage = min(stage, max_stage) + COOLDOWN_START(src, stage_time, PREGNANCY_STAGE_DURATION) /datum/component/pregnancy/proc/handle_life(seconds) SIGNAL_HANDLER @@ -346,29 +393,17 @@ //not how genetics work but okay /datum/component/pregnancy/proc/determine_baby_dna(mob/living/carbon/human/babby) - if(mother_dna && father_dna) - mother_dna.transfer_identity_random(father_dna, babby) - else if(mother_dna && !father_dna) - mother_dna.transfer_identity_random(babby.dna, babby) - else if(!mother_dna && father_dna) - father_dna.transfer_identity_random(babby.dna, babby) + egg_dna.transfer_identity(babby) /datum/component/pregnancy/proc/determine_baby_features(mob/living/carbon/human/babby) var/list/final_features = list() - transfer_randomized_list(final_features, mother_features, father_features) - - if(final_features["skin_tone"]) - babby.skin_tone = final_features["skin_tone"] - if(final_features["hair_color"]) - babby.hair_color = final_features["hair_color"] - if(final_features["facial_hair_color"]) - babby.facial_hair_color = final_features["facial_hair_color"] - if(final_features["left_eye_color"]) - babby.left_eye_color = final_features["left_eye_color"] - if(final_features["right_eye_color"]) - babby.right_eye_color = final_features["right_eye_color"] + babby.skin_tone = egg_features["skin_tone"] + babby.hair_color = egg_features["hair_color"] + babby.facial_hair_color = egg_features["facial_hair_color"] + babby.left_eye_color = egg_features["left_eye_color"] + babby.right_eye_color = egg_features["right_eye_color"] babby.hair_style = pick("Bedhead", "Bedhead 2", "Bedhead 3") babby.facial_hair_style = "Shaved" diff --git a/modular_splurt/code/datums/dna.dm b/modular_splurt/code/datums/dna.dm index 1bccb678b2ed..b82b23487a30 100644 --- a/modular_splurt/code/datums/dna.dm +++ b/modular_splurt/code/datums/dna.dm @@ -94,10 +94,12 @@ if(prob(50)) destination.set_species(species.type, FALSE) + destination.dna.species = new species.type destination.dna.species.say_mod = species.say_mod destination.dna.custom_species = custom_species else destination.set_species(second_set.species.type, FALSE) + destination.dna.species = new second_set.species.type destination.dna.species.say_mod = second_set.species.say_mod destination.dna.custom_species = second_set.custom_species @@ -113,4 +115,26 @@ destination.updateappearance(icon_update=TRUE, mutcolor_update=TRUE, mutations_overlay_update=TRUE) +/datum/dna/proc/transfer_identity_random_dna_only(datum/dna/second_set) + var/datum/dna/destination= new + + TRANSFER_RANDOMIZED(destination.blood_type, blood_type, second_set.blood_type) + TRANSFER_RANDOMIZED(destination.skin_tone_override, skin_tone_override, second_set.skin_tone_override) + transfer_randomized_list(destination.features, features, second_set.features) + transfer_randomized_list(destination.temporary_mutations, temporary_mutations, second_set.temporary_mutations) + + if(prob(50)) + destination.species = new species.type + destination.species.say_mod = species.say_mod + destination.custom_species = custom_species + else + destination.species = new second_set.species.type + destination.species.say_mod = second_set.species.say_mod + destination.custom_species = second_set.custom_species + + destination.update_dna_identity() + destination.generate_dna_blocks() + + return destination + #undef TRANSFER_RANDOMIZED diff --git a/modular_splurt/code/game/machinery/embryonator.dm b/modular_splurt/code/game/machinery/embryonator.dm new file mode 100644 index 000000000000..e3883bb76236 --- /dev/null +++ b/modular_splurt/code/game/machinery/embryonator.dm @@ -0,0 +1,101 @@ +#define DUMMY_HUMAN_SLOT_DAYCARE "dummy_daycare" +/obj/machinery/embryonator + name = "Honeystone's Genetic Daycare" + desc = "A machine used to modify an egg's genetics. It has a nursery theme to it" + density = TRUE + layer = BELOW_OBJ_LAYER + icon = 'modular_splurt/icons/obj/machinery/honeystones_genetic_daycare.dmi' + icon_state = "empty" + use_power = ACTIVE_POWER_USE + active_power_usage = 150 + resistance_flags = FIRE_PROOF | ACID_PROOF + circuit = /obj/item/circuitboard/machine/embryonator + var/icon/child_image + var/obj/item/embryo_egg + + +/obj/machinery/embryonator/update_icon_state() + if((!is_operational()) || (!is_on)) + if (panel_open) + icon_state = "panel_open" + else + icon_state = "empty" + else + if(embryo_egg) + icon_state = "inserted" + else + icon_state = "empty" + +/obj/machinery/embryonator/attackby(obj/item/I, mob/user, params) + if(default_deconstruction_screwdriver(user, "panel-open", "empty", I)) + return + + else if(default_deconstruction_crowbar(I)) + return + + if(default_unfasten_wrench(user, I)) + return + + if(embryo_egg) + to_chat(user, span_warning("You can't insert [I] into [src], there is already an egg inside the machine")) + return ..() + + if(!I.GetComponent(/datum/component/pregnancy)) + to_chat(user, span_warning("You can't insert [I] into [src], the machine only accepts viable eggs")) + return ..() + + . = TRUE // no afterattack + if(panel_open) + to_chat(user, "You can't use the [src.name] while its panel is opened!") + return + if(!user.transferItemToLoc(I, src)) + return + to_chat(user, "You add [I] to [src].") + update_icon() + + embryo_egg = I + var/datum/component/pregnancy/preggo = I.GetComponent(/datum/component/pregnancy) + var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy(DUMMY_HUMAN_SLOT_HALLUCINATION) + mannequin.setDir(NORTH) + preggo.determine_baby_features(mannequin) + preggo.determine_baby_dna(mannequin) + mannequin.update_appearance() + child_image = getFlatIcon(mannequin) + unset_busy_human_dummy(DUMMY_HUMAN_SLOT_DAYCARE) + +/obj/machinery/embryonator/Destroy() + embryo_egg = null + for(var/obj/item/viable_egg in contents) + viable_egg.forceMove(get_turf(src)) + . = ..() + +/obj/machinery/embryonator/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Daycare") + ui.open() + ui.set_autoupdate(FALSE) + +/obj/machinery/embryonator/ui_static_data(mob/user) + . = ..() + +/obj/machinery/embryonator/ui_data(mob/user) + . = ..() + if(!embryo_egg) + child_image = null + return + .["image"] = icon2base64(child_image) + .["egg"] = REF(embryo_egg) + + .["features"] = list() + for(var/thing in embryo_egg.features) + var/list/data = list( + name = embryo_egg.features[thing], + setting = thing + ) + .["features"] += list(data) + + + +/obj/machinery/embryonator/ui_act(action, params) + . = ..() diff --git a/modular_splurt/code/game/machinery/incubator.dm b/modular_splurt/code/game/machinery/incubator.dm new file mode 100644 index 000000000000..484c8767b2c2 --- /dev/null +++ b/modular_splurt/code/game/machinery/incubator.dm @@ -0,0 +1,121 @@ +/obj/machinery/incubator + name = "Egg Incubator" + desc = "Used to incubate eggs for hatching. It's looks too big for regular chicken eggs..." + density = TRUE + layer = BELOW_OBJ_LAYER + icon = 'icons/obj/chemical.dmi' + icon_state = "smoke0" + use_power = IDLE_POWER_USE + active_power_usage = 200 + idle_power_usage = 20 + resistance_flags = FIRE_PROOF | ACID_PROOF + circuit = /obj/item/circuitboard/machine/incubator + + var/is_on = FALSE + +/obj/machinery/incubator/update_icon_state() + if((!is_operational()) || (!is_on)) + if (panel_open) + icon_state = "smoke0-o" + else + icon_state = "smoke0" + else + icon_state = "smoke1" + +/obj/machinery/incubator/attackby(obj/item/I, mob/user, params) + if(default_deconstruction_screwdriver(user, "smoke0-o", "smoke0", I)) + return + + else if(default_deconstruction_crowbar(I)) + return + + if(default_unfasten_wrench(user, I)) + return + + if(I.GetComponent(/datum/component/pregnancy)) + . = TRUE // no afterattack + if(panel_open) + to_chat(user, "You can't use the [src.name] while its panel is opened!") + return + if(!user.transferItemToLoc(I, src)) + return + to_chat(user, "You add [I] to [src].") + update_icon() + else + to_chat(user, "You can't insert [I] into [src], it only accepts viable eggs!") + return ..() + +/obj/machinery/incubator/CtrlClick(mob/user) + . = ..() + if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + return + toggle_power() + if(is_on) + to_chat(user, span_notice("You turn [src] on")) + else + to_chat(user, span_notice("You turn [src] off")) + +/obj/machinery/incubator/proc/toggle_power() + is_on = !is_on + if(is_on) + START_PROCESSING(SSmachines, src) + use_power = ACTIVE_POWER_USE + else + STOP_PROCESSING(SSmachines, src) + use_power = IDLE_POWER_USE + update_icon() + +/obj/machinery/incubator/process() + if(!is_on) + return PROCESS_KILL + if(!is_operational()) + is_on = FALSE + update_icon() + return PROCESS_KILL + for(var/obj/item/viable_egg in contents) + SEND_SIGNAL(viable_egg, COMSIG_MACHINERY_INCUBATOR_PROCESS) + +/obj/machinery/incubator/Destroy() + for(var/obj/item/viable_egg in contents) + viable_egg.forceMove(get_turf(src)) + . = ..() + +/obj/machinery/incubator/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "IncubatorControl", name) + ui.open() + +/obj/machinery/incubator/ui_data(mob/user) + . = list() + .["is_on"] = is_on + .["eggs"] = list() + for(var/obj/item/egg_item in contents) + var/datum/component/pregnancy/preggo = egg_item.GetComponent(/datum/component/pregnancy) + if(!preggo) + continue + var/list/data = list( + name = egg_item.name, + stage = preggo.stage / preggo.max_stage, + ref = REF(egg_item) + ) + .["eggs"] += list(data) + +/obj/machinery/incubator/ui_act(action, params) + if(..()) + return + switch(action) + if("toggle_power") + toggle_power() + if(is_on) + to_chat(usr, span_notice("You turn [src] on")) + use_power = ACTIVE_POWER_USE + else + to_chat(usr, span_notice("You turn [src] off")) + use_power = IDLE_POWER_USE + if("remove") + var/ref = params["ref"] + var/obj/item/eggo = locate(ref) in contents + if(!eggo) + return + usr.put_in_active_hand(eggo) diff --git a/modular_splurt/code/game/objects/items/circuitboards/machine_circuitboards.dm b/modular_splurt/code/game/objects/items/circuitboards/machine_circuitboards.dm index c14e62482e3d..2c7d35066230 100644 --- a/modular_splurt/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/modular_splurt/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -10,3 +10,25 @@ /obj/item/stock_parts/scanning_module = 2, /obj/item/stock_parts/matter_bin = 1 ) + +/obj/item/circuitboard/machine/incubator + name = "Egg Incubator (Machine Board)" + icon_state = "medical" + build_path = /obj/machinery/incubator + req_components = list( + /obj/item/reagent_containers/glass/beaker = 2, + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/sheet/glass = 2) + +/obj/item/circuitboard/machine/embryonator + name = "Honeystone's Genetic Daycare (Machine Board)" + icon_state = "medical" + build_path = /obj/machinery/embryonator + req_components = list( + /obj/item/stock_parts/scanning_module = 1, + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/sheet/glass = 1, + /obj/item/stack/cable_coil = 2) diff --git a/modular_splurt/code/modules/research/designs/medical_designs.dm b/modular_splurt/code/modules/research/designs/medical_designs.dm index 99343587b587..f38a3de2a38c 100644 --- a/modular_splurt/code/modules/research/designs/medical_designs.dm +++ b/modular_splurt/code/modules/research/designs/medical_designs.dm @@ -174,3 +174,19 @@ /datum/design/adv_r_arm category = list("Prosthetics", "Medical Designs") + +/datum/design/board/incubator + name = "Machine Design (Egg Incubator)" + desc = "The circuit board for an Incubator." + id = "incubator" + build_path = /obj/item/circuitboard/machine/incubator + category = list ("Medical Machinery") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING + +/datum/design/board/embryonator + name = "Machine Design (Honeystone's Genetic Daycare)" + desc = "The circuit board for a genetic daycare machine." + id = "embryonator" + build_path = /obj/item/circuitboard/machine/embryonator + category = list ("Medical Machinery") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_ENGINEERING diff --git a/modular_splurt/code/modules/research/techweb/nodes/medical_nodes.dm b/modular_splurt/code/modules/research/techweb/nodes/medical_nodes.dm index 9b049b908f66..a530f94ea1df 100644 --- a/modular_splurt/code/modules/research/techweb/nodes/medical_nodes.dm +++ b/modular_splurt/code/modules/research/techweb/nodes/medical_nodes.dm @@ -44,3 +44,11 @@ prereq_ids = list("cyber_organs", "alien_surgery") design_ids = list("ci-hypnoeyes") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000) + +/datum/techweb_node/cloning/New() + var/list/extra_designs = list( + "incubator", + "embryonator" + ) + LAZYADD(design_ids, extra_designs) + . = ..() diff --git a/modular_splurt/icons/obj/machinery/honeystones_genetic_daycare.dmi b/modular_splurt/icons/obj/machinery/honeystones_genetic_daycare.dmi new file mode 100644 index 000000000000..4ca04a6003a4 Binary files /dev/null and b/modular_splurt/icons/obj/machinery/honeystones_genetic_daycare.dmi differ diff --git a/tgstation.dme b/tgstation.dme index deca5594b617..9f0f1b5eb5e3 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4492,6 +4492,8 @@ #include "modular_splurt\code\game\machinery\chem_alert.dm" #include "modular_splurt\code\game\machinery\cryopod.dm" #include "modular_splurt\code\game\machinery\deployable.dm" +#include "modular_splurt\code\game\machinery\embryonator.dm" +#include "modular_splurt\code\game\machinery\incubator.dm" #include "modular_splurt\code\game\machinery\limbgrower.dm" #include "modular_splurt\code\game\machinery\research_table.dm" #include "modular_splurt\code\game\machinery\computer\slavery.dm" diff --git a/tgui/packages/tgui/interfaces/Daycare.tsx b/tgui/packages/tgui/interfaces/Daycare.tsx new file mode 100644 index 000000000000..c9860a56ab53 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Daycare.tsx @@ -0,0 +1,49 @@ +import { useBackend } from '../backend'; +import { Stack, Section, ByondUi } from '../components'; +import { Window } from '../layouts'; + +type DaycareData = { + image: String, +} + +export const Daycare = (props, context) => { + + const { act, data } = useBackend(context); + return ( + +
+ + + { + data ? + + : "Insert Egg" + } + + + + + + + +
+
+ ) +} + +const Prefs = (props, context) => { + return ( + + Prefs + + + ) +} diff --git a/tgui/packages/tgui/interfaces/IncubatorControl.tsx b/tgui/packages/tgui/interfaces/IncubatorControl.tsx new file mode 100644 index 000000000000..d3f8b5af67f9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/IncubatorControl.tsx @@ -0,0 +1,80 @@ +import { Window } from '../layouts'; +import { Button, Table, Section, ProgressBar } from '../components'; +import { useBackend } from '../backend'; + +type IncubatorData = { + eggs: EggsData[], + is_on: boolean, +} + +type EggsData = { + name: String, + ref: String, + progress: number, +} + +export const IncubatorControl = (props, context) => { + const { act, data } = useBackend(context); + const { is_on, eggs } = data; + + return ( + + +
act("toggle_power") + } + /> + )}> + + {eggs.map( + egg => { + + } + ) + } +
+
+
+
+ ) +} + +const EggRow = (props, context) => { + const { act, data } = useBackend(context); + const { + name, + progress, + ref + } = props; + return ( + + + {name} + + + + + + + + + ) +} +