Fish (ya know, from fishing) is now edible (#86110)

## About The Pull Request
Fish (the item that you catch with a fishing rod) now has an edible
component attached to it, making it possible to eat them if you really
have to, at the cost of eventually killing and deleting the fish,
however, you normally shouldn't.
Along with the seafood and meat foodtypes flags, it possess the gore and
raw foodtypes too, making them pretty awful to eat unless you're a
(non-vegan) lizard, felinid, or wearing the strange bandana*, which can
only be found in the cqc kit case. Furthermore, it carry diseases like
the ones from food left on the floors for too long, so a strong stomach
is required to safely eat it even if you actually like it, dummy...
UNLESS you fry or grill it, thus killing the diseases (as well as the
fish) and removing both the gore and raw food types, then it becomes an
actually ok meal... UNLESS you're dumb enough to eat a pufferfish, a
donkfish or a slimefish.

That is more or less the general rule. A few fish stray for it. For
example, lavaloops are never raw (still gorey). The skeleton fish are
never edibles, and holodeck fish is, well, holographic and thus
disappears if you try to eat it.

*the strange bandana is a reference to MSG, and this is a reference to
the MGS3 fish eating animation.

This is WIP btw, I'll have to test it and add some then polish it.

## Why It's Good For The Game
Whole unprocessed fish should be technically edible, even if not safe to
eat nine times out of ten. Also I kinda need this if I want to add a
tasty fishing spot to the kitchen deepfriers.

## Changelog

🆑
add: Whole, unprocessed fish is now edible. However it's pretty much
reccomended to grill or fry it for over 30 spess seconds before
attempting to eat it.
fix: germ-covered, dirty food no longer tries to infect you through
contact.
/🆑
This commit is contained in:
Ghom
2024-09-15 13:13:47 +02:00
committed by GitHub
parent d460c4a9ef
commit 8d0e6734fe
45 changed files with 692 additions and 161 deletions
@@ -22,6 +22,8 @@
#define COMSIG_FISH_LIFE "fish_life"
///From /datum/fish_trait/eat_fish: (predator)
#define COMSIG_FISH_EATEN_BY_OTHER_FISH "fish_eaten_by_other_fish"
///From /obj/item/fish/generate_reagents_to_add, which returns a holder when the fish is eaten or composted for example: (list/reagents)
#define COMSIG_GENERATE_REAGENTS_TO_ADD "generate_reagents_to_add"
///From /obj/item/fish/feed: (fed_reagents, fed_reagent_type)
#define COMSIG_FISH_FED "fish_on_fed"
///from /obj/item/fish/pet_fish
+7
View File
@@ -141,6 +141,10 @@
#define FISH_WEIGHT_SLOWDOWN_EXPONENT 0.54
///Used to calculate the force of the fish by comparing (1 + log(weight/this_define)) and the w_class of the item.
#define FISH_WEIGHT_FORCE_DIVISOR 250
///The multiplier used in the FISH_WEIGHT_BITE_DIVISOR define
#define FISH_WEIGHT_GRIND_TO_BITE_MULT 0.4
///Used to calculate how many bites a fish can take and therefore the amount of reagents it has.
#define FISH_WEIGHT_BITE_DIVISOR (FISH_GRIND_RESULTS_WEIGHT_DIVISOR * FISH_WEIGHT_GRIND_TO_BITE_MULT)
///The breeding timeout for newly instantiated fish is multiplied by this.
#define NEW_FISH_BREEDING_TIMEOUT_MULT 2
@@ -190,6 +194,9 @@
/// The height of the minigame slider. Not in pixels, but minigame units.
#define FISHING_MINIGAME_AREA 1000
///The fish needs to be cooked for at least this long so that it can be safely eaten
#define FISH_SAFE_COOKING_DURATION 30 SECONDS
///Defines for fish properties from the collect_fish_properties proc
#define FISH_PROPERTIES_FAV_BAIT "fav_bait"
#define FISH_PROPERTIES_BAD_BAIT "bad_bait"
+9
View File
@@ -170,12 +170,21 @@ GLOBAL_LIST_INIT(food_buffs, list(
#define FOOD_IN_CONTAINER (1<<0)
/// Finger food can be eaten while walking / running around
#define FOOD_FINGER_FOOD (1<<1)
/// Examining this edible won't show infos on food types, bites and remote tasting etc.
#define FOOD_NO_EXAMINE (1<<2)
/// This food item doesn't track bitecounts, use responsibly.
#define FOOD_NO_BITECOUNT (1<<3)
DEFINE_BITFIELD(food_flags, list(
"FOOD_FINGER_FOOD" = FOOD_FINGER_FOOD,
"FOOD_IN_CONTAINER" = FOOD_IN_CONTAINER,
"FOOD_NO_EXAMINE" = FOOD_NO_EXAMINE,
"FOOD_NO_BITECOUNT" = FOOD_NO_BITECOUNT,
))
///Define for return value of the after_eat callback that will call OnConsume if it hasn't already.
#define FOOD_AFTER_EAT_CONSUME_ANYWAY 2
#define STOP_SERVING_BREAKFAST (15 MINUTES)
#define FOOD_MEAT_HUMAN 50
+8
View File
@@ -221,6 +221,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NO_STAGGER "no_stagger"
/// Getting hit by thrown movables won't push you away
#define TRAIT_NO_THROW_HITPUSH "no_throw_hitpush"
/// This mob likes to eat fish. Raw, uncut fish.
#define TRAIT_FISH_EATER "fish_eater"
///Added to mob or mind, changes the icons of the fish shown in the minigame UI depending on the possible reward.
#define TRAIT_REVEAL_FISH "reveal_fish"
///This trait gets you a list of fishes that can be caught when examining a fishing spot.
@@ -741,6 +743,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_T_RAY_VISIBLE "t-ray-visible"
/// If this item's been fried
#define TRAIT_FOOD_FRIED "food_fried"
/// If this item's been bbq grilled
#define TRAIT_FOOD_BBQ_GRILLED "food_bbq_grilled"
/// This is a silver slime created item
#define TRAIT_FOOD_SILVER "food_silver"
/// If this item's been made by a chef instead of being map-spawned or admin-spawned or such
@@ -802,6 +806,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_HAUNTED "haunted"
/// An item that, if it has contents, will ignore its contents when scanning for contraband.
#define TRAIT_CONTRABAND_BLOCKER "contraband_blocker"
/// For edible items that cannot be composted inside hydro trays
#define TRAIT_UNCOMPOSTABLE "uncompostable"
//quirk traits
#define TRAIT_ALCOHOL_TOLERANCE "alcohol_tolerance"
@@ -838,6 +844,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_THROWINGARM "throwing_arm"
#define TRAIT_SETTLER "settler"
#define TRAIT_STRONG_STOMACH "strong_stomach"
#define TRAIT_VEGETARIAN "trait_vegetarian"
/// This mob always lands on their feet when they fall, for better or for worse.
#define TRAIT_CATLIKE_GRACE "catlike_grace"
@@ -962,6 +969,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_FISH_AMPHIBIOUS "fish_amphibious"
///Trait needed for the lubefish evolution
#define TRAIT_FISH_FED_LUBE "fish_fed_lube"
#define TRAIT_FISH_WELL_COOKED "fish_well_cooked"
#define TRAIT_FISH_NO_HUNGER "fish_no_hunger"
///It comes from a fish case. Relevant for bounties so far.
#define TRAIT_FISH_FROM_CASE "fish_from_case"
+5
View File
@@ -238,6 +238,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_FEARLESS" = TRAIT_FEARLESS,
"TRAIT_FENCE_CLIMBER" = TRAIT_FENCE_CLIMBER,
"TRAIT_FINGERPRINT_PASSTHROUGH" = TRAIT_FINGERPRINT_PASSTHROUGH,
"TRAIT_FISH_EATER" = TRAIT_FISH_EATER,
"TRAIT_FIST_MINING" = TRAIT_FIST_MINING,
"TRAIT_FIXED_MUTANT_COLORS" = TRAIT_FIXED_MUTANT_COLORS,
"TRAIT_FLESH_DESIRE" = TRAIT_FLESH_DESIRE,
@@ -523,6 +524,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_USER_SCOPED" = TRAIT_USER_SCOPED,
"TRAIT_USES_SKINTONES" = TRAIT_USES_SKINTONES,
"TRAIT_VATGROWN" = TRAIT_VATGROWN,
"TRAIT_VEGETARIAN" = TRAIT_VEGETARIAN,
"TRAIT_VENTCRAWLER_ALWAYS" = TRAIT_VENTCRAWLER_ALWAYS,
"TRAIT_VENTCRAWLER_NUDE" = TRAIT_VENTCRAWLER_NUDE,
"TRAIT_VIRUSIMMUNE" = TRAIT_VIRUSIMMUNE,
@@ -557,6 +559,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_CUSTOM_TAP_SOUND" = TRAIT_CUSTOM_TAP_SOUND,
"TRAIT_DANGEROUS_OBJECT" = TRAIT_DANGEROUS_OBJECT,
"TRAIT_FISHING_BAIT" = TRAIT_FISHING_BAIT,
"TRAIT_FOOD_BBQ_GRILLED" = TRAIT_FOOD_BBQ_GRILLED,
"TRAIT_GERM_SENSITIVE" = TRAIT_GERM_SENSITIVE,
"TRAIT_GOOD_QUALITY_BAIT" = TRAIT_GOOD_QUALITY_BAIT,
"TRAIT_GREAT_QUALITY_BAIT" = TRAIT_GREAT_QUALITY_BAIT,
@@ -577,6 +580,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_T_RAY_VISIBLE" = TRAIT_T_RAY_VISIBLE,
"TRAIT_TRANSFORM_ACTIVE" = TRAIT_TRANSFORM_ACTIVE,
"TRAIT_UNCATCHABLE" = TRAIT_UNCATCHABLE,
"TRAIT_UNCOMPOSTABLE" = TRAIT_UNCOMPOSTABLE,
"TRAIT_UNIQUE_AQUARIUM_CONTENT" = TRAIT_UNIQUE_AQUARIUM_CONTENT,
"TRAIT_WIELDED" = TRAIT_WIELDED,
),
@@ -619,6 +623,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_FISH_STINGER" = TRAIT_FISH_STINGER,
"TRAIT_FISH_TOXIN_IMMUNE" = TRAIT_FISH_TOXIN_IMMUNE,
"TRAIT_RESIST_EMULSIFY" = TRAIT_RESIST_EMULSIFY,
"TRAIT_FISH_WELL_COOKED" = TRAIT_FISH_WELL_COOKED,
"TRAIT_YUCKY_FISH" = TRAIT_YUCKY_FISH,
),
/obj/item/fishing_rod = list(
+2
View File
@@ -101,6 +101,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_FAT" = TRAIT_FAT,
"TRAIT_FEARLESS" = TRAIT_FEARLESS,
"TRAIT_FENCE_CLIMBER" = TRAIT_FENCE_CLIMBER,
"TRAIT_FISH_EATER" = TRAIT_FISH_EATER,
"TRAIT_FIST_MINING" = TRAIT_FIST_MINING,
"TRAIT_FIXED_MUTANT_COLORS" = TRAIT_FIXED_MUTANT_COLORS,
"TRAIT_FLESH_DESIRE" = TRAIT_FLESH_DESIRE,
@@ -299,6 +300,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_UNSTABLE" = TRAIT_UNSTABLE,
"TRAIT_USED_DNA_VAULT" = TRAIT_USED_DNA_VAULT,
"TRAIT_USES_SKINTONES" = TRAIT_USES_SKINTONES,
"TRAIT_VEGETARIAN" = TRAIT_VEGETARIAN,
"TRAIT_VENTCRAWLER_ALWAYS" = TRAIT_VENTCRAWLER_ALWAYS,
"TRAIT_VENTCRAWLER_NUDE" = TRAIT_VENTCRAWLER_NUDE,
"TRAIT_VIRUSIMMUNE" = TRAIT_VIRUSIMMUNE,
+10 -15
View File
@@ -40,8 +40,6 @@ Behavior that's still missing from this component that original food items had t
var/volume = 50
///The flavortext for taste (haha get it flavor text)
var/list/tastes
///Whether to tell the examiner that this is edible or not.
var/show_examine = TRUE
/datum/component/edible/Initialize(
list/initial_reagents,
@@ -57,7 +55,6 @@ Behavior that's still missing from this component that original food items had t
datum/callback/on_consume,
datum/callback/check_liked,
reagent_purity = 0.5,
show_examine = TRUE,
)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
@@ -73,7 +70,6 @@ Behavior that's still missing from this component that original food items had t
src.on_consume = on_consume
src.tastes = string_assoc_list(tastes)
src.check_liked = check_liked
src.show_examine = show_examine
setup_initial_reagents(initial_reagents, reagent_purity)
@@ -81,9 +77,9 @@ Behavior that's still missing from this component that original food items had t
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(examine))
RegisterSignal(parent, COMSIG_ATOM_ATTACK_ANIMAL, PROC_REF(UseByAnimal))
RegisterSignal(parent, COMSIG_ATOM_CHECKPARTS, PROC_REF(OnCraft))
RegisterSignal(parent, COMSIG_ATOM_CREATEDBY_PROCESSING, PROC_REF(OnProcessed))
RegisterSignal(parent, COMSIG_FOOD_INGREDIENT_ADDED, PROC_REF(edible_ingredient_added))
RegisterSignal(parent, COMSIG_OOZE_EAT_ATOM, PROC_REF(on_ooze_eat))
RegisterSignal(parent, COMSIG_FOOD_INGREDIENT_ADDED, PROC_REF(edible_ingredient_added))
RegisterSignal(parent, COMSIG_ATOM_CREATEDBY_PROCESSING, PROC_REF(OnProcessed))
if(isturf(parent))
RegisterSignal(parent, COMSIG_ATOM_ENTERED, PROC_REF(on_entered))
@@ -216,7 +212,7 @@ Behavior that's still missing from this component that original food items had t
SIGNAL_HANDLER
var/atom/owner = parent
if(!show_examine)
if(food_flags & FOOD_NO_EXAMINE)
return
if(foodtypes)
var/list/types = bitfield_to_list(foodtypes, FOOD_FLAGS)
@@ -316,7 +312,6 @@ Behavior that's still missing from this component that original food items had t
SIGNAL_HANDLER
var/atom/this_food = parent
for(var/obj/item/food/crafted_part in parts_list)
if(!crafted_part.reagents)
continue
@@ -325,7 +320,7 @@ Behavior that's still missing from this component that original food items had t
this_food.reagents.maximum_volume = ROUND_UP(this_food.reagents.maximum_volume) // Just because I like whole numbers for this.
BLACKBOX_LOG_FOOD_MADE(this_food.type)
BLACKBOX_LOG_FOOD_MADE(parent.type)
///Makes sure the thing hasn't been destroyed or fully eaten to prevent eating phantom edibles
/datum/component/edible/proc/IsFoodGone(atom/owner, mob/living/feeder)
@@ -462,7 +457,7 @@ Behavior that's still missing from this component that original food items had t
var/atom/owner = parent
if(!owner?.reagents)
if(!owner.reagents)
stack_trace("[eater] failed to bite [owner], because [owner] had no reagents.")
return FALSE
if(eater.satiety > -200)
@@ -479,7 +474,8 @@ Behavior that's still missing from this component that original food items had t
if(bitecount == 0)
apply_buff(eater)
var/fraction = min(bite_consumption / owner.reagents.total_volume, 1)
var/fraction = 0.3
fraction = min(bite_consumption / owner.reagents.total_volume, 1)
owner.reagents.trans_to(eater, bite_consumption, transferred_by = feeder, methods = INGEST)
bitecount++
@@ -489,8 +485,7 @@ Behavior that's still missing from this component that original food items had t
On_Consume(eater, feeder)
//Invoke our after eat callback if it is valid
if(after_eat)
after_eat.Invoke(eater, feeder, bitecount)
after_eat?.Invoke(eater, feeder, bitecount)
//Invoke the eater's stomach's after_eat callback if valid
if(iscarbon(eater))
@@ -531,7 +526,7 @@ Behavior that's still missing from this component that original food items had t
if(recipe_complexity <= 0)
return
var/obj/item/food/food = parent
if(!isnull(food.crafted_food_buff))
if(istype(food) && !isnull(food.crafted_food_buff))
buff = food.crafted_food_buff
else
buff = pick_weight(GLOB.food_buffs[min(recipe_complexity, FOOD_COMPLEXITY_5)])
@@ -666,7 +661,7 @@ Behavior that's still missing from this component that original food items had t
/datum/component/edible/proc/UseByAnimal(datum/source, mob/living/basic/pet/dog/doggy)
SIGNAL_HANDLER
if(!isdog(doggy))
if(!isdog(doggy) || (food_flags & FOOD_NO_BITECOUNT)) //this entirely relies on bitecounts alas
return
var/atom/food = parent
+74 -52
View File
@@ -8,43 +8,78 @@
var/weak_infection_chance = 10
/datum/component/infective/Initialize(list/datum/disease/_diseases, expire_in, weak = FALSE)
if(islist(_diseases))
diseases = _diseases
else
diseases = list(_diseases)
/datum/component/infective/Initialize(list/datum/disease/diseases, expire_in, weak = FALSE, weak_infection_chance = 10)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
if(!islist(diseases))
diseases = islist(diseases)
///Make sure the diseases list is populated with instances of diseases so that it doesn't have to be for each AddComponent call.
for(var/datum/disease/disease as anything in diseases)
if(!disease) //empty entry, remove.
diseases -= disease
if(ispath(disease, /datum/disease))
var/datum/disease/instance = new disease
diseases -= disease
diseases += instance
else if(!istype(disease))
stack_trace("found [isdatum(disease) ? "an instance of [disease.type]" : disease] inside the diseases list argument for [type]")
diseases -= disease
src.diseases = diseases
if(expire_in)
expire_time = world.time + expire_in
QDEL_IN(src, expire_in)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
is_weak = weak
src.weak_infection_chance = weak_infection_chance
/datum/component/infective/Destroy()
QDEL_NULL(diseases)
return ..()
/datum/component/infective/RegisterWithParent()
if(is_weak && isitem(parent))
RegisterSignal(parent, COMSIG_FOOD_EATEN, PROC_REF(try_infect_eat))
RegisterSignal(parent, COMSIG_PILL_CONSUMED, PROC_REF(try_infect_eat))
else
var/static/list/disease_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(try_infect_crossed),
)
AddComponent(/datum/component/connect_loc_behalf, parent, disease_connections)
return
var/static/list/disease_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(try_infect_crossed),
)
AddComponent(/datum/component/connect_loc_behalf, parent, disease_connections)
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean))
RegisterSignal(parent, COMSIG_MOVABLE_BUCKLE, PROC_REF(try_infect_buckle))
RegisterSignal(parent, COMSIG_MOVABLE_BUMP, PROC_REF(try_infect_collide))
RegisterSignal(parent, COMSIG_MOVABLE_IMPACT_ZONE, PROC_REF(try_infect_impact_zone))
if(isitem(parent))
RegisterSignal(parent, COMSIG_ITEM_ATTACK_ZONE, PROC_REF(try_infect_attack_zone))
RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(try_infect_attack))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(try_infect_equipped))
RegisterSignal(parent, COMSIG_FOOD_EATEN, PROC_REF(try_infect_eat))
RegisterSignal(parent, COMSIG_PILL_CONSUMED, PROC_REF(try_infect_eat))
if(istype(parent, /obj/item/reagent_containers/cup))
RegisterSignal(parent, COMSIG_GLASS_DRANK, PROC_REF(try_infect_drink))
if(isorgan(parent))
RegisterSignal(parent, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_organ_insertion))
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean))
RegisterSignal(parent, COMSIG_MOVABLE_BUCKLE, PROC_REF(try_infect_buckle))
RegisterSignal(parent, COMSIG_MOVABLE_BUMP, PROC_REF(try_infect_collide))
RegisterSignal(parent, COMSIG_MOVABLE_IMPACT_ZONE, PROC_REF(try_infect_impact_zone))
if(isitem(parent))
RegisterSignal(parent, COMSIG_ITEM_ATTACK_ZONE, PROC_REF(try_infect_attack_zone))
RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(try_infect_attack))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(try_infect_equipped))
RegisterSignal(parent, COMSIG_FOOD_EATEN, PROC_REF(try_infect_eat))
RegisterSignal(parent, COMSIG_PILL_CONSUMED, PROC_REF(try_infect_eat))
if(istype(parent, /obj/item/reagent_containers/cup))
RegisterSignal(parent, COMSIG_GLASS_DRANK, PROC_REF(try_infect_drink))
if(isorgan(parent))
RegisterSignal(parent, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_organ_insertion))
/datum/component/infective/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(
COMSIG_FOOD_EATEN,
COMSIG_PILL_CONSUMED,
COMSIG_COMPONENT_CLEAN_ACT,
COMSIG_MOVABLE_BUMP,
COMSIG_MOVABLE_IMPACT_ZONE,
COMSIG_ITEM_ATTACK_ZONE,
COMSIG_ITEM_ATTACK,
COMSIG_ITEM_EQUIPPED,
COMSIG_GLASS_DRANK,
COMSIG_ORGAN_IMPLANTED,
))
qdel(GetComponent(/datum/component/connect_loc_behalf))
/datum/component/infective/proc/on_organ_insertion(obj/item/organ/target, mob/living/carbon/receiver)
SIGNAL_HANDLER
@@ -62,16 +97,16 @@
eater.add_mood_event("disgust", /datum/mood_event/disgust/dirty_food)
if(is_weak && !prob(weak_infection_chance))
return
for(var/datum/disease/disease in diseases)
for(var/datum/disease/disease as anything in diseases)
if(is_weak && !prob(weak_infection_chance))
continue
if(!disease.has_required_infectious_organ(eater, ORGAN_SLOT_STOMACH))
continue
eater.ForceContractDisease(disease)
try_infect(feeder, BODY_ZONE_L_ARM)
if(!is_weak)
try_infect(feeder, BODY_ZONE_L_ARM)
/datum/component/infective/proc/try_infect_drink(datum/source, mob/living/drinker, mob/living/feeder)
SIGNAL_HANDLER
@@ -79,11 +114,14 @@
if(HAS_TRAIT(drinker, TRAIT_STRONG_STOMACH))
return
var/appendage_zone = feeder.held_items.Find(source)
appendage_zone = appendage_zone == 0 ? BODY_ZONE_CHEST : appendage_zone % 2 ? BODY_ZONE_R_ARM : BODY_ZONE_L_ARM
try_infect(feeder, appendage_zone)
if(!is_weak)
var/appendage_zone = feeder.held_items.Find(source)
appendage_zone = appendage_zone == 0 ? BODY_ZONE_CHEST : (appendage_zone % 2 ? BODY_ZONE_R_ARM : BODY_ZONE_L_ARM)
try_infect(feeder, appendage_zone)
for(var/datum/disease/disease in diseases)
for(var/datum/disease/disease as anything in diseases)
if(is_weak && !prob(weak_infection_chance))
continue
if(!disease.has_required_infectious_organ(drinker, ORGAN_SLOT_STOMACH))
continue
@@ -163,19 +201,3 @@
/datum/component/infective/proc/try_infect(mob/living/L, target_zone)
for(var/V in diseases)
L.ContactContractDisease(V, target_zone)
/datum/component/infective/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(
COMSIG_FOOD_EATEN,
COMSIG_PILL_CONSUMED,
COMSIG_COMPONENT_CLEAN_ACT,
COMSIG_MOVABLE_BUMP,
COMSIG_MOVABLE_IMPACT_ZONE,
COMSIG_ITEM_ATTACK_ZONE,
COMSIG_ITEM_ATTACK,
COMSIG_ITEM_EQUIPPED,
COMSIG_GLASS_DRANK,
COMSIG_ORGAN_IMPLANTED,
))
qdel(GetComponent(/datum/component/connect_loc_behalf))
@@ -41,7 +41,7 @@
switch(stage)
if(2)
if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS)
if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS && affected_mob.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL))
to_chat(affected_mob, span_warning("You want to wag your tail..."))
affected_mob.emote("wag")
if(3)
+1 -1
View File
@@ -23,7 +23,7 @@
/datum/element/consumable_mob/proc/on_consume(atom/movable/source, mob/living/consumer)
SIGNAL_HANDLER
if(!consumer.combat_mode || !consumer.reagents)
if(!consumer.combat_mode || !consumer.reagents || HAS_TRAIT(consumer, TRAIT_PACIFISM))
return
for(var/reagent_type in reagents_list)
if(isnull(reagents_list[reagent_type]))
+5 -5
View File
@@ -17,28 +17,27 @@
var/atom/this_food = target
switch(fry_time)
if(0 to 15)
if(0 to 15 SECONDS)
this_food.add_atom_colour(fried_colors[1], FIXED_COLOUR_PRIORITY)
this_food.name = "lightly-fried [this_food.name]"
this_food.desc += " It's been lightly fried in a deep fryer."
if(15 to 50)
if(15 SECONDS to 50 SECONDS)
this_food.add_atom_colour(fried_colors[2], FIXED_COLOUR_PRIORITY)
this_food.name = "fried [this_food.name]"
this_food.desc += " It's been fried, increasing its tastiness value by [rand(1, 75)]%."
if(50 to 85)
if(50 SECONDS to 85 SECONDS)
this_food.add_atom_colour(fried_colors[3], FIXED_COLOUR_PRIORITY)
this_food.name = "deep-fried [this_food.name]"
this_food.desc += " Deep-fried to perfection."
if(85 to INFINITY)
if(85 SECONDS to INFINITY)
this_food.add_atom_colour(fried_colors[4], FIXED_COLOUR_PRIORITY)
this_food.name = "\proper the physical manifestation of the very concept of fried foods"
this_food.desc = "A heavily-fried... something. Who can tell anymore?"
ADD_TRAIT(this_food, TRAIT_FOOD_FRIED, ELEMENT_TRAIT(type))
SEND_SIGNAL(this_food, COMSIG_ITEM_FRIED, fry_time)
// Already edible items will inherent these parameters
// Otherwise, we will become edible.
this_food.AddComponent( \
@@ -49,6 +48,7 @@
foodtypes = FRIED, \
volume = this_food.reagents?.maximum_volume, \
)
SEND_SIGNAL(this_food, COMSIG_ITEM_FRIED, fry_time)
/datum/element/fried_item/Detach(atom/source, ...)
for(var/color in fried_colors)
+3 -1
View File
@@ -28,10 +28,12 @@
if(grill_time > 30 SECONDS && isnull(this_food.GetComponent(/datum/component/edible)))
this_food.AddComponent(/datum/component/edible, foodtypes = FRIED)
SEND_SIGNAL(this_food, COMSIG_ITEM_BARBEQUE_GRILLED)
SEND_SIGNAL(this_food, COMSIG_ITEM_BARBEQUE_GRILLED, grill_time)
ADD_TRAIT(this_food, TRAIT_FOOD_BBQ_GRILLED, ELEMENT_TRAIT(type))
/datum/element/grilled_item/Detach(atom/source, ...)
source.name = initial(source.name)
source.desc = initial(source.desc)
qdel(source.GetComponent(/datum/component/edible)) // Don't care if it was initially edible
REMOVE_TRAIT(src, TRAIT_FOOD_BBQ_GRILLED, ELEMENT_TRAIT(type))
return ..()
+5
View File
@@ -49,3 +49,8 @@
/datum/mood_event/food/top
quality = FOOD_QUALITY_TOP
/datum/mood_event/pacifist_eating_fish_item
description = "I shouldn't be eating living creatures..."
mood_change = -1 //The disgusting food moodlet already has a pretty big negative value, this is just for context.
timeout = 4 MINUTES
@@ -7,17 +7,4 @@
lose_text = span_notice("You feel like eating meat isn't that bad.")
medical_record_text = "Patient reports a vegetarian diet."
mail_goodies = list(/obj/effect/spawner/random/food_or_drink/salad)
/datum/quirk/vegetarian/add(client/client_source)
var/obj/item/organ/internal/tongue/tongue = quirk_holder.get_organ_slot(ORGAN_SLOT_TONGUE)
if(!tongue)
return
tongue.liked_foodtypes &= ~MEAT
tongue.disliked_foodtypes |= MEAT
/datum/quirk/vegetarian/remove()
var/obj/item/organ/internal/tongue/tongue = quirk_holder.get_organ_slot(ORGAN_SLOT_TONGUE)
if(!tongue)
return
tongue.liked_foodtypes = initial(tongue.liked_foodtypes)
tongue.disliked_foodtypes = initial(tongue.disliked_foodtypes)
mob_trait = TRAIT_VEGETARIAN
+2 -2
View File
@@ -690,10 +690,10 @@
created_atoms.Add(created_atom)
to_chat(user, span_notice("You manage to create [amount_to_create] [initial(atom_to_create.gender) == PLURAL ? "[initial(atom_to_create.name)]" : "[initial(atom_to_create.name)][plural_s(initial(atom_to_create.name))]"] from [src]."))
SEND_SIGNAL(src, COMSIG_ATOM_PROCESSED, user, process_item, created_atoms)
UsedforProcessing(user, process_item, chosen_option)
UsedforProcessing(user, process_item, chosen_option, created_atoms)
return
/atom/proc/UsedforProcessing(mob/living/user, obj/item/used_item, list/chosen_option)
/atom/proc/UsedforProcessing(mob/living/user, obj/item/used_item, list/chosen_option, list/created_atoms)
qdel(src)
return
+1 -2
View File
@@ -198,7 +198,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
// "It is called a cigarette"
AddComponent(/datum/component/edible,\
initial_reagents = list_reagents,\
food_flags = null,\
food_flags = FOOD_NO_EXAMINE,\
foodtypes = JUNKFOOD,\
volume = 50,\
eat_time = 0 SECONDS,\
@@ -208,7 +208,6 @@ CIGARETTE PACKETS ARE IN FANCY.DM
junkiness = 0,\
reagent_purity = null,\
on_consume = CALLBACK(src, PROC_REF(on_consume)),\
show_examine = FALSE, \
)
/obj/item/cigarette/Destroy()
+10 -4
View File
@@ -57,7 +57,7 @@
food_reagents = list(
/datum/reagent/consumable/nutriment/protein = 4,
/datum/reagent/consumable/nutriment/vitamin = 3,
/datum/reagent/consumable/nutriment/fat/oil = 2,
/datum/reagent/consumable/nutriment/fat = 2,
)
bite_consumption = 4.5
crafting_complexity = FOOD_COMPLEXITY_1
@@ -99,14 +99,20 @@
/obj/item/food/fishmeat/gunner_jellyfish
name = "filleted gunner jellyfish"
desc = "A gunner jellyfish with the stingers removed. Mildly hallucinogenic."
desc = "A gunner jellyfish with the stingers removed. Mildly hallucinogenic when raw."
icon = 'icons/obj/food/lizard.dmi'
icon_state = "jellyfish_fillet"
food_reagents = list(
/datum/reagent/consumable/nutriment/protein = 4,
/datum/reagent/toxin/mindbreaker = 2,
/datum/reagent/consumable/nutriment/protein = 4, //The halluginogen comes from the fish trait.
)
///Premade gunner jellyfish fillets from supply orders. Contains the halluginogen that'd be normally from the fish trait.
/obj/item/food/fishmeat/gunner_jellyfish/supply
/obj/item/food/fishmeat/gunner_jellyfish/supply/Initialize(mapload)
food_reagents[/datum/reagent/toxin/mindbreaker/fish] = 2
return ..()
/obj/item/food/fishmeat/armorfish
name = "cleaned armorfish"
desc = "An armorfish with its guts and shell removed, ready for use in cooking."
@@ -301,7 +301,7 @@
new /obj/item/food/fishmeat/armorfish(src)
new /obj/item/food/fishmeat/carp(src)
new /obj/item/food/fishmeat/moonfish(src)
new /obj/item/food/fishmeat/gunner_jellyfish(src)
new /obj/item/food/fishmeat/gunner_jellyfish/supply(src)
/obj/item/storage/box/ingredients/salads
theme_name = "salads"
@@ -410,6 +410,7 @@
desc = "A bandana. It seems to have a little carp embroidered on the inside, as well as the kanji '魚'."
icon_state = "snake_eater"
inhand_icon_state = null
clothing_traits = list(TRAIT_FISH_EATER)
/obj/item/clothing/head/costume/knight
name = "fake medieval helmet"
+229 -5
View File
@@ -20,8 +20,6 @@
drop_sound = 'sound/creatures/fish/fish_drop1.ogg'
pickup_sound = SFX_FISH_PICKUP
sound_vary = TRUE
///The grind results of the fish. They scale with the weight of the fish.
grind_results = list(/datum/reagent/blood = 5, /datum/reagent/consumable/liquidgibs = 5)
obj_flags = UNIQUE_RENAME
item_flags = IMMUTABLE_SLOW|SLOWS_WHILE_IN_HAND
@@ -150,6 +148,14 @@
///have we recently pet this fish
var/recently_petted = FALSE
/**
* If you wonder why this isn't being tracked by the edible component instead:
* We reset the this value when revived, and slowly chip it away as we heal.
* Of course, it would be daunting to get this to be handled by the edible component
* given its complexity.
*/
var/bites_amount = 0
/obj/item/fish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
//It's important that we register the signals before the component is attached.
@@ -163,6 +169,7 @@
RegisterSignal(src, COMSIG_ATOM_TEMPORARY_ANIMATION_START, PROC_REF(on_temp_animation))
check_environment()
if(status != FISH_DEAD)
ADD_TRAIT(src, TRAIT_UNCOMPOSTABLE, REF(src)) //Composting a food that is not real food wouldn't work anyway.
START_PROCESSING(SSobj, src)
//stops new fish from being able to reproduce right away.
@@ -175,6 +182,174 @@
register_evolutions()
///Main proc that makes the fish edible.
/obj/item/fish/proc/make_edible()
var/foodtypes = get_food_types()
if(foodtypes & RAW)
AddComponent(/datum/component/infective, GLOB.floor_diseases.Copy(), weak = TRUE, weak_infection_chance = PERFORM_ALL_TESTS(edible_fish) ? 100 : 15)
else
AddComponent(/datum/component/germ_sensitive)
var/bites_to_finish = weight / FISH_WEIGHT_BITE_DIVISOR
create_reagents(INFINITY) //We'll set this to the total volume of the reagents right after generate_fish_reagents() is over
generate_fish_reagents(bites_to_finish)
reagents.maximum_volume = round(reagents.total_volume * 1.25) //make some meager space for condiments.
AddComponent(/datum/component/edible, \
food_flags = FOOD_NO_EXAMINE|FOOD_NO_BITECOUNT, \
foodtypes = foodtypes, \
volume = reagents.total_volume, \
eat_time = 1.5 SECONDS, \
bite_consumption = reagents.total_volume / bites_to_finish, \
after_eat = CALLBACK(src, PROC_REF(after_eat)), \
check_liked = CALLBACK(src, PROC_REF(check_liked)), \
reagent_purity = 1, \
)
RegisterSignals(src, list(COMSIG_ITEM_FRIED, COMSIG_ITEM_BARBEQUE_GRILLED), PROC_REF(on_fish_cooked))
///A proc that returns the food types the edible component has when initialized.
/obj/item/fish/proc/get_food_types()
return SEAFOOD|MEAT|RAW|GORE
///Kill the fish, remove the raw and gore food types, and the infectiveness too if not under-cooked.
/obj/item/fish/proc/on_fish_cooked(datum/source, cooking_time)
SIGNAL_HANDLER
SHOULD_NOT_OVERRIDE(TRUE)
adjust_health(0)
//Remove the blood from the reagents holder and reward the player with some extra nutriment added to the fish.
var/datum/reagent/consumable/nutriment/protein/protein = reagents.has_reagent(/datum/reagent/consumable/nutriment/protein, check_subtypes = TRUE)
var/datum/reagent/blood/blood = reagents.has_reagent(/datum/reagent/blood)
var/old_blood_volume = blood?.volume
reagents.del_reagent(/datum/reagent/blood)
///Make space for the additional nutriment
var/volume_mult = 1
if(bites_amount)
var/initial_bites_left = weight / FISH_WEIGHT_BITE_DIVISOR
var/bites_left = initial_bites_left - bites_amount
volume_mult = initial_bites_left / bites_left
adjust_reagents_capacity((protein?.volume - old_blood_volume) * volume_mult)
///Add the extra nutriment
if(protein)
reagents.multiply_single_reagent(/datum/reagent/consumable/nutriment/protein, 2)
var/datum/component/edible/edible = GetComponent(/datum/component/edible)
edible.foodtypes &= ~(RAW|GORE)
if(cooking_time >= FISH_SAFE_COOKING_DURATION)
well_cooked()
///override the signals so they don't mess with blood and proteins again.
RegisterSignals(src, list(COMSIG_ITEM_FRIED, COMSIG_ITEM_BARBEQUE_GRILLED), PROC_REF(on_fish_cooked_again), TRUE)
///Just kill the fish, again, and perhaps remove the infective comp.
/obj/item/fish/proc/on_fish_cooked_again(datum/source, cooking_time)
SIGNAL_HANDLER
adjust_health(0)
if(cooking_time >= FISH_SAFE_COOKING_DURATION)
well_cooked()
///The fish is well cooked. Change how the fish tastes, remove the infective comp and add the relative trait.
/obj/item/fish/proc/well_cooked()
qdel(GetComponent(/datum/component/infective))
AddComponent(/datum/component/germ_sensitive)
ADD_TRAIT(src, TRAIT_FISH_WELL_COOKED, INNATE_TRAIT)
var/datum/reagent/consumable/nutriment/protein/protein = reagents.has_reagent(/datum/reagent/consumable/nutriment/protein, check_subtypes = TRUE)
if(protein)
protein.data = get_fish_taste_cooked()
///Checks if the fish is liked or not when eaten by a human.
/obj/item/fish/proc/check_liked(mob/living/eater)
if(HAS_TRAIT(eater, TRAIT_PACIFISM) && (status == FISH_ALIVE ||HAS_MIND_TRAIT(eater, TRAIT_NAIVE)))
eater.add_mood_event("eating_fish", /datum/mood_event/pacifist_eating_fish_item)
return FOOD_TOXIC
if(HAS_TRAIT(eater, TRAIT_AGEUSIA))
return
if(HAS_TRAIT(eater, TRAIT_FISH_EATER) && !HAS_TRAIT(eater, TRAIT_VEGETARIAN))
return FOOD_LIKED
/**
* Fish is not a reagent holder yet it's edible, so it doen't behave like most other snacks
* which means it has its own way of handling being bitten, which is defined here.
*/
/obj/item/fish/proc/after_eat(mob/living/eater, mob/living/feeder)
SHOULD_CALL_PARENT(TRUE)
if(!reagents.total_volume)
return
bites_amount++
var/bites_to_finish = weight / FISH_WEIGHT_BITE_DIVISOR
adjust_health(health - (initial(health) / bites_to_finish) * 3)
if(status == FISH_ALIVE && prob(50) && feeder.is_holding(src) && feeder.dropItemToGround(src))
to_chat(feeder, span_warning("[src] slips out of your hands in pain!"))
var/turf/target_turf = get_ranged_target_turf(get_turf(src), pick(GLOB.alldirs), 2)
throw_at(target_turf)
///A proc that returns a static reagent holder with a set reagents that you'd get when eating this fish.
/obj/item/fish/proc/generate_fish_reagents(multiplier = 1)
SHOULD_NOT_OVERRIDE(TRUE)
var/list/reagents_to_add = get_base_edible_reagents_to_add()
SEND_SIGNAL(src, COMSIG_GENERATE_REAGENTS_TO_ADD, reagents_to_add)
if(multiplier != 1)
for(var/reagent in reagents_to_add)
reagents_to_add[reagent] *= multiplier
reagents.add_reagent_list(reagents_to_add, added_purity = 1)
var/datum/reagent/consumable/nutriment/protein/protein = reagents.has_reagent(/datum/reagent/consumable/nutriment/protein, check_subtypes = TRUE)
if(protein)
protein.data = HAS_TRAIT(src, TRAIT_FISH_WELL_COOKED) ? get_fish_taste_cooked() : get_fish_taste()
/obj/item/fish/proc/get_fish_taste()
return list("raw fish" = 2.5, "scales" = 1)
/obj/item/fish/proc/get_fish_taste_cooked()
return list("cooked fish" = 2)
///The proc that adds in the main reagents this fish has when eaten (without accounting for traits)
/obj/item/fish/proc/get_base_edible_reagents_to_add()
var/return_list = list(
/datum/reagent/consumable/nutriment/protein = 2,
/datum/reagent/blood = 1,
)
//It has been at the very least under-cooked.
if(HAS_TRAIT(src, TRAIT_FOOD_FRIED) || HAS_TRAIT(src, TRAIT_FOOD_BBQ_GRILLED))
return_list[/datum/reagent/consumable/nutriment/protein] *= 2
return_list -= /datum/reagent/blood
if(required_fluid_type == AQUARIUM_FLUID_SALTWATER)
return_list[/datum/reagent/consumable/salt] = 0.4
return return_list
///adjusts the maximum volume of the fish reagents holder and update the amount of food to bite
/obj/item/fish/proc/adjust_reagents_capacity(amount_to_add)
if(!reagents)
return
reagents.maximum_volume += amount_to_add
var/bites_to_finish = weight / FISH_WEIGHT_BITE_DIVISOR
///updates how many units of reagent one bite takes if edible.
if(IS_EDIBLE(src))
AddComponent(/datum/component/edible, bite_consumption = reagents.maximum_volume / bites_to_finish)
///Grinding a fish replaces some the protein it has with blood and gibs. You ain't getting a clean smoothie out of it.
/obj/item/fish/on_grind()
. = ..()
if(!reagents)
return
reagents.convert_reagent(/datum/reagent/consumable/nutriment/protein, /datum/reagent/consumable/liquidgibs, 0.4, include_source_subtypes = TRUE)
reagents.convert_reagent(/datum/reagent/consumable/nutriment/protein, /datum/reagent/blood, 0.2, include_source_subtypes = TRUE)
///When processed, the reagents inside this fish will be passed to the created atoms.
/obj/item/fish/UsedforProcessing(mob/living/user, obj/item/used_item, list/chosen_option, list/created_atoms)
var/created_len = length(created_atoms)
for(var/atom/movable/created as anything in created_atoms)
if(!created.reagents)
continue
for(var/datum/reagent/reagent as anything in reagents.reagent_list)
var/transfer_vol = reagent.volume / created_len
var/datum/reagent/result_reagent = created.reagents.has_reagent(reagent.type)
if(!result_reagent)
created.reagents.add_reagent(reagent.type, transfer_vol, reagents.copy_data(reagent), reagents.chem_temp, reagent.purity, reagent.ph, no_react = TRUE)
continue
var/multiplier = transfer_vol / result_reagent.volume
created.reagents.multiply_single_reagent(reagent.type, multiplier)
return ..()
/obj/item/fish/update_icon_state()
if(status == FISH_DEAD && icon_state_dead)
icon_state = icon_state_dead
@@ -214,6 +389,8 @@
. += span_notice("It weighs [weight] g.")
if(HAS_TRAIT(src, TRAIT_FISHING_BAIT))
. += span_smallnoticeital("It can be used as a fishing bait.")
if(bites_amount)
. += span_warning("It's been bitten by someone.")
///Randomizes weight and size.
/obj/item/fish/proc/randomize_size_and_weight(base_size = average_size, base_weight = average_weight, deviation = weight_size_deviation)
@@ -272,11 +449,32 @@
num_fillets = amount
AddElement(/datum/element/processable, TOOL_KNIFE, fillet_type, num_fillets, 0.5 SECONDS * num_fillets, screentip_verb = "Cut")
var/make_edible = TRUE
if(weight)
for(var/reagent_type in grind_results)
grind_results[reagent_type] /= FLOOR(weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR, 0.1)
if(reagents) //This fish has reagents. Adjust the maximum volume of the reagent holder and do some math to adjut the reagents too.
var/new_weight_ratio = new_weight / weight
var/volume_diff = reagents.maximum_volume * new_weight_ratio - reagents.maximum_volume
if(new_weight_ratio > weight)
adjust_reagents_capacity(volume_diff)
///As always, we want to maintain proportions here, so we need to get the ratio of bites left and initial bites left.
var/weight_diff = new_weight - weight
var/multiplier = weight_diff / FISH_WEIGHT_BITE_DIVISOR
var/initial_bites_left = weight / FISH_WEIGHT_BITE_DIVISOR
var/bites_left = initial_bites_left - bites_amount
var/amount_to_gen = bites_left / initial_bites_left * multiplier
generate_fish_reagents(amount_to_gen)
else
reagents.multiply_reagents(new_weight_ratio)
adjust_reagents_capacity(volume_diff)
make_edible = FALSE
weight = new_weight
if(make_edible)
make_edible()
if(weight >= FISH_WEIGHT_SLOWDOWN)
slowdown = round(((weight/FISH_WEIGHT_SLOWDOWN_DIVISOR)**FISH_WEIGHT_SLOWDOWN_EXPONENT)-1.3, 0.1)
drag_slowdown = round(slowdown * 0.5, 1)
@@ -492,12 +690,15 @@
if(FISH_ALIVE)
status = FISH_ALIVE
health = initial(health) // since the fishe has been revived
regenerate_bites(bites_amount)
last_feeding = world.time //reset hunger
check_environment()
START_PROCESSING(SSobj, src)
ADD_TRAIT(src, TRAIT_UNCOMPOSTABLE, INNATE_TRAIT)
if(FISH_DEAD)
status = FISH_DEAD
STOP_PROCESSING(SSobj, src)
REMOVE_TRAIT(src, TRAIT_UNCOMPOSTABLE, INNATE_TRAIT)
stop_flopping()
if(!silent)
var/message = span_notice(replacetext(death_text, "%SRC", "[src]"))
@@ -652,10 +853,28 @@
health_change_per_second += 0.5 //Slowly healing
adjust_health(health + health_change_per_second * seconds_per_tick)
/obj/item/fish/proc/adjust_health(amt)
health = clamp(amt, 0, initial(health))
/obj/item/fish/proc/adjust_health(amount)
if(status == FISH_DEAD || amount == health)
return
var/pre_health = health
var/initial_health = initial(health)
health = clamp(amount, 0, initial_health)
if(health <= 0)
set_status(FISH_DEAD)
return
if(amount < pre_health || !bites_amount)
return
var/health_to_pre_health_diff = amount - pre_health
var/init_health_to_pre_diff = initial_health - pre_health
var/bites_to_recover = bites_amount * (health_to_pre_health_diff / init_health_to_pre_diff)
regenerate_bites(bites_to_recover)
/obj/item/fish/proc/regenerate_bites(amount)
amount = min(amount, bites_amount)
if(amount <= 0)
return
bites_amount -= amount
generate_fish_reagents(amount)
/obj/item/fish/proc/ready_to_reproduce(being_targeted = FALSE)
var/obj/structure/aquarium/aquarium = loc
@@ -845,6 +1064,7 @@
if(HAS_TRAIT(src, TRAIT_FISH_FROM_CASE)) //Avoid printing money by simply ordering fish and sending it back.
calculated_price *= 0.05
return round(calculated_price)
/obj/item/fish/proc/get_happiness_value()
var/happiness_value = 0
if(recently_petted)
@@ -858,7 +1078,11 @@
happiness_value++
if(ISINRANGE(aquarium.fluid_temp, required_temperature_min, required_temperature_max))
happiness_value++
return happiness_value
if(bites_amount) // ouch
happiness_value -= 2
if(health < initial(health) * 0.6)
happiness_value -= 1
return clamp(happiness_value, FISH_SAD, FISH_VERY_HAPPY)
/obj/item/fish/proc/pet_fish(mob/living/user)
if(recently_petted)
+40 -23
View File
@@ -37,7 +37,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
var/list/fish_whitelist
/// Depending on the value, fish with trait will be reported as more or less difficult in the catalog.
var/added_difficulty = 0
/// Reagents added to the fish when gained
/// 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
/// Difficulty modifier from this mod, needs to return a list with two values
@@ -57,10 +57,8 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/// Applies some special qualities to the fish that has been spawned
/datum/fish_trait/proc/apply_to_fish(obj/item/fish/fish)
SHOULD_CALL_PARENT(TRUE)
if(reagents_to_add)
for(var/reagent in reagents_to_add)
add_to_reagents(fish, reagent, reagents_to_add[reagent])
RegisterSignal(fish, COMSIG_ATOM_PROCESSED, PROC_REF(process_reagents))
if(length(reagents_to_add))
RegisterSignal(fish, COMSIG_GENERATE_REAGENTS_TO_ADD, PROC_REF(add_reagents))
/// Applies some special qualities to basic mobs generated by fish (i.e. chasm chrab --> young lobstrosity --> lobstrosity).
/datum/fish_trait/proc/apply_to_mob(mob/living/basic/mob)
@@ -79,25 +77,15 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
SEND_SIGNAL(prey, COMSIG_FISH_EATEN_BY_OTHER_FISH, predator)
qdel(prey)
/// Proc that inserts a reagent to the grind_results list of the fish. You'll still have to set the processed comsig proc yourself.
/datum/fish_trait/proc/add_to_reagents(obj/item/fish/fish, reagent_type, amount)
LAZYINITLIST(fish.grind_results)
fish.grind_results.Insert(1, reagent_type)
fish.grind_results[reagent_type] = amount
/// Proc that handles adding reagents from the trait to the fillets from butchered fish.
/datum/fish_trait/proc/process_reagents(obj/item/fish/source, mob/living/user, obj/item/process_item, list/results)
/**
* Signal sent when we need to generate an abstract holder containing
* reagents to be transfered, usually as a result of the fish being eaten by someone
*/
/datum/fish_trait/proc/add_reagents(obj/item/fish/fish, list/reagents)
SIGNAL_HANDLER
var/results_with_reagents = 0
for(var/atom/result as anything in results)
if(result.reagents)
results_with_reagents++
if(!results_with_reagents)
return
for(var/reagent in reagents_to_add)
var/amount = round(source.grind_results[reagent] / results_with_reagents, 0.1)
for(var/atom/result as anything in results)
result.reagents?.add_reagent(reagent, amount)
reagents[reagent] += reagents_to_add[reagent]
/// Proc that adds or changes the venomous when the fish size and/or weight are updated
/datum/fish_trait/proc/add_venom(obj/item/fish/source, venom_path, new_weight, mult = 0.25)
@@ -413,7 +401,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/datum/fish_trait/yucky
name = "Yucky"
catalog_description = "This fish tastes so repulsive, other fishes won't try to eat it."
reagents_to_add = list(/datum/reagent/yuck = 3)
reagents_to_add = list(/datum/reagent/yuck = 1.2)
/datum/fish_trait/yucky/apply_to_fish(obj/item/fish/fish)
. = ..()
@@ -423,7 +411,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
name = "Toxic"
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 = 2.5)
reagents_to_add = list(/datum/reagent/toxin/tetrodotoxin = 1)
/datum/fish_trait/toxic/apply_to_fish(obj/item/fish/fish)
. = ..()
@@ -512,6 +500,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
spontaneous_manifest_types = list(/obj/item/fish/clownfish/lube = 100)
catalog_description = "This fish exudes a viscous, slippery lubrificant. It's recommended not to step on it."
added_difficulty = 5
reagents_to_add = list(/datum/reagent/lube = 1.2)
/datum/fish_trait/lubed/apply_to_fish(obj/item/fish/fish)
. = ..()
@@ -597,11 +586,30 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
inheritability = 60
diff_traits_inheritability = 30
catalog_description = "This fish is electroreceptive, and will generate electric fields. Can be harnessed inside a bioelectric generator."
reagents_to_add = list(/datum/reagent/consumable/liquidelectricity = 1.5)
/datum/fish_trait/electrogenesis/apply_to_fish(obj/item/fish/fish)
. = ..()
ADD_TRAIT(fish, TRAIT_FISH_ELECTROGENESIS, FISH_TRAIT_DATUM)
RegisterSignal(fish, COMSIG_FISH_FORCE_UPDATED, PROC_REF(on_force_updated))
RegisterSignals(fish, list(COMSIG_ITEM_FRIED, TRAIT_FOOD_BBQ_GRILLED), PROC_REF(on_fish_cooked))
/datum/fish_trait/electrogenesis/proc/on_fish_cooked(obj/item/fish/fish, cooked_time)
SIGNAL_HANDLER
if(cooked_time >= FISH_SAFE_COOKING_DURATION)
fish.reagents.del_reagent(/datum/reagent/consumable/liquidelectricity)
else
fish.reagents.multiply_single_reagent(/datum/reagent/consumable/liquidelectricity, 0.66)
/datum/fish_trait/electrogenesis/add_reagents(obj/item/fish/fish, list/reagents)
. = ..()
if(HAS_TRAIT(fish, TRAIT_FISH_WELL_COOKED)) // Cooking it well removes all liquid electricity
reagents -= /datum/reagent/consumable/liquidelectricity
else
reagents -= /datum/reagent/blood
//Otherwise, undercooking it will remove 2/3 of it.
if(!HAS_TRAIT(fish, TRAIT_FOOD_FRIED) && !HAS_TRAIT(fish, TRAIT_FOOD_BBQ_GRILLED))
reagents[/datum/reagent/consumable/liquidelectricity] -= 1
/datum/fish_trait/electrogenesis/proc/on_force_updated(obj/item/fish/fish, weight_rank, bonus_or_malus)
SIGNAL_HANDLER
@@ -670,6 +678,15 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
return
change_venom_on_death(source, /datum/reagent/toxin/venom, 0.7, 0.3)
/datum/fish_trait/hallucinogenic
name = "Hallucinogenic"
catalog_description = "This fish is coated with hallucinogenic neurotoxin. We advise cooking it before consumption."
reagents_to_add = list(/datum/reagent/toxin/mindbreaker/fish = 1)
/datum/fish_trait/hallucinogenic/add_reagents(obj/item/fish/fish, list/reagents)
if(!HAS_TRAIT(src, TRAIT_FOOD_FRIED) && !HAS_TRAIT(src, TRAIT_FOOD_BBQ_GRILLED))
return ..()
/datum/fish_trait/ink
name = "Ink Production"
catalog_description = "This fish possess a sac that produces ink."
@@ -40,6 +40,12 @@
),
)
/obj/item/fish/sand_crab/get_fish_taste()
return list("raw crab" = 2)
/obj/item/fish/sand_crab/get_fish_taste_cooked()
return list("cooked crab" = 2)
/obj/item/fish/bumpy
name = "bump-fish"
desc = "An misshapen fish-thing all covered in stubby little tendrils"
@@ -11,6 +11,11 @@
fillet_type = /obj/item/food/fishmeat/salmon
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/sockeye_salmon/get_base_edible_reagents_to_add()
var/return_list = ..()
return_list[/datum/reagent/consumable/nutriment/fat] = 1
return return_list
/obj/item/fish/arctic_char
name = "arctic char"
desc = "A cold-water anadromous fish widespread around the Northern Hemisphere of Earth, yet it has somehow found a way here."
@@ -31,6 +31,9 @@
compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/three_eyes)
fish_traits = list(/datum/fish_trait/recessive)
/obj/item/fish/goldfish/gill/get_fish_taste()
return list("raw fish" = 2.5, "objection" = 1)
/obj/item/fish/goldfish/three_eyes
name = "three-eyed goldfish"
desc = "A goldfish with an extra half a pair of eyes. You wonder what it's been feeding on lately..."
@@ -50,6 +53,9 @@
),
)
/obj/item/fish/goldfish/three_eyes/get_fish_taste()
return list("raw fish" = 2.5, "chemical waste" = 0.5)
/obj/item/fish/goldfish/three_eyes/gill
name = "McGill"
desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case. It looks kinda different today..."
@@ -59,6 +65,9 @@
stable_population = 1
random_case_rarity = FISH_RARITY_NOPE
/obj/item/fish/goldfish/three_eyes/gill/get_fish_taste()
return list("raw fish" = 2.5, "objection" = 1)
/obj/item/fish/angelfish
name = "angelfish"
desc = "Young Angelfish often live in groups, while adults prefer solitary life. They become territorial and aggressive toward other fish when they reach adulthood."
@@ -142,6 +151,9 @@
electrogenesis_power = 6.7 MEGA JOULES
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/zipzap/get_fish_taste()
return list("raw fish" = 2, "anxiety" = 1)
/obj/item/fish/tadpole
name = "tadpole"
desc = "The larval spawn of an amphibian. A very minuscle, round creature with a long tail it uses to swim around."
@@ -167,6 +179,9 @@
RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(growth_checks))
RegisterSignal(src, COMSIG_FISH_FINISH_GROWING, PROC_REF(on_growth))
/obj/item/fish/tadpole/make_edible()
return
/obj/item/fish/tadpole/set_status(new_status, silent = FALSE)
. = ..()
if(status == FISH_DEAD)
@@ -13,7 +13,6 @@
average_size = /obj/item/fish/goldfish::average_size
average_weight = /obj/item/fish/goldfish::average_weight
required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
grind_results = null
fillet_type = null
death_text = "%SRC gently disappears."
fish_traits = list(/datum/fish_trait/no_mating) //just to be sure, these shouldn't reproduce
@@ -28,6 +27,9 @@
return
holo_area.linked.add_to_spawned(src)
/obj/item/fish/holo/make_edible(weight_val)
return
/obj/item/fish/holo/set_status(new_status, silent = FALSE)
. = ..()
if(status == FISH_DEAD)
+15
View File
@@ -37,6 +37,12 @@
RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(growth_checks))
RegisterSignal(src, COMSIG_FISH_FINISH_GROWING, PROC_REF(on_growth))
/obj/item/fish/chasm_crab/get_fish_taste()
return list("raw crab" = 2)
/obj/item/fish/chasm_crab/get_fish_taste_cooked()
return list("cooked crab" = 2)
///A chasm crab growth speed is determined by its initial weight and size, ergo bigger crabs for faster lobstrosities
/obj/item/fish/chasm_crab/update_size_and_weight(new_size = average_size, new_weight = average_weight)
. = ..()
@@ -125,6 +131,9 @@
evolution_types = list(/datum/fish_evolution/mastodon)
beauty = FISH_BEAUTY_UGLY
/obj/item/fish/boned/make_edible(weight_val)
return //it's all bones and no meat.
/obj/item/fish/lavaloop
name = "lavaloop fish"
desc = "Due to its curvature, it can be used as make-shift boomerang."
@@ -164,6 +173,12 @@
effect_on_success = /obj/effect/temp_visual/guardian/phase,\
)
/obj/item/fish/lavaloop/get_fish_taste()
return list("chewy fish" = 2)
/obj/item/fish/lavaloop/get_food_types()
return SEAFOOD|MEAT|GORE //Well-cooked in lava
/obj/item/fish/lavaloop/proc/explode_on_user(mob/living/user)
var/obj/item/bodypart/arm/active_arm = user.get_active_hand()
active_arm?.dismember()
+17
View File
@@ -26,6 +26,9 @@
fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/amphibious, /datum/fish_trait/revival, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/aggressive)
beauty = FISH_BEAUTY_BAD
/obj/item/fish/mastodon/make_edible(weight_val)
return //it's all bones and gibs.
///From the cursed spring
/obj/item/fish/soul
name = "soulfish"
@@ -50,6 +53,15 @@
required_temperature_max = MIN_AQUARIUM_TEMP+38
random_case_rarity = FISH_RARITY_NOPE
/obj/item/fish/soul/get_food_types()
return MEAT|RAW|GORE //Not-so-quite-seafood
/obj/item/fish/soul/get_fish_taste()
return list("meat" = 2, "soulfulness" = 1)
/obj/item/fish/soul/get_fish_taste_cooked()
return list("cooked meat" = 2)
///From the cursed spring
/obj/item/fish/skin_crab
name = "skin crab"
@@ -71,3 +83,8 @@
fillet_type = /obj/item/food/meat/slab/rawcrab
random_case_rarity = FISH_RARITY_NOPE
/obj/item/fish/skin_crab/get_fish_taste()
return list("raw crab" = 2)
/obj/item/fish/skin_crab/get_fish_taste_cooked()
return list("cooked crab" = 2)
+10 -1
View File
@@ -14,6 +14,9 @@
required_temperature_min = MIN_AQUARIUM_TEMP+22
required_temperature_max = MIN_AQUARIUM_TEMP+30
/obj/item/fish/clownfish/get_fish_taste()
return list("raw fish" = 2, "something funny" = 1)
/obj/item/fish/clownfish/lube
name = "lubefish"
desc = "A clownfish exposed to cherry-flavored lube for far too long. First discovered the days following a cargo incident around the seas of Europa, when thousands of thousands of thousands..."
@@ -227,9 +230,15 @@
required_temperature_max = MIN_AQUARIUM_TEMP+26
fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/ink, /datum/fish_trait/camouflage, /datum/fish_trait/wary)
/obj/item/fish/squid/get_fish_taste()
return list("raw mollusk" = 2)
/obj/item/fish/squid/get_fish_taste_cooked()
return list("cooked mollusk" = 2, "tenderness" = 0.5)
/obj/item/fish/monkfish
name = "monkfish"
desc = "A member of the Lophiid family of anglerfish. It goes by several different names, however none of them will make it look any prettier, nor any less delicious."
desc = "A member of the Lophiid family of anglerfish. It goes by several different names, however none of them will make it look any prettier, nor be any less delicious."
icon_state = "monkfish"
required_fluid_type = AQUARIUM_FLUID_SALTWATER
sprite_height = 7
+23 -1
View File
@@ -20,6 +20,20 @@
)
beauty = FISH_BEAUTY_DISGUSTING
/obj/item/fish/ratfish/get_fish_taste()
return list("vermin" = 2, "maintenance" = 1)
/obj/item/fish/ratfish/get_fish_taste_cooked()
return list("cooked vermin" = 2, "burned fur" = 0.5)
/obj/item/fish/ratfish/get_food_types()
return MEAT|RAW|GORE //Not-so-quite-seafood
/obj/item/fish/ratfish/get_base_edible_reagents_to_add()
var/list/return_list = ..()
return_list[/datum/reagent/rat_spit] = 1
return return_list
/obj/item/fish/ratfish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
//stable pop reflects the config for how many mice migrate. powerful...
@@ -43,6 +57,9 @@
evolution_types = list(/datum/fish_evolution/purple_sludgefish)
beauty = FISH_BEAUTY_NULL
/obj/item/fish/sludgefish/get_fish_taste()
return list("raw fish" = 2, "eau de toilet" = 1)
/obj/item/fish/sludgefish/purple
name = "purple sludgefish"
desc = "A misshapen, fragile, loosely fish-like living goop. This one has developed sexual reproduction mechanisms, and a purple tint to boot."
@@ -63,7 +80,6 @@
stable_population = 4
health = 150
fillet_type = /obj/item/slime_extract/grey
grind_results = list(/datum/reagent/toxin/slimejelly = 10)
fish_traits = list(/datum/fish_trait/toxin_immunity, /datum/fish_trait/crossbreeder)
favorite_bait = list(
list(
@@ -78,3 +94,9 @@
)
required_temperature_min = MIN_AQUARIUM_TEMP+20
beauty = FISH_BEAUTY_GREAT
/obj/item/fish/slimefish/get_food_types()
return SEAFOOD|TOXIC
/obj/item/fish/slimefish/get_base_edible_reagents_to_add()
return list(/datum/reagent/toxin/slimejelly = 5)
@@ -13,6 +13,9 @@
required_temperature_max = MIN_AQUARIUM_TEMP+40
beauty = FISH_BEAUTY_BAD
/obj/item/fish/emulsijack/get_fish_taste()
return list("raw fish" = 2, "acid" = 1) //no scales
/obj/item/fish/donkfish
name = "donk co. company patent donkfish"
desc = "A lab-grown donkfish. Its invention was an accident for the most part, as it was intended to be consumed in donk pockets. Unfortunately, it tastes horrible, so it has now become a pseudo-mascot."
@@ -94,6 +97,9 @@
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
/obj/item/fish/chainsawfish/get_fish_taste()
return list("raw fish" = 2.5, "anger" = 1)
/obj/item/fish/chainsawfish/update_icon_state()
if(status == FISH_DEAD)
inhand_icon_state = "chainsawfish_dead"
@@ -202,6 +208,12 @@
/obj/item/fish,
)
/obj/item/fish/pike/armored/get_fish_taste()
return list("raw fish" = 2.5, "metal" = 1)
/obj/item/fish/pike/armored/get_fish_taste()
return list("cooked fish" = 2.5, "metal" = 1)
/obj/item/fish/swordfish/get_force_rank()
switch(w_class)
if(WEIGHT_CLASS_TINY)
@@ -24,10 +24,21 @@
required_fluid_type = AQUARIUM_FLUID_SALTWATER
stable_population = 4
fillet_type = /obj/item/food/fishmeat/gunner_jellyfish
fish_traits = list(/datum/fish_trait/hallucinogenic)
required_temperature_min = MIN_AQUARIUM_TEMP+24
required_temperature_max = MIN_AQUARIUM_TEMP+32
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/gunner_jellyfish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
AddElement(/datum/element/quality_food_ingredient, FOOD_COMPLEXITY_2)
/obj/item/fish/gunner_jellyfish/get_fish_taste()
return list("cold jelly" = 2)
/obj/item/fish/gunner_jellyfish/get_fish_taste_cooked()
return list("crunchy tenderness" = 2)
/obj/item/fish/needlefish
name = "needlefish"
desc = "A tiny, transparent fish which resides in large schools in the oceans of Tizira. A common food for other, larger fish."
@@ -67,3 +78,9 @@
/obj/item/fish/armorfish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
add_traits(list(TRAIT_FISHING_BAIT, TRAIT_GOOD_QUALITY_BAIT), INNATE_TRAIT)
/obj/item/fish/chasm_crab/get_fish_taste()
return list("raw prawn" = 2)
/obj/item/fish/chasm_crab/get_fish_taste_cooked()
return list("cooked prawn" = 2)
+3
View File
@@ -704,4 +704,7 @@
override_origin_pixel_x = lefthand ? lefthand_n_px : righthand_n_px
override_origin_pixel_y = lefthand ? lefthand_n_py : righthand_n_py
override_origin_pixel_x += origin.pixel_x
override_origin_pixel_y += origin.pixel_y
#undef FISHING_ROD_REEL_CAST_RANGE
@@ -154,7 +154,7 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
reagents.trans_to(frying, oil_use * seconds_per_tick, multiplier = fry_speed * 3) //Fried foods gain more of the reagent thanks to space magic
grease_level += prob(grease_increase_chance) * grease_Increase_amount
cook_time += fry_speed * seconds_per_tick
cook_time += fry_speed * seconds_per_tick SECONDS
if(cook_time >= DEEPFRYER_COOKTIME && !frying_fried)
frying_fried = TRUE //frying... frying... fried
playsound(src.loc, 'sound/machines/ding.ogg', 50, TRUE)
@@ -245,7 +245,7 @@
// Everything else will just get fried
if(isnull(ingredient.reagents) && !is_type_in_list(ingredient, required_ingredients))
ingredient.AddElement(/datum/element/fried_item, 30)
ingredient.AddElement(/datum/element/fried_item, 30 SECONDS)
continue
// Things that had reagents or ingredients in the soup will get deleted
+1 -1
View File
@@ -48,7 +48,7 @@
/obj/item/food/grown/onion/red/make_processable()
AddElement(/datum/element/processable, TOOL_KNIFE, /obj/item/food/onion_slice/red, 2, 15, screentip_verb = "Cut")
/obj/item/food/grown/onion/UsedforProcessing(mob/living/user, obj/item/I, list/chosen_option)
/obj/item/food/grown/onion/UsedforProcessing(mob/living/user, obj/item/I, list/chosen_option, list/created_atoms)
var/datum/effect_system/fluid_spread/smoke/chem/cry_about_it = new //Since the onion is destroyed when it's sliced,
var/splat_location = get_turf(src) //we need to set up the smoke beforehand
cry_about_it.attach(splat_location)
+6 -3
View File
@@ -139,8 +139,8 @@
context[SCREENTIP_CONTEXT_LMB] = "Lock mutation"
return CONTEXTUAL_SCREENTIP_SET
// Edibles and pills can be composted.
if(IS_EDIBLE(held_item) || istype(held_item, /obj/item/reagent_containers/pill))
// Edibles can be composted (most of the times).
if(IS_EDIBLE(held_item) && HAS_TRAIT(held_item, TRAIT_UNCOMPOSTABLE))
context[SCREENTIP_CONTEXT_LMB] = "Compost"
return CONTEXTUAL_SCREENTIP_SET
@@ -855,7 +855,10 @@
var/visi_msg = ""
var/transfer_amount
if(IS_EDIBLE(reagent_source) || istype(reagent_source, /obj/item/reagent_containers/pill))
if(IS_EDIBLE(reagent_source))
if(HAS_TRAIT(reagent_source, TRAIT_UNCOMPOSTABLE))
to_chat(user, "[reagent_source] cannot be composted in its current state")
return
visi_msg="[user] composts [reagent_source], spreading it through [target]"
transfer_amount = reagent_source.reagents.total_volume
SEND_SIGNAL(reagent_source, COMSIG_ITEM_ON_COMPOSTED, user)
+13 -10
View File
@@ -52,27 +52,31 @@
return NONE
/mob/living/carbon/get_liked_foodtypes()
var/obj/item/organ/internal/tongue/tongue = get_organ_slot(ORGAN_SLOT_TONGUE)
// No tongue, no tastin'
if(!tongue?.sense_of_taste || HAS_TRAIT(src, TRAIT_AGEUSIA))
if(HAS_TRAIT(src, TRAIT_AGEUSIA))
return NONE
// Handled in here since the brain trauma can't modify taste directly (/datum/brain_trauma/severe/flesh_desire)
if(HAS_TRAIT(src, TRAIT_FLESH_DESIRE))
return GORE | MEAT
return tongue.liked_foodtypes
var/obj/item/organ/internal/tongue/tongue = get_organ_slot(ORGAN_SLOT_TONGUE)
. = tongue.liked_foodtypes
if(HAS_TRAIT(src, TRAIT_VEGETARIAN))
. &= ~MEAT
/**
* Gets food flags that this mob dislikes
**/
/mob/living/proc/get_disliked_foodtypes()
if(HAS_TRAIT(src, TRAIT_VEGETARIAN))
return MEAT
return NONE
/mob/living/carbon/get_disliked_foodtypes()
var/obj/item/organ/internal/tongue/tongue = get_organ_slot(ORGAN_SLOT_TONGUE)
// No tongue, no tastin'
if(!tongue?.sense_of_taste || HAS_TRAIT(src, TRAIT_AGEUSIA))
if(HAS_TRAIT(src, TRAIT_AGEUSIA))
return NONE
return tongue.disliked_foodtypes
var/obj/item/organ/internal/tongue/tongue = get_organ_slot(ORGAN_SLOT_TONGUE)
. = tongue.disliked_foodtypes
if(HAS_TRAIT(src, TRAIT_VEGETARIAN))
. |= MEAT
/**
* Gets food flags that this mob hates
@@ -83,9 +87,8 @@
/mob/living/carbon/get_toxic_foodtypes()
var/obj/item/organ/internal/tongue/tongue = get_organ_slot(ORGAN_SLOT_TONGUE)
// No tongue, no tastin'
if(!tongue)
return TOXIC
return ..()
if(HAS_TRAIT(src, TRAIT_FLESH_DESIRE))
return VEGETABLES | DAIRY | FRUIT | FRIED
return tongue.toxic_foodtypes
@@ -204,11 +204,12 @@
*
* * [list_reagents][list] - list to add. Format it like this: list(/datum/reagent/toxin = 10, "beer" = 15)
* * [data][list] - additional data to add
* * [added_purity][number] - an override to the default purity for each reagent to add.
*/
/datum/reagents/proc/add_reagent_list(list/list_reagents, list/data = null)
/datum/reagents/proc/add_reagent_list(list/list_reagents, list/data = null, added_purity = null)
for(var/r_id in list_reagents)
var/amt = list_reagents[r_id]
add_reagent(r_id, amt, data)
add_reagent(r_id, amt, data, added_purity = added_purity)
/**
* Removes a specific reagent. can supress reactions if needed
@@ -589,10 +590,11 @@
*/
/datum/reagents/proc/multiply_reagents(multiplier = 1)
var/list/cached_reagents = reagent_list
if(!total_volume)
if(!total_volume || multiplier == 1)
return
var/change = (multiplier - 1) //Get the % change
for(var/datum/reagent/reagent as anything in cached_reagents)
_multiply_reagent(reagent, change)
if(change > 0)
add_reagent(reagent.type, reagent.volume * change, added_purity = reagent.purity, ignore_splitting = reagent.chemical_flags & REAGENT_DONOTSPLIT)
else
@@ -601,6 +603,28 @@
update_total()
handle_reactions()
/**
* Multiplies a single inside this holder by a specific amount
* Arguments
* * reagent_path - The path of the reagent we want to multiply the volume of.
* * multiplier - the amount to multiply each reagent by
*/
/datum/reagents/proc/multiply_single_reagent(reagent_path, multiplier = 1)
var/datum/reagent/reagent = locate(reagent_path) in reagent_list
if(!reagent || multiplier == 1)
return
var/change = (multiplier - 1) //Get the % change
_multiply_reagent(reagent, change)
update_total()
handle_reactions()
///Proc containing the operations called by both multiply_reagents() and multiply_single_reagent()
/datum/reagents/proc/_multiply_reagent(datum/reagent/reagent, change)
if(change > 0)
add_reagent(reagent.type, reagent.volume * change, added_purity = reagent.purity, ignore_splitting = reagent.chemical_flags & REAGENT_DONOTSPLIT)
else
remove_reagent(reagent.type, abs(reagent.volume * change)) //absolute value to prevent a double negative situation (removing -50% would be adding 50%)
/// Updates [/datum/reagents/var/total_volume]
/datum/reagents/proc/update_total()
var/list/cached_reagents = reagent_list
@@ -166,8 +166,8 @@
return
exposed_obj.visible_message(span_warning("[exposed_obj] rapidly fries as it's splashed with hot oil! Somehow."))
exposed_obj.AddElement(/datum/element/fried_item, volume)
exposed_obj.reagents.add_reagent(src.type, reac_volume)
exposed_obj.AddElement(/datum/element/fried_item, volume SECONDS)
exposed_obj.reagents.add_reagent(src.type, reac_volume, reagtemp = holder.chem_temp)
/datum/reagent/consumable/nutriment/fat/expose_mob(mob/living/exposed_mob, methods = TOUCH, reac_volume, show_message = TRUE, touch_protection = 0)
. = ..()
@@ -326,6 +326,19 @@
else
affected_mob.adjust_hallucinations(10 SECONDS * REM * seconds_per_tick)
/datum/reagent/toxin/mindbreaker/fish
name = "Jellyfish Hallucinogen"
description = "A hallucinogen structurally similar to the mindbreaker toxin, but with weaker molecular bonds, making it easily degradeable by heat."
/datum/reagent/toxin/mindbreaker/fish/on_new(data)
. = ..()
if(holder?.my_atom)
RegisterSignals(holder.my_atom, list(COMSIG_ITEM_FRIED, TRAIT_FOOD_BBQ_GRILLED), PROC_REF(on_atom_cooked))
/datum/reagent/toxin/mindbreaker/fish/proc/on_atom_cooked(datum/source, cooking_time)
SIGNAL_HANDLER
holder.del_reagent(type)
/datum/reagent/toxin/plantbgone
name = "Plant-B-Gone"
description = "A harmful toxic mixture to kill plantlife. Do not ingest!"
@@ -41,7 +41,7 @@
required_other = TRUE
/datum/chemical_reaction/sodiumchloride/pre_reaction_other_checks(datum/reagents/holder)
. = ..()
. = ..()
if(holder.has_reagent(/datum/reagent/consumable/liquidelectricity) || holder.has_reagent(/datum/reagent/consumable/liquidelectricity/enriched))
return FALSE
@@ -990,3 +990,16 @@
var/location = get_turf(holder.my_atom)
for(var/i in 1 to created_volume)
new /obj/item/stack/sheet/hauntium(location)
/datum/chemical_reaction/fish_hallucinogen_degradation
results = list(/datum/reagent/consumable/nutriment/protein = 0.1)
required_reagents = list(/datum/reagent/toxin/mindbreaker/fish = 1)
required_temp = 363.15 // 90°
optimal_temp = 450
rate_up_lim = 8
temp_exponent_factor = 1.5
optimal_ph_min = 2
optimal_ph_max = 10
thermic_constant = 80
H_ion_release = 2
reaction_tags = REACTION_TAG_EASY
@@ -147,7 +147,7 @@
var/obj/item/food_item = new chosen(T)
ADD_TRAIT(food_item, TRAIT_FOOD_SILVER, INNATE_TRAIT)
if(prob(5))//Fry it!
food_item.AddElement(/datum/element/fried_item, rand(15, 60))
food_item.AddElement(/datum/element/fried_item, rand(15, 60) SECONDS)
if(prob(5))//Grill it!
food_item.AddElement(/datum/element/grilled_item, rand(30 SECONDS, 100 SECONDS))
if(prob(50))
@@ -595,7 +595,7 @@ GLOBAL_LIST_INIT(english_to_zombie, list())
say_mod = "meows"
liked_foodtypes = SEAFOOD | ORANGES | BUGS | GORE
disliked_foodtypes = GROSS | CLOTH | RAW
organ_traits = list(TRAIT_WOUND_LICKER)
organ_traits = list(TRAIT_WOUND_LICKER, TRAIT_FISH_EATER)
/obj/item/organ/internal/tongue/jelly
name = "jelly tongue"
+63 -4
View File
@@ -1,4 +1,5 @@
#define TRAIT_FISH_TESTING "made_you_read_this"
#define FISH_REAGENT_AMOUNT (10 * FISH_WEIGHT_GRIND_TO_BITE_MULT)
///Ensures that all fish have an aquarium icon state and that sprite_width and sprite_height have been set.
/datum/unit_test/fish_aquarium_icons
@@ -21,7 +22,10 @@
/datum/unit_test/fish_size_weight/Run()
var/obj/item/fish/fish = allocate(/obj/item/fish/testdummy)
TEST_ASSERT_EQUAL(fish.grind_results[/datum/reagent], 20, "the test fish has [fish.grind_results[/datum/reagent]] units of reagent when it should have 20")
var/datum/reagent/reagent = fish.reagents?.has_reagent(/datum/reagent/fishdummy)
TEST_ASSERT(reagent, "the test fish doesn't have the test reagent.[fish.reagents ? "" : " It doesn't even have a reagent holder."]")
var/expected_units = FISH_REAGENT_AMOUNT * fish.weight / FISH_WEIGHT_BITE_DIVISOR
TEST_ASSERT_EQUAL(reagent.volume, expected_units, "the test fish has [reagent.volume] units of the test reagent when it should have [expected_units]")
TEST_ASSERT_EQUAL(fish.w_class, WEIGHT_CLASS_BULKY, "the test fish has w_class of [fish.w_class] when it should have been [WEIGHT_CLASS_BULKY]")
var/expected_num_fillets = round(FISH_SIZE_BULKY_MAX / FISH_FILLET_NUMBER_SIZE_DIVISOR * 2, 1)
TEST_ASSERT_EQUAL(fish.num_fillets, expected_num_fillets, "the test fish has [fish.num_fillets] number of fillets when it should have [expected_num_fillets]")
@@ -93,7 +97,7 @@
incompatible_traits = list(/datum/fish_trait/dummy/two)
inheritability = 100
diff_traits_inheritability = 100
reagents_to_add = list(/datum/reagent = 10)
reagents_to_add = list(/datum/reagent/fishdummy = FISH_REAGENT_AMOUNT)
/datum/fish_trait/dummy/apply_to_fish(obj/item/fish/fish)
. = ..()
@@ -102,6 +106,10 @@
/datum/fish_trait/dummy/two
incompatible_traits = list(/datum/fish_trait/dummy)
/datum/reagent/fishdummy
name = "fish test reagent"
description = "It smells fishy."
/obj/structure/aquarium/traits
allow_breeding = TRUE
var/obj/item/fish/testdummy/crossbreeder/crossbreeder
@@ -283,12 +291,63 @@
/datum/fish_source/unit_test
fish_table = list(
/obj/item/wrench = 1,
/obj/item/screwdriver = INFINITY,
/obj/item/screwdriver = INFINITY, //infinite weight, so if fish counts doesn't work as intended, this'll be always picked.
)
fish_counts = list(
/obj/item/wrench = 1,
/obj/item/screwdriver = 0,
/obj/item/screwdriver = 0, //this should never be picked.
)
/datum/unit_test/edible_fish
/datum/unit_test/edible_fish/Run()
var/obj/item/fish/fish = allocate(/obj/item/fish/testdummy/food)
var/datum/component/edible/edible = fish.GetComponent(/datum/component/edible)
TEST_ASSERT(edible, "Fish is not edible")
edible.eat_time = 0
TEST_ASSERT(fish.GetComponent(/datum/component/infective), "Fish doesn't have the infective component")
var/bite_size = edible.bite_consumption
var/mob/living/carbon/human/consistent/gourmet = allocate(/mob/living/carbon/human/consistent)
var/food_quality = edible.get_perceived_food_quality(gourmet)
TEST_ASSERT(food_quality < 0, "Humans don't seem to dislike raw, unprocessed fish when they should")
ADD_TRAIT(gourmet, TRAIT_FISH_EATER, TRAIT_FISH_TESTING)
food_quality = edible.get_perceived_food_quality(gourmet)
TEST_ASSERT(food_quality >= LIKED_FOOD_QUALITY_CHANGE, "mobs with the TRAIT_FISH_EATER traits don't seem to like fish when they should")
REMOVE_TRAIT(gourmet, TRAIT_FISH_EATER, TRAIT_FISH_TESTING)
fish.attack(gourmet, gourmet)
TEST_ASSERT(gourmet.has_reagent(/datum/reagent/consumable/nutriment/protein), "Human doesn't have ingested protein after eating fish")
TEST_ASSERT(gourmet.has_reagent(/datum/reagent/blood), "Human doesn't have ingested blood after eating fish")
TEST_ASSERT(gourmet.has_reagent(/datum/reagent/fishdummy), "Human doesn't have the reagent from /datum/fish_trait/dummy after eating fish")
TEST_ASSERT_EQUAL(fish.status, FISH_DEAD, "The fish is not dead, despite having sustained enough damage that it should. health: [fish.health]")
var/obj/item/organ/internal/stomach/belly = gourmet.get_organ_slot(ORGAN_SLOT_STOMACH)
belly.reagents.clear_reagents()
fish.set_status(FISH_ALIVE)
TEST_ASSERT(!fish.bites_amount, "bites_amount wasn't reset after the fish revived")
fish.update_size_and_weight(fish.size, FISH_WEIGHT_BITE_DIVISOR)
fish.AddElement(/datum/element/fried_item, FISH_SAFE_COOKING_DURATION)
TEST_ASSERT_EQUAL(fish.status, FISH_DEAD, "The fish didn't die after being cooked")
TEST_ASSERT(bite_size < edible.bite_consumption, "The bite_consumption value hasn't increased after being cooked (it removes blood but doubles protein). Value: [bite_size]")
TEST_ASSERT(!(edible.foodtypes & (RAW|GORE)), "Fish still has the GORE and/or RAW foodtypes flags after being cooked")
TEST_ASSERT(!fish.GetComponent(/datum/component/infective), "Fish still has the infective component after being cooked for long enough")
food_quality = edible.get_perceived_food_quality(gourmet)
TEST_ASSERT(food_quality >= 0, "Humans still dislike fish, even when it's cooked")
fish.attack(gourmet, gourmet)
TEST_ASSERT(!gourmet.has_reagent(/datum/reagent/blood), "Human has ingested blood from eating a fish when it shouldn't since the fish has been cooked")
TEST_ASSERT(QDELETED(fish), "The fish is not being deleted, despite having sustained enough bites. Reagents volume left: [fish.reagents.total_volume]")
/obj/item/fish/testdummy/food
average_weight = FISH_WEIGHT_BITE_DIVISOR * 2 //One bite, it's death; the other, it's gone.
#undef FISH_REAGENT_AMOUNT
#undef TRAIT_FISH_TESTING
+6 -4
View File
@@ -22,7 +22,7 @@ The fishing portal generator has different modules, all of which can be unlocked
A fish's traits influence how you can catch them. Carnivore fish will ignore dough balls, and herbivore fish ignore anything that's not from botany.
Telescopic fishing rods can be bought from cargo.
Once grown from chrabs and tamed, lobstrosities can be heeded to fish on fishing spots for you.
Aquariums can be upgraded to bioelectricity generators can a specific kit. From there, you can add electric-generating fish like the anxious zip zap to generate power.
Aquariums can be upgraded to bioelectricity generators with a specific kit. From there, you can add electric-generating fish like the anxious zip zap to generate power for the station.
Getting better at fishing will net you some small additional advantages, such as receiving more information when examining a fish or a fishing spot.
The size and weight of a fish can influence the amount of reagents and fillets you can harvest from them, their force as a weapon and how easy it is to store them in containers.
While most fish make for shoddy weapons, a few, like the swordfish and the chainsawfish, can be quite powerful. In general, the bigger they are, the more forceful they get.
@@ -33,12 +33,14 @@ 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.
Depending on the kind of fish inside it and whether they're alive or dead, an aquarium can improve the beauty of the room or worsen it.
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.
In a jiffy, you can scoop tadpoles from ponds with your bare hands, place them inside aquariums and quickly raise them into frogs.
The legendary fishing hat isn't just cosmetic. Space carps (as well as young lobstrosities and frogs) do truly fear those who wear it.
Have you ever heard a lobster or crab talk? Well, neither have I, but they say they're quite the fishy punsters.
You can get an experiscanner from science to perform fish scanning experiments, which can unlock more modules for the fishing portal, as well as fishing technology nodes (better equipment) for research.
You can get an experiscanner from science to perform fish scanning experiments, which can unlock more modules for the fishing portal, as well as fishing technology nodes (better equipment) to research.
Fish is, of course, edible. Is it safe to eat raw? Well, if you've strong stomach, otherwise your best option is to cook it for a at least half a spessman minute if you don't want to catch nasty diseases.
After researching the Advanced Fishing Technology Node, you can print special fishing gloves that let you fish without having to carry around a fishing rod. There's one pair that even trains athletics on top of fishing.You can get an experiscanner from science to perform fish scanning experiments, which can unlock more modules for the fishing portal, as well as fishing technology nodes (better equipment) for research.
If you have enough credits, you can buy a set of fishing lures from cargo. Each lure allows you to catch different species of fish and won't get consumed, however they need to be spun at intervals to work.
This may sound silly, but squids and their ink sacs can be used as weapons to temporarily blind people.
This may sound silly, but squids and their ink sacs can be used to temporarily blind foes.