Skip to content

Commit

Permalink
[MIRROR] Adds an 'auto-reel' line reel to the fishing tech node. Mino…
Browse files Browse the repository at this point in the history
…r gameplay changes to fishing. (#1056)

* Adds an 'auto-reel' line reel to the fishing tech node. Minor gameplay changes to fishing. (#81407)

## About The Pull Request
This PR adds a new line reel, which speeds up the baiting phase of the
fishing minigame and skips the biting phase, thus starting the minigame
without the initial input from the player.
The auto-reel line will also throw items (or other people/things, if you
have the right hook) in your direction when snagged, with the added
bonus of catching the item mid-air. Turn your fishing rod into a
discount meat hook.

I've lowered the deceleration coefficient and bounce threshold of the
minigame by 1/4. My rationale is that these two numbers are a must lest
we end up with an uncontrollable mess of a minigame, though they also
feel like a sack of flour hitting gravel rn, making specific hooks like
the bi-directional one and the weighted other a bit useless.

Another change is to the baiting and biting phase. Previously, if you
clicked anywhere during the baiting phase, it'd reset the whole timer
back to any value between 1 and 30 seconds, spelling futility to the
time you've just spent waiting. Now, it'll simply add another 4 seconds
or so to the current timer, capping at 30s.

One last thing*. Once the biting phase start, the faster your input is,
the higher the starting completion of the minigame will be, and the
other way around, if you're very slow. The difficulty variable can also
lower the starting completion.

*I lied. I've also added a short cooldown to casting a fishing rod so
you can't just spam it.


## Why It's Good For The Game
Finetuning the minigame, quality of life and balance, making fishing
even more gimmicky.

## Changelog

:cl: Ghommie
add: Added a new fishing line reel that speeds up the first half of the
fishing minigame, and also let's you catch things from afar like a
discount meat hook.
balance: During the biting phase preceeding the actual minigame,
initiating it as soon as the "!!!" alert pops up will net you an
advantage. Conversely...
qol: Clicking during the baiting phase will no longer wholly reset it
and make you lose your patience. Instead, it'll delay the next phase by
about 4 seconds.
balance: The deceleration and bounce should feel less sudden and stiff,
meaning the controls are 25% more slippery again.
balance: Added a cooldown to spam-casting fishing rods.
imageadd: Resprited line reels a little.
/:cl:

* Adds an 'auto-reel' line reel to the fishing tech node. Minor gameplay changes to fishing.

---------

Co-authored-by: Ghom <[email protected]>
  • Loading branch information
2 people authored and StealsThePRs committed Feb 20, 2024
1 parent 52f47dc commit cdf9640
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 46 deletions.
2 changes: 2 additions & 0 deletions code/__DEFINES/dcs/signals/signals_fish.dm
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
#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"

/// Sent when the challenge is to be interrupted: (reason)
#define COMSIG_FISHING_SOURCE_INTERRUPT_CHALLENGE "fishing_spot_interrupt_challenge"
Expand Down
5 changes: 5 additions & 0 deletions code/__DEFINES/fish.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

// Baseline fishing difficulty levels
#define FISHING_DEFAULT_DIFFICULTY 15
#define FISHING_EASY_DIFFICULTY 10

/// Difficulty modifier when bait is fish's favorite
#define FAV_BAIT_DIFFICULTY_MOD -5
Expand Down Expand Up @@ -52,6 +53,8 @@
#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)
///Skip the biting phase and go straight to the fishing phase.
#define FISHING_LINE_AUTOREEL (1 << 4)

///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)
Expand All @@ -65,6 +68,8 @@
#define FISHING_MINIGAME_RULE_ANTIGRAV (1 << 4)
///Will filp the minigame hud for the duration of the effect
#define FISHING_MINIGAME_RULE_FLIP (1 << 5)
///Skip the biting phase and go straight to the minigame, avoiding the penalty for having slow reflexes.
#define FISHING_MINIGAME_AUTOREEL (1 << 6)

///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)
Expand Down
18 changes: 9 additions & 9 deletions code/game/objects/items.dm
Original file line number Diff line number Diff line change
Expand Up @@ -545,20 +545,20 @@
return
return attempt_pickup(user)

/obj/item/proc/attempt_pickup(mob/user)
/obj/item/proc/attempt_pickup(mob/user, skip_grav = FALSE)
. = TRUE

if(!(interaction_flags_item & INTERACT_ITEM_ATTACK_HAND_PICKUP)) //See if we're supposed to auto pickup.
return

//Heavy gravity makes picking up things very slow.
var/grav = user.has_gravity()
if(grav > STANDARD_GRAVITY)
var/grav_power = min(3,grav - STANDARD_GRAVITY)
to_chat(user,span_notice("You start picking up [src]..."))
if(!do_after(user, 30 * grav_power, src))
return

if(!skip_grav)
//Heavy gravity makes picking up things very slow.
var/grav = user.has_gravity()
if(grav > STANDARD_GRAVITY)
var/grav_power = min(3,grav - STANDARD_GRAVITY)
to_chat(user,span_notice("You start picking up [src]..."))
if(!do_after(user, 30 * grav_power, src))
return

//If the item is in a storage item, take it out
var/outside_storage = !loc.atom_storage
Expand Down
60 changes: 58 additions & 2 deletions code/modules/fishing/fishing_equipment.dm
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,68 @@
/obj/item/fishing_line/sinew
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 = 'icons/obj/fishing.dmi'
icon_state = "reel_sinew"
icon_state = "reel_green"
fishing_line_traits = FISHING_LINE_REINFORCED|FISHING_LINE_STIFF
line_color = "#d1cca3"

/**
* 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.
* It may also work on mobs if the right hook is attached.
*/
/obj/item/fishing_line/auto_reel
name = "fishing line auto-reel"
desc = "A fishing line that automatically starts reeling in fish the moment they bite. Also good for hurling things at yourself."
icon_state = "reel_auto"
fishing_line_traits = FISHING_LINE_AUTOREEL
line_color = "#F88414"

/obj/item/fishing_line/auto_reel/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_FISHING_EQUIPMENT_SLOTTED, PROC_REF(line_equipped))

/obj/item/fishing_line/auto_reel/proc/line_equipped(datum/source, obj/item/fishing_rod/rod)
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)
SIGNAL_HANDLER
UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
UnregisterSignal(old_loc, 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
if(!ismovable(target))
return
var/atom/movable/movable_target = target
var/please_be_gentle = FALSE
var/atom/destination
var/datum/callback/throw_callback
if(isliving(movable_target) || !isitem(movable_target))
destination = get_step_towards(user, target)
please_be_gentle = TRUE
else
destination = user
throw_callback = CALLBACK(src, PROC_REF(clear_hitby_signal), movable_target)
RegisterSignal(movable_target, COMSIG_ATOM_PREHITBY, PROC_REF(catch_it_chucklenut))

if(!movable_target.safe_throw_at(destination, source.cast_range, 2, callback = throw_callback, gentle = please_be_gentle))
UnregisterSignal(movable_target, COMSIG_ATOM_PREHITBY)
else
playsound(src, 'sound/weapons/batonextend.ogg', 50, TRUE)

/obj/item/fishing_line/auto_reel/proc/catch_it_chucklenut(obj/item/source, atom/hit_atom, datum/thrownthing/throwingdatum)
SIGNAL_HANDLER
var/mob/living/user = throwingdatum.initial_target.resolve()
if(QDELETED(user) || hit_atom != user)
return
if(user.try_catch_item(source, skip_throw_mode_check = TRUE, try_offhand = TRUE))
return COMSIG_HIT_PREVENTED

/obj/item/fishing_line/auto_reel/proc/clear_hitby_signal(obj/item/item)
UnregisterSignal(item, COMSIG_ATOM_PREHITBY)

// Hooks

/obj/item/fishing_hook
Expand Down
52 changes: 43 additions & 9 deletions code/modules/fishing/fishing_minigame.dm
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@
// Acceleration mod when bait is over fish
#define FISH_ON_BAIT_ACCELERATION_MULT 0.6
/// The minimum velocity required for the bait to bounce
#define BAIT_MIN_VELOCITY_BOUNCE 200
#define BAIT_MIN_VELOCITY_BOUNCE 150
/// The extra deceleration of velocity that happens when the bait switches direction
#define BAIT_DECELERATION_MULT 2
#define BAIT_DECELERATION_MULT 1.5

/// Reduce initial completion rate depending on difficulty
#define MAX_FISH_COMPLETION_MALUS 15
/// The window of time between biting phase and back to baiting phase
#define BITING_TIME_WINDOW 4 SECONDS


///Defines to know how the bait is moving on the minigame slider.
#define REELING_STATE_IDLE 0
Expand Down Expand Up @@ -162,6 +168,8 @@
if(rod.line.fishing_line_traits & FISHING_LINE_STIFF)
completion_loss += 1
completion_gain -= 1
if(rod.line.fishing_line_traits & FISHING_LINE_AUTOREEL)
special_effects |= FISHING_MINIGAME_AUTOREEL
if(rod.hook)
if(rod.hook.fishing_hook_traits & FISHING_HOOK_WEIGHTED)
bait_bounce_mult = 0.1
Expand All @@ -180,6 +188,9 @@
difficulty += comp.fish_source.calculate_difficulty(reward_path, rod, user, src)
difficulty = clamp(round(difficulty), 1, 100)

if(difficulty > FISHING_EASY_DIFFICULTY)
completion -= round(MAX_FISH_COMPLETION_MALUS * (difficulty/100), 1)

if(HAS_TRAIT(user, TRAIT_REVEAL_FISH) || (user.mind && HAS_TRAIT(user.mind, TRAIT_REVEAL_FISH)))
fish_icon = GLOB.specific_fish_icons[reward_path] || "fish"

Expand Down Expand Up @@ -259,7 +270,7 @@
return
if(phase == WAIT_PHASE) //Reset wait
send_alert("miss!")
start_baiting_phase()
start_baiting_phase(TRUE)
else if(phase == BITING_PHASE)
start_minigame_phase()
return COMSIG_MOB_CANCEL_CLICKON
Expand Down Expand Up @@ -302,14 +313,19 @@
if(!QDELETED(src))
qdel(src)

/datum/fishing_challenge/proc/start_baiting_phase()
/datum/fishing_challenge/proc/start_baiting_phase(penalty = FALSE)
var/wait_time
if(penalty)
wait_time = min(timeleft(next_phase_timer) + rand(3 SECONDS, 5 SECONDS), 30 SECONDS)
else
wait_time = rand(1 SECONDS, 30 SECONDS)
if(special_effects & FISHING_MINIGAME_AUTOREEL && wait_time >= 15 SECONDS)
wait_time = max(wait_time - 7.5 SECONDS, 15 SECONDS)
deltimer(next_phase_timer)
phase = WAIT_PHASE
//Bobbing animation
animate(lure, pixel_y = 1, time = 1 SECONDS, loop = -1, flags = ANIMATION_RELATIVE)
animate(pixel_y = -1, time = 1 SECONDS, flags = ANIMATION_RELATIVE)
//Setup next phase
var/wait_time = rand(1 SECONDS, 30 SECONDS)
next_phase_timer = addtimer(CALLBACK(src, PROC_REF(start_biting_phase)), wait_time, TIMER_STOPPABLE)

/datum/fishing_challenge/proc/start_biting_phase()
Expand Down Expand Up @@ -342,9 +358,11 @@
send_alert("!!!")
animate(lure, pixel_y = 3, time = 5, loop = -1, flags = ANIMATION_RELATIVE)
animate(pixel_y = -3, time = 5, flags = ANIMATION_RELATIVE)
if(special_effects & FISHING_MINIGAME_AUTOREEL)
start_minigame_phase(auto_reel = TRUE)
return
// Setup next phase
var/wait_time = rand(3 SECONDS, 6 SECONDS)
next_phase_timer = addtimer(CALLBACK(src, PROC_REF(start_baiting_phase)), wait_time, TIMER_STOPPABLE)
next_phase_timer = addtimer(CALLBACK(src, PROC_REF(start_baiting_phase)), BITING_TIME_WINDOW, TIMER_STOPPABLE)

///The damage dealt per second to the fish when FISHING_MINIGAME_RULE_KILL is active.
#define FISH_DAMAGE_PER_SECOND 2
Expand All @@ -366,7 +384,21 @@
var/damage = CEILING((world.time - start_time)/10 * FISH_DAMAGE_PER_SECOND, 1)
reward.adjust_health(reward.health - damage)

/datum/fishing_challenge/proc/start_minigame_phase()
/datum/fishing_challenge/proc/start_minigame_phase(auto_reel = FALSE)
if(auto_reel)
completion *= 1.3
else
var/time_left = timeleft(next_phase_timer)
switch(time_left)
if(0 to BITING_TIME_WINDOW - 3 SECONDS)
completion *= 0.65
if(BITING_TIME_WINDOW - 3 SECONDS to BITING_TIME_WINDOW - 2 SECONDS)
completion *= 0.82
if(BITING_TIME_WINDOW - 1 SECONDS to BITING_TIME_WINDOW - 0.5 SECONDS)
completion *= 1.2
if(BITING_TIME_WINDOW - 0.5 SECONDS to BITING_TIME_WINDOW)
completion *= 1.4
completion = round(completion, 1)
if(!prepare_minigame_hud())
return
phase = MINIGAME_PHASE
Expand Down Expand Up @@ -691,3 +723,5 @@
#undef REELING_STATE_UP
#undef REELING_STATE_DOWN

#undef MAX_FISH_COMPLETION_MALUS
#undef BITING_TIME_WINDOW
13 changes: 10 additions & 3 deletions code/modules/fishing/fishing_rod.dm
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
///The name of the icon state of the reel overlay
var/reel_overlay = "reel_overlay"

///Prevents spamming the line casting, without affecting the player's click cooldown.
COOLDOWN_DECLARE(casting_cd)

/obj/item/fishing_rod/Initialize(mapload)
. = ..()
register_context()
Expand Down Expand Up @@ -220,6 +223,8 @@
if(!CheckToolReach(user, target, cast_range))
balloon_alert(user, "cannot reach there!")
return
if(!COOLDOWN_FINISHED(src, casting_cd))
return
/// Annoyingly pre attack is only called in melee
SEND_SIGNAL(target, COMSIG_PRE_FISHING)
casting = TRUE
Expand All @@ -232,6 +237,7 @@
cast_projectile.impacted = list(user = TRUE)
cast_projectile.preparePixelProjectile(target, user)
cast_projectile.fire()
COOLDOWN_START(src, casting_cd, 1 SECONDS)

/// Called by hook projectile when hitting things
/obj/item/fishing_rod/proc/hook_hit(atom/atom_hit_by_hook_projectile)
Expand Down Expand Up @@ -361,9 +367,7 @@
if("slot_action")
// Simple click with empty hand to remove, click with item to insert/switch
var/obj/item/held_item = user.get_active_held_item()
if(held_item == src)
return
use_slot(params["slot"], user, held_item)
use_slot(params["slot"], user, held_item == src ? null : held_item)
return TRUE

/// Ideally this will be replaced with generic slotted storage datum + display
Expand Down Expand Up @@ -404,6 +408,9 @@
user.put_in_hands(current_item)
balloon_alert(user, "[slot] swapped")

if(new_item)
SEND_SIGNAL(new_item, COMSIG_FISHING_EQUIPMENT_SLOTTED, src)

update_icon()
playsound(src, 'sound/items/click.ogg', 50, TRUE)

Expand Down
6 changes: 3 additions & 3 deletions code/modules/fishing/sources/source_types.dm
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@
fish_counts = list(
/obj/item/storage/wallet/money = 2,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY - 5 //For beginners
fishing_difficulty = FISHING_EASY_DIFFICULTY //For beginners

/datum/fish_source/holographic
catalog_description = "Holographic water"
Expand All @@ -275,7 +275,7 @@
/obj/item/fish/holo/checkered = 5,
/obj/item/fish/holo/halffish = 5,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY - 5
fishing_difficulty = FISHING_EASY_DIFFICULTY

/datum/fish_source/holographic/reason_we_cant_fish(obj/item/fishing_rod/rod, mob/fisherman, atom/parent)
. = ..()
Expand Down Expand Up @@ -332,7 +332,7 @@
/mob/living/basic/frog = 1,
/mob/living/basic/axolotl = 1,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY - 10
fishing_difficulty = FISHING_EASY_DIFFICULTY - 5

/datum/fish_source/hydro_tray/reason_we_cant_fish(obj/item/fishing_rod/rod, mob/fisherman, atom/parent)
if(!istype(parent, /obj/machinery/hydroponics/constructable))
Expand Down
28 changes: 11 additions & 17 deletions code/modules/mob/living/carbon/carbon_defense.dm
Original file line number Diff line number Diff line change
Expand Up @@ -69,25 +69,19 @@
if(P.catastropic_dismemberment)
apply_damage(P.damage, P.damtype, BODY_ZONE_CHEST, wound_bonus = P.wound_bonus) //stops a projectile blowing off a limb effectively doing no damage. Mostly relevant for sniper rifles.

/mob/living/carbon/proc/can_catch_item(skip_throw_mode_check)
. = FALSE
/mob/living/carbon/try_catch_item(obj/item/item, skip_throw_mode_check = FALSE, try_offhand = FALSE)
. = ..()
if(.)
throw_mode_off(THROW_MODE_TOGGLE)

/mob/living/carbon/can_catch_item(skip_throw_mode_check = FALSE, try_offhand = FALSE)
if(!skip_throw_mode_check && !throw_mode)
return
if(get_active_held_item())
return
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
return
return TRUE
return FALSE
return ..()

/mob/living/carbon/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(!skipcatch && can_catch_item() && isitem(AM) && !HAS_TRAIT(AM, TRAIT_UNCATCHABLE) && isturf(AM.loc))
var/obj/item/I = AM
I.attack_hand(src)
if(get_active_held_item() == I) //if our attack_hand() picks up the item...
visible_message(span_warning("[src] catches [I]!"), \
span_userdanger("You catch [I] in mid-air!"))
throw_mode_off(THROW_MODE_TOGGLE)
return TRUE
/mob/living/carbon/hitby(atom/movable/movable, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(!skipcatch && try_catch_item(movable))
return TRUE
return ..()

/mob/living/carbon/send_item_attack_message(obj/item/I, mob/living/user, hit_area, def_zone)
Expand Down
20 changes: 20 additions & 0 deletions code/modules/mob/living/living_defense.dm
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,26 @@
hitpush = FALSE
return ..()

///The core of catching thrown items, which non-carbons cannot without the help of items or abilities yet, as they've no throw mode.
/mob/living/proc/try_catch_item(obj/item/item, skip_throw_mode_check = FALSE, try_offhand = FALSE)
if(!can_catch_item(skip_throw_mode_check, try_offhand) || !isitem(item) || HAS_TRAIT(item, TRAIT_UNCATCHABLE) || !isturf(item.loc))
return FALSE
if(!can_hold_items(item))
return FALSE
INVOKE_ASYNC(item, TYPE_PROC_REF(/obj/item, attempt_pickup), src, TRUE)
if(get_active_held_item() == item) //if our attack_hand() picks up the item...
visible_message(span_warning("[src] catches [item]!"), \
span_userdanger("You catch [item] in mid-air!"))
return TRUE

///Checks the requites for catching a throw item.
/mob/living/proc/can_catch_item(skip_throw_mode_check = FALSE, try_offhand = FALSE)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
return FALSE
if(get_active_held_item() && (!try_offhand || get_inactive_held_item() || !swap_hand()))
return FALSE
return TRUE

/mob/living/fire_act()
. = ..()
adjust_fire_stacks(3)
Expand Down
Loading

0 comments on commit cdf9640

Please sign in to comment.