[MIRROR] Fixes DNA Infuser's brain swapping, a little bit of qol, ui reworked [MDB IGNORE] (#17844)

* Fixes DNA Infuser's brain swapping, a little bit of qol, ui reworked

* conflicts

* fix the bad

Co-authored-by: tralezab <40974010+tralezab@users.noreply.github.com>
Co-authored-by: Tom <8881105+tf-4@users.noreply.github.com>
This commit is contained in:
SkyratBot
2022-12-08 17:41:31 +00:00
committed by GitHub
co-authored by tralezab Tom
parent d484d7b188
commit c681cee605
19 changed files with 198 additions and 51 deletions
+10
View File
@@ -831,6 +831,16 @@
UNTYPED_LIST_ADD(keys, key)
return keys
/// Turns an associative list into a flat list of keys, but for sprite accessories, respecting the locked variable
/proc/assoc_to_keys_features(list/input)
var/list/keys = list()
for(var/key in input)
var/datum/sprite_accessory/value = input[key]
if(value?.locked)
continue
UNTYPED_LIST_ADD(keys, key)
return keys
///compare two lists, returns TRUE if they are the same
/proc/compare_list(list/l,list/d)
if(!islist(l) || !islist(d))
+39 -5
View File
@@ -18,6 +18,8 @@
var/atom/movable/infusing_from
///what we're turning into
var/datum/infuser_entry/infusing_into
///a message for relaying that the machine is locked if someone tries to leave while it's active
COOLDOWN_DECLARE(message_cooldown)
/obj/machinery/dna_infuser/Initialize(mapload)
. = ..()
@@ -38,11 +40,13 @@
. += span_notice("Missing [span_bold("an infusion source")].")
else
. += span_notice("[span_bold(infusing_from.name)] is in the infusion slot.")
. += span_notice("To operate: Obtain dead creature. Depending on size, drag or drop into the infuser slot.")
. += span_notice("Subject enters the chamber, someone activates the machine. Voila! One of your organs has... changed!")
. += span_notice("Alt-click to eject the infusion source, if one is inside.")
/obj/machinery/dna_infuser/interact(mob/user)
if(user == occupant)
balloon_alert(user, "can't reach!")
toggle_open(user)
return
if(infusing)
balloon_alert(user, "not while it's on!")
@@ -64,6 +68,7 @@
if(!infusing_into)
//no valid recipe, so you get a fly mutation
infusing_into = GLOB.infuser_entries[1]
playsound(src, 'sound/machines/blender.ogg', 50, TRUE)
to_chat(hoccupant, span_danger("Little needles repeatedly prick you! And with each prick, you feel yourself becoming more... [infusing_into.infusion_desc]?"))
hoccupant.take_overall_damage(10)
Shake(15, 15, INFUSING_TIME)
@@ -93,7 +98,16 @@
if(potential_new_organs.len)
var/obj/item/organ/new_organ = pick(potential_new_organs)
new_organ = new new_organ()
new_organ.Insert(target, special = TRUE, drop_if_replaced = FALSE)
if(istype(new_organ, /obj/item/organ/internal/brain))
// brains REALLY like ghosting people. we need special tricks to avoid that, namely removing the old brain with no_id_transfer
var/obj/item/organ/internal/brain/new_brain = new_organ
var/obj/item/organ/internal/brain/old_brain = target.getorganslot(ORGAN_SLOT_BRAIN)
if(old_brain)
old_brain.Remove(target, special = TRUE, no_id_transfer = TRUE)
qdel(old_brain)
new_brain.Insert(target, special = TRUE, drop_if_replaced = FALSE, no_id_transfer = TRUE)
else
new_organ.Insert(target, special = TRUE, drop_if_replaced = FALSE)
infusing_into = null
QDEL_NULL(infusing_from)
@@ -147,6 +161,21 @@
add_infusion_item(used, user)
return ..()
/obj/machinery/dna_infuser/relaymove(mob/living/user, direction)
if(user.stat)
if(COOLDOWN_FINISHED(src, message_cooldown))
COOLDOWN_START(src, message_cooldown, 4 SECONDS)
to_chat(user, span_warning("[src]'s door won't budge!"))
return
if(infusing)
if(COOLDOWN_FINISHED(src, message_cooldown))
COOLDOWN_START(src, message_cooldown, 4 SECONDS)
to_chat(user, span_danger("[src]'s door won't budge while all the needles are infusing you!"))
return
open_machine(drop = FALSE)
//we set drop to false to manually call it with an allowlist
dump_inventory_contents(list(occupant))
// mostly good for dead mobs that turn into items like dead mice (smack to add)
/obj/machinery/dna_infuser/proc/add_infusion_item(obj/item/target, mob/user)
if(!is_valid_infusion(target, user))
@@ -163,6 +192,11 @@
if(user.stat != CONSCIOUS || HAS_TRAIT(user, TRAIT_UI_BLOCKED) || !Adjacent(user) || !user.Adjacent(target) || !ISADVANCEDTOOLUSER(user))
return
if(iscarbon(target))
if(ishuman(target))
close_machine(target)
return
if(!is_valid_infusion(target, user))
return
@@ -170,6 +204,7 @@
infusing_from.forceMove(src)
/obj/machinery/dna_infuser/proc/is_valid_infusion(atom/movable/target, mob/user)
var/datum/component/edible/food_comp = IS_EDIBLE(target)
if(infusing_from)
balloon_alert(user, "empty the machine first!")
return FALSE
@@ -178,9 +213,8 @@
if(living_target.stat != DEAD)
balloon_alert(user, "only dead creatures!")
return FALSE
else if(istype(target, /obj/item/food))
var/obj/item/food/food_target = target
if(!(food_target.foodtypes & GORE))
else if(food_comp)
if(!(food_comp.foodtypes & GORE))
balloon_alert(user, "only creatures!")
return FALSE
else
@@ -1,6 +1,9 @@
/// A list of all infuser entries
GLOBAL_LIST_INIT(infuser_entries, prepare_entries())
/// just clarifying that no threshold does some special stuff, since only meme mutants have it
#define NO_THRESHOLD ""
/proc/prepare_entries()
var/list/entries = list()
//regardless of names we want the failed mutant case to show first
@@ -24,7 +27,7 @@ GLOBAL_LIST_INIT(infuser_entries, prepare_entries())
var/infuse_mob_name = "rejected creature"
/// general desc
var/desc = "For whatever reason, when the body rejects DNA, the DNA goes sour, ending up as some kind of fly-like DNA jumble."
/// desc of what passing the threshold gets you
/// desc of what passing the threshold gets you. if this is empty, there is no threshold, so this is also really a tally of whether this is a "meme" mutant or not
var/threshold_desc = "the DNA mess takes over, and you become a full-fledged flyperson."
/// various little bits
var/list/qualities = list(
@@ -96,11 +99,13 @@ GLOBAL_LIST_INIT(infuser_entries, prepare_entries())
)
infusion_desc = "nomadic"
// just some meme entries, these basically encourage killing staff pets but do not play with the organ bonus system
/datum/infuser_entry/felinid
name = "Cat"
infuse_mob_name = "feline"
desc = "EVERYONE CALM DOWN! I'm not implying anything with this entry. Are we really so surprised that felinids are humans with mixed feline DNA?"
threshold_desc = "nothing happens. I guess this is why it's a big genetic craze, feline DNA is notably stable."
threshold_desc = NO_THRESHOLD
qualities = list(
"oh, let me guess, you're a big fan of those japanese tourist bots",
)
@@ -117,7 +122,7 @@ GLOBAL_LIST_INIT(infuser_entries, prepare_entries())
name = "Fox"
infuse_mob_name = "vulpini"
desc = "Foxes are now quite rare because of the \"fox ears\" craze back in 2555. I mean, also because we're spacefarers who destroyed foxes' natural habitats ages ago, but that applies to most animals."
threshold_desc = "nothing happens. Stable DNA."
threshold_desc = NO_THRESHOLD
qualities = list(
"oh come on really",
"you bring SHAME to all geneticists",
@@ -23,8 +23,7 @@
/obj/item/organ/internal/lungs/carp
name = "mutated carp-lungs"
desc = "Carp DNA infused into what was once some normal lungs."
safe_oxygen_max = 16
safe_oxygen_min = 0
safe_oxygen_min = 0 //we don't breathe this!
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "lungs"
@@ -54,10 +53,9 @@
return
var/mob/living/carbon/human/human_receiver = tongue_owner
var/datum/species/rec_species = human_receiver.dna.species
if(!(rec_species.no_equip_flags & ITEM_SLOT_MASK))
rec_species.no_equip_flags += ITEM_SLOT_MASK
rec_species.update_no_equip_flags(tongue_owner, rec_species.no_equip_flags | ITEM_SLOT_MASK)
var/obj/item/bodypart/head/head = human_receiver.get_bodypart(BODY_ZONE_HEAD)
head.unarmed_damage_low = 10 // Yeah, biteing is pretty weak, blame the monkey super-nerf
head.unarmed_damage_low = 10
head.unarmed_damage_high = 15
head.unarmed_stun_threshold = 15
@@ -67,8 +65,7 @@
return
var/mob/living/carbon/human/human_receiver = tongue_owner
var/datum/species/rec_species = human_receiver.dna.species
if(!(initial(rec_species.no_equip_flags) & ITEM_SLOT_MASK))
rec_species.no_equip_flags -= ITEM_SLOT_MASK
rec_species.update_no_equip_flags(tongue_owner, initial(rec_species.no_equip_flags))
var/obj/item/bodypart/head/head = human_receiver.get_bodypart(BODY_ZONE_HEAD)
head.unarmed_damage_low = initial(head.unarmed_damage_low)
head.unarmed_damage_high = initial(head.unarmed_damage_high)
@@ -76,7 +73,7 @@
/obj/item/organ/internal/tongue/carp/on_life(delta_time, times_fired)
. = ..()
if(!prob(1))
if(owner.stat != CONSCIOUS || !prob(0.1))
return
owner.emote("cough")
var/turf/tooth_fairy = get_turf(owner)
@@ -147,4 +144,8 @@
AddElement(/datum/element/noticable_organ, "skin has small patches of scales growing...")
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/carp)
#undef CARP_ORGAN_COLOR
#undef CARP_SCLERA_COLOR
#undef CARP_PUPIL_COLOR
#undef CARP_COLORS
@@ -11,7 +11,7 @@
icon_state = "brain-x-d"
var/datum/action/cooldown/spell/jaunt/shadow_walk/our_jaunt
/obj/item/organ/internal/brain/shadow/nightmare/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
/obj/item/organ/internal/brain/shadow/nightmare/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE, no_id_transfer = FALSE)
. = ..()
if(M.dna.species.id != SPECIES_NIGHTMARE)
M.set_species(/datum/species/shadow/nightmare)
@@ -469,6 +469,8 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
for (var/name in sprite_accessories)
var/datum/sprite_accessory/sprite_accessory = sprite_accessories[name]
if(sprite_accessory.locked)
continue
var/icon/final_icon
@@ -92,7 +92,7 @@
relevant_species_trait = FACEHAIR
/datum/preference/choiced/facial_hair_gradient/init_possible_values()
return assoc_to_keys(GLOB.facial_hair_gradients_list)
return assoc_to_keys_features(GLOB.facial_hair_gradients_list)
/datum/preference/choiced/facial_hair_gradient/apply_to_human(mob/living/carbon/human/target, value)
LAZYSETLEN(target.grad_style, GRADIENTS_LEN)
@@ -155,7 +155,7 @@
relevant_species_trait = HAIR
/datum/preference/choiced/hair_gradient/init_possible_values()
return assoc_to_keys(GLOB.hair_gradients_list)
return assoc_to_keys_features(GLOB.hair_gradients_list)
/datum/preference/choiced/hair_gradient/apply_to_human(mob/living/carbon/human/target, value)
LAZYSETLEN(target.grad_style, GRADIENTS_LEN)
@@ -7,7 +7,7 @@
relevant_external_organ = /obj/item/organ/external/tail/cat
/datum/preference/choiced/tail_human/init_possible_values()
return assoc_to_keys(GLOB.sprite_accessories["tail"])
return assoc_to_keys_features(GLOB.tails_list_human)
/datum/preference/choiced/tail_human/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["tail_cat"] = value
@@ -24,7 +24,7 @@
relevant_mutant_bodypart = "ears"
/datum/preference/choiced/ears/init_possible_values()
return GLOB.sprite_accessories["ears"]
return assoc_to_keys_features(GLOB.ears_list)
/datum/preference/choiced/ears/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["ears"] = value
@@ -98,7 +98,7 @@
relevant_mutant_bodypart = "legs"
/datum/preference/choiced/lizard_legs/init_possible_values()
return assoc_to_keys(GLOB.legs_list)
return assoc_to_keys_features(GLOB.legs_list)
/datum/preference/choiced/lizard_legs/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["legs"] = value
@@ -123,7 +123,7 @@
relevant_mutant_bodypart = "spines"
/datum/preference/choiced/lizard_spines/init_possible_values()
return assoc_to_keys(GLOB.spines_list)
return assoc_to_keys_features(GLOB.spines_list)
/datum/preference/choiced/lizard_spines/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["spines"] = value
@@ -135,7 +135,7 @@
relevant_external_organ = /obj/item/organ/external/tail/lizard
/datum/preference/choiced/lizard_tail/init_possible_values()
return assoc_to_keys(GLOB.tails_list_lizard)
return assoc_to_keys_features(GLOB.tails_list_lizard)
/datum/preference/choiced/lizard_tail/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["tail_lizard"] = value
@@ -14,6 +14,8 @@
for (var/antennae_name in GLOB.moth_antennae_list)
var/datum/sprite_accessory/antennae = GLOB.moth_antennae_list[antennae_name]
if(antennae.locked)
continue
var/icon/icon_with_antennae = new(moth_head)
icon_with_antennae.Blend(icon(antennae.icon, "m_moth_antennae_[antennae.icon_state]_FRONT"), ICON_OVERLAY)
@@ -57,6 +59,8 @@
for (var/markings_name in GLOB.moth_markings_list)
var/datum/sprite_accessory/markings = GLOB.moth_markings_list[markings_name]
if(markings.locked)
continue
var/icon/icon_with_markings = new(moth_body)
if (markings_name != "None")
@@ -12,6 +12,8 @@
for (var/pod_name in GLOB.pod_hair_list)
var/datum/sprite_accessory/pod_hair = GLOB.pod_hair_list[pod_name]
if(pod_hair.locked)
continue
var/icon/icon_with_hair = new(pod_head)
var/icon/icon_adj = icon(pod_hair.icon, "m_pod_hair_[pod_hair.icon_state]_ADJ")
@@ -27,7 +29,7 @@
return values
/datum/preference/choiced/pod_hair/create_default_value()
return pick(GLOB.pod_hair_list)
return pick(assoc_to_keys_features(GLOB.pod_hair_list))
/datum/preference/choiced/pod_hair/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["pod_hair"] = value
@@ -16,7 +16,7 @@
from doing this unless you absolutely know what you are doing, and have defined a
conversion in savefile.dm
*/
/proc/init_sprite_accessory_subtypes(prototype, list/L, list/male, list/female,roundstart = FALSE, add_blank)//Roundstart argument builds a specific list for roundstart parts where some parts may be locked
/proc/init_sprite_accessory_subtypes(prototype, list/L, list/male, list/female, add_blank)//Roundstart argument builds a specific list for roundstart parts where some parts may be locked
if(!istype(L))
L = list()
if(!istype(male))
@@ -25,10 +25,6 @@
female = list()
for(var/path in subtypesof(prototype))
if(roundstart)
var/datum/sprite_accessory/P = path
if(initial(P.locked))
continue
var/datum/sprite_accessory/D = new path()
if(D.icon_state)
@@ -1863,6 +1859,7 @@
icon_state = "fox"
hasinner = TRUE
color_src = HAIR
locked = TRUE
/datum/sprite_accessory/wings/none
name = "None"
@@ -433,6 +433,11 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(!equipped_item.mob_can_equip(wearer, equipped_item_slot, bypass_equip_delay_self = TRUE, ignore_equipped = TRUE))
wearer.dropItemToGround(equipped_item)
/datum/species/proc/update_no_equip_flags(mob/living/carbon/wearer, new_flags)
no_equip_flags = new_flags
wearer.hud_used?.update_locked_slots()
worn_items_fit_body_check(wearer)
/**
* Proc called when a carbon becomes this species.
*
@@ -230,7 +230,7 @@
UpdateButtons()
/obj/item/organ/internal/brain/primate/Insert(mob/living/carbon/primate, special = FALSE, drop_if_replaced = FALSE)
/obj/item/organ/internal/brain/primate/Insert(mob/living/carbon/primate, special = FALSE, drop_if_replaced = FALSE, no_id_transfer = FALSE)
. = ..()
RegisterSignal(primate, COMSIG_MOVABLE_CROSS, PROC_REF(on_crossed), TRUE)
@@ -103,7 +103,7 @@
return (passed_initial_check || overriding) && part_enabled
/datum/preference/choiced/mutant_choice/init_possible_values()
return assoc_to_keys(GLOB.sprite_accessories[relevant_mutant_bodypart])
return assoc_to_keys_features(GLOB.sprite_accessories[relevant_mutant_bodypart])
/datum/preference/choiced/mutant_choice/create_default_value()
return initial(default_accessory_type?.name) || "None"
@@ -68,7 +68,7 @@
return initial(default_accessory_type?.name) || "None"
/datum/preference/choiced/genital/init_possible_values()
return assoc_to_keys(GLOB.sprite_accessories[relevant_mutant_bodypart])
return assoc_to_keys_features(GLOB.sprite_accessories[relevant_mutant_bodypart])
/datum/preference/toggle/genital_skin_tone
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
@@ -11,7 +11,7 @@
/datum/preference/choiced/digitigrade_legs/init_possible_values()
return assoc_to_keys(GLOB.sprite_accessories["legs"])
return assoc_to_keys_features(GLOB.sprite_accessories["legs"])
/datum/preference/choiced/digitigrade_legs/is_accessible(datum/preferences/preferences)
return ..() && is_usable(preferences)
@@ -12,7 +12,7 @@
#define SYNTH_EMP_BRAIN_DAMAGE_LIGHT 12
#define SYNTH_EMP_BRAIN_DAMAGE_MAXIMUM 75
/obj/item/organ/internal/brain/ipc_positron/Insert(mob/living/carbon/user, special = 0, drop_if_replaced = TRUE)
/obj/item/organ/internal/brain/ipc_positron/Insert(mob/living/carbon/user, special = FALSE, drop_if_replaced = TRUE, no_id_transfer = FALSE)
..()
if(user.stat == DEAD && ishuman(user))
var/mob/living/carbon/human/user_human = user
+103 -16
View File
@@ -1,6 +1,7 @@
import { useBackend } from '../backend';
import { BlockQuote, Box, Section, Stack } from '../components';
import { useBackend, useLocalState } from '../backend';
import { BlockQuote, Box, Button, Section, Stack } from '../components';
import { Window } from '../layouts';
import { multiline } from 'common/string';
type Entry = {
name: string;
@@ -17,40 +18,126 @@ type DnaInfuserData = {
export const InfuserBook = (props, context) => {
const { data } = useBackend<DnaInfuserData>(context);
const { entries } = data;
const [entryIndex, setEntryIndex] = useLocalState(context, 'entry', 0);
const wrapEntries = (newIndex) => {
if (newIndex < 0) {
newIndex = entries.length - 1;
} else if (newIndex > entries.length - 1) {
newIndex = 0;
}
setEntryIndex(newIndex);
};
return (
<Window width={620} height={320}>
<Window.Content scrollable>
<Window title="DNA Infusion Manual" width={620} height={550}>
<Window.Content>
<Stack vertical>
{entries.map((entry) => {
return (
<Stack.Item key={entry.name}>
<InfuserEntry entry={entry} />
<Stack.Item>
<InfuserInstructions />
</Stack.Item>
<Stack.Item>
<InfuserEntry entry={entries[entryIndex]} />
</Stack.Item>
<Stack.Item textAlign="center">
<Stack fontSize="18px" fill>
<Stack.Item grow={2}>
<Button onClick={() => wrapEntries(entryIndex - 1)} fluid>
Last Entry
</Button>
</Stack.Item>
);
})}
<Stack.Item grow={1}>
<Section fitted fill pt="3px">
Entry {entryIndex + 1}/{entries.length}
</Section>
</Stack.Item>
<Stack.Item grow={2}>
<Button onClick={() => wrapEntries(entryIndex + 1)} fluid>
Next Entry
</Button>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};
export const InfuserInstructions = (props, context) => {
return (
<Section title="DNA Infusion Guide">
<Stack vertical>
<Stack.Item fontSize="16px">What does it do?</Stack.Item>
<Stack.Item color="label">
DNA Infusion is the practice of integrating dead creature DNA into
yourself, mutating one of your organs into a genetic slurry that sits
somewhere between being yours or the creature&apos;s. While this does
bring you further away from being human, and gives a slew of...
unfortunate side effects, it also grants new capabilities.{' '}
<b>
Above all else, you have to understand that gene-mutants are usually
very good at specific things, especially with their threshold
bonuses.
</b>
</Stack.Item>
<Stack.Item fontSize="16px">I&apos;m sold! How do I do it?</Stack.Item>
<Stack.Item color="label">
1. Load a dead creature into the machine. This is what you&apos;re
infusing from.
<br />
2. Enter the machine, like you would the DNA scanner.
<br />
3. Have someone activate the machine externally.
<br />
<Box inline color="white">
And you&apos;re done! Note that the infusion source will be
obliterated in the process.
</Box>
</Stack.Item>
</Stack>
</Section>
);
};
type InfuserEntryProps = {
entry: Entry;
};
const InfuserEntry = (props: InfuserEntryProps, context) => {
const { entry } = props;
const isLesser = !entry.threshold_desc;
const lesserDesc = multiline`
Lesser Mutants usually have a smaller list of potential mutations, and
do not have bonuses for infusing many organs.
`;
const greaterDesc = multiline`
Greater Mutants have more upsides and downsides in their organs, more organs
to infuse overall, and come with special bonuses for infusing enough DNA
into yourself.
`;
return (
<Section>
<Stack vertical>
<Stack.Item fontSize={'18px'}>{entry.name} Mutant</Stack.Item>
<Section
fill
title={entry.name + ' Mutant'}
height="225px"
buttons={
<Button
tooltip={isLesser ? lesserDesc : greaterDesc}
icon={isLesser ? 'minus-circle' : 'plus-circle'}
color={isLesser ? 'red' : 'green'}>
{isLesser ? 'Lesser' : 'Greater'} Mutant
</Button>
}>
<Stack vertical fill>
<Stack.Item>
<BlockQuote>
{entry.desc} If a subject infuses with enough DNA,{' '}
{entry.threshold_desc}
{entry.desc}{' '}
{!isLesser && (
<>If a subject infuses with enough DNA, {entry.threshold_desc}</>
)}
</BlockQuote>
</Stack.Item>
<Stack.Item>
<Stack.Item grow>
Qualities:
{entry.qualities.map((quality) => {
return (