@@ -0,0 +1,46 @@
|
||||
/obj/item/organ/appendix
|
||||
name = "appendix"
|
||||
icon_state = "appendix"
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_APPENDIX
|
||||
healing_factor = STANDARD_ORGAN_HEALING
|
||||
decay_factor = STANDARD_ORGAN_DECAY
|
||||
|
||||
now_failing = "<span class='warning'>An explosion of pain erupts in your lower right abdomen!</span>"
|
||||
now_fixed = "<span class='info'>The pain in your abdomen has subsided.</span>"
|
||||
|
||||
var/inflamed
|
||||
|
||||
/obj/item/organ/appendix/on_life()
|
||||
..()
|
||||
if(!(organ_flags & ORGAN_FAILING))
|
||||
return
|
||||
var/mob/living/carbon/M = owner
|
||||
if(M)
|
||||
M.adjustToxLoss(4, TRUE, TRUE) //forced to ensure people don't use it to gain tox as slime person
|
||||
|
||||
/obj/item/organ/appendix/update_icon()
|
||||
if(inflamed)
|
||||
icon_state = "appendixinflamed"
|
||||
name = "inflamed appendix"
|
||||
else
|
||||
icon_state = "appendix"
|
||||
name = "appendix"
|
||||
|
||||
/obj/item/organ/appendix/Remove(mob/living/carbon/M, special = 0)
|
||||
for(var/datum/disease/appendicitis/A in M.diseases)
|
||||
A.cure()
|
||||
inflamed = TRUE
|
||||
update_icon()
|
||||
..()
|
||||
|
||||
/obj/item/organ/appendix/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
|
||||
..()
|
||||
if(inflamed)
|
||||
M.ForceContractDisease(new /datum/disease/appendicitis(), FALSE, TRUE)
|
||||
|
||||
/obj/item/organ/appendix/prepare_eat()
|
||||
var/obj/S = ..()
|
||||
if(inflamed)
|
||||
S.reagents.add_reagent("bad_food", 5)
|
||||
return S
|
||||
@@ -0,0 +1,209 @@
|
||||
/obj/item/organ/cyberimp/chest
|
||||
name = "cybernetic torso implant"
|
||||
desc = "Implants for the organs in your torso."
|
||||
icon_state = "chest_implant"
|
||||
implant_overlay = "chest_implant_overlay"
|
||||
zone = BODY_ZONE_CHEST
|
||||
|
||||
/obj/item/organ/cyberimp/chest/nutriment
|
||||
name = "Nutriment pump implant"
|
||||
desc = "This implant will synthesize and pump into your bloodstream a small amount of nutriment when you are starving."
|
||||
icon_state = "chest_implant"
|
||||
implant_color = "#00AA00"
|
||||
var/hunger_threshold = NUTRITION_LEVEL_STARVING
|
||||
var/synthesizing = 0
|
||||
var/poison_amount = 5
|
||||
slot = ORGAN_SLOT_STOMACH_AID
|
||||
|
||||
/obj/item/organ/cyberimp/chest/nutriment/on_life()
|
||||
if(synthesizing)
|
||||
return
|
||||
|
||||
if(owner.nutrition <= hunger_threshold)
|
||||
synthesizing = TRUE
|
||||
to_chat(owner, "<span class='notice'>You feel less hungry...</span>")
|
||||
owner.nutrition += 50
|
||||
addtimer(CALLBACK(src, .proc/synth_cool), 50)
|
||||
|
||||
/obj/item/organ/cyberimp/chest/nutriment/proc/synth_cool()
|
||||
synthesizing = FALSE
|
||||
|
||||
/obj/item/organ/cyberimp/chest/nutriment/emp_act(severity)
|
||||
. = ..()
|
||||
if(!owner || . & EMP_PROTECT_SELF)
|
||||
return
|
||||
owner.reagents.add_reagent("bad_food", poison_amount / severity)
|
||||
to_chat(owner, "<span class='warning'>You feel like your insides are burning.</span>")
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/chest/nutriment/plus
|
||||
name = "Nutriment pump implant PLUS"
|
||||
desc = "This implant will synthesize and pump into your bloodstream a small amount of nutriment when you are hungry."
|
||||
icon_state = "chest_implant"
|
||||
implant_color = "#006607"
|
||||
hunger_threshold = NUTRITION_LEVEL_HUNGRY
|
||||
poison_amount = 10
|
||||
|
||||
/obj/item/organ/cyberimp/chest/reviver
|
||||
name = "Reviver implant"
|
||||
desc = "This implant will attempt to revive and heal you if you lose consciousness. For the faint of heart!"
|
||||
icon_state = "chest_implant"
|
||||
implant_color = "#AD0000"
|
||||
slot = ORGAN_SLOT_HEART_AID
|
||||
var/revive_cost = 0
|
||||
var/reviving = 0
|
||||
var/cooldown = 0
|
||||
|
||||
/obj/item/organ/cyberimp/chest/reviver/on_life()
|
||||
if(reviving)
|
||||
if(owner.stat == UNCONSCIOUS)
|
||||
addtimer(CALLBACK(src, .proc/heal), 30)
|
||||
else
|
||||
cooldown = revive_cost + world.time
|
||||
reviving = FALSE
|
||||
to_chat(owner, "<span class='notice'>Your reviver implant shuts down and starts recharging. It will be ready again in [DisplayTimeText(revive_cost)].</span>")
|
||||
return
|
||||
|
||||
if(cooldown > world.time)
|
||||
return
|
||||
if(owner.stat != UNCONSCIOUS)
|
||||
return
|
||||
if(owner.suiciding)
|
||||
return
|
||||
|
||||
revive_cost = 0
|
||||
reviving = TRUE
|
||||
to_chat(owner, "<span class='notice'>You feel a faint buzzing as your reviver implant starts patching your wounds...</span>")
|
||||
|
||||
/obj/item/organ/cyberimp/chest/reviver/proc/heal()
|
||||
if(owner.getOxyLoss())
|
||||
owner.adjustOxyLoss(-5)
|
||||
revive_cost += 5
|
||||
if(owner.getBruteLoss())
|
||||
owner.adjustBruteLoss(-2)
|
||||
revive_cost += 40
|
||||
if(owner.getFireLoss())
|
||||
owner.adjustFireLoss(-2)
|
||||
revive_cost += 40
|
||||
if(owner.getToxLoss())
|
||||
owner.adjustToxLoss(-1)
|
||||
revive_cost += 40
|
||||
|
||||
/obj/item/organ/cyberimp/chest/reviver/emp_act(severity)
|
||||
. = ..()
|
||||
if(!owner || . & EMP_PROTECT_SELF)
|
||||
return
|
||||
|
||||
if(reviving)
|
||||
revive_cost += 200
|
||||
else
|
||||
cooldown += 200
|
||||
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
if(H.stat != DEAD && prob(50 / severity) && H.can_heartattack())
|
||||
H.set_heartattack(TRUE)
|
||||
to_chat(H, "<span class='userdanger'>You feel a horrible agony in your chest!</span>")
|
||||
addtimer(CALLBACK(src, .proc/undo_heart_attack), 600 / severity)
|
||||
|
||||
/obj/item/organ/cyberimp/chest/reviver/proc/undo_heart_attack()
|
||||
var/mob/living/carbon/human/H = owner
|
||||
if(!istype(H))
|
||||
return
|
||||
H.set_heartattack(FALSE)
|
||||
if(H.stat == CONSCIOUS)
|
||||
to_chat(H, "<span class='notice'>You feel your heart beating again!</span>")
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/chest/thrusters
|
||||
name = "implantable thrusters set"
|
||||
desc = "An implantable set of thruster ports. They use the gas from environment or subject's internals for propulsion in zero-gravity areas. \
|
||||
Unlike regular jetpacks, this device has no stabilization system."
|
||||
slot = ORGAN_SLOT_THRUSTERS
|
||||
icon_state = "imp_jetpack"
|
||||
implant_overlay = null
|
||||
implant_color = null
|
||||
actions_types = list(/datum/action/item_action/organ_action/toggle)
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
var/on = FALSE
|
||||
var/datum/effect_system/trail_follow/ion/ion_trail
|
||||
|
||||
/obj/item/organ/cyberimp/chest/thrusters/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
|
||||
. = ..()
|
||||
if(!ion_trail)
|
||||
ion_trail = new
|
||||
ion_trail.set_up(M)
|
||||
|
||||
/obj/item/organ/cyberimp/chest/thrusters/Remove(mob/living/carbon/M, special = 0)
|
||||
if(on)
|
||||
toggle(silent = TRUE)
|
||||
..()
|
||||
|
||||
/obj/item/organ/cyberimp/chest/thrusters/ui_action_click()
|
||||
toggle()
|
||||
|
||||
/obj/item/organ/cyberimp/chest/thrusters/proc/toggle(silent = FALSE)
|
||||
if(!on)
|
||||
if(crit_fail || (organ_flags & ORGAN_FAILING))
|
||||
if(!silent)
|
||||
to_chat(owner, "<span class='warning'>Your thrusters set seems to be broken!</span>")
|
||||
return 0
|
||||
on = TRUE
|
||||
if(allow_thrust(0.01))
|
||||
ion_trail.start()
|
||||
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, .proc/move_react)
|
||||
owner.add_movespeed_modifier(MOVESPEED_ID_CYBER_THRUSTER, priority=100, multiplicative_slowdown=-2, movetypes=FLOATING, conflict=MOVE_CONFLICT_JETPACK)
|
||||
if(!silent)
|
||||
to_chat(owner, "<span class='notice'>You turn your thrusters set on.</span>")
|
||||
else
|
||||
ion_trail.stop()
|
||||
UnregisterSignal(owner, COMSIG_MOVABLE_MOVED)
|
||||
owner.remove_movespeed_modifier(MOVESPEED_ID_CYBER_THRUSTER)
|
||||
if(!silent)
|
||||
to_chat(owner, "<span class='notice'>You turn your thrusters set off.</span>")
|
||||
on = FALSE
|
||||
update_icon()
|
||||
|
||||
/obj/item/organ/cyberimp/chest/thrusters/update_icon()
|
||||
if(on)
|
||||
icon_state = "imp_jetpack-on"
|
||||
else
|
||||
icon_state = "imp_jetpack"
|
||||
for(var/X in actions)
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
|
||||
/obj/item/organ/cyberimp/chest/thrusters/proc/move_react()
|
||||
allow_thrust(0.01)
|
||||
|
||||
/obj/item/organ/cyberimp/chest/thrusters/proc/allow_thrust(num)
|
||||
if(!on || !owner)
|
||||
return 0
|
||||
|
||||
var/turf/T = get_turf(owner)
|
||||
if(!T) // No more runtimes from being stuck in nullspace.
|
||||
return 0
|
||||
|
||||
// Priority 1: use air from environment.
|
||||
var/datum/gas_mixture/environment = T.return_air()
|
||||
if(environment && environment.return_pressure() > 30)
|
||||
return 1
|
||||
|
||||
// Priority 2: use plasma from internal plasma storage.
|
||||
// (just in case someone would ever use this implant system to make cyber-alien ops with jetpacks and taser arms)
|
||||
if(owner.getPlasma() >= num*100)
|
||||
owner.adjustPlasma(-num*100)
|
||||
return 1
|
||||
|
||||
// Priority 3: use internals tank.
|
||||
var/obj/item/tank/I = owner.internal
|
||||
if(I && I.air_contents && I.air_contents.total_moles() > num)
|
||||
var/datum/gas_mixture/removed = I.air_contents.remove(num)
|
||||
if(removed.total_moles() > 0.005)
|
||||
T.assume_air(removed)
|
||||
return 1
|
||||
else
|
||||
T.assume_air(removed)
|
||||
|
||||
toggle(silent = TRUE)
|
||||
return 0
|
||||
@@ -0,0 +1,144 @@
|
||||
#define STUN_SET_AMOUNT 40
|
||||
|
||||
/obj/item/organ/cyberimp
|
||||
name = "cybernetic implant"
|
||||
desc = "A state-of-the-art implant that improves a baseline's functionality."
|
||||
status = ORGAN_ROBOTIC
|
||||
organ_flags = ORGAN_SYNTHETIC
|
||||
var/implant_color = "#FFFFFF"
|
||||
var/implant_overlay
|
||||
var/syndicate_implant = FALSE //Makes the implant invisible to health analyzers and medical HUDs.
|
||||
|
||||
/obj/item/organ/cyberimp/New(var/mob/M = null)
|
||||
if(iscarbon(M))
|
||||
src.Insert(M)
|
||||
if(implant_overlay)
|
||||
var/mutable_appearance/overlay = mutable_appearance(icon, implant_overlay)
|
||||
overlay.color = implant_color
|
||||
add_overlay(overlay)
|
||||
return ..()
|
||||
|
||||
|
||||
|
||||
//[[[[BRAIN]]]]
|
||||
|
||||
/obj/item/organ/cyberimp/brain
|
||||
name = "cybernetic brain implant"
|
||||
desc = "Injectors of extra sub-routines for the brain."
|
||||
icon_state = "brain_implant"
|
||||
implant_overlay = "brain_implant_overlay"
|
||||
zone = BODY_ZONE_HEAD
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
|
||||
/obj/item/organ/cyberimp/brain/emp_act(severity)
|
||||
. = ..()
|
||||
if(!owner || . & EMP_PROTECT_SELF)
|
||||
return
|
||||
var/stun_amount = 200/severity
|
||||
owner.Stun(stun_amount)
|
||||
to_chat(owner, "<span class='warning'>Your body seizes up!</span>")
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/brain/anti_drop
|
||||
name = "anti-drop implant"
|
||||
desc = "This cybernetic brain implant will allow you to force your hand muscles to contract, preventing item dropping. Twitch ear to toggle."
|
||||
var/active = 0
|
||||
var/list/stored_items = list()
|
||||
implant_color = "#DE7E00"
|
||||
slot = ORGAN_SLOT_BRAIN_ANTIDROP
|
||||
actions_types = list(/datum/action/item_action/organ_action/toggle)
|
||||
|
||||
/obj/item/organ/cyberimp/brain/anti_drop/ui_action_click()
|
||||
active = !active
|
||||
if(active)
|
||||
for(var/obj/item/I in owner.held_items)
|
||||
stored_items += I
|
||||
|
||||
var/list/L = owner.get_empty_held_indexes()
|
||||
if(LAZYLEN(L) == owner.held_items.len)
|
||||
to_chat(owner, "<span class='notice'>You are not holding any items, your hands relax...</span>")
|
||||
active = 0
|
||||
stored_items = list()
|
||||
else
|
||||
for(var/obj/item/I in stored_items)
|
||||
to_chat(owner, "<span class='notice'>Your [owner.get_held_index_name(owner.get_held_index_of_item(I))]'s grip tightens.</span>")
|
||||
ADD_TRAIT(I, TRAIT_NODROP, ANTI_DROP_IMPLANT_TRAIT)
|
||||
|
||||
else
|
||||
release_items()
|
||||
to_chat(owner, "<span class='notice'>Your hands relax...</span>")
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/brain/anti_drop/emp_act(severity)
|
||||
. = ..()
|
||||
if(!owner || . & EMP_PROTECT_SELF)
|
||||
return
|
||||
var/range = severity ? 10 : 5
|
||||
var/atom/A
|
||||
if(active)
|
||||
release_items()
|
||||
for(var/obj/item/I in stored_items)
|
||||
A = pick(oview(range))
|
||||
I.throw_at(A, range, 2)
|
||||
to_chat(owner, "<span class='warning'>Your [owner.get_held_index_name(owner.get_held_index_of_item(I))] spasms and throws the [I.name]!</span>")
|
||||
stored_items = list()
|
||||
|
||||
/obj/item/organ/cyberimp/brain/anti_drop/proc/release_items()
|
||||
for(var/obj/item/I in stored_items)
|
||||
REMOVE_TRAIT(I, TRAIT_NODROP, ANTI_DROP_IMPLANT_TRAIT)
|
||||
stored_items = list()
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/brain/anti_drop/Remove(var/mob/living/carbon/M, special = 0)
|
||||
if(active)
|
||||
ui_action_click()
|
||||
..()
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/brain/anti_stun
|
||||
name = "CNS Rebooter implant"
|
||||
desc = "This implant will automatically give you back control over your central nervous system, reducing downtime when stunned."
|
||||
implant_color = "#FFFF00"
|
||||
slot = ORGAN_SLOT_BRAIN_ANTISTUN
|
||||
|
||||
/obj/item/organ/cyberimp/brain/anti_stun/on_life()
|
||||
..()
|
||||
if(crit_fail || !(organ_flags & ORGAN_FAILING))
|
||||
return
|
||||
owner.adjustStaminaLoss(-3.5) //Citadel edit, makes it more useful in Stamina based combat
|
||||
if(owner.AmountStun() > STUN_SET_AMOUNT)
|
||||
owner.SetStun(STUN_SET_AMOUNT)
|
||||
if(owner.AmountKnockdown() > STUN_SET_AMOUNT)
|
||||
owner.SetKnockdown(STUN_SET_AMOUNT)
|
||||
|
||||
/obj/item/organ/cyberimp/brain/anti_stun/emp_act(severity)
|
||||
. = ..()
|
||||
if(crit_fail || (organ_flags & ORGAN_FAILING) || . & EMP_PROTECT_SELF)
|
||||
return
|
||||
crit_fail = TRUE
|
||||
organ_flags |= ORGAN_FAILING
|
||||
addtimer(CALLBACK(src, .proc/reboot), 90 / severity)
|
||||
|
||||
/obj/item/organ/cyberimp/brain/anti_stun/proc/reboot()
|
||||
crit_fail = FALSE
|
||||
organ_flags &= ~ORGAN_FAILING
|
||||
|
||||
|
||||
//[[[[MOUTH]]]]
|
||||
/obj/item/organ/cyberimp/mouth
|
||||
zone = BODY_ZONE_PRECISE_MOUTH
|
||||
|
||||
/obj/item/organ/cyberimp/mouth/breathing_tube
|
||||
name = "breathing tube implant"
|
||||
desc = "This simple implant adds an internals connector to your back, allowing you to use internals without a mask and protecting you from being choked."
|
||||
icon_state = "implant_mask"
|
||||
slot = ORGAN_SLOT_BREATHING_TUBE
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
|
||||
/obj/item/organ/cyberimp/mouth/breathing_tube/emp_act(severity)
|
||||
. = ..()
|
||||
if(!owner || . & EMP_PROTECT_SELF)
|
||||
return
|
||||
if(prob(60/severity))
|
||||
to_chat(owner, "<span class='warning'>Your breathing tube suddenly closes!</span>")
|
||||
owner.losebreath += 2
|
||||
@@ -0,0 +1,149 @@
|
||||
#define INFINITE -1
|
||||
|
||||
/obj/item/autosurgeon
|
||||
name = "autosurgeon"
|
||||
desc = "A device that automatically inserts an implant or organ into the user without the hassle of extensive surgery. It has a slot to insert implants/organs and a screwdriver slot for removing accidentally added items."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "autoimplanter"
|
||||
item_state = "nothing"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
var/obj/item/organ/storedorgan
|
||||
var/organ_type = /obj/item/organ
|
||||
var/uses = INFINITE
|
||||
var/starting_organ
|
||||
|
||||
/obj/item/autosurgeon/Initialize(mapload)
|
||||
. = ..()
|
||||
if(starting_organ)
|
||||
insert_organ(new starting_organ(src))
|
||||
|
||||
/obj/item/autosurgeon/proc/insert_organ(var/obj/item/organ/I)
|
||||
storedorgan = I
|
||||
I.forceMove(src)
|
||||
I.organ_flags |= ORGAN_FROZEN //Stops decay
|
||||
name = "[initial(name)] ([storedorgan.name])"
|
||||
|
||||
/obj/item/autosurgeon/attack_self(mob/user)//when the object it used...
|
||||
if(!uses)
|
||||
to_chat(user, "<span class='warning'>[src] has already been used. The tools are dull and won't reactivate.</span>")
|
||||
return
|
||||
else if(!storedorgan)
|
||||
to_chat(user, "<span class='notice'>[src] currently has no implant stored.</span>")
|
||||
return
|
||||
storedorgan.Insert(user)//insert stored organ into the user
|
||||
user.visible_message("<span class='notice'>[user] presses a button on [src], and you hear a short mechanical noise.</span>", "<span class='notice'>You feel a sharp sting as [src] plunges into your body.</span>")
|
||||
playsound(get_turf(user), 'sound/weapons/circsawhit.ogg', 50, 1)
|
||||
storedorgan = null
|
||||
name = initial(name)
|
||||
if(uses != INFINITE)
|
||||
uses--
|
||||
if(!uses)
|
||||
desc = "[initial(desc)] Looks like it's been used up."
|
||||
|
||||
/obj/item/autosurgeon/attack_self_tk(mob/user)
|
||||
return //stops TK fuckery
|
||||
|
||||
/obj/item/autosurgeon/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, organ_type))
|
||||
if(storedorgan)
|
||||
to_chat(user, "<span class='notice'>[src] already has an implant stored.</span>")
|
||||
return
|
||||
else if(!uses)
|
||||
to_chat(user, "<span class='notice'>[src] has already been used up.</span>")
|
||||
return
|
||||
if(!user.transferItemToLoc(I, src))
|
||||
return
|
||||
storedorgan = I
|
||||
to_chat(user, "<span class='notice'>You insert the [I] into [src].</span>")
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/autosurgeon/screwdriver_act(mob/living/user, obj/item/I)
|
||||
if(..())
|
||||
return TRUE
|
||||
if(!storedorgan)
|
||||
to_chat(user, "<span class='notice'>There's no implant in [src] for you to remove.</span>")
|
||||
else
|
||||
var/atom/drop_loc = user.drop_location()
|
||||
for(var/J in src)
|
||||
var/atom/movable/AM = J
|
||||
AM.forceMove(drop_loc)
|
||||
|
||||
to_chat(user, "<span class='notice'>You remove the [storedorgan] from [src].</span>")
|
||||
I.play_tool_sound(src)
|
||||
storedorgan = null
|
||||
if(uses != INFINITE)
|
||||
uses--
|
||||
if(!uses)
|
||||
desc = "[initial(desc)] Looks like it's been used up."
|
||||
return TRUE
|
||||
|
||||
/obj/item/autosurgeon/cmo
|
||||
desc = "A single use autosurgeon that contains a medical heads-up display augment. A screwdriver can be used to remove it, but implants can't be placed back in."
|
||||
uses = 1
|
||||
starting_organ = /obj/item/organ/cyberimp/eyes/hud/medical
|
||||
|
||||
/obj/item/autosurgeon/gloweyes
|
||||
desc = "A single use autosurgeon that contains a set of Luminescent Eyes augments. A screwdriver can be used to remove it, but implants can't be placed back in."
|
||||
uses = 1
|
||||
starting_organ = /obj/item/organ/eyes/robotic/glow
|
||||
|
||||
/obj/item/autosurgeon/thermal_eyes
|
||||
starting_organ = /obj/item/organ/eyes/robotic/thermals
|
||||
|
||||
/obj/item/autosurgeon/xray_eyes
|
||||
starting_organ = /obj/item/organ/eyes/robotic/xray
|
||||
|
||||
/obj/item/autosurgeon/anti_stun
|
||||
starting_organ = /obj/item/organ/cyberimp/brain/anti_stun
|
||||
|
||||
/obj/item/autosurgeon/reviver
|
||||
starting_organ = /obj/item/organ/cyberimp/chest/reviver
|
||||
|
||||
/obj/item/autosurgeon/anti_drop
|
||||
starting_organ = /obj/item/organ/cyberimp/brain/anti_drop
|
||||
|
||||
//BOX O' IMPLANTS
|
||||
|
||||
/obj/item/storage/box/cyber_implants
|
||||
name = "boxed cybernetic implants"
|
||||
desc = "A sleek, sturdy box."
|
||||
icon_state = "syndiebox"
|
||||
illustration = "cyber_implants"
|
||||
var/list/boxed = list(
|
||||
/obj/item/autosurgeon/thermal_eyes,
|
||||
/obj/item/autosurgeon/xray_eyes,
|
||||
/obj/item/autosurgeon/anti_stun,
|
||||
/obj/item/autosurgeon/reviver)
|
||||
var/amount = 5
|
||||
|
||||
/obj/item/storage/box/cyber_implants/PopulateContents()
|
||||
var/implant
|
||||
while(contents.len <= amount)
|
||||
implant = pick(boxed)
|
||||
new implant(src)
|
||||
|
||||
/obj/item/autosurgeon/penis
|
||||
desc = "A single use autosurgeon that contains a penis. A screwdriver can be used to remove it, but implants can't be placed back in."
|
||||
uses = 1
|
||||
starting_organ = /obj/item/organ/genital/penis
|
||||
|
||||
/obj/item/autosurgeon/testicles
|
||||
desc = "A single use autosurgeon that contains a set of testicles. A screwdriver can be used to remove it, but implants can't be placed back in."
|
||||
uses = 1
|
||||
starting_organ = /obj/item/organ/genital/testicles
|
||||
|
||||
/obj/item/autosurgeon/vagina
|
||||
desc = "A single use autosurgeon that contains a vagina. A screwdriver can be used to remove it, but implants can't be placed back in."
|
||||
uses = 1
|
||||
starting_organ = /obj/item/organ/genital/vagina
|
||||
|
||||
/obj/item/autosurgeon/breasts
|
||||
desc = "A single use autosurgeon that contains a set of breasts. A screwdriver can be used to remove it, but implants can't be placed back in."
|
||||
uses = 1
|
||||
starting_organ = /obj/item/organ/genital/breasts
|
||||
|
||||
/obj/item/autosurgeon/womb
|
||||
desc = "A single use autosurgeon that contains a womb. A screwdriver can be used to remove it, but implants can't be placed back in."
|
||||
uses = 1
|
||||
starting_organ = /obj/item/organ/genital/womb
|
||||
@@ -0,0 +1,376 @@
|
||||
/obj/item/organ/eyes
|
||||
name = BODY_ZONE_PRECISE_EYES
|
||||
icon_state = "eyeballs"
|
||||
desc = "I see you!"
|
||||
zone = BODY_ZONE_PRECISE_EYES
|
||||
slot = ORGAN_SLOT_EYES
|
||||
gender = PLURAL
|
||||
|
||||
healing_factor = STANDARD_ORGAN_HEALING
|
||||
decay_factor = STANDARD_ORGAN_DECAY
|
||||
maxHealth = 0.5 * STANDARD_ORGAN_THRESHOLD //half the normal health max since we go blind at 30, a permanent blindness at 50 therefore makes sense unless medicine is administered
|
||||
high_threshold = 0.3 * STANDARD_ORGAN_THRESHOLD //threshold at 30
|
||||
low_threshold = 0.15 * STANDARD_ORGAN_THRESHOLD //threshold at 15
|
||||
|
||||
low_threshold_passed = "<span class='info'>Distant objects become somewhat less tangible.</span>"
|
||||
high_threshold_passed = "<span class='info'>Everything starts to look a lot less clear.</span>"
|
||||
now_failing = "<span class='warning'>Darkness envelopes you, as your eyes go blind!</span>"
|
||||
now_fixed = "<span class='info'>Color and shapes are once again perceivable.</span>"
|
||||
high_threshold_cleared = "<span class='info'>Your vision functions passably once more.</span>"
|
||||
low_threshold_cleared = "<span class='info'>Your vision is cleared of any ailment.</span>"
|
||||
|
||||
var/sight_flags = 0
|
||||
var/see_in_dark = 2
|
||||
var/eye_damage = 0
|
||||
var/tint = 0
|
||||
var/eye_color = "" //set to a hex code to override a mob's eye color
|
||||
var/old_eye_color = "fff"
|
||||
var/flash_protect = 0
|
||||
var/see_invisible = SEE_INVISIBLE_LIVING
|
||||
var/lighting_alpha
|
||||
var/damaged = FALSE //damaged indicates that our eyes are undergoing some level of negative effect
|
||||
|
||||
/obj/item/organ/eyes/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = FALSE)
|
||||
..()
|
||||
if(damage == initial(damage))
|
||||
clear_eye_trauma()
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/HMN = owner
|
||||
old_eye_color = HMN.eye_color
|
||||
if(eye_color)
|
||||
HMN.eye_color = eye_color
|
||||
HMN.regenerate_icons()
|
||||
else
|
||||
eye_color = HMN.eye_color
|
||||
if(HAS_TRAIT(HMN, TRAIT_NIGHT_VISION) && !lighting_alpha)
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_NV_TRAIT
|
||||
see_in_dark = 8
|
||||
M.update_tint()
|
||||
owner.update_sight()
|
||||
|
||||
|
||||
/obj/item/organ/eyes/Remove(mob/living/carbon/M, special = 0)
|
||||
clear_eye_trauma()
|
||||
..()
|
||||
if(ishuman(M) && eye_color)
|
||||
var/mob/living/carbon/human/HMN = M
|
||||
HMN.eye_color = old_eye_color
|
||||
HMN.regenerate_icons()
|
||||
M.update_tint()
|
||||
M.update_sight()
|
||||
|
||||
/obj/item/organ/eyes/on_life()
|
||||
..()
|
||||
var/mob/living/carbon/C = owner
|
||||
//since we can repair fully damaged eyes, check if healing has occurred
|
||||
if((organ_flags & ORGAN_FAILING) && (damage < maxHealth))
|
||||
organ_flags &= ~ORGAN_FAILING
|
||||
C.cure_blind(EYE_DAMAGE)
|
||||
//various degrees of "oh fuck my eyes", from "point a laser at your eye" to "staring at the Sun" intensities
|
||||
if(damage > 20)
|
||||
damaged = TRUE
|
||||
if(organ_flags & ORGAN_FAILING)
|
||||
C.become_blind(EYE_DAMAGE)
|
||||
else if(damage > 30)
|
||||
C.overlay_fullscreen("eye_damage", /obj/screen/fullscreen/impaired, 2)
|
||||
else
|
||||
C.overlay_fullscreen("eye_damage", /obj/screen/fullscreen/impaired, 1)
|
||||
//called once since we don't want to keep clearing the screen of eye damage for people who are below 20 damage
|
||||
else if(damaged)
|
||||
damaged = FALSE
|
||||
C.clear_fullscreen("eye_damage")
|
||||
return
|
||||
|
||||
/obj/item/organ/eyes/proc/clear_eye_trauma()
|
||||
var/mob/living/carbon/C = owner
|
||||
C.clear_fullscreen("eye_damage")
|
||||
C.cure_blind(EYE_DAMAGE)
|
||||
damaged = FALSE
|
||||
|
||||
/obj/item/organ/eyes/night_vision
|
||||
name = "shadow eyes"
|
||||
desc = "A spooky set of eyes that can see in the dark."
|
||||
see_in_dark = 8
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
|
||||
actions_types = list(/datum/action/item_action/organ_action/use)
|
||||
var/night_vision = TRUE
|
||||
|
||||
/obj/item/organ/eyes/night_vision/ui_action_click()
|
||||
sight_flags = initial(sight_flags)
|
||||
switch(lighting_alpha)
|
||||
if (LIGHTING_PLANE_ALPHA_VISIBLE)
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
|
||||
if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
||||
if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE)
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE
|
||||
else
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
|
||||
sight_flags &= ~SEE_BLACKNESS
|
||||
owner.update_sight()
|
||||
|
||||
/obj/item/organ/eyes/night_vision/alien
|
||||
name = "alien eyes"
|
||||
desc = "It turned out they had them after all!"
|
||||
sight_flags = SEE_MOBS
|
||||
|
||||
/obj/item/organ/eyes/night_vision/zombie
|
||||
name = "undead eyes"
|
||||
desc = "Somewhat counterintuitively, these half-rotten eyes actually have superior vision to those of a living human."
|
||||
sight_flags = SEE_MOBS
|
||||
|
||||
/obj/item/organ/eyes/night_vision/nightmare
|
||||
name = "burning red eyes"
|
||||
desc = "Even without their shadowy owner, looking at these eyes gives you a sense of dread."
|
||||
icon_state = "burning_eyes"
|
||||
|
||||
/obj/item/organ/eyes/night_vision/mushroom
|
||||
name = "fung-eye"
|
||||
desc = "While on the outside they look inert and dead, the eyes of mushroom people are actually very advanced."
|
||||
|
||||
///Robotic
|
||||
|
||||
/obj/item/organ/eyes/robotic
|
||||
name = "robotic eyes"
|
||||
icon_state = "cybernetic_eyeballs"
|
||||
desc = "Your vision is augmented."
|
||||
status = ORGAN_ROBOTIC
|
||||
organ_flags = ORGAN_SYNTHETIC
|
||||
|
||||
/obj/item/organ/eyes/robotic/emp_act(severity)
|
||||
. = ..()
|
||||
if(!owner || . & EMP_PROTECT_SELF)
|
||||
return
|
||||
to_chat(owner, "<span class='warning'>Static obfuscates your vision!</span>")
|
||||
owner.flash_act(visual = 1)
|
||||
if(severity == EMP_HEAVY)
|
||||
owner.adjustOrganLoss(ORGAN_SLOT_EYES, 20)
|
||||
|
||||
|
||||
/obj/item/organ/eyes/robotic/xray
|
||||
name = "\improper X-ray eyes"
|
||||
desc = "These cybernetic eyes will give you X-ray vision. Blinking is futile."
|
||||
eye_color = "000"
|
||||
see_in_dark = 8
|
||||
sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS
|
||||
|
||||
/obj/item/organ/eyes/robotic/thermals
|
||||
name = "thermal eyes"
|
||||
desc = "These cybernetic eye implants will give you thermal vision. Vertical slit pupil included."
|
||||
eye_color = "FC0"
|
||||
sight_flags = SEE_MOBS
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
|
||||
flash_protect = -1
|
||||
see_in_dark = 8
|
||||
|
||||
/obj/item/organ/eyes/robotic/flashlight
|
||||
name = "flashlight eyes"
|
||||
desc = "It's two flashlights rigged together with some wire. Why would you put these in someone's head?"
|
||||
eye_color ="fee5a3"
|
||||
icon = 'icons/obj/lighting.dmi'
|
||||
icon_state = "flashlight_eyes"
|
||||
flash_protect = 2
|
||||
tint = INFINITY
|
||||
var/obj/item/flashlight/eyelight/eye
|
||||
|
||||
/obj/item/organ/eyes/robotic/flashlight/emp_act(severity)
|
||||
return
|
||||
|
||||
/obj/item/organ/eyes/robotic/flashlight/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = FALSE)
|
||||
..()
|
||||
if(!eye)
|
||||
eye = new /obj/item/flashlight/eyelight()
|
||||
eye.on = TRUE
|
||||
eye.forceMove(M)
|
||||
eye.update_brightness(M)
|
||||
M.become_blind("flashlight_eyes")
|
||||
|
||||
|
||||
/obj/item/organ/eyes/robotic/flashlight/Remove(var/mob/living/carbon/M, special = FALSE)
|
||||
eye.on = FALSE
|
||||
eye.update_brightness(M)
|
||||
eye.forceMove(src)
|
||||
M.cure_blind("flashlight_eyes")
|
||||
..()
|
||||
|
||||
// Welding shield implant
|
||||
/obj/item/organ/eyes/robotic/shield
|
||||
name = "shielded robotic eyes"
|
||||
desc = "These reactive micro-shields will protect you from welders and flashes without obscuring your vision."
|
||||
flash_protect = 2
|
||||
|
||||
/obj/item/organ/eyes/robotic/shield/emp_act(severity)
|
||||
return
|
||||
|
||||
#define RGB2EYECOLORSTRING(definitionvar) ("[copytext(definitionvar,2,3)][copytext(definitionvar,4,5)][copytext(definitionvar,6,7)]")
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow
|
||||
name = "High Luminosity Eyes"
|
||||
desc = "Special glowing eyes, used by snowflakes who want to be special."
|
||||
eye_color = "000"
|
||||
actions_types = list(/datum/action/item_action/organ_action/use, /datum/action/item_action/organ_action/toggle)
|
||||
var/current_color_string = "#ffffff"
|
||||
var/active = FALSE
|
||||
var/max_light_beam_distance = 5
|
||||
var/light_beam_distance = 5
|
||||
var/light_object_range = 1
|
||||
var/light_object_power = 2
|
||||
var/list/obj/effect/abstract/eye_lighting/eye_lighting
|
||||
var/obj/effect/abstract/eye_lighting/on_mob
|
||||
var/image/mob_overlay
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/Initialize()
|
||||
. = ..()
|
||||
mob_overlay = image('icons/mob/human_face.dmi', "eyes_glow_gs")
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/Destroy()
|
||||
terminate_effects()
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/Remove(mob/living/carbon/M, special = FALSE)
|
||||
terminate_effects()
|
||||
. = ..()
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/terminate_effects()
|
||||
if(owner && active)
|
||||
deactivate(TRUE)
|
||||
active = FALSE
|
||||
clear_visuals(TRUE)
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/ui_action_click(owner, action)
|
||||
if(istype(action, /datum/action/item_action/organ_action/toggle))
|
||||
toggle_active()
|
||||
else if(istype(action, /datum/action/item_action/organ_action/use))
|
||||
prompt_for_controls(owner)
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/toggle_active()
|
||||
if(active)
|
||||
deactivate()
|
||||
else
|
||||
activate()
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/prompt_for_controls(mob/user)
|
||||
var/C = input(owner, "Select Color", "Select color", "#ffffff") as color|null
|
||||
if(!C || QDELETED(src) || QDELETED(user) || QDELETED(owner) || owner != user)
|
||||
return
|
||||
var/range = input(user, "Enter range (0 - [max_light_beam_distance])", "Range Select", 0) as null|num
|
||||
|
||||
set_distance(CLAMP(range, 0, max_light_beam_distance))
|
||||
assume_rgb(C)
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/assume_rgb(newcolor)
|
||||
current_color_string = newcolor
|
||||
eye_color = RGB2EYECOLORSTRING(current_color_string)
|
||||
sync_light_effects()
|
||||
cycle_mob_overlay()
|
||||
if(!QDELETED(owner) && ishuman(owner)) //Other carbon mobs don't have eye color.
|
||||
owner.dna.species.handle_body(owner)
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/cycle_mob_overlay()
|
||||
remove_mob_overlay()
|
||||
mob_overlay.color = current_color_string
|
||||
add_mob_overlay()
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/add_mob_overlay()
|
||||
if(!QDELETED(owner))
|
||||
owner.add_overlay(mob_overlay)
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/remove_mob_overlay()
|
||||
if(!QDELETED(owner))
|
||||
owner.cut_overlay(mob_overlay)
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/emp_act()
|
||||
. = ..()
|
||||
if(!active || . & EMP_PROTECT_SELF)
|
||||
return
|
||||
deactivate(silent = TRUE)
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/activate(silent = FALSE)
|
||||
start_visuals()
|
||||
if(!silent)
|
||||
to_chat(owner, "<span class='warning'>Your [src] clicks and makes a whining noise, before shooting out a beam of light!</span>")
|
||||
active = TRUE
|
||||
RegisterSignal(owner, COMSIG_ATOM_DIR_CHANGE, .proc/update_visuals)
|
||||
cycle_mob_overlay()
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/deactivate(silent = FALSE)
|
||||
clear_visuals()
|
||||
if(!silent)
|
||||
to_chat(owner, "<span class='warning'>Your [src] shuts off!</span>")
|
||||
active = FALSE
|
||||
UnregisterSignal(owner, COMSIG_ATOM_DIR_CHANGE)
|
||||
remove_mob_overlay()
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/update_visuals(datum/source, olddir, newdir)
|
||||
if((LAZYLEN(eye_lighting) < light_beam_distance) || !on_mob)
|
||||
regenerate_light_effects()
|
||||
var/turf/scanfrom = get_turf(owner)
|
||||
var/scandir = owner.dir
|
||||
if (newdir && scandir != newdir) // COMSIG_ATOM_DIR_CHANGE happens before the dir change, but with a reference to the new direction.
|
||||
scandir = newdir
|
||||
if(!istype(scanfrom))
|
||||
clear_visuals()
|
||||
var/turf/scanning = scanfrom
|
||||
var/stop = FALSE
|
||||
on_mob.forceMove(scanning)
|
||||
for(var/i in 1 to light_beam_distance)
|
||||
scanning = get_step(scanning, scandir)
|
||||
if(scanning.opacity || scanning.has_opaque_atom)
|
||||
stop = TRUE
|
||||
var/obj/effect/abstract/eye_lighting/L = LAZYACCESS(eye_lighting, i)
|
||||
if(stop)
|
||||
L.forceMove(src)
|
||||
else
|
||||
L.forceMove(scanning)
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/clear_visuals(delete_everything = FALSE)
|
||||
if(delete_everything)
|
||||
QDEL_LIST(eye_lighting)
|
||||
QDEL_NULL(on_mob)
|
||||
else
|
||||
for(var/i in eye_lighting)
|
||||
var/obj/effect/abstract/eye_lighting/L = i
|
||||
L.forceMove(src)
|
||||
if(!QDELETED(on_mob))
|
||||
on_mob.forceMove(src)
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/start_visuals()
|
||||
if(!islist(eye_lighting))
|
||||
regenerate_light_effects()
|
||||
if((LAZYLEN(eye_lighting) < light_beam_distance) || !on_mob)
|
||||
regenerate_light_effects()
|
||||
sync_light_effects()
|
||||
update_visuals()
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/set_distance(dist)
|
||||
light_beam_distance = dist
|
||||
regenerate_light_effects()
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/regenerate_light_effects()
|
||||
clear_visuals(TRUE)
|
||||
on_mob = new(src)
|
||||
for(var/i in 1 to light_beam_distance)
|
||||
LAZYADD(eye_lighting,new /obj/effect/abstract/eye_lighting(src))
|
||||
sync_light_effects()
|
||||
|
||||
/obj/item/organ/eyes/robotic/glow/proc/sync_light_effects()
|
||||
for(var/I in eye_lighting)
|
||||
var/obj/effect/abstract/eye_lighting/L = I
|
||||
L.set_light(light_object_range, light_object_power, current_color_string)
|
||||
if(on_mob)
|
||||
on_mob.set_light(1, 1, current_color_string)
|
||||
|
||||
/obj/effect/abstract/eye_lighting
|
||||
var/obj/item/organ/eyes/robotic/glow/parent
|
||||
|
||||
/obj/effect/abstract/eye_lighting/Initialize()
|
||||
. = ..()
|
||||
parent = loc
|
||||
if(!istype(parent))
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
/obj/item/organ/eyes/insect
|
||||
name = "insect eyes"
|
||||
desc = "These eyes seem to have increased sensitivity to bright light, with no improvement to low light vision."
|
||||
flash_protect = -1
|
||||
@@ -0,0 +1,247 @@
|
||||
/obj/item/organ/heart
|
||||
name = "heart"
|
||||
desc = "I feel bad for the heartless bastard who lost this."
|
||||
icon_state = "heart-on"
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_HEART
|
||||
|
||||
healing_factor = STANDARD_ORGAN_HEALING
|
||||
decay_factor = 3 * STANDARD_ORGAN_DECAY //designed to fail about 5 minutes after death
|
||||
|
||||
low_threshold_passed = "<span class='info'>Prickles of pain appear then die out from within your chest...</span>"
|
||||
high_threshold_passed = "<span class='warning'>Something inside your chest hurts, and the pain isn't subsiding. You notice yourself breathing far faster than before.</span>"
|
||||
now_fixed = "<span class='info'>Your heart begins to beat again.</span>"
|
||||
high_threshold_cleared = "<span class='info'>The pain in your chest has died down, and your breathing becomes more relaxed.</span>"
|
||||
|
||||
// Heart attack code is in code/modules/mob/living/carbon/human/life.dm
|
||||
var/beating = 1
|
||||
var/icon_base = "heart"
|
||||
attack_verb = list("beat", "thumped")
|
||||
var/beat = BEAT_NONE//is this mob having a heatbeat sound played? if so, which?
|
||||
|
||||
var/failed = FALSE //to prevent constantly running failing code
|
||||
var/operated = FALSE //whether the heart's been operated on to fix some of its damages
|
||||
|
||||
/obj/item/organ/heart/update_icon()
|
||||
if(beating)
|
||||
icon_state = "[icon_base]-on"
|
||||
else
|
||||
icon_state = "[icon_base]-off"
|
||||
|
||||
/obj/item/organ/heart/Remove(mob/living/carbon/M, special = 0)
|
||||
..()
|
||||
if(!special)
|
||||
addtimer(CALLBACK(src, .proc/stop_if_unowned), 120)
|
||||
|
||||
/obj/item/organ/heart/proc/stop_if_unowned()
|
||||
if(!owner)
|
||||
Stop()
|
||||
|
||||
/obj/item/organ/heart/attack_self(mob/user)
|
||||
..()
|
||||
if(!beating)
|
||||
user.visible_message("<span class='notice'>[user] squeezes [src] to \
|
||||
make it beat again!</span>","<span class='notice'>You squeeze [src] to make it beat again!</span>")
|
||||
Restart()
|
||||
addtimer(CALLBACK(src, .proc/stop_if_unowned), 80)
|
||||
|
||||
/obj/item/organ/heart/proc/Stop()
|
||||
beating = 0
|
||||
update_icon()
|
||||
return 1
|
||||
|
||||
/obj/item/organ/heart/proc/Restart()
|
||||
beating = 1
|
||||
update_icon()
|
||||
return 1
|
||||
|
||||
/obj/item/organ/heart/prepare_eat()
|
||||
var/obj/S = ..()
|
||||
S.icon_state = "[icon_base]-off"
|
||||
return S
|
||||
|
||||
/obj/item/organ/heart/on_life()
|
||||
..()
|
||||
if(owner.client && beating)
|
||||
failed = FALSE
|
||||
var/sound/slowbeat = sound('sound/health/slowbeat.ogg', repeat = TRUE)
|
||||
var/sound/fastbeat = sound('sound/health/fastbeat.ogg', repeat = TRUE)
|
||||
var/mob/living/carbon/H = owner
|
||||
|
||||
if(H.health <= H.crit_threshold && beat != BEAT_SLOW)
|
||||
beat = BEAT_SLOW
|
||||
H.playsound_local(get_turf(H), slowbeat,40,0, channel = CHANNEL_HEARTBEAT)
|
||||
to_chat(owner, "<span class = 'notice'>You feel your heart slow down...</span>")
|
||||
if(beat == BEAT_SLOW && H.health > H.crit_threshold)
|
||||
H.stop_sound_channel(CHANNEL_HEARTBEAT)
|
||||
beat = BEAT_NONE
|
||||
|
||||
if(H.jitteriness)
|
||||
if(H.health > HEALTH_THRESHOLD_FULLCRIT && (!beat || beat == BEAT_SLOW))
|
||||
H.playsound_local(get_turf(H),fastbeat,40,0, channel = CHANNEL_HEARTBEAT)
|
||||
beat = BEAT_FAST
|
||||
else if(beat == BEAT_FAST)
|
||||
H.stop_sound_channel(CHANNEL_HEARTBEAT)
|
||||
beat = BEAT_NONE
|
||||
|
||||
if(organ_flags & ORGAN_FAILING) //heart broke, stopped beating, death imminent
|
||||
if(owner.stat == CONSCIOUS)
|
||||
owner.visible_message("<span class='userdanger'>[owner] clutches at [owner.p_their()] chest as if [owner.p_their()] heart is stopping!</span>")
|
||||
owner.set_heartattack(TRUE)
|
||||
failed = TRUE
|
||||
|
||||
obj/item/organ/heart/slime
|
||||
name = "slime heart"
|
||||
desc = "It seems we've gotten to the slimy core of the matter."
|
||||
icon_state = "heart-s-on"
|
||||
icon_base = "heart-s"
|
||||
|
||||
/obj/item/organ/heart/cursed
|
||||
name = "cursed heart"
|
||||
desc = "A heart that, when inserted, will force you to pump it manually."
|
||||
icon_state = "cursedheart-off"
|
||||
icon_base = "cursedheart"
|
||||
decay_factor = 0
|
||||
actions_types = list(/datum/action/item_action/organ_action/cursed_heart)
|
||||
var/last_pump = 0
|
||||
var/add_colour = TRUE //So we're not constantly recreating colour datums
|
||||
var/pump_delay = 30 //you can pump 1 second early, for lag, but no more (otherwise you could spam heal)
|
||||
var/blood_loss = 100 //600 blood is human default, so 5 failures (below 122 blood is where humans die because reasons?)
|
||||
|
||||
//How much to heal per pump, negative numbers would HURT the player
|
||||
var/heal_brute = 0
|
||||
var/heal_burn = 0
|
||||
var/heal_oxy = 0
|
||||
|
||||
|
||||
/obj/item/organ/heart/cursed/attack(mob/living/carbon/human/H, mob/living/carbon/human/user, obj/target)
|
||||
if(H == user && istype(H))
|
||||
playsound(user,'sound/effects/singlebeat.ogg',40,1)
|
||||
user.temporarilyRemoveItemFromInventory(src, TRUE)
|
||||
Insert(user)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/organ/heart/cursed/on_life()
|
||||
if(world.time > (last_pump + pump_delay))
|
||||
if(ishuman(owner) && owner.client) //While this entire item exists to make people suffer, they can't control disconnects.
|
||||
var/mob/living/carbon/human/H = owner
|
||||
if(H.dna && !(NOBLOOD in H.dna.species.species_traits))
|
||||
H.blood_volume = max(H.blood_volume - blood_loss, 0)
|
||||
to_chat(H, "<span class = 'userdanger'>You have to keep pumping your blood!</span>")
|
||||
if(add_colour)
|
||||
H.add_client_colour(/datum/client_colour/cursed_heart_blood) //bloody screen so real
|
||||
add_colour = FALSE
|
||||
else
|
||||
last_pump = world.time //lets be extra fair *sigh*
|
||||
|
||||
/obj/item/organ/heart/cursed/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
|
||||
..()
|
||||
if(owner)
|
||||
to_chat(owner, "<span class ='userdanger'>Your heart has been replaced with a cursed one, you have to pump this one manually otherwise you'll die!</span>")
|
||||
|
||||
/obj/item/organ/heart/cursed/Remove(mob/living/carbon/M, special = 0)
|
||||
..()
|
||||
M.remove_client_colour(/datum/client_colour/cursed_heart_blood)
|
||||
|
||||
/datum/action/item_action/organ_action/cursed_heart
|
||||
name = "Pump your blood"
|
||||
|
||||
//You are now brea- pumping blood manually
|
||||
/datum/action/item_action/organ_action/cursed_heart/Trigger()
|
||||
. = ..()
|
||||
if(. && istype(target, /obj/item/organ/heart/cursed))
|
||||
var/obj/item/organ/heart/cursed/cursed_heart = target
|
||||
|
||||
if(world.time < (cursed_heart.last_pump + (cursed_heart.pump_delay-10))) //no spam
|
||||
to_chat(owner, "<span class='userdanger'>Too soon!</span>")
|
||||
return
|
||||
|
||||
cursed_heart.last_pump = world.time
|
||||
playsound(owner,'sound/effects/singlebeat.ogg',40,1)
|
||||
to_chat(owner, "<span class = 'notice'>Your heart beats.</span>")
|
||||
|
||||
var/mob/living/carbon/human/H = owner
|
||||
if(istype(H))
|
||||
if(H.dna && !(NOBLOOD in H.dna.species.species_traits))
|
||||
H.blood_volume = min(H.blood_volume + cursed_heart.blood_loss*0.5, BLOOD_VOLUME_MAXIMUM)
|
||||
H.remove_client_colour(/datum/client_colour/cursed_heart_blood)
|
||||
cursed_heart.add_colour = TRUE
|
||||
H.adjustBruteLoss(-cursed_heart.heal_brute)
|
||||
H.adjustFireLoss(-cursed_heart.heal_burn)
|
||||
H.adjustOxyLoss(-cursed_heart.heal_oxy)
|
||||
|
||||
|
||||
/datum/client_colour/cursed_heart_blood
|
||||
priority = 100 //it's an indicator you're dieing, so it's very high priority
|
||||
colour = "red"
|
||||
|
||||
/obj/item/organ/heart/cybernetic
|
||||
name = "cybernetic heart"
|
||||
desc = "An electronic device designed to mimic the functions of an organic human heart. Offers no benefit over an organic heart other than being easy to make."
|
||||
icon_state = "heart-c"
|
||||
organ_flags = ORGAN_SYNTHETIC
|
||||
|
||||
/obj/item/organ/heart/cybernetic/emp_act(severity)
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
Stop()
|
||||
addtimer(CALLBACK(src, .proc/Restart), 20/severity SECONDS)
|
||||
damage += 100/severity
|
||||
|
||||
/obj/item/organ/heart/cybernetic/upgraded
|
||||
name = "upgraded cybernetic heart"
|
||||
desc = "An electronic device designed to mimic the functions of an organic human heart. Also holds an emergency dose of epinephrine, used automatically after facing severe trauma. This upgraded model can regenerate its dose after use."
|
||||
icon_state = "heart-c-u"
|
||||
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
|
||||
|
||||
//I put it on upgraded for now.
|
||||
var/dose_available = TRUE
|
||||
var/rid = /datum/reagent/medicine/epinephrine
|
||||
var/ramount = 10
|
||||
|
||||
obj/item/organ/heart/cybernetic/upgraded/on_life()
|
||||
. = ..()
|
||||
if(dose_available && owner.health <= owner.crit_threshold && !owner.reagents.has_reagent(rid))
|
||||
owner.reagents.add_reagent(rid, ramount)
|
||||
used_dose()
|
||||
if(ramount < 10) //eats your nutrition to regen epinephrine
|
||||
var/regen_amount = owner.nutrition/2000
|
||||
owner.nutrition -= regen_amount
|
||||
ramount += regen_amount
|
||||
|
||||
/obj/item/organ/heart/cybernetic/upgraded/proc/used_dose()
|
||||
. = ..()
|
||||
addtimer(VARSET_CALLBACK(src, dose_available, TRUE), 5 MINUTES)
|
||||
ramount = 0
|
||||
|
||||
|
||||
|
||||
/obj/item/organ/heart/ipc
|
||||
name = "IPC heart"
|
||||
desc = "An electronic pump that regulates hydraulic functions, they have an auto-restart after EMPs."
|
||||
icon_state = "heart-c"
|
||||
organ_flags = ORGAN_SYNTHETIC
|
||||
|
||||
/obj/item/organ/heart/ipc/emp_act()
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
Stop()
|
||||
addtimer(CALLBACK(src, .proc/Restart), 10)
|
||||
|
||||
/obj/item/organ/heart/freedom
|
||||
name = "heart of freedom"
|
||||
desc = "This heart pumps with the passion to give... something freedom."
|
||||
organ_flags = ORGAN_SYNTHETIC //the power of freedom prevents heart attacks
|
||||
var/min_next_adrenaline = 0
|
||||
|
||||
/obj/item/organ/heart/freedom/on_life()
|
||||
. = ..()
|
||||
if(owner.health < 5 && world.time > min_next_adrenaline)
|
||||
min_next_adrenaline = world.time + rand(250, 600) //anywhere from 4.5 to 10 minutes
|
||||
to_chat(owner, "<span class='userdanger'>You feel yourself dying, but you refuse to give up!</span>")
|
||||
owner.heal_overall_damage(15, 15)
|
||||
if(owner.reagents.get_reagent_amount("ephedrine") < 20)
|
||||
owner.reagents.add_reagent("ephedrine", 10)
|
||||
@@ -0,0 +1,367 @@
|
||||
#define STANDARD_ORGAN_THRESHOLD 100
|
||||
#define STANDARD_ORGAN_HEALING 0.001
|
||||
|
||||
/obj/item/organ
|
||||
name = "organ"
|
||||
icon = 'icons/obj/surgery.dmi'
|
||||
var/mob/living/carbon/owner = null
|
||||
var/status = ORGAN_ORGANIC
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
throwforce = 0
|
||||
var/zone = BODY_ZONE_CHEST
|
||||
var/slot
|
||||
// DO NOT add slots with matching names to different zones - it will break internal_organs_slot list!
|
||||
var/organ_flags = 0
|
||||
var/maxHealth = STANDARD_ORGAN_THRESHOLD
|
||||
var/damage = 0 //total damage this organ has sustained
|
||||
///Healing factor and decay factor function on % of maxhealth, and do not work by applying a static number per tick
|
||||
var/healing_factor = 0 //fraction of maxhealth healed per on_life(), set to 0 for generic organs
|
||||
var/decay_factor = 0 //same as above but when without a living owner, set to 0 for generic organs
|
||||
var/high_threshold = STANDARD_ORGAN_THRESHOLD * 0.45 //when severe organ damage occurs
|
||||
var/low_threshold = STANDARD_ORGAN_THRESHOLD * 0.1 //when minor organ damage occurs
|
||||
|
||||
///Organ variables for determining what we alert the owner with when they pass/clear the damage thresholds
|
||||
var/prev_damage = 0
|
||||
var/low_threshold_passed
|
||||
var/high_threshold_passed
|
||||
var/now_failing
|
||||
var/now_fixed
|
||||
var/high_threshold_cleared
|
||||
var/low_threshold_cleared
|
||||
|
||||
/obj/item/organ/proc/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
|
||||
if(!iscarbon(M) || owner == M)
|
||||
return FALSE
|
||||
|
||||
var/obj/item/organ/replaced = M.getorganslot(slot)
|
||||
if(replaced)
|
||||
replaced.Remove(M, special = 1)
|
||||
if(drop_if_replaced)
|
||||
replaced.forceMove(get_turf(M))
|
||||
else
|
||||
qdel(replaced)
|
||||
|
||||
//Hopefully this doesn't cause problems
|
||||
organ_flags &= ~ORGAN_FROZEN
|
||||
|
||||
owner = M
|
||||
M.internal_organs |= src
|
||||
M.internal_organs_slot[slot] = src
|
||||
moveToNullspace()
|
||||
for(var/X in actions)
|
||||
var/datum/action/A = X
|
||||
A.Grant(M)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
return TRUE
|
||||
|
||||
//Special is for instant replacement like autosurgeons
|
||||
/obj/item/organ/proc/Remove(mob/living/carbon/M, special = FALSE)
|
||||
owner = null
|
||||
if(M)
|
||||
M.internal_organs -= src
|
||||
if(M.internal_organs_slot[slot] == src)
|
||||
M.internal_organs_slot.Remove(slot)
|
||||
if((organ_flags & ORGAN_VITAL) && !special && !(M.status_flags & GODMODE))
|
||||
M.death()
|
||||
for(var/X in actions)
|
||||
var/datum/action/A = X
|
||||
A.Remove(M)
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/item/organ/proc/on_find(mob/living/finder)
|
||||
return
|
||||
|
||||
/obj/item/organ/process() //runs decay when outside of a person AND ONLY WHEN OUTSIDE (i.e. long obj).
|
||||
on_death() //Kinda hate doing it like this, but I really don't want to call process directly.
|
||||
|
||||
//Sources; life.dm process_organs
|
||||
/obj/item/organ/proc/on_death() //Runs when outside AND inside.
|
||||
decay()
|
||||
|
||||
//Applys the slow damage over time decay
|
||||
/obj/item/organ/proc/decay()
|
||||
if(!can_decay())
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return
|
||||
is_cold()
|
||||
if(organ_flags & ORGAN_FROZEN)
|
||||
return
|
||||
applyOrganDamage(maxHealth * decay_factor)
|
||||
|
||||
/obj/item/organ/proc/can_decay()
|
||||
if(CHECK_BITFIELD(organ_flags, ORGAN_NO_SPOIL | ORGAN_SYNTHETIC | ORGAN_FAILING))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
//Checks to see if the organ is frozen from temperature
|
||||
/obj/item/organ/proc/is_cold()
|
||||
var/freezing_objects = list(/obj/structure/closet/crate/freezer, /obj/structure/closet/secure_closet/freezer, /obj/structure/bodycontainer, /obj/item/autosurgeon)
|
||||
if(istype(loc, /obj/))//Freezer of some kind, I hope.
|
||||
if(is_type_in_list(loc, freezing_objects))
|
||||
if(!(organ_flags & ORGAN_FROZEN))//Incase someone puts them in when cold, but they warm up inside of the thing. (i.e. they have the flag, the thing turns it off, this rights it.)
|
||||
organ_flags |= ORGAN_FROZEN
|
||||
return TRUE
|
||||
return
|
||||
|
||||
var/local_temp
|
||||
if(istype(loc, /turf/))//Only concern is adding an organ to a freezer when the area around it is cold.
|
||||
var/turf/T = loc
|
||||
var/datum/gas_mixture/enviro = T.return_air()
|
||||
local_temp = enviro.temperature
|
||||
|
||||
else if(istype(loc, /mob/) && !owner)
|
||||
var/mob/M = loc
|
||||
if(is_type_in_list(M.loc, freezing_objects))
|
||||
if(!(organ_flags & ORGAN_FROZEN))
|
||||
organ_flags |= ORGAN_FROZEN
|
||||
return TRUE
|
||||
var/turf/T = M.loc
|
||||
var/datum/gas_mixture/enviro = T.return_air()
|
||||
local_temp = enviro.temperature
|
||||
|
||||
if(owner)
|
||||
//Don't interfere with bodies frozen by structures.
|
||||
if(is_type_in_list(owner.loc, freezing_objects))
|
||||
if(!(organ_flags & ORGAN_FROZEN))
|
||||
organ_flags |= ORGAN_FROZEN
|
||||
return TRUE
|
||||
local_temp = owner.bodytemperature
|
||||
|
||||
if(!local_temp)//Shouldn't happen but in case
|
||||
return
|
||||
if(local_temp < 154)//I have a pretty shaky citation that states -120 allows indefinite cyrostorage
|
||||
organ_flags |= ORGAN_FROZEN
|
||||
return TRUE
|
||||
organ_flags &= ~ORGAN_FROZEN
|
||||
return FALSE
|
||||
|
||||
/obj/item/organ/proc/on_life() //repair organ damage if the organ is not failing
|
||||
if(organ_flags & ORGAN_FAILING)
|
||||
return
|
||||
if(is_cold())
|
||||
return
|
||||
///Damage decrements by a percent of its maxhealth
|
||||
var/healing_amount = -(maxHealth * healing_factor)
|
||||
///Damage decrements again by a percent of its maxhealth, up to a total of 4 extra times depending on the owner's health
|
||||
healing_amount -= owner.satiety > 0 ? 4 * healing_factor * owner.satiety / MAX_SATIETY : 0
|
||||
applyOrganDamage(healing_amount) //to FERMI_TWEAK
|
||||
//Make it so each threshold is stuck.
|
||||
|
||||
/obj/item/organ/examine(mob/user)
|
||||
. = ..()
|
||||
if(organ_flags & ORGAN_FAILING)
|
||||
if(status == ORGAN_ROBOTIC)
|
||||
. += "<span class='warning'>[src] seems to be broken!</span>"
|
||||
return
|
||||
. += "<span class='warning'>[src] has decayed for too long, and has turned a sickly color! It doesn't look like it will work anymore!</span>"
|
||||
return
|
||||
if(damage > high_threshold)
|
||||
. += "<span class='warning'>[src] is starting to look discolored.</span>"
|
||||
|
||||
|
||||
/obj/item/organ/proc/prepare_eat()
|
||||
var/obj/item/reagent_containers/food/snacks/organ/S = new
|
||||
S.name = name
|
||||
S.desc = desc
|
||||
S.icon = icon
|
||||
S.icon_state = icon_state
|
||||
S.w_class = w_class
|
||||
|
||||
return S
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/organ
|
||||
name = "appendix"
|
||||
icon_state = "appendix"
|
||||
icon = 'icons/obj/surgery.dmi'
|
||||
list_reagents = list("nutriment" = 5)
|
||||
foodtype = RAW | MEAT | GROSS
|
||||
|
||||
|
||||
/obj/item/organ/Initialize()
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/organ/Destroy()
|
||||
if(owner)
|
||||
// The special flag is important, because otherwise mobs can die
|
||||
// while undergoing transformation into different mobs.
|
||||
Remove(owner, TRUE)
|
||||
return ..()
|
||||
|
||||
/obj/item/organ/attack(mob/living/carbon/M, mob/user)
|
||||
if(M == user && ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(status == ORGAN_ORGANIC)
|
||||
var/obj/item/reagent_containers/food/snacks/S = prepare_eat()
|
||||
if(S)
|
||||
qdel(src)
|
||||
if(H.put_in_active_hand(S))
|
||||
S.attack(H, H)
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/item/organ/item_action_slot_check(slot,mob/user)
|
||||
return //so we don't grant the organ's action to mobs who pick up the organ.
|
||||
|
||||
///Adjusts an organ's damage by the amount "d", up to a maximum amount, which is by default max damage
|
||||
/obj/item/organ/proc/applyOrganDamage(var/d, var/maximum = maxHealth) //use for damaging effects
|
||||
if(!d) //Micro-optimization.
|
||||
return
|
||||
if(maximum < damage)
|
||||
return
|
||||
damage = CLAMP(damage + d, 0, maximum)
|
||||
var/mess = check_damage_thresholds(owner)
|
||||
prev_damage = damage
|
||||
if(mess && owner)
|
||||
to_chat(owner, mess)
|
||||
|
||||
///SETS an organ's damage to the amount "d", and in doing so clears or sets the failing flag, good for when you have an effect that should fix an organ if broken
|
||||
/obj/item/organ/proc/setOrganDamage(var/d) //use mostly for admin heals
|
||||
applyOrganDamage(d - damage)
|
||||
|
||||
/** check_damage_thresholds
|
||||
* input: M (a mob, the owner of the organ we call the proc on)
|
||||
* output: returns a message should get displayed.
|
||||
* description: By checking our current damage against our previous damage, we can decide whether we've passed an organ threshold.
|
||||
* If we have, send the corresponding threshold message to the owner, if such a message exists.
|
||||
*/
|
||||
/obj/item/organ/proc/check_damage_thresholds(var/M)
|
||||
if(damage == prev_damage)
|
||||
return
|
||||
var/delta = damage - prev_damage
|
||||
if(delta > 0)
|
||||
if(damage >= maxHealth)
|
||||
organ_flags |= ORGAN_FAILING
|
||||
return now_failing
|
||||
if(damage > high_threshold && prev_damage <= high_threshold)
|
||||
return high_threshold_passed
|
||||
if(damage > low_threshold && prev_damage <= low_threshold)
|
||||
return low_threshold_passed
|
||||
else
|
||||
organ_flags &= ~ORGAN_FAILING
|
||||
if(!owner)//Processing is stopped when the organ is dead and outside of someone. This hopefully should restart it if a removed organ is repaired outside of a body.
|
||||
START_PROCESSING(SSobj, src)
|
||||
if(prev_damage > low_threshold && damage <= low_threshold)
|
||||
return low_threshold_cleared
|
||||
if(prev_damage > high_threshold && damage <= high_threshold)
|
||||
return high_threshold_cleared
|
||||
if(prev_damage == maxHealth)
|
||||
return now_fixed
|
||||
|
||||
//Runs some code on the organ when damage is taken/healed
|
||||
/obj/item/organ/proc/onDamage(var/d, var/maximum = maxHealth)
|
||||
return
|
||||
|
||||
//Runs some code on the organ when damage is taken/healed
|
||||
/obj/item/organ/proc/onSetDamage(var/d, var/maximum = maxHealth)
|
||||
return
|
||||
|
||||
//Looking for brains?
|
||||
//Try code/modules/mob/living/carbon/brain/brain_item.dm
|
||||
|
||||
/mob/living/proc/regenerate_organs()
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/regenerate_organs(only_one = FALSE)
|
||||
var/breathes = TRUE
|
||||
var/blooded = TRUE
|
||||
if(dna && dna.species)
|
||||
if(HAS_TRAIT_FROM(src, TRAIT_NOBREATH, SPECIES_TRAIT))
|
||||
breathes = FALSE
|
||||
if(NOBLOOD in dna.species.species_traits)
|
||||
blooded = FALSE
|
||||
var/has_liver = (!(NOLIVER in dna.species.species_traits))
|
||||
var/has_stomach = (!(NOSTOMACH in dna.species.species_traits))
|
||||
|
||||
if(has_liver && !getorganslot(ORGAN_SLOT_LIVER))
|
||||
var/obj/item/organ/liver/LI
|
||||
|
||||
if(dna.species.mutantliver)
|
||||
LI = new dna.species.mutantliver()
|
||||
else
|
||||
LI = new()
|
||||
LI.Insert(src)
|
||||
if(only_one)
|
||||
return TRUE
|
||||
|
||||
if(has_stomach && !getorganslot(ORGAN_SLOT_STOMACH))
|
||||
var/obj/item/organ/stomach/S
|
||||
|
||||
if(dna.species.mutantstomach)
|
||||
S = new dna.species.mutantstomach()
|
||||
else
|
||||
S = new()
|
||||
S.Insert(src)
|
||||
if(only_one)
|
||||
return TRUE
|
||||
|
||||
if(breathes && !getorganslot(ORGAN_SLOT_LUNGS))
|
||||
var/obj/item/organ/lungs/L = new()
|
||||
L.Insert(src)
|
||||
if(only_one)
|
||||
return TRUE
|
||||
|
||||
if(blooded && !getorganslot(ORGAN_SLOT_HEART))
|
||||
var/obj/item/organ/heart/H = new()
|
||||
H.Insert(src)
|
||||
if(only_one)
|
||||
return TRUE
|
||||
|
||||
if(!getorganslot(ORGAN_SLOT_TONGUE))
|
||||
var/obj/item/organ/tongue/T
|
||||
|
||||
if(dna && dna.species && dna.species.mutanttongue)
|
||||
T = new dna.species.mutanttongue()
|
||||
else
|
||||
T = new()
|
||||
|
||||
// if they have no mutant tongues, give them a regular one
|
||||
T.Insert(src)
|
||||
if(only_one)
|
||||
return TRUE
|
||||
|
||||
else if (!only_one)
|
||||
var/obj/item/organ/tongue/oT = getorganslot(ORGAN_SLOT_TONGUE)
|
||||
if(oT.name == "fluffy tongue")
|
||||
var/obj/item/organ/tongue/T
|
||||
if(dna && dna.species && dna.species.mutanttongue)
|
||||
T = new dna.species.mutanttongue()
|
||||
else
|
||||
T = new()
|
||||
oT.Remove(src)
|
||||
qdel(oT)
|
||||
T.Insert(src)
|
||||
|
||||
if(!getorganslot(ORGAN_SLOT_EYES))
|
||||
var/obj/item/organ/eyes/E
|
||||
|
||||
if(dna && dna.species && dna.species.mutanteyes)
|
||||
E = new dna.species.mutanteyes()
|
||||
|
||||
else
|
||||
E = new()
|
||||
E.Insert(src)
|
||||
if(only_one)
|
||||
return TRUE
|
||||
|
||||
if(!getorganslot(ORGAN_SLOT_EARS))
|
||||
var/obj/item/organ/ears/ears
|
||||
if(dna && dna.species && dna.species.mutantears)
|
||||
ears = new dna.species.mutantears
|
||||
else
|
||||
ears = new
|
||||
|
||||
ears.Insert(src)
|
||||
if(only_one)
|
||||
return TRUE
|
||||
|
||||
if(!getorganslot(ORGAN_SLOT_TAIL))
|
||||
var/obj/item/organ/tail/tail
|
||||
if(dna && dna.species && dna.species.mutanttail)
|
||||
tail = new dna.species.mutanttail
|
||||
tail.Insert(src)
|
||||
if(only_one)
|
||||
return TRUE
|
||||
@@ -0,0 +1,293 @@
|
||||
#define TONGUE_MAX_HEALTH 60
|
||||
|
||||
/obj/item/organ/tongue
|
||||
name = "tongue"
|
||||
desc = "A fleshy muscle mostly used for lying."
|
||||
icon_state = "tonguenormal"
|
||||
zone = BODY_ZONE_PRECISE_MOUTH
|
||||
slot = ORGAN_SLOT_TONGUE
|
||||
attack_verb = list("licked", "slobbered", "slapped", "frenched", "tongued")
|
||||
var/list/languages_possible
|
||||
var/say_mod = null
|
||||
var/taste_sensitivity = 15 // lower is more sensitive.
|
||||
maxHealth = TONGUE_MAX_HEALTH
|
||||
var/modifies_speech = FALSE
|
||||
var/static/list/languages_possible_base = typecacheof(list(
|
||||
/datum/language/common,
|
||||
/datum/language/draconic,
|
||||
/datum/language/codespeak,
|
||||
/datum/language/monkey,
|
||||
/datum/language/narsie,
|
||||
/datum/language/beachbum,
|
||||
/datum/language/ratvar,
|
||||
/datum/language/aphasia,
|
||||
/datum/language/slime,
|
||||
))
|
||||
healing_factor = STANDARD_ORGAN_HEALING*5 //Fast!!
|
||||
decay_factor = STANDARD_ORGAN_DECAY/2
|
||||
|
||||
/obj/item/organ/tongue/Initialize(mapload)
|
||||
. = ..()
|
||||
languages_possible = languages_possible_base
|
||||
|
||||
/obj/item/organ/tongue/proc/handle_speech(datum/source, list/speech_args)
|
||||
|
||||
/obj/item/organ/tongue/emp_act(severity)
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
if(organ_flags & ORGAN_SYNTHETIC)
|
||||
var/errormessage = list("Runtime in tongue.dm, line 39: Undefined operation \"zapzap ow my tongue\"", "afhsjifksahgjkaslfhashfjsak", "-1.#IND", "Graham's number", "inside you all along", "awaiting at least 1 approving review before merging this taste request")
|
||||
owner.say("The pH is appropriately [pick(errormessage)].")
|
||||
|
||||
/obj/item/organ/tongue/applyOrganDamage(var/d, var/maximum = maxHealth)
|
||||
|
||||
if(!d) //Micro-optimization.
|
||||
return
|
||||
if(maximum < damage)
|
||||
return
|
||||
damage = CLAMP(damage + d, 0, maximum)
|
||||
var/mess = check_damage_thresholds(owner)
|
||||
prev_damage = damage
|
||||
if(mess && owner)
|
||||
to_chat(owner, mess)
|
||||
|
||||
if ((damage / maxHealth) > 1)
|
||||
to_chat(owner, "<span class='userdanger'>Your tongue is singed beyond recognition, and disintegrates!</span>")
|
||||
SSblackbox.record_feedback("tally", "fermi_chem", 1, "Tongues lost to Fermi")
|
||||
qdel(src)
|
||||
else if ((damage / maxHealth) > 0.85)
|
||||
to_chat(owner, "<span class='warning'>Your tongue feels like it's about to fall out!.</span>")
|
||||
else if ((damage / maxHealth) > 0.5)
|
||||
to_chat(owner, "<span class='notice'>Your tongue is really starting to hurt.</span>")
|
||||
|
||||
|
||||
/obj/item/organ/tongue/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
|
||||
..()
|
||||
if(say_mod && M.dna && M.dna.species)
|
||||
M.dna.species.say_mod = say_mod
|
||||
if (modifies_speech)
|
||||
RegisterSignal(M, COMSIG_MOB_SAY, .proc/handle_speech)
|
||||
M.UnregisterSignal(M, COMSIG_MOB_SAY)
|
||||
|
||||
/obj/item/organ/tongue/Remove(mob/living/carbon/M, special = 0)
|
||||
..()
|
||||
if(say_mod && M.dna && M.dna.species)
|
||||
M.dna.species.say_mod = initial(M.dna.species.say_mod)
|
||||
UnregisterSignal(M, COMSIG_MOB_SAY, .proc/handle_speech)
|
||||
M.RegisterSignal(M, COMSIG_MOB_SAY, /mob/living/carbon/.proc/handle_tongueless_speech)
|
||||
|
||||
/obj/item/organ/tongue/could_speak_in_language(datum/language/dt)
|
||||
return is_type_in_typecache(dt, languages_possible)
|
||||
|
||||
/obj/item/organ/tongue/lizard
|
||||
name = "forked tongue"
|
||||
desc = "A thin and long muscle typically found in reptilian races, apparently moonlights as a nose."
|
||||
icon_state = "tonguelizard"
|
||||
say_mod = "hisses"
|
||||
taste_sensitivity = 10 // combined nose + tongue, extra sensitive
|
||||
maxHealth = 40 //extra sensitivity means tongue is more susceptible to damage
|
||||
modifies_speech = TRUE
|
||||
|
||||
/obj/item/organ/tongue/lizard/handle_speech(datum/source, list/speech_args)
|
||||
var/static/regex/lizard_hiss = new("s+", "g")
|
||||
var/static/regex/lizard_hiSS = new("S+", "g")
|
||||
var/message = speech_args[SPEECH_MESSAGE]
|
||||
if(message[1] != "*")
|
||||
message = lizard_hiss.Replace(message, "sss")
|
||||
message = lizard_hiSS.Replace(message, "SSS")
|
||||
speech_args[SPEECH_MESSAGE] = message
|
||||
|
||||
/obj/item/organ/tongue/fly
|
||||
name = "proboscis"
|
||||
desc = "A freakish looking meat tube that apparently can take in liquids."
|
||||
icon_state = "tonguefly"
|
||||
say_mod = "buzzes"
|
||||
taste_sensitivity = 25 // you eat vomit, this is a mercy
|
||||
maxHealth = 80 //years of eatting trash has made your tongue strong
|
||||
modifies_speech = TRUE
|
||||
|
||||
/obj/item/organ/tongue/fly/handle_speech(datum/source, list/speech_args)
|
||||
var/static/regex/fly_buzz = new("z+", "g")
|
||||
var/static/regex/fly_buZZ = new("Z+", "g")
|
||||
var/message = speech_args[SPEECH_MESSAGE]
|
||||
if(message[1] != "*")
|
||||
message = fly_buzz.Replace(message, "zzz")
|
||||
message = fly_buZZ.Replace(message, "ZZZ")
|
||||
speech_args[SPEECH_MESSAGE] = message
|
||||
|
||||
/obj/item/organ/tongue/abductor
|
||||
name = "superlingual matrix"
|
||||
desc = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something."
|
||||
icon_state = "tongueayylmao"
|
||||
say_mod = "gibbers"
|
||||
taste_sensitivity = 101 // ayys cannot taste anything.
|
||||
maxHealth = 120 //Ayys probe a lot
|
||||
modifies_speech = TRUE
|
||||
|
||||
/obj/item/organ/tongue/abductor/handle_speech(datum/source, list/speech_args)
|
||||
//Hacks
|
||||
var/message = speech_args[SPEECH_MESSAGE]
|
||||
var/mob/living/carbon/human/user = usr
|
||||
var/rendered = "<span class='abductor'><b>[user.name]:</b> [message]</span>"
|
||||
user.log_talk(message, LOG_SAY, tag="abductor")
|
||||
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
|
||||
var/obj/item/organ/tongue/T = H.getorganslot(ORGAN_SLOT_TONGUE)
|
||||
if(!T || T.type != type)
|
||||
continue
|
||||
if(H.dna && H.dna.species.id == "abductor" && user.dna && user.dna.species.id == "abductor")
|
||||
var/datum/antagonist/abductor/A = user.mind.has_antag_datum(/datum/antagonist/abductor)
|
||||
if(!A || !(H.mind in A.team.members))
|
||||
continue
|
||||
to_chat(H, rendered)
|
||||
for(var/mob/M in GLOB.dead_mob_list)
|
||||
var/link = FOLLOW_LINK(M, user)
|
||||
to_chat(M, "[link] [rendered]")
|
||||
speech_args[SPEECH_MESSAGE] = ""
|
||||
|
||||
/obj/item/organ/tongue/zombie
|
||||
name = "rotting tongue"
|
||||
desc = "Between the decay and the fact that it's just lying there you doubt a tongue has ever seemed less sexy."
|
||||
icon_state = "tonguezombie"
|
||||
say_mod = "moans"
|
||||
taste_sensitivity = 32
|
||||
maxHealth = 65 //Stop! It's already dead...!
|
||||
modifies_speech = TRUE
|
||||
|
||||
/obj/item/organ/tongue/zombie/handle_speech(datum/source, list/speech_args)
|
||||
var/list/message_list = splittext(speech_args[SPEECH_MESSAGE], " ")
|
||||
var/maxchanges = max(round(message_list.len / 1.5), 2)
|
||||
|
||||
for(var/i = rand(maxchanges / 2, maxchanges), i > 0, i--)
|
||||
var/insertpos = rand(1, message_list.len - 1)
|
||||
var/inserttext = message_list[insertpos]
|
||||
|
||||
if(!(copytext(inserttext, length(inserttext) - 2) == "..."))
|
||||
message_list[insertpos] = inserttext + "..."
|
||||
|
||||
if(prob(20) && message_list.len > 3)
|
||||
message_list.Insert(insertpos, "[pick("BRAINS", "Brains", "Braaaiinnnsss", "BRAAAIIINNSSS")]...")
|
||||
|
||||
speech_args[SPEECH_MESSAGE] = jointext(message_list, " ")
|
||||
|
||||
/obj/item/organ/tongue/alien
|
||||
name = "alien tongue"
|
||||
desc = "According to leading xenobiologists the evolutionary benefit of having a second mouth in your mouth is \"that it looks badass\"."
|
||||
icon_state = "tonguexeno"
|
||||
say_mod = "hisses"
|
||||
taste_sensitivity = 10 // LIZARDS ARE ALIENS CONFIRMED
|
||||
maxHealth = 500 //They've a little mouth for a tongue, so it's pretty rhobust
|
||||
modifies_speech = TRUE // not really, they just hiss
|
||||
var/static/list/languages_possible_alien = typecacheof(list(
|
||||
/datum/language/xenocommon,
|
||||
/datum/language/common,
|
||||
/datum/language/draconic,
|
||||
/datum/language/ratvar,
|
||||
/datum/language/monkey))
|
||||
|
||||
/obj/item/organ/tongue/alien/Initialize(mapload)
|
||||
. = ..()
|
||||
languages_possible = languages_possible_alien
|
||||
|
||||
/obj/item/organ/tongue/alien/handle_speech(datum/source, list/speech_args)
|
||||
playsound(owner, "hiss", 25, 1, 1)
|
||||
|
||||
/obj/item/organ/tongue/bone
|
||||
name = "bone \"tongue\""
|
||||
desc = "Apparently skeletons alter the sounds they produce through oscillation of their teeth, hence their characteristic rattling."
|
||||
icon_state = "tonguebone"
|
||||
say_mod = "rattles"
|
||||
organ_flags = ORGAN_NO_SPOIL
|
||||
attack_verb = list("bitten", "chattered", "chomped", "enamelled", "boned")
|
||||
taste_sensitivity = 101 // skeletons cannot taste anything
|
||||
maxHealth = 75 //Take brute damage instead
|
||||
modifies_speech = TRUE
|
||||
var/chattering = FALSE
|
||||
var/phomeme_type = "sans"
|
||||
var/list/phomeme_types = list("sans", "papyrus")
|
||||
|
||||
/obj/item/organ/tongue/bone/Initialize()
|
||||
. = ..()
|
||||
phomeme_type = pick(phomeme_types)
|
||||
|
||||
/obj/item/organ/tongue/bone/applyOrganDamage(var/d, var/maximum = maxHealth)
|
||||
if(d < 0)
|
||||
return
|
||||
if(!owner)
|
||||
return
|
||||
var/target = owner.get_bodypart(BODY_ZONE_HEAD)
|
||||
owner.apply_damage(d, BURN, target)
|
||||
to_chat(owner, "<span class='userdanger'>You feel your skull burning! Oof, your bones!</span>")
|
||||
return
|
||||
|
||||
/obj/item/organ/tongue/bone/handle_speech(datum/source, list/speech_args)
|
||||
if (chattering)
|
||||
chatter(speech_args[SPEECH_MESSAGE], phomeme_type, source)
|
||||
switch(phomeme_type)
|
||||
if("sans")
|
||||
speech_args[SPEECH_SPANS] |= SPAN_SANS
|
||||
if("papyrus")
|
||||
speech_args[SPEECH_SPANS] |= SPAN_PAPYRUS
|
||||
|
||||
/obj/item/organ/tongue/bone/plasmaman
|
||||
name = "plasma bone \"tongue\""
|
||||
desc = "Like animated skeletons, Plasmamen vibrate their teeth in order to produce speech."
|
||||
icon_state = "tongueplasma"
|
||||
modifies_speech = FALSE
|
||||
|
||||
/obj/item/organ/tongue/robot
|
||||
name = "robotic voicebox"
|
||||
desc = "A voice synthesizer that can interface with organic lifeforms."
|
||||
status = ORGAN_ROBOTIC
|
||||
icon_state = "tonguerobot"
|
||||
say_mod = "states"
|
||||
attack_verb = list("beeped", "booped")
|
||||
modifies_speech = TRUE
|
||||
taste_sensitivity = 25 // not as good as an organic tongue
|
||||
maxHealth = 100 //RoboTongue!
|
||||
var/electronics_magic = TRUE
|
||||
|
||||
/obj/item/organ/tongue/robot/can_speak_in_language(datum/language/dt)
|
||||
return ..() || electronics_magic
|
||||
|
||||
/obj/item/organ/tongue/robot/handle_speech(datum/source, list/speech_args)
|
||||
speech_args[SPEECH_SPANS] |= SPAN_ROBOT
|
||||
|
||||
/obj/item/organ/tongue/fluffy
|
||||
name = "fluffy tongue"
|
||||
desc = "OwO what's this?"
|
||||
icon_state = "tonguefluffy"
|
||||
taste_sensitivity = 10 // extra sensitive and inquisitive uwu
|
||||
maxHealth = 35 //Sensitive tongue!
|
||||
modifies_speech = TRUE
|
||||
|
||||
/obj/item/organ/tongue/fluffy/handle_speech(datum/source, list/speech_args)
|
||||
var/message = speech_args[SPEECH_MESSAGE]
|
||||
if(copytext(message, 1, 2) != "*")
|
||||
message = replacetext(message, "ne", "nye")
|
||||
message = replacetext(message, "nu", "nyu")
|
||||
message = replacetext(message, "na", "nya")
|
||||
message = replacetext(message, "no", "nyo")
|
||||
message = replacetext(message, "ove", "uv")
|
||||
message = replacetext(message, "l", "w")
|
||||
message = replacetext(message, "r", "w")
|
||||
message = lowertext(message)
|
||||
speech_args[SPEECH_MESSAGE] = message
|
||||
|
||||
/obj/item/organ/tongue/cybernetic
|
||||
name = "cybernetic tongue"
|
||||
desc = "A state of the art robotic tongue that can detect the pH of anything drank."
|
||||
icon_state = "tonguecybernetic"
|
||||
taste_sensitivity = 10
|
||||
maxHealth = 60 //It's robotic!
|
||||
organ_flags = ORGAN_SYNTHETIC
|
||||
|
||||
/obj/item/organ/tongue/cybernetic/handle_speech(datum/source, list/speech_args)
|
||||
speech_args[SPEECH_SPANS] |= SPAN_ROBOT
|
||||
|
||||
/obj/item/organ/tongue/robot/ipc
|
||||
name = "positronic voicebox"
|
||||
say_mod = "beeps"
|
||||
desc = "A voice synthesizer used by IPCs to smoothly interface with organic lifeforms."
|
||||
electronics_magic = FALSE
|
||||
organ_flags = ORGAN_SYNTHETIC
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user