From 2d7b8a5ff63eb8291d740ceaf1b7c894bf4485a9 Mon Sep 17 00:00:00 2001 From: Ghom <42542238+Ghommie@users.noreply.github.com> Date: Sun, 3 Nov 2024 17:20:39 +0100 Subject: [PATCH] material fishing rods and material fish. (#87108) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## About The Pull Request The reason why I refactored material effects code is here. In this PR I add the possibility of printing fishing rods from different materials at your nearest autolathe, though it doesn't stop there. Each material has different values for (so far) fishing difficulty, casting range, experience gained and foremost the chance of catching fish made of the same material as the fishing rod used to catch it. The material the fish is made from can either increase or decrease the weight of the fish. In a many cases, material rods can also have other small effects on fishing, like removing the chances of duds even without a bait, or attracting shiny-loving fish, or passing some other fish trait checks. In a few cases, these fishing rods can have bigger, more impactful effects on fishing. That said, here's a list of more or less the effects (skipping very minor ones and those inherited from being items) and modifiers across materials so far, a few things will be added later:
Huge-ass list (out-fucking-dated now that I've added 5 more variables 😢) - Iron: +30% fish weight, 8% chance of material fish - Glass: +20% fish weight, +5 difficulty, +20% experience, 8% chance of material fish - Silver: +35% fish weight, -5 difficulty, - 15% experience, 15.5% chance of material fish - Gold: +50% fish weight, -10 difficulty, -25% experience, 20.5% material fish, +1 casting range - Diamond: +40% fish weight, -13 difficulty, -30% experience, 23% material fish, -1 casting range - Uranium: +100% fish weight, 8% material fish - Plasma: +30% fish weight, 8% material fish - Bluespace: +30% fish weight, -5 difficulty, -15% experience, 23% material fish, +5 range - - 33% chance of selecting a reward from (almost) any fishing source instead of the current one. - Bananium +20 difficulty, +60% experience, 38% material fish, +3 casting range - - 20% chance of fishing either a clownfish, lubefish, donkfish, soulfish or skin crab instead - Titanium: +20% fish weight, -5 difficulty, 8% material fish, +1 range - Runite: +50% fish weight, -18 difficulty, +220% experience, 38% material fish, +1 range - Plastic: -20% fish weight -5 difficulty, +20% experience, 8% material fish, +2 range - Wood: -50% fish weight, +8 difficulty, +30% experience, 13% material fish, -1 range - Adamantine: +60% fish weight, -23 difficulty, -40% experience, 33% material fish, +1 range - Mythril: +40% fish weight, -25 difficulty, -50% experience, 43% material fish, +2 range - Hot Ice: -10% fish weight, -10 diffiulty, -10% experience, 18% material fish, +1 range - Metal Hydrogen: -40% weight, -15 difficulty, -20% experience, 23% material, +4 range - Sand: +20% weight, +30 difficulty, -80% experience, 8% material, -2 range - Sandstone: +20% weight, +25 difficulty, -70% experience, 8% material, -2 range - Snow: -20% weight, +25 difficulty, -70% experience, 8% material, -2 range - Runed Metal: +50% weight, -12 diff, -10% experience, 38% material - Bronze, +40% weight, 13% material - Paper: -60% weight, +40 diff, -90% experience, 8% material, -2 range - - Has a 20% chance of fishing up an aggressive stickman - Cardboard: Same as above, but without the stickmen - Bone: +5% weight, +15 diff, -15% experience, 8% material, -2 range - - 20% chance of fishing up either a unmarine bonemass, a unmarine mastodon, a hostile skellie or rarely a single-use spectral instrument that can turn you or someone else into a spooky scary skeleton. - Bamboo: -50% weight, -4 difficulty, +30% experience, 13% material, -1 range - Zeukerite: 20% weight, -16 difficulty, -10% experience, 28% material - Plasteel: +75% weihgt, +5 difficulty, +10% experience, 8% material - Plastitanium: +10% weight, -10 difficulty, -5% experience, 8% material, +1 range - Plasmaglass: +20% weight, +5 difficulty, +30% experience, 8% material - Titanium Glass: +25% weight, -5 difficulty, +25% experience 8% material - Plastitanium Glass: +20% weight, +50% experience, 8% material - Alien Alloy: +140% weight, -25 difficulty, 33% material, -40% experience, +2 range - Hauntium (good luck using it): +40% weight, -30 difficulty, +50% experience, 38% material, +2 range - Meat: +13 difficulty, -20% experience, 28% material, -2 range - Pizza: -10% weight, +13 difficulty, -20% experience, 28% material, -2 range
## Why It's Good For The Game I think it's nice to have two features interact with each other (fishing and materials), and it adds a layer of interesting uses to some materials. ## Changelog :cl: add: Autolathes can now print fishing rods of different materials, which can increase or decrease fishing difficulty, casting range, and experience gained, and can have other interesting effects. /:cl: --- code/__DEFINES/construction/material.dm | 2 + code/__DEFINES/dcs/signals/signals_fish.dm | 9 +- code/__DEFINES/fish.dm | 33 +- code/__DEFINES/traits/declarations.dm | 10 +- code/_globalvars/traits/_traits.dm | 5 +- code/_globalvars/traits/admin_tooling.dm | 4 + code/datums/elements/shiny_bait.dm | 34 ++ code/datums/elements/spooky.dm | 14 +- code/datums/materials/_material.dm | 28 +- code/datums/materials/alloys.dm | 52 +++ code/datums/materials/basemats.dm | 362 +++++++++++++++++- code/datums/materials/hauntium.dm | 7 + code/datums/materials/meat.dm | 9 + code/datums/materials/pizza.dm | 10 + code/game/atom/atom_materials.dm | 29 +- code/game/objects/items.dm | 4 +- code/modules/fishing/bait.dm | 26 +- code/modules/fishing/fish/_fish.dm | 132 ++++++- code/modules/fishing/fish/fish_traits.dm | 58 ++- code/modules/fishing/fish/types/air_space.dm | 2 +- code/modules/fishing/fish/types/mining.dm | 6 +- code/modules/fishing/fish/types/ruins.dm | 4 + code/modules/fishing/fish/types/station.dm | 2 +- code/modules/fishing/fishing_equipment.dm | 70 +++- code/modules/fishing/fishing_minigame.dm | 41 +- code/modules/fishing/fishing_rod.dm | 171 ++++++++- code/modules/fishing/sources/_fish_source.dm | 15 +- code/modules/fishing/sources/source_types.dm | 18 +- code/modules/instruments/items.dm | 18 +- .../reagents/drinks/drink_reagents.dm | 6 + .../chemistry/reagents/toxin_reagents.dm | 5 + .../designs/autolathe/service_designs.dm | 13 +- icons/obj/fishing.dmi | Bin 28941 -> 31186 bytes strings/fishing_tips.txt | 7 +- tgstation.dme | 1 + 35 files changed, 1065 insertions(+), 142 deletions(-) create mode 100644 code/datums/elements/shiny_bait.dm diff --git a/code/__DEFINES/construction/material.dm b/code/__DEFINES/construction/material.dm index 2764c0bbfaf..a8bef8cbec9 100644 --- a/code/__DEFINES/construction/material.dm +++ b/code/__DEFINES/construction/material.dm @@ -58,6 +58,8 @@ #define MATERIAL_AFFECT_STATISTICS (1<<3) /// Applies the material greyscale color to the atom's greyscale color. #define MATERIAL_GREYSCALE (1<<4) +/// Materials like plasteel and alien alloy won't apply slowdowns. +#define MATERIAL_NO_SLOWDOWN (1<<5) //Special return values of [/datum/component/material_container/insert_item] /// No material was found inside them item diff --git a/code/__DEFINES/dcs/signals/signals_fish.dm b/code/__DEFINES/dcs/signals/signals_fish.dm index f7606b63a01..494d0541326 100644 --- a/code/__DEFINES/dcs/signals/signals_fish.dm +++ b/code/__DEFINES/dcs/signals/signals_fish.dm @@ -32,6 +32,8 @@ ///From /obj/item/fish/interact_with_atom_secondary, sent to the target: (fish) #define COMSIG_FISH_RELEASED_INTO "fish_released_into" +///From /datum/fishing_challenge/New: (datum/fishing_challenge/challenge) +#define COMSIG_ROD_BEGIN_FISHING "rod_begin_fishing" ///From /datum/fishing_challenge/New: (datum/fishing_challenge/challenge) #define COMSIG_MOB_BEGIN_FISHING "mob_begin_fishing" ///From /datum/fishing_challenge/start_minigame_phase: (datum/fishing_challenge/challenge) @@ -62,8 +64,11 @@ #define COMSIG_FISHING_ROD_CAUGHT_FISH "fishing_rod_caught_fish" /// From /obj/item/fishing_rod/proc/hook_item(): (reward, user) #define COMSIG_FISHING_ROD_HOOKED_ITEM "fishing_rod_hooked_item" -/// From /datum/fish_source/proc/use_slot(), sent to the slotted item: (obj/item/fishing_rod/rod) -#define COMSIG_FISHING_EQUIPMENT_SLOTTED "fishing_equipment_slotted" + +/// From /obj/item/fishing_rod/set_slot: (obj/item/fishing_rod/rod, slot) +#define COMSIG_ITEM_FISHING_ROD_SLOTTED "item_fishing_rod_slotted" +/// From /obj/item/fishing_rod/Exited: (obj/item/fishing_rod/rod, slot) +#define COMSIG_ITEM_FISHING_ROD_UNSLOTTED "item_fishing_rod_unslotted" /// Sent when the challenge is to be interrupted: (reason) #define COMSIG_FISHING_SOURCE_INTERRUPT_CHALLENGE "fishing_spot_interrupt_challenge" diff --git a/code/__DEFINES/fish.dm b/code/__DEFINES/fish.dm index 43e0e8587f9..1eb3df686ed 100644 --- a/code/__DEFINES/fish.dm +++ b/code/__DEFINES/fish.dm @@ -33,30 +33,25 @@ // through the fishing rod, and not the hook itself. They could probably be // handled differently, but for now that's how they work. It's grounds for // a future refactor, however. -/// Fishing hook trait that signifies that it's shiny. Useful for fishes -/// that care about shiner hooks more. -#define FISHING_HOOK_SHINY (1 << 0) /// Fishing hook trait that lessens the bounce from hitting the edges of the minigame bar. -#define FISHING_HOOK_WEIGHTED (1 << 1) +#define FISHING_HOOK_WEIGHTED (1 << 0) ///See FISHING_MINIGAME_RULE_BIDIRECTIONAL -#define FISHING_HOOK_BIDIRECTIONAL (1 << 2) +#define FISHING_HOOK_BIDIRECTIONAL (1 << 1) ///Prevents the user from losing the game by letting the fish get away. -#define FISHING_HOOK_NO_ESCAPE (1 << 3) +#define FISHING_HOOK_NO_ESCAPE (1 << 2) ///Limits the completion loss of the minigame when the fsh is not on the bait area. -#define FISHING_HOOK_ENSNARE (1 << 4) +#define FISHING_HOOK_ENSNARE (1 << 3) ///Automatically kills the fish after a while, at the cost of killing it. -#define FISHING_HOOK_KILL (1 << 5) +#define FISHING_HOOK_KILL (1 << 4) ///Reduces the difficulty of the minigame #define FISHING_LINE_CLOAKED (1 << 0) -///Required to cast a line on lava. -#define FISHING_LINE_REINFORCED (1 << 1) /// Much like FISHING_HOOK_ENSNARE but for the fishing line. -#define FISHING_LINE_BOUNCY (1 << 2) +#define FISHING_LINE_BOUNCY (1 << 1) /// The sorta opposite of FISHING_LINE_BOUNCY. It makes it slower to gain completion and faster to lose it. -#define FISHING_LINE_STIFF (1 << 3) +#define FISHING_LINE_STIFF (1 << 2) ///Skip the biting phase and go straight to the fishing phase. -#define FISHING_LINE_AUTOREEL (1 << 4) +#define FISHING_LINE_AUTOREEL (1 << 3) ///Keeps the bait from falling from gravity, instead allowing the player to move the bait down with right click. #define FISHING_MINIGAME_RULE_BIDIRECTIONAL (1 << 0) @@ -161,6 +156,9 @@ #define FISH_FLAG_PETTED (1<<2) ///This fish can be scanned to complete fish scanning experiments #define FISH_FLAG_EXPERIMENT_SCANNABLE (1<<3) +///It lets us know that fish/update_size_and_weight() is currently running. +#define FISH_FLAG_UPDATING_SIZE_AND_WEIGHT (1<<3) + #define MIN_AQUARIUM_TEMP T0C #define MAX_AQUARIUM_TEMP (T0C + 100) @@ -226,6 +224,15 @@ ///We multiply the weight of fish inside the loot table by this value if we are goofy enough to fish without a bait. #define FISH_WEIGHT_MULT_WITHOUT_BAIT 0.15 + +/** + * Flag for fish sources. It makes large explosions less efficient at spawning fish. + * Meant for lazy fishing spots that cover multiple turfs (rivers, oceans etc.) + */ +#define FISH_SOURCE_FLAG_EXPLOSIVE_MALUS (1<<0) +/// The fish source is not elegible for random rewards from bluespace fishing rods +#define FISH_SOURCE_FLAG_NO_BLUESPACE_ROD (1<<1) + /** * A macro to ensure the wikimedia filenames of fish icons are unique, especially since there're a couple fish that have * quite ambiguous names/icon_states like "checkered" or "pike" diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 08e54d92eed..f4f5688819b 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -787,6 +787,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_UNCATCHABLE "uncatchable" /// You won't catch duds while fishing with this rod. #define TRAIT_ROD_REMOVE_FISHING_DUD "rod_remove_fishing_dud" +/// This rod ignores environmental conditions for fishing (like low light for nocturnal fish) +#define TRAIT_ROD_IGNORE_ENVIRONMENT "rod_ignore_environment" +/// This rod attracts fish with the shiny lover fish trait +#define TRAIT_ROD_ATTRACT_SHINY_LOVERS "rod_attract_shiny_lovers" +/// This rod can be used to fish on lava +#define TRAIT_ROD_LAVA_USABLE "rod_lava_usable" /// Stuff that can go inside fish cases #define TRAIT_FISH_CASE_COMPATIBILE "fish_case_compatibile" /// If the item can be used as a bit. @@ -801,8 +807,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_OMNI_BAIT "omni_bait" /// The bait won't be consumed when used #define TRAIT_BAIT_UNCONSUMABLE "bait_unconsumable" -/// This bait ignores environmental conditions for fishing (like low light for nocturnal fish) -#define TRAIT_BAIT_IGNORE_ENVIRONMENT "bait_ignore_environment" /** * This bait won't apply TRAIT_ROD_REMOVE_FISHING_DUD to the rod it's attached on, * instead, it'll allow the fishing dud to be there unless there's at least one fish that likes the bait @@ -1023,6 +1027,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_FISH_SHOULD_TWOHANDED "fish_should_twohanded" ///This fish won't be killed when cooked. #define TRAIT_FISH_SURVIVE_COOKING "fish_survive_cooking" +///This fish is healed by milk and hurt by bone hurting juice +#define TRAIT_FISH_MADE_OF_BONE "fish_made_of_bone" /** * This fish has been fed teslium without the electrogenesis having trait. * Gives the electrogenesis, but at halved output, and it hurts the fish over time. diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index b0d5e835907..46a91854931 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -573,7 +573,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( /obj/item = list( "TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING, "TRAIT_BAIT_ALLOW_FISHING_DUD" = TRAIT_BAIT_ALLOW_FISHING_DUD, - "TRAIT_BAIT_IGNORE_ENVIRONMENT" = TRAIT_BAIT_IGNORE_ENVIRONMENT, "TRAIT_BAIT_UNCONSUMABLE" = TRAIT_BAIT_UNCONSUMABLE, "TRAIT_BAKEABLE" = TRAIT_BAKEABLE, "TRAIT_BASIC_QUALITY_BAIT" = TRAIT_BASIC_QUALITY_BAIT, @@ -638,6 +637,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_FISH_FLOPPING" = TRAIT_FISH_FLOPPING, "TRAIT_FISH_FROM_CASE" = TRAIT_FISH_FROM_CASE, "TRAIT_FISH_INK_ON_COOLDOWN" = TRAIT_FISH_INK_ON_COOLDOWN, + "TRAIT_FISH_MADE_OF_BONE" = TRAIT_FISH_MADE_OF_BONE, "TRAIT_FISH_MUTAGENIC" = TRAIT_FISH_MUTAGENIC, "TRAIT_FISH_NO_HUNGER" = TRAIT_FISH_NO_HUNGER, "TRAIT_FISH_NO_MATING" = TRAIT_FISH_NO_MATING, @@ -655,6 +655,9 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_YUCKY_FISH" = TRAIT_YUCKY_FISH, ), /obj/item/fishing_rod = list( + "TRAIT_ROD_ATTRACT_SHINY_LOVERS" = TRAIT_ROD_ATTRACT_SHINY_LOVERS, + "TRAIT_ROD_IGNORE_ENVIRONMENT" = TRAIT_ROD_IGNORE_ENVIRONMENT, + "TRAIT_ROD_LAVA_USABLE" = TRAIT_ROD_LAVA_USABLE, "TRAIT_ROD_REMOVE_FISHING_DUD" = TRAIT_ROD_REMOVE_FISHING_DUD, ), /obj/item/integrated_circuit = list( diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 31c88b492d6..9d1b47800b8 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -357,6 +357,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_FISH_FED_LUBE" = TRAIT_FISH_FED_LUBE, "TRAIT_FISH_FROM_CASE" = TRAIT_FISH_FROM_CASE, "TRAIT_FISH_INK_ON_COOLDOWN" = TRAIT_FISH_INK_ON_COOLDOWN, + "TRAIT_FISH_MADE_OF_BONE" = TRAIT_FISH_MADE_OF_BONE, "TRAIT_FISH_MUTAGENIC" = TRAIT_FISH_MUTAGENIC, "TRAIT_FISH_NO_HUNGER" = TRAIT_FISH_NO_HUNGER, "TRAIT_FISH_NO_MATING" = TRAIT_FISH_NO_MATING, @@ -373,6 +374,9 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_YUCKY_FISH" = TRAIT_YUCKY_FISH, ), /obj/item/fishing_rod = list( + "TRAIT_ROD_ATTRACT_SHINY_LOVERS" = TRAIT_ROD_ATTRACT_SHINY_LOVERS, + "TRAIT_ROD_IGNORE_ENVIRONMENT" = TRAIT_ROD_IGNORE_ENVIRONMENT, + "TRAIT_ROD_LAVA_USABLE" = TRAIT_ROD_LAVA_USABLE, "TRAIT_ROD_REMOVE_FISHING_DUD" = TRAIT_ROD_REMOVE_FISHING_DUD, ), /obj/item/organ/liver = list( diff --git a/code/datums/elements/shiny_bait.dm b/code/datums/elements/shiny_bait.dm new file mode 100644 index 00000000000..72b34ec8808 --- /dev/null +++ b/code/datums/elements/shiny_bait.dm @@ -0,0 +1,34 @@ +///A component that gives fishing rod TRAIT_ROD_ATTRACT_SHINY_LOVERS when the attached item is used as bait. +/datum/element/shiny_bait + +/datum/element/shiny_bait/Attach(obj/item/target) + . = ..() + if(!isitem(target)) + return ELEMENT_INCOMPATIBLE + RegisterSignal(target, COMSIG_ITEM_FISHING_ROD_SLOTTED, PROC_REF(on_fishing_rod_slotted)) + RegisterSignal(target, COMSIG_ITEM_FISHING_ROD_UNSLOTTED, PROC_REF(on_fishing_rod_unslotted)) + if(istype(target.loc, /obj/item/fishing_rod)) + ADD_TRAIT(target.loc, TRAIT_ROD_ATTRACT_SHINY_LOVERS, REF(src)) + else if(istype(target, /obj/item/fishing_rod)) + ADD_TRAIT(target, TRAIT_ROD_ATTRACT_SHINY_LOVERS, "[REF(src)]_rod") + +/datum/element/shiny_bait/Detach(obj/item/source) + UnregisterSignal(source, list( + COMSIG_ITEM_FISHING_ROD_SLOTTED, + COMSIG_ITEM_FISHING_ROD_UNSLOTTED, + )) + if(istype(source.loc, /obj/item/fishing_rod)) + REMOVE_TRAIT(source.loc, TRAIT_ROD_ATTRACT_SHINY_LOVERS, REF(src)) + else if(istype(source, /obj/item/fishing_rod)) + REMOVE_TRAIT(source, TRAIT_ROD_ATTRACT_SHINY_LOVERS, "[REF(src)]_rod") + return ..() + +/datum/element/shiny_bait/proc/on_fishing_rod_slotted(datum/source, obj/item/fishing_rod/rod, slot) + SIGNAL_HANDLER + if(slot == ROD_SLOT_BAIT) + ADD_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS, REF(source)) + +/datum/element/shiny_bait/proc/on_fishing_rod_unslotted(datum/source, obj/item/fishing_rod/rod, slot) + SIGNAL_HANDLER + if(slot == ROD_SLOT_BAIT) + REMOVE_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS, REF(source)) diff --git a/code/datums/elements/spooky.dm b/code/datums/elements/spooky.dm index 89d53c4e997..62189371b61 100644 --- a/code/datums/elements/spooky.dm +++ b/code/datums/elements/spooky.dm @@ -1,13 +1,17 @@ /datum/element/spooky element_flags = ELEMENT_BESPOKE argument_hash_start_idx = 2 - var/too_spooky = TRUE //will it spawn a new instrument? + ///will it spawn a new instrument + var/too_spooky = TRUE + ///Once used, the element is detached + var/single_use = FALSE -/datum/element/spooky/Attach(datum/target, too_spooky = TRUE) +/datum/element/spooky/Attach(datum/target, too_spooky = TRUE, single_use = FALSE) . = ..() if(!isitem(target)) return ELEMENT_INCOMPATIBLE src.too_spooky = too_spooky + src.single_use = single_use RegisterSignal(target, COMSIG_ITEM_ATTACK, PROC_REF(spectral_attack)) /datum/element/spooky/Detach(datum/source) @@ -26,6 +30,9 @@ if(U.getStaminaLoss() > 95) to_chat(U, "Your ears weren't meant for this spectral sound.") INVOKE_ASYNC(src, PROC_REF(spectral_change), U) + if(single_use) + to_chat(user, span_warning("You feel like [source] has lost its spookiness...")) + Detach(source) return if(ishuman(C)) @@ -42,6 +49,9 @@ to_chat(C, "DOOT") to_chat(C, span_robot("You're feeling more bony.")) INVOKE_ASYNC(src, PROC_REF(spectral_change), H) + if(single_use) + to_chat(user, span_warning("You feel like [source] has lost its spookiness...")) + Detach(source) else //the sound will spook monkeys. C.set_jitter_if_lower(30 SECONDS) diff --git a/code/datums/materials/_material.dm b/code/datums/materials/_material.dm index cff0ea0e6d1..d179be2dc01 100644 --- a/code/datums/materials/_material.dm +++ b/code/datums/materials/_material.dm @@ -78,6 +78,26 @@ Simple datum which is instanced once per type and is used for every object of sa /// The slowdown that is added to items. var/added_slowdown = 0 + /// Fish made of or infused with this material have their weight multiplied by this value. + var/fish_weight_modifier = 1 + + /// Additive bonus/malus to the fishing difficulty modifier of any rod made of this item. Negative is good, positive bad + var/fishing_difficulty_modifier = 0 + /// Additive bonus/malus to the cast range of the fishing rod + var/fishing_cast_range = 0 + /// The multiplier of how much experience is gained when using a fishing rod made of this material + var/fishing_experience_multiplier = 1 + /// The multiplier to the completion gain of the fishing rod made of this material + var/fishing_completion_speed = 1 + /// The multiplier of the bait/bobber speed of the fishing challenge for fishing rods made of this material + var/fishing_bait_speed_mult = 1 + /// The multiplier of the deceleration/friction for fishing rods made of this material + var/fishing_deceleration_mult = 1 + /// The multiplier of the bounciness of the bait/bobber upon hitting the edges of the minigame area + var/fishing_bounciness_mult = 1 + /// The multiplier of negative velocity that pulls the bait/bobber of a fishing rod down when not holding the click + var/fishing_gravity_mult = 1 + /** Handles initializing the material. * * Arguments: @@ -100,7 +120,9 @@ Simple datum which is instanced once per type and is used for every object of sa ///This proc is called when the material becomes the one the object is composed of the most /datum/material/proc/on_main_applied(atom/source, mat_amount, multiplier) - return + SHOULD_CALL_PARENT(TRUE) + if(beauty_modifier >= 0.15 && HAS_TRAIT(source, TRAIT_FISHING_BAIT)) + source.AddElement(/datum/element/shiny_bait) /datum/material/proc/setup_glow(turf/on) if(GET_TURF_PLANE_OFFSET(on) != GET_LOWEST_STACK_OFFSET(on.z)) // We ain't the bottom brother @@ -126,7 +148,9 @@ Simple datum which is instanced once per type and is used for every object of sa ///This proc is called when the material is no longer the one the object is composed by the most /datum/material/proc/on_main_removed(atom/source, mat_amount, multiplier) - return + SHOULD_CALL_PARENT(TRUE) + if(beauty_modifier >= 0.15 && HAS_TRAIT(source, TRAIT_FISHING_BAIT)) + source.RemoveElement(/datum/element/shiny_bait) /** * This proc is called when the mat is found in an item that's consumed by accident. see /obj/item/proc/on_accidental_consumption. diff --git a/code/datums/materials/alloys.dm b/code/datums/materials/alloys.dm index d13a88c49c3..d8c107d21aa 100644 --- a/code/datums/materials/alloys.dm +++ b/code/datums/materials/alloys.dm @@ -42,6 +42,20 @@ composition = list(/datum/material/iron=1, /datum/material/plasma=1) mat_rust_resistance = RUST_RESISTANCE_REINFORCED added_slowdown = 0.05 + fish_weight_modifier = 1.75 + fishing_difficulty_modifier = 5 + fishing_experience_multiplier = 1.1 + fishing_gravity_mult = 1.6 + +/datum/material/alloy/plasteel/on_applied(atom/target, mat_amount, multiplier) + . = ..() + if(istype(target, /obj/item/fishing_rod)) + ADD_TRAIT(target, TRAIT_ROD_LAVA_USABLE, REF(src)) + +/datum/material/alloy/plasteel/on_removed(atom/target, mat_amount, multiplier) + . = ..() + if(istype(target, /obj/item/fishing_rod)) + REMOVE_TRAIT(target, TRAIT_ROD_LAVA_USABLE, REF(src)) /** Plastitanium * @@ -65,6 +79,20 @@ ) composition = list(/datum/material/titanium=1, /datum/material/plasma=1) mat_rust_resistance = RUST_RESISTANCE_TITANIUM + fish_weight_modifier = 1.1 + fishing_difficulty_modifier = -10 + fishing_cast_range = 1 + fishing_experience_multiplier = 0.95 + +/datum/material/alloy/plastitanium/on_applied(atom/target, mat_amount, multiplier) + . = ..() + if(istype(target, /obj/item/fishing_rod)) + ADD_TRAIT(target, TRAIT_ROD_LAVA_USABLE, REF(src)) + +/datum/material/alloy/plastitanium/on_removed(atom/target, mat_amount, multiplier) + . = ..() + if(istype(target, /obj/item/fishing_rod)) + REMOVE_TRAIT(target, TRAIT_ROD_LAVA_USABLE, REF(src)) /** Plasmaglass * @@ -90,6 +118,10 @@ MAT_CATEGORY_ITEM_MATERIAL_COMPLEMENTARY = TRUE, ) composition = list(/datum/material/glass=1, /datum/material/plasma=0.5) + fish_weight_modifier = 1.2 + fishing_difficulty_modifier = 5 + fishing_experience_multiplier = 1.3 + fishing_gravity_mult = 0.9 /** Titaniumglass * @@ -114,6 +146,10 @@ MAT_CATEGORY_ITEM_MATERIAL_COMPLEMENTARY = TRUE, ) composition = list(/datum/material/glass=1, /datum/material/titanium=0.5) + fish_weight_modifier = 1.25 + fishing_difficulty_modifier = -5 + fishing_experience_multiplier = 1.25 + fishing_gravity_mult = 0.95 /** Plastitanium Glass * @@ -139,6 +175,9 @@ MAT_CATEGORY_ITEM_MATERIAL_COMPLEMENTARY = TRUE, ) composition = list(/datum/material/glass=1, /datum/material/alloy/plastitanium=0.5) + fish_weight_modifier = 1.2 + fishing_experience_multiplier = 1.5 + fishing_gravity_mult = 0.9 /** Alien Alloy * @@ -164,13 +203,26 @@ ) composition = list(/datum/material/iron=2, /datum/material/plasma=2) added_slowdown = 0.1 + fish_weight_modifier = 2.4 + fishing_difficulty_modifier = -25 + fishing_cast_range = 2 + fishing_experience_multiplier = 0.5 + fishing_completion_speed = 2 + fishing_bait_speed_mult = 1.25 + fishing_deceleration_mult = 1.5 + fishing_bounciness_mult = 0.5 + fishing_gravity_mult = 2 /datum/material/alloy/alien/on_applied(atom/target, mat_amount, multiplier) . = ..() if(isobj(target)) target.AddElement(/datum/element/obj_regen, _rate=0.02) // 2% regen per tick. + if(istype(target, /obj/item/fishing_rod)) + ADD_TRAIT(target, TRAIT_ROD_LAVA_USABLE, REF(src)) /datum/material/alloy/alien/on_removed(atom/target, mat_amount, multiplier) . = ..() if(isobj(target)) target.RemoveElement(/datum/element/obj_regen, _rate=0.02) + if(istype(target, /obj/item/fishing_rod)) + REMOVE_TRAIT(target, TRAIT_ROD_LAVA_USABLE, REF(src)) diff --git a/code/datums/materials/basemats.dm b/code/datums/materials/basemats.dm index 76d44c1f164..f21759f42a7 100644 --- a/code/datums/materials/basemats.dm +++ b/code/datums/materials/basemats.dm @@ -19,6 +19,8 @@ minimum_value_override = 0 tradable = TRUE tradable_base_quantity = MATERIAL_QUANTITY_COMMON + fish_weight_modifier = 1.3 + fishing_gravity_mult = 1.1 /datum/material/iron/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.apply_damage(10, BRUTE, BODY_ZONE_HEAD, wound_bonus = 5) @@ -51,6 +53,13 @@ mineral_rarity = MATERIAL_RARITY_COMMON points_per_unit = 1 / SHEET_MATERIAL_AMOUNT texture_layer_icon_state = "shine" + fish_weight_modifier = 1.2 + fishing_difficulty_modifier = 5 + fishing_experience_multiplier = 1.2 + fishing_bait_speed_mult = 0.9 + fishing_deceleration_mult = 1.2 + fishing_bounciness_mult = 0.5 + fishing_gravity_mult = 0.9 /datum/material/glass/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.apply_damage(10, BRUTE, BODY_ZONE_HEAD, wound_bonus = 5, sharpness = TRUE) //cronch @@ -92,6 +101,14 @@ Unless you know what you're doing, only use the first three numbers. They're in mineral_rarity = MATERIAL_RARITY_SEMIPRECIOUS points_per_unit = 16 / SHEET_MATERIAL_AMOUNT texture_layer_icon_state = "shine" + fish_weight_modifier = 1.35 + fishing_difficulty_modifier = -5 + fishing_experience_multiplier = 0.85 + fishing_completion_speed = 1.1 + fishing_bait_speed_mult = 1.1 + fishing_deceleration_mult = 1.1 + fishing_bounciness_mult = 0.9 + fishing_gravity_mult = 1.15 /datum/material/silver/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.apply_damage(10, BRUTE, BODY_ZONE_HEAD, wound_bonus = 5) @@ -120,6 +137,15 @@ Unless you know what you're doing, only use the first three numbers. They're in mineral_rarity = MATERIAL_RARITY_PRECIOUS points_per_unit = 18 / SHEET_MATERIAL_AMOUNT texture_layer_icon_state = "shine" + fish_weight_modifier = 1.5 + fishing_difficulty_modifier = -10 + fishing_cast_range = 1 + fishing_experience_multiplier = 0.75 + fishing_completion_speed = 1.2 + fishing_bait_speed_mult = 1.3 + fishing_deceleration_mult = 1.2 + fishing_bounciness_mult = 0.8 + fishing_gravity_mult = 1.25 /datum/material/gold/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.apply_damage(10, BRUTE, BODY_ZONE_HEAD, wound_bonus = 5) @@ -142,12 +168,23 @@ Unless you know what you're doing, only use the first three numbers. They're in alpha = 132 starlight_color = COLOR_BLUE_LIGHT value_per_unit = 500 / SHEET_MATERIAL_AMOUNT + strength_modifier = 1.1 + integrity_modifier = 1.25 tradable = TRUE tradable_base_quantity = MATERIAL_QUANTITY_EXOTIC beauty_modifier = 0.3 armor_modifiers = list(MELEE = 1.3, BULLET = 1.3, LASER = 0.6, ENERGY = 1, BOMB = 1.2, BIO = 1, FIRE = 1, ACID = 1) mineral_rarity = MATERIAL_RARITY_RARE points_per_unit = 50 / SHEET_MATERIAL_AMOUNT + fish_weight_modifier = 1.4 + fishing_difficulty_modifier = -13 + fishing_cast_range = -1 + fishing_experience_multiplier = 0.7 + fishing_completion_speed = 1.25 + fishing_bait_speed_mult = 1.3 + fishing_deceleration_mult = 1.25 + fishing_bounciness_mult = 0.8 + fishing_gravity_mult = 1.2 /datum/material/diamond/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.apply_damage(15, BRUTE, BODY_ZONE_HEAD, wound_bonus = 7) @@ -174,6 +211,12 @@ Unless you know what you're doing, only use the first three numbers. They're in armor_modifiers = list(MELEE = 1.5, BULLET = 1.4, LASER = 0.5, ENERGY = 0.5, FIRE = 1, ACID = 1) mineral_rarity = MATERIAL_RARITY_SEMIPRECIOUS points_per_unit = 30 / SHEET_MATERIAL_AMOUNT + fish_weight_modifier = 2 + fishing_completion_speed = 0.9 + fishing_bait_speed_mult = 0.8 + fishing_deceleration_mult = 1.4 + fishing_bounciness_mult = 0.6 + fishing_gravity_mult = 1.6 /datum/material/uranium/on_applied(atom/source, mat_amount, multiplier) . = ..() @@ -217,18 +260,25 @@ Unless you know what you're doing, only use the first three numbers. They're in armor_modifiers = list(MELEE = 1.4, BULLET = 0.7, ENERGY = 1.2, BIO = 1.2, ACID = 0.5) mineral_rarity = MATERIAL_RARITY_PRECIOUS points_per_unit = 15 / SHEET_MATERIAL_AMOUNT + fish_weight_modifier = 1.3 + fishing_deceleration_mult = 1.3 + fishing_bounciness_mult = 0.6 /datum/material/plasma/on_applied(atom/source, mat_amount, multiplier) . = ..() if(ismovable(source)) source.AddElement(/datum/element/firestacker, 1 * multiplier) source.AddComponent(/datum/component/combustible_flooder, "plasma", mat_amount * 0.05 * multiplier) //Empty temp arg, fully dependent on whatever ignited it. + if(istype(source, /obj/item/fishing_rod)) + ADD_TRAIT(source, TRAIT_ROD_LAVA_USABLE, REF(src)) /datum/material/plasma/on_removed(atom/source, mat_amount, multiplier) . = ..() source.RemoveElement(/datum/element/firestacker, mat_amount = 1 * multiplier) qdel(source.GetComponent(/datum/component/combustible_flooder)) qdel(source.GetComponent(/datum/component/explodable)) + if(istype(source, /obj/item/fishing_rod)) + ADD_TRAIT(source, TRAIT_ROD_LAVA_USABLE, REF(src)) /datum/material/plasma/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.reagents.add_reagent(/datum/reagent/toxin/plasma, rand(6, 8)) @@ -258,6 +308,35 @@ Unless you know what you're doing, only use the first three numbers. They're in tradable = TRUE tradable_base_quantity = MATERIAL_QUANTITY_EXOTIC texture_layer_icon_state = "shine" + fish_weight_modifier = 1.3 + fishing_difficulty_modifier = -5 + fishing_cast_range = 5 //space-bending scifi magic + fishing_experience_multiplier = 0.85 + fishing_completion_speed = 1.1 + fishing_bait_speed_mult = 1.2 + fishing_deceleration_mult = 0.9 + fishing_bounciness_mult = 1.1 + +/datum/material/bluespace/on_main_applied(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + RegisterSignal(source, COMSIG_ROD_BEGIN_FISHING, PROC_REF(on_begin_fishing)) + +/datum/material/bluespace/proc/on_begin_fishing(obj/item/fishing_rod/rod, datum/fishing_challenge/challenge) + SIGNAL_HANDLER + if(prob(67)) + return + var/list/elegible_fish_sources = flatten_list(GLOB.preset_fish_sources) + for(var/datum/fish_source/source as anything in elegible_fish_sources) + if(source.fish_source_flags & FISH_SOURCE_FLAG_NO_BLUESPACE_ROD) + elegible_fish_sources -= source + var/datum/fish_source/new_source = pick(elegible_fish_sources) + challenge.register_reward_signals(new_source) + +/datum/material/bluespace/on_main_removed(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + UnregisterSignal(source, COMSIG_ROD_BEGIN_FISHING) /datum/material/bluespace/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.reagents.add_reagent(/datum/reagent/bluespace, rand(5, 8)) @@ -284,17 +363,50 @@ Unless you know what you're doing, only use the first three numbers. They're in armor_modifiers = list(BOMB = 100, FIRE = 10) //Clowns cant be blown away. mineral_rarity = MATERIAL_RARITY_UNDISCOVERED points_per_unit = 60 / SHEET_MATERIAL_AMOUNT + fishing_difficulty_modifier = 20 //can't get a good grip on slipperiness. + fishing_cast_range = 3 //long slide + fishing_experience_multiplier = 1.6 + fishing_completion_speed = 1.3 + fishing_bait_speed_mult = 1.5 + fishing_deceleration_mult = 0.5 + fishing_bounciness_mult = 2 /datum/material/bananium/on_applied(atom/source, mat_amount, multiplier) . = ..() source.LoadComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 50 * multiplier, falloff_exponent = 20) source.AddComponent(/datum/component/slippery, min(mat_amount / 10 * multiplier, 80 * multiplier)) +/datum/material/bananium/on_main_applied(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + RegisterSignal(source, COMSIG_ROD_BEGIN_FISHING, PROC_REF(on_begin_fishing)) + +/datum/material/bananium/proc/on_begin_fishing(obj/item/fishing_rod/rod, datum/fishing_challenge/challenge) + SIGNAL_HANDLER + if(prob(40)) + RegisterSignal(challenge, COMSIG_FISHING_CHALLENGE_ROLL_REWARD, PROC_REF(roll_funny_fish)) + +/datum/material/bananium/proc/roll_funny_fish(datum/source, obj/item/fishing_rod/rod, mob/fisherman, atom/location, list/rewards) + SIGNAL_HANDLER + var/static/list/funny_fish = list( + /obj/item/fish/clownfish = 5, + /obj/item/fish/clownfish/lube = 3, + /obj/item/fish/soul = 2, + /obj/item/fish/skin_crab = 2, + /obj/item/fish/donkfish = 2, + ) + rewards += pick_weight(funny_fish) + /datum/material/bananium/on_removed(atom/source, mat_amount, multiplier) . = ..() qdel(source.GetComponent(/datum/component/slippery)) qdel(source.GetComponent(/datum/component/squeak)) +/datum/material/bananium/on_main_removed(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + UnregisterSignal(source, COMSIG_ROD_BEGIN_FISHING) + /datum/material/bananium/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.reagents.add_reagent(/datum/reagent/consumable/banana, rand(8, 12)) source_item?.reagents?.add_reagent(/datum/reagent/consumable/banana, source_item.reagents.total_volume*(2/5)) @@ -323,6 +435,14 @@ Unless you know what you're doing, only use the first three numbers. They're in mat_rust_resistance = RUST_RESISTANCE_TITANIUM mineral_rarity = MATERIAL_RARITY_SEMIPRECIOUS texture_layer_icon_state = "shine" + fish_weight_modifier = 1.2 + fishing_difficulty_modifier = -5 + fishing_cast_range = 1 + fishing_completion_speed = 1.15 + fishing_bait_speed_mult = 1.15 + fishing_deceleration_mult = 1.3 + fishing_bounciness_mult = 0.75 + fishing_gravity_mult = 1.1 /datum/material/titanium/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.apply_damage(15, BRUTE, BODY_ZONE_HEAD, wound_bonus = 7) @@ -345,6 +465,24 @@ Unless you know what you're doing, only use the first three numbers. They're in armor_modifiers = list(MELEE = 1.35, BULLET = 2, LASER = 0.5, ENERGY = 1.25, BOMB = 1.25, BIO = 1, FIRE = 1.4, ACID = 1) //rune is weak against magic lasers but strong against bullets. This is the combat triangle. mineral_rarity = MATERIAL_RARITY_UNDISCOVERED points_per_unit = 100 / SHEET_MATERIAL_AMOUNT + fish_weight_modifier = 1.5 + fishing_difficulty_modifier = -18 + fishing_cast_range = 1 + fishing_experience_multiplier = 3.2 //grind all the way to level 100 in no time. + fishing_completion_speed = 1.3 + fishing_bait_speed_mult = 0.9 + fishing_deceleration_mult = 1.2 + fishing_gravity_mult = 1.25 + +/datum/material/runite/on_applied(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + ADD_TRAIT(source, TRAIT_ROD_REMOVE_FISHING_DUD, REF(src)) //light-absorbing, environment-cancelling fishing rod. + +/datum/material/runite/on_removed(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + REMOVE_TRAIT(source, TRAIT_ROD_REMOVE_FISHING_DUD, REF(src)) //light-absorbing, environment-cancelling fishing rod. /datum/material/runite/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.apply_damage(20, BRUTE, BODY_ZONE_HEAD, wound_bonus = 10) @@ -370,6 +508,14 @@ Unless you know what you're doing, only use the first three numbers. They're in armor_modifiers = list(MELEE = 1.5, BULLET = 1.1, LASER = 0.3, ENERGY = 0.5, BOMB = 1, BIO = 1, FIRE = 1.1, ACID = 1) mineral_rarity = MATERIAL_RARITY_UNDISCOVERED //Nobody's found oil on lavaland yet. points_per_unit = 4 / SHEET_MATERIAL_AMOUNT + fish_weight_modifier = 0.8 + fishing_difficulty_modifier = -5 + fishing_cast_range = 2 + fishing_experience_multiplier = 1.2 + fishing_bait_speed_mult = 1.3 + fishing_deceleration_mult = 0.8 + fishing_bounciness_mult = 1.3 + fishing_gravity_mult = 0.85 /datum/material/plastic/on_accidental_mat_consumption(mob/living/carbon/eater, obj/item/food) eater.reagents.add_reagent(/datum/reagent/plastic_polymers, rand(6, 8)) @@ -400,6 +546,15 @@ Unless you know what you're doing, only use the first three numbers. They're in beauty_modifier = 0.1 armor_modifiers = list(MELEE = 1.1, BULLET = 1.1, LASER = 0.4, ENERGY = 0.4, BOMB = 1, BIO = 0.2, ACID = 0.3) texture_layer_icon_state = "woodgrain" + fish_weight_modifier = 0.5 + fishing_difficulty_modifier = 8 + fishing_cast_range = -1 + fishing_experience_multiplier = 1.3 + fishing_completion_speed = 0.9 + fishing_bait_speed_mult = 0.8 + fishing_deceleration_mult = 1.3 + fishing_bounciness_mult = 0.4 + fishing_gravity_mult = 0.8 /datum/material/wood/on_main_applied(atom/source, mat_amount, multiplier) . = ..() @@ -438,6 +593,25 @@ Unless you know what you're doing, only use the first three numbers. They're in armor_modifiers = list(MELEE = 1.5, BULLET = 1.5, LASER = 1.3, ENERGY = 1.3, BOMB = 1, BIO = 1, FIRE = 2.5, ACID = 1) mineral_rarity = MATERIAL_RARITY_UNDISCOVERED //Doesn't naturally spawn on lavaland. points_per_unit = 100 / SHEET_MATERIAL_AMOUNT + fish_weight_modifier = 1.6 + fishing_difficulty_modifier = -23 + fishing_cast_range = 1 + fishing_experience_multiplier = 0.6 + fishing_completion_speed = 1.3 + fishing_bait_speed_mult = 1.3 + fishing_deceleration_mult = 1.3 + fishing_bounciness_mult = 0.7 + fishing_gravity_mult = 1.4 + +/datum/material/adamantine/on_applied(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + ADD_TRAIT(source, TRAIT_ROD_REMOVE_FISHING_DUD, REF(src)) //light-absorbing, environment-cancelling fishing rod. + +/datum/material/adamantine/on_removed(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + REMOVE_TRAIT(source, TRAIT_ROD_REMOVE_FISHING_DUD, REF(src)) //light-absorbing, environment-cancelling fishing rod. /datum/material/adamantine/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.apply_damage(20, BRUTE, BODY_ZONE_HEAD, wound_bonus = 10) @@ -461,6 +635,15 @@ Unless you know what you're doing, only use the first three numbers. They're in beauty_modifier = 0.5 mineral_rarity = MATERIAL_RARITY_UNDISCOVERED //Doesn't naturally spawn on lavaland. points_per_unit = 100 / SHEET_MATERIAL_AMOUNT + fish_weight_modifier = 1.4 + fishing_difficulty_modifier = -25 + fishing_cast_range = 2 + fishing_experience_multiplier = 0.5 + fishing_completion_speed = 1.35 + fishing_bait_speed_mult = 1.35 + fishing_deceleration_mult = 1.35 + fishing_bounciness_mult = 0.65 + fishing_gravity_mult = 1.4 /datum/material/mythril/on_applied(atom/source, mat_amount, multiplier) . = ..() @@ -494,6 +677,15 @@ Unless you know what you're doing, only use the first three numbers. They're in sheet_type = /obj/item/stack/sheet/hot_ice value_per_unit = 400 / SHEET_MATERIAL_AMOUNT beauty_modifier = 0.2 + fish_weight_modifier = 0.9 + fishing_difficulty_modifier = -10 + fishing_cast_range = 1 + fishing_experience_multiplier = 0.9 + fishing_completion_speed = 1.4 + fishing_bait_speed_mult = 1.5 + fishing_deceleration_mult = 0.5 + fishing_bounciness_mult = 0.3 + fishing_gravity_mult = 0.8 /datum/material/hot_ice/on_applied(atom/source, mat_amount, multiplier) . = ..() @@ -526,6 +718,15 @@ Unless you know what you're doing, only use the first three numbers. They're in beauty_modifier = 0.35 strength_modifier = 1.2 armor_modifiers = list(MELEE = 1.35, BULLET = 1.3, LASER = 1.3, ENERGY = 1.25, BOMB = 0.7, BIO = 1, FIRE = 1.3, ACID = 1) + fish_weight_modifier = 0.6 //It may be metallic, but it's just "denser" hydrogen at the end of the day, no? + fishing_difficulty_modifier = -15 + fishing_cast_range = 4 + fishing_experience_multiplier = 0.8 + fishing_completion_speed = 1.4 + fishing_bait_speed_mult = 1.6 + fishing_deceleration_mult = 0.8 + fishing_bounciness_mult = 1.7 + fishing_gravity_mult = 0.7 /datum/material/metalhydrogen/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.apply_damage(15, BRUTE, BODY_ZONE_HEAD, wound_bonus = 7) @@ -549,6 +750,15 @@ Unless you know what you're doing, only use the first three numbers. They're in beauty_modifier = 0.25 turf_sound_override = FOOTSTEP_SAND texture_layer_icon_state = "sand" + fish_weight_modifier = 1.2 + fishing_difficulty_modifier = 30 //Sand fishing rods? What the hell are you doing? + fishing_cast_range = -2 + fishing_experience_multiplier = 0.2 + fishing_completion_speed = 0.8 + fishing_bait_speed_mult = 0.8 + fishing_deceleration_mult = 2.5 + fishing_bounciness_mult = 0.3 + fishing_gravity_mult = 0.9 /datum/material/sand/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.adjust_disgust(17) @@ -571,6 +781,15 @@ Unless you know what you're doing, only use the first three numbers. They're in beauty_modifier = 0.3 turf_sound_override = FOOTSTEP_WOOD texture_layer_icon_state = "brick" + fish_weight_modifier = 1.2 + fishing_difficulty_modifier = 25 //Sand fishing rods? What the hell are you doing? + fishing_cast_range = -2 + fishing_experience_multiplier = 0.3 + fishing_completion_speed = 0.9 + fishing_bait_speed_mult = 0.8 + fishing_deceleration_mult = 2.5 + fishing_bounciness_mult = 0.2 + fishing_gravity_mult = 0.9 /datum/material/snow name = "snow" @@ -582,11 +801,22 @@ Unless you know what you're doing, only use the first three numbers. They're in MAT_CATEGORY_ITEM_MATERIAL_COMPLEMENTARY = TRUE, ) sheet_type = /obj/item/stack/sheet/mineral/snow + strength_modifier = 0.4 + integrity_modifier = 0.4 value_per_unit = 5 / SHEET_MATERIAL_AMOUNT armor_modifiers = list(MELEE = 0.25, BULLET = 0.25, LASER = 0.25, ENERGY = 0.25, BOMB = 0.25, BIO = 0.25, FIRE = 0.25, ACID = 1.5) beauty_modifier = 0.3 turf_sound_override = FOOTSTEP_SAND texture_layer_icon_state = "sand" + fish_weight_modifier = 0.8 + fishing_difficulty_modifier = 25 + fishing_cast_range = -2 + fishing_experience_multiplier = 0.3 + fishing_completion_speed = 0.9 + fishing_bait_speed_mult = 0.75 + fishing_deceleration_mult = 0.3 + fishing_bounciness_mult = 0.2 + fishing_gravity_mult = 0.7 /datum/material/snow/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.reagents.add_reagent(/datum/reagent/water, rand(5, 10)) @@ -607,6 +837,14 @@ Unless you know what you're doing, only use the first three numbers. They're in armor_modifiers = list(MELEE = 1.2, BULLET = 1.2, LASER = 1, ENERGY = 1, BOMB = 1.2, BIO = 1.2, FIRE = 1.5, ACID = 1.5) beauty_modifier = -0.15 texture_layer_icon_state = "runed" + fish_weight_modifier = 1.5 + fishing_difficulty_modifier = -12 + fishing_experience_multiplier = 0.9 + fishing_completion_speed = 1.2 + fishing_bait_speed_mult = 1.666 + fishing_deceleration_mult = 1.666 + fishing_bounciness_mult = 0.666 + fishing_gravity_mult = 1.666 /datum/material/runedmetal/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.reagents.add_reagent(/datum/reagent/fuel/unholywater, rand(8, 12)) @@ -627,6 +865,11 @@ Unless you know what you're doing, only use the first three numbers. They're in value_per_unit = 50 / SHEET_MATERIAL_AMOUNT armor_modifiers = list(MELEE = 1, BULLET = 1, LASER = 1, ENERGY = 1, BOMB = 1, BIO = 1, FIRE = 1.5, ACID = 1.5) beauty_modifier = 0.2 + fish_weight_modifier = 1.4 + fishing_bait_speed_mult = 1.1 + fishing_deceleration_mult = 0.8 + fishing_bounciness_mult = 1.2 + fishing_gravity_mult = 1.05 /datum/material/paper name = "paper" @@ -640,23 +883,47 @@ Unless you know what you're doing, only use the first three numbers. They're in ) sheet_type = /obj/item/stack/sheet/paperframes value_per_unit = 5 / SHEET_MATERIAL_AMOUNT + strength_modifier = 0.3 armor_modifiers = list(MELEE = 0.1, BULLET = 0.1, LASER = 0.1, ENERGY = 0.1, BOMB = 0.1, BIO = 0.1, ACID = 1.5) beauty_modifier = 0.3 turf_sound_override = FOOTSTEP_SAND texture_layer_icon_state = "paper" + fish_weight_modifier = 0.4 + fishing_difficulty_modifier = 40 //child's play + fishing_cast_range = -2 + fishing_experience_multiplier = 0.1 + fishing_bait_speed_mult = 0.7 + fishing_deceleration_mult = 1.5 + fishing_bounciness_mult = 0.2 + fishing_gravity_mult = 0.6 /datum/material/paper/on_main_applied(atom/source, mat_amount, multiplier) . = ..() - if(isobj(source) && source.material_flags & MATERIAL_AFFECT_STATISTICS) - var/obj/paper = source - paper.resistance_flags |= FLAMMABLE - paper.obj_flags |= UNIQUE_RENAME + if(!isobj(source) || !(source.material_flags & MATERIAL_AFFECT_STATISTICS)) + return + var/obj/paper = source + paper.resistance_flags |= FLAMMABLE + paper.obj_flags |= UNIQUE_RENAME + if(istype(paper, /obj/item/fishing_rod)) + RegisterSignal(paper, COMSIG_ROD_BEGIN_FISHING, PROC_REF(on_begin_fishing)) + +/datum/material/paper/proc/on_begin_fishing(obj/item/fishing_rod/rod, datum/fishing_challenge/challenge) + SIGNAL_HANDLER + if(prob(40)) //consider the default reward and it's 15% + RegisterSignal(challenge, COMSIG_FISHING_CHALLENGE_ROLL_REWARD, PROC_REF(roll_stickman)) + +/datum/material/paper/proc/roll_stickman(datum/source, obj/item/fishing_rod/rod, mob/fisherman, atom/location, list/rewards) + SIGNAL_HANDLER + rewards += pick(/mob/living/basic/stickman, /mob/living/basic/stickman/dog, /mob/living/basic/stickman/ranged) /datum/material/paper/on_main_removed(atom/source, mat_amount, multiplier) - if(isobj(source) && source.material_flags & MATERIAL_AFFECT_STATISTICS) - var/obj/paper = source - paper.resistance_flags &= ~FLAMMABLE - return ..() + . = ..() + if(!isobj(source) || !(source.material_flags & MATERIAL_AFFECT_STATISTICS)) + return + var/obj/paper = source + paper.resistance_flags &= ~FLAMMABLE + if(istype(paper, /obj/item/fishing_rod)) + UnregisterSignal(paper, COMSIG_ROD_BEGIN_FISHING) /datum/material/cardboard name = "cardboard" @@ -670,8 +937,17 @@ Unless you know what you're doing, only use the first three numbers. They're in ) sheet_type = /obj/item/stack/sheet/cardboard value_per_unit = 6 / SHEET_MATERIAL_AMOUNT + strength_modifier = 0.3 armor_modifiers = list(MELEE = 0.25, BULLET = 0.25, LASER = 0.25, ENERGY = 0.25, BOMB = 0.25, BIO = 0.25, ACID = 1.5) beauty_modifier = -0.1 + fish_weight_modifier = 0.4 + fishing_difficulty_modifier = 40 //child's play + fishing_cast_range = -2 + fishing_experience_multiplier = 0.1 + fishing_bait_speed_mult = 0.7 + fishing_deceleration_mult = 1.5 + fishing_bounciness_mult = 0.2 + fishing_gravity_mult = 0.6 /datum/material/cardboard/on_main_applied(atom/source, mat_amount, multiplier) . = ..() @@ -700,6 +976,48 @@ Unless you know what you're doing, only use the first three numbers. They're in value_per_unit = 100 / SHEET_MATERIAL_AMOUNT armor_modifiers = list(MELEE = 1.2, BULLET = 0.75, LASER = 0.75, ENERGY = 1.2, BOMB = 1, BIO = 1, FIRE = 1.5, ACID = 1.5) beauty_modifier = -0.2 + fish_weight_modifier = 1.05 + fishing_difficulty_modifier = 15 + fishing_cast_range = -2 + fishing_experience_multiplier = 0.85 + fishing_completion_speed = 0.9 + fishing_bait_speed_mult = 0.9 + fishing_deceleration_mult = 0.9 + fishing_bounciness_mult = 0.8 + fishing_gravity_mult = 0.85 + +/datum/material/bone/on_main_applied(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + RegisterSignal(source, COMSIG_ROD_BEGIN_FISHING, PROC_REF(on_begin_fishing)) + else if(istype(source, /obj/item/fish)) + ADD_TRAIT(source, TRAIT_FISH_MADE_OF_BONE, REF(src)) + +/datum/material/bone/proc/on_begin_fishing(obj/item/fishing_rod/rod, datum/fishing_challenge/challenge) + SIGNAL_HANDLER + if(prob(40)) + RegisterSignal(challenge, COMSIG_FISHING_CHALLENGE_ROLL_REWARD, PROC_REF(roll_bones)) + +/datum/material/bone/proc/roll_bones(datum/source, obj/item/fishing_rod/rod, mob/fisherman, atom/location, list/rewards) + SIGNAL_HANDLER + var/static/list/bones = list( + /obj/item/fish/boned = 65, + /obj/item/fish/mastodon = 8, + /mob/living/basic/skeleton = 6, + /mob/living/basic/skeleton/ice = 6, + /mob/living/basic/skeleton/templar = 6, + /obj/item/instrument/trumpet/spectral/one_doot = 3, + /obj/item/instrument/saxophone/spectral/one_doot = 3, + /obj/item/instrument/trombone/spectral/one_doot = 3, + ) + rewards += pick_weight(bones) + +/datum/material/bone/on_main_removed(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + UnregisterSignal(source, COMSIG_ROD_BEGIN_FISHING) + else if(istype(source, /obj/item/fish)) + REMOVE_TRAIT(source, TRAIT_FISH_MADE_OF_BONE, REF(src)) /datum/material/bamboo name = "bamboo" @@ -712,11 +1030,21 @@ Unless you know what you're doing, only use the first three numbers. They're in MAT_CATEGORY_ITEM_MATERIAL_COMPLEMENTARY = TRUE, ) sheet_type = /obj/item/stack/sheet/mineral/bamboo + strength_modifier = 0.5 value_per_unit = 5 / SHEET_MATERIAL_AMOUNT armor_modifiers = list(MELEE = 0.5, BULLET = 0.5, LASER = 0.5, ENERGY = 0.5, BOMB = 0.5, BIO = 0.51, FIRE = 0.5, ACID = 1.5) beauty_modifier = 0.2 turf_sound_override = FOOTSTEP_WOOD texture_layer_icon_state = "bamboo" + fish_weight_modifier = 0.5 + fishing_difficulty_modifier = -4 + fishing_cast_range = -1 + fishing_experience_multiplier = 1.3 + fishing_completion_speed = 1.15 + fishing_bait_speed_mult = 1.2 + fishing_deceleration_mult = 0.8 + fishing_bounciness_mult = 0.7 + fishing_gravity_mult = 0.7 /datum/material/zaukerite name = "zaukerite" @@ -732,6 +1060,24 @@ Unless you know what you're doing, only use the first three numbers. They're in value_per_unit = 900 / SHEET_MATERIAL_AMOUNT armor_modifiers = list(MELEE = 0.9, BULLET = 0.9, LASER = 1.75, ENERGY = 1.75, BOMB = 0.5, BIO = 1, FIRE = 0.1, ACID = 1) beauty_modifier = 0.001 + fish_weight_modifier = 1.2 + fishing_difficulty_modifier = -16 + fishing_experience_multiplier = 0.9 + fishing_completion_speed = 1.3 + fishing_bait_speed_mult = 1.3 + fishing_deceleration_mult = 1.3 + fishing_bounciness_mult = 1.1 + fishing_gravity_mult = 1.35 + +/datum/material/zaukerite/on_applied(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + ADD_TRAIT(source, TRAIT_ROD_IGNORE_ENVIRONMENT, REF(src)) //light-absorbing, environment-cancelling fishing rod. + +/datum/material/zaukerite/on_removed(atom/source, mat_amount, multiplier) + . = ..() + if(istype(source, /obj/item/fishing_rod)) + REMOVE_TRAIT(source, TRAIT_ROD_IGNORE_ENVIRONMENT, REF(src)) //light-absorbing, environment-cancelling fishing rod. /datum/material/zaukerite/on_accidental_mat_consumption(mob/living/carbon/victim, obj/item/source_item) victim.apply_damage(30, BURN, BODY_ZONE_HEAD, wound_bonus = 5) diff --git a/code/datums/materials/hauntium.dm b/code/datums/materials/hauntium.dm index b8eee26a08f..995c566a4d6 100644 --- a/code/datums/materials/hauntium.dm +++ b/code/datums/materials/hauntium.dm @@ -17,6 +17,13 @@ //pretty good but only the undead can actually make use of these modifiers strength_modifier = 1.2 armor_modifiers = list(MELEE = 1.1, BULLET = 1.1, LASER = 1.15, ENERGY = 1.15, BOMB = 1, BIO = 1, FIRE = 1, ACID = 0.7) + fish_weight_modifier = 1.4 + fishing_difficulty_modifier = -30 //Only the undead and the coroner can game this. + fishing_cast_range = 2 + fishing_experience_multiplier = 1.5 + fishing_completion_speed = 1.1 + fishing_bait_speed_mult = 0.85 + fishing_gravity_mult = 0.8 /datum/material/hauntium/on_main_applied(atom/source, mat_amount, multiplier) . = ..() diff --git a/code/datums/materials/meat.dm b/code/datums/materials/meat.dm index 008099a526e..512b97f81be 100644 --- a/code/datums/materials/meat.dm +++ b/code/datums/materials/meat.dm @@ -18,11 +18,19 @@ item_sound_override = 'sound/effects/meatslap.ogg' turf_sound_override = FOOTSTEP_MEAT texture_layer_icon_state = "meat" + fishing_difficulty_modifier = 13 + fishing_cast_range = -2 + fishing_experience_multiplier = 0.8 + fishing_bait_speed_mult = 0.9 + fishing_deceleration_mult = 0.9 + fishing_bounciness_mult = 0.9 + fishing_gravity_mult = 0.85 /datum/material/meat/on_main_applied(atom/source, mat_amount, multiplier) . = ..() if(!IS_EDIBLE(source)) make_edible(source, mat_amount, multiplier) + ADD_TRAIT(source, TRAIT_ROD_REMOVE_FISHING_DUD, REF(src)) //The rod itself is the bait... sorta. /datum/material/meat/on_applied(atom/source, mat_amount, multiplier) . = ..() @@ -64,6 +72,7 @@ /datum/material/meat/on_main_removed(atom/source, mat_amount, multiplier) . = ..() qdel(source.GetComponent(/datum/component/edible)) + REMOVE_TRAIT(source, TRAIT_ROD_REMOVE_FISHING_DUD, REF(src)) /datum/material/meat/mob_meat init_flags = MATERIAL_INIT_BESPOKE diff --git a/code/datums/materials/pizza.dm b/code/datums/materials/pizza.dm index 1906e5786d2..1bbaaefa909 100644 --- a/code/datums/materials/pizza.dm +++ b/code/datums/materials/pizza.dm @@ -16,11 +16,20 @@ item_sound_override = 'sound/effects/meatslap.ogg' turf_sound_override = FOOTSTEP_MEAT texture_layer_icon_state = "pizza" + fish_weight_modifier = 0.9 + fishing_difficulty_modifier = 13 + fishing_cast_range = -2 + fishing_experience_multiplier = 0.8 + fishing_bait_speed_mult = 0.9 + fishing_deceleration_mult = 0.9 + fishing_bounciness_mult = 0.9 + fishing_gravity_mult = 0.8 /datum/material/pizza/on_main_applied(atom/source, mat_amount, multiplier) . = ..() if(!IS_EDIBLE(source)) make_edible(source, mat_amount) + ADD_TRAIT(source, TRAIT_ROD_REMOVE_FISHING_DUD, REF(src)) //the fishing rod itself is the bait... sorta. /datum/material/pizza/on_applied(atom/source, mat_amount, multiplier) . = ..() @@ -39,3 +48,4 @@ /datum/material/pizza/on_main_removed(atom/source, mat_amount, multiplier) . = ..() qdel(source.GetComponent(/datum/component/edible)) + REMOVE_TRAIT(source, TRAIT_ROD_REMOVE_FISHING_DUD, REF(src)) diff --git a/code/game/atom/atom_materials.dm b/code/game/atom/atom_materials.dm index cb88c83f3eb..a04d3aac566 100644 --- a/code/game/atom/atom_materials.dm +++ b/code/game/atom/atom_materials.dm @@ -10,11 +10,11 @@ /// Sets the custom materials for an atom. This is what you want to call, since most of the ones below are mainly internal. /atom/proc/set_custom_materials(list/materials, multiplier = 1) SHOULD_NOT_OVERRIDE(TRUE) + var/replace_mats = length(materials) if(length(custom_materials)) - remove_material_effects() + remove_material_effects(replace_mats) - if(!length(materials)) - custom_materials = null + if(!replace_mats) return initialize_materials(materials, multiplier) @@ -31,27 +31,30 @@ materials[current_material] *= multiplier apply_material_effects(materials) - custom_materials = SSmaterials.FindOrCreateMaterialCombo(materials) ///proc responsible for applying material effects when setting materials. /atom/proc/apply_material_effects(list/materials) SHOULD_CALL_PARENT(TRUE) - if(!materials || !(material_flags & MATERIAL_EFFECTS)) - return - var/list/material_effects = get_material_effects_list(materials) - finalize_material_effects(material_effects) + if(material_flags & MATERIAL_EFFECTS) + var/list/material_effects = get_material_effects_list(materials) + finalize_material_effects(material_effects) + + custom_materials = SSmaterials.FindOrCreateMaterialCombo(materials) /// Proc responsible for removing material effects when setting materials. -/atom/proc/remove_material_effects() +/atom/proc/remove_material_effects(replace_mats = TRUE) SHOULD_CALL_PARENT(TRUE) //Only runs if custom materials existed at first and affected src. - if(!custom_materials || !(material_flags & MATERIAL_EFFECTS)) - return - var/list/material_effects = get_material_effects_list(custom_materials) - finalize_remove_material_effects(material_effects) + if(material_flags & MATERIAL_EFFECTS) + var/list/material_effects = get_material_effects_list(custom_materials) + finalize_remove_material_effects(material_effects) + + if(!replace_mats) + custom_materials = null /atom/proc/get_material_effects_list(list/materials) SHOULD_NOT_OVERRIDE(TRUE) + PRIVATE_PROC(TRUE) var/list/material_effects = list() var/index = 1 for(var/current_material in materials) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 2522b8b9af3..28d52d32604 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1806,13 +1806,13 @@ /obj/item/apply_single_mat_effect(datum/material/material, mat_amount, multiplier) . = ..() - if(!(material_flags & MATERIAL_AFFECT_STATISTICS) || !slowdown) + if(!(material_flags & MATERIAL_AFFECT_STATISTICS) || (material_flags & MATERIAL_NO_SLOWDOWN) || !material.added_slowdown) return slowdown += GET_MATERIAL_MODIFIER(material.added_slowdown * mat_amount, multiplier) /obj/item/remove_single_mat_effect(datum/material/material, mat_amount, multiplier) . = ..() - if(!(material_flags & MATERIAL_AFFECT_STATISTICS) || !slowdown) + if(!(material_flags & MATERIAL_AFFECT_STATISTICS) || (material_flags & MATERIAL_NO_SLOWDOWN) || !material.added_slowdown) return slowdown -= GET_MATERIAL_MODIFIER(material.added_slowdown * mat_amount, multiplier) diff --git a/code/modules/fishing/bait.dm b/code/modules/fishing/bait.dm index ec758be7042..aad7a8d3d1d 100644 --- a/code/modules/fishing/bait.dm +++ b/code/modules/fishing/bait.dm @@ -76,16 +76,15 @@ /obj/item/fishing_lure/Initialize(mapload) . = ..() add_traits(list(TRAIT_FISHING_BAIT, TRAIT_BAIT_ALLOW_FISHING_DUD, TRAIT_OMNI_BAIT, TRAIT_BAIT_UNCONSUMABLE), INNATE_TRAIT) - RegisterSignal(src, COMSIG_FISHING_EQUIPMENT_SLOTTED, PROC_REF(lure_equipped)) + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_SLOTTED, PROC_REF(on_fishing_rod_slotted)) + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_UNSLOTTED, PROC_REF(on_fishing_rod_unslotted)) -/obj/item/fishing_lure/proc/lure_equipped(datum/source, obj/item/fishing_rod/rod) +/obj/item/fishing_lure/proc/on_fishing_rod_slotted(datum/source, obj/item/fishing_rod/rod, slot) SIGNAL_HANDLER rod.spin_frequency = spin_frequency - RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_removed)) -/obj/item/fishing_lure/proc/on_removed(atom/movable/source, obj/item/fishing_rod/rod, dir, forced) +/obj/item/fishing_lure/proc/on_fishing_rod_unslotted(datum/source, obj/item/fishing_rod/rod, slot) SIGNAL_HANDLER - UnregisterSignal(src, COMSIG_MOVABLE_MOVED) rod.spin_frequency = null ///Called for every fish subtype by the fishing subsystem when initializing, to populate the list of fish that can be catched with this lure. @@ -203,13 +202,20 @@ /obj/item/fishing_lure/led/Initialize(mapload) . = ..() - ADD_TRAIT(src, TRAIT_BAIT_IGNORE_ENVIRONMENT, INNATE_TRAIT) update_appearance(UPDATE_OVERLAYS) /obj/item/fishing_lure/led/update_overlays() . = ..() . += emissive_appearance(icon, "led_emissive", src) +/obj/item/fishing_lure/led/on_fishing_rod_slotted(obj/item/fishing_rod/rod, slot) + . = ..() + ADD_TRAIT(rod, TRAIT_ROD_IGNORE_ENVIRONMENT, type) + +/obj/item/fishing_lure/led/on_fishing_rod_unslotted(obj/item/fishing_rod/rod, slot) + . = ..() + REMOVE_TRAIT(rod, TRAIT_ROD_IGNORE_ENVIRONMENT, type) + /obj/item/fishing_lure/led/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties) var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS] if(length(list(/datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits)) @@ -222,6 +228,14 @@ icon_state = "lucky_coin" spin_frequency = list(1.5 SECONDS, 2.7 SECONDS) +/obj/item/fishing_lure/lucky_coin/on_fishing_rod_slotted(obj/item/fishing_rod/rod, slot) + . = ..() + ADD_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS, REF(src)) + +/obj/item/fishing_lure/lucky_coin/on_fishing_rod_unslotted(obj/item/fishing_rod/rod, slot) + . = ..() + REMOVE_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS, REF(src)) + /obj/item/fishing_lure/lucky_coin/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties) var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS] if(/datum/fish_trait/shiny_lover in fish_traits) diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index 021f1be45d8..aa889057ecd 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -21,6 +21,8 @@ sound_vary = TRUE obj_flags = UNIQUE_RENAME item_flags = SLOWS_WHILE_IN_HAND + //we handle slowdowns internally, and the fish weight modifier from materials already contributes to it. + material_flags = MATERIAL_EFFECTS|MATERIAL_AFFECT_STATISTICS|MATERIAL_COLOR|MATERIAL_ADD_PREFIX|MATERIAL_NO_SLOWDOWN /// Flags for fish variables that would otherwise be TRUE/FALSE var/fish_flags = FISH_FLAG_SHOW_IN_CATALOG|FISH_DO_FLOP_ANIM|FISH_FLAG_EXPERIMENT_SCANNABLE @@ -116,6 +118,8 @@ var/size /// Average size for this fish type in centimeters. Will be used as gaussian distribution with 20% deviation for fishing, bought fish are always standard size var/average_size = 50 + /// Temporarily stores the new size of the fish from randomize_size_and_weight() to be used by update_size_weight() later, so that it can be deferred. + var/temp_size /// The maximum size this fish can reach, calculated the first time update_size_and_weight() is called. var/maximum_size @@ -123,8 +127,12 @@ var/weight /// Average weight for this fish type in grams var/average_weight = 1000 + /// Temporarily stores the new weight of the fish from randomize_size_and_weight() to be used by update_size_weight() later, so that it can be deferred. + var/temp_weight /// The maximum weight this fish can reach, calculated the first time update_size_and_weight() is called. var/maximum_weight + /// Stores the current total weight modifier from materials. + var/material_weight_mult = 1 ///The general deviation from the average weight and size this fish has in the wild var/weight_size_deviation = 0.2 @@ -439,25 +447,40 @@ 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) +/** + * This proc takes a base size, base weight and deviation arguments to generate new size and weight through a gaussian distribution (bell curve) + * Mainly used to determinate the size and weight of caught fish. + */ +/obj/item/fish/proc/randomize_size_and_weight(base_size = average_size, base_weight = average_weight, deviation = weight_size_deviation, update = TRUE) var/size_deviation = 0.2 * base_size - var/new_size = round(clamp(gaussian(base_size, size_deviation), average_size * 1/MAX_FISH_DEVIATION_COEFF, average_size * MAX_FISH_DEVIATION_COEFF)) + temp_size = round(clamp(gaussian(base_size, size_deviation), average_size * 1/MAX_FISH_DEVIATION_COEFF, average_size * MAX_FISH_DEVIATION_COEFF)) var/weight_deviation = 0.2 * base_weight - var/new_weight = round(clamp(gaussian(base_weight, weight_deviation), average_weight * 1/MAX_FISH_DEVIATION_COEFF, average_weight * MAX_FISH_DEVIATION_COEFF)) + temp_weight = round(clamp(gaussian(base_weight, weight_deviation), average_weight * 1/MAX_FISH_DEVIATION_COEFF, average_weight * MAX_FISH_DEVIATION_COEFF)) - update_size_and_weight(new_size, new_weight) + set_max_size_and_weight(temp_size, temp_weight) + if(update) + update_size_and_weight(temp_size, temp_weight) + +///Set the maximum size and weight a fish can reach from base size and weight args if they have't been set already. +/obj/item/fish/proc/set_max_size_and_weight(base_size, base_weight) + if(!maximum_size) + maximum_size = min(base_size * 2, average_size * MAX_FISH_DEVIATION_COEFF) + if(!maximum_weight) + maximum_weight = min(base_weight * 2, average_size * MAX_FISH_DEVIATION_COEFF) ///Updates weight and size, along with weight class, number of fillets you can get and grind results. -/obj/item/fish/proc/update_size_and_weight(new_size = average_size, new_weight = average_weight) +/obj/item/fish/proc/update_size_and_weight(new_size = average_size, new_weight = average_weight, update_materials = TRUE) + fish_flags |= FISH_FLAG_UPDATING_SIZE_AND_WEIGHT SEND_SIGNAL(src, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, new_size, new_weight) + if(size) remove_fillet_type() if(size > FISH_SIZE_TWO_HANDS_REQUIRED) qdel(GetComponent(/datum/component/two_handed)) else - maximum_size = min(new_size * 2, average_size * MAX_FISH_DEVIATION_COEFF) + set_max_size_and_weight(new_size, new_weight) + size = new_size var/init_icon_state = initial(inhand_icon_state) @@ -512,8 +535,6 @@ else reagents.multiply_reagents(new_weight_ratio) adjust_reagents_capacity(volume_diff) - else - maximum_weight = min(new_weight * 2, new_weight * MAX_FISH_DEVIATION_COEFF) weight = new_weight @@ -533,8 +554,22 @@ for(var/reagent_type in grind_results) grind_results[reagent_type] *= max(FLOOR(weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR, 0.1), 0.1) + var/mats_len = length(custom_materials) + if(update_materials && mats_len) + var/list/new_mats_list = custom_materials.Copy() + var/multiplier = 1 / mats_len + var/unmodified_weight = weight + for(var/mat_type in custom_materials) + var/datum/material/material = GET_MATERIAL_REF(mat_type) + unmodified_weight /= GET_MATERIAL_MODIFIER(material.fish_weight_modifier, multiplier) + multiplier = unmodified_weight / weight + for(var/mat_type in new_mats_list) + new_mats_list[mat_type] *= multiplier + set_custom_materials(new_mats_list) // apply_material_effects() will call update_fish_force for us update_fish_force() + fish_flags &= ~FISH_FLAG_UPDATING_SIZE_AND_WEIGHT + /obj/item/fish/proc/remove_fillet_type() if(!fillet_type) return @@ -586,17 +621,24 @@ if(bonus_malus) calculate_fish_force_bonus(bonus_malus) - throwforce = force + if(material_flags & MATERIAL_EFFECTS && length(custom_materials)) //struck by metal gen or something. + var/multiplier = 1 / length(custom_materials) + if(material_flags & MATERIAL_AFFECT_STATISTICS) + for(var/current_material in custom_materials) + var/datum/material/material = GET_MATERIAL_REF(current_material) + force *= GET_MATERIAL_MODIFIER(material.strength_modifier, multiplier) + var/datum/material/master = get_master_material() + if(master?.item_sound_override) + hitsound = master.item_sound_override + usesound = master.item_sound_override + mob_throw_hit_sound = master.item_sound_override + equip_sound = master.item_sound_override + pickup_sound = master.item_sound_override + drop_sound = master.item_sound_override SEND_SIGNAL(src, COMSIG_FISH_FORCE_UPDATED, weight_rank, bonus_malus) - if(material_flags & MATERIAL_EFFECTS) //struck by metal gen or something. - for(var/current_material in custom_materials) - var/datum/material/material = GET_MATERIAL_REF(current_material) - force *= material.strength_modifier - throwforce *= material.strength_modifier - if(material.item_sound_override) - hitsound = material.item_sound_override + throwforce = force if(force >=15 && hitsound == SFX_DEFAULT_FISH_SLAP) // don't override special attack sounds hitsound = SFX_ALT_FISH_SLAP // do more damage - do heavier slap sound @@ -626,6 +668,29 @@ attack_speed += 0.4 SECONDS demolition_mod += 0.4 +/obj/item/fish/apply_single_mat_effect(datum/material/custom_material, amount, multiplier) + . = ..() + //The materials are being increased/decreased along with the weight. + if(fish_flags & FISH_FLAG_UPDATING_SIZE_AND_WEIGHT) + return + material_weight_mult *= GET_MATERIAL_MODIFIER(custom_material.fish_weight_modifier, multiplier) + +/obj/item/fish/apply_material_effects() + . = ..() + //Either effects aren't applied of he materials are simply being increased/decreased along with the weight. Avoids recursion. + if(!(material_flags & MATERIAL_EFFECTS) || (fish_flags & FISH_FLAG_UPDATING_SIZE_AND_WEIGHT) || material_weight_mult == 1) + return + maximum_weight *= material_weight_mult + update_size_and_weight(size, (temp_weight || weight) * material_weight_mult, update_materials = FALSE) + +/obj/item/fish/remove_material_effects(replace_mats = TRUE) + . = ..() + if(replace_mats || !(material_flags & MATERIAL_EFFECTS) || material_weight_mult == 1) + return + maximum_weight /= material_weight_mult + update_size_and_weight(size, weight / material_weight_mult) + material_weight_mult = 1 + /** * This proc has fish_traits list populated with fish_traits paths from three different lists: * traits from x_traits and y_traits are compared, and inserted if conditions are met; @@ -720,6 +785,7 @@ ///If one of the reagent with fish effects is also our food reagent this is set to TRUE var/already_fed = FALSE + var/was_hungry = get_hunger() for(var/datum/reagent/reagent as anything in fed_reagents.reagent_list) if(!fed_reagents.has_reagent(reagent.type, 0.1) || !reagent.used_on_fish(src)) continue @@ -727,6 +793,9 @@ if(reagent.type == food) already_fed = TRUE + if(was_hungry && !get_hunger()) //one of the other reagents already sated its hunger. + return + if(already_fed) sate_hunger() return @@ -1149,16 +1218,39 @@ //Try to pass down compatible traits based on inheritability new_fish.inherit_traits(fish_traits, partner?.fish_traits, evolution?.new_traits, evolution?.removed_traits) + ///If set, the offspring will inherit materials from the parent. + var/obj/item/fish/chosen_material_giver //We combine two methods for determining the size and weight of the offspring for less extreme results. if(partner) var/ratio_size = new_fish.average_size * (((size / average_size) + (partner.size / partner.average_size)) / 2) var/mean_size = (size + partner.size)/2 var/ratio_weight = new_fish.average_size * (((weight / average_weight) + (partner.weight / partner.average_weight)) / 2) var/mean_weight = (weight + partner.weight)/2 - new_fish.randomize_size_and_weight((mean_size + ratio_size) * 0.5, (mean_weight + ratio_weight) * 0.5, 0.3) + new_fish.randomize_size_and_weight((mean_size + ratio_size) * 0.5, (mean_weight + ratio_weight) * 0.5, 0.3, update = FALSE) partner.breeding_wait = world.time + breeding_timeout - else //Make a close of this fish. - new_fish.update_size_and_weight(size, weight) + + if(length(partner.custom_materials)) + if(length(custom_materials)) + chosen_material_giver = pick(src, partner) + else if(prob(50)) + chosen_material_giver = partner + else if(length(custom_materials) && prob(50)) + chosen_material_giver = src + else + new_fish.temp_size = size + new_fish.temp_weight = weight + if(length(custom_materials)) + chosen_material_giver = src + + if(chosen_material_giver) + //We need the original weight of the fish to set the correct amount (it scales with weight) of mats for the offspring + var/mats_multiplier = new_fish.temp_weight / (chosen_material_giver.weight / material_weight_mult) + var/list/new_mats = chosen_material_giver.custom_materials.Copy() + for(var/material in new_mats) + new_mats[material] *= mats_multiplier + new_fish.set_custom_materials(new_mats) // apply_material_effects() will call update_size_and_weight() + else + new_fish.update_size_and_weight(new_fish.temp_size, new_fish.temp_weight) breeding_wait = world.time + breeding_timeout diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm index 2030c1b6158..75fa7b5f981 100644 --- a/code/modules/fishing/fish/fish_traits.dm +++ b/code/modules/fishing/fish/fish_traits.dm @@ -112,6 +112,8 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/wary/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman) . = ..() + if(rod.alpha <= /datum/material/glass::alpha) + return // Wary fish require transparent line or they're harder if(!rod.line || !(rod.line.fishing_line_traits & FISHING_LINE_CLOAKED)) .[ADDITIVE_FISHING_MOD] += FISH_TRAIT_MINOR_DIFFICULTY_BOOST @@ -123,13 +125,13 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/shiny_lover/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman) . = ..() // These fish are easier to catch with shiny hook - if(rod.hook && rod.hook.fishing_hook_traits & FISHING_HOOK_SHINY) + if(HAS_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS)) .[ADDITIVE_FISHING_MOD] -= FISH_TRAIT_MINOR_DIFFICULTY_BOOST /datum/fish_trait/shiny_lover/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman) . = ..() // These fish are harder to find without a shiny hook - if(rod.hook && rod.hook.fishing_hook_traits & FISHING_HOOK_SHINY) + if(!HAS_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS)) .[MULTIPLICATIVE_FISHING_MOD] = 0.5 /datum/fish_trait/picky_eater @@ -138,13 +140,18 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/picky_eater/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type) . = ..() + var/list/fav_baits = SSfishing.fish_properties[fish_type][FISH_PROPERTIES_FAV_BAIT] + for(var/list/identifier in fav_baits) + if(identifier[FISH_BAIT_TYPE] != FISH_BAIT_FOODTYPE) + continue + if(is_matching_bait(rod, identifier)) //Bait or no bait, it's a yummy rod. + return if(!rod.bait) .[MULTIPLICATIVE_FISHING_MOD] = 0 return if(HAS_TRAIT(rod.bait, TRAIT_OMNI_BAIT)) return - var/list/fav_baits = SSfishing.fish_properties[fish_type][FISH_PROPERTIES_FAV_BAIT] for(var/identifier in fav_baits) if(is_matching_bait(rod.bait, identifier)) //we like this bait anyway return @@ -164,7 +171,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/nocturnal/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type) . = ..() - if(rod.bait && HAS_TRAIT(rod.bait, TRAIT_BAIT_IGNORE_ENVIRONMENT)) + if(HAS_TRAIT(rod, TRAIT_ROD_IGNORE_ENVIRONMENT)) return var/turf/turf = get_turf(location) var/light_amount = turf?.get_lumcount() @@ -207,6 +214,14 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) name = "Demersal" catalog_description = "This fish tends to stay near the waterbed." +/datum/fish_trait/heavy/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type) + . = ..() + var/datum/material/material = rod.get_master_material() + if(!material) + return + //the fish weight modifier of the material influences the chance of cathing this type of fish. + .[MULTIPLICATIVE_FISHING_MOD] = material.fish_weight_modifier + /datum/fish_trait/heavy/apply_to_mob(mob/living/basic/mob) . = ..() mob.add_movespeed_modifier(/datum/movespeed_modifier/heavy_fish) @@ -226,18 +241,18 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/carnivore/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type) . = ..() + if(istype(rod.get_master_material(), /datum/material/meat)) //who cares about the bait, that fishing rod is yummy! + return if(!rod.bait) .[MULTIPLICATIVE_FISHING_MOD] = 0 return if(HAS_TRAIT(rod.bait, TRAIT_OMNI_BAIT)) return - if(isfish(rod.bait)) - return - if(!istype(rod.bait, /obj/item/food)) - .[MULTIPLICATIVE_FISHING_MOD] = 0 - return - var/obj/item/food/food_bait = rod.bait - if(!(food_bait.foodtypes & (MEAT|SEAFOOD|BUGS))) + var/list/bait_identifier = list( + FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE, + FISH_BAIT_VALUE = MEAT|SEAFOOD|BUGS, + ) + if(!is_matching_bait(rod.bait, bait_identifier)) .[MULTIPLICATIVE_FISHING_MOD] = 0 /datum/fish_trait/vegan @@ -247,18 +262,24 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/vegan/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type) . = ..() + if(istype(rod.get_master_material(), /datum/material/bamboo)) //bamboo is technically grass. + return if(!rod.bait) .[MULTIPLICATIVE_FISHING_MOD] = 0 return if(HAS_TRAIT(rod.bait, TRAIT_OMNI_BAIT)) return - if(!istype(rod.bait, /obj/item/food)) - .[MULTIPLICATIVE_FISHING_MOD] = 0 - return if(istype(rod.bait, /obj/item/food/grown)) return - var/obj/item/food/food_bait = rod.bait - if(food_bait.foodtypes & (MEAT|SEAFOOD|GORE|BUGS|DAIRY) || !(food_bait.foodtypes & (VEGETABLES|FRUIT))) + var/list/bait_liked_identifier = list( + FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE, + FISH_BAIT_VALUE = VEGETABLES|FRUIT, + ) + var/list/bait_hated_identifier = list( + FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE, + FISH_BAIT_VALUE = MEAT|SEAFOOD|GORE|BUGS|DAIRY, + ) + if(!is_matching_bait(rod.bait, bait_liked_identifier) || is_matching_bait(rod.bait, bait_hated_identifier)) .[MULTIPLICATIVE_FISHING_MOD] = 0 /datum/fish_trait/emulsijack @@ -530,6 +551,11 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) added_difficulty = 5 reagents_to_add = list(/datum/reagent/lube = 1.2) +/datum/fish_trait/lubed/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type) + . = ..() + if(istype(rod.get_master_material(), /datum/material/bananium)) //x5 chance of catching lubefish & co with a bananium rod. + .[MULTIPLICATIVE_FISHING_MOD] *= 5 + /datum/fish_trait/lubed/apply_to_fish(obj/item/fish/fish) . = ..() fish.AddComponent(/datum/component/slippery, 8 SECONDS, SLIDE|GALOSHES_DONT_HELP) diff --git a/code/modules/fishing/fish/types/air_space.dm b/code/modules/fishing/fish/types/air_space.dm index 177ae9c6e0e..b0c8208fe81 100644 --- a/code/modules/fishing/fish/types/air_space.dm +++ b/code/modules/fishing/fish/types/air_space.dm @@ -154,7 +154,7 @@ . += eyes ///Determines the speed at which the carp grows based on how big it's -/obj/item/fish/baby_carp/update_size_and_weight(new_size = average_size, new_weight = average_weight) +/obj/item/fish/baby_carp/update_size_and_weight(new_size = average_size, new_weight = average_weight, update_materials = TRUE) . = ..() var/growth_rate = 4.5 MINUTES growth_rate *= clamp(size/average_size, 0.5, 2) diff --git a/code/modules/fishing/fish/types/mining.dm b/code/modules/fishing/fish/types/mining.dm index 9e44e08ae31..f3f4137fae3 100644 --- a/code/modules/fishing/fish/types/mining.dm +++ b/code/modules/fishing/fish/types/mining.dm @@ -42,7 +42,7 @@ 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) +/obj/item/fish/chasm_crab/update_size_and_weight(new_size = average_size, new_weight = average_weight, update_materials = TRUE) . = ..() var/multiplier = 1 switch(size) @@ -134,6 +134,10 @@ evolution_types = list(/datum/fish_evolution/mastodon) beauty = FISH_BEAUTY_UGLY +/obj/item/fish/boned/Initialize(mapload, apply_qualities = TRUE) + . = ..() + ADD_TRAIT(src, TRAIT_FISH_MADE_OF_BONE, INNATE_TRAIT) + /obj/item/fish/boned/make_edible(weight_val) return //it's all bones and no meat. diff --git a/code/modules/fishing/fish/types/ruins.dm b/code/modules/fishing/fish/types/ruins.dm index 153a5bc3b7b..ccf6560139c 100644 --- a/code/modules/fishing/fish/types/ruins.dm +++ b/code/modules/fishing/fish/types/ruins.dm @@ -26,6 +26,10 @@ 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/Initialize(mapload, apply_qualities = TRUE) + . = ..() + ADD_TRAIT(src, TRAIT_FISH_MADE_OF_BONE, INNATE_TRAIT) + /obj/item/fish/mastodon/make_edible(weight_val) return //it's all bones and gibs. diff --git a/code/modules/fishing/fish/types/station.dm b/code/modules/fishing/fish/types/station.dm index 96a7ca7e99f..c4c851dc97d 100644 --- a/code/modules/fishing/fish/types/station.dm +++ b/code/modules/fishing/fish/types/station.dm @@ -146,7 +146,7 @@ add_traits(list(TRAIT_FISHING_BAIT, TRAIT_GREAT_QUALITY_BAIT), INNATE_TRAIT) ADD_TRAIT(src, TRAIT_FISH_SURVIVE_COOKING, INNATE_TRAIT) -/obj/item/fish/fryish/update_size_and_weight(new_size = average_size, new_weight = average_weight) +/obj/item/fish/fryish/update_size_and_weight(new_size = average_size, new_weight = average_weight, update_materials = FALSE) . = ..() if(!next_type) return diff --git a/code/modules/fishing/fishing_equipment.dm b/code/modules/fishing/fishing_equipment.dm index cddabd90c06..79a43fc6052 100644 --- a/code/modules/fishing/fishing_equipment.dm +++ b/code/modules/fishing/fishing_equipment.dm @@ -23,10 +23,22 @@ name = "reinforced fishing line reel" desc = "Essential for fishing in extreme environments." icon_state = "reel_green" - fishing_line_traits = FISHING_LINE_REINFORCED line_color = "#2b9c2b" wiki_desc = "Allows you to fish in lava and plasma rivers and lakes." +/obj/item/fishing_line/reinforced/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_SLOTTED, PROC_REF(on_fishing_rod_slotted)) + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_UNSLOTTED, PROC_REF(on_fishing_rod_unslotted)) + +/obj/item/fishing_line/reinforced/proc/on_fishing_rod_slotted(datum/source, obj/item/fishing_rod/rod, slot) + SIGNAL_HANDLER + ADD_TRAIT(rod, TRAIT_ROD_LAVA_USABLE, REF(src)) + +/obj/item/fishing_line/reinforced/proc/on_fishing_rod_unslotted(datum/source, obj/item/fishing_rod/rod, slot) + SIGNAL_HANDLER + REMOVE_TRAIT(rod, TRAIT_ROD_LAVA_USABLE, REF(src)) + /obj/item/fishing_line/cloaked name = "cloaked fishing line reel" desc = "Even harder to notice than the common variety." @@ -47,10 +59,23 @@ name = "fishing sinew" desc = "An all-natural fishing line made of stretched out sinew. A bit stiff, but usable to fish in extreme enviroments." icon_state = "reel_sinew" - fishing_line_traits = FISHING_LINE_REINFORCED|FISHING_LINE_STIFF + fishing_line_traits = FISHING_LINE_STIFF line_color = "#d1cca3" wiki_desc = "Crafted from sinew. It allows you to fish in lava and plasma like the reinforced line, but it'll make the minigame harder." +/obj/item/fishing_line/sinew/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_SLOTTED, PROC_REF(on_fishing_rod_slotted)) + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_UNSLOTTED, PROC_REF(on_fishing_rod_unslotted)) + +/obj/item/fishing_line/sinew/proc/on_fishing_rod_slotted(datum/source, obj/item/fishing_rod/rod, slot) + SIGNAL_HANDLER + ADD_TRAIT(rod, TRAIT_ROD_LAVA_USABLE, REF(src)) + +/obj/item/fishing_line/sinew/proc/on_fishing_rod_unslotted(datum/source, obj/item/fishing_rod/rod, slot) + SIGNAL_HANDLER + REMOVE_TRAIT(rod, TRAIT_ROD_LAVA_USABLE, REF(src)) + /** * A special line reel that let you skip the biting phase of the minigame, netting you a completion bonus, * and thrown hooked items at you, so you can rapidly catch them from afar. @@ -68,17 +93,16 @@ /obj/item/fishing_line/auto_reel/Initialize(mapload) . = ..() - RegisterSignal(src, COMSIG_FISHING_EQUIPMENT_SLOTTED, PROC_REF(line_equipped)) + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_SLOTTED, PROC_REF(on_fishing_rod_slotted)) + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_UNSLOTTED, PROC_REF(on_fishing_rod_unslotted)) -/obj/item/fishing_line/auto_reel/proc/line_equipped(datum/source, obj/item/fishing_rod/rod) +/obj/item/fishing_line/auto_reel/proc/on_fishing_rod_slotted(datum/source, obj/item/fishing_rod/rod, slot) SIGNAL_HANDLER RegisterSignal(rod, COMSIG_FISHING_ROD_HOOKED_ITEM, PROC_REF(on_hooked_item)) - RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_removed)) -/obj/item/fishing_line/auto_reel/proc/on_removed(atom/movable/source, atom/old_loc, dir, forced) +/obj/item/fishing_line/auto_reel/proc/on_fishing_rod_unslotted(datum/source, obj/item/fishing_rod/rod, slot) SIGNAL_HANDLER - UnregisterSignal(src, COMSIG_MOVABLE_MOVED) - UnregisterSignal(old_loc, COMSIG_FISHING_ROD_HOOKED_ITEM) + UnregisterSignal(rod, COMSIG_FISHING_ROD_HOOKED_ITEM) /obj/item/fishing_line/auto_reel/proc/on_hooked_item(obj/item/fishing_rod/source, atom/target, mob/living/user) SIGNAL_HANDLER @@ -176,18 +200,16 @@ /obj/item/fishing_hook/magnet/Initialize(mapload) . = ..() - RegisterSignal(src, COMSIG_FISHING_EQUIPMENT_SLOTTED, PROC_REF(hook_equipped)) + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_SLOTTED, PROC_REF(on_fishing_rod_slotted)) + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_UNSLOTTED, PROC_REF(on_fishing_rod_unslotted)) -///We make sure that the fishng rod doesn't need a bait to reliably catch non-fish loot. -/obj/item/fishing_hook/magnet/proc/hook_equipped(datum/source, obj/item/fishing_rod/rod) +/obj/item/fishing_hook/magnet/proc/on_fishing_rod_slotted(datum/source, obj/item/fishing_rod/rod, slot) SIGNAL_HANDLER - ADD_TRAIT(rod, TRAIT_ROD_REMOVE_FISHING_DUD, type) - RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_removed)) + ADD_TRAIT(rod, TRAIT_ROD_REMOVE_FISHING_DUD, REF(src)) -/obj/item/fishing_hook/magnet/proc/on_removed(atom/movable/source, atom/old_loc, dir, forced) +/obj/item/fishing_hook/magnet/proc/on_fishing_rod_unslotted(datum/source, obj/item/fishing_rod/rod, slot) SIGNAL_HANDLER - REMOVE_TRAIT(old_loc, TRAIT_ROD_REMOVE_FISHING_DUD, type) - UnregisterSignal(src, COMSIG_MOVABLE_MOVED) + REMOVE_TRAIT(rod, TRAIT_ROD_REMOVE_FISHING_DUD, REF(src)) /obj/item/fishing_hook/magnet/get_hook_bonus_multiplicative(fish_type, datum/fish_source/source) if(fish_type == FISHING_DUD || ispath(fish_type, /obj/item/fish)) @@ -199,10 +221,24 @@ /obj/item/fishing_hook/shiny name = "shiny lure hook" icon_state = "gold_shiny" - fishing_hook_traits = FISHING_HOOK_SHINY rod_overlay_icon_state = "hook_shiny_overlay" wiki_desc = "It's used to attract shiny-loving fish and make them easier to catch." +/obj/item/fishing_hook/shiny/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_SLOTTED, PROC_REF(on_fishing_rod_slotted)) + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_UNSLOTTED, PROC_REF(on_fishing_rod_unslotted)) + +/obj/item/fishing_hook/shiny/proc/on_fishing_rod_slotted(datum/source, obj/item/fishing_rod/rod, slot) + SIGNAL_HANDLER + rod.material_fish_chance += 15 //Increases the chance of catching a shiny po... erh, material fish + ADD_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS, REF(src)) + +/obj/item/fishing_hook/shiny/proc/on_fishing_rod_unslotted(datum/source, obj/item/fishing_rod/rod, slot) + SIGNAL_HANDLER + rod.material_fish_chance -= 15 + REMOVE_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS, REF(src)) + /obj/item/fishing_hook/weighted name = "weighted hook" icon_state = "weighted" diff --git a/code/modules/fishing/fishing_minigame.dm b/code/modules/fishing/fishing_minigame.dm index 646b9816a6e..8d4b312c4f6 100644 --- a/code/modules/fishing/fishing_minigame.dm +++ b/code/modules/fishing/fishing_minigame.dm @@ -9,8 +9,6 @@ #define FISH_ON_BAIT_ACCELERATION_MULT 0.6 /// The minimum velocity required for the bait to bounce #define BAIT_MIN_VELOCITY_BOUNCE 150 -/// The extra deceleration of velocity that happens when the bait switches direction -#define BAIT_DECELERATION_MULT 1.8 /// Reduce initial completion rate depending on difficulty #define MAX_FISH_COMPLETION_MALUS 15 @@ -107,10 +105,15 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) var/reeling_velocity = 1200 /// By how much the bait recoils back when hitting the bounds of the slider while idle var/bait_bounce_mult = 0.6 + /// The multiplier of deceleration of velocity that happens when the bait switches direction + var/deceleration_mult = 1.8 ///The background as shown in the minigame, and the holder of the other visual overlays var/atom/movable/screen/fishing_hud/fishing_hud + ///Keep track of the fish source from which we're pulling the reward + var/datum/fish_source/fish_source + /datum/fishing_challenge/New(datum/component/fishing_spot/comp, obj/item/fishing_rod/rod, mob/user) src.user = user used_rod = rod @@ -119,12 +122,12 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) float.spin_frequency = rod.spin_frequency RegisterSignal(location, COMSIG_QDELETING, PROC_REF(on_spot_gone)) RegisterSignal(comp, COMSIG_QDELETING, PROC_REF(on_spot_gone)) - RegisterSignal(comp.fish_source, COMSIG_FISHING_SOURCE_INTERRUPT_CHALLENGE, PROC_REF(interrupt_challenge)) - comp.fish_source.RegisterSignal(src, COMSIG_FISHING_CHALLENGE_ROLL_REWARD, TYPE_PROC_REF(/datum/fish_source, roll_reward_minigame)) - comp.fish_source.RegisterSignal(src, COMSIG_FISHING_CHALLENGE_GET_DIFFICULTY, TYPE_PROC_REF(/datum/fish_source, calculate_difficulty_minigame)) - comp.fish_source.RegisterSignal(user, COMSIG_MOB_COMPLETE_FISHING, TYPE_PROC_REF(/datum/fish_source, on_challenge_completed)) + register_reward_signals(comp.fish_source) + RegisterSignal(fish_source, COMSIG_FISHING_SOURCE_INTERRUPT_CHALLENGE, PROC_REF(interrupt_challenge)) + fish_source.RegisterSignal(user, COMSIG_MOB_COMPLETE_FISHING, TYPE_PROC_REF(/datum/fish_source, on_challenge_completed)) background = comp.fish_source.background SEND_SIGNAL(user, COMSIG_MOB_BEGIN_FISHING, src) + SEND_SIGNAL(rod, COMSIG_ROD_BEGIN_FISHING, src) GLOB.fishing_challenges_by_user[user] = src /// Enable special parameters @@ -153,6 +156,12 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) completion_loss += user.mind?.get_skill_modifier(/datum/skill/fishing, SKILL_VALUE_MODIFIER)/5 completion_gain -= user.mind?.get_skill_modifier(/datum/skill/fishing, SKILL_VALUE_MODIFIER)/7.5 + reeling_velocity *= rod.bait_speed_mult + completion_gain *= rod.completion_speed_mult + bait_bounce_mult *= rod.bounciness_mult + deceleration_mult *= rod.deceleration_mult + gravity_velocity *= rod.gravity_mult + /datum/fishing_challenge/Destroy(force) GLOB.fishing_challenges_by_user -= user if(!completed) @@ -169,6 +178,20 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) QDEL_NULL(mover) return ..() +/** + * Proc responsible for registering the signals for difficulty and possible reward. + * Call this if you want to override the fish source from which we roll rewards (preferably before the minigame phase). + */ +/datum/fishing_challenge/proc/register_reward_signals(datum/fish_source/fish_source) + if(fish_source) + fish_source.UnregisterSignal(src, list( + COMSIG_FISHING_CHALLENGE_ROLL_REWARD, + COMSIG_FISHING_CHALLENGE_GET_DIFFICULTY, + )) + src.fish_source = fish_source + fish_source.RegisterSignal(src, COMSIG_FISHING_CHALLENGE_ROLL_REWARD, TYPE_PROC_REF(/datum/fish_source, roll_reward_minigame)) + fish_source.RegisterSignal(src, COMSIG_FISHING_CHALLENGE_GET_DIFFICULTY, TYPE_PROC_REF(/datum/fish_source, calculate_difficulty_minigame)) + /datum/fishing_challenge/proc/send_alert(message) location?.balloon_alert(user, message) @@ -321,6 +344,7 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) var/extra_exp_malus = user.mind.get_skill_level(/datum/skill/fishing) - difficulty * 0.1 if(extra_exp_malus > 0) experience_multiplier /= (1 + extra_exp_malus * EXPERIENCE_MALUS_MULT) + experience_multiplier *= used_rod.experience_multiplier user.mind.adjust_experience(/datum/skill/fishing, round(seconds_spent * FISHING_SKILL_EXP_PER_SECOND * experience_multiplier)) if(user.mind.get_skill_level(/datum/skill/fishing) >= SKILL_LEVEL_LEGENDARY) user.client?.give_award(/datum/award/achievement/skill/legendary_fisher, user) @@ -663,9 +687,9 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) * have different directions, making the bait less slippery, thus easier to control */ if(bait_velocity > 0 && velocity_change < 0) - bait_velocity += max(-bait_velocity, velocity_change * BAIT_DECELERATION_MULT) + bait_velocity += max(-bait_velocity, velocity_change * deceleration_mult) else if(bait_velocity < 0 && velocity_change > 0) - bait_velocity += min(-bait_velocity, velocity_change * BAIT_DECELERATION_MULT) + bait_velocity += min(-bait_velocity, velocity_change * deceleration_mult) ///bidirectional baits stay bouyant while idle if(bidirectional && reeling_state == REELING_STATE_IDLE) @@ -832,7 +856,6 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) #undef FISH_ON_BAIT_ACCELERATION_MULT #undef BAIT_MIN_VELOCITY_BOUNCE -#undef BAIT_DECELERATION_MULT #undef MAX_FISH_COMPLETION_MALUS #undef BITING_TIME_WINDOW diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index bfa701d36eb..e876f4764f5 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -55,6 +55,21 @@ ///Prevents spamming the line casting, without affecting the player's click cooldown. COOLDOWN_DECLARE(casting_cd) + ///The chance of catching fish made of the same material of the fishing rod (if MATERIAL_EFFECTS is enabled) + var/material_fish_chance = 10 + ///The multiplier of how much experience is gained when fishing with this rod. + var/experience_multiplier = 1 + ///The multiplier of the completion gain during the minigame + var/completion_speed_mult = 1 + ///The multiplier of the speed of the bobber/bait during the minigame + var/bait_speed_mult = 1 + ///The multiplier of the decelaration during the minigame + var/deceleration_mult = 1 + ///The multiplier of the bounciness of the bobber/bait upon hitting the edges of the minigame area + var/bounciness_mult = 1 + /// The multiplier of negative velocity that pulls the bait/bobber down when not holding the click + var/gravity_mult = 1 + /obj/item/fishing_rod/Initialize(mapload) . = ..() register_context() @@ -105,6 +120,85 @@ . += span_notice("It has \a [english_list(equipped_stuff)] equipped.") if(!bait) . += span_warning("It doesn't have a bait attached to it. Fishing will be more tedious!") + if(HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISH)) + . += "" //add a new line + . += span_notice("Thanks to your fishing skills, you can examine it again for more in-depth information.") + return + +/obj/item/fishing_rod/examine_more(mob/user) + . = ..() + if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISH)) + return + + var/list/block = list() + var/get_percent = HAS_MIND_TRAIT(user, TRAIT_EXAMINE_DEEPER_FISH) + block += span_info("You think you can cast it up to [get_cast_range()] tiles away.") + block += get_stat_info(get_percent, difficulty_modifier, "Fishing will be", "easier", "harder", "with this fishing rod") + block += get_stat_info(get_percent, experience_multiplier, "You will gain experience", "faster", "slower") + block += get_stat_info(get_percent, completion_speed_mult, "You should complete the minigame", "faster", "slower") + block += get_stat_info(get_percent, bait_speed_mult, "Reeling is", "faster", "slower") + block += get_stat_info(get_percent, deceleration_mult, "Deceleration is", "faster", "slower", less_is_better = TRUE) + block += get_stat_info(get_percent, bounciness_mult, "This fishing rod is ", "bouncier", "less bouncy", "than a normal one", less_is_better = TRUE) + block += get_stat_info(get_percent, gravity_mult, "The lure will sink", "faster", "slower", span_info = TRUE) + + . += examine_block(block.Join("\n")) + + if(get_percent && (material_flags & MATERIAL_EFFECTS) && length(custom_materials)) + block = list() + block += span_info("Right now, fish caught by this fishing rod have a [get_material_fish_chance(user)]% of being made of its same materials.") + var/datum/material/material = get_master_material() + if(material.fish_weight_modifier != 1) + var/heavier = material.fish_weight_modifier > 1 ? "heavier" : "lighter" + block += span_info("Fish made of the same material as this rod tend to be [abs(material.fish_weight_modifier - 1) * 100]% [heavier].") + . += examine_block(block.Join("\n")) + + block = list() + if(HAS_TRAIT(src, TRAIT_ROD_ATTRACT_SHINY_LOVERS)) + block += span_info("This fishing rod will attract shiny-loving fish.") + if(HAS_TRAIT(src, TRAIT_ROD_IGNORE_ENVIRONMENT)) + block += span_info("Environment and light shouldn't be an issue with this rod.") + if(HAS_TRAIT_NOT_FROM(src, TRAIT_ROD_REMOVE_FISHING_DUD, INNATE_TRAIT)) // Duds are innately removed by baits, we all know that. + block += span_info("You won't catch duds with this rod.") + if(HAS_TRAIT(src, TRAIT_ROD_LAVA_USABLE)) + block += span_info("This fishing rod can be used to fish on lava.") + if(length(block)) + . += examine_block(block.Join("\n")) + +///Used in examine_more to reduce all the copypasta when getting more information about the various stats of the fishing rod. +/obj/item/fishing_rod/proc/get_stat_info(get_percent, value, prefix, easier, harder, suffix = "with this fishing rod", span_info = FALSE, less_is_better = FALSE) + if(value == 1) + return + var/percent = get_percent ? "[abs(value)]% " : "" + var/harder_easier = value > 1 ? easier : harder + . = "[prefix] [percent][harder_easier] [suffix]." + if(span_info) + return span_info(.) + if(less_is_better ? value < 1 : value > 1) + return span_nicegreen(.) + return span_danger(.) + +/obj/item/fishing_rod/apply_single_mat_effect(datum/material/custom_material, amount, multiplier) + . = ..() + difficulty_modifier += custom_material.fishing_difficulty_modifier * multiplier + cast_range += custom_material.fishing_cast_range * multiplier + experience_multiplier *= GET_MATERIAL_MODIFIER(custom_material.fishing_experience_multiplier, multiplier) + completion_speed_mult *= GET_MATERIAL_MODIFIER(custom_material.fishing_completion_speed, multiplier) + bait_speed_mult *= GET_MATERIAL_MODIFIER(custom_material.fishing_bait_speed_mult, multiplier) + deceleration_mult *= GET_MATERIAL_MODIFIER(custom_material.fishing_deceleration_mult, multiplier) + bounciness_mult *= GET_MATERIAL_MODIFIER(custom_material.fishing_bounciness_mult, multiplier) + gravity_mult *= GET_MATERIAL_MODIFIER(custom_material.fishing_gravity_mult, multiplier) + + +/obj/item/fishing_rod/remove_single_mat_effect(datum/material/custom_material, amount, multiplier) + . = ..() + difficulty_modifier -= custom_material.fishing_difficulty_modifier * multiplier + cast_range -= custom_material.fishing_cast_range * multiplier + experience_multiplier /= GET_MATERIAL_MODIFIER(custom_material.fishing_experience_multiplier, multiplier) + completion_speed_mult /= GET_MATERIAL_MODIFIER(custom_material.fishing_completion_speed, multiplier) + bait_speed_mult /= GET_MATERIAL_MODIFIER(custom_material.fishing_bait_speed_mult, multiplier) + deceleration_mult /= GET_MATERIAL_MODIFIER(custom_material.fishing_deceleration_mult, multiplier) + bounciness_mult /= GET_MATERIAL_MODIFIER(custom_material.fishing_bounciness_mult, multiplier) + gravity_mult /= GET_MATERIAL_MODIFIER(custom_material.fishing_gravity_mult, multiplier) /** * Is there a reason why this fishing rod couldn't fish in target_fish_source? @@ -116,16 +210,25 @@ /obj/item/fishing_rod/proc/reason_we_cant_fish(datum/fish_source/target_fish_source) return hook?.reason_we_cant_fish(target_fish_source) -/obj/item/fishing_rod/proc/consume_bait(atom/movable/reward) +///Called at the end of on_challenge_completed() once the reward has been spawned +/obj/item/fishing_rod/proc/on_reward_caught(atom/movable/reward, mob/user) + if(isnull(reward)) + return + var/isfish = isfish(reward) + if((material_flags & MATERIAL_EFFECTS) && isfish && length(custom_materials)) + if(prob(get_material_fish_chance(user))) + var/obj/item/fish/fish = reward + var/datum/material/material = get_master_material() + fish.set_custom_materials(list(material.type = fish.weight)) // catching things that aren't fish or alive mobs doesn't consume baits. - if(isnull(reward) || isnull(bait) || HAS_TRAIT(bait, TRAIT_BAIT_UNCONSUMABLE)) + if(isnull(bait) || HAS_TRAIT(bait, TRAIT_BAIT_UNCONSUMABLE)) return if(isliving(reward)) var/mob/living/caught_mob = reward if(caught_mob.stat == DEAD) return else - if(!isfish(reward)) + if(!isfish) return var/obj/item/fish/fish = reward if(HAS_TRAIT(bait, TRAIT_POISONOUS_BAIT) && !HAS_TRAIT(fish, TRAIT_FISH_TOXIN_IMMUNE)) @@ -140,6 +243,18 @@ QDEL_NULL(bait) update_icon() +///Returns the probability that a fish caught by this (custom material) rod will be of the same material. +/obj/item/fishing_rod/proc/get_material_fish_chance(mob/user) + var/material_chance = material_fish_chance + if(bait) + if(HAS_TRAIT(bait, TRAIT_GREAT_QUALITY_BAIT)) + material_chance += 16 + else if(HAS_TRAIT(bait, TRAIT_GOOD_QUALITY_BAIT)) + material_chance += 8 + else if(HAS_TRAIT(bait, TRAIT_BASIC_QUALITY_BAIT)) + material_chance += 4 + material_chance += user.mind?.get_skill_level(/datum/skill/fishing) * 1.5 + ///Fishing rodss should only bane fish DNA-infused spessman /obj/item/fishing_rod/proc/attempt_bane(datum/source, mob/living/fish) SIGNAL_HANDLER @@ -162,7 +277,7 @@ return playsound(src, SFX_REEL, 50, vary = FALSE) - var/time = (0.8 - round(user.mind?.get_skill_level(/datum/skill/fishing) * 0.04, 0.1)) SECONDS + var/time = (0.8 - round(user.mind?.get_skill_level(/datum/skill/fishing) * 0.04, 0.1)) SECONDS * bait_speed_mult if(!do_after(user, time, currently_hooked, timed_action_flags = IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(fishing_line_check)))) return @@ -171,7 +286,7 @@ return //About thirty minutes of non-stop reeling to get from zero to master... not worth it but hey, you do what you do. - user.mind?.adjust_experience(/datum/skill/fishing, time * 0.13) + user.mind?.adjust_experience(/datum/skill/fishing, time * 0.13 * experience_multiplier) //Try to move it 'till it's under the user's feet, then try to pick it up if(isitem(currently_hooked)) @@ -221,13 +336,14 @@ currently_hooked = null /obj/item/fishing_rod/proc/get_cast_range(mob/living/user) - . = cast_range + . = max(cast_range, 1) if(!user && !isliving(loc)) return user = loc if(!user.is_holding(src) || !user.mind) return . += round(user.mind.get_skill_level(/datum/skill/fishing) * 0.3) + return max(., 1) /obj/item/fishing_rod/dropped(mob/user, silent) . = ..() @@ -336,12 +452,14 @@ /// Line part by the rod. if(reel_overlay) var/mutable_appearance/reel_appearance = mutable_appearance(icon, reel_overlay) + reel_appearance.appearance_flags = RESET_COLOR reel_appearance.color = line_color . += reel_appearance // Line & hook is also visible when only bait is equipped but it uses default appearances then if(hook || bait) var/mutable_appearance/line_overlay = mutable_appearance(icon, "line_overlay") + line_overlay.appearance_flags = RESET_COLOR line_overlay.color = line_color . += line_overlay . += hook?.rod_overlay_icon_state || "hook_overlay" @@ -501,20 +619,27 @@ else CRASH("set_slot called with an undefined slot: [slot]") - SEND_SIGNAL(equipment, COMSIG_FISHING_EQUIPMENT_SLOTTED, src) + SEND_SIGNAL(equipment, COMSIG_ITEM_FISHING_ROD_SLOTTED, src, slot) /obj/item/fishing_rod/Exited(atom/movable/gone, direction) . = ..() + var/slot if(gone == bait) + slot = ROD_SLOT_BAIT bait = null REMOVE_TRAIT(src, TRAIT_ROD_REMOVE_FISHING_DUD, INNATE_TRAIT) if(gone == line) + slot = ROD_SLOT_LINE cast_range -= FISHING_ROD_REEL_CAST_RANGE line = null if(gone == hook) + slot = ROD_SLOT_HOOK QDEL_NULL(fishing_line) hook = null + if(slot) + SEND_SIGNAL(gone, COMSIG_ITEM_FISHING_ROD_UNSLOTTED, src, slot) + ///Found in the fishing toolbox (the hook and line are separate items) /obj/item/fishing_rod/unslotted hook = null @@ -547,6 +672,11 @@ ui_description = "A collapsible fishing rod that can fit within a backpack." wiki_description = "It has to be bought from Cargo." reel_overlay = "reel_telescopic" + completion_speed_mult = 1.1 + bait_speed_mult = 1.1 + deceleration_mult = 1.1 + bounciness_mult = 0.9 + gravity_mult = 0.9 ///The force of the item when extended. var/active_force = 8 @@ -615,6 +745,12 @@ cast_range = 5 line = /obj/item/fishing_line/bouncy hook = /obj/item/fishing_hook/weighted + completion_speed_mult = 1.55 + bait_speed_mult = 1.2 + deceleration_mult = 1.55 + bounciness_mult = 0.3 + gravity_mult = 1.2 + material_fish_chance = 33 //if somehow you metalgen it. /obj/item/fishing_rod/tech name = "advanced fishing rod" @@ -624,6 +760,10 @@ icon_state = "fishing_rod_science" reel_overlay = "reel_science" bait = /obj/item/food/bait/doughball/synthetic/unconsumable + completion_speed_mult = 1.1 + bait_speed_mult = 1.1 + deceleration_mult = 1.1 + gravity_mult = 1.2 /obj/item/fishing_rod/tech/Initialize(mapload) . = ..() @@ -650,6 +790,23 @@ return return ..() +/obj/item/fishing_rod/material + name = "material fishing rod" //name shown on the autowiki. + desc = "A custom fishing rod from your local autolathe." + icon_state = "fishing_rod_material" + reel_overlay = "reel_material" + ui_description = "An autolathe-printable fishing rod made of some material." + wiki_description = "Different materials can have different effects. They also catch fish made of the same material used to print the rod." + material_flags = MATERIAL_EFFECTS|MATERIAL_AFFECT_STATISTICS|MATERIAL_COLOR|MATERIAL_ADD_PREFIX + +/obj/item/fishing_rod/material/Initialize(mapload) + . = ..() + name = "fishing_rod" + +/obj/item/fishing_rod/material/finalize_remove_material_effects(list/materials) + . = ..() + name = "fishing_rod" //so it doesn't reset to "material fishing rod" + #undef ROD_SLOT_BAIT #undef ROD_SLOT_LINE #undef ROD_SLOT_HOOK diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm index 0e80ee423ca..2ad6a2bc5bb 100644 --- a/code/modules/fishing/sources/_fish_source.dm +++ b/code/modules/fishing/sources/_fish_source.dm @@ -16,6 +16,8 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) /mob/living/basic/frog = FISH_ICON_CRITTER, /mob/living/basic/carp = FISH_ICON_DEF, /mob/living/basic/mining = FISH_ICON_HOSTILE, + /mob/living/basic/skeleton = FISH_ICON_BONE, + /mob/living/basic/stickman = FISH_ICON_HOSTILE, /obj/effect/decal/remains = FISH_ICON_BONE, /obj/effect/mob_spawn/corpse = FISH_ICON_BONE, /obj/effect/spawner/message_in_a_bottle = FISH_ICON_BOTTLE, @@ -41,6 +43,9 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) /obj/item/fish/stingray = FISH_ICON_WEAPON, /obj/item/fish/swordfish = FISH_ICON_WEAPON, /obj/item/fish/zipzap = FISH_ICON_ELECTRIC, + /obj/item/instrument/trumpet/spectral = FISH_ICON_BONE, + /obj/item/instrument/saxophone/spectral = FISH_ICON_BONE, + /obj/item/instrument/trombone/spectral = FISH_ICON_BONE, /obj/item/knife/carp = FISH_ICON_WEAPON, /obj/item/seeds/grass = FISH_ICON_SEED, /obj/item/seeds/random = FISH_ICON_SEED, @@ -79,14 +84,14 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) var/catalog_description /// Background image name from /datum/asset/simple/fishing_minigame var/background = "background_default" - /// It true, repeated and large explosions won't be as efficient. This is usually for fish sources that cover multiple turfs (i.e. rivers, oceans). - var/explosive_malus = FALSE - /// If explosive_malus is true, this will be used to keep track of the turfs where an explosion happened for when we'll spawn the loot. + var/fish_source_flags = NONE + /// If FISH_SOURCE_FLAG_EXPLOSIVE_MALUS is set, this will be used to keep track of the turfs where an explosion happened for when we'll spawn the loot. var/list/exploded_turfs ///When linked to a fishing portal, this will be the icon_state of this option in the radial menu var/radial_state = "default" ///When selected by the fishing portal, this will be the icon_state of the overlay shown on the machine. var/overlay_state = "portal_aquarium" + /// Mindless mobs that can fish will never pull up items on this list var/static/list/profound_fisher_blacklist = typecacheof(list( /mob/living/basic/mining/lobstrosity, @@ -241,7 +246,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) if(reward) user.add_mob_memory(/datum/memory/caught_fish, protagonist = user, deuteragonist = reward.name) SEND_SIGNAL(challenge.used_rod, COMSIG_FISHING_ROD_CAUGHT_FISH, reward, user) - challenge.used_rod.consume_bait(reward) + challenge.used_rod.on_reward_caught(reward, user) /// Gives out the reward if possible /datum/fish_source/proc/dispense_reward(reward_path, mob/fisherman, turf/fishing_spot) @@ -461,7 +466,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) examine_text += span_info("[info]: [english_list(known_fishes)].") /datum/fish_source/proc/spawn_reward_from_explosion(atom/location, severity) - if(!explosive_malus) + if(!(fish_source_flags & FISH_SOURCE_FLAG_EXPLOSIVE_MALUS)) explosive_spawn(location, severity) return if(isnull(exploded_turfs)) diff --git a/code/modules/fishing/sources/source_types.dm b/code/modules/fishing/sources/source_types.dm index 382ffeb3867..4663a7f2f10 100644 --- a/code/modules/fishing/sources/source_types.dm +++ b/code/modules/fishing/sources/source_types.dm @@ -31,7 +31,7 @@ /obj/item/fish/swordfish = 5 MINUTES, ) fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 5 - explosive_malus = TRUE + fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_MALUS /datum/fish_source/ocean/beach catalog_description = "Beach shore water" @@ -76,7 +76,7 @@ /obj/item/fish/pike = 4 MINUTES, ) fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 5 - explosive_malus = TRUE + fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_MALUS /datum/fish_source/sand catalog_description = "Sand" @@ -89,7 +89,7 @@ /obj/item/coin/gold = 3, ) fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 20 - explosive_malus = TRUE + fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_MALUS /datum/fish_source/cursed_spring catalog_description = null //it's a secret (sorta, I know you're reading this) @@ -104,7 +104,7 @@ /obj/item/fishing_rod/telescopic/master = 1, ) fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 25 - explosive_malus = TRUE + fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_MALUS /datum/fish_source/portal fish_table = list( @@ -240,8 +240,9 @@ catalog_description = null // it'd make a bad entry in the catalog. radial_name = "Randomizer" overlay_state = "portal_randomizer" - var/static/list/all_portal_fish_sources_at_once radial_state = "misaligned_question_mark" + fish_source_flags = FISH_SOURCE_FLAG_NO_BLUESPACE_ROD + var/static/list/all_portal_fish_sources_at_once ///Generate the fish table if we don't have one already. /datum/fish_source/portal/random/on_fishing_spot_init(datum/component/fishing_spot/spot) @@ -350,12 +351,12 @@ ) fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 10 - explosive_malus = TRUE + fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_MALUS /datum/fish_source/lavaland/reason_we_cant_fish(obj/item/fishing_rod/rod, mob/fisherman, atom/parent) . = ..() - if(!rod.line || !(rod.line.fishing_line_traits & FISHING_LINE_REINFORCED)) - return "You'll need reinforced fishing line to fish in there" + if(!HAS_TRAIT(rod, TRAIT_ROD_LAVA_USABLE)) + return "You'll need reinforced fishing line to fish in there." /datum/fish_source/lavaland/icemoon catalog_description = "Liquid plasma vents" @@ -416,6 +417,7 @@ /obj/item/fish/holo/halffish = 5, ) fishing_difficulty = FISHING_EASY_DIFFICULTY + fish_source_flags = FISH_SOURCE_FLAG_NO_BLUESPACE_ROD /datum/fish_source/holographic/on_fishing_spot_init(datum/component/fishing_spot/spot) ADD_TRAIT(spot.parent, TRAIT_UNLINKABLE_FISHING_SPOT, REF(src)) //You would have to be inside the holodeck anyway... diff --git a/code/modules/instruments/items.dm b/code/modules/instruments/items.dm index 4cf7df5a671..d9225b8e905 100644 --- a/code/modules/instruments/items.dm +++ b/code/modules/instruments/items.dm @@ -112,15 +112,19 @@ force = 0 attack_verb_continuous = list("plays", "jazzes", "trumpets", "mourns", "doots", "spooks") attack_verb_simple = list("play", "jazz", "trumpet", "mourn", "doot", "spook") + var/single_use = FALSE /obj/item/instrument/trumpet/spectral/Initialize(mapload) . = ..() - AddElement(/datum/element/spooky) + AddElement(/datum/element/spooky, too_spooky = !single_use, single_use = single_use) /obj/item/instrument/trumpet/spectral/attack(mob/living/target_mob, mob/living/user, params) playsound(src, 'sound/runtime/instruments/trombone/En4.mid', 1000, 1, -1) return ..() +/obj/item/instrument/trumpet/spectral/one_doot + single_use = TRUE + /obj/item/instrument/saxophone name = "saxophone" desc = "This soothing sound will be sure to leave your audience in tears." @@ -136,15 +140,19 @@ force = 0 attack_verb_continuous = list("plays", "jazzes", "saxxes", "mourns", "doots", "spooks") attack_verb_simple = list("play", "jazz", "sax", "mourn", "doot", "spook") + var/single_use = FALSE /obj/item/instrument/saxophone/spectral/Initialize(mapload) . = ..() - AddElement(/datum/element/spooky) + AddElement(/datum/element/spooky, too_spooky = !single_use, single_use = single_use) /obj/item/instrument/saxophone/spectral/attack(mob/living/target_mob, mob/living/user, params) playsound(src, 'sound/runtime/instruments/trombone/En4.mid', 1000, 1, -1) return ..() +/obj/item/instrument/saxophone/spectral/one_doot + single_use = TRUE + /obj/item/instrument/trombone name = "trombone" desc = "How can any pool table ever hope to compete?" @@ -160,10 +168,14 @@ force = 0 attack_verb_continuous = list("plays", "jazzes", "trombones", "mourns", "doots", "spooks") attack_verb_simple = list("play", "jazz", "trombone", "mourn", "doot", "spook") + var/single_use = FALSE /obj/item/instrument/trombone/spectral/Initialize(mapload) . = ..() - AddElement(/datum/element/spooky) + AddElement(/datum/element/spooky, too_spooky = !single_use, single_use = single_use) + +/obj/item/instrument/trombone/spectral/one_doot + single_use = TRUE /obj/item/instrument/trombone/spectral/attack(mob/living/target_mob, mob/living/user, params) playsound(src, 'sound/runtime/instruments/trombone/Cn4.mid', 1000, 1, -1) diff --git a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm index f70ffb329d2..09da70baa16 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm @@ -226,6 +226,12 @@ holder.remove_reagent(/datum/reagent/consumable/capsaicin, seconds_per_tick) return ..() || . +/datum/reagent/milk/used_on_fish(obj/item/fish/fish) + if(HAS_TRAIT(fish, TRAIT_FISH_MADE_OF_BONE)) + fish.adjust_health(fish.health + initial(fish.health) * max(fish.get_hunger() * 0.5, 0.12)) + fish.sate_hunger() + return TRUE + /datum/reagent/consumable/soymilk name = "Soy Milk" description = "An opaque white liquid made from soybeans." diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 1c340cbc657..a5d8fdaae18 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -1230,6 +1230,11 @@ to_chat(affected_mob, span_warning("A phantom limb hurts!")) affected_mob.say("Why are we still here, just to suffer?", forced = /datum/reagent/toxin/bonehurtingjuice) +/datum/reagent/toxin/bonehurtingjuice/used_on_fish(obj/item/fish/fish) + if(HAS_TRAIT(fish, TRAIT_FISH_MADE_OF_BONE)) + fish.adjust_health(fish.health - 30) + return TRUE + /datum/reagent/toxin/bungotoxin name = "Bungotoxin" description = "A horrible cardiotoxin that protects the humble bungo pit." diff --git a/code/modules/research/designs/autolathe/service_designs.dm b/code/modules/research/designs/autolathe/service_designs.dm index 29523bf5820..21fda49fc1b 100644 --- a/code/modules/research/designs/autolathe/service_designs.dm +++ b/code/modules/research/designs/autolathe/service_designs.dm @@ -531,7 +531,7 @@ name = "Fishing Rod" id = "fishing_rod" build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE - materials = list(/datum/material/iron =SMALL_MATERIAL_AMOUNT * 2, /datum/material/glass =SMALL_MATERIAL_AMOUNT * 2) + materials = list(/datum/material/iron =SMALL_MATERIAL_AMOUNT * 2, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2) build_path = /obj/item/fishing_rod category = list( RND_CATEGORY_INITIAL, @@ -539,6 +539,17 @@ ) departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_SCIENCE +/datum/design/fishing_rod_material + name = "Material Fishing Rod" + id = "fishing_rod_material" + build_type = AUTOLATHE + materials = list(MAT_CATEGORY_ITEM_MATERIAL = SMALL_MATERIAL_AMOUNT * 4) + build_path = /obj/item/fishing_rod/material + category = list( + RND_CATEGORY_INITIAL, + RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_SERVICE, + ) + /datum/design/fish_case name = "Stasis Fish Case" id = "fish_case" diff --git a/icons/obj/fishing.dmi b/icons/obj/fishing.dmi index 36344d15e019c5fe63ca686b13b86dc1e6b5e922..d97384f1f799494606d9e0c3b99935d60727e9bc 100644 GIT binary patch literal 31186 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 oIK!q0W4&f0q{pnNkj3Mf?4qY0?rI^{Qv*} literal 28941 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�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