diff --git a/code/__DEFINES/dcs/signals/signals_fish.dm b/code/__DEFINES/dcs/signals/signals_fish.dm index b82f4b4657e..92c65e3b25e 100644 --- a/code/__DEFINES/dcs/signals/signals_fish.dm +++ b/code/__DEFINES/dcs/signals/signals_fish.dm @@ -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 diff --git a/code/__DEFINES/fish.dm b/code/__DEFINES/fish.dm index be92fed0466..7f8e8462a98 100644 --- a/code/__DEFINES/fish.dm +++ b/code/__DEFINES/fish.dm @@ -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" diff --git a/code/__DEFINES/food.dm b/code/__DEFINES/food.dm index c9b7cb43cf0..b7e5b38482d 100644 --- a/code/__DEFINES/food.dm +++ b/code/__DEFINES/food.dm @@ -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 diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 7cc8085888b..bfd46d89418 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -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" diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 1a0fc110722..0484682f913 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -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( diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 69d1a84784d..ecd8bb3d860 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -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, diff --git a/code/datums/components/food/edible.dm b/code/datums/components/food/edible.dm index c034300f982..9e2964273fd 100644 --- a/code/datums/components/food/edible.dm +++ b/code/datums/components/food/edible.dm @@ -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 diff --git a/code/datums/components/infective.dm b/code/datums/components/infective.dm index bc7cc2e6af3..5163ca69dac 100644 --- a/code/datums/components/infective.dm +++ b/code/datums/components/infective.dm @@ -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)) diff --git a/code/datums/diseases/advance/floor_diseases/carpellosis.dm b/code/datums/diseases/advance/floor_diseases/carpellosis.dm index a0482215494..cdeb6051537 100644 --- a/code/datums/diseases/advance/floor_diseases/carpellosis.dm +++ b/code/datums/diseases/advance/floor_diseases/carpellosis.dm @@ -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) diff --git a/code/datums/elements/consumable_mob.dm b/code/datums/elements/consumable_mob.dm index 1a7c67a4312..fafdb8cbcab 100644 --- a/code/datums/elements/consumable_mob.dm +++ b/code/datums/elements/consumable_mob.dm @@ -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])) diff --git a/code/datums/elements/food/fried_item.dm b/code/datums/elements/food/fried_item.dm index 2afab84d1cb..bc21e51f24c 100644 --- a/code/datums/elements/food/fried_item.dm +++ b/code/datums/elements/food/fried_item.dm @@ -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) diff --git a/code/datums/elements/food/grilled_item.dm b/code/datums/elements/food/grilled_item.dm index 74e772eb73c..6899f47faa4 100644 --- a/code/datums/elements/food/grilled_item.dm +++ b/code/datums/elements/food/grilled_item.dm @@ -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 ..() diff --git a/code/datums/mood_events/food_events.dm b/code/datums/mood_events/food_events.dm index 7d33e7e57ce..e64d975902e 100644 --- a/code/datums/mood_events/food_events.dm +++ b/code/datums/mood_events/food_events.dm @@ -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 diff --git a/code/datums/quirks/neutral_quirks/vegetarian.dm b/code/datums/quirks/neutral_quirks/vegetarian.dm index 0ade72acafe..0568e2f1e22 100644 --- a/code/datums/quirks/neutral_quirks/vegetarian.dm +++ b/code/datums/quirks/neutral_quirks/vegetarian.dm @@ -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 diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index c025428a17b..2b70cfde5f8 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -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 diff --git a/code/game/objects/items/cigarettes.dm b/code/game/objects/items/cigarettes.dm index 76be69b0375..168817bf1ff 100644 --- a/code/game/objects/items/cigarettes.dm +++ b/code/game/objects/items/cigarettes.dm @@ -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() diff --git a/code/game/objects/items/food/meatdish.dm b/code/game/objects/items/food/meatdish.dm index 5ad5cea20fa..a619be72062 100644 --- a/code/game/objects/items/food/meatdish.dm +++ b/code/game/objects/items/food/meatdish.dm @@ -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." diff --git a/code/game/objects/items/storage/boxes/food_boxes.dm b/code/game/objects/items/storage/boxes/food_boxes.dm index 86d59123c72..bac558ce3be 100644 --- a/code/game/objects/items/storage/boxes/food_boxes.dm +++ b/code/game/objects/items/storage/boxes/food_boxes.dm @@ -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" diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm index d2bb90e69e4..873b45210cd 100644 --- a/code/game/objects/items/storage/toolbox.dm +++ b/code/game/objects/items/storage/toolbox.dm @@ -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" diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index 7c964d3e50f..12a2ad2d448 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -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) diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm index c289e1d8907..8c96df6e4ac 100644 --- a/code/modules/fishing/fish/fish_traits.dm +++ b/code/modules/fishing/fish/fish_traits.dm @@ -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." diff --git a/code/modules/fishing/fish/types/air_space.dm b/code/modules/fishing/fish/types/air_space.dm index f3b985edfa7..dda3794ff4e 100644 --- a/code/modules/fishing/fish/types/air_space.dm +++ b/code/modules/fishing/fish/types/air_space.dm @@ -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" diff --git a/code/modules/fishing/fish/types/anadromous.dm b/code/modules/fishing/fish/types/anadromous.dm index ecdbda1fde7..4edbce5d0c3 100644 --- a/code/modules/fishing/fish/types/anadromous.dm +++ b/code/modules/fishing/fish/types/anadromous.dm @@ -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." diff --git a/code/modules/fishing/fish/types/freshwater.dm b/code/modules/fishing/fish/types/freshwater.dm index 4da73c94dd9..b5891e19fb9 100644 --- a/code/modules/fishing/fish/types/freshwater.dm +++ b/code/modules/fishing/fish/types/freshwater.dm @@ -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) diff --git a/code/modules/fishing/fish/types/holographic.dm b/code/modules/fishing/fish/types/holographic.dm index dfcdbeedb6b..159334002bc 100644 --- a/code/modules/fishing/fish/types/holographic.dm +++ b/code/modules/fishing/fish/types/holographic.dm @@ -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) diff --git a/code/modules/fishing/fish/types/mining.dm b/code/modules/fishing/fish/types/mining.dm index 7614f259e59..f70e557fdc8 100644 --- a/code/modules/fishing/fish/types/mining.dm +++ b/code/modules/fishing/fish/types/mining.dm @@ -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() diff --git a/code/modules/fishing/fish/types/ruins.dm b/code/modules/fishing/fish/types/ruins.dm index da9e8388c0c..a01cd062354 100644 --- a/code/modules/fishing/fish/types/ruins.dm +++ b/code/modules/fishing/fish/types/ruins.dm @@ -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) diff --git a/code/modules/fishing/fish/types/saltwater.dm b/code/modules/fishing/fish/types/saltwater.dm index 0271c7f3008..afb14436fad 100644 --- a/code/modules/fishing/fish/types/saltwater.dm +++ b/code/modules/fishing/fish/types/saltwater.dm @@ -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 diff --git a/code/modules/fishing/fish/types/station.dm b/code/modules/fishing/fish/types/station.dm index 8dd459452ab..0fa8032d979 100644 --- a/code/modules/fishing/fish/types/station.dm +++ b/code/modules/fishing/fish/types/station.dm @@ -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) diff --git a/code/modules/fishing/fish/types/syndicate.dm b/code/modules/fishing/fish/types/syndicate.dm index 7710423f4c1..c1c6eea06e4 100644 --- a/code/modules/fishing/fish/types/syndicate.dm +++ b/code/modules/fishing/fish/types/syndicate.dm @@ -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) diff --git a/code/modules/fishing/fish/types/tiziran.dm b/code/modules/fishing/fish/types/tiziran.dm index a39e82ce6c4..b6fd43709f2 100644 --- a/code/modules/fishing/fish/types/tiziran.dm +++ b/code/modules/fishing/fish/types/tiziran.dm @@ -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) diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index 122378e99e6..9b088778d57 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -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 diff --git a/code/modules/food_and_drinks/machinery/deep_fryer.dm b/code/modules/food_and_drinks/machinery/deep_fryer.dm index 322c0a42c55..92270d92fd0 100644 --- a/code/modules/food_and_drinks/machinery/deep_fryer.dm +++ b/code/modules/food_and_drinks/machinery/deep_fryer.dm @@ -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) diff --git a/code/modules/food_and_drinks/recipes/soup_mixtures.dm b/code/modules/food_and_drinks/recipes/soup_mixtures.dm index 446782d00cb..c69de62fbfc 100644 --- a/code/modules/food_and_drinks/recipes/soup_mixtures.dm +++ b/code/modules/food_and_drinks/recipes/soup_mixtures.dm @@ -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 diff --git a/code/modules/hydroponics/grown/onion.dm b/code/modules/hydroponics/grown/onion.dm index 0d33c3e1f39..4287bf9eb3e 100644 --- a/code/modules/hydroponics/grown/onion.dm +++ b/code/modules/hydroponics/grown/onion.dm @@ -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) diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 041ff97b73c..4c9dd50bcfc 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -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) diff --git a/code/modules/mob/living/taste.dm b/code/modules/mob/living/taste.dm index 72adcbb43df..28dbbc078c5 100644 --- a/code/modules/mob/living/taste.dm +++ b/code/modules/mob/living/taste.dm @@ -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 diff --git a/code/modules/reagents/chemistry/holder/holder.dm b/code/modules/reagents/chemistry/holder/holder.dm index f084a7eaa39..593bc6d4710 100644 --- a/code/modules/reagents/chemistry/holder/holder.dm +++ b/code/modules/reagents/chemistry/holder/holder.dm @@ -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 diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 5365be6fe8d..c41ab8ffca5 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -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) . = ..() diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 5e553fce962..95f73e552be 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -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!" diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index bff4d526333..8e2353555be 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -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 diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm index 13eaffca3a8..21d076e949f 100644 --- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm +++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm @@ -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)) diff --git a/code/modules/surgery/organs/internal/tongue/_tongue.dm b/code/modules/surgery/organs/internal/tongue/_tongue.dm index 7ddcfb6164f..9486389aa51 100644 --- a/code/modules/surgery/organs/internal/tongue/_tongue.dm +++ b/code/modules/surgery/organs/internal/tongue/_tongue.dm @@ -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" diff --git a/code/modules/unit_tests/fish_unit_tests.dm b/code/modules/unit_tests/fish_unit_tests.dm index e3ede57fc29..63bb5e30616 100644 --- a/code/modules/unit_tests/fish_unit_tests.dm +++ b/code/modules/unit_tests/fish_unit_tests.dm @@ -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 diff --git a/strings/fishing_tips.txt b/strings/fishing_tips.txt index eda70bb4252..31d18f10196 100644 --- a/strings/fishing_tips.txt +++ b/strings/fishing_tips.txt @@ -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. \ No newline at end of file +This may sound silly, but squids and their ink sacs can be used to temporarily blind foes. \ No newline at end of file