mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-18 03:26:31 +01:00
755 lines
30 KiB
Plaintext
755 lines
30 KiB
Plaintext
/obj/item/organ/brain
|
|
name = "brain"
|
|
desc = "A piece of juicy meat found in a person's head."
|
|
icon_state = "brain"
|
|
visual = TRUE
|
|
throw_speed = 3
|
|
throw_range = 5
|
|
layer = ABOVE_MOB_LAYER
|
|
zone = BODY_ZONE_HEAD
|
|
slot = ORGAN_SLOT_BRAIN
|
|
organ_flags = ORGAN_ORGANIC | ORGAN_VITAL | ORGAN_PROMINENT
|
|
attack_verb_continuous = list("attacks", "slaps", "whacks")
|
|
attack_verb_simple = list("attack", "slap", "whack")
|
|
|
|
///The brain's organ variables are significantly more different than the other organs, with half the decay rate for balance reasons, and twice the maxHealth
|
|
decay_factor = STANDARD_ORGAN_DECAY * 0.5 //30 minutes of decaying to result in a fully damaged brain, since a fast decay rate would be unfun gameplay-wise
|
|
|
|
maxHealth = BRAIN_DAMAGE_DEATH
|
|
low_threshold = 45
|
|
high_threshold = 120
|
|
|
|
organ_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_LITERATE, TRAIT_CAN_STRIP)
|
|
|
|
var/suicided = FALSE
|
|
var/mob/living/brain/brainmob = null
|
|
/// If it's a fake brain with no brainmob assigned. Feedback messages will be faked as if it does have a brainmob. See changelings & dullahans.
|
|
var/decoy_override = FALSE
|
|
|
|
|
|
var/list/datum/brain_trauma/traumas = list()
|
|
|
|
/// List of skillchip items, their location should be this brain.
|
|
var/list/obj/item/skillchip/skillchips
|
|
/// Maximum skillchip complexity we can support before they stop working. Do not reference this var directly and instead call get_max_skillchip_complexity()
|
|
var/max_skillchip_complexity = 3
|
|
/// Maximum skillchip slots available. Do not reference this var directly and instead call get_max_skillchip_slots()
|
|
var/max_skillchip_slots = 5
|
|
|
|
/// Size modifier for the sprite
|
|
var/brain_size = 1
|
|
/// Can this brain become smooth after it gets washed
|
|
var/can_smoothen_out = TRUE
|
|
/// We got smooth from being washed
|
|
var/smooth_brain = FALSE
|
|
/// Variance in brain traits added by subtypes
|
|
var/variant_traits_added
|
|
/// Variance in brain traits removed by subtypes
|
|
var/variant_traits_removed
|
|
/// The color of this brain. Fluff, only used when repairing (shade of...).
|
|
var/shade_color = "pink"
|
|
|
|
/obj/item/organ/brain/Initialize(mapload)
|
|
. = ..()
|
|
// Brain size logic
|
|
transform = transform.Scale(brain_size)
|
|
organ_traits.Remove(variant_traits_removed)
|
|
organ_traits |= variant_traits_added
|
|
|
|
/obj/item/organ/brain/on_mob_insert(mob/living/carbon/brain_owner, special = FALSE, movement_flags)
|
|
. = ..()
|
|
|
|
name = initial(name)
|
|
|
|
// Special check for if you're trapped in a body you can't control because it's owned by a ling.
|
|
if(IS_CHANGELING(brain_owner) && !(movement_flags & NO_ID_TRANSFER))
|
|
if(brainmob && !(brain_owner.stat == DEAD || (HAS_TRAIT(brain_owner, TRAIT_DEATHCOMA))))
|
|
to_chat(brainmob, span_danger("You can't feel your body! You're still just a brain!"))
|
|
forceMove(brain_owner)
|
|
brain_owner.update_body_parts()
|
|
return
|
|
|
|
if(brainmob)
|
|
// If it's a ling decoy brain, nothing to transfer, just throw it out
|
|
if(decoy_override)
|
|
if(brainmob?.key)
|
|
stack_trace("Decoy override brain with a key assigned - This should never happen.")
|
|
|
|
// Not a ling - assume direct control
|
|
else
|
|
if(brain_owner.key)
|
|
brain_owner.ghostize()
|
|
|
|
if(brainmob.mind)
|
|
brainmob.mind.transfer_to(brain_owner)
|
|
else
|
|
brain_owner.PossessByPlayer(brainmob.key)
|
|
|
|
brain_owner.set_suicide(HAS_TRAIT(brainmob, TRAIT_SUICIDED))
|
|
|
|
QDEL_NULL(brainmob)
|
|
else
|
|
brain_owner.set_suicide(suicided)
|
|
|
|
for(var/datum/brain_trauma/trauma as anything in traumas)
|
|
if(trauma.owner)
|
|
if(trauma.owner == brain_owner)
|
|
// if we're being special replaced, the trauma is already applied, so this is expected
|
|
// but if we're not... this is likely a bug, and should be reported
|
|
if(!special)
|
|
stack_trace("A brain trauma ([trauma]) is being re-applied to its owning mob ([brain_owner])!")
|
|
continue
|
|
|
|
stack_trace("A brain trauma ([trauma]) is being applied to a new mob ([brain_owner]) when it's owned by someone else ([trauma.owner])!")
|
|
continue
|
|
|
|
trauma.owner = brain_owner
|
|
if(!trauma.on_gain())
|
|
qdel(trauma)
|
|
|
|
//Update the body's icon so it doesnt appear debrained anymore
|
|
if(!special && !(brain_owner.living_flags & STOP_OVERLAY_UPDATE_BODY_PARTS))
|
|
brain_owner.update_body_parts()
|
|
|
|
/obj/item/organ/brain/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
|
|
. = ..()
|
|
// Delete skillchips first as parent proc sets owner to null, and skillchips need to know the brain's owner.
|
|
if(!QDELETED(organ_owner) && length(skillchips))
|
|
if(!special)
|
|
to_chat(organ_owner, span_notice("You feel your skillchips enable emergency power saving mode, deactivating as your brain leaves your body..."))
|
|
for(var/chip in skillchips)
|
|
var/obj/item/skillchip/skillchip = chip
|
|
// Run the try_ proc with force = TRUE.
|
|
skillchip.try_deactivate_skillchip(silent = special, force = TRUE, brain_owner = organ_owner)
|
|
|
|
for(var/X in traumas)
|
|
var/datum/brain_trauma/BT = X
|
|
BT.on_lose(TRUE)
|
|
BT.owner = null
|
|
|
|
if((!QDELETED(src) || !QDELETED(owner)) && !(movement_flags & NO_ID_TRANSFER))
|
|
transfer_identity(organ_owner)
|
|
if(!special)
|
|
organ_owner.clear_mood_event("brain_damage")
|
|
organ_owner.med_hud_set_status()
|
|
|
|
/obj/item/organ/brain/update_icon_state()
|
|
icon_state = "[initial(icon_state)][smooth_brain ? "-smooth" : ""]"
|
|
return ..()
|
|
|
|
/obj/item/organ/brain/proc/transfer_identity(mob/living/L)
|
|
name = "[L.name]'s [initial(name)]"
|
|
if(brainmob)
|
|
if(!decoy_override)
|
|
return
|
|
|
|
// it's just a dummy, throw it out
|
|
QDEL_NULL(brainmob)
|
|
|
|
if(!L.mind)
|
|
return
|
|
brainmob = new(src)
|
|
brainmob.name = L.real_name
|
|
brainmob.real_name = L.real_name
|
|
brainmob.timeofdeath = L.timeofdeath
|
|
|
|
if(suicided)
|
|
ADD_TRAIT(brainmob, TRAIT_SUICIDED, REF(src))
|
|
|
|
if(L.has_dna())
|
|
var/mob/living/carbon/C = L
|
|
if(!brainmob.stored_dna)
|
|
brainmob.stored_dna = new /datum/dna/stored(brainmob)
|
|
C.dna.copy_dna(brainmob.stored_dna)
|
|
// Hack, fucked dna needs to follow the brain to prevent memes, so we need to copy over the trait sources and shit
|
|
for(var/source in GET_TRAIT_SOURCES(L, TRAIT_BADDNA))
|
|
ADD_TRAIT(brainmob, TRAIT_BADDNA, source)
|
|
|
|
if(L.mind && L.mind.current && !decoy_override)
|
|
L.mind.transfer_to(brainmob)
|
|
to_chat(brainmob, span_notice("You feel slightly disoriented. That's normal when you're just a brain."))
|
|
|
|
/obj/item/organ/brain/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers)
|
|
user.changeNext_move(CLICK_CD_MELEE)
|
|
|
|
if(istype(item, /obj/item/borg/apparatus/organ_storage))
|
|
return //Borg organ bags shouldn't be killing brains
|
|
|
|
if (check_for_repair(item, user))
|
|
return TRUE
|
|
|
|
// Cutting out skill chips.
|
|
if(length(skillchips) && item.get_sharpness() == SHARP_EDGED)
|
|
to_chat(user,span_notice("You begin to excise skillchips from [src]."))
|
|
if(do_after(user, 15 SECONDS, target = src))
|
|
for(var/chip in skillchips)
|
|
var/obj/item/skillchip/skillchip = chip
|
|
|
|
if(!istype(skillchip))
|
|
stack_trace("Item of type [skillchip.type] qdel'd from [src] skillchip list.")
|
|
qdel(skillchip)
|
|
continue
|
|
|
|
remove_skillchip(skillchip)
|
|
|
|
if(skillchip.removable)
|
|
skillchip.forceMove(drop_location())
|
|
continue
|
|
|
|
qdel(skillchip)
|
|
|
|
skillchips = null
|
|
return
|
|
|
|
if(brainmob) //if we aren't trying to heal the brain, pass the attack onto the brainmob.
|
|
item.attack(brainmob, user) //Oh noooeeeee
|
|
|
|
if(item.force != 0 && !(item.item_flags & NOBLUDGEON))
|
|
user.do_attack_animation(src)
|
|
playsound(loc, 'sound/effects/meatslap.ogg', 50)
|
|
set_organ_damage(maxHealth) //fails the brain as the brain was attacked, they're pretty fragile.
|
|
visible_message(span_danger("[user] hits [src] with [item]!"))
|
|
to_chat(user, span_danger("You hit [src] with [item]!"))
|
|
|
|
/obj/item/organ/brain/proc/check_for_repair(obj/item/item, mob/user)
|
|
if(damage && item.is_drainable() && item.reagents.has_reagent(/datum/reagent/medicine/mannitol) && (organ_flags & ORGAN_ORGANIC)) //attempt to heal the brain
|
|
if(brainmob?.health <= HEALTH_THRESHOLD_DEAD) //if the brain is fucked anyway, do nothing
|
|
to_chat(user, span_warning("[src] is far too damaged, there's nothing else we can do for it!"))
|
|
return TRUE
|
|
|
|
user.visible_message(span_notice("[user] starts to slowly pour the contents of [item] onto [src]."), span_notice("You start to slowly pour the contents of [item] onto [src]."))
|
|
if(!do_after(user, 3 SECONDS, src))
|
|
to_chat(user, span_warning("You failed to pour the contents of [item] onto [src]!"))
|
|
return TRUE
|
|
var/and_bright_shade = !shade_color ? "" : " and turn a slightly brighter shade of [shade_color]"
|
|
user.visible_message(span_notice("[user] pours the contents of [item] onto [src], causing it to reform its original shape[and_bright_shade]."), span_notice("You pour the contents of [item] onto [src], causing it to reform its original shape[and_bright_shade]."))
|
|
var/amount = item.reagents.get_reagent_amount(/datum/reagent/medicine/mannitol)
|
|
var/healto = max(0, damage - amount * 2)
|
|
item.reagents.remove_all(ROUND_UP(item.reagents.total_volume / amount * (damage - healto) * 0.5)) //only removes however much solution is needed while also taking into account how much of the solution is mannitol
|
|
set_organ_damage(healto) //heals 2 damage per unit of mannitol, and by using "set_organ_damage", we clear the failing variable if that was up
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/obj/item/organ/brain/examine(mob/user)
|
|
. = ..()
|
|
if(length(skillchips))
|
|
. += span_info("It has a skillchip embedded in it.")
|
|
. += brain_damage_examine()
|
|
if (smooth_brain)
|
|
. += span_notice("All the pesky wrinkles are gone. Now it just needs a good drying...")
|
|
if(brain_size < 1)
|
|
. += span_notice("It is a bit on the smaller side...")
|
|
if(brain_size > 1)
|
|
. += span_notice("It is bigger than average...")
|
|
|
|
/// Needed so subtypes can override examine text while still calling parent
|
|
/obj/item/organ/brain/proc/brain_damage_examine()
|
|
if(suicided)
|
|
return span_info("It's started turning slightly grey. They must not have been able to handle the stress of it all.")
|
|
if(brainmob && (decoy_override || brainmob.client || brainmob.get_ghost()))
|
|
if(organ_flags & ORGAN_FAILING)
|
|
return span_info("It seems to still have a bit of energy within it, but it's rather damaged... You may be able to restore it with some <b>mannitol</b>.")
|
|
else if(damage >= maxHealth*0.5)
|
|
return span_info("You can feel the small spark of life still left in this one, but it's got some bruises. You may be able to restore it with some <b>mannitol</b>.")
|
|
else
|
|
return span_info("You can feel the small spark of life still left in this one.")
|
|
else
|
|
return span_info("This one is completely devoid of life.")
|
|
|
|
/obj/item/organ/brain/get_status_appendix(advanced, add_tooltips)
|
|
var/list/trauma_text
|
|
for(var/datum/brain_trauma/trauma as anything in traumas)
|
|
var/trauma_desc = ""
|
|
switch(trauma.resilience)
|
|
if(TRAUMA_RESILIENCE_BASIC)
|
|
trauma_desc = conditional_tooltip("Mild ", "Repair via brain surgery or medication such as [/datum/reagent/medicine/neurine::name].", add_tooltips)
|
|
if(TRAUMA_RESILIENCE_SURGERY)
|
|
trauma_desc = conditional_tooltip("Severe ", "Repair via brain surgery.", add_tooltips)
|
|
if(TRAUMA_RESILIENCE_LOBOTOMY)
|
|
trauma_desc = conditional_tooltip("Deep-rooted ", "Repair via Lobotomy.", add_tooltips)
|
|
if(TRAUMA_RESILIENCE_WOUND)
|
|
trauma_desc = conditional_tooltip("Fracture-derived ", "Repair via treatment of wounds afflicting the head.", add_tooltips)
|
|
if(TRAUMA_RESILIENCE_MAGIC)
|
|
// BUBBER EDIT CHANGE BEGIN - Blessed lobotomy
|
|
trauma_desc = conditional_tooltip("Soul-bound ", "Repair via Blessed Lobotomy.", add_tooltips)
|
|
// BUBBER EDIT CHANGE END - Blessed lobotomy
|
|
if(TRAUMA_RESILIENCE_ABSOLUTE)
|
|
trauma_desc = conditional_tooltip("Permanent ", "Irreparable under normal circumstances.", add_tooltips)
|
|
trauma_desc += capitalize(trauma.scan_desc)
|
|
LAZYADD(trauma_text, trauma_desc)
|
|
if(LAZYLEN(trauma_text))
|
|
return "Mental trauma: [english_list(trauma_text, and_text = ", and ")]."
|
|
|
|
/obj/item/organ/brain/feel_for_damage(self_aware)
|
|
if(damage < low_threshold)
|
|
return ""
|
|
if(self_aware)
|
|
if(damage < high_threshold)
|
|
return span_warning("Your brain hurts a bit.")
|
|
return span_warning("Your brain hurts a lot.")
|
|
if(damage < high_threshold)
|
|
return span_warning("It feels a bit fuzzy.")
|
|
return span_warning("It aches incessantly.")
|
|
|
|
/obj/item/organ/brain/attack(mob/living/carbon/C, mob/user)
|
|
if(!istype(C))
|
|
return ..()
|
|
|
|
add_fingerprint(user)
|
|
|
|
if(user.zone_selected != BODY_ZONE_HEAD)
|
|
return ..()
|
|
|
|
var/target_has_brain = C.get_organ_by_type(/obj/item/organ/brain)
|
|
|
|
if(!target_has_brain && C.is_eyes_covered())
|
|
to_chat(user, span_warning("You're going to need to remove [C.p_their()] head cover first!"))
|
|
return
|
|
|
|
//since these people will be dead M != usr
|
|
|
|
if(!target_has_brain)
|
|
if(!C.get_bodypart(BODY_ZONE_HEAD) || !user.temporarilyRemoveItemFromInventory(src))
|
|
return
|
|
var/msg = "[C] has [src] inserted into [C.p_their()] head by [user]."
|
|
if(C == user)
|
|
msg = "[user] inserts [src] into [user.p_their()] head!"
|
|
|
|
C.visible_message(span_danger("[msg]"),
|
|
span_userdanger("[msg]"))
|
|
|
|
if(C != user)
|
|
to_chat(C, span_notice("[user] inserts [src] into your head."))
|
|
to_chat(user, span_notice("You insert [src] into [C]'s head."))
|
|
else
|
|
to_chat(user, span_notice("You insert [src] into your head.") )
|
|
|
|
Insert(C)
|
|
else
|
|
..()
|
|
|
|
/obj/item/organ/brain/Destroy() //copypasted from MMIs.
|
|
QDEL_NULL(brainmob)
|
|
QDEL_LIST(traumas)
|
|
|
|
destroy_all_skillchips()
|
|
owner?.mind?.set_current(null) //You aren't allowed to return to brains that don't exist
|
|
return ..()
|
|
|
|
/obj/item/organ/brain/on_life(seconds_per_tick)
|
|
. = ..()
|
|
|
|
if(HAS_TRAIT(src, TRAIT_BRAIN_DAMAGE_NODEATH))
|
|
return
|
|
if(damage >= maxHealth) //rip
|
|
to_chat(owner, span_userdanger("The last spark of life in your brain fizzles out..."))
|
|
owner.investigate_log("has been killed by brain damage.", INVESTIGATE_DEATHS)
|
|
owner.death()
|
|
|
|
/obj/item/organ/brain/on_bodypart_insert(obj/item/bodypart/limb)
|
|
. = ..()
|
|
if(ishuman(limb.owner))
|
|
limb.owner.update_hair()
|
|
else
|
|
limb.update_icon_dropped()
|
|
|
|
/obj/item/organ/brain/on_bodypart_remove(obj/item/bodypart/limb, movement_flags)
|
|
. = ..()
|
|
if(ishuman(limb.owner))
|
|
limb.owner.update_hair()
|
|
else
|
|
limb.update_icon_dropped()
|
|
// once it's out in the world we need to make sure it's the right color
|
|
update_brain_color(animate = FALSE)
|
|
|
|
/obj/item/organ/brain/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag = NONE)
|
|
. = ..()
|
|
var/delta_dam = . //for the sake of clarity
|
|
if(isnull(bodypart_owner)) // no need to color it if it's in someone's noggin
|
|
update_brain_color()
|
|
if(delta_dam < 0 && damage > BRAIN_DAMAGE_MILD)
|
|
roll_for_brain_trauma(-delta_dam) // parent call returns negative numbers if take damage and positive if we heal
|
|
|
|
/// Rolls a random chance to gain a brain trauma based on damage taken and current damage level
|
|
/obj/item/organ/brain/proc/roll_for_brain_trauma(delta_dam)
|
|
if(prob(delta_dam * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% //learn how to do your bloody math properly goddamnit
|
|
gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE)
|
|
|
|
var/is_boosted = (owner && HAS_TRAIT(src, TRAIT_SPECIAL_TRAUMA_BOOST))
|
|
if(damage < BRAIN_DAMAGE_SEVERE)
|
|
return
|
|
if(prob(delta_dam * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1%
|
|
if(prob(20 + (is_boosted * 30)))
|
|
gain_trauma_type(BRAIN_TRAUMA_SPECIAL, is_boosted ? TRAUMA_RESILIENCE_SURGERY : null, natural_gain = TRUE)
|
|
else
|
|
gain_trauma_type(BRAIN_TRAUMA_SEVERE, natural_gain = TRUE)
|
|
|
|
#define BRAIN_DAMAGE_FILTER "brain_damage_color_filter"
|
|
|
|
/// Updates the brain's color based on damage level - the more damaged, the darker and grayer it gets
|
|
/obj/item/organ/brain/proc/update_brain_color(animate = TRUE)
|
|
if(damage <= 0)
|
|
if(get_filter(BRAIN_DAMAGE_FILTER))
|
|
if(animate)
|
|
transition_filter(BRAIN_DAMAGE_FILTER, color_matrix_filter("#ffffff"), time = 1 SECONDS)
|
|
addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, remove_filter), "brain_damage_color_filter"), 1.2 SECONDS, TIMER_UNIQUE)
|
|
else
|
|
remove_filter(BRAIN_DAMAGE_FILTER)
|
|
return
|
|
|
|
var/gradient = rgb_gradient(round(damage / maxHealth, 0.01), 0, "#ffffff", 1, "#7f7f7f")
|
|
if(animate)
|
|
if(!get_filter(BRAIN_DAMAGE_FILTER))
|
|
add_filter(BRAIN_DAMAGE_FILTER, 1, color_matrix_filter("#ffffff"))
|
|
transition_filter(BRAIN_DAMAGE_FILTER, color_matrix_filter(gradient), time = 1 SECONDS)
|
|
else if(get_filter(BRAIN_DAMAGE_FILTER))
|
|
modify_filter(BRAIN_DAMAGE_FILTER, color_matrix_filter(gradient))
|
|
else
|
|
add_filter(BRAIN_DAMAGE_FILTER, 1, color_matrix_filter(gradient))
|
|
|
|
#undef BRAIN_DAMAGE_FILTER
|
|
|
|
/obj/item/organ/brain/check_damage_thresholds()
|
|
. = ..()
|
|
if(!owner)
|
|
return
|
|
// If we crossed blinking brain damage thresholds either way, update our blinking
|
|
if((prev_damage >= BRAIN_DAMAGE_ASYNC_BLINKING && damage < BRAIN_DAMAGE_ASYNC_BLINKING) || (prev_damage < BRAIN_DAMAGE_ASYNC_BLINKING && damage >= BRAIN_DAMAGE_ASYNC_BLINKING))
|
|
var/obj/item/organ/eyes/eyes = owner.get_organ_slot(ORGAN_SLOT_EYES)
|
|
if(eyes?.blink_animation)
|
|
eyes.animate_eyelids(owner)
|
|
if(damage >= 60 && prev_damage < 60)
|
|
owner.add_mood_event("brain_damage", /datum/mood_event/brain_damage)
|
|
else if(prev_damage >= 60 && damage < 60)
|
|
owner.clear_mood_event("brain_damage")
|
|
|
|
// If we're not more injured than before, return without gambling for a trauma
|
|
if(damage <= prev_damage)
|
|
return
|
|
|
|
// Conscious or soft-crit
|
|
var/brain_message
|
|
if(prev_damage < BRAIN_DAMAGE_MILD && damage >= BRAIN_DAMAGE_MILD)
|
|
brain_message = span_warning("You feel lightheaded.")
|
|
else if(prev_damage < BRAIN_DAMAGE_SEVERE && damage >= BRAIN_DAMAGE_SEVERE)
|
|
brain_message = span_warning("You feel less in control of your thoughts.")
|
|
else if(prev_damage < (maxHealth - 20) && damage >= (maxHealth - 20))
|
|
brain_message = span_warning("You can feel your mind flickering on and off...")
|
|
|
|
if(.)
|
|
. += "\n[brain_message]"
|
|
else
|
|
return brain_message
|
|
|
|
/obj/item/organ/brain/before_organ_replacement(obj/item/organ/replacement)
|
|
. = ..()
|
|
var/obj/item/organ/brain/replacement_brain = replacement
|
|
if(!istype(replacement_brain))
|
|
return
|
|
|
|
// Transfer over skillcips to the new brain
|
|
|
|
// If we have some sort of brain type or subtype change and have skillchips, engage the failsafe procedure!
|
|
if(owner && length(skillchips) && (replacement_brain.type != type))
|
|
activate_skillchip_failsafe()
|
|
|
|
// Check through all our skillchips, remove them from this brain, add them to the replacement brain.
|
|
for(var/chip in skillchips)
|
|
var/obj/item/skillchip/skillchip = chip
|
|
|
|
// We're technically doing a little hackery here by bypassing the procs, but I'm the one who wrote them
|
|
// and when you know the rules, you can break the rules.
|
|
|
|
// Technically the owning mob is the same. We don't need to activate or deactivate the skillchips.
|
|
// All the skillchips themselves care about is what brain they're in.
|
|
// Because the new brain will ultimately be owned by the same body, we can safely leave skillchip logic alone.
|
|
|
|
// Directly change the new holding_brain.
|
|
skillchip.holding_brain = replacement_brain
|
|
//And move the actual obj into the new brain (contents)
|
|
skillchip.forceMove(replacement_brain)
|
|
|
|
// Directly add them to the skillchip list in the new brain.
|
|
LAZYADD(replacement_brain.skillchips, skillchip)
|
|
|
|
// Any skillchips has been transferred over, time to empty the list.
|
|
LAZYCLEARLIST(skillchips)
|
|
|
|
// Transfer over traumas as well
|
|
for(var/datum/brain_trauma/trauma as anything in traumas)
|
|
remove_trauma_from_traumas(trauma)
|
|
replacement_brain.add_trauma_to_traumas(trauma)
|
|
|
|
/obj/item/organ/brain/machine_wash(obj/machinery/washing_machine/brainwasher)
|
|
. = ..()
|
|
if (can_smoothen_out && !smooth_brain)
|
|
smooth_brain = TRUE
|
|
update_appearance()
|
|
|
|
if(HAS_TRAIT(brainwasher, TRAIT_BRAINWASHING))
|
|
set_organ_damage(0)
|
|
cure_all_traumas(TRAUMA_RESILIENCE_LOBOTOMY)
|
|
else
|
|
set_organ_damage(maxHealth)
|
|
|
|
/obj/item/organ/brain/zombie
|
|
name = "zombie brain"
|
|
desc = "This glob of green mass can't have much intelligence inside it."
|
|
icon_state = "brain-x"
|
|
variant_traits_added = list(TRAIT_PRIMITIVE)
|
|
variant_traits_removed = list(TRAIT_LITERATE, TRAIT_ADVANCEDTOOLUSER)
|
|
shade_color = "green"
|
|
|
|
/obj/item/organ/brain/alien
|
|
name = "alien brain"
|
|
desc = "We barely understand the brains of terrestial animals. Who knows what we may find in the brain of such an advanced species?"
|
|
icon_state = "brain-x"
|
|
variant_traits_removed = list(TRAIT_LITERATE, TRAIT_ADVANCEDTOOLUSER)
|
|
shade_color = "green"
|
|
|
|
/obj/item/organ/brain/primitive //No like books and stompy metal men
|
|
name = "primitive brain"
|
|
desc = "This juicy piece of meat has a clearly underdeveloped frontal lobe."
|
|
variant_traits_removed = list(TRAIT_LITERATE)
|
|
variant_traits_added = list(
|
|
TRAIT_PRIMITIVE, // No literacy
|
|
TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION,
|
|
TRAIT_EXPERT_FISHER, // live off land, fish from river
|
|
TRAIT_ROUGHRIDER, // ride beast, chase down prey, flee from danger
|
|
TRAIT_BEAST_EMPATHY, // know the way of beast, calm with food
|
|
TRAIT_NECROPOLIS_WORSHIP,
|
|
TRAIT_TACKLING_TAILED_DEFENDER,
|
|
)
|
|
|
|
/obj/item/organ/brain/golem
|
|
name = "crystalline matrix"
|
|
desc = "This collection of sparkling gems somehow allows a golem to think."
|
|
icon_state = "adamantine_resonator"
|
|
can_smoothen_out = FALSE
|
|
color = COLOR_GOLEM_GRAY
|
|
shade_color = "teal"
|
|
organ_flags = ORGAN_MINERAL
|
|
variant_traits_added = list(TRAIT_ROCK_METAMORPHIC)
|
|
|
|
/obj/item/organ/brain/lustrous
|
|
name = "lustrous brain"
|
|
desc = "This is your brain on bluespace dust. Not even once."
|
|
icon_state = "random_fly_4"
|
|
can_smoothen_out = FALSE
|
|
shade_color = null
|
|
|
|
// This fixes an edge case from species/regenerate_organs that would transfer the brain trauma before organ/on_mob_remove can remove it
|
|
// Prevents wizards from using the magic mirror to gain bluespace_prophet trauma and then switching to another race
|
|
/obj/item/organ/brain/lustrous/before_organ_replacement(obj/item/organ/replacement)
|
|
if(owner)
|
|
owner.cure_trauma_type(/datum/brain_trauma/special/bluespace_prophet, TRAUMA_RESILIENCE_ABSOLUTE)
|
|
owner.RemoveElement(/datum/element/tenacious)
|
|
. = ..()
|
|
|
|
/obj/item/organ/brain/lustrous/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
|
|
. = ..()
|
|
organ_owner.cure_trauma_type(/datum/brain_trauma/special/bluespace_prophet, TRAUMA_RESILIENCE_ABSOLUTE)
|
|
organ_owner.RemoveElement(/datum/element/tenacious)
|
|
|
|
/obj/item/organ/brain/lustrous/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
|
|
. = ..()
|
|
organ_owner.gain_trauma(/datum/brain_trauma/special/bluespace_prophet, TRAUMA_RESILIENCE_ABSOLUTE)
|
|
organ_owner.AddElement(/datum/element/tenacious)
|
|
|
|
/obj/item/organ/brain/felinid //A bit smaller than average
|
|
brain_size = 0.8
|
|
|
|
// Sometimes, felinids go a bit haywire and bite people. Based entirely on mania and hunger.
|
|
/obj/item/organ/brain/felinid/get_attacking_limb(mob/living/carbon/human/target)
|
|
var/starving_cat_bonus = owner.nutrition <= NUTRITION_LEVEL_HUNGRY ? 1 : 10
|
|
var/crazy_feral_cat = clamp((starving_cat_bonus * owner.mob_mood?.sanity_level), 0, 100)
|
|
if(prob(crazy_feral_cat) || HAS_TRAIT(owner, TRAIT_FERAL_BITER))
|
|
return owner.get_bodypart(BODY_ZONE_HEAD) || ..()
|
|
return ..()
|
|
|
|
/obj/item/organ/brain/lizard
|
|
name = "lizard brain"
|
|
desc = "This juicy piece of meat has a oversized brain stem and cerebellum, with not much of a limbic system to speak of at all. You would expect its owner to be pretty cold blooded."
|
|
variant_traits_added = list(TRAIT_TACKLING_TAILED_DEFENDER)
|
|
|
|
/obj/item/organ/brain/ghost
|
|
name = "ghost brain"
|
|
desc = "How are you even able to hold this?"
|
|
icon_state = "brain-ghost"
|
|
movement_type = PHASING
|
|
organ_flags = parent_type::organ_flags | ORGAN_GHOST
|
|
shade_color = "ectoplasmic white"
|
|
|
|
/obj/item/organ/brain/abductor
|
|
name = "grey brain"
|
|
desc = "A piece of juicy meat found in an ayy lmao's head."
|
|
icon_state = "brain-x"
|
|
brain_size = 1.3
|
|
variant_traits_added = list(TRAIT_REMOTE_TASTING)
|
|
shade_color = "grey"
|
|
|
|
////////////////////////////////////TRAUMAS////////////////////////////////////////
|
|
|
|
/obj/item/organ/brain/proc/has_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience = TRAUMA_RESILIENCE_ABSOLUTE)
|
|
for(var/X in traumas)
|
|
var/datum/brain_trauma/BT = X
|
|
if(istype(BT, brain_trauma_type) && (BT.resilience <= resilience))
|
|
return BT
|
|
|
|
/obj/item/organ/brain/proc/get_traumas_type(brain_trauma_type = /datum/brain_trauma, resilience = TRAUMA_RESILIENCE_ABSOLUTE)
|
|
. = list()
|
|
for(var/X in traumas)
|
|
var/datum/brain_trauma/BT = X
|
|
if(istype(BT, brain_trauma_type) && (BT.resilience <= resilience))
|
|
. += BT
|
|
|
|
/obj/item/organ/brain/proc/can_gain_trauma(datum/brain_trauma/trauma, resilience, natural_gain = FALSE)
|
|
if(HAS_TRAIT(src, TRAIT_BRAIN_TRAUMA_IMMUNITY))
|
|
return FALSE
|
|
|
|
if(!ispath(trauma))
|
|
trauma = trauma.type
|
|
if(!initial(trauma.can_gain))
|
|
return FALSE
|
|
if(!resilience)
|
|
resilience = initial(trauma.resilience)
|
|
|
|
var/resilience_tier_count = 0
|
|
for(var/X in traumas)
|
|
if(istype(X, trauma))
|
|
return FALSE
|
|
var/datum/brain_trauma/T = X
|
|
if(resilience == T.resilience)
|
|
resilience_tier_count++
|
|
|
|
var/max_traumas
|
|
switch(resilience)
|
|
if(TRAUMA_RESILIENCE_BASIC)
|
|
max_traumas = TRAUMA_LIMIT_BASIC
|
|
if(TRAUMA_RESILIENCE_SURGERY)
|
|
max_traumas = TRAUMA_LIMIT_SURGERY
|
|
if(TRAUMA_RESILIENCE_WOUND)
|
|
max_traumas = TRAUMA_LIMIT_WOUND
|
|
if(TRAUMA_RESILIENCE_LOBOTOMY)
|
|
max_traumas = TRAUMA_LIMIT_LOBOTOMY
|
|
if(TRAUMA_RESILIENCE_MAGIC)
|
|
max_traumas = TRAUMA_LIMIT_MAGIC
|
|
if(TRAUMA_RESILIENCE_ABSOLUTE)
|
|
max_traumas = TRAUMA_LIMIT_ABSOLUTE
|
|
|
|
if(natural_gain && resilience_tier_count >= max_traumas)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
//Proc to use when directly adding a trauma to the brain, so extra args can be given
|
|
/obj/item/organ/brain/proc/gain_trauma(datum/brain_trauma/trauma, resilience, ...)
|
|
var/list/arguments = list()
|
|
if(args.len > 2)
|
|
arguments = args.Copy(3)
|
|
. = brain_gain_trauma(trauma, resilience, arguments)
|
|
|
|
/obj/item/organ/brain/vv_edit_var(var_name, var_value)
|
|
. = ..()
|
|
if(var_name == NAMEOF(src, smooth_brain))
|
|
update_appearance()
|
|
|
|
//Direct trauma gaining proc. Necessary to assign a trauma to its brain. Avoid using directly.
|
|
/obj/item/organ/brain/proc/brain_gain_trauma(datum/brain_trauma/trauma, resilience, list/arguments)
|
|
if(!can_gain_trauma(trauma, resilience))
|
|
return null
|
|
|
|
var/datum/brain_trauma/actual_trauma
|
|
if(ispath(trauma))
|
|
if(!LAZYLEN(arguments))
|
|
actual_trauma = new trauma() //arglist with an empty list runtimes for some reason
|
|
else
|
|
actual_trauma = new trauma(arglist(arguments))
|
|
else
|
|
actual_trauma = trauma
|
|
|
|
if(actual_trauma.brain) //we don't accept used traumas here
|
|
WARNING("gain_trauma was given an already active trauma.")
|
|
return null
|
|
|
|
add_trauma_to_traumas(actual_trauma)
|
|
if(owner)
|
|
actual_trauma.owner = owner
|
|
if(SEND_SIGNAL(owner, COMSIG_CARBON_GAIN_TRAUMA, trauma, resilience) & COMSIG_CARBON_BLOCK_TRAUMA)
|
|
qdel(actual_trauma)
|
|
return null
|
|
if(!actual_trauma.on_gain())
|
|
qdel(actual_trauma)
|
|
return null
|
|
log_game("[key_name_and_tag(owner)] has gained the following brain trauma: [trauma.type]")
|
|
if(resilience)
|
|
actual_trauma.resilience = resilience
|
|
SSblackbox.record_feedback("tally", "traumas", 1, actual_trauma.type)
|
|
return actual_trauma
|
|
|
|
/// Adds the passed trauma instance to our list of traumas and links it to our brain.
|
|
/// DOES NOT handle setting up the trauma, that's done by [proc/brain_gain_trauma]!
|
|
/obj/item/organ/brain/proc/add_trauma_to_traumas(datum/brain_trauma/trauma)
|
|
trauma.brain = src
|
|
traumas += trauma
|
|
|
|
/// Removes the passed trauma instance to our list of traumas and links it to our brain
|
|
/// DOES NOT handle removing the trauma's effects, that's done by [/datum/brain_trauma/Destroy()]!
|
|
/obj/item/organ/brain/proc/remove_trauma_from_traumas(datum/brain_trauma/trauma)
|
|
trauma.brain = null
|
|
traumas -= trauma
|
|
|
|
//Add a random trauma of a certain subtype
|
|
/obj/item/organ/brain/proc/gain_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience, natural_gain = FALSE)
|
|
var/list/datum/brain_trauma/possible_traumas = list()
|
|
for(var/T in subtypesof(brain_trauma_type))
|
|
var/datum/brain_trauma/BT = T
|
|
if(can_gain_trauma(BT, resilience, natural_gain) && initial(BT.random_gain))
|
|
possible_traumas += BT
|
|
|
|
if(!LAZYLEN(possible_traumas))
|
|
return
|
|
|
|
var/trauma_type = pick(possible_traumas)
|
|
return gain_trauma(trauma_type, resilience)
|
|
|
|
//Cure a random trauma of a certain resilience level
|
|
/obj/item/organ/brain/proc/cure_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience = TRAUMA_RESILIENCE_BASIC)
|
|
var/list/traumas = get_traumas_type(brain_trauma_type, resilience)
|
|
if(LAZYLEN(traumas))
|
|
qdel(pick(traumas))
|
|
|
|
/obj/item/organ/brain/proc/cure_all_traumas(resilience = TRAUMA_RESILIENCE_BASIC)
|
|
var/amount_cured = 0
|
|
var/list/traumas = get_traumas_type(resilience = resilience)
|
|
for(var/X in traumas)
|
|
qdel(X)
|
|
amount_cured++
|
|
return amount_cured
|
|
|
|
/// This proc lets the mob's brain decide what bodypart to attack with in an unarmed strike.
|
|
/obj/item/organ/brain/proc/get_attacking_limb(mob/living/carbon/human/target)
|
|
var/obj/item/bodypart/arm/active_hand = owner.get_active_hand()
|
|
if(HAS_TRAIT(owner, TRAIT_FERAL_BITER)) //Feral biters will always prefer biting.
|
|
var/obj/item/bodypart/head/found_head = owner.get_bodypart(BODY_ZONE_HEAD)
|
|
return found_head || active_hand // If we are a feral biter, return a usable head.
|
|
if(target.pulledby == owner) // if we're grabbing our target we're beating them to death with our bare hands
|
|
return active_hand
|
|
if(target.body_position == LYING_DOWN && owner.usable_legs)
|
|
var/obj/item/bodypart/found_bodypart = owner.get_bodypart(IS_LEFT_INDEX(active_hand.held_index) ? BODY_ZONE_L_LEG : BODY_ZONE_R_LEG)
|
|
return found_bodypart || active_hand
|
|
return active_hand
|
|
|
|
/// Brains REALLY like ghosting people. we need special tricks to avoid that, namely removing the old brain with no_id_transfer
|
|
/obj/item/organ/brain/replace_into(mob/living/carbon/new_owner)
|
|
var/obj/item/organ/brain/old_brain = new_owner.get_organ_slot(ORGAN_SLOT_BRAIN)
|
|
old_brain.Remove(new_owner, special = TRUE, movement_flags = NO_ID_TRANSFER)
|
|
qdel(old_brain)
|
|
return Insert(new_owner, special = TRUE, movement_flags = NO_ID_TRANSFER | DELETE_IF_REPLACED)
|
|
|
|
/obj/item/organ/brain/pod
|
|
name = "pod nucleus"
|
|
desc = "The brain of a pod person, it's a bit more plant-like than a human brain."
|
|
foodtype_flags = PODPERSON_ORGAN_FOODTYPES
|
|
color = COLOR_LIME
|
|
shade_color = "lime"
|