mirror of
https://github.com/KabKebab/GS13.git
synced 2026-07-20 12:29:32 +01:00
Uploading all files.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/obj/item/organ/appendix
|
||||
name = "appendix"
|
||||
icon_state = "appendix"
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_APPENDIX
|
||||
var/inflamed = 0
|
||||
|
||||
/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 = 1
|
||||
update_icon()
|
||||
..()
|
||||
|
||||
/obj/item/organ/appendix/Insert(mob/living/carbon/M, special = 0)
|
||||
..()
|
||||
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,235 @@
|
||||
/obj/item/organ/cyberimp/arm
|
||||
name = "arm-mounted implant"
|
||||
desc = "You shouldn't see this! Adminhelp and report this as an issue on github!"
|
||||
zone = BODY_ZONE_R_ARM
|
||||
icon_state = "implant-toolkit"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
actions_types = list(/datum/action/item_action/organ_action/toggle)
|
||||
|
||||
var/list/items_list = list()
|
||||
// Used to store a list of all items inside, for multi-item implants.
|
||||
// I would use contents, but they shuffle on every activation/deactivation leading to interface inconsistencies.
|
||||
|
||||
var/obj/item/holder = null
|
||||
// You can use this var for item path, it would be converted into an item on New()
|
||||
|
||||
/obj/item/organ/cyberimp/arm/Initialize()
|
||||
. = ..()
|
||||
if(ispath(holder))
|
||||
holder = new holder(src)
|
||||
|
||||
update_icon()
|
||||
SetSlotFromZone()
|
||||
items_list = contents.Copy()
|
||||
|
||||
/obj/item/organ/cyberimp/arm/proc/SetSlotFromZone()
|
||||
switch(zone)
|
||||
if(BODY_ZONE_L_ARM)
|
||||
slot = ORGAN_SLOT_LEFT_ARM_AUG
|
||||
if(BODY_ZONE_R_ARM)
|
||||
slot = ORGAN_SLOT_RIGHT_ARM_AUG
|
||||
else
|
||||
CRASH("Invalid zone for [type]")
|
||||
|
||||
/obj/item/organ/cyberimp/arm/update_icon()
|
||||
if(zone == BODY_ZONE_R_ARM)
|
||||
transform = null
|
||||
else // Mirroring the icon
|
||||
transform = matrix(-1, 0, 0, 0, 1, 0)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/examine(mob/user)
|
||||
..()
|
||||
to_chat(user, "<span class='info'>[src] is assembled in the [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm configuration. You can use a screwdriver to reassemble it.</span>")
|
||||
|
||||
/obj/item/organ/cyberimp/arm/screwdriver_act(mob/living/user, obj/item/I)
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
I.play_tool_sound(src)
|
||||
if(zone == BODY_ZONE_R_ARM)
|
||||
zone = BODY_ZONE_L_ARM
|
||||
else
|
||||
zone = BODY_ZONE_R_ARM
|
||||
SetSlotFromZone()
|
||||
to_chat(user, "<span class='notice'>You modify [src] to be installed on the [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.</span>")
|
||||
update_icon()
|
||||
|
||||
/obj/item/organ/cyberimp/arm/Remove(mob/living/carbon/M, special = 0)
|
||||
Retract()
|
||||
..()
|
||||
|
||||
/obj/item/organ/cyberimp/arm/emp_act(severity)
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
if(prob(15/severity) && owner)
|
||||
to_chat(owner, "<span class='warning'>[src] is hit by EMP!</span>")
|
||||
// give the owner an idea about why his implant is glitching
|
||||
Retract()
|
||||
|
||||
/obj/item/organ/cyberimp/arm/proc/Retract()
|
||||
if(!holder || (holder in src))
|
||||
return
|
||||
|
||||
owner.visible_message("<span class='notice'>[owner] retracts [holder] back into [owner.p_their()] [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.</span>",
|
||||
"<span class='notice'>[holder] snaps back into your [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.</span>",
|
||||
"<span class='italics'>You hear a short mechanical noise.</span>")
|
||||
|
||||
if(istype(holder, /obj/item/assembly/flash/armimplant))
|
||||
var/obj/item/assembly/flash/F = holder
|
||||
F.set_light(0)
|
||||
|
||||
owner.transferItemToLoc(holder, src, TRUE)
|
||||
holder = null
|
||||
playsound(get_turf(owner), 'sound/mecha/mechmove03.ogg', 50, 1)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/proc/Extend(var/obj/item/item)
|
||||
if(!(item in src))
|
||||
return
|
||||
|
||||
holder = item
|
||||
|
||||
ADD_TRAIT(holder, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT)
|
||||
holder.resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
holder.slot_flags = null
|
||||
holder.materials = null
|
||||
|
||||
if(istype(holder, /obj/item/assembly/flash/armimplant))
|
||||
var/obj/item/assembly/flash/F = holder
|
||||
F.set_light(7)
|
||||
|
||||
var/obj/item/arm_item = owner.get_active_held_item()
|
||||
|
||||
if(arm_item)
|
||||
if(!owner.dropItemToGround(arm_item))
|
||||
to_chat(owner, "<span class='warning'>Your [arm_item] interferes with [src]!</span>")
|
||||
return
|
||||
else
|
||||
to_chat(owner, "<span class='notice'>You drop [arm_item] to activate [src]!</span>")
|
||||
|
||||
var/result = (zone == BODY_ZONE_R_ARM ? owner.put_in_r_hand(holder) : owner.put_in_l_hand(holder))
|
||||
if(!result)
|
||||
to_chat(owner, "<span class='warning'>Your [name] fails to activate!</span>")
|
||||
return
|
||||
|
||||
// Activate the hand that now holds our item.
|
||||
owner.swap_hand(result)//... or the 1st hand if the index gets lost somehow
|
||||
|
||||
owner.visible_message("<span class='notice'>[owner] extends [holder] from [owner.p_their()] [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.</span>",
|
||||
"<span class='notice'>You extend [holder] from your [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.</span>",
|
||||
"<span class='italics'>You hear a short mechanical noise.</span>")
|
||||
playsound(get_turf(owner), 'sound/mecha/mechmove03.ogg', 50, 1)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/ui_action_click()
|
||||
if(crit_fail || (!holder && !contents.len))
|
||||
to_chat(owner, "<span class='warning'>The implant doesn't respond. It seems to be broken...</span>")
|
||||
return
|
||||
|
||||
if(!holder || (holder in src))
|
||||
holder = null
|
||||
if(contents.len == 1)
|
||||
Extend(contents[1])
|
||||
else
|
||||
var/list/choice_list = list()
|
||||
for(var/obj/item/I in items_list)
|
||||
choice_list[I] = image(I)
|
||||
var/obj/item/choice = show_radial_menu(owner, owner, choice_list)
|
||||
if(owner && owner == usr && owner.stat != DEAD && (src in owner.internal_organs) && !holder && (choice in contents))
|
||||
// This monster sanity check is a nice example of how bad input is.
|
||||
Extend(choice)
|
||||
else
|
||||
Retract()
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/arm/gun/emp_act(severity)
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
if(prob(30/severity) && owner && !crit_fail)
|
||||
Retract()
|
||||
owner.visible_message("<span class='danger'>A loud bang comes from [owner]\'s [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm!</span>")
|
||||
playsound(get_turf(owner), 'sound/weapons/flashbang.ogg', 100, 1)
|
||||
to_chat(owner, "<span class='userdanger'>You feel an explosion erupt inside your [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm as your implant breaks!</span>")
|
||||
owner.adjust_fire_stacks(20)
|
||||
owner.IgniteMob()
|
||||
owner.adjustFireLoss(25)
|
||||
crit_fail = 1
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/arm/gun/laser
|
||||
name = "arm-mounted laser implant"
|
||||
desc = "A variant of the arm cannon implant that fires lethal laser beams. The cannon emerges from the subject's arm and remains inside when not in use."
|
||||
icon_state = "arm_laser"
|
||||
contents = newlist(/obj/item/gun/energy/laser/mounted)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/gun/laser/l
|
||||
zone = BODY_ZONE_L_ARM
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/arm/gun/taser
|
||||
name = "arm-mounted taser implant"
|
||||
desc = "A variant of the arm cannon implant that fires electrodes and disabler shots. The cannon emerges from the subject's arm and remains inside when not in use."
|
||||
icon_state = "arm_taser"
|
||||
contents = newlist(/obj/item/gun/energy/e_gun/advtaser/mounted)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/gun/taser/l
|
||||
zone = BODY_ZONE_L_ARM
|
||||
|
||||
/obj/item/organ/cyberimp/arm/toolset
|
||||
name = "integrated toolset implant"
|
||||
desc = "A stripped-down version of the engineering cyborg toolset, designed to be installed on subject's arm. Contains all necessary tools."
|
||||
contents = newlist(/obj/item/screwdriver/cyborg, /obj/item/wrench/cyborg, /obj/item/weldingtool/largetank/cyborg,
|
||||
/obj/item/crowbar/cyborg, /obj/item/wirecutters/cyborg, /obj/item/multitool/cyborg)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/toolset/l
|
||||
zone = BODY_ZONE_L_ARM
|
||||
|
||||
/obj/item/organ/cyberimp/arm/toolset/emag_act()
|
||||
if(!(locate(/obj/item/kitchen/knife/combat/cyborg) in items_list))
|
||||
to_chat(usr, "<span class='notice'>You unlock [src]'s integrated knife!</span>")
|
||||
items_list += new /obj/item/kitchen/knife/combat/cyborg(src)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/item/organ/cyberimp/arm/esword
|
||||
name = "arm-mounted energy blade"
|
||||
desc = "An illegal and highly dangerous cybernetic implant that can project a deadly blade of concentrated energy."
|
||||
contents = newlist(/obj/item/melee/transforming/energy/blade/hardlight)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/medibeam
|
||||
name = "integrated medical beamgun"
|
||||
desc = "A cybernetic implant that allows the user to project a healing beam from their hand."
|
||||
contents = newlist(/obj/item/gun/medbeam)
|
||||
|
||||
|
||||
/obj/item/organ/cyberimp/arm/flash
|
||||
name = "integrated high-intensity photon projector" //Why not
|
||||
desc = "An integrated projector mounted onto a user's arm that is able to be used as a powerful flash."
|
||||
contents = newlist(/obj/item/assembly/flash/armimplant)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/flash/Initialize()
|
||||
. = ..()
|
||||
if(locate(/obj/item/assembly/flash/armimplant) in items_list)
|
||||
var/obj/item/assembly/flash/armimplant/F = locate(/obj/item/assembly/flash/armimplant) in items_list
|
||||
F.I = src
|
||||
|
||||
/obj/item/organ/cyberimp/arm/baton
|
||||
name = "arm electrification implant"
|
||||
desc = "An illegal combat implant that allows the user to administer disabling shocks from their arm."
|
||||
contents = newlist(/obj/item/borg/stun)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/combat
|
||||
name = "combat cybernetics implant"
|
||||
desc = "A powerful cybernetic implant that contains combat modules built into the user's arm."
|
||||
contents = newlist(/obj/item/melee/transforming/energy/blade/hardlight, /obj/item/gun/medbeam, /obj/item/borg/stun, /obj/item/assembly/flash/armimplant)
|
||||
|
||||
/obj/item/organ/cyberimp/arm/combat/Initialize()
|
||||
. = ..()
|
||||
if(locate(/obj/item/assembly/flash/armimplant) in items_list)
|
||||
var/obj/item/assembly/flash/armimplant/F = locate(/obj/item/assembly/flash/armimplant) in items_list
|
||||
F.I = src
|
||||
|
||||
/obj/item/organ/cyberimp/arm/surgery
|
||||
name = "surgical toolset implant"
|
||||
desc = "A set of surgical tools hidden behind a concealed panel on the user's arm."
|
||||
contents = newlist(/obj/item/retractor/augment, /obj/item/hemostat/augment, /obj/item/cautery/augment, /obj/item/surgicaldrill/augment, /obj/item/scalpel/augment, /obj/item/circular_saw/augment, /obj/item/surgical_drapes)
|
||||
@@ -0,0 +1,202 @@
|
||||
/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)
|
||||
..()
|
||||
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)
|
||||
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()
|
||||
if(!silent)
|
||||
to_chat(owner, "<span class='notice'>You turn your thrusters set on.</span>")
|
||||
else
|
||||
ion_trail.stop()
|
||||
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/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,42 @@
|
||||
/obj/item/organ/cyberimp/eyes/hud
|
||||
name = "cybernetic hud"
|
||||
desc = "artificial photoreceptors with specialized functionality"
|
||||
icon_state = "eye_implant"
|
||||
implant_overlay = "eye_implant_overlay"
|
||||
slot = ORGAN_SLOT_EYES
|
||||
zone = BODY_ZONE_PRECISE_EYES
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
|
||||
// HUD implants
|
||||
/obj/item/organ/cyberimp/eyes/hud
|
||||
name = "HUD implant"
|
||||
desc = "These cybernetic eyes will display a HUD over everything you see. Maybe."
|
||||
slot = ORGAN_SLOT_HUD
|
||||
var/HUD_type = 0
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/hud/Insert(var/mob/living/carbon/M, var/special = 0, drop_if_replaced = FALSE)
|
||||
..()
|
||||
if(HUD_type)
|
||||
var/datum/atom_hud/H = GLOB.huds[HUD_type]
|
||||
H.add_hud_to(M)
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/hud/Remove(var/mob/living/carbon/M, var/special = 0)
|
||||
if(HUD_type)
|
||||
var/datum/atom_hud/H = GLOB.huds[HUD_type]
|
||||
H.remove_hud_from(M)
|
||||
..()
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/hud/medical
|
||||
name = "Medical HUD implant"
|
||||
desc = "These cybernetic eye implants will display a medical HUD over everything you see."
|
||||
HUD_type = DATA_HUD_MEDICAL_ADVANCED
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/hud/security
|
||||
name = "Security HUD implant"
|
||||
desc = "These cybernetic eye implants will display a security HUD over everything you see."
|
||||
HUD_type = DATA_HUD_SECURITY_ADVANCED
|
||||
|
||||
/obj/item/organ/cyberimp/eyes/hud/security/syndicate
|
||||
name = "Contraband Security HUD Implant"
|
||||
desc = "A Cybersun Industries brand Security HUD Implant. These illicit cybernetic eye implants will display a security HUD over everything you see."
|
||||
syndicate_implant = TRUE
|
||||
@@ -0,0 +1,160 @@
|
||||
#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
|
||||
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)
|
||||
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 || . & EMP_PROTECT_SELF)
|
||||
return
|
||||
crit_fail = TRUE
|
||||
addtimer(CALLBACK(src, .proc/reboot), 90 / severity)
|
||||
|
||||
/obj/item/organ/cyberimp/brain/anti_stun/proc/reboot()
|
||||
crit_fail = FALSE
|
||||
|
||||
|
||||
//[[[[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
|
||||
|
||||
//BOX O' IMPLANTS
|
||||
|
||||
/obj/item/storage/box/cyber_implants
|
||||
name = "boxed cybernetic implants"
|
||||
desc = "A sleek, sturdy box."
|
||||
icon_state = "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)
|
||||
@@ -0,0 +1,125 @@
|
||||
#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/I)
|
||||
storedorgan = I
|
||||
I.forceMove(src)
|
||||
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/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,102 @@
|
||||
/obj/item/organ/ears
|
||||
name = "ears"
|
||||
icon_state = "ears"
|
||||
desc = "There are three parts to the ear. Inner, middle and outer. Only one of these parts should be normally visible."
|
||||
zone = BODY_ZONE_HEAD
|
||||
slot = ORGAN_SLOT_EARS
|
||||
gender = PLURAL
|
||||
|
||||
// `deaf` measures "ticks" of deafness. While > 0, the person is unable
|
||||
// to hear anything.
|
||||
var/deaf = 0
|
||||
|
||||
// `ear_damage` measures long term damage to the ears, if too high,
|
||||
// the person will not have either `deaf` or `ear_damage` decrease
|
||||
// without external aid (earmuffs, drugs)
|
||||
var/ear_damage = 0
|
||||
|
||||
//Resistance against loud noises
|
||||
var/bang_protect = 0
|
||||
// Multiplier for both long term and short term ear damage
|
||||
var/damage_multiplier = 1
|
||||
|
||||
/obj/item/organ/ears/on_life()
|
||||
if(!iscarbon(owner))
|
||||
return
|
||||
var/mob/living/carbon/C = owner
|
||||
// genetic deafness prevents the body from using the ears, even if healthy
|
||||
if(HAS_TRAIT(C, TRAIT_DEAF))
|
||||
deaf = max(deaf, 1)
|
||||
else if(ear_damage < UNHEALING_EAR_DAMAGE) // if higher than UNHEALING_EAR_DAMAGE, no natural healing occurs.
|
||||
ear_damage = max(ear_damage - 0.05, 0)
|
||||
deaf = max(deaf - 1, 0)
|
||||
|
||||
/obj/item/organ/ears/proc/restoreEars()
|
||||
deaf = 0
|
||||
ear_damage = 0
|
||||
|
||||
var/mob/living/carbon/C = owner
|
||||
|
||||
if(iscarbon(owner) && HAS_TRAIT(C, TRAIT_DEAF))
|
||||
deaf = 1
|
||||
|
||||
/obj/item/organ/ears/proc/adjustEarDamage(ddmg, ddeaf)
|
||||
ear_damage = max(ear_damage + (ddmg*damage_multiplier), 0)
|
||||
deaf = max(deaf + (ddeaf*damage_multiplier), 0)
|
||||
|
||||
/obj/item/organ/ears/proc/minimumDeafTicks(value)
|
||||
deaf = max(deaf, value)
|
||||
|
||||
/obj/item/organ/ears/invincible
|
||||
damage_multiplier = 0
|
||||
|
||||
|
||||
/mob/proc/restoreEars()
|
||||
|
||||
/mob/living/carbon/restoreEars()
|
||||
var/obj/item/organ/ears/ears = getorgan(/obj/item/organ/ears)
|
||||
if(ears)
|
||||
ears.restoreEars()
|
||||
|
||||
/mob/proc/adjustEarDamage()
|
||||
|
||||
/mob/living/carbon/adjustEarDamage(ddmg, ddeaf)
|
||||
var/obj/item/organ/ears/ears = getorgan(/obj/item/organ/ears)
|
||||
if(ears)
|
||||
ears.adjustEarDamage(ddmg, ddeaf)
|
||||
|
||||
/mob/proc/minimumDeafTicks()
|
||||
|
||||
/mob/living/carbon/minimumDeafTicks(value)
|
||||
var/obj/item/organ/ears/ears = getorgan(/obj/item/organ/ears)
|
||||
if(ears)
|
||||
ears.minimumDeafTicks(value)
|
||||
|
||||
|
||||
/obj/item/organ/ears/cat
|
||||
name = "cat ears"
|
||||
icon = 'icons/obj/clothing/hats.dmi'
|
||||
icon_state = "kitty"
|
||||
damage_multiplier = 2
|
||||
|
||||
/obj/item/organ/ears/cat/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE)
|
||||
..()
|
||||
if(istype(H))
|
||||
color = H.hair_color
|
||||
H.dna.species.mutant_bodyparts |= "ears"
|
||||
H.dna.features["ears"] = "Cat"
|
||||
H.update_body()
|
||||
|
||||
/obj/item/organ/ears/cat/Remove(mob/living/carbon/human/H, special = 0)
|
||||
..()
|
||||
if(istype(H))
|
||||
color = H.hair_color
|
||||
H.dna.features["ears"] = "None"
|
||||
H.dna.species.mutant_bodyparts -= "ears"
|
||||
H.update_body()
|
||||
|
||||
/obj/item/organ/ears/bronze
|
||||
name = "tin ears"
|
||||
desc = "The robust ears of a bronze golem. "
|
||||
damage_multiplier = 0.1 //STRONK
|
||||
bang_protect = 1 //Fear me weaklings.
|
||||
@@ -0,0 +1,327 @@
|
||||
/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
|
||||
|
||||
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
|
||||
|
||||
/obj/item/organ/eyes/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = FALSE)
|
||||
..()
|
||||
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)
|
||||
..()
|
||||
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/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."
|
||||
|
||||
/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
|
||||
|
||||
/obj/item/organ/eyes/robotic/emp_act(severity)
|
||||
. = ..()
|
||||
if(!owner || . & EMP_PROTECT_SELF)
|
||||
return
|
||||
if(prob(10 * severity))
|
||||
return
|
||||
to_chat(owner, "<span class='warning'>Static obfuscates your vision!</span>")
|
||||
owner.flash_act(visual = 1)
|
||||
|
||||
/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, var/special = 0)
|
||||
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()
|
||||
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((eye_lighting.len < 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/moth
|
||||
name = "moth 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,177 @@
|
||||
/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
|
||||
// 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?
|
||||
|
||||
/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 = "heart-off"
|
||||
return S
|
||||
|
||||
/obj/item/organ/heart/on_life()
|
||||
if(owner.client && beating)
|
||||
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
|
||||
|
||||
/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"
|
||||
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)
|
||||
..()
|
||||
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"
|
||||
synthetic = TRUE
|
||||
|
||||
/obj/item/organ/heart/cybernetic/emp_act()
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
Stop()
|
||||
|
||||
/obj/item/organ/heart/freedom
|
||||
name = "heart of freedom"
|
||||
desc = "This heart pumps with the passion to give... something freedom."
|
||||
synthetic = TRUE //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,29 @@
|
||||
/mob/proc/getorgan(typepath)
|
||||
return
|
||||
|
||||
/mob/proc/getorganszone(zone)
|
||||
return
|
||||
|
||||
/mob/proc/getorganslot(slot)
|
||||
return
|
||||
|
||||
/mob/living/carbon/getorgan(typepath)
|
||||
return (locate(typepath) in internal_organs)
|
||||
|
||||
/mob/living/carbon/getorganszone(zone, subzones = 0)
|
||||
var/list/returnorg = list()
|
||||
if(subzones)
|
||||
// Include subzones - groin for chest, eyes and mouth for head
|
||||
if(zone == BODY_ZONE_HEAD)
|
||||
returnorg = getorganszone(BODY_ZONE_PRECISE_EYES) + getorganszone(BODY_ZONE_PRECISE_MOUTH)
|
||||
if(zone == BODY_ZONE_CHEST)
|
||||
returnorg = getorganszone(BODY_ZONE_PRECISE_GROIN)
|
||||
|
||||
for(var/X in internal_organs)
|
||||
var/obj/item/organ/O = X
|
||||
if(zone == O.zone)
|
||||
returnorg += O
|
||||
return returnorg
|
||||
|
||||
/mob/living/carbon/getorganslot(slot)
|
||||
return internal_organs_slot[slot]
|
||||
@@ -0,0 +1,119 @@
|
||||
#define LIVER_DEFAULT_HEALTH 100 //amount of damage required for liver failure
|
||||
#define LIVER_DEFAULT_TOX_TOLERANCE 3 //amount of toxins the liver can filter out
|
||||
#define LIVER_DEFAULT_TOX_LETHALITY 0.01 //lower values lower how harmful toxins are to the liver
|
||||
#define LIVER_SWELLING_MOVE_MODIFY "pharma"
|
||||
|
||||
/obj/item/organ/liver
|
||||
name = "liver"
|
||||
icon_state = "liver"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_LIVER
|
||||
desc = "Pairing suggestion: chianti and fava beans."
|
||||
var/damage = 0 //liver damage, 0 is no damage, damage=maxHealth causes liver failure
|
||||
var/alcohol_tolerance = ALCOHOL_RATE//affects how much damage the liver takes from alcohol
|
||||
var/failing //is this liver failing?
|
||||
var/maxHealth = LIVER_DEFAULT_HEALTH
|
||||
var/toxTolerance = LIVER_DEFAULT_TOX_TOLERANCE//maximum amount of toxins the liver can just shrug off
|
||||
var/toxLethality = LIVER_DEFAULT_TOX_LETHALITY//affects how much damage toxins do to the liver
|
||||
var/filterToxins = TRUE //whether to filter toxins
|
||||
var/swelling = 0
|
||||
var/cachedmoveCalc = 1
|
||||
|
||||
/obj/item/organ/liver/on_life()
|
||||
var/mob/living/carbon/C = owner
|
||||
|
||||
if(istype(C))
|
||||
if(!failing)//can't process reagents with a failing liver
|
||||
//slowly heal liver damage
|
||||
damage = max(0, damage - 0.1)
|
||||
|
||||
if(filterToxins && !HAS_TRAIT(owner, TRAIT_TOXINLOVER))
|
||||
//handle liver toxin filtration
|
||||
for(var/I in C.reagents.reagent_list)
|
||||
var/datum/reagent/pickedreagent = I
|
||||
if(istype(pickedreagent, /datum/reagent/toxin))
|
||||
var/thisamount = C.reagents.get_reagent_amount(initial(pickedreagent.id))
|
||||
if (thisamount <= toxTolerance && thisamount)
|
||||
C.reagents.remove_reagent(initial(pickedreagent.id), 1)
|
||||
else
|
||||
damage += (thisamount*toxLethality)
|
||||
|
||||
//metabolize reagents
|
||||
C.reagents.metabolize(C, can_overdose=TRUE)
|
||||
|
||||
if(damage > 10 && prob(damage/3))//the higher the damage the higher the probability
|
||||
to_chat(C, "<span class='warning'>You feel a dull pain in your abdomen.</span>")
|
||||
|
||||
if(damage > maxHealth)//cap liver damage
|
||||
damage = maxHealth
|
||||
|
||||
if(swelling >= 10)
|
||||
pharmacokinesis()
|
||||
|
||||
/obj/item/organ/liver/prepare_eat()
|
||||
var/obj/S = ..()
|
||||
S.reagents.add_reagent("iron", 5)
|
||||
return S
|
||||
|
||||
//Just in case
|
||||
/obj/item/organ/liver/Remove(mob/living/carbon/M, special = 0)
|
||||
..()
|
||||
M.remove_movespeed_modifier(LIVER_SWELLING_MOVE_MODIFY)
|
||||
M.ResetBloodVol() //At the moment, this shouldn't allow application twice. You either have this OR a thirsty ferret.
|
||||
sizeMoveMod(1, M)
|
||||
|
||||
//Applies some of the effects to the patient.
|
||||
/obj/item/organ/liver/proc/pharmacokinesis()
|
||||
var/moveCalc = 1+((round(swelling) - 9)/3)
|
||||
if(moveCalc == cachedmoveCalc)//reduce calculations
|
||||
return
|
||||
if(prob(5))
|
||||
to_chat(owner, "<span class='notice'>You feel a stange ache in your side, almost like a sitch. This pain is affecting your movements and making you feel lightheaded.</span>")
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.add_movespeed_modifier(LIVER_SWELLING_MOVE_MODIFY, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
|
||||
H.AdjustBloodVol(moveCalc/3)
|
||||
sizeMoveMod(moveCalc, H)
|
||||
|
||||
/obj/item/organ/liver/proc/sizeMoveMod(var/value, mob/living/carbon/human/H)
|
||||
if(cachedmoveCalc == value)
|
||||
return
|
||||
H.next_move_modifier /= cachedmoveCalc
|
||||
H.next_move_modifier *= value
|
||||
cachedmoveCalc = value
|
||||
|
||||
/obj/item/organ/liver/fly
|
||||
name = "insectoid liver"
|
||||
icon_state = "liver-x" //xenomorph liver? It's just a black liver so it fits.
|
||||
desc = "A mutant liver designed to handle the unique diet of a flyperson."
|
||||
alcohol_tolerance = 0.007 //flies eat vomit, so a lower alcohol tolerance is perfect!
|
||||
|
||||
/obj/item/organ/liver/plasmaman
|
||||
name = "reagent processing crystal"
|
||||
icon_state = "liver-p"
|
||||
desc = "A large crystal that is somehow capable of metabolizing chemicals, these are found in plasmamen."
|
||||
|
||||
/obj/item/organ/liver/cybernetic
|
||||
name = "cybernetic liver"
|
||||
icon_state = "liver-c"
|
||||
desc = "An electronic device designed to mimic the functions of a human liver. It has no benefits over an organic liver, but is easy to produce."
|
||||
synthetic = TRUE
|
||||
|
||||
/obj/item/organ/liver/cybernetic/upgraded
|
||||
name = "upgraded cybernetic liver"
|
||||
icon_state = "liver-c-u"
|
||||
desc = "An upgraded version of the cybernetic liver, designed to improve upon organic livers. It is resistant to alcohol poisoning and is very robust at filtering toxins."
|
||||
alcohol_tolerance = 0.001
|
||||
maxHealth = 200 //double the health of a normal liver
|
||||
toxTolerance = 15 //can shrug off up to 15u of toxins
|
||||
toxLethality = 0.008 //20% less damage than a normal liver
|
||||
|
||||
/obj/item/organ/liver/cybernetic/emp_act(severity)
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
switch(severity)
|
||||
if(1)
|
||||
damage+=100
|
||||
if(2)
|
||||
damage+=50
|
||||
@@ -0,0 +1,502 @@
|
||||
#define LUNGS_MAX_HEALTH 300
|
||||
|
||||
/obj/item/organ/lungs
|
||||
name = "lungs"
|
||||
icon_state = "lungs"
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_LUNGS
|
||||
gender = PLURAL
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
|
||||
//Breath damage
|
||||
|
||||
var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa
|
||||
var/safe_oxygen_max = 50 // Too much of a good thing, in kPa as well.
|
||||
var/safe_nitro_min = 0
|
||||
var/safe_nitro_max = 0
|
||||
var/safe_co2_min = 0
|
||||
var/safe_co2_max = 10 // Yes it's an arbitrary value who cares?
|
||||
var/safe_toxins_min = 0
|
||||
var/safe_toxins_max = MOLES_GAS_VISIBLE
|
||||
var/SA_para_min = 1 //Sleeping agent
|
||||
var/SA_sleep_min = 5 //Sleeping agent
|
||||
var/BZ_trip_balls_min = 1 //BZ gas
|
||||
var/gas_stimulation_min = 0.002 //Nitryl and Stimulum
|
||||
|
||||
var/oxy_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
|
||||
var/oxy_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
|
||||
var/oxy_damage_type = OXY
|
||||
var/nitro_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
|
||||
var/nitro_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
|
||||
var/nitro_damage_type = OXY
|
||||
var/co2_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
|
||||
var/co2_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
|
||||
var/co2_damage_type = OXY
|
||||
var/tox_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
|
||||
var/tox_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
|
||||
var/tox_damage_type = TOX
|
||||
|
||||
var/cold_message = "your face freezing and an icicle forming"
|
||||
var/cold_level_1_threshold = 260
|
||||
var/cold_level_2_threshold = 200
|
||||
var/cold_level_3_threshold = 120
|
||||
var/cold_level_1_damage = COLD_GAS_DAMAGE_LEVEL_1 //Keep in mind with gas damage levels, you can set these to be negative, if you want someone to heal, instead.
|
||||
var/cold_level_2_damage = COLD_GAS_DAMAGE_LEVEL_2
|
||||
var/cold_level_3_damage = COLD_GAS_DAMAGE_LEVEL_3
|
||||
var/cold_damage_type = BURN
|
||||
|
||||
var/hot_message = "your face burning and a searing heat"
|
||||
var/heat_level_1_threshold = 360
|
||||
var/heat_level_2_threshold = 400
|
||||
var/heat_level_3_threshold = 1000
|
||||
var/heat_level_1_damage = HEAT_GAS_DAMAGE_LEVEL_1
|
||||
var/heat_level_2_damage = HEAT_GAS_DAMAGE_LEVEL_2
|
||||
var/heat_level_3_damage = HEAT_GAS_DAMAGE_LEVEL_3
|
||||
var/heat_damage_type = BURN
|
||||
|
||||
var/crit_stabilizing_reagent = "epinephrine"
|
||||
|
||||
//health
|
||||
var/maxHealth = LUNGS_MAX_HEALTH
|
||||
var/damage = 0
|
||||
|
||||
//TODO: lung health affects lung function
|
||||
/obj/item/organ/lungs/proc/adjustLungLoss(damage_mod, mob/living/carbon/M) //damage might be too low atm.
|
||||
if (maxHealth == INFINITY)
|
||||
return
|
||||
if(damage+damage_mod < 0)
|
||||
damage = 0
|
||||
return
|
||||
|
||||
damage += damage_mod
|
||||
if ((damage / maxHealth) > 1)
|
||||
to_chat(M, "<span class='userdanger'>You feel your lungs collapse within your chest as you gasp for air, unable to inflate them anymore!</span>")
|
||||
M.emote("gasp")
|
||||
SSblackbox.record_feedback("tally", "fermi_chem", 1, "Lungs lost")
|
||||
qdel(src)
|
||||
else if ((damage / maxHealth) > 0.75)
|
||||
to_chat(M, "<span class='warning'>It's getting really hard to breathe!!</span>")
|
||||
M.emote("gasp")
|
||||
M.Dizzy(3)
|
||||
else if ((damage / maxHealth) > 0.5)
|
||||
M.Dizzy(2)
|
||||
to_chat(M, "<span class='notice'>Your chest is really starting to hurt.</span>")
|
||||
M.emote("cough")
|
||||
else if ((damage / maxHealth) > 0.2)
|
||||
to_chat(M, "<span class='notice'>You feel an ache within your chest.</span>")
|
||||
M.emote("cough")
|
||||
M.Dizzy(1)
|
||||
|
||||
/obj/item/organ/lungs/proc/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/H)
|
||||
//TODO: add lung damage = less oxygen gains
|
||||
var/breathModifier = (5-(5*(damage/maxHealth)/2)) //range 2.5 - 5
|
||||
if((H.status_flags & GODMODE))
|
||||
return
|
||||
if(HAS_TRAIT(H, TRAIT_NOBREATH))
|
||||
return
|
||||
|
||||
if(!breath || (breath.total_moles() == 0))
|
||||
if(H.reagents.has_reagent(crit_stabilizing_reagent))
|
||||
return
|
||||
if(H.health >= H.crit_threshold)
|
||||
H.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
|
||||
else if(!HAS_TRAIT(H, TRAIT_NOCRITDAMAGE))
|
||||
H.adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS)
|
||||
|
||||
H.failed_last_breath = TRUE
|
||||
if(safe_oxygen_min)
|
||||
H.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
|
||||
else if(safe_toxins_min)
|
||||
H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox)
|
||||
else if(safe_co2_min)
|
||||
H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
|
||||
else if(safe_nitro_min)
|
||||
H.throw_alert("not_enough_nitro", /obj/screen/alert/not_enough_nitro)
|
||||
return FALSE
|
||||
|
||||
var/gas_breathed = 0
|
||||
|
||||
var/list/breath_gases = breath.gases
|
||||
|
||||
//Partial pressures in our breath
|
||||
var/O2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/oxygen])+(8*breath.get_breath_partial_pressure(breath_gases[/datum/gas/pluoxium]))
|
||||
var/N2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrogen])
|
||||
var/Toxins_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/plasma])
|
||||
var/CO2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/carbon_dioxide])
|
||||
|
||||
|
||||
//-- OXY --//
|
||||
|
||||
//Too much oxygen! //Yes, some species may not like it.
|
||||
if(safe_oxygen_max)
|
||||
if((O2_pp > safe_oxygen_max) && safe_oxygen_max == 0) //I guess plasma men technically need to have a check.
|
||||
var/ratio = (breath_gases[/datum/gas/oxygen]/safe_oxygen_max) * 10
|
||||
H.apply_damage_type(CLAMP(ratio, oxy_breath_dam_min, oxy_breath_dam_max), oxy_damage_type)
|
||||
H.throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy)
|
||||
|
||||
else if((O2_pp > safe_oxygen_max) && !(safe_oxygen_max == 0)) //Why yes, this is like too much CO2 and spahget. Dirty lizards.
|
||||
if(!H.o2overloadtime)
|
||||
H.o2overloadtime = world.time
|
||||
else if(world.time - H.o2overloadtime > 120)
|
||||
H.Dizzy(10) // better than a minute of you're fucked KO, but certainly a wake up call. Honk.
|
||||
H.adjustOxyLoss(3)
|
||||
if(world.time - H.o2overloadtime > 300)
|
||||
H.adjustOxyLoss(8)
|
||||
if(prob(20))
|
||||
H.emote("cough")
|
||||
H.throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy)
|
||||
|
||||
else
|
||||
H.o2overloadtime = 0
|
||||
H.clear_alert("too_much_oxy")
|
||||
|
||||
//Too little oxygen!
|
||||
if(safe_oxygen_min)
|
||||
if(O2_pp < safe_oxygen_min)
|
||||
gas_breathed = handle_too_little_breath(H, O2_pp, safe_oxygen_min, breath_gases[/datum/gas/oxygen])
|
||||
H.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
|
||||
else
|
||||
H.failed_last_breath = FALSE
|
||||
if(H.health >= H.crit_threshold)
|
||||
H.adjustOxyLoss(-breathModifier) //More damaged lungs = slower oxy rate up to a factor of half
|
||||
gas_breathed = breath_gases[/datum/gas/oxygen]
|
||||
H.clear_alert("not_enough_oxy")
|
||||
|
||||
//Exhale
|
||||
breath_gases[/datum/gas/oxygen] -= gas_breathed
|
||||
breath_gases[/datum/gas/carbon_dioxide] += gas_breathed
|
||||
gas_breathed = 0
|
||||
|
||||
//-- Nitrogen --//
|
||||
|
||||
//Too much nitrogen!
|
||||
if(safe_nitro_max)
|
||||
if(N2_pp > safe_nitro_max)
|
||||
var/ratio = (breath_gases[/datum/gas/nitrogen]/safe_nitro_max) * 10
|
||||
H.apply_damage_type(CLAMP(ratio, nitro_breath_dam_min, nitro_breath_dam_max), nitro_damage_type)
|
||||
H.throw_alert("too_much_nitro", /obj/screen/alert/too_much_nitro)
|
||||
H.losebreath += 2
|
||||
else
|
||||
H.clear_alert("too_much_nitro")
|
||||
|
||||
//Too little nitrogen!
|
||||
if(safe_nitro_min)
|
||||
if(N2_pp < safe_nitro_min)
|
||||
gas_breathed = handle_too_little_breath(H, N2_pp, safe_nitro_min, breath_gases[/datum/gas/nitrogen])
|
||||
H.throw_alert("nitro", /obj/screen/alert/not_enough_nitro)
|
||||
else
|
||||
H.failed_last_breath = FALSE
|
||||
if(H.health >= H.crit_threshold)
|
||||
H.adjustOxyLoss(-breathModifier)
|
||||
gas_breathed = breath_gases[/datum/gas/nitrogen]
|
||||
H.clear_alert("nitro")
|
||||
|
||||
//Exhale
|
||||
breath_gases[/datum/gas/nitrogen] -= gas_breathed
|
||||
breath_gases[/datum/gas/carbon_dioxide] += gas_breathed
|
||||
gas_breathed = 0
|
||||
|
||||
//-- CO2 --//
|
||||
|
||||
//CO2 does not affect failed_last_breath. So if there was enough oxygen in the air but too much co2, this will hurt you, but only once per 4 ticks, instead of once per tick.
|
||||
if(safe_co2_max)
|
||||
if(CO2_pp > safe_co2_max)
|
||||
if(!H.co2overloadtime) // If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so.
|
||||
H.co2overloadtime = world.time
|
||||
else if(world.time - H.co2overloadtime > 120)
|
||||
H.Unconscious(60)
|
||||
H.apply_damage_type(3, co2_damage_type) // Lets hurt em a little, let them know we mean business
|
||||
if(world.time - H.co2overloadtime > 300) // They've been in here 30s now, lets start to kill them for their own good!
|
||||
H.apply_damage_type(8, co2_damage_type)
|
||||
H.throw_alert("too_much_co2", /obj/screen/alert/too_much_co2)
|
||||
if(prob(20)) // Lets give them some chance to know somethings not right though I guess.
|
||||
H.emote("cough")
|
||||
|
||||
else
|
||||
H.co2overloadtime = 0
|
||||
H.clear_alert("too_much_co2")
|
||||
|
||||
//Too little CO2!
|
||||
if(safe_co2_min)
|
||||
if(CO2_pp < safe_co2_min)
|
||||
gas_breathed = handle_too_little_breath(H, CO2_pp, safe_co2_min, breath_gases[/datum/gas/carbon_dioxide])
|
||||
H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
|
||||
else
|
||||
H.failed_last_breath = FALSE
|
||||
if(H.health >= H.crit_threshold)
|
||||
H.adjustOxyLoss(-breathModifier)
|
||||
gas_breathed = breath_gases[/datum/gas/carbon_dioxide]
|
||||
H.clear_alert("not_enough_co2")
|
||||
|
||||
//Exhale
|
||||
breath_gases[/datum/gas/carbon_dioxide] -= gas_breathed
|
||||
breath_gases[/datum/gas/oxygen] += gas_breathed
|
||||
gas_breathed = 0
|
||||
|
||||
|
||||
//-- TOX --//
|
||||
|
||||
//Too much toxins!
|
||||
if(safe_toxins_max)
|
||||
if(Toxins_pp > safe_toxins_max)
|
||||
var/ratio = (breath_gases[/datum/gas/plasma]/safe_toxins_max) * 10
|
||||
H.apply_damage_type(CLAMP(ratio, tox_breath_dam_min, tox_breath_dam_max), tox_damage_type)
|
||||
H.throw_alert("too_much_tox", /obj/screen/alert/too_much_tox)
|
||||
else
|
||||
H.clear_alert("too_much_tox")
|
||||
|
||||
|
||||
//Too little toxins!
|
||||
if(safe_toxins_min)
|
||||
if(Toxins_pp < safe_toxins_min)
|
||||
gas_breathed = handle_too_little_breath(H, Toxins_pp, safe_toxins_min, breath_gases[/datum/gas/plasma])
|
||||
H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox)
|
||||
else
|
||||
H.failed_last_breath = FALSE
|
||||
if(H.health >= H.crit_threshold)
|
||||
H.adjustOxyLoss(-breathModifier)
|
||||
gas_breathed = breath_gases[/datum/gas/plasma]
|
||||
H.clear_alert("not_enough_tox")
|
||||
|
||||
//Exhale
|
||||
breath_gases[/datum/gas/plasma] -= gas_breathed
|
||||
breath_gases[/datum/gas/carbon_dioxide] += gas_breathed
|
||||
gas_breathed = 0
|
||||
|
||||
|
||||
//-- TRACES --//
|
||||
|
||||
if(breath) // If there's some other shit in the air lets deal with it here.
|
||||
|
||||
// N2O
|
||||
|
||||
var/SA_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrous_oxide])
|
||||
if(SA_pp > SA_para_min) // Enough to make us stunned for a bit
|
||||
H.Unconscious(60) // 60 gives them one second to wake up and run away a bit!
|
||||
if(SA_pp > SA_sleep_min) // Enough to make us sleep as well
|
||||
H.Sleeping(max(H.AmountSleeping() + 40, 200))
|
||||
else if(SA_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
|
||||
if(prob(20))
|
||||
H.emote(pick("giggle", "laugh"))
|
||||
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "chemical_euphoria", /datum/mood_event/chemical_euphoria)
|
||||
else
|
||||
SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "chemical_euphoria")
|
||||
|
||||
// BZ
|
||||
|
||||
var/bz_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/bz])
|
||||
if(bz_pp > BZ_trip_balls_min)
|
||||
H.hallucination += 10
|
||||
H.reagents.add_reagent("bz_metabolites",5)
|
||||
if(prob(33))
|
||||
H.adjustBrainLoss(3, 150)
|
||||
|
||||
else if(bz_pp > 0.01)
|
||||
H.hallucination += 5
|
||||
H.reagents.add_reagent("bz_metabolites",1)
|
||||
|
||||
|
||||
// Tritium
|
||||
var/trit_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/tritium])
|
||||
if (trit_pp > 50)
|
||||
H.radiation += trit_pp/2 //If you're breathing in half an atmosphere of radioactive gas, you fucked up.
|
||||
else
|
||||
H.radiation += trit_pp/10
|
||||
|
||||
// Nitryl
|
||||
var/nitryl_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitryl])
|
||||
if (prob(nitryl_pp))
|
||||
to_chat(H, "<span class='alert'>Your mouth feels like it's burning!</span>")
|
||||
if (nitryl_pp >40)
|
||||
H.emote("gasp")
|
||||
H.adjustFireLoss(10)
|
||||
if (prob(nitryl_pp/2))
|
||||
to_chat(H, "<span class='alert'>Your throat closes up!</span>")
|
||||
H.silent = max(H.silent, 3)
|
||||
else
|
||||
H.adjustFireLoss(nitryl_pp/4)
|
||||
gas_breathed = breath_gases[/datum/gas/nitryl]
|
||||
if (gas_breathed > gas_stimulation_min)
|
||||
H.reagents.add_reagent("no2",1)
|
||||
|
||||
breath_gases[/datum/gas/nitryl]-=gas_breathed
|
||||
|
||||
// Stimulum
|
||||
gas_breathed = breath_gases[/datum/gas/stimulum]
|
||||
if (gas_breathed > gas_stimulation_min)
|
||||
var/existing = H.reagents.get_reagent_amount("stimulum")
|
||||
H.reagents.add_reagent("stimulum", max(0, 5 - existing))
|
||||
breath_gases[/datum/gas/stimulum]-=gas_breathed
|
||||
|
||||
// Miasma
|
||||
if (breath_gases[/datum/gas/miasma])
|
||||
var/miasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/miasma])
|
||||
if(miasma_pp > MINIMUM_MOLES_DELTA_TO_MOVE)
|
||||
|
||||
//Miasma sickness
|
||||
if(prob(0.05 * miasma_pp))
|
||||
var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(2,3)
|
||||
miasma_disease.name = "Unknown"
|
||||
miasma_disease.try_infect(owner)
|
||||
|
||||
// Miasma side effects
|
||||
switch(miasma_pp)
|
||||
if(1 to 5)
|
||||
// At lower pp, give out a little warning
|
||||
SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "smell")
|
||||
if(prob(5))
|
||||
to_chat(owner, "<span class='notice'>There is an unpleasant smell in the air.</span>")
|
||||
if(5 to 15)
|
||||
//At somewhat higher pp, warning becomes more obvious
|
||||
if(prob(15))
|
||||
to_chat(owner, "<span class='warning'>You smell something horribly decayed inside this room.</span>")
|
||||
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/bad_smell)
|
||||
if(15 to 30)
|
||||
//Small chance to vomit. By now, people have internals on anyway
|
||||
if(prob(5))
|
||||
to_chat(owner, "<span class='warning'>The stench of rotting carcasses is unbearable!</span>")
|
||||
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench)
|
||||
owner.vomit()
|
||||
if(30 to INFINITY)
|
||||
//Higher chance to vomit. Let the horror start
|
||||
if(prob(15))
|
||||
to_chat(owner, "<span class='warning'>The stench of rotting carcasses is unbearable!</span>")
|
||||
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench)
|
||||
owner.vomit()
|
||||
else
|
||||
SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "smell")
|
||||
|
||||
// In a full miasma atmosphere with 101.34 pKa, about 10 disgust per breath, is pretty low compared to threshholds
|
||||
// Then again, this is a purely hypothetical scenario and hardly reachable
|
||||
owner.adjust_disgust(0.1 * miasma_pp)
|
||||
|
||||
breath_gases[/datum/gas/miasma]-=gas_breathed
|
||||
|
||||
// Clear out moods when no miasma at all
|
||||
else
|
||||
SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "smell")
|
||||
|
||||
handle_breath_temperature(breath, H)
|
||||
GAS_GARBAGE_COLLECT(breath.gases)
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/item/organ/lungs/proc/handle_too_little_breath(mob/living/carbon/human/H = null, breath_pp = 0, safe_breath_min = 0, true_pp = 0)
|
||||
. = 0
|
||||
if(!H || !safe_breath_min) //the other args are either: Ok being 0 or Specifically handled.
|
||||
return FALSE
|
||||
|
||||
if(prob(20))
|
||||
H.emote("gasp")
|
||||
if(breath_pp > 0)
|
||||
var/ratio = safe_breath_min/breath_pp
|
||||
H.adjustOxyLoss(min(5*ratio, HUMAN_MAX_OXYLOSS)) // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!
|
||||
H.failed_last_breath = TRUE
|
||||
. = true_pp*ratio/6
|
||||
else
|
||||
H.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
|
||||
H.failed_last_breath = TRUE
|
||||
|
||||
|
||||
/obj/item/organ/lungs/proc/handle_breath_temperature(datum/gas_mixture/breath, mob/living/carbon/human/H) // called by human/life, handles temperatures
|
||||
var/breath_temperature = breath.temperature
|
||||
|
||||
if(!HAS_TRAIT(H, TRAIT_RESISTCOLD)) // COLD DAMAGE
|
||||
var/cold_modifier = H.dna.species.coldmod
|
||||
if(breath_temperature < cold_level_3_threshold)
|
||||
H.apply_damage_type(cold_level_3_damage*cold_modifier, cold_damage_type)
|
||||
adjustLungLoss((cold_level_3_damage*cold_modifier*2), H)
|
||||
if(breath_temperature > cold_level_3_threshold && breath_temperature < cold_level_2_threshold)
|
||||
H.apply_damage_type(cold_level_2_damage*cold_modifier, cold_damage_type)
|
||||
adjustLungLoss((cold_level_2_damage*cold_modifier*2), H)
|
||||
if(breath_temperature > cold_level_2_threshold && breath_temperature < cold_level_1_threshold)
|
||||
H.apply_damage_type(cold_level_1_damage*cold_modifier, cold_damage_type)
|
||||
adjustLungLoss((cold_level_1_damage*cold_modifier*2), H)
|
||||
if(breath_temperature < cold_level_1_threshold)
|
||||
if(prob(20))
|
||||
to_chat(H, "<span class='warning'>You feel [cold_message] in your [name]!</span>")
|
||||
|
||||
if(!HAS_TRAIT(H, TRAIT_RESISTHEAT)) // HEAT DAMAGE
|
||||
var/heat_modifier = H.dna.species.heatmod
|
||||
if(breath_temperature > heat_level_1_threshold && breath_temperature < heat_level_2_threshold)
|
||||
H.apply_damage_type(heat_level_1_damage*heat_modifier, heat_damage_type)
|
||||
adjustLungLoss((heat_level_1_damage*heat_modifier*2), H)
|
||||
if(breath_temperature > heat_level_2_threshold && breath_temperature < heat_level_3_threshold)
|
||||
H.apply_damage_type(heat_level_2_damage*heat_modifier, heat_damage_type)
|
||||
adjustLungLoss((heat_level_2_damage*heat_modifier*2), H)
|
||||
if(breath_temperature > heat_level_3_threshold)
|
||||
H.apply_damage_type(heat_level_3_damage*heat_modifier, heat_damage_type)
|
||||
adjustLungLoss((heat_level_3_damage*heat_modifier*2), H)
|
||||
if(breath_temperature > heat_level_1_threshold)
|
||||
if(prob(20))
|
||||
to_chat(H, "<span class='warning'>You feel [hot_message] in your [name]!</span>")
|
||||
|
||||
/obj/item/organ/lungs/prepare_eat()
|
||||
var/obj/S = ..()
|
||||
S.reagents.add_reagent("salbutamol", 5)
|
||||
return S
|
||||
|
||||
/obj/item/organ/lungs/plasmaman
|
||||
name = "plasma filter"
|
||||
desc = "A spongy rib-shaped mass for filtering plasma from the air."
|
||||
icon_state = "lungs-plasma"
|
||||
|
||||
safe_oxygen_min = 0 //We don't breath this
|
||||
safe_oxygen_max = 0 // Like, at all.
|
||||
safe_toxins_min = 16 //We breath THIS!
|
||||
safe_toxins_max = 0
|
||||
maxHealth = INFINITY//I don't understand how plamamen work, so I'm not going to try t give them special lungs atm
|
||||
|
||||
/obj/item/organ/lungs/cybernetic
|
||||
name = "cybernetic lungs"
|
||||
desc = "A cybernetic version of the lungs found in traditional humanoid entities. It functions the same as an organic lung and is merely meant as a replacement."
|
||||
icon_state = "lungs-c"
|
||||
synthetic = TRUE
|
||||
maxHealth = 400
|
||||
|
||||
/obj/item/organ/lungs/cybernetic/emp_act()
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
owner.losebreath = 20
|
||||
|
||||
|
||||
/obj/item/organ/lungs/cybernetic/upgraded
|
||||
name = "upgraded cybernetic lungs"
|
||||
desc = "A more advanced version of the stock cybernetic lungs. They are capable of filtering out lower levels of toxins and carbon dioxide."
|
||||
icon_state = "lungs-c-u"
|
||||
safe_toxins_max = 20
|
||||
safe_co2_max = 20
|
||||
safe_oxygen_max = 250
|
||||
|
||||
cold_level_1_threshold = 200
|
||||
cold_level_2_threshold = 140
|
||||
cold_level_3_threshold = 100
|
||||
maxHealth = 550
|
||||
|
||||
/obj/item/organ/lungs/ashwalker
|
||||
name = "ash lungs"
|
||||
desc = "blackened lungs identical from specimens recovered from lavaland, unsuited to higher air pressures."
|
||||
icon_state = "lungs-ll"
|
||||
safe_oxygen_min = 3 //able to handle much thinner oxygen, something something ash storm adaptation
|
||||
safe_oxygen_max = 18 // Air standard is 22kpA of O2, LL is 14kpA
|
||||
safe_nitro_max = 28 // Air standard is 82kpA of N2, LL is 23kpA
|
||||
|
||||
cold_level_1_threshold = 280 // Ash Lizards can't take the cold very well, station air is only just warm enough
|
||||
cold_level_2_threshold = 240
|
||||
cold_level_3_threshold = 200
|
||||
|
||||
heat_level_1_threshold = 400 // better adapted for heat, obv. Lavaland standard is 300
|
||||
heat_level_2_threshold = 600 // up 200 from level 1, 1000 is silly but w/e for level 3
|
||||
|
||||
/obj/item/organ/lungs/slime
|
||||
name = "vacuole"
|
||||
desc = "A large organelle designed to store oxygen and other important gasses."
|
||||
|
||||
safe_toxins_max = 0 //We breathe this to gain POWER.
|
||||
|
||||
/obj/item/organ/lungs/slime/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/H)
|
||||
. = ..()
|
||||
if (breath && breath.gases[/datum/gas/plasma])
|
||||
var/plasma_pp = breath.get_breath_partial_pressure(breath.gases[/datum/gas/plasma])
|
||||
owner.blood_volume += (0.2 * plasma_pp) // 10/s when breathing literally nothing but plasma, which will suffocate you.
|
||||
@@ -0,0 +1,191 @@
|
||||
/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/vital = 0
|
||||
//Was this organ implanted/inserted/etc, if true will not be removed during species change.
|
||||
var/external = FALSE
|
||||
var/synthetic = FALSE // To distinguish between organic and synthetic organs
|
||||
|
||||
|
||||
/obj/item/organ/proc/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
|
||||
if(!iscarbon(M) || owner == M)
|
||||
return
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
//Special is for instant replacement like autosurgeons
|
||||
/obj/item/organ/proc/Remove(mob/living/carbon/M, special = 0)
|
||||
owner = null
|
||||
if(M)
|
||||
M.internal_organs -= src
|
||||
if(M.internal_organs_slot[slot] == src)
|
||||
M.internal_organs_slot.Remove(slot)
|
||||
if(vital && !special && !(M.status_flags & GODMODE))
|
||||
M.death()
|
||||
for(var/X in actions)
|
||||
var/datum/action/A = X
|
||||
A.Remove(M)
|
||||
|
||||
|
||||
/obj/item/organ/proc/on_find(mob/living/finder)
|
||||
return
|
||||
|
||||
/obj/item/organ/proc/on_life()
|
||||
return
|
||||
|
||||
/obj/item/organ/examine(mob/user)
|
||||
..()
|
||||
if(status == ORGAN_ROBOTIC && crit_fail)
|
||||
to_chat(user, "<span class='warning'>[src] seems to be broken!</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/Destroy()
|
||||
if(owner)
|
||||
// The special flag is important, because otherwise mobs can die
|
||||
// while undergoing transformation into different mobs.
|
||||
Remove(owner, special=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.
|
||||
|
||||
//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()
|
||||
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(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(breathes && !getorganslot(ORGAN_SLOT_LUNGS))
|
||||
var/obj/item/organ/lungs/L = new()
|
||||
L.Insert(src)
|
||||
|
||||
if(blooded && !getorganslot(ORGAN_SLOT_HEART))
|
||||
var/obj/item/organ/heart/H = new()
|
||||
H.Insert(src)
|
||||
|
||||
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)
|
||||
else
|
||||
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(!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(!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)
|
||||
@@ -0,0 +1,68 @@
|
||||
/obj/item/organ/stomach
|
||||
name = "stomach"
|
||||
icon_state = "stomach"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_STOMACH
|
||||
attack_verb = list("gored", "squished", "slapped", "digested")
|
||||
desc = "Onaka ga suite imasu."
|
||||
var/disgust_metabolism = 1
|
||||
|
||||
/obj/item/organ/stomach/on_life()
|
||||
var/mob/living/carbon/human/H = owner
|
||||
|
||||
if(istype(H))
|
||||
H.dna.species.handle_digestion(H)
|
||||
handle_disgust(H)
|
||||
|
||||
/obj/item/organ/stomach/proc/handle_disgust(mob/living/carbon/human/H)
|
||||
if(H.disgust)
|
||||
var/pukeprob = 5 + 0.05 * H.disgust
|
||||
if(H.disgust >= DISGUST_LEVEL_GROSS)
|
||||
if(prob(10))
|
||||
H.stuttering += 1
|
||||
H.confused += 2
|
||||
if(prob(10) && !H.stat)
|
||||
to_chat(H, "<span class='warning'>You feel kind of iffy...</span>")
|
||||
H.jitteriness = max(H.jitteriness - 3, 0)
|
||||
if(H.disgust >= DISGUST_LEVEL_VERYGROSS)
|
||||
if(prob(pukeprob)) //iT hAndLeS mOrE ThaN PukInG
|
||||
H.confused += 2.5
|
||||
H.stuttering += 1
|
||||
H.vomit(10, 0, 1, 0, 1, 0)
|
||||
H.Dizzy(5)
|
||||
if(H.disgust >= DISGUST_LEVEL_DISGUSTED)
|
||||
if(prob(25))
|
||||
H.blur_eyes(3) //We need to add more shit down here
|
||||
|
||||
H.adjust_disgust(-0.5 * disgust_metabolism)
|
||||
switch(H.disgust)
|
||||
if(0 to DISGUST_LEVEL_GROSS)
|
||||
H.clear_alert("disgust")
|
||||
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "disgust")
|
||||
if(DISGUST_LEVEL_GROSS to DISGUST_LEVEL_VERYGROSS)
|
||||
H.throw_alert("disgust", /obj/screen/alert/gross)
|
||||
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/gross)
|
||||
if(DISGUST_LEVEL_VERYGROSS to DISGUST_LEVEL_DISGUSTED)
|
||||
H.throw_alert("disgust", /obj/screen/alert/verygross)
|
||||
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/verygross)
|
||||
if(DISGUST_LEVEL_DISGUSTED to INFINITY)
|
||||
H.throw_alert("disgust", /obj/screen/alert/disgusted)
|
||||
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/disgusted)
|
||||
|
||||
/obj/item/organ/stomach/Remove(mob/living/carbon/M, special = 0)
|
||||
var/mob/living/carbon/human/H = owner
|
||||
if(istype(H))
|
||||
H.clear_alert("disgust")
|
||||
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "disgust")
|
||||
..()
|
||||
|
||||
/obj/item/organ/stomach/fly
|
||||
name = "insectoid stomach"
|
||||
icon_state = "stomach-x" //xenomorph liver? It's just a black liver so it fits.
|
||||
desc = "A mutant stomach designed to handle the unique diet of a flyperson."
|
||||
|
||||
/obj/item/organ/stomach/plasmaman
|
||||
name = "digestive crystal"
|
||||
icon_state = "stomach-p"
|
||||
desc = "A strange crystal that is responsible for metabolizing the unseen energy force that feeds plasmamen."
|
||||
@@ -0,0 +1,65 @@
|
||||
// Note: tails only work in humans. They use human-specific parameters and rely on human code for displaying.
|
||||
|
||||
/obj/item/organ/tail
|
||||
name = "tail"
|
||||
desc = "A severed tail. What did you cut this off of?"
|
||||
icon_state = "severedtail"
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_TAIL
|
||||
var/tail_type = "None"
|
||||
|
||||
/obj/item/organ/tail/Remove(mob/living/carbon/human/H, special = 0)
|
||||
..()
|
||||
if(H && H.dna && H.dna.species)
|
||||
H.dna.species.stop_wagging_tail(H)
|
||||
|
||||
/obj/item/organ/tail/cat
|
||||
name = "cat tail"
|
||||
desc = "A severed cat tail. Who's wagging now?"
|
||||
tail_type = "Cat"
|
||||
|
||||
/obj/item/organ/tail/cat/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE)
|
||||
..()
|
||||
if(istype(H))
|
||||
if(!("tail_human" in H.dna.species.mutant_bodyparts))
|
||||
H.dna.species.mutant_bodyparts |= "tail_human"
|
||||
H.dna.features["tail_human"] = tail_type
|
||||
H.update_body()
|
||||
|
||||
/obj/item/organ/tail/cat/Remove(mob/living/carbon/human/H, special = 0)
|
||||
..()
|
||||
if(istype(H))
|
||||
H.dna.features["tail_human"] = "None"
|
||||
H.dna.species.mutant_bodyparts -= "tail_human"
|
||||
color = H.hair_color
|
||||
H.update_body()
|
||||
|
||||
/obj/item/organ/tail/lizard
|
||||
name = "lizard tail"
|
||||
desc = "A severed lizard tail. Somewhere, no doubt, a lizard hater is very pleased with themselves."
|
||||
color = "#116611"
|
||||
tail_type = "Smooth"
|
||||
var/spines = "None"
|
||||
|
||||
/obj/item/organ/tail/lizard/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE)
|
||||
..()
|
||||
if(istype(H))
|
||||
// Checks here are necessary so it wouldn't overwrite the tail of a lizard it spawned in
|
||||
if(!("tail_lizard" in H.dna.species.mutant_bodyparts))
|
||||
H.dna.features["tail_lizard"] = tail_type
|
||||
H.dna.species.mutant_bodyparts |= "tail_lizard"
|
||||
|
||||
if(!("spines" in H.dna.species.mutant_bodyparts))
|
||||
H.dna.features["spines"] = spines
|
||||
H.dna.species.mutant_bodyparts |= "spines"
|
||||
H.update_body()
|
||||
|
||||
/obj/item/organ/tail/lizard/Remove(mob/living/carbon/human/H, special = 0)
|
||||
..()
|
||||
if(istype(H))
|
||||
H.dna.species.mutant_bodyparts -= "tail_lizard"
|
||||
H.dna.species.mutant_bodyparts -= "spines"
|
||||
color = "#" + H.dna.features["mcolor"]
|
||||
tail_type = H.dna.features["tail_lizard"]
|
||||
spines = H.dna.features["spines"]
|
||||
H.update_body()
|
||||
@@ -0,0 +1,273 @@
|
||||
#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.
|
||||
var/maxHealth = TONGUE_MAX_HEALTH
|
||||
var/damage = 0
|
||||
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,
|
||||
))
|
||||
|
||||
/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/proc/adjustTongueLoss(mob/living/carbon/M, damage_mod)
|
||||
if (maxHealth == "alien")
|
||||
return
|
||||
if (maxHealth == "bone")
|
||||
var/target = M.get_bodypart(BODY_ZONE_HEAD)
|
||||
M.apply_damage(damage_mod, BURN, target)
|
||||
to_chat(M, "<span class='userdanger'>The drink burns your skull! Oof, your bones!</span>")
|
||||
return
|
||||
if(damage+damage_mod < 0)
|
||||
damage = 0
|
||||
return
|
||||
|
||||
damage += damage_mod
|
||||
if ((damage / maxHealth) > 1)
|
||||
to_chat(M, "<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(M, "<span class='warning'>Your tongue feels like it's about to fall out!.</span>")
|
||||
else if ((damage / maxHealth) > 0.5)
|
||||
to_chat(M, "<span class='notice'>Your tongue is really starting to hurt.</span>")
|
||||
|
||||
|
||||
/obj/item/organ/tongue/Insert(mob/living/carbon/M, special = 0)
|
||||
..()
|
||||
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 = "alien" //Their blood is acid, so, no, though a tongueless xeno might be funny
|
||||
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"
|
||||
attack_verb = list("bitten", "chattered", "chomped", "enamelled", "boned")
|
||||
taste_sensitivity = 101 // skeletons cannot taste anything
|
||||
maxHealth = "bone" //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/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"
|
||||
maxHealth = "alien"
|
||||
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!
|
||||
|
||||
/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
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user