Fish infusion (#87030)

## About The Pull Request
I'm adding a new infusion ~~(actually four, but two of them are just
holders for specific organs tied to a couple fish traits)~~ to the game.
As the title says, it's about fish.

The infusion is composed of three primary organs, plus another few that
can be gotten from fish with specific traits.

The primary organs are:
- Gills (lungs): Instead of breathing oxygen, you now need to stay wet
or breathe water vapor.
- fish-DNA infused stomach: Can safely eat raw fish.
- fish tail: On its own, it only speeds you up on water turfs, but it
has another effect once past the organ set threshold. It also makes you
waddle and flop like a fish while crawling (I still gotta finish sprites
on this one)

Other organs are:
- semi-aquatic lungs: A subtype of gills from fish with the 'amphibious'
trait, falls back on oxygen if there's no water. Can also be gotten from
frogs, axolotl and crabs.
- fish-DNA infused liver: From fish with the 'toxic' trait. Uses
tetrodotoxin as a healing chem instead of a toxin. Also better tolerance
to alcohol if you want to drink like a fish (ba dum tsh).
- inky tongue: From fish with the 'ink production' trait. Gives mobs the
ability to spit ink on a cooldown, blinding and confusion foes
temporarily.

The main gimmick of this infusion revolves around being drenched in
water to benefit from it, In the case you get the gills organ, this also
becomes a necessity, to not suffocate to death (alternatively, you can
breathe water vapor, without any benefit). To enable the bonus of the
organs set, three organs need to be infused. They can be gills, stomach,
tail and/or liver, while the inky tongue doesn't count towards it.

Once the threshold is reached, the following bonus are enabled:
- Wetness decays a lot slower and resists fire a bit more.
- Ink spit becomes stronger, allowing it to very briefly knock down
foes.
- Fishing bonuses and experience
- Resistance to high pressures
- Slightly expanded FOV
- drinking water and showers mildly heal you over time.
- for felinids: You won't hate getting sprayed by water or taking a
shower.
- While wet:
- - If the fish tail is implanted, crawling speed is boosted.
- - You no longer slip on wet tiles.
- - You also become slippery when lying on the floor.
- - You get a very mild damage resistance and passive stamina
regeneration, and cool down faster.
- - You resist grabs better.
- - get a very weak positive moodlet.
- However, being dry will make you quite squisher, especially against
fire damage, slower and give you a modest negative moodlet.

While working on it, I've also noticed a few things that explained why
tetrodotoxin (TTX) did jackshit at low doses, because livers have a set
toxin tolerance value, below which, any amount of toxin does nothing.
Also I've felt like reagents like multiver & co were a bit too strong
against a reagent that's supposed to work at very low doses, with slow
metabolization, so I've added a couple variables to buff TTX a bit,
making it harder to purge and resistant to liver toxin tolerance (also
added a bit of lungs damage).



## Why It's Good For The Game
I wanted to take a shot at coding a DNA infusion and see how chock-full
I could make it. DNA infusions are like a middle point between "aha,
small visual trinket" and organs with generally ok effects. I seek to
make something a bit more complex ~~(also tied to fishing ofc because
that's more or less the recurrent gag of my recent features)~~ primaly
focused around the unique theme of being strong when wet and weaker when
dry.

EDIT: The PR is now ready, have a set of screenshots of the (fairly mid)
fish tails (and gills, barely visible) on randomly generated spessman
and one consistent joe:

![immagine](https://github.com/user-attachments/assets/a4965508-22e2-4d3a-8523-29fec6bce91e)


## Changelog

🆑
add: Added a new infusion to the game: Fish. Its main gimmick revolves
around being stronger and slippery when wet while weaker when dry.
balance: Buffed tetrodotoxin a little against liver tolerance and
purging reagents.
/🆑
This commit is contained in:
Ghom
2024-10-09 02:03:50 +02:00
committed by GitHub
parent 08bb51fc40
commit 96c0c0b12c
63 changed files with 1080 additions and 121 deletions

View File

@@ -60,6 +60,7 @@
#define DNA_MOTH_MARKINGS_BLOCK 13
#define DNA_MUSHROOM_CAPS_BLOCK 14
#define DNA_POD_HAIR_BLOCK 15
#define DNA_FISH_TAIL_BLOCK 16
// Hey! Listen up if you're here because you're adding a species feature!
//
@@ -68,7 +69,7 @@
// (Which means having a DNA block for a feature tied to a mob without DNA is entirely pointless.)
/// Total amount of DNA blocks, must be equal to the highest DNA block number
#define DNA_FEATURE_BLOCKS 15
#define DNA_FEATURE_BLOCKS 16
#define DNA_SEQUENCE_LENGTH 4
#define DNA_MUTATION_BLOCKS 8

View File

@@ -14,6 +14,8 @@
#define ALERT_TOO_MUCH_NITRO "too_much_nitro"
#define ALERT_NOT_ENOUGH_NITRO "not_enough_nitro"
#define ALERT_NOT_ENOUGH_WATER "not_enough_water"
/** Mob related */
#define ALERT_SUCCUMB "succumb"
#define ALERT_BUCKLED "buckled"

View File

@@ -48,3 +48,6 @@
///from /datum/component/on_hit_effect/send_signal(): (user, target, hit_zone)
#define COMSIG_ON_HIT_EFFECT "comsig_on_hit_effect"
///from /datum/component/bubble_icon_override/get_bubble_icon(): (list/holder)
#define COMSIG_GET_BUBBLE_ICON "get_bubble_icon"

View File

@@ -272,6 +272,9 @@
#define COMSIG_LIVING_GRAB "living_grab"
// Return COMPONENT_CANCEL_ATTACK_CHAIN / COMPONENT_SKIP_ATTACK_CHAIN to stop the grab
/// From /datum/component/edible/get_perceived_food_quality(): (datum/component/edible/edible, list/extra_quality)
#define COMSIG_LIVING_GET_PERCEIVED_FOOD_QUALITY "get_perceived_food_quality"
///Called when living finish eat (/datum/component/edible/proc/On_Consume)
#define COMSIG_LIVING_FINISH_EAT "living_finish_eat"

View File

@@ -376,6 +376,8 @@
#define SLIP_WHEN_CRAWLING (1<<4)
/// the mob won't slip if the turf has the TRAIT_TURF_IGNORE_SLIPPERY trait.
#define SLIPPERY_TURF (1<<5)
/// For mobs who are slippery, this requires the mob holding it to be lying down.
#define SLIPPERY_WHEN_LYING_DOWN (1<<6)
#define MAX_CHICKENS 50

View File

@@ -122,3 +122,7 @@
/// Meaning that if the message is visual, and sourced from a blind mob, they will not see it.
/// This flag skips that behavior, and will always show the self message to the mob.
#define ALWAYS_SHOW_SELF_MESSAGE (1<<1)
///Defines for priorities for the bubble_icon_override comp
#define BUBBLE_ICON_PRIORITY_ACCESSORY 2
#define BUBBLE_ICON_PRIORITY_ORGAN 1

View File

@@ -185,6 +185,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_DISGUISED "disguised"
/// Use when you want a mob to be able to metabolize plasma temporarily (e.g. plasma fixation disease symptom)
#define TRAIT_PLASMA_LOVER_METABOLISM "plasma_lover_metabolism"
/// The mob is not harmed by tetrodotoxin. Instead, it heals them like omnizine
#define TRAIT_TETRODOTOXIN_HEALING "tetrodotoxin_healing"
#define TRAIT_EASYDISMEMBER "easy_dismember"
#define TRAIT_LIMBATTACHMENT "limb_attach"
#define TRAIT_NOLIMBDISABLE "no_limb_disable"
@@ -247,6 +249,16 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION "message_in_a_bottle_location"
/// Stops other objects of the same type from being inserted inside the same aquarium it's in.
#define TRAIT_UNIQUE_AQUARIUM_CONTENT "unique_aquarium_content"
/// Mobs that hate showers, being sprayed with water etc.
#define TRAIT_WATER_HATER "water_hater"
/// Improved boons from showers and some features centered around water, should also suppress TRAIT_WATER_HATER
#define TRAIT_WATER_ADAPTATION "water_adaptation"
/// Tells us that the mob urrently has the fire_handler/wet_stacks status effect
#define TRAIT_IS_WET "is_wet"
/// Mobs with this trait stay wet for longer and resist fire decaying wetness
#define TRAIT_WET_FOR_LONGER "wet_for_longer"
/// Mobs with this trait will be immune to slipping while also being slippery themselves when lying on the floor
#define TRAIT_SLIPPERY_WHEN_WET "slippery_when_wet"
/// This trait lets you evaluate someone's fitness level against your own
#define TRAIT_EXAMINE_FITNESS "reveal_power_level"
/// These mobs have particularly hygienic tongues
@@ -724,6 +736,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_HONKSPAMMING "trait_honkspamming"
/// Required by the waddling element since there are multiple sources of it.
#define TRAIT_WADDLING "trait_waddling"
/// Mobs with trait will still waddle even when lying on the floor and make a different footstep sound when doing so.
#define TRAIT_FLOPPING "trait_flopping"
/// Required by the on_hit_effect element, which is in turn added by other elements.
#define TRAIT_ON_HIT_EFFECT "trait_on_hit_effect"
@@ -839,6 +853,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_EVIL "evil"
#define TRAIT_FRIENDLY "friendly"
#define TRAIT_GRABWEAKNESS "grab_weakness"
#define TRAIT_GRABRESISTANCE "grab_resistance"
#define TRAIT_SNOB "snob"
#define TRAIT_BALD "bald"
#define TRAIT_SHAVED "shaved"

View File

@@ -248,6 +248,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_FIXED_MUTANT_COLORS" = TRAIT_FIXED_MUTANT_COLORS,
"TRAIT_FLESH_DESIRE" = TRAIT_FLESH_DESIRE,
"TRAIT_FLOORED" = TRAIT_FLOORED,
"TRAIT_FLOPPING" = TRAIT_FLOPPING,
"TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION" = TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION,
"TRAIT_FORCED_GRAVITY" = TRAIT_FORCED_GRAVITY,
"TRAIT_FORCED_STANDING" = TRAIT_FORCED_STANDING,
@@ -265,6 +266,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_GIANT" = TRAIT_GIANT,
"TRAIT_GODMODE" = TRAIT_GODMODE,
"TRAIT_GOOD_HEARING" = TRAIT_GOOD_HEARING,
"TRAIT_GRABRESISTANCE" = TRAIT_GRABRESISTANCE,
"TRAIT_GRABWEAKNESS" = TRAIT_GRABWEAKNESS,
"TRAIT_GREENTEXT_CURSED" = TRAIT_GREENTEXT_CURSED,
"TRAIT_GUNFLIP" = TRAIT_GUNFLIP,
@@ -300,6 +302,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_INVISIBLE_MAN" = TRAIT_INVISIBLE_MAN,
"TRAIT_INVISIMIN" = TRAIT_INVISIMIN,
"TRAIT_IN_CALL" = TRAIT_IN_CALL,
"TRAIT_IS_WET" = TRAIT_IS_WET,
"TRAIT_IWASBATONED" = TRAIT_IWASBATONED,
"TRAIT_JOLLY" = TRAIT_JOLLY,
"TRAIT_KISS_OF_DEATH" = TRAIT_KISS_OF_DEATH,
@@ -463,6 +466,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_SIXTHSENSE" = TRAIT_SIXTHSENSE,
"TRAIT_SKITTISH" = TRAIT_SKITTISH,
"TRAIT_SLEEPIMMUNE" = TRAIT_SLEEPIMMUNE,
"TRAIT_SLIPPERY_WHEN_WET" = TRAIT_SLIPPERY_WHEN_WET,
"TRAIT_SMOKER" = TRAIT_SMOKER,
"TRAIT_SNEAK" = TRAIT_SNEAK,
"TRAIT_SNOB" = TRAIT_SNOB,
@@ -503,6 +507,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_TENACIOUS" = TRAIT_TENACIOUS,
"TRAIT_TENTACLE_IMMUNE" = TRAIT_TENTACLE_IMMUNE,
"TRAIT_TESLA_SHOCKIMMUNE" = TRAIT_TESLA_SHOCKIMMUNE,
"TRAIT_TETRODOTOXIN_HEALING" = TRAIT_TETRODOTOXIN_HEALING,
"TRAIT_THERMAL_VISION" = TRAIT_THERMAL_VISION,
"TRAIT_THINKING_IN_CHARACTER" = TRAIT_THINKING_IN_CHARACTER,
"TRAIT_THROWINGARM" = TRAIT_THROWINGARM,
@@ -536,9 +541,12 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_VIRUS_RESISTANCE" = TRAIT_VIRUS_RESISTANCE,
"TRAIT_VORACIOUS" = TRAIT_VORACIOUS,
"TRAIT_WAS_EVOLVED" = TRAIT_WAS_EVOLVED,
"TRAIT_WATER_ADAPTATION" = TRAIT_WATER_ADAPTATION,
"TRAIT_WATER_HATER" = TRAIT_WATER_HATER,
"TRAIT_WEAK_SOUL" = TRAIT_WEAK_SOUL,
"TRAIT_WEB_SURFER" = TRAIT_WEB_SURFER,
"TRAIT_WEB_WEAVER" = TRAIT_WEB_WEAVER,
"TRAIT_WET_FOR_LONGER" = TRAIT_WET_FOR_LONGER,
"TRAIT_WINE_TASTER" = TRAIT_WINE_TASTER,
"TRAIT_WING_BUFFET" = TRAIT_WING_BUFFET,
"TRAIT_WING_BUFFET_TIRED" = TRAIT_WING_BUFFET_TIRED,

View File

@@ -121,6 +121,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_GIANT" = TRAIT_GIANT,
"TRAIT_GODMODE" = TRAIT_GODMODE,
"TRAIT_GOOD_HEARING" = TRAIT_GOOD_HEARING,
"TRAIT_GRABRESISTANCE" = TRAIT_GRABRESISTANCE,
"TRAIT_GRABWEAKNESS" = TRAIT_GRABWEAKNESS,
"TRAIT_GREENTEXT_CURSED" = TRAIT_GREENTEXT_CURSED,
"TRAIT_GUNFLIP" = TRAIT_GUNFLIP,
@@ -262,6 +263,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_SIXTHSENSE" = TRAIT_SIXTHSENSE,
"TRAIT_SKITTISH" = TRAIT_SKITTISH,
"TRAIT_SLEEPIMMUNE" = TRAIT_SLEEPIMMUNE,
"TRAIT_SLIPPERY_WHEN_WET" = TRAIT_SLIPPERY_WHEN_WET,
"TRAIT_SMOKER" = TRAIT_SMOKER,
"TRAIT_SNOB" = TRAIT_SNOB,
"TRAIT_SOFTSPOKEN" = TRAIT_SOFTSPOKEN,
@@ -283,6 +285,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_TAGGER" = TRAIT_TAGGER,
"TRAIT_TENTACLE_IMMUNE" = TRAIT_TENTACLE_IMMUNE,
"TRAIT_TESLA_SHOCKIMMUNE" = TRAIT_TESLA_SHOCKIMMUNE,
"TRAIT_TETRODOTOXIN_HEALING" = TRAIT_TETRODOTOXIN_HEALING,
"TRAIT_THERMAL_VISION" = TRAIT_THERMAL_VISION,
"TRAIT_THROWINGARM" = TRAIT_THROWINGARM,
"TRAIT_TIME_STOP_IMMUNE" = TRAIT_TIME_STOP_IMMUNE,
@@ -308,11 +311,14 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_VIRUSIMMUNE" = TRAIT_VIRUSIMMUNE,
"TRAIT_VIRUS_RESISTANCE" = TRAIT_VIRUS_RESISTANCE,
"TRAIT_VORACIOUS" = TRAIT_VORACIOUS,
"TRAIT_WOUND_LICKER" = TRAIT_WOUND_LICKER,
"TRAIT_WATER_ADAPTATION" = TRAIT_WATER_ADAPTATION,
"TRAIT_WATER_HATER" = TRAIT_WATER_HATER,
"TRAIT_WEAK_SOUL" = TRAIT_WEAK_SOUL,
"TRAIT_WEB_SURFER" = TRAIT_WEB_SURFER,
"TRAIT_WEB_WEAVER" = TRAIT_WEB_WEAVER,
"TRAIT_WET_FOR_LONGER" = TRAIT_WET_FOR_LONGER,
"TRAIT_WINE_TASTER" = TRAIT_WINE_TASTER,
"TRAIT_WOUND_LICKER" = TRAIT_WOUND_LICKER,
"TRAIT_XENO_HOST" = TRAIT_XENO_HOST,
"TRAIT_XENO_IMMUNE" = TRAIT_XENO_IMMUNE,
"TRAIT_XRAY_HEARING" = TRAIT_XRAY_HEARING,

View File

@@ -183,6 +183,11 @@
desc = "There's sleeping gas in the air and you're breathing it in. Find some fresh air. The box in your backpack has an oxygen tank and breath mask in it."
icon_state = ALERT_TOO_MUCH_N2O
/atom/movable/screen/alert/not_enough_water
name = "Choking (No H2O)"
desc = "You're not getting enough water. Drench yourself in some water (e.g. showers) or get some water vapor before you pass out!"
icon_state = ALERT_NOT_ENOUGH_WATER
//End gas alerts
/atom/movable/screen/alert/gross

View File

@@ -43,9 +43,10 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
var/list/tail_spines_list
//Mutant Human bits
var/list/tails_list_human
var/list/tails_list_felinid
var/list/tails_list_lizard
var/list/tails_list_monkey
var/list/tails_list_fish
var/list/ears_list
var/list/wings_list
var/list/wings_open_list
@@ -87,9 +88,11 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
socks_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/socks)[DEFAULT_SPRITE_LIST]
lizard_markings_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/lizard_markings, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
tails_list_human = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
tails_list_felinid = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/felinid, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
tails_list_lizard = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard)[DEFAULT_SPRITE_LIST]
tails_list_monkey = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/monkey)[DEFAULT_SPRITE_LIST]
//tails fo fish organ infusions, not for prefs.
tails_list_fish = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/fish)[DEFAULT_SPRITE_LIST]
snouts_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts)[DEFAULT_SPRITE_LIST]
horns_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/horns, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
ears_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, add_blank = TRUE)[DEFAULT_SPRITE_LIST]

View File

@@ -127,15 +127,21 @@
if(ORGAN_COLOR_INHERIT)
draw_color = bodypart_owner.draw_color
if(ORGAN_COLOR_HAIR)
var/datum/species/species = bodypart_owner.owner?.dna?.species
var/fixed_color = species?.get_fixed_hair_color(bodypart_owner)
if(!ishuman(bodypart_owner.owner))
draw_color = fixed_color
return
var/mob/living/carbon/human/human_owner = bodypart_owner.owner
var/obj/item/bodypart/head/my_head = human_owner.get_bodypart(BODY_ZONE_HEAD) //not always the same as bodypart_owner
//head hair color takes priority, owner hair color is a backup if we lack a head or something
if(my_head)
draw_color = my_head.hair_color
else
draw_color = human_owner.hair_color
if(!my_head)
draw_color = fixed_color || human_owner.hair_color
return
if(my_head.head_flags & (HEAD_HAIR|HEAD_FACIAL_HAIR))
draw_color = my_head.fixed_hair_color || my_head.hair_color
else //inherit mutant color of the bodypart if the owner doesn't have hair.
draw_color = bodypart_owner.draw_color
return TRUE

View File

@@ -0,0 +1,99 @@
/**
* A component that overrides the bubble_icon variable when equipped or implanted
* while having a simple priority system, so accessories have higher priority than
* organs, for example.
*/
/datum/component/bubble_icon_override
dupe_mode = COMPONENT_DUPE_ALLOWED
can_transfer = TRUE //sure why not
///The override to the default bubble icon for the atom
var/bubble_icon
///The priority of this bubble icon compared to others
var/priority
/datum/component/bubble_icon_override/Initialize(bubble_icon, priority)
if(!isclothing(parent) && !isorgan(parent))
return COMPONENT_INCOMPATIBLE
src.bubble_icon = bubble_icon
src.priority = priority
/datum/component/bubble_icon_override/RegisterWithParent()
if(isclothing(parent))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equipped))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_dropped))
else if(isorgan(parent))
RegisterSignal(parent, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_organ_implanted))
RegisterSignal(parent, COMSIG_ORGAN_REMOVED, PROC_REF(on_organ_removed))
var/mob/living/target = get_bubble_icon_target()
if(target)
register_owner(target)
/datum/component/bubble_icon_override/proc/register_owner(mob/living/owner)
RegisterSignal(owner, COMSIG_GET_BUBBLE_ICON, PROC_REF(return_bubble_icon))
get_bubble_icon(owner)
/datum/component/bubble_icon_override/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ITEM_EQUIPPED,
COMSIG_ITEM_DROPPED,
COMSIG_ORGAN_IMPLANTED,
COMSIG_ORGAN_REMOVED,
))
var/mob/living/target = get_bubble_icon_target()
if(target)
unregister_owner(target)
/datum/component/bubble_icon_override/proc/unregister_owner(mob/living/owner)
UnregisterSignal(owner, list(COMSIG_GET_BUBBLE_ICON))
get_bubble_icon(owner)
///Returns the potential wearer/owner of the object when the component is un/registered to/from it
/datum/component/bubble_icon_override/proc/get_bubble_icon_target()
if(isclothing(parent))
var/obj/item/clothing/clothing = parent
if(istype(clothing, /obj/item/clothing/accessory))
clothing = clothing.loc
if(!istype(clothing))
return null
var/mob/living/wearer = clothing.loc
if(istype(wearer) && (wearer.get_slot_by_item(clothing) & clothing.slot_flags))
return parent
else if(isorgan(parent))
var/obj/item/organ/organ = parent
return organ.owner
/datum/component/bubble_icon_override/proc/on_equipped(obj/item/source, mob/equipper, slot)
SIGNAL_HANDLER
if(slot & source.slot_flags)
register_owner(equipper)
/datum/component/bubble_icon_override/proc/on_dropped(obj/item/source, mob/dropper)
SIGNAL_HANDLER
unregister_owner(dropper)
/datum/component/bubble_icon_override/proc/on_organ_implanted(obj/item/organ/source, mob/owner)
SIGNAL_HANDLER
register_owner(owner)
/datum/component/bubble_icon_override/proc/on_organ_removed(obj/item/organ/source, mob/owner)
SIGNAL_HANDLER
unregister_owner(owner)
/**
* Get the bubble icon with the highest priority from all instances of bubble_icon_override
* currently registered with the target.
*/
/datum/component/bubble_icon_override/proc/get_bubble_icon(mob/living/target)
if(QDELETED(parent))
return
var/list/holder = list(null)
SEND_SIGNAL(target, COMSIG_GET_BUBBLE_ICON, holder)
var/bubble_icon = holder[1]
target.bubble_icon = bubble_icon || initial(target.bubble_icon)
/datum/component/bubble_icon_override/proc/return_bubble_icon(datum/source, list/holder)
SIGNAL_HANDLER
var/enemy_priority = holder[holder[1]]
if(enemy_priority < priority)
holder[1] = bubble_icon
holder[bubble_icon] = priority

View File

@@ -599,8 +599,12 @@ Behavior that's still missing from this component that original food items had t
return food.crafting_complexity + complexity_to_add
/// Get food quality adjusted according to eater's preferences
/datum/component/edible/proc/get_perceived_food_quality(mob/living/carbon/human/eater)
/datum/component/edible/proc/get_perceived_food_quality(mob/living/eater)
var/food_quality = get_recipe_complexity()
var/list/extra_quality = list()
SEND_SIGNAL(eater, COMSIG_LIVING_GET_PERCEIVED_FOOD_QUALITY, src, extra_quality)
for(var/quality in extra_quality)
food_quality += quality
if(HAS_TRAIT(parent, TRAIT_FOOD_SILVER)) // it's not real food
if(!isjellyperson(eater)) //if you aren't a jellyperson, it makes you sick no matter how nice it looks

View File

@@ -38,14 +38,11 @@
COMSIG_ATOM_ENTERED = PROC_REF(Slip),
)
///what we give to connect_loc if we're an item and get equipped by a mob. makes slippable mobs moving over our holder slip
var/static/list/holder_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(Slip_on_wearer),
///what we give to connect_loc if we're an item and get equipped by a mob, or if we're a mob. makes slippable mobs moving over the mob slip
var/static/list/mob_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(slip_on_mob),
)
/// The connect_loc_behalf component for the holder_connections list.
var/datum/weakref/holder_connect_loc_behalf
/**
* Initialize the slippery component behaviour
*
@@ -79,14 +76,14 @@
src.slot_whitelist = slot_whitelist
add_connect_loc_behalf_to_parent()
if(ismovable(parent))
if(isitem(parent))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
RegisterSignal(parent, COMSIG_ITEM_APPLY_FANTASY_BONUSES, PROC_REF(apply_fantasy_bonuses))
RegisterSignal(parent, COMSIG_ITEM_REMOVE_FANTASY_BONUSES, PROC_REF(remove_fantasy_bonuses))
else
if(!ismovable(parent))
RegisterSignal(parent, COMSIG_ATOM_ENTERED, PROC_REF(Slip))
else if(isitem(parent))
src.lube_flags |= SLIPPERY_WHEN_LYING_DOWN
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
RegisterSignal(parent, COMSIG_ITEM_APPLY_FANTASY_BONUSES, PROC_REF(apply_fantasy_bonuses))
RegisterSignal(parent, COMSIG_ITEM_REMOVE_FANTASY_BONUSES, PROC_REF(remove_fantasy_bonuses))
/datum/component/slippery/Destroy(force)
can_slip_callback = null
@@ -114,8 +111,13 @@
lube_flags = previous_lube_flags
/datum/component/slippery/proc/add_connect_loc_behalf_to_parent()
if(ismovable(parent))
AddComponent(/datum/component/connect_loc_behalf, parent, default_connections)
var/list/connections_to_use
if(isliving(parent))
connections_to_use = mob_connections
else if(ismovable(parent))
connections_to_use = default_connections
if(connections_to_use)
AddComponent(/datum/component/connect_loc_behalf, parent, connections_to_use)
/datum/component/slippery/InheritComponent(
datum/component/slippery/component,
@@ -184,7 +186,7 @@
if((!LAZYLEN(slot_whitelist) || (slot in slot_whitelist)) && isliving(equipper))
holder = equipper
qdel(GetComponent(/datum/component/connect_loc_behalf))
AddComponent(/datum/component/connect_loc_behalf, holder, holder_connections)
AddComponent(/datum/component/connect_loc_behalf, holder, mob_connections)
RegisterSignal(holder, COMSIG_QDELETING, PROC_REF(holder_deleted))
/**
@@ -227,10 +229,11 @@
* * source - the source of the signal
* * arrived - the atom/movable that slipped on us.
*/
/datum/component/slippery/proc/Slip_on_wearer(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
/datum/component/slippery/proc/slip_on_mob(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
if(holder.body_position == LYING_DOWN && !holder.buckled)
var/mob/living/living = holder || parent
if(!(lube_flags & SLIPPERY_WHEN_LYING_DOWN) || (living.body_position == LYING_DOWN && !living.buckled))
Slip(source, arrived)
/datum/component/slippery/UnregisterFromParent()

View File

@@ -71,4 +71,5 @@
if(can_splat_on && is_type_in_typecache(hit_atom, GLOB.splattable))
hit_atom.AddComponent(/datum/component/face_decal/splat, icon_state, layer, splat_color || source.color, memory_type, moodlet_type)
SEND_SIGNAL(source, COMSIG_MOVABLE_SPLAT, hit_atom)
qdel(source)
if(!isprojectile(source))
qdel(source)

View File

@@ -223,7 +223,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
if(features["lizard_markings"])
L[DNA_LIZARD_MARKINGS_BLOCK] = construct_block(SSaccessories.lizard_markings_list.Find(features["lizard_markings"]), length(SSaccessories.lizard_markings_list))
if(features["tail_cat"])
L[DNA_TAIL_BLOCK] = construct_block(SSaccessories.tails_list_human.Find(features["tail_cat"]), length(SSaccessories.tails_list_human))
L[DNA_TAIL_BLOCK] = construct_block(SSaccessories.tails_list_felinid.Find(features["tail_cat"]), length(SSaccessories.tails_list_felinid))
if(features["tail_lizard"])
L[DNA_LIZARD_TAIL_BLOCK] = construct_block(SSaccessories.tails_list_lizard.Find(features["tail_lizard"]), length(SSaccessories.tails_list_lizard))
if(features["snout"])
@@ -246,6 +246,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
L[DNA_MUSHROOM_CAPS_BLOCK] = construct_block(SSaccessories.caps_list.Find(features["caps"]), length(SSaccessories.caps_list))
if(features["pod_hair"])
L[DNA_POD_HAIR_BLOCK] = construct_block(SSaccessories.pod_hair_list.Find(features["pod_hair"]), length(SSaccessories.pod_hair_list))
if(features["fish_tail"])
L[DNA_FISH_TAIL_BLOCK] = construct_block(SSaccessories.tails_list_fish.Find(features["fish_tail"]), length(SSaccessories.tails_list_fish))
for(var/blocknum in 1 to DNA_FEATURE_BLOCKS)
. += L[blocknum] || random_string(GET_UI_BLOCK_LEN(blocknum), GLOB.hex_characters)
@@ -367,7 +369,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
if(DNA_LIZARD_MARKINGS_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.lizard_markings_list.Find(features["lizard_markings"]), length(SSaccessories.lizard_markings_list)))
if(DNA_TAIL_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.tails_list_human.Find(features["tail_cat"]), length(SSaccessories.tails_list_human)))
set_uni_feature_block(blocknumber, construct_block(SSaccessories.tails_list_felinid.Find(features["tail_cat"]), length(SSaccessories.tails_list_felinid)))
if(DNA_LIZARD_TAIL_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.tails_list_lizard.Find(features["tail_lizard"]), length(SSaccessories.tails_list_lizard)))
if(DNA_SNOUT_BLOCK)
@@ -390,6 +392,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
set_uni_feature_block(blocknumber, construct_block(SSaccessories.caps_list.Find(features["caps"]), length(SSaccessories.caps_list)))
if(DNA_POD_HAIR_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.pod_hair_list.Find(features["pod_hair"]), length(SSaccessories.pod_hair_list)))
if(DNA_FISH_TAIL_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.tails_list_fish.Find(features["fish_tail"]), length(SSaccessories.tails_list_fish)))
//Please use add_mutation or activate_mutation instead
/datum/dna/proc/force_give(datum/mutation/human/human_mutation)
@@ -672,7 +676,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
if(dna.features["spines"])
dna.features["spines"] = SSaccessories.spines_list[deconstruct_block(get_uni_feature_block(features, DNA_SPINES_BLOCK), length(SSaccessories.spines_list))]
if(dna.features["tail_cat"])
dna.features["tail_cat"] = SSaccessories.tails_list_human[deconstruct_block(get_uni_feature_block(features, DNA_TAIL_BLOCK), length(SSaccessories.tails_list_human))]
dna.features["tail_cat"] = SSaccessories.tails_list_felinid[deconstruct_block(get_uni_feature_block(features, DNA_TAIL_BLOCK), length(SSaccessories.tails_list_felinid))]
if(dna.features["tail_lizard"])
dna.features["tail_lizard"] = SSaccessories.tails_list_lizard[deconstruct_block(get_uni_feature_block(features, DNA_LIZARD_TAIL_BLOCK), length(SSaccessories.tails_list_lizard))]
if(dna.features["ears"])
@@ -691,6 +695,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
dna.features["caps"] = SSaccessories.caps_list[deconstruct_block(get_uni_feature_block(features, DNA_MUSHROOM_CAPS_BLOCK), length(SSaccessories.caps_list))]
if(dna.features["pod_hair"])
dna.features["pod_hair"] = SSaccessories.pod_hair_list[deconstruct_block(get_uni_feature_block(features, DNA_POD_HAIR_BLOCK), length(SSaccessories.pod_hair_list))]
if(dna.features["fish_tail"])
dna.features["fish_tail"] = SSaccessories.tails_list_fish[deconstruct_block(get_uni_feature_block(features, DNA_FISH_TAIL_BLOCK), length(SSaccessories.tails_list_fish))]
for(var/obj/item/organ/organ in organs)
organ.mutate_feature(features, src)

View File

@@ -71,7 +71,10 @@
if(source.body_position == LYING_DOWN) //play crawling sound if we're lying
if(turf.footstep)
playsound(turf, 'sound/effects/footstep/crawl1.ogg', 15 * volume, falloff_distance = 1, vary = sound_vary)
var/sound = 'sound/effects/footstep/crawl1.ogg'
if(HAS_TRAIT(source, TRAIT_FLOPPING))
sound = pick(SFX_FISH_PICKUP, 'sound/mobs/non-humanoids/fish/fish_drop1.ogg')
playsound(turf, sound, 15 * volume, falloff_distance = 1, vary = sound_vary)
return
if(iscarbon(source) && source.move_intent == MOVE_INTENT_WALK)

View File

@@ -18,7 +18,7 @@
return
if(isliving(moved))
var/mob/living/living_moved = moved
if (living_moved.incapacitated || living_moved.body_position == LYING_DOWN)
if (living_moved.incapacitated || (living_moved.body_position == LYING_DOWN && !HAS_TRAIT(living_moved, TRAIT_FLOPPING)))
return
waddling_animation(moved)

View File

@@ -2,3 +2,8 @@
name = "Mutant Organ"
icon_file = 'icons/obj/medical/organs/infuser_organs.dmi'
json_config = 'code/datums/greyscale/json_configs/mutant_organs.json'
/datum/greyscale_config/fish_tail
name = "Fish Tail"
icon_file = 'icons/obj/medical/organs/infuser_organs.dmi'
json_config = 'code/datums/greyscale/json_configs/fish_tail.json'

View File

@@ -0,0 +1,15 @@
{
"fish_tail": [
{
"type": "icon_state",
"icon_state": "fish_tail",
"blend_mode": "overlay",
"color_ids": [ 1 ]
},
{
"type": "icon_state",
"icon_state": "fish_tail_meat",
"blend_mode": "overlay"
}
]
}

View File

@@ -7,3 +7,11 @@
description = "There's a lot that could be on your mind right now. But this feeling of contentedness, a universal calling to simply sit back and observe is washing over you..."
mood_change = 10
special_screen_obj = "mood_gondola"
/datum/mood_event/fish_waterless
mood_change = -3
description = "It sucks to be dry. I feel like a fish out of water."
/datum/mood_event/fish_water
mood_change = 1
description = "Glug glug!"

View File

@@ -1744,6 +1744,23 @@
/// Describes which tail spine sprites to use, if any.
var/spine_key = NONE
///Used for fish-infused tails, which come in different flavors.
/datum/sprite_accessory/tails/fish
icon = 'icons/mob/human/fish_features.dmi'
color_src = HAIR_COLOR
/datum/sprite_accessory/tails/fish/default
name = "Fish"
icon_state = "fish"
/datum/sprite_accessory/tails/fish/shark
name = "Shark"
icon_state = "shark"
/datum/sprite_accessory/tails/fish/orca
name = "Orca"
icon_state = "orca"
/datum/sprite_accessory/tails/lizard
icon = 'icons/mob/human/species/lizard/lizard_tails.dmi'
spine_key = SPINE_KEY_LIZARD
@@ -1774,7 +1791,7 @@
icon_state = "short"
spine_key = NONE
/datum/sprite_accessory/tails/human/cat
/datum/sprite_accessory/tails/felinid/cat
name = "Cat"
icon = 'icons/mob/human/cat_features.dmi'
icon_state = "default"

View File

@@ -300,12 +300,45 @@
enemy_types = list(/datum/status_effect/fire_handler/fire_stacks)
stack_modifier = -1
///If the mob has the TRAIT_SLIPPERY_WHEN_WET trait, the mob gets this component while it's wet
var/datum/component/slippery/slipperiness
/datum/status_effect/fire_handler/wet_stacks/on_apply()
. = ..()
RegisterSignals(owner, list(SIGNAL_ADDTRAIT(TRAIT_WET_FOR_LONGER), SIGNAL_REMOVETRAIT(TRAIT_WET_FOR_LONGER)), PROC_REF(update_wet_stack_modifier))
update_wet_stack_modifier()
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_SLIPPERY_WHEN_WET), PROC_REF(become_slippery))
RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_SLIPPERY_WHEN_WET), PROC_REF(no_longer_slippery))
if(HAS_TRAIT(owner, TRAIT_SLIPPERY_WHEN_WET))
become_slippery()
ADD_TRAIT(owner, TRAIT_IS_WET, TRAIT_STATUS_EFFECT(id))
/datum/status_effect/fire_handler/wet_stacks/on_remove()
. = ..()
REMOVE_TRAIT(owner, TRAIT_IS_WET, TRAIT_STATUS_EFFECT(id))
if(HAS_TRAIT(owner, TRAIT_SLIPPERY_WHEN_WET))
no_longer_slippery()
/datum/status_effect/fire_handler/wet_stacks/proc/update_wet_stack_modifier()
SIGNAL_HANDLER
stack_modifier = HAS_TRAIT(owner, TRAIT_WET_FOR_LONGER) ? -3.5 : -1
/datum/status_effect/fire_handler/wet_stacks/proc/become_slippery()
SIGNAL_HANDLER
slipperiness = owner.AddComponent(/datum/component/slippery, 5 SECONDS, lube_flags = SLIPPERY_WHEN_LYING_DOWN)
ADD_TRAIT(owner, TRAIT_NO_SLIP_WATER, TRAIT_STATUS_EFFECT(id))
/datum/status_effect/fire_handler/wet_stacks/proc/no_longer_slippery()
SIGNAL_HANDLER
QDEL_NULL(slipperiness)
REMOVE_TRAIT(owner, TRAIT_NO_SLIP_WATER, TRAIT_STATUS_EFFECT(id))
/datum/status_effect/fire_handler/wet_stacks/get_examine_text()
return "[owner.p_They()] look[owner.p_s()] a little soaked."
/datum/status_effect/fire_handler/wet_stacks/tick(seconds_between_ticks)
adjust_stacks(-0.5 * seconds_between_ticks)
var/decay = HAS_TRAIT(owner, TRAIT_WET_FOR_LONGER) ? -0.035 : -0.5
adjust_stacks(decay * seconds_between_ticks)
if(stacks <= 0)
qdel(src)

View File

@@ -615,10 +615,10 @@
desc = "A good wash fills me with energy!"
icon_state = "shower_regen"
/atom/movable/screen/alert/status_effect/shower_regen/catgirl
/atom/movable/screen/alert/status_effect/shower_regen/hater
name = "Washing"
desc = "Waaater... Fuck this WATER!!"
icon_state = "shower_regen_catgirl"
icon_state = "shower_regen_hater"
/datum/status_effect/shower_regen
id = "shower_regen"
@@ -630,11 +630,17 @@
/datum/status_effect/shower_regen/on_apply()
. = ..()
if(isfelinid(owner))
alert_type = /atom/movable/screen/alert/status_effect/shower_regen/catgirl
if(HAS_TRAIT(owner, TRAIT_WATER_HATER) && !HAS_TRAIT(owner, TRAIT_WATER_ADAPTATION))
alert_type = /atom/movable/screen/alert/status_effect/shower_regen/hater
/datum/status_effect/shower_regen/tick(seconds_between_ticks)
. = ..()
var/heal_or_deal = isfelinid(owner) ? 1 : -1
var/water_adaptation = HAS_TRAIT(owner, TRAIT_WATER_ADAPTATION)
var/heal_or_deal = HAS_TRAIT(owner, TRAIT_WATER_HATER) && !water_adaptation ? 1 : -1
if(water_adaptation) //very mild healing for those with the water adaptation trait (fish infusion)
owner.adjustOxyLoss(-1 * seconds_between_ticks, updating_health = FALSE, required_biotype = MOB_ORGANIC)
owner.adjustFireLoss(-0.6 * seconds_between_ticks, updating_health = FALSE, required_bodytype = BODYTYPE_ORGANIC)
owner.adjustToxLoss(-0.6 * seconds_between_ticks, updating_health = FALSE, required_biotype = MOB_ORGANIC)
owner.adjustBruteLoss(-0.6 * seconds_between_ticks, updating_health = FALSE, required_bodytype = BODYTYPE_ORGANIC)
heal_or_deal *= 1.5
owner.adjustStaminaLoss(stamina_heal_per_tick * heal_or_deal * seconds_between_ticks)

View File

@@ -47,6 +47,8 @@
// Valid organ successfully picked.
new_organ = new new_organ()
new_organ.replace_into(src)
//make sure bodypart overlays are correctly displayed.
update_body_parts()
return TRUE
/// Picks a random mutated organ from the given infuser entry which is also compatible with this human.

View File

@@ -0,0 +1,61 @@
///Action from the inky tongue, from fish with the ink production trait.
/datum/action/cooldown/ink_spit
name = "Spit Ink"
desc = "Spits ink at someone, blinding them temporarily."
button_icon = 'icons/hud/radial_fishing.dmi'
button_icon_state = "oil"
base_background_icon_state = "bg_default"
active_background_icon_state = "bg_default_on"
check_flags = AB_CHECK_IMMOBILE | AB_CHECK_CONSCIOUS | AB_CHECK_INCAPACITATED
click_to_activate = TRUE
unset_after_click = TRUE
cooldown_time = 21 SECONDS
/datum/action/cooldown/ink_spit/IsAvailable(feedback = FALSE)
var/mob/living/carbon/as_carbon = owner
if(istype(as_carbon) && as_carbon.is_mouth_covered(ITEM_SLOT_MASK))
return FALSE
if(!isturf(owner.loc))
return FALSE
return ..()
/datum/action/cooldown/ink_spit/set_click_ability(mob/on_who)
. = ..()
if(!.)
return
to_chat(on_who, span_notice("You prepare your ink glands. <B>Right-click to fire at a target!</B>"))
build_all_button_icons()
/datum/action/cooldown/ink_spit/unset_click_ability(mob/on_who, refund_cooldown = TRUE)
. = ..()
if(!.)
return
build_all_button_icons()
// We do this in InterceptClickOn() instead of Activate()
// because we use the click parameters for aiming the projectile
// (or something like that)
/datum/action/cooldown/ink_spit/InterceptClickOn(mob/living/caller, params, atom/target)
if(!LAZYACCESS(params2list(params), RIGHT_CLICK))
return
. = ..()
var/modifiers = params2list(params)
caller.visible_message(
span_danger("[caller] spits ink!"),
span_bold("You spit ink."),
)
var/obj/projectile/ink_spit/ink = new /obj/projectile/ink_spit(caller.loc)
ink.preparePixelProjectile(target, caller, modifiers)
ink.firer = caller
ink.fire()
playsound(caller, 'sound/items/weapons/pierce.ogg', 20, TRUE, -1)
caller.newtonian_move(get_angle(target, caller))
StartCooldown()
return TRUE
// Has to return TRUE, otherwise is skipped.
/datum/action/cooldown/ink_spit/Activate(atom/target)
return TRUE

View File

@@ -105,3 +105,86 @@
infusion_desc = "kafkaesque" // Gregor Samsa !!
tier = DNA_MUTANT_TIER_ONE
status_effect_type = /datum/status_effect/organ_set_bonus/roach
/datum/infuser_entry/fish
name = "Fish"
infuse_mob_name = "fish"
desc = "Aquatic life comes in several forms. A fisherman could tell you more about it, but that's beside the point. \
This infusion comes with many benefits and one potential major drawback being fish-mutated lungs, with \
additional organs depending on the traits of the fish used for the infusion."
threshold_desc = "While wet, you're slightly sturdier, immune to slips, and both slippery and faster while crawling. \
Drinking water and showers heal you, and it takes longer to dry out, however you're weaker when dry. \
Finally, you resist high pressures and are better at fishing. "
qualities = list(
"faster in water",
"resistant to food diseases",
"enjoy eating raw fish",
"flopping and waddling",
"fishing is easier",
"Need water. badly!",
"possibly more",
)
input_obj_or_mob = list(
/obj/item/fish,
)
output_organs = list(
/obj/item/organ/internal/lungs/fish,
/obj/item/organ/internal/stomach/fish,
/obj/item/organ/external/tail/fish,
)
infusion_desc = "piscine"
tier = DNA_MUTANT_TIER_ONE
status_effect_type = /datum/status_effect/organ_set_bonus/fish
/datum/infuser_entry/squid
name = "Ink Production"
infuse_mob_name = "ink-producing sealife"
desc = "Some marine mollusks like cuttlefish, squids and octopus release ink when threatened as a smokescreen for their escape. \
This kind of infusion enhances the salivary glands, producing excessive quantities of ink which can later be spat to blind foes."
threshold_desc = DNA_INFUSION_NO_THRESHOLD
qualities = list(
"spit ink to blind foes",
)
output_organs = list(
/obj/item/organ/internal/tongue/inky
)
tier = DNA_MUTANT_TIER_ONE
/datum/infuser_entry/ttx_healing
name = "TTX healing"
infuse_mob_name = "Tetraodontiformes"
desc = "Fish of the Tetraodontiformes (pufferfish etc.) order are known for the highly poisonous tetrodotoxin (TTX) in their bodies. \
Extracting their DNA can provide a way to utilize it for healing instead. It also enables better alcohol metabolization."
threshold_desc = DNA_INFUSION_NO_THRESHOLD
qualities = list(
"TTX healing",
"drink like a fish",
)
output_organs = list(
/obj/item/organ/internal/liver/fish
)
tier = DNA_MUTANT_TIER_ONE
unreachable_effect = TRUE
status_effect_type = /datum/status_effect/organ_set_bonus/fish
/datum/infuser_entry/amphibious
name = "Amphibious"
infuse_mob_name = "Semi-aquatic critters"
desc = "Some animals breathe air, some breath water, a few can breath both, even if none (at least on Earth) can breathe in space."
threshold_desc = DNA_INFUSION_NO_THRESHOLD
qualities = list(
"no need to breathe while wet",
"can beathe water vapor",
)
input_obj_or_mob = list(
/mob/living/basic/frog,
/mob/living/basic/axolotl,
/mob/living/basic/crab,
)
output_organs = list(
/obj/item/organ/internal/lungs/fish/amphibious,
)
infusion_desc = "semi-aquatic"
tier = DNA_MUTANT_TIER_ONE
unreachable_effect = TRUE
status_effect_type = /datum/status_effect/organ_set_bonus/fish

View File

@@ -28,8 +28,16 @@ GLOBAL_LIST_INIT(infuser_entries, prepare_infuser_entries())
)
/// status effect type of the corresponding bonus, if it has one. tier zero won't ever set this.
var/status_effect_type
/// essentially how difficult it is to get this infusion, and if it will be locked behind some progression. see defines for more info
/// ...overwrite this, please
/**
* This var clarifies that while the infuser entry has organs that contribute towards an organ set bonus
* It cannot reach the organ threshold of the bonus on its own, meaning it relies on some other infuser entry for that.
* This is mainly the case for fish organs from fish with specific traits, for example. We don't want the unit test to bith about it.
*/
var/unreachable_effect = FALSE
/**
* essentially how difficult it is to get this infusion, and if it will be locked behind some progression. see defines for more info
* ...overwrite this, please
*/
var/tier = DNA_MUTANT_UNOBTAINABLE
//-- Vars for DNA Infuser Machine --//

View File

@@ -0,0 +1,388 @@
#define FISH_ORGAN_COLOR "#875652" //dark moderate magenta
#define FISH_SCLERA_COLOR COLOR_WHITE
#define FISH_PUPIL_COLOR COLOR_BLUE
#define FISH_COLORS FISH_ORGAN_COLOR + FISH_SCLERA_COLOR + FISH_PUPIL_COLOR
///bonus of the observing gondola: you can ignore environmental hazards
/datum/status_effect/organ_set_bonus/fish
id = "organ_set_bonus_fish"
tick_interval = 1 SECONDS
organs_needed = 3
bonus_activate_text = span_notice("Fish DNA is deeply infused with you! While wet, you crawl faster, are slippery, and cannot slip, and it takes longer to dry out. \
You're also more resistant to high pressure, better at fishing, but less resilient when dry, especially against burns.")
bonus_deactivate_text = span_notice("You no longer feel as fishy. The moisture around your body begins to dissipate faster...")
bonus_traits = list(
TRAIT_RESISTHIGHPRESSURE,
TRAIT_EXPERT_FISHER,
TRAIT_EXAMINE_FISH,
TRAIT_EXAMINE_DEEPER_FISH,
TRAIT_REVEAL_FISH,
TRAIT_EXAMINE_FISHING_SPOT,
TRAIT_WET_FOR_LONGER,
TRAIT_SLIPPERY_WHEN_WET,
TRAIT_EXPANDED_FOV, //fish vision
TRAIT_WATER_ADAPTATION,
)
/datum/status_effect/organ_set_bonus/fish/enable_bonus()
. = ..()
if(!.)
return
RegisterSignals(owner, list(COMSIG_CARBON_GAIN_ORGAN, COMSIG_CARBON_LOSE_ORGAN), PROC_REF(check_tail))
RegisterSignals(owner, list(SIGNAL_ADDTRAIT(TRAIT_IS_WET), SIGNAL_REMOVETRAIT(TRAIT_IS_WET)), PROC_REF(update_wetness))
RegisterSignals(owner, COMSIG_LIVING_GET_PERCEIVED_FOOD_QUALITY, PROC_REF(get_perceived_food_quality))
if(ishuman(owner))
var/mob/living/carbon/human/human = owner
human.physiology.damage_resistance += 8 //base 8% damage resistance, much wow.
if(!HAS_TRAIT(owner, TRAIT_IS_WET))
apply_debuff()
else
ADD_TRAIT(owner, TRAIT_GRABRESISTANCE, REF(src))
owner.add_mood_event("fish_organs_bonus", /datum/mood_event/fish_water)
if(HAS_TRAIT(owner, TRAIT_IS_WET) && istype(owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL), /obj/item/organ/external/tail/fish))
add_speed_buff()
owner.mind?.adjust_experience(/datum/skill/fishing, SKILL_EXP_JOURNEYMAN, silent = TRUE)
/datum/status_effect/organ_set_bonus/fish/disable_bonus()
. = ..()
UnregisterSignal(owner, list(
COMSIG_CARBON_GAIN_ORGAN,
COMSIG_CARBON_LOSE_ORGAN,
SIGNAL_ADDTRAIT(TRAIT_IS_WET),
SIGNAL_REMOVETRAIT(TRAIT_IS_WET),
COMSIG_LIVING_TREAT_MESSAGE,
COMSIG_LIVING_GET_PERCEIVED_FOOD_QUALITY,
))
if(!HAS_TRAIT(owner, TRAIT_IS_WET))
remove_debuff()
else
REMOVE_TRAIT(owner, TRAIT_GRABRESISTANCE, REF(src))
owner.clear_mood_event("fish_organs_bonus")
if(ishuman(owner))
var/mob/living/carbon/human/human = owner
human.physiology.damage_resistance -= 8
if(HAS_TRAIT(owner, TRAIT_IS_WET) && istype(owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL), /obj/item/organ/external/tail/fish))
remove_speed_buff()
owner.mind?.adjust_experience(/datum/skill/fishing, -SKILL_EXP_JOURNEYMAN, silent = TRUE)
/datum/status_effect/organ_set_bonus/fish/proc/get_perceived_food_quality(datum/source, datum/component/edible/edible, list/extra_quality)
SIGNAL_HANDLER
if(HAS_TRAIT(edible.parent, TRAIT_GREAT_QUALITY_BAIT))
extra_quality += LIKED_FOOD_QUALITY_CHANGE * 3
else if(HAS_TRAIT(edible.parent, TRAIT_GOOD_QUALITY_BAIT))
extra_quality += LIKED_FOOD_QUALITY_CHANGE * 2
else if(HAS_TRAIT(edible.parent, TRAIT_BASIC_QUALITY_BAIT))
extra_quality += LIKED_FOOD_QUALITY_CHANGE
/datum/status_effect/organ_set_bonus/fish/tick(seconds_between_ticks)
. = ..()
if(!bonus_active || !HAS_TRAIT(owner, TRAIT_IS_WET))
return
owner.adjust_bodytemperature(-2 * seconds_between_ticks, min_temp = owner.get_body_temp_normal())
owner.adjustStaminaLoss(-1.5 * seconds_between_ticks)
/datum/status_effect/organ_set_bonus/fish/proc/update_wetness(datum/source)
SIGNAL_HANDLER
if(HAS_TRAIT(owner, TRAIT_IS_WET)) //remove the debuffs from being dry
remove_debuff()
if(istype(owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL), /obj/item/organ/external/tail/fish))
add_speed_buff()
return
apply_debuff()
if(istype(owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL), /obj/item/organ/external/tail/fish))
remove_speed_buff()
/datum/status_effect/organ_set_bonus/fish/proc/apply_debuff()
REMOVE_TRAIT(owner, TRAIT_GRABRESISTANCE, REF(src))
owner.add_movespeed_modifier(/datum/movespeed_modifier/fish_waterless)
owner.add_mood_event("fish_organs_bonus", /datum/mood_event/fish_waterless)
if(!ishuman(owner))
return
var/mob/living/carbon/human/human = owner
human.physiology.burn_mod *= 1.5
human.physiology.heat_mod *= 1.2
human.physiology.brute_mod *= 1.1
human.physiology.stun_mod *= 1.1
human.physiology.knockdown_mod *= 1.1
human.physiology.stamina_mod *= 1.1
human.physiology.damage_resistance -= 16 //from +8% to -8%
/datum/status_effect/organ_set_bonus/fish/proc/remove_debuff()
ADD_TRAIT(owner, TRAIT_GRABRESISTANCE, REF(src)) //harder to grab when wet.
owner.remove_movespeed_modifier(/datum/movespeed_modifier/fish_waterless)
owner.add_mood_event("fish_organs_bonus", /datum/mood_event/fish_water)
if(!ishuman(owner))
return
var/mob/living/carbon/human/human = owner
human.physiology.burn_mod /= 1.5
human.physiology.heat_mod /= 1.2
human.physiology.brute_mod /= 1.1
human.physiology.stun_mod /= 1.1
human.physiology.knockdown_mod /= 1.1
human.physiology.stamina_mod /= 1.1
human.physiology.damage_resistance += 16 //from -8% to +8%
/datum/status_effect/organ_set_bonus/fish/proc/check_tail(mob/living/carbon/source, obj/item/organ/organ, special)
SIGNAL_HANDLER
if(!HAS_TRAIT(owner, TRAIT_IS_WET) || !istype(organ, /obj/item/organ/external/tail/fish))
return
var/obj/item/organ/tail = owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL)
if(tail != organ)
remove_speed_buff()
return
add_speed_buff()
/datum/status_effect/organ_set_bonus/fish/proc/add_speed_buff(datum/source)
SIGNAL_HANDLER
RegisterSignal(owner, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(check_body_position))
check_body_position()
/datum/status_effect/organ_set_bonus/fish/proc/remove_speed_buff(datum/source)
SIGNAL_HANDLER
UnregisterSignal(owner, COMSIG_LIVING_SET_BODY_POSITION)
owner.remove_movespeed_modifier(/datum/movespeed_modifier/fish_flopping)
/datum/status_effect/organ_set_bonus/fish/proc/check_body_position(datum/source)
SIGNAL_HANDLER
if(owner.body_position == LYING_DOWN)
owner.add_movespeed_modifier(/datum/movespeed_modifier/fish_flopping)
else
owner.remove_movespeed_modifier(/datum/movespeed_modifier/fish_flopping)
///Tail for fish DNA-infused spacemen. It provides a speed buff while in water. It's also needed for the crawl speed bonus once the threshold is reached.
/obj/item/organ/external/tail/fish
name = "fish tail"
desc = "A severed tail from some sort of marine creature... or a fish-infused spaceman. It's smooth, faintly wet and definitely not flopping."
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "fish_tail"
greyscale_config = /datum/greyscale_config/fish_tail
greyscale_colors = FISH_ORGAN_COLOR
bodypart_overlay = /datum/bodypart_overlay/mutant/tail/fish
dna_block = DNA_FISH_TAIL_BLOCK
wag_flags = WAG_ABLE
organ_traits = list(TRAIT_FLOPPING)
/obj/item/organ/external/tail/fish/Initialize(mapload)
. = ..()
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/fish)
/obj/item/organ/external/tail/fish/on_mob_insert(mob/living/carbon/owner)
. = ..()
owner.AddElementTrait(TRAIT_WADDLING, type, /datum/element/waddling)
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(check_location))
check_location(owner, null)
/obj/item/organ/external/tail/fish/on_mob_remove(mob/living/carbon/owner)
. = ..()
owner.remove_traits(list(TRAIT_WADDLING, TRAIT_NO_STAGGER), type)
owner.remove_movespeed_modifier(/datum/movespeed_modifier/fish_on_water)
owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/fish_on_water)
UnregisterSignal(owner, COMSIG_MOVABLE_MOVED)
/obj/item/organ/external/tail/fish/get_greyscale_color_from_draw_color()
set_greyscale(bodypart_overlay.draw_color)
/obj/item/organ/external/tail/fish/proc/check_location(mob/living/carbon/source, atom/movable/old_loc, dir, forced)
SIGNAL_HANDLER
var/was_water = istype(old_loc, /turf/open/water)
var/is_water = istype(source.loc, /turf/open/water) && !HAS_TRAIT(source.loc, TRAIT_TURF_IGNORE_SLOWDOWN)
if(was_water && !is_water)
source.remove_movespeed_modifier(/datum/movespeed_modifier/fish_on_water)
source.remove_actionspeed_modifier(/datum/actionspeed_modifier/fish_on_water)
source.add_traits(list(TRAIT_OFF_BALANCE_TACKLER, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), type)
else if(!was_water && is_water)
source.add_movespeed_modifier(/datum/movespeed_modifier/fish_on_water)
source.add_actionspeed_modifier(/datum/actionspeed_modifier/fish_on_water)
source.add_traits(list(TRAIT_OFF_BALANCE_TACKLER, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), type)
/datum/bodypart_overlay/mutant/tail/fish
feature_key = "fish_tail"
color_source = ORGAN_COLOR_HAIR
/datum/bodypart_overlay/mutant/tail/fish/on_mob_insert(obj/item/organ/parent, mob/living/carbon/receiver)
//Initialize the related dna feature block if we don't have any so it doesn't error out.
//This isn't tied to any species, but I kinda want it to be mutable instead of having a fixed sprite accessory.
if(imprint_on_next_insertion && !receiver.dna.features["fish_tail"])
receiver.dna.features["fish_tail"] = pick(SSaccessories.tails_list_fish)
receiver.dna.update_uf_block(DNA_FISH_TAIL_BLOCK)
return ..()
/datum/bodypart_overlay/mutant/tail/fish/get_global_feature_list()
return SSaccessories.tails_list_fish
///Lungs that replace the need of oxygen with water vapor or being wet
/obj/item/organ/internal/lungs/fish
name = "mutated gills"
desc = "Fish DNA infused on what once was a normal pair of lungs that now require spacemen to breathe water vapor, or keep themselves covered in water."
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "gills"
safe_oxygen_min = 0 //We don't breathe this
///The required partial pressure of water_vapor for not drowing
var/safe_water_level = 29
/// Bodypart overlay applied to the chest where the lungs are in
var/datum/bodypart_overlay/simple/gills/gills
var/has_gills = TRUE
/obj/item/organ/internal/lungs/fish/Initialize(mapload)
. = ..()
add_gas_reaction(/datum/gas/water_vapor, always = PROC_REF(breathe_water))
respiration_type |= RESPIRATION_OXYGEN //after all, we get oxygen from water
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/fish)
if(has_gills)
gills = new()
AddElement(/datum/element/noticable_organ, "%PRONOUN_Theyve a set of gills on %PRONOUN_their neck.", BODY_ZONE_PRECISE_MOUTH)
AddComponent(/datum/component/bubble_icon_override, "fish", BUBBLE_ICON_PRIORITY_ORGAN)
/obj/item/organ/internal/lungs/fish/Destroy()
QDEL_NULL(gills)
return ..()
/obj/item/organ/internal/lungs/fish/on_bodypart_insert(obj/item/bodypart/limb)
. = ..()
if(gills)
limb.add_bodypart_overlay(gills)
/obj/item/organ/internal/lungs/fish/on_bodypart_remove(obj/item/bodypart/limb)
. = ..()
if(gills)
limb.remove_bodypart_overlay(gills)
/obj/item/organ/internal/lungs/fish/on_mob_remove(mob/living/carbon/owner)
. = ..()
owner.clear_alert(ALERT_NOT_ENOUGH_WATER)
/// Requires the spaceman to have either water vapor or be wet.
/obj/item/organ/internal/lungs/fish/proc/breathe_water(mob/living/carbon/breather, datum/gas_mixture/breath, water_pp, old_water_pp)
var/need_to_breathe = !HAS_TRAIT(src, TRAIT_SPACEBREATHING) && !HAS_TRAIT(breather, TRAIT_IS_WET)
if(water_pp < safe_water_level && need_to_breathe)
on_low_water(breather, breath, water_pp)
return
if(old_water_pp < safe_water_level || breather.failed_last_breath)
breather.failed_last_breath = FALSE
breather.clear_alert(ALERT_NOT_ENOUGH_WATER)
if(need_to_breathe)
breathe_gas_volume(breath, /datum/gas/water_vapor, /datum/gas/carbon_dioxide)
// Heal mob if not in crit.
if(breather.health >= breather.crit_threshold && breather.oxyloss)
breather.adjustOxyLoss(-5)
/// Called when there isn't enough water to breath
/obj/item/organ/internal/lungs/fish/proc/on_low_water(mob/living/carbon/breather, datum/gas_mixture/breath, water_pp)
breather.throw_alert(ALERT_NOT_ENOUGH_WATER, /atom/movable/screen/alert/not_enough_water)
var/gas_breathed = handle_suffocation(breather, water_pp, safe_water_level, breath.gases[/datum/gas/water_vapor][MOLES])
if(water_pp)
breathe_gas_volume(breath, /datum/gas/water_vapor, /datum/gas/carbon_dioxide, volume = gas_breathed)
// Simple overlay so we can add gills to those with fish lungs
/datum/bodypart_overlay/simple/gills
icon = 'icons/mob/human/fish_features.dmi'
icon_state = "gills"
layers = EXTERNAL_ADJACENT
/datum/bodypart_overlay/simple/gills/get_image(image_layer, obj/item/bodypart/limb)
return image(
icon = icon,
icon_state = "[icon_state]_[mutant_bodyparts_layertext(image_layer)]",
layer = image_layer,
)
/// Subtype of gills that allow the mob to optionally breathe water.
/obj/item/organ/internal/lungs/fish/amphibious
name = "mutated semi-aquatic lungs"
desc = "DNA from an amphibious or semi-aquatic creature infused on a pair lungs. Enjoy breathing underwater without drowning outside water."
safe_oxygen_min = /obj/item/organ/internal/lungs::safe_oxygen_min
safe_water_level = 19
has_gills = FALSE
/**
* If false, we don't breathe air since we've got water instead.
* Set to FALSE at the start of each cycle and TRUE on on_low_water()
*/
var/should_breathe_oxygen = FALSE
/obj/item/organ/internal/lungs/fish/amphibious/Initialize(mapload)
. = ..()
/**
* We're setting the gas reaction for breathing oxygen here,
* since gas reation procs are run in the order they're added,
* and we want breathe_water() to run before breathe_oxygen,
* so that if we're breathing water vapor (or are wet), we won't have to breathe oxygen.
*/
safe_oxygen_min = /obj/item/organ/internal/lungs::safe_oxygen_min
add_gas_reaction(/datum/gas/oxygen, always = PROC_REF(breathe_oxygen))
/obj/item/organ/internal/lungs/fish/amphibious/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/breather)
should_breathe_oxygen = FALSE //assume we don't have to breathe oxygen until we fail to breathe water
return ..()
/obj/item/organ/internal/lungs/fish/amphibious/on_low_water(mob/living/carbon/breather, datum/gas_mixture/breath, water_pp)
should_breathe_oxygen = TRUE
return
/obj/item/organ/internal/lungs/fish/amphibious/breathe_oxygen(mob/living/carbon/breather, datum/gas_mixture/breath, o2_pp, old_o2_pp)
if(!should_breathe_oxygen)
if(breather.failed_last_breath) //in case we had neither oxygen nor water last tick.
breather.clear_alert(ALERT_NOT_ENOUGH_OXYGEN)
return
return ..()
///Fish infuser organ, allows mobs to safely eat raw fish.
/obj/item/organ/internal/stomach/fish
name = "mutated fish-stomach"
desc = "Fish DNA infused into a stomach now parmated by the faint smell of salt and slightly putrified fish."
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "stomach"
greyscale_config = /datum/greyscale_config/mutant_organ
greyscale_colors = FISH_COLORS
organ_traits = list(TRAIT_STRONG_STOMACH, TRAIT_FISH_EATER)
disgust_metabolism = 2.5
/obj/item/organ/internal/stomach/fish/Initialize(mapload)
. = ..()
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/fish)
///Organ from fish with the ink production trait. Doesn't count toward the organ set bonus but is buffed once it's active.
/obj/item/organ/internal/tongue/inky
name = "ink-secreting tongue"
desc = "A black tongue linked to two swollen black sacs underneath the palate."
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "inky_tongue"
actions_types = list(/datum/action/cooldown/ink_spit)
/obj/item/organ/internal/tongue/inky/Initialize(mapload)
. = ..()
AddElement(/datum/element/noticable_organ, "Slick black ink seldom rivulets from %PRONOUN_their mouth.", BODY_ZONE_PRECISE_MOUTH)
///Organ from fish with the toxic trait. Allows the user to use tetrodotoxin as a healing chem instead of a toxin.
/obj/item/organ/internal/liver/fish
name = "mutated fish-liver"
desc = "Fish DNA infused into a stomach that now uses tetrodotoxin as regenerative material. It also processes alcohol quite well."
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "liver"
greyscale_config = /datum/greyscale_config/mutant_organ
greyscale_colors = FISH_COLORS
organ_traits = list(TRAIT_TETRODOTOXIN_HEALING, TRAIT_ALCOHOL_TOLERANCE) //drink like a fish :^)
liver_resistance = parent_type::liver_resistance * 1.5
food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/iron = 5, /datum/reagent/toxin/tetrodotoxin = 5)
grind_results = list(/datum/reagent/consumable/nutriment/peptides = 5, /datum/reagent/toxin/tetrodotoxin = 5)
/obj/item/organ/internal/liver/fish/Initialize(mapload)
. = ..()
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/fish)
#undef FISH_ORGAN_COLOR
#undef FISH_SCLERA_COLOR
#undef FISH_PUPIL_COLOR
#undef FISH_COLORS

View File

@@ -402,7 +402,12 @@
duration = 6
/obj/effect/temp_visual/impact_effect/neurotoxin
icon_state = "impact_neurotoxin"
icon_state = "impact_spit"
color = "#5BDD04"
/obj/effect/temp_visual/impact_effect/ink_spit
icon_state = "impact_spit"
color = COLOR_NEARLY_ALL_BLACK
/obj/effect/temp_visual/heart
name = "heart"

View File

@@ -0,0 +1,3 @@
///speed bonus given by the fish tail organ when inside water.
/datum/actionspeed_modifier/fish_on_water
multiplicative_slowdown = -0.15

View File

@@ -1,32 +1,32 @@
/datum/preference/choiced/tail_human
savefile_key = "feature_human_tail"
/datum/preference/choiced/tail_felinid
savefile_key = "feature_human_tail" //savefile keys cannot be changed, blame whoever named them this way.
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
can_randomize = FALSE
relevant_external_organ = /obj/item/organ/external/tail/cat
/datum/preference/choiced/tail_human/init_possible_values()
return assoc_to_keys_features(SSaccessories.tails_list_human)
/datum/preference/choiced/tail_felinid/init_possible_values()
return assoc_to_keys_features(SSaccessories.tails_list_felinid)
/datum/preference/choiced/tail_human/apply_to_human(mob/living/carbon/human/target, value)
/datum/preference/choiced/tail_felinid/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["tail_cat"] = value
/datum/preference/choiced/tail_human/create_default_value()
var/datum/sprite_accessory/tails/human/cat/tail = /datum/sprite_accessory/tails/human/cat
/datum/preference/choiced/tail_felinid/create_default_value()
var/datum/sprite_accessory/tails/felinid/cat/tail = /datum/sprite_accessory/tails/felinid/cat
return initial(tail.name)
/datum/preference/choiced/ears
savefile_key = "feature_human_ears"
/datum/preference/choiced/felinid_ears
savefile_key = "feature_human_ears" //savefile keys cannot be changed, blame whoever named them this way.
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
can_randomize = FALSE
relevant_external_organ = /obj/item/organ/internal/ears/cat
/datum/preference/choiced/ears/init_possible_values()
/datum/preference/choiced/felinid_ears/init_possible_values()
return assoc_to_keys_features(SSaccessories.ears_list)
/datum/preference/choiced/ears/apply_to_human(mob/living/carbon/human/target, value)
/datum/preference/choiced/felinid_ears/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["ears"] = value
/datum/preference/choiced/ears/create_default_value()
/datum/preference/choiced/felinid_ears/create_default_value()
return /datum/sprite_accessory/ears/cat::name

View File

@@ -4,6 +4,10 @@
desc = "Fills you with the conviction of JUSTICE. Lawyers tend to want to show it to everyone they meet."
icon_state = "lawyerbadge"
/obj/item/clothing/accessory/lawyers_badge/Initialize(mapload)
. = ..()
AddComponent(/datum/component/bubble_icon_override, "lawyer", BUBBLE_ICON_PRIORITY_ACCESSORY)
/obj/item/clothing/accessory/lawyers_badge/interact(mob/user)
. = ..()
if(prob(1))
@@ -12,11 +16,9 @@
/obj/item/clothing/accessory/lawyers_badge/accessory_equipped(obj/item/clothing/under/clothes, mob/living/user)
RegisterSignal(user, COMSIG_LIVING_SLAM_TABLE, PROC_REF(table_slam))
user.bubble_icon = "lawyer"
/obj/item/clothing/accessory/lawyers_badge/accessory_dropped(obj/item/clothing/under/clothes, mob/living/user)
UnregisterSignal(user, COMSIG_LIVING_SLAM_TABLE)
user.bubble_icon = initial(user.bubble_icon)
/obj/item/clothing/accessory/lawyers_badge/proc/table_slam(mob/living/source, obj/structure/table/the_table)
SIGNAL_HANDLER

View File

@@ -1308,6 +1308,23 @@
. = ..()
aquarium_vc_color = color || initial(aquarium_vc_color)
/obj/item/fish/get_infusion_entry()
var/amphibious = required_fluid_type == AQUARIUM_FLUID_AIR || HAS_TRAIT(src, TRAIT_FISH_AMPHIBIOUS)
var/list/possible_infusions = list(/datum/infuser_entry/fish)
for(var/type in fish_traits)
var/datum/fish_trait/trait = GLOB.fish_traits[type]
if(!trait.infusion_entry)
continue
possible_infusions |= trait.infusion_entry
if(!length(possible_infusions) && !amphibious)
return GLOB.infuser_entries[/datum/infuser_entry/fish]
var/datum/infuser_entry/fish/entry = new
if(amphibious)
entry.output_organs -= /obj/item/organ/internal/lungs/fish
for(var/key in possible_infusions)
var/datum/infuser_entry/infusion = GLOB.infuser_entries[key]
entry.output_organs |= infusion.output_organs
/// Returns random fish, using random_case_rarity probabilities.
/proc/random_fish_type(required_fluid)
var/static/probability_table

View File

@@ -39,6 +39,8 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
var/added_difficulty = 0
/// Reagents to add to the fish whenever the COMSIG_GENERATE_REAGENTS_TO_ADD signal is sent. Their values will be multiplied later.
var/list/reagents_to_add
/// If set, the fish may return this infusion entry when get_infusion_entry is called instead of /datum/infuser_entry/fish
var/infusion_entry
/// Difficulty modifier from this mod, needs to return a list with two values
/datum/fish_trait/proc/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman)
@@ -427,6 +429,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "This fish contains toxins. Feeding it to predatory fishes or people is not recommended."
diff_traits_inheritability = 25
reagents_to_add = list(/datum/reagent/toxin/tetrodotoxin = 1)
infusion_entry = /datum/infuser_entry/ttx_healing
///The amount of venom injected if the fish has a stinger is multiplied by this value.
var/venom_mult = 1
@@ -472,6 +475,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "This fish contains carpotoxin. Definitely not safe for consumption."
diff_traits_inheritability = 50
reagents_to_add = list(/datum/reagent/toxin/carpotoxin = 4)
infusion_entry = null
venom_mult = 6
/datum/fish_trait/toxin_immunity
@@ -543,6 +547,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
inheritability = 80
diff_traits_inheritability = 40
catalog_description = "This fish has developed a primitive adaptation to life on both land and water."
infusion_entry = /datum/infuser_entry/amphibious
/datum/fish_trait/amphibious/apply_to_fish(obj/item/fish/fish)
. = ..()
@@ -721,6 +726,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "This fish possess a sac that produces ink."
diff_traits_inheritability = 70
spontaneous_manifest_types = list(/obj/item/fish/squid = 35)
infusion_entry = /datum/infuser_entry/squid
/datum/fish_trait/ink/apply_to_fish(obj/item/fish/fish)
. = ..()

View File

@@ -69,6 +69,11 @@
update_appearance()
//Bane effect that make it extra-effective against mobs with water adaptation (read: fish infusion)
AddElement(/datum/element/bane, target_type = /mob/living, damage_multiplier = 1.25)
RegisterSignal(src, COMSIG_OBJECT_PRE_BANING, PROC_REF(attempt_bane))
RegisterSignal(src, COMSIG_OBJECT_ON_BANING, PROC_REF(bane_effects))
/obj/item/fishing_rod/add_context(atom/source, list/context, obj/item/held_item, mob/user)
if(src == held_item)
if(currently_hooked)
@@ -135,6 +140,19 @@
QDEL_NULL(bait)
update_icon()
///Fishing rodss should only bane fish DNA-infused spessman
/obj/item/fishing_rod/proc/attempt_bane(datum/source, mob/living/fish)
SIGNAL_HANDLER
if(!force || !HAS_TRAIT(fish, TRAIT_WATER_ADAPTATION))
return COMPONENT_CANCEL_BANING
///Fishing rods should hard-counter fish DNA-infused spessman
/obj/item/fishing_rod/proc/bane_effects(datum/source, mob/living/fish)
SIGNAL_HANDLER
fish.adjust_staggered_up_to(STAGGERED_SLOWDOWN_LENGTH, 4 SECONDS)
fish.adjust_confusion_up_to(1.5 SECONDS, 3 SECONDS)
fish.adjust_wet_stacks(-4)
/obj/item/fishing_rod/interact(mob/user)
if(currently_hooked)
reel(user)

View File

@@ -37,7 +37,6 @@
return ..()
/datum/hallucination/fire/start()
hallucinator.set_fire_stacks(max(hallucinator.fire_stacks, 0.1)) //Placebo flammability
fire_overlay = image(fire_icon, hallucinator, fire_icon_state, ABOVE_MOB_LAYER)
hallucinator.client?.images |= fire_overlay
to_chat(hallucinator, span_userdanger("You're set on fire!"))
@@ -47,7 +46,6 @@
return TRUE
/datum/hallucination/fire/Destroy()
hallucinator.adjust_fire_stacks(-0.1)
hallucinator.clear_alert(ALERT_FIRE, clear_override = TRUE)
hallucinator.clear_alert(ALERT_TEMPERATURE, clear_override = TRUE)
if(fire_overlay)

View File

@@ -2002,7 +2002,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
*
* Returns a color string or null.
*/
/datum/species/proc/get_fixed_hair_color(mob/living/carbon/human/for_mob)
/datum/species/proc/get_fixed_hair_color(mob/living/carbon/for_mob)
ASSERT(!isnull(for_mob))
switch(hair_color_mode)
if(USE_MUTANT_COLOR)

View File

@@ -115,7 +115,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
target.dna.features["moth_wings"] = get_consistent_feature_entry(SSaccessories.moth_wings_list)
target.dna.features["snout"] = get_consistent_feature_entry(SSaccessories.snouts_list)
target.dna.features["spines"] = get_consistent_feature_entry(SSaccessories.spines_list)
target.dna.features["tail_cat"] = get_consistent_feature_entry(SSaccessories.tails_list_human) // it's a lie
target.dna.features["tail_cat"] = get_consistent_feature_entry(SSaccessories.tails_list_felinid) // it's a lie
target.dna.features["tail_lizard"] = get_consistent_feature_entry(SSaccessories.tails_list_lizard)
target.dna.features["tail_monkey"] = get_consistent_feature_entry(SSaccessories.tails_list_monkey)
target.dna.features["pod_hair"] = get_consistent_feature_entry(SSaccessories.pod_hair_list)

View File

@@ -13,6 +13,7 @@
TRAIT_CATLIKE_GRACE,
TRAIT_HATED_BY_DOGS,
TRAIT_USES_SKINTONES,
TRAIT_WATER_HATER,
)
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | RACE_SWAP | ERT_SPAWN | SLIME_EXTRACT
species_language_holder = /datum/language_holder/felinid

View File

@@ -1204,8 +1204,11 @@
var/altered_grab_state = pulledby.grab_state
if((body_position == LYING_DOWN || HAS_TRAIT(src, TRAIT_GRABWEAKNESS) || get_timed_status_effect_duration(/datum/status_effect/staggered)) && pulledby.grab_state < GRAB_KILL) //If prone, resisting out of a grab is equivalent to 1 grab state higher. won't make the grab state exceed the normal max, however
altered_grab_state++
var/resist_chance = BASE_GRAB_RESIST_CHANCE /// see defines/combat.dm, this should be baseline 60%
resist_chance = (resist_chance/altered_grab_state) ///Resist chance divided by the value imparted by your grab state. It isn't until you reach neckgrab that you gain a penalty to escaping a grab.
if(HAS_TRAIT(src, TRAIT_GRABRESISTANCE))
altered_grab_state--
// see defines/combat.dm, this should be baseline 60%
// Resist chance divided by the value imparted by your grab state. It isn't until you reach neckgrab that you gain a penalty to escaping a grab.
var/resist_chance = altered_grab_state ? (BASE_GRAB_RESIST_CHANCE / altered_grab_state) : 100
if(prob(resist_chance))
visible_message(span_danger("[src] breaks free of [pulledby]'s grip!"), \
span_danger("You break free of [pulledby]'s grip!"), null, null, pulledby)

View File

@@ -175,3 +175,17 @@
/datum/movespeed_modifier/magic_ties
multiplicative_slowdown = 0.5
///movespeed modifier that makes you go faster when wet and lying on the floor once past the fish organ set threshold.
/datum/movespeed_modifier/fish_flopping
blacklisted_movetypes = MOVETYPES_NOT_TOUCHING_GROUND
multiplicative_slowdown = - (CRAWLING_ADD_SLOWDOWN * 0.71)
///speed bonus given by the fish tail organ when inside water.
/datum/movespeed_modifier/fish_on_water
blacklisted_movetypes = MOVETYPES_NOT_TOUCHING_GROUND
multiplicative_slowdown = - /turf/open/water::slowdown
///speed malus given by the fish organ set when dry
/datum/movespeed_modifier/fish_waterless
multiplicative_slowdown = 0.36

View File

@@ -1,17 +0,0 @@
/obj/projectile/neurotoxin
name = "neurotoxin spit"
icon_state = "neurotoxin"
damage = 65
damage_type = STAMINA
armor_flag = BIO
impact_effect_type = /obj/effect/temp_visual/impact_effect/neurotoxin
armour_penetration = 50
/obj/projectile/neurotoxin/on_hit(atom/target, blocked = 0, pierce_hit)
if(isalien(target))
damage = 0
return ..()
/obj/projectile/neurotoxin/damaging //for ai controlled aliums
damage = 30
paralyze = 0 SECONDS

View File

@@ -0,0 +1,58 @@
/obj/projectile/neurotoxin
name = "neurotoxin spit"
icon_state = "neurotoxin"
damage = 65
damage_type = STAMINA
armor_flag = BIO
impact_effect_type = /obj/effect/temp_visual/impact_effect/neurotoxin
armour_penetration = 50
/obj/projectile/neurotoxin/on_hit(atom/target, blocked = 0, pierce_hit)
if(isalien(target))
damage = 0
return ..()
/obj/projectile/neurotoxin/damaging //for ai controlled aliums
damage = 30
paralyze = 0 SECONDS
/obj/projectile/ink_spit
name = "ink spit"
icon_state = "ink_spit"
damage = 5
damage_type = STAMINA
armor_flag = BIO
impact_effect_type = /obj/effect/temp_visual/impact_effect/ink_spit
armour_penetration = 50
hitsound = SFX_DESECRATION
hitsound_wall = SFX_DESECRATION
/obj/projectile/ink_spit/Initialize(mapload)
. = ..()
if(isliving(firer))
var/mob/living/living = firer
var/datum/status_effect/organ_set_bonus/fish/bonus = living?.has_status_effect(/datum/status_effect/organ_set_bonus/fish)
if(bonus?.bonus_active)
damage = 12
armour_penetration = 65
AddComponent(/datum/component/splat, \
memory_type = /datum/memory/witnessed_inking, \
smudge_type = /obj/effect/decal/cleanable/food/squid_ink, \
moodlet_type = /datum/mood_event/inked, \
splat_color = COLOR_NEARLY_ALL_BLACK, \
hit_callback = CALLBACK(src, PROC_REF(blind_em)), \
)
/obj/projectile/ink_spit/proc/blind_em(mob/living/victim, can_splat_on)
if(!can_splat_on)
return
var/powered_up = FALSE
if(isliving(firer))
var/mob/living/living = firer
var/datum/status_effect/organ_set_bonus/fish/bonus = living?.has_status_effect(/datum/status_effect/organ_set_bonus/fish)
powered_up = bonus?.bonus_active
victim.adjust_temp_blindness_up_to((powered_up ? 6.5 : 4.5) SECONDS, 10 SECONDS)
victim.adjust_confusion_up_to((powered_up ? 3 : 1.5) SECONDS, 6 SECONDS)
if(powered_up)
victim.Knockdown(2 SECONDS) //splat!

View File

@@ -40,7 +40,7 @@
if(belly)
amount += belly.reagents.get_reagent_amount(toxin.type)
if(amount <= liver_tolerance)
if(amount <= liver_tolerance * toxin.liver_tolerance_multiplier)
owner.reagents.remove_reagent(toxin.type, toxin.metabolization_rate * owner.metabolism_efficiency * seconds_per_tick)
continue

View File

@@ -78,6 +78,8 @@
var/list/metabolized_traits
/// A list of traits to apply while the reagent is in a mob.
var/list/added_traits
/// Multiplier of the amount purged by reagents such as calomel, multiver, syniver etc.
var/purge_multiplier = 1
///The default reagent container for the reagent, used for icon generation
var/obj/item/reagent_containers/default_container = /obj/item/reagent_containers/cup/bottle

View File

@@ -388,16 +388,13 @@
var/need_mob_update
need_mob_update = affected_mob.adjustToxLoss(-0.5 * min(medibonus, 3 * normalise_creation_purity()) * REM * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype) //not great at healing but if you have nothing else it will work
need_mob_update += affected_mob.adjustOrganLoss(ORGAN_SLOT_LUNGS, 0.5 * REM * seconds_per_tick, required_organ_flag = affected_organ_flags) //kills at 40u
for(var/r2 in affected_mob.reagents.reagent_list)
var/datum/reagent/the_reagent2 = r2
if(the_reagent2 == src)
continue
var/amount2purge = 3
if(holder.has_reagent(/datum/reagent/toxin/anacea))
amount2purge = 0
if(medibonus >= 3 && istype(the_reagent2, /datum/reagent/medicine)) //3 unique meds (2+multiver) | (1 + pure multiver) will make it not purge medicines
continue
affected_mob.reagents.remove_reagent(the_reagent2.type, amount2purge * REM * seconds_per_tick)
if(!holder.has_reagent(/datum/reagent/toxin/anacea))
for(var/datum/reagent/second_reagent as anything in affected_mob.reagents.reagent_list)
if(second_reagent == src)
continue
if(medibonus >= 3 && istype(second_reagent, /datum/reagent/medicine)) //3 unique meds (2+multiver) | (1 + pure multiver) will make it not purge medicines
continue
affected_mob.reagents.remove_reagent(second_reagent.type, 3 * second_reagent.purge_multiplier * REM * seconds_per_tick)
if(need_mob_update)
return UPDATE_MOB_HEALTH
@@ -468,10 +465,10 @@
var/need_mob_update
need_mob_update = affected_mob.adjustOrganLoss(ORGAN_SLOT_LIVER, 0.1 * REM * seconds_per_tick, required_organ_flag = affected_organ_flags)
need_mob_update += affected_mob.adjustToxLoss(-1.5 * REM * seconds_per_tick * normalise_creation_purity(), updating_health = FALSE, required_biotype = affected_biotype)
for(var/datum/reagent/R in affected_mob.reagents.reagent_list)
if(issyrinormusc(R))
for(var/datum/reagent/reagent as anything in affected_mob.reagents.reagent_list)
if(issyrinormusc(reagent))
continue
affected_mob.reagents.remove_reagent(R.type, 0.2 * REM * seconds_per_tick)
affected_mob.reagents.remove_reagent(reagent.type, 0.2 * reagent.purge_multiplier * REM * seconds_per_tick)
if(need_mob_update)
return UPDATE_MOB_HEALTH

View File

@@ -432,18 +432,17 @@
/datum/reagent/medicine/calomel/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
. = ..()
for(var/datum/reagent/target_reagent in affected_mob.reagents.reagent_list)
for(var/datum/reagent/target_reagent as anything in affected_mob.reagents.reagent_list)
if(istype(target_reagent, /datum/reagent/medicine/calomel))
continue
affected_mob.reagents.remove_reagent(target_reagent.type, 3 * REM * seconds_per_tick)
affected_mob.reagents.remove_reagent(target_reagent.type, 3 * target_reagent.purge_multiplier * REM * seconds_per_tick)
var/toxin_amount = round(affected_mob.health / 40, 0.1)
if(affected_mob.adjustToxLoss(toxin_amount * REM * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype))
return UPDATE_MOB_HEALTH
/datum/reagent/medicine/calomel/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired)
. = ..()
for(var/datum/reagent/medicine/calomel/target_reagent in affected_mob.reagents.reagent_list)
affected_mob.reagents.remove_reagent(target_reagent.type, 2 * REM * seconds_per_tick)
affected_mob.reagents.remove_reagent(type, 2 * REM * seconds_per_tick)
if(affected_mob.adjustToxLoss(2.5 * REM * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype))
return UPDATE_MOB_HEALTH
@@ -467,7 +466,7 @@
var/toxin_chem_amount = 0
for(var/datum/reagent/toxin/target_reagent in affected_mob.reagents.reagent_list)
toxin_chem_amount += 1
affected_mob.reagents.remove_reagent(target_reagent.type, 5 * REM * seconds_per_tick)
affected_mob.reagents.remove_reagent(target_reagent.type, 5 * target_reagent.purge_multiplier * REM * seconds_per_tick)
var/toxin_amount = round(affected_mob.getBruteLoss() / 15, 0.1) + round(affected_mob.getFireLoss() / 30, 0.1) - 3
if(affected_mob.adjustToxLoss(toxin_amount * REM * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype))
. = UPDATE_MOB_HEALTH
@@ -512,9 +511,9 @@
. = ..()
if(affected_mob.adjustToxLoss(-2 * REM * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype))
. = UPDATE_MOB_HEALTH
for(var/datum/reagent/R in affected_mob.reagents.reagent_list)
if(R != src)
affected_mob.reagents.remove_reagent(R.type, 2 * REM * seconds_per_tick)
for(var/datum/reagent/reagent as anything in affected_mob.reagents.reagent_list)
if(reagent != src)
affected_mob.reagents.remove_reagent(reagent.type, 2 * reagent.purge_multiplier * REM * seconds_per_tick)
/datum/reagent/medicine/sal_acid
name = "Salicylic Acid"
@@ -1339,8 +1338,8 @@
/datum/reagent/medicine/haloperidol/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
. = ..()
for(var/datum/reagent/drug/R in affected_mob.reagents.reagent_list)
affected_mob.reagents.remove_reagent(R.type, 5 * REM * seconds_per_tick)
for(var/datum/reagent/drug/reagent in affected_mob.reagents.reagent_list)
affected_mob.reagents.remove_reagent(reagent.type, 5 * reagent.purge_multiplier * REM * seconds_per_tick)
affected_mob.adjust_drowsiness(4 SECONDS * REM * seconds_per_tick)
if(affected_mob.get_timed_status_effect_duration(/datum/status_effect/jitter) >= 6 SECONDS)

View File

@@ -277,7 +277,7 @@
if(methods & VAPOR)
exposed_mob.adjust_wet_stacks(reac_volume * WATER_TO_WET_STACKS_FACTOR_VAPOR) // Spraying someone with water with the hope to put them out is just simply too funny to me not to add it.
if(!isfelinid(exposed_mob))
if(!HAS_TRAIT(exposed_mob, TRAIT_WATER_HATER) || HAS_TRAIT(exposed_mob, TRAIT_WATER_ADAPTATION))
return
exposed_mob.incapacitate(1) // startles the felinid, canceling any do_after
@@ -290,9 +290,18 @@
/datum/reagent/water/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
. = ..()
var/water_adaptation = HAS_TRAIT(affected_mob, TRAIT_WATER_ADAPTATION)
if(affected_mob.blood_volume)
affected_mob.blood_volume += 0.1 * REM * seconds_per_tick // water is good for you!
affected_mob.adjust_drunk_effect(-0.25 * REM * seconds_per_tick) // and even sobers you up slowly!!
var/blood_restored = water_adaptation ? 0.3 : 0.1
affected_mob.blood_volume += blood_restored * REM * seconds_per_tick // water is good for you!
var/drunkness_restored = water_adaptation ? -0.5 : -0.25
affected_mob.adjust_drunk_effect(drunkness_restored * REM * seconds_per_tick) // and even sobers you up slowly!!
if(water_adaptation)
var/need_mob_update = FALSE
need_mob_update = affected_mob.adjustToxLoss(-0.2 * REM * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype)
need_mob_update += affected_mob.adjustFireLoss(-0.2 * REM * seconds_per_tick, updating_health = FALSE, required_bodytype = affected_bodytype)
need_mob_update += affected_mob.adjustBruteLoss(-0.2 * REM * seconds_per_tick, updating_health = FALSE, required_bodytype = affected_bodytype)
return need_mob_update ? UPDATE_MOB_HEALTH : .
// For weird backwards situations where water manages to get added to trays nutrients, as opposed to being snowflaked away like usual.
/datum/reagent/water/on_hydroponics_apply(obj/machinery/hydroponics/mytray, mob/user)

View File

@@ -12,6 +12,8 @@
var/toxpwr = 1.5
///The amount to multiply the liver damage this toxin does by (Handled solely in liver code)
var/liver_damage_multiplier = 1
///The multiplier of the liver toxin tolerance, below which any amount toxin will be simply metabolized out with no effect.
var/liver_tolerance_multiplier = 1
///won't produce a pain message when processed by liver/life() if there isn't another non-silent toxin present if true
var/silent_toxin = FALSE
///The afflicted must be above this health value in order for the toxin to deal damage
@@ -956,9 +958,9 @@
if(prob(50))
constructed_flags |= MOB_VOMIT_STUN
affected_mob.vomit(vomit_flags = constructed_flags, distance = rand(0,4))
for(var/datum/reagent/toxin/R in affected_mob.reagents.reagent_list)
if(R != src)
affected_mob.reagents.remove_reagent(R.type, 1)
for(var/datum/reagent/toxin/reagent in affected_mob.reagents.reagent_list)
if(reagent != src)
affected_mob.reagents.remove_reagent(reagent.type, 1 * reagent.purge_multiplier * REM * seconds_per_tick)
/datum/reagent/toxin/spewium/overdose_process(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
. = ..()
@@ -1051,8 +1053,8 @@
if(holder.has_reagent(/datum/reagent/medicine/calomel) || holder.has_reagent(/datum/reagent/medicine/pen_acid))
remove_amt = 0.5
. = ..()
for(var/datum/reagent/medicine/R in affected_mob.reagents.reagent_list)
affected_mob.reagents.remove_reagent(R.type, remove_amt * REM * normalise_creation_purity() * seconds_per_tick)
for(var/datum/reagent/medicine/reagent in affected_mob.reagents.reagent_list)
affected_mob.reagents.remove_reagent(reagent.type, remove_amt * reagent.purge_multiplier * REM * normalise_creation_purity() * seconds_per_tick)
//ACID
@@ -1293,6 +1295,9 @@
reagent_state = SOLID
color = COLOR_VERY_LIGHT_GRAY
metabolization_rate = 0.1 * REAGENTS_METABOLISM
liver_tolerance_multiplier = 0.1
liver_damage_multiplier = 1.25
purge_multiplier = 0.15
toxpwr = 0
taste_mult = 0
chemical_flags = REAGENT_NO_RANDOM_RECIPE|REAGENT_CAN_BE_SYNTHESIZED
@@ -1305,9 +1310,22 @@
/datum/reagent/toxin/tetrodotoxin/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
. = ..()
var/need_mob_update
if(HAS_TRAIT(affected_mob, TRAIT_TETRODOTOXIN_HEALING))
toxpwr = 0
liver_tolerance_multiplier = 0
silent_toxin = TRUE
remove_paralysis()
need_mob_update += affected_mob.adjustOxyLoss(-0.7 * REM * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype, required_respiration_type = affected_respiration_type)
need_mob_update = affected_mob.adjustToxLoss(-0.75 * REM * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype)
need_mob_update += affected_mob.adjustBruteLoss(-1.2 * REM * seconds_per_tick, updating_health = FALSE, required_bodytype = affected_bodytype)
need_mob_update += affected_mob.adjustFireLoss(-1.35 * REM * seconds_per_tick, updating_health = FALSE, required_bodytype = affected_bodytype)
return need_mob_update ? UPDATE_MOB_HEALTH : .
liver_tolerance_multiplier = initial(liver_tolerance_multiplier)
//be ready for a cocktail of symptoms, including:
//numbness, nausea, vomit, breath loss, weakness, paralysis and nerve damage/impairment and eventually a heart attack if enough time passes.
var/need_mob_update
switch(current_cycle)
if(7 to 13)
if(SPT_PROB(20, seconds_per_tick))
@@ -1338,6 +1356,7 @@
if(21 to 29)
toxpwr = 1
need_mob_update = affected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.5)
need_mob_update = affected_mob.adjustOrganLoss(ORGAN_SLOT_LUNGS, 0.7)
if(SPT_PROB(40, seconds_per_tick))
affected_mob.losebreath += 2 * REM * seconds_per_tick
need_mob_update = TRUE
@@ -1354,13 +1373,14 @@
if(29 to INFINITY)
toxpwr = 1.5
need_mob_update = affected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1, BRAIN_DAMAGE_DEATH)
need_mob_update = affected_mob.adjustOrganLoss(ORGAN_SLOT_LUNGS, 1.4)
affected_mob.set_silence_if_lower(3 SECONDS * REM * seconds_per_tick)
need_mob_update += affected_mob.adjustStaminaLoss(5 * REM * seconds_per_tick, updating_stamina = FALSE)
affected_mob.adjust_disgust(2 * REM * seconds_per_tick)
if(SPT_PROB(15, seconds_per_tick))
paralyze_limb(affected_mob)
need_mob_update = TRUE
if(SPT_PROB(10, seconds_per_tick))
if(SPT_PROB(20, seconds_per_tick))
affected_mob.adjust_confusion(rand(6 SECONDS, 8 SECONDS))
if(current_cycle > 38 && !length(traits_not_applied) && SPT_PROB(5, seconds_per_tick) && !affected_mob.undergoing_cardiac_arrest())
@@ -1376,6 +1396,11 @@
var/added_trait = pick_n_take(traits_not_applied)
ADD_TRAIT(affected_mob, added_trait, REF(src))
/datum/reagent/toxin/tetrodotoxin/on_mob_add(mob/living/affected_mob)
. = ..()
if(HAS_TRAIT(affected_mob, TRAIT_TETRODOTOXIN_HEALING))
liver_tolerance_multiplier = 0
/datum/reagent/toxin/tetrodotoxin/on_mob_metabolize(mob/living/affected_mob)
. = ..()
RegisterSignal(affected_mob, COMSIG_CARBON_ATTEMPT_BREATHE, PROC_REF(block_breath))
@@ -1383,6 +1408,9 @@
/datum/reagent/toxin/tetrodotoxin/on_mob_end_metabolize(mob/living/affected_mob)
. = ..()
UnregisterSignal(affected_mob, COMSIG_CARBON_ATTEMPT_BREATHE, PROC_REF(block_breath))
remove_paralysis(affected_mob)
/datum/reagent/toxin/tetrodotoxin/proc/remove_paralysis(mob/living/affected_mob)
// the initial() proc doesn't work for lists.
var/list/initial_list = list(
TRAIT_PARALYSIS_L_ARM = BODY_ZONE_L_ARM,
@@ -1395,5 +1423,5 @@
/datum/reagent/toxin/tetrodotoxin/proc/block_breath(mob/living/source)
SIGNAL_HANDLER
if(current_cycle > 28)
if(current_cycle > 28 && !HAS_TRAIT(source, TRAIT_TETRODOTOXIN_HEALING))
return COMSIG_CARBON_BLOCK_BREATH

View File

@@ -158,9 +158,6 @@
wag_flags = WAG_ABLE
/datum/bodypart_overlay/mutant/tail/get_global_feature_list()
return SSaccessories.tails_list_human
/obj/item/organ/external/tail/cat/get_butt_sprite()
return icon('icons/mob/butts.dmi', BUTT_SPRITE_CAT)
@@ -169,6 +166,9 @@
feature_key = "tail_cat"
color_source = ORGAN_COLOR_HAIR
/datum/bodypart_overlay/mutant/tail/cat/get_global_feature_list()
return SSaccessories.tails_list_felinid
/obj/item/organ/external/tail/monkey
name = "monkey tail"
preference = "feature_monkey_tail"

View File

@@ -240,6 +240,15 @@
color = bodypart_overlay.draw_color // so a pink felinid doesn't drop a gray tail
if(greyscale_config)
get_greyscale_color_from_draw_color()
else
color = bodypart_overlay.draw_color // so a pink felinid doesn't drop a gray tail
///Here we define how draw_color from the bodypart overlay sets the greyscale colors of organs that use GAGS
/obj/item/organ/proc/get_greyscale_color_from_draw_color()
color = bodypart_overlay.draw_color //Defaults to the legacy behaviour of applying the color to the item.
/// In space station videogame, nothing is sacred. If somehow an organ is removed unexpectedly, handle it properly
/obj/item/organ/proc/forced_removal()
SIGNAL_HANDLER

View File

@@ -34,7 +34,9 @@
inserted_organs += organ
// Search for added Status Effect.
var/datum/status_effect/organ_set_bonus/added_status = locate(/datum/status_effect/organ_set_bonus) in lab_rat.status_effects
var/datum/status_effect/organ_set_bonus/added_status
if(!infuser_entry.unreachable_effect)
added_status = locate(/datum/status_effect/organ_set_bonus) in lab_rat.status_effects
// If threshold_desc is filled-in, it implies the organ_set_bonus Status Effect should be activated.
// Without it, we'll assume there isn't a Status Effect to look for.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 KiB

After

Width:  |  Height:  |  Size: 901 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

@@ -35,6 +35,7 @@ Some species of fish can be bred into new species under the right conditions.
Most fish don't survive outside water, so get them somewhere safe like an aquarium or a fish case, or even a toilet or a moisture trap!
No matter how you look at it, most people won't care about fishing. Don't let that stop you. They're just jealous.
To fish on ice you have to puncture the ice layer with a pick or shovel first.
Fishing rods are particularly effective melee weapons against spacemen deeply infused with fish DNA from genetics.
Depending on the kinds of fish inside it and whether they're alive or dead, an aquarium can improve the beauty of the room or worsen it.
Almost all fish can be ground in an All-in-one-Grinder. Don't think too hard about how you're fitting a giant fish into a blender. Nanotrasen technology is weird like that.
The sludgefish from the toilets can be used as a steady supply of cheap fish and fillets due to its self-reproducing behaviour. However it's quite fragile.

View File

@@ -1062,6 +1062,7 @@
#include "code\datums\components\boomerang.dm"
#include "code\datums\components\boss_music.dm"
#include "code\datums\components\breeding.dm"
#include "code\datums\components\bubble_icon_override.dm"
#include "code\datums\components\bullet_intercepting.dm"
#include "code\datums\components\bumpattack.dm"
#include "code\datums\components\burning.dm"
@@ -2163,12 +2164,14 @@
#include "code\game\machinery\computer\records\security.dm"
#include "code\game\machinery\dna_infuser\dna_infuser.dm"
#include "code\game\machinery\dna_infuser\dna_infusion.dm"
#include "code\game\machinery\dna_infuser\infuser_actions.dm"
#include "code\game\machinery\dna_infuser\infuser_book.dm"
#include "code\game\machinery\dna_infuser\infuser_entry.dm"
#include "code\game\machinery\dna_infuser\infuser_entries\infuser_tier_one_entries.dm"
#include "code\game\machinery\dna_infuser\infuser_entries\infuser_tier_two_entries.dm"
#include "code\game\machinery\dna_infuser\infuser_entries\infuser_tier_zero_entries.dm"
#include "code\game\machinery\dna_infuser\organ_sets\carp_organs.dm"
#include "code\game\machinery\dna_infuser\organ_sets\fish_organs.dm"
#include "code\game\machinery\dna_infuser\organ_sets\fly_organs.dm"
#include "code\game\machinery\dna_infuser\organ_sets\fox_organs.dm"
#include "code\game\machinery\dna_infuser\organ_sets\goliath_organs.dm"
@@ -2865,6 +2868,7 @@
#include "code\modules\actionspeed\modifiers\addiction.dm"
#include "code\modules\actionspeed\modifiers\base.dm"
#include "code\modules\actionspeed\modifiers\drugs.dm"
#include "code\modules\actionspeed\modifiers\mobs.dm"
#include "code\modules\actionspeed\modifiers\mood.dm"
#include "code\modules\actionspeed\modifiers\status_effects.dm"
#include "code\modules\actionspeed\modifiers\wound.dm"
@@ -5661,10 +5665,10 @@
#include "code\modules\projectiles\projectile\special\ion.dm"
#include "code\modules\projectiles\projectile\special\meteor.dm"
#include "code\modules\projectiles\projectile\special\mindflayer.dm"
#include "code\modules\projectiles\projectile\special\neurotoxin.dm"
#include "code\modules\projectiles\projectile\special\plasma.dm"
#include "code\modules\projectiles\projectile\special\rocket.dm"
#include "code\modules\projectiles\projectile\special\saboteur.dm"
#include "code\modules\projectiles\projectile\special\spit.dm"
#include "code\modules\projectiles\projectile\special\temperature.dm"
#include "code\modules\projectiles\projectile\special\wormhole.dm"
#include "code\modules\reagents\chem_splash.dm"