Fishing hook and reel line tweaks (also qol for the fish bounties) (#80599)

## About The Pull Request
This PR includes a series of small changes and additions to hooks and
reel lines, as well as the aforementioned bounties. My objective here is
to make the (base) hook and reel line feel more useful. Ditto with a few
other things.

Here's the list of changes:
- Almost all fishing rods come with a hook and reel line pre-installed.
The rod from the fishing toolbox comes with separate hook and reel line.
- Without a hook, you cannot fish. Without a reel line, the cast range
is reduced (from 5 to 3).
- Fishing with a reel line installed provides a mild boon to completion
speed by default.
- The sinewy reel line (craftable from lavaland mobs) can now be used to
fish on lava / liquid plasma, but it's a bit stiff and gives a mild
malus to completion speed.
- Unlike other hooks, the rescue and jawed hooks now allow you to cast a
line and reel in living mobs too, not only items. They even get a status
alert they can click to un-snag themselves, or move away far enough to
achieve the same effect.
- Master fishing rod buffed. It now comes with a flexible reel line and
weighted hook pre-installed, and can be cast further than other rods.
- On top of that, the jawed hook will slow down living mobs snagged by
it and is a bit harder to remove.
- when casting a line, it will now show the icon of the current hook as
projectile, not a generic one.
- Reeling now correctly checks movement resistance and anchorage.
- Reeling an object (or a mob) now plays a sound. Ditto for
installing/removing slotted items.
- A few balloon alert feedbacks.
- Minor code improvements.
- The jawed hook should look a smidge sharper.
- reel lines are now small items.

## Why It's Good For The Game
The base hook and reel line didn't do anything begin with, and you would
do just fine without them. In the end it's just a bit unintuitive for
them not to be a requirement.

Beside that, it makes sense for the rescue hook to be able to snag mobs,
so they may as well be used to drag people out of lava or liquid plasma.
As for the jawed hook, it always felt to me a bit underwhelming and a
bit of a filler I had cooked up on the moment. It really could have some
'tactical' utility in virtue of being one of those niche black market
items.

## Changelog

🆑
add: Most fishing rods come with a hook and line preinstalled. Fishing
toolboxes come with separate reel and lines as usual.
balance: Fishing hooks are now required to fish.
balance: Without a reel line, the range of fishing rods is reduced by
two tiles. Conversely, having one installed gives a mild buff to the
minigame completion speed.
balance: The craftable sinewy reel line can now be used to fish on lava
or liquid plasma, but it's a bit harder to use.
balance: The rare-to-find-in-maintenance master fishing rod now comes
with a flexible line and weighted hook preinstalled, and has better
range than other rods.
balance: Fishing reel lines are now small enough to fit pockets.
add: The rescue and jawed hook can now snag and reel in mobs, not only
items. The jawed hook also slows down when applied, a la beartrap.
qol: Fish bounties now accept filled (stasis) fish cases.
qol: Several balloon alerts for fishing rod interactions.
fix: Reeling in items (and mobs) now respects movement resistance and
anchorage.
fix: Fixed the fishing rod equipment UI being too small to fit its
components.
sound: Reeling in something now plays a sound.
/🆑
This commit is contained in:
Ghom
2024-01-17 04:37:52 -05:00
committed by GitHub
parent fa9a2a701a
commit aa9caef0ed
26 changed files with 305 additions and 161 deletions
+1
View File
@@ -7,3 +7,4 @@
#define DOAFTER_SOURCE_HEAL_TOUCH "doafter_heal_touch"
#define DOAFTER_SOURCE_PLANTING_DEVICE "doafter_planting_device"
#define DOAFTER_SOURCE_CHARGE_CRANKRECHARGE "doafter_charge_crank_recharge"
#define DOAFTER_SOURCE_REMOVING_HOOK "doafter_removing_hook"
+13 -6
View File
@@ -18,6 +18,11 @@
#define FISH_AI_ZIPPY "zippy"
#define FISH_AI_SLOW "slow"
///Slot defines for the fishing rod and its equipment
#define ROD_SLOT_BAIT "bait"
#define ROD_SLOT_LINE "line"
#define ROD_SLOT_HOOK "hook"
#define ADDITIVE_FISHING_MOD "additive"
#define MULTIPLICATIVE_FISHING_MOD "multiplicative"
@@ -45,19 +50,21 @@
#define FISHING_LINE_REINFORCED (1 << 1)
/// Much like FISHING_HOOK_ENSNARE but for the fishing line.
#define FISHING_LINE_BOUNCY (1 << 2)
/// 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)
///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 << 2)
#define FISHING_MINIGAME_RULE_BIDIRECTIONAL (1 << 0)
///Prevents the player from losing the minigame when the completion reaches 0
#define FISHING_MINIGAME_RULE_NO_ESCAPE (1 << 3)
#define FISHING_MINIGAME_RULE_NO_ESCAPE (1 << 1)
///Automatically kills the fish after a while, at the cost of killing it
#define FISHING_MINIGAME_RULE_KILL (1 << 4)
#define FISHING_MINIGAME_RULE_KILL (1 << 2)
///Prevents the fishing skill from having an effect on the minigame and experience from being awarded
#define FISHING_MINIGAME_RULE_NO_EXP (1 << 5)
#define FISHING_MINIGAME_RULE_NO_EXP (1 << 3)
///If enabled, the minigame will occasionally screw around and invert the velocity of the bait
#define FISHING_MINIGAME_RULE_ANTIGRAV (1 << 6)
#define FISHING_MINIGAME_RULE_ANTIGRAV (1 << 4)
///Will filp the minigame hud for the duration of the effect
#define FISHING_MINIGAME_RULE_FLIP (1 << 7)
#define FISHING_MINIGAME_RULE_FLIP (1 << 5)
///all the effects that are active and will last for a few seconds before triggering a cooldown
#define FISHING_MINIGAME_ACTIVE_EFFECTS (FISHING_MINIGAME_RULE_ANTIGRAV|FISHING_MINIGAME_RULE_FLIP)
+1
View File
@@ -172,3 +172,4 @@ GLOBAL_LIST_INIT(announcer_keys, list(
#define SFX_TREE_CHOP "tree_chop"
#define SFX_ROCK_TAP "rock_tap"
#define SFX_SEAR "sear"
#define SFX_REEL "reel"
+23
View File
@@ -55,3 +55,26 @@
)
result = /obj/item/stack/medical/bandage/makeshift
category = CAT_TOOLS
/datum/crafting_recipe/bone_rod
name = "Bone Fishing Rod"
result = /obj/item/fishing_rod/bone
time = 5 SECONDS
reqs = list(/obj/item/stack/sheet/leather = 1,
/obj/item/stack/sheet/sinew = 2,
/obj/item/stack/sheet/bone = 2)
category = CAT_TOOLS
/datum/crafting_recipe/sinew_line
name = "Sinew Fishing Line Reel"
result = /obj/item/fishing_line/sinew
reqs = list(/obj/item/stack/sheet/sinew = 2)
time = 2 SECONDS
category = CAT_TOOLS
/datum/crafting_recipe/bone_hook
name = "Goliath Bone Hook"
result = /obj/item/fishing_hook/bone
reqs = list(/obj/item/stack/sheet/bone = 1)
time = 2 SECONDS
category = CAT_TOOLS
+1 -1
View File
@@ -33,7 +33,7 @@
var/obj/item/fishing_rod/rod = possibly_rod
if(!istype(rod))
return
if(HAS_TRAIT(user,TRAIT_GONE_FISHING) || rod.currently_hooked_item)
if(HAS_TRAIT(user,TRAIT_GONE_FISHING) || rod.fishing_line)
user.balloon_alert(user, "already fishing")
return COMPONENT_NO_AFTERATTACK
var/denial_reason = fish_source.reason_we_cant_fish(rod, user, parent)
@@ -0,0 +1,63 @@
///Status effect applied when casting a fishing rod at someone, provided the attached fishing hook allows it.
/datum/status_effect/grouped/hooked
id = "hooked"
duration = -1
tick_interval = -1
status_type = STATUS_EFFECT_MULTIPLE
alert_type = /atom/movable/screen/alert/status_effect/hooked
/datum/status_effect/grouped/hooked/proc/try_unhook()
return do_after(owner, 2 SECONDS, timed_action_flags = IGNORE_USER_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(still_exists)), interaction_key = DOAFTER_SOURCE_REMOVING_HOOK)
/datum/status_effect/grouped/hooked/proc/still_exists()
return !QDELETED(src)
/datum/status_effect/grouped/hooked/on_creation(mob/living/new_owner, datum/beam/fishing_line/source)
. = ..()
if(!.) //merged with an existing effect
return
RegisterSignal(source, COMSIG_QDELETING, PROC_REF(on_fishing_line_deleted))
/datum/status_effect/grouped/hooked/merge_with_existing(datum/status_effect/grouped/hooked/existing, datum/beam/fishing_line/source)
existing.RegisterSignal(source, COMSIG_QDELETING, PROC_REF(on_fishing_line_deleted))
/datum/status_effect/grouped/hooked/proc/on_fishing_line_deleted(datum/source)
SIGNAL_HANDLER
owner.remove_status_effect(type, source)
/atom/movable/screen/alert/status_effect/hooked
name = "Snagged By Hook"
desc = "You're being caught like a fish by some asshat! Click to safely remove the hook or move away far enough to snap it off."
icon_state = "hooked"
/atom/movable/screen/alert/status_effect/hooked/Click()
if(!owner.can_resist())
return
owner.balloon_alert(owner, "removing hook...")
var/datum/status_effect/grouped/hooked/effect = owner.has_status_effect(attached_effect.type)
if(!effect.try_unhook())
return
owner.balloon_alert(owner, "hook removed")
var/datum/beam/fishing_line/rand_source = pick(effect.sources)
qdel(rand_source)
///Version used by the jawed fishing hook, which also applies slowdown
/datum/status_effect/grouped/hooked/jaws
id = "hooked_jaws"
alert_type = /atom/movable/screen/alert/status_effect/hooked/jaws
/datum/status_effect/grouped/hooked/jaws/on_apply()
. = ..()
owner.add_movespeed_modifier(/datum/movespeed_modifier/hook_jawed)
/datum/status_effect/grouped/hooked/jaws/on_remove()
. = ..()
owner.remove_movespeed_modifier(/datum/movespeed_modifier/hook_jawed)
/datum/status_effect/grouped/hooked/jaws/try_unhook()
return do_after(owner, 10 SECONDS, extra_checks = CALLBACK(src, PROC_REF(still_exists)), interaction_key = DOAFTER_SOURCE_REMOVING_HOOK)
/atom/movable/screen/alert/status_effect/hooked/jaws
name = "Snagged By Jaws"
desc = "You've been snagged by some sort of beartrap-slash-fishing-hook-gizmo! Click to safely remove the hook or move away far enough to snap it off."
icon_state = "hooked_jaws"
@@ -9,12 +9,16 @@
var/datum/status_effect/grouped/existing = new_owner.has_status_effect(type)
if(existing)
existing.sources |= source
merge_with_existing(existing, source)
qdel(src)
return FALSE
sources |= source
return ..()
/datum/status_effect/grouped/proc/merge_with_existing(datum/status_effect/grouped/existing, source)
return
/datum/status_effect/grouped/before_remove(source)
sources -= source
return !length(sources)
+8
View File
@@ -416,4 +416,12 @@
soundin = pick('sound/effects/rocktap1.ogg', 'sound/effects/rocktap2.ogg', 'sound/effects/rocktap3.ogg')
if(SFX_SEAR)
soundin = 'sound/weapons/sear.ogg'
if(SFX_REEL)
soundin = pick(
'sound/items/reel1.ogg',
'sound/items/reel2.ogg',
'sound/items/reel3.ogg',
'sound/items/reel4.ogg',
'sound/items/reel5.ogg',
)
return soundin
+19 -7
View File
@@ -215,7 +215,7 @@
description = "We need fish to populate our aquariums with. Fishes that are dead or bought from cargo will only be paid half as much."
reward = CARGO_CRATE_VALUE * 9
required_count = 4
wanted_types = list(/obj/item/fish = TRUE)
wanted_types = list(/obj/item/fish = TRUE, /obj/item/storage/fish_case = TRUE)
///the penalty for shipping dead/bought fish, which can subtract up to half the reward in total.
var/shipping_penalty
@@ -223,11 +223,27 @@
..()
shipping_penalty = reward * 0.5 / required_count
/datum/bounty/item/assistant/fish/applies_to(obj/shipped)
. = ..()
if(!.)
return
var/obj/item/fish/fishie = shipped
if(istype(shipped, /obj/item/storage/fish_case))
fishie = locate() in shipped
if(!fishie || !is_type_in_typecache(fishie, wanted_types))
return FALSE
return can_ship_fish(fishie)
/datum/bounty/item/assistant/fish/proc/can_ship_fish(obj/item/fish/fishie)
return TRUE
/datum/bounty/item/assistant/fish/ship(obj/shipped)
. = ..()
if(!.)
return
var/obj/item/fish/fishie = shipped
if(istype(shipped, /obj/item/storage/fish_case))
fishie = locate() in shipped
if(fishie.status == FISH_DEAD || HAS_TRAIT(fishie, TRAIT_FISH_FROM_CASE))
reward -= shipping_penalty
@@ -243,11 +259,7 @@
name = "[fluid_type] Fish"
description = "We need [lowertext(fluid_type)] fish to populate our aquariums with. Fishes that are dead or bought from cargo will only be paid half as much."
/datum/bounty/item/assistant/fish/fluid/applies_to(obj/shipped)
. = ..()
if(!.)
return
var/obj/item/fish/fishie = shipped
/datum/bounty/item/assistant/fish/fluid/can_ship_fish(obj/item/fish/fishie)
return compatible_fluid_type(fishie.required_fluid_type, fluid_type)
///A subtype of the fish bounty that requires specific fish types. The higher their rarity, the better the pay.
@@ -255,7 +267,7 @@
description = "Our prestigious fish collection is currently lacking a few specific species. Fishes that are dead or bought from cargo will only be paid half as much."
reward = CARGO_CRATE_VALUE * 16
required_count = 3
wanted_types = list()
wanted_types = list(/obj/item/storage/fish_case = TRUE)
/datum/bounty/item/assistant/fish/specific/New()
var/static/list/choosable_fishes
+11 -15
View File
@@ -28,10 +28,7 @@
.["rod_types"] = typesof(/obj/item/fishing_rod)
.["hook_types"] = typesof(/obj/item/fishing_hook)
.["line_types"] = typesof(/obj/item/fishing_line)
var/list/spot_keys = list()
for(var/key in GLOB.preset_fish_sources)
spot_keys += key
.["spot_types"] = subtypesof(/datum/fish_source) + spot_keys
.["spot_types"] = subtypesof(/datum/fish_source)
/datum/fishing_calculator/ui_data(mob/user)
return list("info" = current_table)
@@ -45,22 +42,20 @@
var/bait_type = text2path(params["bait"])
var/hook_type = text2path(params["hook"])
var/line_type = text2path(params["line"])
var/spot_type = text2path(params["spot"]) || params["spot"] //can be also key from presets
//validate here against nonsense values
var/datum/fish_source/spot
if(ispath(spot_type))
spot = new spot_type
else
spot = GLOB.preset_fish_sources[spot_type]
var/datum/fish_source/spot = GLOB.preset_fish_sources[text2path(params["spot"])]
var/obj/item/fishing_rod/temporary_rod = new rod_type
qdel(temporary_rod.bait)
qdel(temporary_rod.line)
qdel(temporary_rod.hook)
if(bait_type)
temporary_rod.bait = new bait_type
temporary_rod.set_slot(new bait_type(temporary_rod), ROD_SLOT_BAIT)
if(hook_type)
temporary_rod.hook = new hook_type
temporary_rod.set_slot(new hook_type(temporary_rod), ROD_SLOT_HOOK)
if(line_type)
temporary_rod.line = new line_type
temporary_rod.set_slot(new line_type(temporary_rod), ROD_SLOT_HOOK)
var/result_table = list()
var/modified_table = spot.get_modified_fish_table(temporary_rod,user)
for(var/result_type in spot.fish_table) // through this not modified to display 0 chance ones too
@@ -71,5 +66,6 @@
info["count"] = spot.fish_counts[result_type] || "Infinite"
result_table += list(info)
current_table = result_table
qdel(temporary_rod)
return TRUE
+31 -19
View File
@@ -8,9 +8,10 @@
/obj/item/fishing_line
name = "fishing line reel"
desc = "Simple fishing line."
desc = "A fishing line. In spite of its simplicity, the added length will make fishing a speck easier."
icon = 'icons/obj/fishing.dmi'
icon_state = "reel_blue"
w_class = WEIGHT_CLASS_SMALL
///A list of traits that this fishing line has, checked by fish traits and the minigame.
var/list/fishing_line_traits
/// Color of the fishing line
@@ -39,23 +40,18 @@
/obj/item/fishing_line/sinew
name = "fishing sinew"
desc = "An all-natural fishing line made of stretched out sinew."
desc = "An all-natural fishing line made of stretched out sinew. A bit stiff, but usable to fish in extreme enviroments."
icon = 'icons/obj/fishing.dmi'
icon_state = "reel_sinew"
icon_state = "reel_green"
fishing_line_traits = FISHING_LINE_REINFORCED|FISHING_LINE_STIFF
line_color = "#d1cca3"
/datum/crafting_recipe/sinew_line
name = "Sinew Fishing Line Reel"
result = /obj/item/fishing_line/sinew
reqs = list(/obj/item/stack/sheet/sinew = 2)
time = 2 SECONDS
category = CAT_TOOLS
// Hooks
/obj/item/fishing_hook
name = "simple fishing hook"
desc = "A simple fishing hook."
desc = "A simple fishing hook. Don't expect to hook onto anything without one."
icon = 'icons/obj/fishing.dmi'
icon_state = "hook"
w_class = WEIGHT_CLASS_TINY
@@ -85,6 +81,13 @@
/obj/item/fishing_hook/proc/get_hook_bonus_multiplicative(fish_type)
return FISHING_DEFAULT_HOOK_BONUS_MULTIPLICATIVE
///Check if tha target can be caught by the hook
/obj/item/fishing_hook/proc/can_be_hooked(atom/target)
return isitem(target)
///Any special effect when hooking a target that's not managed by the fishing rod.
/obj/item/fishing_hook/proc/hook_attached(atom/target, obj/item/fishing_rod/rod)
return
/**
* Is there a reason why this hook couldn't fish in target_fish_source?
@@ -133,6 +136,13 @@
rod_overlay_icon_state = "hook_rescue_overlay"
chasm_detritus_type = /datum/chasm_detritus/restricted/bodies
/obj/item/fishing_hook/rescue/can_be_hooked(atom/target)
return ..() || isliving(target)
/obj/item/fishing_hook/rescue/hook_attached(atom/target, obj/item/fishing_rod/rod)
if(isliving(target))
var/mob/living/living_target = target
living_target.apply_status_effect(/datum/status_effect/grouped/hooked, rod.fishing_line)
// This hook can only fish in chasms.
/obj/item/fishing_hook/rescue/reason_we_cant_fish(datum/fish_source/target_fish_source)
@@ -155,13 +165,6 @@
desc = "A simple hook carved from sharpened bone"
icon_state = "hook_bone"
/datum/crafting_recipe/bone_hook
name = "Goliath Bone Hook"
result = /obj/item/fishing_hook/bone
reqs = list(/obj/item/stack/sheet/bone = 1)
time = 2 SECONDS
category = CAT_TOOLS
/obj/item/fishing_hook/stabilized
name = "gyro-stabilized hook"
desc = "A quirky hook that grants the user a better control of the tool, allowing them to move the bait both and up and down when reeling in, otherwise keeping it in place."
@@ -177,9 +180,18 @@
name = "jawed hook"
desc = "Despite hints of rust, this gritty beartrap-like hook hybrid manages to look even more threating than the real thing. May neptune have mercy of whatever gets caught in its jaws."
icon_state = "jaws"
w_class = WEIGHT_CLASS_NORMAL
fishing_hook_traits = FISHING_HOOK_NO_ESCAPE|FISHING_HOOK_NO_ESCAPE|FISHING_HOOK_KILL
rod_overlay_icon_state = "hook_jaws_overlay"
/obj/item/fishing_hook/jaws/can_be_hooked(atom/target)
return ..() || isliving(target)
/obj/item/fishing_hook/jaws/hook_attached(atom/target, obj/item/fishing_rod/rod)
if(isliving(target))
var/mob/living/living_target = target
living_target.apply_status_effect(/datum/status_effect/grouped/hooked/jaws, rod.fishing_line)
/obj/item/storage/toolbox/fishing
name = "fishing toolbox"
desc = "Contains everything you need for your fishing trip."
@@ -198,7 +210,7 @@
/obj/item/storage/toolbox/fishing/PopulateContents()
new /obj/item/bait_can/worm(src)
new /obj/item/fishing_rod(src)
new /obj/item/fishing_rod/unslotted(src)
new /obj/item/fishing_hook(src)
new /obj/item/fishing_line(src)
@@ -214,7 +226,7 @@
atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL //It can still hold a fishing rod
/obj/item/storage/toolbox/fishing/small/PopulateContents()
new /obj/item/fishing_rod(src)
new /obj/item/fishing_rod/unslotted(src)
new /obj/item/fishing_hook(src)
new /obj/item/fishing_line(src)
+4
View File
@@ -156,8 +156,12 @@
trait.minigame_mod(rod, user, src)
/// Enable special parameters
if(rod.line)
completion_gain += 1 // Any fishing line will provide a small boost by default
if(rod.line.fishing_line_traits & FISHING_LINE_BOUNCY)
completion_loss -= 2
if(rod.line.fishing_line_traits & FISHING_LINE_STIFF)
completion_loss += 1
completion_gain -= 1
if(rod.hook)
if(rod.hook.fishing_hook_traits & FISHING_HOOK_WEIGHTED)
bait_bounce_mult = 0.1
+110 -95
View File
@@ -1,6 +1,4 @@
#define ROD_SLOT_BAIT "bait"
#define ROD_SLOT_LINE "line"
#define ROD_SLOT_HOOK "hook"
#define FISHING_ROD_REEL_CAST_RANGE 2
/obj/item/fishing_rod
name = "fishing rod"
@@ -16,18 +14,18 @@
w_class = WEIGHT_CLASS_HUGE
/// How far can you cast this
var/cast_range = 5
var/cast_range = 3
/// Fishing minigame difficulty modifier (additive)
var/difficulty_modifier = 0
/// Explaination of rod functionality shown in the ui
var/ui_description = "A classic fishing rod, with no special qualities."
var/obj/item/bait
var/obj/item/fishing_line/line
var/obj/item/fishing_hook/hook
var/obj/item/fishing_line/line = /obj/item/fishing_line
var/obj/item/fishing_hook/hook = /obj/item/fishing_hook
/// Currently hooked item for item reeling
var/obj/item/currently_hooked_item
var/atom/movable/currently_hooked
/// Fishing line visual for the hooked item
var/datum/beam/fishing_line/fishing_line
@@ -45,11 +43,19 @@
. = ..()
register_context()
register_item_context()
if(ispath(bait))
set_slot(new bait(src), ROD_SLOT_BAIT)
if(ispath(hook))
set_slot(new hook(src), ROD_SLOT_HOOK)
if(ispath(line))
set_slot(new line(src), ROD_SLOT_LINE)
update_appearance()
/obj/item/fishing_rod/add_context(atom/source, list/context, obj/item/held_item, mob/user)
if(src == held_item)
if(currently_hooked_item)
if(currently_hooked)
context[SCREENTIP_CONTEXT_LMB] = "Reel in"
context[SCREENTIP_CONTEXT_RMB] = "Modify"
return CONTEXTUAL_SCREENTIP_SET
@@ -57,14 +63,11 @@
/obj/item/fishing_rod/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
. = ..()
if(currently_hooked_item)
if(currently_hooked)
context[SCREENTIP_CONTEXT_LMB] = "Reel in"
return CONTEXTUAL_SCREENTIP_SET
return NONE
/obj/item/fishing_rod/Destroy(force)
return ..()
/obj/item/fishing_rod/examine(mob/user)
. = ..()
var/list/equipped_stuff = list()
@@ -72,35 +75,12 @@
equipped_stuff += "[icon2html(line, user)] <b>[line.name]</b>"
if(hook)
equipped_stuff += "[icon2html(hook, user)] <b>[hook.name]</b>"
if(bait)
equipped_stuff += "[icon2html(bait, user)] <b>[bait]</b> as bait."
if(length(equipped_stuff))
. += span_notice("It has \a [english_list(equipped_stuff)] equipped.")
if(bait)
. += span_notice("\a [icon2html(bait, user)] <b>[bait]</b> is being used as bait.")
else
. += span_warning("It doesn't have any bait attached. Fishing will be more tedious!")
. += span_notice("<b>Right-Click</b> in your active hand to access its slots UI")
/**
* Catch weight modifier for the given fish_type (or FISHING_DUD)
* and source, multiplicative. Called before `additive_fish_bonus()`.
*/
/obj/item/fishing_rod/proc/multiplicative_fish_bonus(fish_type, datum/fish_source/source)
if(!hook)
return FISHING_DEFAULT_HOOK_BONUS_MULTIPLICATIVE
return hook.get_hook_bonus_multiplicative(fish_type)
/**
* Catch weight modifier for the given fish_type (or FISHING_DUD)
* and source, additive. Called after `multiplicative_fish_bonus()`.
*/
/obj/item/fishing_rod/proc/additive_fish_bonus(fish_type, datum/fish_source/source)
if(!hook)
return FISHING_DEFAULT_HOOK_BONUS_ADDITIVE
return hook.get_hook_bonus_additive(fish_type)
if(!bait)
. += span_warning("It doesn't have a bait attached to it. Fishing will be more tedious!")
/**
* Is there a reason why this fishing rod couldn't fish in target_fish_source?
@@ -126,16 +106,34 @@
update_icon()
/obj/item/fishing_rod/interact(mob/user)
if(currently_hooked_item)
if(currently_hooked)
reel(user)
/obj/item/fishing_rod/proc/reel(mob/user)
//Could use sound here for feedback
if(do_after(user, 1 SECONDS, currently_hooked_item))
// Should probably respect and used force move later
step_towards(currently_hooked_item, get_turf(src))
if(get_dist(currently_hooked_item,get_turf(src)) < 1)
if(DOING_INTERACTION_WITH_TARGET(user, currently_hooked))
return
playsound(src, SFX_REEL, 50, vary = FALSE)
if(!do_after(user, 0.8 SECONDS, currently_hooked, timed_action_flags = IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(fishing_line_check))))
return
if(currently_hooked.anchored || currently_hooked.move_resist >= MOVE_FORCE_STRONG)
balloon_alert(user, "[currently_hooked.p_they()] won't budge!")
return
//Try to move it 'till it's under the user's feet, then try to pick it up
if(isitem(currently_hooked))
step_towards(currently_hooked, get_turf(src))
if(currently_hooked.loc == user.loc)
user.put_in_inactive_hand(currently_hooked)
QDEL_NULL(fishing_line)
//Not an item, so just delete the line if it's adjacent to the user.
else if(get_dist(currently_hooked,get_turf(src)) > 1)
step_towards(currently_hooked, get_turf(src))
if(get_dist(currently_hooked,get_turf(src)) <= 1)
QDEL_NULL(fishing_line)
else
QDEL_NULL(fishing_line)
/obj/item/fishing_rod/proc/fishing_line_check()
return !QDELETED(fishing_line)
/obj/item/fishing_rod/attack_self_secondary(mob/user, modifiers)
. = ..()
@@ -144,9 +142,11 @@
/obj/item/fishing_rod/pre_attack(atom/targeted_atom, mob/living/user, params)
. = ..()
/// Reel in if able
if(currently_hooked_item)
if(currently_hooked)
reel(user)
return TRUE
if(!hook)
balloon_alert(user, "install a hook first!")
SEND_SIGNAL(targeted_atom, COMSIG_PRE_FISHING)
/// Generates the fishing line visual from the current user to the target and updates inhands
@@ -171,7 +171,7 @@
var/mob/user = loc
user.update_held_items()
fishing_line = null
currently_hooked_item = null
currently_hooked = null
/obj/item/fishing_rod/dropped(mob/user, silent)
. = ..()
@@ -179,19 +179,15 @@
/// Hooks the item
/obj/item/fishing_rod/proc/hook_item(mob/user, atom/target_atom)
if(currently_hooked_item)
if(currently_hooked)
return
if(!can_be_hooked(target_atom))
if(!hook.can_be_hooked(target_atom))
return
currently_hooked_item = target_atom
currently_hooked = target_atom
create_fishing_line(target_atom)
hook.hook_attached(target_atom, src)
SEND_SIGNAL(src, COMSIG_FISHING_ROD_HOOKED_ITEM, target_atom, user)
/// Checks what can be hooked
/obj/item/fishing_rod/proc/can_be_hooked(atom/movable/target)
// Could be made dependent on actual hook, ie magnet to hook metallic items
return isitem(target)
// Checks fishing line for interruptions and range
/obj/item/fishing_rod/proc/check_los(datum/beam/source)
SIGNAL_HANDLER
@@ -206,7 +202,7 @@
. |= AFTERATTACK_PROCESSED_ITEM
/// Reel in if able
if(currently_hooked_item)
if(currently_hooked)
reel(user)
return .
@@ -216,7 +212,13 @@
///Called by afterattack(). If the line to whatever that is is clear and we're not already busy, try fishing in it
/obj/item/fishing_rod/proc/cast_line(atom/target, mob/user, proximity_flag)
if(casting || currently_hooked_item || proximity_flag || !CheckToolReach(user, target, cast_range))
if(casting || currently_hooked || proximity_flag)
return
if(!hook)
balloon_alert(user, "install a hook first!")
return
if(!CheckToolReach(user, target, cast_range))
balloon_alert(user, "cannot reach there!")
return
/// Annoyingly pre attack is only called in melee
SEND_SIGNAL(target, COMSIG_PRE_FISHING)
@@ -234,7 +236,7 @@
/// Called by hook projectile when hitting things
/obj/item/fishing_rod/proc/hook_hit(atom/atom_hit_by_hook_projectile)
var/mob/user = loc
if(!istype(user))
if(!hook || !istype(user))
return
if(SEND_SIGNAL(atom_hit_by_hook_projectile, COMSIG_FISHING_ROD_CAST, src, user) & FISHING_ROD_CAST_HANDLED)
return
@@ -328,6 +330,8 @@
data["hook_name"] = format_text(hook?.name)
data["hook_icon"] = hook != null ? icon2base64(icon(hook.icon, hook.icon_state)) : null
data["busy"] = fishing_line
data["description"] = ui_description
return data
@@ -377,23 +381,16 @@
// Trying to remove the item
if(!new_item && current_item)
user.put_in_hands(current_item)
update_icon()
return
balloon_alert(user, "[slot] removed")
// Trying to insert item into empty slot
if(new_item && !current_item)
else if(new_item && !current_item)
if(!slot_check(new_item, slot))
return
if(user.transferItemToLoc(new_item,src))
switch(slot)
if(ROD_SLOT_BAIT)
bait = new_item
if(ROD_SLOT_HOOK)
hook = new_item
if(ROD_SLOT_LINE)
line = new_item
update_icon()
set_slot(new_item, slot)
balloon_alert(user, "[slot] installed")
/// Trying to swap item
if(new_item && current_item)
else if(new_item && current_item)
if(!slot_check(new_item,slot))
return
if(user.transferItemToLoc(new_item,src))
@@ -405,33 +402,46 @@
if(ROD_SLOT_LINE)
line = new_item
user.put_in_hands(current_item)
update_icon()
balloon_alert(user, "[slot] swapped")
update_icon()
playsound(src, 'sound/items/click.ogg', 50, TRUE)
///assign an item to the given slot and its standard effects, while Exited() should handle unsetting the slot.
/obj/item/fishing_rod/proc/set_slot(obj/item/equipment, slot)
switch(slot)
if(ROD_SLOT_BAIT)
bait = equipment
if(ROD_SLOT_HOOK)
hook = equipment
if(ROD_SLOT_LINE)
line = equipment
cast_range += FISHING_ROD_REEL_CAST_RANGE
/obj/item/fishing_rod/Exited(atom/movable/gone, direction)
. = ..()
if(gone == bait)
bait = null
if(gone == line)
cast_range -= FISHING_ROD_REEL_CAST_RANGE
line = null
if(gone == hook)
QDEL_NULL(fishing_line)
hook = null
///Found in the fishing toolbox (the hook and line are separate items)
/obj/item/fishing_rod/unslotted
hook = null
line = null
/obj/item/fishing_rod/bone
name = "bone fishing rod"
desc = "A humble rod, made with whatever happened to be on hand."
icon_state = "fishing_rod_bone"
reel_overlay = "reel_bone"
default_line_color = "red"
/datum/crafting_recipe/bone_rod
name = "Bone Fishing Rod"
result = /obj/item/fishing_rod/bone
time = 5 SECONDS
reqs = list(/obj/item/stack/sheet/leather = 1,
/obj/item/stack/sheet/sinew = 2,
/obj/item/stack/sheet/bone = 2)
category = CAT_TOOLS
line = null //sinew line (usable to fish in lava) not included
hook = /obj/item/fishing_hook/bone
/obj/item/fishing_rod/telescopic
name = "telescopic fishing rod"
@@ -442,8 +452,6 @@
w_class = WEIGHT_CLASS_NORMAL
ui_description = "A collapsible fishing rod that can fit within a backpack."
reel_overlay = "reel_telescopic"
///Whether the rod is exteded or not. Tied to the transforming element.
var/active = FALSE
///The force of the item when extended.
var/active_force = 8
@@ -454,30 +462,31 @@
RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform))
/obj/item/fishing_rod/telescopic/reason_we_cant_fish(datum/fish_source/target_fish_source)
if(!active)
if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
return "You need to extend your fishing rod before you can cast the line."
return ..()
/obj/item/fishing_rod/telescopic/cast_line(atom/target, mob/user, proximity_flag)
if(!active)
to_chat(user, "You need to extend your fishing rod before you can cast the line.")
if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
if(!proximity_flag)
balloon_alert(user, "extend the rod first!")
return
return ..()
/obj/item/fishing_rod/telescopic/get_fishing_overlays()
if(!active)
if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
return list()
return ..()
/obj/item/fishing_rod/telescopic/get_fishing_worn_overlays(mutable_appearance/standing, isinhands, icon_file)
if(!active)
if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
return list()
return ..()
///Stops the fishing rod from being collapsed while fishing.
/obj/item/fishing_rod/telescopic/proc/pre_transform(obj/item/source, mob/user, active)
SIGNAL_HANDLER
if(active)
if(HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
return
//the fishing minigame uses the attack_self signal to let the user end it early without having to drop the rod.
if(HAS_TRAIT(user, TRAIT_GONE_FISHING))
@@ -487,14 +496,12 @@
/obj/item/fishing_rod/telescopic/proc/on_transform(obj/item/source, mob/user, active)
SIGNAL_HANDLER
src.active = active
inhand_icon_state = active ? "rod" : null // When inactive, there is no inhand icon_state.
if(user)
balloon_alert(user, active ? "extended" : "collapsed")
playsound(src, 'sound/weapons/batonextend.ogg', 50, TRUE)
update_appearance(UPDATE_OVERLAYS)
if(fishing_line)
QDEL_NULL(fishing_line)
QDEL_NULL(fishing_line)
return COMPONENT_NO_DEFAULT_MESSAGE
/obj/item/fishing_rod/telescopic/master
@@ -505,6 +512,9 @@
icon_state = "fishing_rod_master"
reel_overlay = "reel_master"
active_force = 13 //It's that sturdy
cast_range = 5
line = /obj/item/fishing_line/bouncy
hook = /obj/item/fishing_hook/weighted
/obj/item/fishing_rod/tech
name = "advanced fishing rod"
@@ -512,6 +522,7 @@
ui_description = "This rod has an infinite supply of synth-bait. Also doubles as an Experi-Scanner for fish."
icon_state = "fishing_rod_science"
reel_overlay = "reel_science"
bait = /obj/item/food/bait/doughball/synthetic
/obj/item/fishing_rod/tech/Initialize(mapload)
. = ..()
@@ -529,10 +540,6 @@
experiment_signals = fishing_signals, \
)
var/obj/item/food/bait/doughball/synthetic/infinite_supply_of_bait = new(src)
bait = infinite_supply_of_bait
update_icon()
/obj/item/fishing_rod/tech/examine(mob/user)
. = ..()
. += span_notice("<b>Alt-Click</b> to access the Experiment Configuration UI")
@@ -561,6 +568,12 @@
var/obj/item/fishing_rod/owner
var/datum/beam/our_line
/obj/projectile/fishing_cast/fire(angle, atom/direct_target)
if(owner.hook)
icon_state = owner.hook.icon_state
transform = transform.Scale(1, -1)
return ..()
/obj/projectile/fishing_cast/Impact(atom/hit_atom)
. = ..()
owner.hook_hit(hit_atom)
@@ -634,3 +647,5 @@
if(NORTH)
override_origin_pixel_x = lefthand ? lefthand_n_px : righthand_n_px
override_origin_pixel_y = lefthand ? lefthand_n_py : righthand_n_py
#undef FISHING_ROD_REEL_CAST_RANGE
+2 -2
View File
@@ -240,8 +240,8 @@ GLOBAL_LIST(fishing_property_cache)
var/list/final_table = fish_table.Copy()
for(var/result in final_table)
final_table[result] *= rod.multiplicative_fish_bonus(result, src)
final_table[result] += rod.additive_fish_bonus(result, src) //Decide on order here so it can be multiplicative
final_table[result] *= rod.hook?.get_hook_bonus_multiplicative(result)
final_table[result] += rod.hook?.get_hook_bonus_additive(result)//Decide on order here so it can be multiplicative
if(ispath(result, /obj/item/fish))
//Modify fish roll chance
var/obj/item/fish/caught_fish = result
@@ -17,8 +17,12 @@
/datum/movespeed_modifier/sphere
multiplicative_slowdown = -0.5
/datum/movespeed_modifier/hook_jawed
multiplicative_slowdown = 4
/datum/movespeed_modifier/shooting_assistant
multiplicative_slowdown = 0.5
/datum/movespeed_modifier/binocs_wielded
multiplicative_slowdown = 1.5
+5 -15
View File
@@ -792,9 +792,7 @@
set_angle(get_angle(src, target))
original_angle = Angle
if(!nondirectional_sprite)
var/matrix/matrix = new
matrix.Turn(Angle)
transform = matrix
transform = transform.Turn(Angle)
trajectory_ignore_forcemove = TRUE
forceMove(starting)
trajectory_ignore_forcemove = FALSE
@@ -811,11 +809,9 @@
pixel_move(pixel_speed_multiplier, FALSE) //move it now!
/obj/projectile/proc/set_angle(new_angle) //wrapper for overrides.
Angle = new_angle
if(!nondirectional_sprite)
var/matrix/matrix = new
matrix.Turn(Angle)
transform = matrix
transform = transform.TurnTo(Angle, new_angle)
Angle = new_angle
if(trajectory)
trajectory.set_angle(new_angle)
if(fired && hitscan && isloc(loc) && (loc != last_angle_set_hitscan_store))
@@ -827,11 +823,9 @@
/// Same as set_angle, but the reflection continues from the center of the object that reflects it instead of the side
/obj/projectile/proc/set_angle_centered(new_angle)
Angle = new_angle
if(!nondirectional_sprite)
var/matrix/matrix = new
matrix.Turn(Angle)
transform = matrix
transform = transform.TurnTo(Angle, new_angle)
Angle = new_angle
if(trajectory)
trajectory.set_angle(new_angle)
@@ -909,10 +903,6 @@
if(!loc || !trajectory)
return
last_projectile_move = world.time
if(!nondirectional_sprite && !hitscanning)
var/matrix/matrix = new
matrix.Turn(Angle)
transform = matrix
if(homing)
process_homing()
var/forcemoved = FALSE
Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

+3
View File
@@ -114,6 +114,9 @@ https://freesound.org/people/junggle/sounds/28917/
https://freesound.org/people/inferno/sounds/18397/
https://freesound.org/people/humanoide9000/sounds/330293/
reel1.ogg, reel2.ogg, reel3.ogg, reel4.ogg and reel5.ogg adapted from pixabay. Free for use under the Pixabay Content License (https://pixabay.com/service/license-summary/):
https://pixabay.com/sound-effects/reel-78063/
throw.ogg, throwhard.ogg and throwsoft.ogg (Royalty-Free and Copyright-Free) are adapted from Jam FX, SmartSound FX and Epic Stock Media in :
https://uppbeat.io/sfx/whoosh-swift-cut/7727/23617
https://uppbeat.io/sfx/whoosh-air-punch/114/1168
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -1776,6 +1776,7 @@
#include "code\datums\status_effects\debuffs\fire_stacks.dm"
#include "code\datums\status_effects\debuffs\genetic_damage.dm"
#include "code\datums\status_effects\debuffs\hallucination.dm"
#include "code\datums\status_effects\debuffs\hooked.dm"
#include "code\datums\status_effects\debuffs\jitteriness.dm"
#include "code\datums\status_effects\debuffs\pacifism.dm"
#include "code\datums\status_effects\debuffs\screen_blur.dm"
+1 -1
View File
@@ -64,7 +64,7 @@ export const FishingRod = (props) => {
} = data;
return (
<Window height={300} width={300}>
<Window height={450} width={400}>
<Window.Content>
<Section>
<FishingRodSlot