You can now link fishing portal generators to other fishing spots. (#86291)

## About The Pull Request
You can now interact with the fishing portal generator with a multitool
to load it in the buffer and subsequently link fishing spots to it by
tapping them with the same multitool. The maximum number of fishing
spots that can be linked at once depends on the tier of the matter bins
of the machinery. Normally, while you can link fishing spots from other
z-levels, they can only be activated if they're on the same z-level as
the machinery (or if both are on station for multi-z stations). This
limitation can be bypassed by upgrading the machinery with a tier 3 or
higher capacitator.

While it's possible, I'm not spriting new fishing portal overlays and
icons for the radial menu for every fish source out there (yet). The
code is enough work for now.

This also comes with a unit test, because there is no such thing as too
many unit tests for fishing.

## Why It's Good For The Game
Fish portal generators are designed to let players fish a different
bunch of things while being able to be moved wherever you like, unlike a
lake or an ocean, with all the comfort of being able to able to catch
fish from distant locations. Allowing players to link other fishing
spots to it fits its design. It also means that you can go out and
explore, find more fishing spots and then return to the station without
having to detach yourself from the ongoing round for several more
minutes.

## Changelog

🆑
add: You can now link fishing portal generators to other fishing spots
with a multitool. The number of fishing spots that can be linked at once
and whether the link can be activated from different z levels depends on
the tier of the stock parts it's built with.
/🆑

---------

Co-authored-by: Emmett Gaines <ninjanomnom@gmail.com>
This commit is contained in:
Ghom
2024-09-18 14:14:04 +00:00
committed by GitHub
co-authored by Emmett Gaines
parent 4687b1725a
commit 1aad3e118c
12 changed files with 291 additions and 16 deletions
+197 -9
View File
@@ -11,6 +11,46 @@
///The current fishing spot loaded in
var/datum/component/fishing_spot/active
///A list of fishing spot it's linked to with a multitool.
var/list/linked_fishing_spots
///The maximum number of fishing spots it can be linked to
var/max_fishing_spots = 1
///If true, the fishing portal can stay connected to a linked fishing spot even on different z-levels
var/long_range_link = FALSE
/obj/machinery/fishing_portal_generator/Initialize(mapload)
. = ..()
var/static/list/tool_screentips = list(
TOOL_MULTITOOL = list(
SCREENTIP_CONTEXT_LMB = "Link",
SCREENTIP_CONTEXT_RMB = "Unlink fishing spots"
),
)
AddElement(/datum/element/contextual_screentip_tools, tool_screentips)
ADD_TRAIT(src, TRAIT_UNLINKABLE_FISHING_SPOT, INNATE_TRAIT)
/obj/machinery/fishing_portal_generator/Destroy()
deactivate()
linked_fishing_spots = null
return ..()
///Higher tier parts let you link to more fishing spots at once and eventually let you connect through different zlevels.
/obj/machinery/fishing_portal_generator/RefreshParts()
. = ..()
max_fishing_spots = 0
long_range_link = FALSE
for(var/datum/stock_part/matter_bin/matter_bin in component_parts)
max_fishing_spots += matter_bin.tier * 0.5
max_fishing_spots = ROUND_UP(max_fishing_spots)
for(var/datum/stock_part/capacitor/capacitor in component_parts)
if(capacitor.tier >= 3)
long_range_link = TRUE
if(!long_range_link)
check_fishing_spot_z()
if(length(linked_fishing_spots) > max_fishing_spots)
if(active)
deactivate()
linked_fishing_spots.len = max_fishing_spots
/obj/machinery/fishing_portal_generator/on_set_panel_open()
update_appearance()
@@ -21,9 +61,94 @@
default_unfasten_wrench(user, tool)
return ITEM_INTERACT_SUCCESS
/obj/machinery/fishing_portal_generator/multitool_act(mob/living/user, obj/item/multitool/tool)
if(machine_stat & NOPOWER)
balloon_alert(user, "no power!")
return ITEM_INTERACT_BLOCKING
var/unlink = tool.buffer == src
tool.set_buffer(unlink ? null : src)
balloon_alert(user, "fish-porter [unlink ? "un" : ""]linked")
if(!unlink)
tool.item_flags |= ITEM_HAS_CONTEXTUAL_SCREENTIPS
RegisterSignal(tool, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, PROC_REF(multitool_context))
RegisterSignal(tool, COMSIG_MULTITOOL_REMOVE_BUFFER, PROC_REF(multitool_unbuffered))
return ITEM_INTERACT_SUCCESS
/obj/machinery/fishing_portal_generator/multitool_act_secondary(mob/living/user, obj/item/tool)
if(machine_stat & NOPOWER)
balloon_alert(user, "no power!")
return ITEM_INTERACT_BLOCKING
if(!length(linked_fishing_spots))
balloon_alert(user, "nothing to unlink!")
return ITEM_INTERACT_BLOCKING
var/list/fishing_list = list()
var/id = 1
for(var/atom/spot as anything in linked_fishing_spots)
var/choice_name = "[spot.name] ([id])"
fishing_list[choice_name] = spot
id++
var/list/choices = list()
for(var/radial_name in fishing_list)
var/datum/fish_source/source = fishing_list[radial_name]
var/mutable_appearance/appearance = mutable_appearance('icons/hud/radial_fishing.dmi', source.radial_state)
appearance.add_overlay('icons/hud/radial_fishing.dmi', "minus_sign")
choices[radial_name] = appearance
var/choice = show_radial_menu(user, src, choices, radius = 38, custom_check = CALLBACK(src, TYPE_PROC_REF(/atom, can_interact), user), tooltips = TRUE)
if(!choice)
return
var/atom/spot = fishing_list[choice]
if(QDELETED(spot) || !(spot in linked_fishing_spots) || !can_interact(user))
return
unlink_fishing_spot(spot)
balloon_alert(user, "fishing spot unlinked")
/obj/machinery/fishing_portal_generator/proc/multitool_context(obj/item/source, list/context, atom/target, mob/living/user)
SIGNAL_HANDLER
if(HAS_TRAIT(target, TRAIT_FISHING_SPOT) && !HAS_TRAIT(target, TRAIT_UNLINKABLE_FISHING_SPOT))
context[SCREENTIP_CONTEXT_LMB] = "Link to fish-porter"
return CONTEXTUAL_SCREENTIP_SET
return NONE
/obj/machinery/fishing_portal_generator/proc/multitool_unbuffered(datum/source, datum/buffer)
SIGNAL_HANDLER
UnregisterSignal(source, list(COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, COMSIG_MULTITOOL_REMOVE_BUFFER))
///Called when using a multitool on any other fishing source.
/obj/machinery/fishing_portal_generator/proc/link_fishing_spot(datum/fish_source/source, atom/spot, mob/living/user)
if(istype(spot, /obj/machinery/fishing_portal_generator)) //Don't link it to itself or other fishing portals.
return
if(length(linked_fishing_spots) >= max_fishing_spots)
spot.balloon_alert(user, "cannot link more!")
return ITEM_INTERACT_BLOCKING
for(var/other_spot in linked_fishing_spots)
var/datum/fish_source/stored = linked_fishing_spots[other_spot]
if(stored == source)
spot.balloon_alert(user, "already linked!")
playsound(src, 'sound/machines/buzz-sigh.ogg', 15, FALSE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
return ITEM_INTERACT_BLOCKING
if(HAS_TRAIT(spot, TRAIT_UNLINKABLE_FISHING_SPOT))
spot.balloon_alert(user, "unlinkable fishing spot!")
playsound(src, 'sound/machines/buzz-sigh.ogg', 15, FALSE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
return ITEM_INTERACT_BLOCKING
LAZYSET(linked_fishing_spots, spot, source)
RegisterSignal(spot, SIGNAL_REMOVETRAIT(TRAIT_FISHING_SPOT), PROC_REF(unlink_fishing_spot))
spot.balloon_alert(user, "fishing spot linked")
playsound(spot, 'sound/machines/ping.ogg', 15, TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
return ITEM_INTERACT_SUCCESS
/obj/machinery/fishing_portal_generator/proc/unlink_fishing_spot(atom/spot)
SIGNAL_HANDLER
var/datum/fish_source/source = linked_fishing_spots[spot]
if(active?.fish_source == source)
deactivate()
LAZYREMOVE(linked_fishing_spots, spot)
UnregisterSignal(spot, SIGNAL_REMOVETRAIT(TRAIT_FISHING_SPOT))
/obj/machinery/fishing_portal_generator/examine(mob/user)
. = ..()
. += span_notice("You can unlock further portal settings by completing fish scanning experiments.")
. += span_notice("You can unlock further portal settings by completing fish scanning experiments, \
or by connecting it to other fishing spots with a multitool.")
/obj/machinery/fishing_portal_generator/emag_act(mob/user, obj/item/card/emag/emag_card)
if(obj_flags & EMAGGED)
@@ -47,21 +172,78 @@
if(!active)
return
. += "portal_on"
var/datum/fish_source/portal/portal = active.fish_source
var/datum/fish_source/portal = active.fish_source
. += portal.overlay_state
. += emissive_appearance(icon, "portal_emissive", src)
/obj/machinery/fishing_portal_generator/proc/activate(datum/fish_source/selected_source)
/obj/machinery/fishing_portal_generator/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
. = ..()
check_fishing_spot_z()
/obj/machinery/fishing_portal_generator/proc/check_fishing_spot_z()
if(!active || long_range_link || istype(active.fish_source, /datum/fish_source/portal))
return
var/turf/new_turf = get_turf(src)
if(!new_turf)
deactivate()
return
for(var/atom/spot as anything in linked_fishing_spots)
if(linked_fishing_spots[spot] != active.fish_source)
continue
var/turf/turf = get_turf(spot)
if(turf.z != new_turf.z && !(is_station_level(turf.z) && is_station_level(new_turf.z)))
deactivate()
/obj/machinery/fishing_portal_generator/proc/activate(datum/fish_source/selected_source, mob/user)
if(QDELETED(selected_source))
return
if(machine_stat & NOPOWER)
balloon_alert(user, "no power!")
return ITEM_INTERACT_BLOCKING
if(!istype(selected_source, /datum/fish_source/portal)) //likely from a linked fishing spot
var/abort = TRUE
for(var/atom/spot as anything in linked_fishing_spots)
if(linked_fishing_spots[spot] != selected_source)
continue
if(long_range_link)
abort = FALSE
var/turf/spot_turf = get_turf(spot)
var/turf/turf = get_turf(src)
if(turf.z == spot_turf.z || (is_station_level(turf.z) && is_station_level(spot_turf.z)))
abort = FALSE
if(!abort)
RegisterSignal(spot, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_fishing_spot_z_level_changed))
break
if(abort)
balloon_alert(user, "cannot reach linked!")
return
active = AddComponent(/datum/component/fishing_spot, selected_source)
ADD_TRAIT(src, TRAIT_CATCH_AND_RELEASE, INNATE_TRAIT)
use_power = ACTIVE_POWER_USE
if(use_power != NO_POWER_USE)
use_power = ACTIVE_POWER_USE
update_icon()
/obj/machinery/fishing_portal_generator/proc/deactivate()
if(!active)
return
if(!istype(active.fish_source, /datum/fish_source/portal))
for(var/atom/spot as anything in linked_fishing_spots)
if(linked_fishing_spots[spot] == active.fish_source)
UnregisterSignal(spot, COMSIG_MOVABLE_Z_CHANGED)
QDEL_NULL(active)
use_power = IDLE_POWER_USE
REMOVE_TRAIT(src, TRAIT_CATCH_AND_RELEASE, INNATE_TRAIT)
update_icon()
if(!QDELETED(src))
if(use_power != NO_POWER_USE)
use_power = IDLE_POWER_USE
update_icon()
/obj/machinery/fishing_portal_generator/proc/on_fishing_spot_z_level_changed(atom/spot, turf/old_turf, turf/new_turf, same_z_layer)
SIGNAL_HANDLER
var/turf/turf = get_turf(src)
if(turf.z != new_turf.z && !(is_station_level(turf.z) && is_station_level(new_turf.z)))
deactivate()
/obj/machinery/fishing_portal_generator/on_set_is_operational(old_value)
if(old_value)
@@ -90,18 +272,24 @@
var/datum/fish_source/portal/reward = GLOB.preset_fish_sources[experiment.fish_source_reward]
available_fish_sources[reward.radial_name] = reward
var/id = 1
for(var/atom/spot as anything in linked_fishing_spots)
var/choice_name = "[spot.name] ([id])"
available_fish_sources[choice_name] = linked_fishing_spots[spot]
id++
if(length(available_fish_sources) == 1)
activate(default)
activate(default, user)
return
var/list/choices = list()
for(var/radial_name in available_fish_sources)
var/datum/fish_source/portal/source = available_fish_sources[radial_name]
var/datum/fish_source/source = available_fish_sources[radial_name]
choices[radial_name] = image(icon = 'icons/hud/radial_fishing.dmi', icon_state = source.radial_state)
var/choice = show_radial_menu(user, src, choices, radius = 38, custom_check = CALLBACK(src, TYPE_PROC_REF(/atom, can_interact), user), tooltips = TRUE)
if(!choice || !can_interact(user))
return
activate(available_fish_sources[choice])
activate(available_fish_sources[choice], user)
/obj/machinery/fishing_portal_generator/emagged
obj_flags = parent_type::obj_flags | EMAGGED
@@ -82,6 +82,10 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
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/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,
+7 -4
View File
@@ -106,12 +106,9 @@
/obj/item/fish/goldfish/three_eyes = 3,
)
catalog_description = "Aquarium dimension (Fishing portal generator)"
radial_state = "fish_tank"
///The name of this option shown in the radial menu on the fishing portal generator
var/radial_name = "Aquarium"
///The icon state shown for this option in the radial menu
var/radial_state = "fish_tank"
///The icon state of the overlay shown on the machine when active.
var/overlay_state = "portal_aquarium"
/datum/fish_source/portal/beach
fish_table = list(
@@ -404,6 +401,12 @@
)
fishing_difficulty = FISHING_EASY_DIFFICULTY
/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...
/datum/fish_source/holographic/on_fishing_spot_del(datum/component/fishing_spot/spot)
REMOVE_TRAIT(spot.parent, TRAIT_UNLINKABLE_FISHING_SPOT, REF(src))
/datum/fish_source/holographic/generate_wiki_contents(datum/autowiki/fish_sources/wiki)
var/obj/item/fish/prototype = /obj/item/fish/holo/checkered
return LIST_VALUE_WRAP_LISTS(list(