[MIRROR] Adds an 'auto-reel' line reel to the fishing tech node. Minor gameplay changes to fishing. (#26580)

* 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

🆑 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.
/🆑

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

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
Co-authored-by: Useroth <37159550+Useroth@users.noreply.github.com>
This commit is contained in:
SkyratBot
2024-02-21 23:46:47 +01:00
committed by GitHub
co-authored by Ghom Useroth
parent 9d6dbe3a8f
commit 1e37ae9c01
12 changed files with 178 additions and 47 deletions
@@ -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"
+5
View File
@@ -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
@@ -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)
@@ -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)
+9 -9
View File
@@ -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
+58 -2
View File
@@ -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
+43 -9
View File
@@ -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
@@ -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
@@ -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"
@@ -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
@@ -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()
@@ -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
@@ -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
@@ -691,3 +723,5 @@
#undef REELING_STATE_UP
#undef REELING_STATE_DOWN
#undef MAX_FISH_COMPLETION_MALUS
#undef BITING_TIME_WINDOW
+10 -3
View File
@@ -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()
@@ -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
@@ -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)
@@ -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
@@ -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)
+3 -3
View File
@@ -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"
@@ -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)
. = ..()
@@ -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))
@@ -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
if(!skip_throw_mode_check && !throw_mode)
return
if(get_active_held_item())
return
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
return
return TRUE
/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/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/can_catch_item(skip_throw_mode_check = FALSE, try_offhand = FALSE)
if(!skip_throw_mode_check && !throw_mode)
return FALSE
return ..()
/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)
+20
View File
@@ -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)
+15 -3
View File
@@ -972,7 +972,7 @@
category = list(
RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SERVICE
)
departmental_flags = DEPARTMENT_BITFLAG_SERVICE
departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_SCIENCE
/datum/design/stabilized_hook
name = "Gyro-Stabilized Hook"
@@ -984,7 +984,19 @@
category = list(
RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SERVICE
)
departmental_flags = DEPARTMENT_BITFLAG_SERVICE
departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_SCIENCE
/datum/design/auto_reel
name = "Fishing Line Auto-Reel"
desc = "An advanced line reel which can be used speed up both fishing and casually snagging other items in your direction."
id = "auto_reel"
build_type = PROTOLATHE | AWAY_LATHE
materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 4, /datum/material/gold = SMALL_MATERIAL_AMOUNT * 3, /datum/material/silver = SMALL_MATERIAL_AMOUNT * 3)
build_path = /obj/item/fishing_line/auto_reel
category = list(
RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SERVICE
)
departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_SCIENCE
/datum/design/fish_analyzer
name = "Fish Analyzer"
@@ -996,7 +1008,7 @@
category = list(
RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SERVICE
)
departmental_flags = DEPARTMENT_BITFLAG_SERVICE
departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_SCIENCE
// Coffeemaker Stuff
@@ -1550,6 +1550,7 @@
design_ids = list(
"fishing_rod_tech",
"stabilized_hook",
"auto_reel",
"fish_analyzer",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB