The fishening v3: Fishing lures. (#86007)

## About The Pull Request
Over half of the line changes are merely from splitting the
fish_types.dm into several files since it was over 1k lines already.

One of the small issues with fishing right now is RNG. You want to get
some specific fish, and you go through all the micromanaging with hooks,
reels and baits only for the random number god to say "nope", and that's
only going to get worse the more fish are in the game.

However, I've a solution: (unconsumable/reusable) fishing lures, each of
which attracts different fish based on different conditions. The only
caveat is that they require to be spun at set intervals (usually 1 to 3
seconds, depending on the lure, with a second-long window). Worry not,
there're visual cues in the form of a green/red light hovering the
fishing float, so you won't get screwed up by the server slowing down or
whatever.
The whole box of lures (12 so far) can be from cargo for the fair price
of 450 credits.

I've also added 5 new fish: monkfish, plaice, pike, another punnier
variant of the pike, perch and squid. The latter is quite special
because of the ink production trait, which lets players use it to blind
others at a close range and when butchered, it yields an ink sac, which
can be processed into a can of squid ink (one less item exclusive to the
produce console), or thrown at people in a sort-of-similar fashion of
banana cream pies (except it's ink).

<details>
  <summary>Images</summary>

Fishing lures (forgot to take my cursor off the veggie one before the
screenshot):

![immagine](https://github.com/user-attachments/assets/8ba7a0f2-2a9f-4177-9c0d-ebeabd8a0ef7)

The five new fish:

![immagine](https://github.com/user-attachments/assets/1c251079-3b39-48bb-af6c-0a35623953a7)

</details>

<details>
<summary>A table of fish catchable wth each lure (excluding
holodeck)</summary>


![table](https://github.com/user-attachments/assets/dee95855-405b-4945-bfc2-70e816e46109)

</details>

A few more things in the CL, baitfish are a thing now.

## Why It's Good For The Game
There should be ways to contrast some of the RNG fishing has. After all,
it's only going to get more random the more fish are in the game.
Furthermore, I find it disappointing that a lot of food stuff is
exclusive to the ingredients console and there're no other ways to get
it.

## Changelog

🆑
add: Added fishing lures to the game. They don't get used up like baits
and let you catch specific kinds of fish, though they need to be spun
every few seconds. The whole set can be ordered from cargo for 450
credits.
balance: The magnet hook now removes dud chances.
add: Added five new fish types: perch, two types of pike, monkfish,
plaice and squid. Squids have a fairly special ink production trait,
which lets you use them (unless dead) to ink people face at close range,
and can be butchered for an ink sac, which can either be processed into
canned squid ink, or thrown at someone.
fix: Refactored throwing a little. Some items (specifically
components/elements) won't be triggered when caught. no more plates
shattering despite being caught for example.
add: Goldfish, lavaloops, needlefish and armorfish can now be used as
baits.
/🆑
This commit is contained in:
Ghom
2024-09-06 19:50:28 -04:00
committed by GitHub
parent a82fac4c7c
commit 10c2b7364e
66 changed files with 2501 additions and 1523 deletions
+3 -4
View File
@@ -60,12 +60,11 @@
* * hit_atom: The atom that has been hit by the boomerang component.
* * init_throwing_datum: The thrownthing datum that originally impacted the object, that we use to build the new throwing datum for the rebound.
*/
/datum/component/boomerang/proc/return_hit_throw(datum/source, atom/hit_atom, datum/thrownthing/init_throwing_datum)
/datum/component/boomerang/proc/return_hit_throw(datum/source, atom/hit_atom, datum/thrownthing/init_throwing_datum, caught)
SIGNAL_HANDLER
if (!COOLDOWN_FINISHED(src, last_boomerang_throw))
if (!COOLDOWN_FINISHED(src, last_boomerang_throw) || caught)
return
var/obj/item/true_parent = parent
aerodynamic_swing(init_throwing_datum, true_parent)
aerodynamic_swing(init_throwing_datum, parent)
/**
* Proc that triggers when the thrown boomerang does not hit a target.
+1 -1
View File
@@ -14,7 +14,7 @@
/obj/effect/constructing_effect,
/obj/effect/dummy/phased_mob,
/obj/effect/ebeam,
/obj/effect/fishing_lure,
/obj/effect/fishing_float,
/obj/effect/hotspot,
/obj/effect/landmark,
/obj/effect/light_emitter/tendril,
+3 -2
View File
@@ -60,10 +60,11 @@
return
check_if_detonate(I)
/datum/component/explodable/proc/explodable_impact(datum/source, atom/hit_atom, datum/thrownthing/throwingdatum)
/datum/component/explodable/proc/explodable_impact(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
SIGNAL_HANDLER
check_if_detonate(hit_atom)
if(!caught)
check_if_detonate(hit_atom)
/datum/component/explodable/proc/explodable_bump(datum/source, atom/A)
SIGNAL_HANDLER
+23 -19
View File
@@ -53,6 +53,8 @@
carbon_parent.update_body_parts()
else
normal_overlay = get_normal_overlay()
normal_overlay.color = color
RegisterSignals(parent, list(
COMSIG_COMPONENT_CLEAN_ACT,
@@ -113,37 +115,39 @@
SIGNAL_HANDLER
qdel(src)
/// Creampie subtype, handling signals and mood logic
/// splat subtype, handling signals and mood logic
GLOBAL_LIST_INIT(creamable, typecacheof(list(
/mob/living/carbon/human,
/mob/living/basic/pet/dog/corgi,
/mob/living/silicon/ai,
GLOBAL_LIST_INIT(splattable, zebra_typecacheof(list(
/mob/living/carbon/human = "human",
/mob/living/basic/pet/dog/corgi = "corgi",
/mob/living/silicon/ai = "ai",
)))
/datum/component/face_decal/creampie/Initialize()
. = ..()
if(!is_type_in_typecache(parent, GLOB.creamable))
/datum/component/face_decal/splat
///The mood_event that we add
var/mood_event_type
/datum/component/face_decal/splat/Initialize(icon_state, layers, color, memory_type = /datum/memory/witnessed_creampie, mood_event_type = /datum/mood_event/creampie)
if(!is_type_in_typecache(parent, GLOB.splattable))
return COMPONENT_INCOMPATIBLE
SEND_SIGNAL(parent, COMSIG_MOB_CREAMED, src)
add_memory_in_range(parent, 7, /datum/memory/witnessed_creampie, protagonist = parent)
. = ..()
/datum/component/face_decal/creampie/get_normal_overlay()
if(iscorgi(parent))
return mutable_appearance('icons/mob/effects/creampie.dmi', "[icon_state]_corgi")
SEND_SIGNAL(parent, COMSIG_MOB_HIT_BY_SPLAT, src)
add_memory_in_range(parent, 7, memory_type, protagonist = parent)
src.mood_event_type = mood_event_type
if(isAI(parent))
return mutable_appearance('icons/mob/effects/creampie.dmi', "[icon_state]_ai")
/datum/component/face_decal/splat/get_normal_overlay()
return mutable_appearance('icons/mob/effects/face_decal.dmi', "[icon_state]_[GLOB.splattable[type]]")
/datum/component/face_decal/creampie/RegisterWithParent()
/datum/component/face_decal/splat/RegisterWithParent()
. = ..()
if(iscarbon(parent))
var/mob/living/carbon/human/carbon_parent = parent
carbon_parent.add_mood_event("creampie", /datum/mood_event/creampie)
carbon_parent.add_mood_event("splat", mood_event_type)
/datum/component/face_decal/creampie/UnregisterFromParent()
/datum/component/face_decal/splat/UnregisterFromParent()
. = ..()
if(iscarbon(parent))
var/mob/living/carbon/carbon_parent = parent
carbon_parent.clear_mood_event("creampie")
carbon_parent.clear_mood_event("splat")
+3 -22
View File
@@ -44,15 +44,7 @@
if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT))
return
var/has_known_fishes = FALSE
for(var/reward in fish_source.fish_table)
if(!ispath(reward, /obj/item/fish))
continue
var/obj/item/fish/prototype = reward
if(initial(prototype.show_in_catalog))
has_known_fishes = TRUE
break
if(!has_known_fishes)
if(!fish_source.has_known_fishes())
return
examine_text += span_tinynoticeital("This is a fishing spot. You can look again to list its fishes...")
@@ -62,18 +54,7 @@
if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT))
return
var/list/known_fishes = list()
for(var/reward in fish_source.fish_table)
if(!ispath(reward, /obj/item/fish))
continue
var/obj/item/fish/prototype = reward
if(initial(prototype.show_in_catalog))
known_fishes += initial(prototype.name)
if(!length(known_fishes))
return
examine_text += span_info("You can catch the following fish here: [english_list(known_fishes)].")
fish_source.get_catchable_fish_names(user, parent, examine_text)
/datum/component/fishing_spot/proc/try_start_fishing(obj/item/possibly_rod, mob/user)
SIGNAL_HANDLER
@@ -94,7 +75,7 @@
/datum/component/fishing_spot/proc/start_fishing_challenge(obj/item/fishing_rod/rod, mob/user)
/// Roll what we caught based on modified table
var/result = fish_source.roll_reward(rod, user)
var/result = fish_source.roll_reward(rod, user, parent)
var/datum/fishing_challenge/challenge = new(src, result, rod, user)
fish_source.pre_challenge_started(rod, user, challenge)
challenge.start(user)
+5 -5
View File
@@ -111,17 +111,17 @@
var/datum/fish_source/fish_spot = fish_spot_container[NPC_FISHING_SPOT]
if(isnull(fish_spot))
return null
var/obj/effect/fishing_lure/lure = new(get_turf(target), target)
playsound(lure, 'sound/effects/splash.ogg', 100)
var/obj/effect/fishing_float/float = new(get_turf(target), target)
playsound(float, 'sound/effects/splash.ogg', 100)
var/happiness_percentage = living_parent.ai_controller?.blackboard[BB_BASIC_HAPPINESS] / 100
var/fishing_speed = 10 SECONDS - round(4 SECONDS * happiness_percentage)
if(!do_after(living_parent, fishing_speed, target = target) && !QDELETED(fish_spot))
qdel(lure)
qdel(float)
return
var/reward_loot = fish_spot.roll_reward(our_rod, parent)
fish_spot.dispense_reward(reward_loot, parent, target)
playsound(lure, 'sound/effects/bigsplash.ogg', 100)
qdel(lure)
playsound(float, 'sound/effects/bigsplash.ogg', 100)
qdel(float)
/obj/item/fishing_rod/mob_fisher
line = /obj/item/fishing_line/reinforced
+1 -1
View File
@@ -1,6 +1,6 @@
/datum/component/wearertargeting/sitcomlaughter
valid_slots = list(ITEM_SLOT_HANDS, ITEM_SLOT_BELT, ITEM_SLOT_ID, ITEM_SLOT_LPOCKET, ITEM_SLOT_RPOCKET, ITEM_SLOT_SUITSTORE, ITEM_SLOT_DEX_STORAGE)
signals = list(COMSIG_MOB_CREAMED, COMSIG_ON_CARBON_SLIP, COMSIG_POST_TILT_AND_CRUSH, COMSIG_MOB_CLUMSY_SHOOT_FOOT)
signals = list(COMSIG_MOB_HIT_BY_SPLAT, COMSIG_ON_CARBON_SLIP, COMSIG_POST_TILT_AND_CRUSH, COMSIG_MOB_CLUMSY_SHOOT_FOOT)
proctype = PROC_REF(EngageInComedy)
mobtype = /mob/living
///Sounds used for when user has a sitcom action occur
+74
View File
@@ -0,0 +1,74 @@
/datum/component/splat
///The icon state to use for the decal
var/icon_state
///The bodypart layer to use for the decal
var/layer
///The type of memory to celebrate the event of getting hit by this
var/memory_type
///The type of smudge we create on the floor
var/smudge_type
///The moodlet passed down to the creamed component
var/moodlet_type
///The color we give to the creamed component/overlay
var/splat_color
///The callback called when a mob is hit by this
var/datum/callback/hit_callback
/datum/component/splat/Initialize(
icon_state = "creampie",
layer = EXTERNAL_FRONT,
memory_type = /datum/memory/witnessed_creampie,
smudge_type = /obj/effect/decal/cleanable/food/pie_smudge,
moodlet_type = /datum/mood_event/creampie,
splat_color,
datum/callback/hit_callback,
)
. = ..()
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
src.icon_state = icon_state
src.layer = layer
src.memory_type = memory_type
src.smudge_type = smudge_type
src.moodlet_type = moodlet_type
src.hit_callback = hit_callback
src.splat_color = splat_color
/datum/component/splat/Destroy()
hit_callback = null
return ..()
/datum/component/splat/RegisterWithParent()
if(isprojectile(parent))
RegisterSignal(parent, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(projectile_splat))
else
RegisterSignal(parent, COMSIG_MOVABLE_IMPACT, PROC_REF(throw_splat))
/datum/component/splat/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_MOVABLE_IMPACT, COMSIG_PROJECTILE_SELF_ON_HIT))
/datum/component/splat/proc/projectile_splat(obj/projectile/source, atom/firer, atom/target, angle, hit_limb_zone, blocked)
SIGNAL_HANDLER
if(blocked != 100)
splat(source, target)
/datum/component/splat/proc/throw_splat(atom/movable/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
SIGNAL_HANDLER
if(caught) //someone caught us!
return
splat(source, hit_atom)
/datum/component/splat/proc/splat(atom/movable/source, atom/hit_atom)
var/turf/hit_turf = get_turf(hit_atom)
new smudge_type(hit_turf)
var/can_splat_on = TRUE
if(isliving(hit_atom))
var/mob/living/living_target_getting_hit = hit_atom
if(iscarbon(living_target_getting_hit))
can_splat_on = !!(living_target_getting_hit.get_bodypart(BODY_ZONE_HEAD))
hit_callback?.Invoke(living_target_getting_hit, can_splat_on)
if(can_splat_on && is_type_in_typecache(hit_atom, GLOB.splattable))
hit_atom.AddComponent(/datum/component/face_decal/splat, icon_state, layer, splat_color || source.color, memory_type, moodlet_type)
SEND_SIGNAL(source, COMSIG_MOVABLE_SPLAT, hit_atom)
qdel(source)