Chasm Fishing (#69252)

Adds the capability of fishing in chasms.
Chasms contain Chasm Chrabs, which are cute little crustaceans you can eat or put in an aquarium.
More importantly, chasms contain everything which has ever fallen into a chasm.
Falling into a chasm is no longer instant permanent round removal... as long as someone is willing to do some fishing, which means your chances of return haven't necessarily increased that much.

Potential rescuers (or people lamenting about how they dropped their necropolis chest into a hole) should also beware, chasm crabs are actually young Lobstrosities and there's a rare but not insubstantial chance that you'll fish one of them up instead, which won't make it very happy.

If you somehow come back to life inside a chasm (generally only likely by being a changeling or some admin interference, but perhaps you have some kind of implant which does it) you'll climb back out again, so holes are no longer a way of disposing of a changeling for good. You think The Thing would be defeated if you threw it down a hole?
Ethereals can't manage this because falling into a chasm causes too much damage and they simply smash on the way down.

add: You can now fish in chasms, and might manage to catch things that careless miners have dropped in there.
add: If your pole is long enough to reach into a deep hole, you might catch crabs.
balance: Changelings can climb out of chasms if you throw them into one.
imageadd: An icon of a nice little crab, who will grow up into something less nice.
add: You can craft fishing equipment from various lavaland materials, so that Ashwalkers can fish up dead miners before you.
add: You can sometimes find worms while digging up tiles, if appropriate for that material.
This commit is contained in:
Jacquerel
2022-08-25 20:13:36 -07:00
committed by GitHub
parent a20d1a8d96
commit bcadead566
14 changed files with 267 additions and 57 deletions
+1
View File
@@ -35,6 +35,7 @@
#define FISHING_SPOT_PRESET_BEACH "beach"
#define FISHING_SPOT_PRESET_LAVALAND_LAVA "lavaland lava"
#define FISHING_SPOT_PRESET_CHASM "chasm"
#define FISHING_MINIGAME_RULE_HEAVY_FISH "heavy"
#define FISHING_MINIGAME_RULE_WEIGHTED_BAIT "weighted"
+133 -52
View File
@@ -1,6 +1,10 @@
/// List of weakrefs to containers for things which have fallen into chasms
GLOBAL_LIST_INIT(chasm_storage, list())
// Used by /turf/open/chasm and subtypes to implement the "dropping" mechanic
/datum/component/chasm
var/turf/target_turf
var/obj/effect/abstract/chasm_storage/storage
var/fall_message = "GAH! Ah... where are you?"
var/oblivion_message = "You stumble and stare into the abyss before you. It stares back, and you fall into the enveloping dark."
@@ -26,12 +30,33 @@
/obj/effect/dummy/phased_mob,
/obj/effect/mapping_helpers,
/obj/effect/wisp,
/obj/effect/ebeam,
/obj/effect/fishing_lure,
))
/datum/component/chasm/Initialize(turf/target)
RegisterSignal(parent, COMSIG_ATOM_ENTERED, .proc/Entered)
target_turf = target
START_PROCESSING(SSobj, src) // process on create, in case stuff is still there
src.parent.AddElement(/datum/element/lazy_fishing_spot, FISHING_SPOT_PRESET_CHASM)
/datum/component/chasm/UnregisterFromParent()
STOP_PROCESSING(SSobj, src)
remove_storage()
/**
* Deletes the chasm storage object and removes empty weakrefs from global list
*/
/datum/component/chasm/proc/remove_storage()
if (!storage)
return
QDEL_NULL(storage)
var/list/chasm_storage = list()
for (var/datum/weakref/ref as anything in GLOB.chasm_storage)
if (!ref.resolve())
continue
chasm_storage += ref
GLOB.chasm_storage = chasm_storage
/datum/component/chasm/proc/Entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
@@ -54,35 +79,36 @@
LAZYREMOVE(found_safeties, S)
return LAZYLEN(found_safeties)
/datum/component/chasm/proc/drop_stuff(AM)
/datum/component/chasm/proc/drop_stuff(dropped_thing)
if (is_safe())
return FALSE
var/atom/parent = src.parent
var/to_check = AM ? list(AM) : parent.contents
var/to_check = dropped_thing ? list(dropped_thing) : parent.contents
for (var/thing in to_check)
if (droppable(thing))
. = TRUE
INVOKE_ASYNC(src, .proc/drop, thing)
/datum/component/chasm/proc/droppable(atom/movable/AM)
var/datum/weakref/falling_ref = WEAKREF(AM)
/datum/component/chasm/proc/droppable(atom/movable/dropped_thing)
var/datum/weakref/falling_ref = WEAKREF(dropped_thing)
// avoid an infinite loop, but allow falling a large distance
if(falling_atoms[falling_ref] && falling_atoms[falling_ref] > 30)
return FALSE
if(!isliving(AM) && !isobj(AM))
if(!isliving(dropped_thing) && !isobj(dropped_thing))
return FALSE
if(is_type_in_typecache(AM, forbidden_types) || AM.throwing || (AM.movement_type & (FLOATING|FLYING)))
if(is_type_in_typecache(dropped_thing, forbidden_types) || dropped_thing.throwing || (dropped_thing.movement_type & (FLOATING|FLYING)))
return FALSE
//Flies right over the chasm
if(ismob(AM))
var/mob/M = AM
if(ismob(dropped_thing))
var/mob/M = dropped_thing
if(M.buckled) //middle statement to prevent infinite loops just in case!
var/mob/buckled_to = M.buckled
if((!ismob(M.buckled) || (buckled_to.buckled != M)) && !droppable(M.buckled))
return FALSE
if(ishuman(AM))
var/mob/living/carbon/human/victim = AM
if(ishuman(dropped_thing))
var/mob/living/carbon/human/victim = dropped_thing
if(istype(victim.belt, /obj/item/wormhole_jaunter))
var/obj/item/wormhole_jaunter/jaunter = victim.belt
var/turf/chasm = get_turf(victim)
@@ -92,63 +118,118 @@
return fall_into_chasm
return TRUE
/datum/component/chasm/proc/drop(atom/movable/AM)
var/datum/weakref/falling_ref = WEAKREF(AM)
/datum/component/chasm/proc/drop(atom/movable/dropped_thing)
var/datum/weakref/falling_ref = WEAKREF(dropped_thing)
//Make sure the item is still there after our sleep
if(!AM || !falling_ref?.resolve())
if(!dropped_thing || !falling_ref?.resolve())
falling_atoms -= falling_ref
return
falling_atoms[falling_ref] = (falling_atoms[falling_ref] || 0) + 1
var/turf/T = target_turf
var/atom/parent = src.parent
if(T)
// send to the turf below
AM.visible_message(span_boldwarning("[AM] falls into [parent]!"), span_userdanger("[fall_message]"))
T.visible_message(span_boldwarning("[AM] falls from above!"))
AM.forceMove(T)
if(isliving(AM))
var/mob/living/L = AM
dropped_thing.visible_message(span_boldwarning("[dropped_thing] falls into [parent]!"), span_userdanger("[fall_message]"))
T.visible_message(span_boldwarning("[dropped_thing] falls from above!"))
dropped_thing.forceMove(T)
if(isliving(dropped_thing))
var/mob/living/L = dropped_thing
L.Paralyze(100)
L.adjustBruteLoss(30)
falling_atoms -= falling_ref
return
else
// send to oblivion
AM.visible_message(span_boldwarning("[AM] falls into [parent]!"), span_userdanger("[oblivion_message]"))
if (isliving(AM))
var/mob/living/L = AM
L.notransform = TRUE
L.Paralyze(20 SECONDS)
// send to oblivion
dropped_thing.visible_message(span_boldwarning("[dropped_thing] falls into [parent]!"), span_userdanger("[oblivion_message]"))
if (isliving(dropped_thing))
var/mob/living/falling_mob = dropped_thing
falling_mob.notransform = TRUE
falling_mob.Paralyze(20 SECONDS)
var/oldtransform = AM.transform
var/oldcolor = AM.color
var/oldalpha = AM.alpha
animate(AM, transform = matrix() - matrix(), alpha = 0, color = rgb(0, 0, 0), time = 10)
for(var/i in 1 to 5)
//Make sure the item is still there after our sleep
if(!AM || QDELETED(AM))
return
AM.pixel_y--
sleep(2)
var/oldtransform = dropped_thing.transform
var/oldcolor = dropped_thing.color
var/oldalpha = dropped_thing.alpha
animate(dropped_thing, transform = matrix() - matrix(), alpha = 0, color = rgb(0, 0, 0), time = 10)
for(var/i in 1 to 5)
//Make sure the item is still there after our sleep
if(!AM || QDELETED(AM))
if(!dropped_thing || QDELETED(dropped_thing))
return
dropped_thing.pixel_y--
sleep(2)
if(iscyborg(AM))
var/mob/living/silicon/robot/S = AM
qdel(S.mmi)
if(isliving(AM))
var/mob/living/L = AM
if(L.stat != DEAD)
L.death(TRUE)
//Make sure the item is still there after our sleep
if(!dropped_thing || QDELETED(dropped_thing))
return
falling_atoms -= falling_ref
qdel(AM)
if(AM && !QDELETED(AM)) //It's indestructible
var/atom/parent = src.parent
parent.visible_message(span_boldwarning("[parent] spits out [AM]!"))
AM.alpha = oldalpha
AM.color = oldcolor
AM.transform = oldtransform
AM.throw_at(get_edge_target_turf(parent,pick(GLOB.alldirs)),rand(1, 10),rand(1, 10))
if (!storage)
storage = new(get_turf(parent))
RegisterSignal(storage, COMSIG_ATOM_EXITED, .proc/left_chasm)
GLOB.chasm_storage += WEAKREF(storage)
if (storage.contains(dropped_thing))
return
dropped_thing.alpha = oldalpha
dropped_thing.color = oldcolor
dropped_thing.transform = oldtransform
if (dropped_thing.forceMove(storage))
if (isliving(dropped_thing))
RegisterSignal(dropped_thing, COMSIG_LIVING_REVIVE, .proc/on_revive)
SEND_SIGNAL(dropped_thing, COMSIG_MOVABLE_SECLUDED_LOCATION)
else
parent.visible_message(span_boldwarning("[parent] spits out [dropped_thing]!"))
dropped_thing.throw_at(get_edge_target_turf(parent, pick(GLOB.alldirs)), rand(1, 10), rand(1, 10))
if (isliving(dropped_thing))
var/mob/living/fallen_mob = dropped_thing
if(fallen_mob.stat != DEAD)
fallen_mob.death(TRUE)
fallen_mob.notransform = FALSE
fallen_mob.apply_damage(300)
falling_atoms -= falling_ref
/**
* Called when something has left the chasm depths storage.
* Arguments
*
* * source - Chasm object holder.
* * gone - Item which has just left the chasm contents.
*/
/datum/component/chasm/proc/left_chasm(atom/source, atom/movable/gone)
SIGNAL_HANDLER
UnregisterSignal(gone, COMSIG_LIVING_REVIVE)
#define CHASM_TRAIT "chasm trait"
/**
* Called if something comes back to life inside the pit. Expected sources are badmins and changelings.
* Ethereals should take enough damage to be smashed and not revive.
*
* Arguments
* * escapee - Lucky guy who just came back to life at the bottom of a hole.
*/
/datum/component/chasm/proc/on_revive(mob/living/escapee)
SIGNAL_HANDLER
var/atom/parent = src.parent
parent.visible_message(span_boldwarning("After a long climb, [escapee] leaps out of [parent]!"))
ADD_TRAIT(escapee, TRAIT_MOVE_FLYING, CHASM_TRAIT) //Otherwise they instantly fall back in
escapee.forceMove(get_turf(parent))
escapee.throw_at(get_edge_target_turf(parent, pick(GLOB.alldirs)), rand(1, 10), rand(1, 10))
REMOVE_TRAIT(escapee, TRAIT_MOVE_FLYING, CHASM_TRAIT)
escapee.Paralyze(20 SECONDS, TRUE)
UnregisterSignal(escapee, COMSIG_LIVING_REVIVE)
#undef CHASM_TRAIT
/**
* An abstract object which is basically just a bag that the chasm puts people inside
*/
/obj/effect/abstract/chasm_storage
name = "chasm depths"
desc = "The bottom of a hole. You shouldn't be able to interact with this."
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+6 -1
View File
@@ -10,8 +10,10 @@
var/action_text
/// What should we tell other people what the user did? (Eg: "Guy digs up the turf.")
var/action_text_third_person
/// Percentage chance of receiving a bonus worm
var/worm_chance
/datum/element/diggable/Attach(datum/target, to_spawn, amount = 1, action_text = "dig up", action_text_third_person = "digs up")
/datum/element/diggable/Attach(datum/target, to_spawn, amount = 1, worm_chance = 30, action_text = "dig up", action_text_third_person = "digs up")
. = ..()
if(!isturf(target))
return ELEMENT_INCOMPATIBLE
@@ -21,6 +23,7 @@
src.to_spawn = to_spawn
src.amount = amount
src.worm_chance = worm_chance
src.action_text = action_text
src.action_text_third_person = action_text_third_person
@@ -36,6 +39,8 @@
for(var/i in 1 to amount)
new to_spawn(source)
if (prob(worm_chance))
new /obj/item/food/bait/worm(source)
user.visible_message(
span_notice("[user] [action_text_third_person] [source]."),
+7
View File
@@ -24,6 +24,8 @@
var/dug = FALSE
/// Icon state to use when broken
var/broken_state = "asteroid_dug"
/// Percentage chance of receiving a bonus worm
var/worm_chance = 30
/turf/open/misc/asteroid/break_tile()
icon_state = broken_state
@@ -39,6 +41,8 @@
/turf/open/misc/asteroid/proc/getDug()
dug = TRUE
new digResult(src, 5)
if (prob(worm_chance))
new /obj/item/food/bait/worm(src)
icon_state = "[base_icon_state]_dug"
/// If the user can dig the turf
@@ -124,6 +128,7 @@ GLOBAL_LIST_EMPTY(dug_up_basalt)
/turf/open/misc/asteroid/basalt/airless
initial_gas_mix = AIRLESS_ATMOS
worm_chance = 0
/turf/open/misc/asteroid/basalt/Initialize(mapload)
. = ..()
@@ -152,6 +157,7 @@ GLOBAL_LIST_EMPTY(dug_up_basalt)
initial_gas_mix = AIRLESS_ATMOS
baseturfs = /turf/open/misc/asteroid/airless
turf_type = /turf/open/misc/asteroid/airless
worm_chance = 0
/turf/open/misc/asteroid/snow
gender = PLURAL
@@ -217,6 +223,7 @@ GLOBAL_LIST_EMPTY(dug_up_basalt)
/turf/open/misc/asteroid/snow/airless
initial_gas_mix = AIRLESS_ATMOS
worm_chance = 0
/turf/open/misc/asteroid/snow/temperatre
initial_gas_mix = "o2=22;n2=82;TEMP=255.37"
+4 -3
View File
@@ -132,7 +132,8 @@
/turf/open/floor/grass/Initialize(mapload)
. = ..()
spawniconchange()
AddElement(/datum/element/diggable, /obj/item/stack/ore/glass, 2, "uproot", "uproots")
AddElement(/datum/element/diggable, /obj/item/stack/ore/glass, 2, worm_chance = 50, \
action_text = "uproot", action_text_third_person = "uproots")
/turf/open/floor/grass/proc/spawniconchange()
icon_state = "grass[rand(0,3)]"
@@ -170,7 +171,7 @@
/turf/open/floor/fake_snow/Initialize(mapload)
. = ..()
AddElement(/datum/element/diggable, /obj/item/stack/tile/mineral/snow, 2)
AddElement(/datum/element/diggable, /obj/item/stack/tile/mineral/snow, 2, worm_chance = 0)
/turf/open/floor/fake_snow/setup_broken_states()
return list("snow_dug")
@@ -197,7 +198,7 @@
/turf/open/floor/fakebasalt/Initialize(mapload)
. = ..()
AddElement(/datum/element/diggable, /obj/item/stack/ore/glass/basalt, 2)
AddElement(/datum/element/diggable, /obj/item/stack/ore/glass/basalt, 2, worm_chance = 0)
if(prob(15))
icon_state = "basalt[rand(0, 12)]"
set_basalt_light(src)
@@ -0,0 +1,46 @@
/// An object which should replace itself on initialisation with something which fell into a chasm.
/obj/item/chasm_detritus
name = "chasm detritus"
desc = "Abstract concept of an object which once fell into a deep hole."
icon_state = "skub"
/// Stuff which you can always fish up even if nothing fell into a hole.
var/static/list/default_contents = list(\
/obj/item/stack/ore/slag = 2, \
/obj/item/stack/sheet/bone = 3, \
/obj/effect/mob_spawn/corpse/human/skeleton = 1, \
/mob/living/simple_animal/hostile/asteroid/lobstrosity/lava = 1, \
)
/obj/item/chasm_detritus/Initialize(mapload)
. = ..()
if (prob(25))
create_default_object()
return
var/list/chasm_stuff = find_chasm_contents()
if (!chasm_stuff.len)
create_default_object()
return
var/atom/movable/detritus = pick(chasm_stuff)
detritus.forceMove(get_turf(src))
qdel(src)
/// Instantiates something from the default list.
/obj/item/chasm_detritus/proc/create_default_object()
var/contents_type = pick(default_contents)
new contents_type(get_turf(src))
qdel(src)
/// Returns a list of every object which is currently inside of a chasm.
/obj/item/chasm_detritus/proc/find_chasm_contents()
var/list/chasm_contents = list()
if (!GLOB.chasm_storage.len)
return chasm_contents
var/list/chasm_storage_resolved = recursive_list_resolve(GLOB.chasm_storage)
for (var/obj/storage as anything in chasm_storage_resolved)
for (var/thing as anything in storage.contents)
chasm_contents += thing
return chasm_contents
+15
View File
@@ -181,6 +181,21 @@
fillet_type = /obj/item/food/fishmeat/armorfish
fish_ai_type = FISH_AI_SLOW
//Chasm fish
/obj/item/fish/chasm_crab
name = "chasm chrab"
desc = "The young of the lobstrosity mature in pools below the earth, eating what falls in until large enough to clamber out. Those found near the station are well-fed."
icon_state = "chrab"
dedicated_in_aquarium_icon_state = "chrab_small"
sprite_height = 9
sprite_width = 8
source_height = 9
source_width = 8
stable_population = 4
feeding_frequency = 15 MINUTES
random_case_rarity = FISH_RARITY_RARE
fillet_type = /obj/item/food/meat/slab/rawcrab
/obj/item/storage/box/fish_debug
name = "box full of fish"
+25
View File
@@ -30,6 +30,20 @@
fishing_line_traits = FISHING_LINE_BOUNCY
line_color = "#99313f"
/obj/item/fishing_line/sinew
name = "fishing sinew"
desc = "an all-natural fishing line made of stretched out sinew"
icon = 'icons/obj/fishing.dmi'
icon_state = "reel_sinew"
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_PRIMAL
// Hooks
/obj/item/fishing_hook
@@ -62,6 +76,17 @@
fishing_hook_traits = FISHING_HOOK_WEIGHTED
rod_overlay_icon_state = "hook_weighted_overlay"
/obj/item/fishing_hook/bone
name = "bone hook"
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_PRIMAL
/obj/item/storage/toolbox/fishing
name = "fishing toolbox"
+14
View File
@@ -362,6 +362,20 @@
if(gone == hook)
hook = 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"
/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_PRIMAL
/obj/item/fishing_rod/master
name = "master fishing rod"
desc = "The mythical rod of a lost fisher king. Said to be imbued with un-paralleled fishing power. There's writing on the back of the pole. \"中国航天制造\""
+4 -1
View File
@@ -11,6 +11,9 @@ GLOBAL_LIST_INIT(preset_fish_sources,init_fishing_configurations())
var/datum/fish_source/lavaland/lava_preset = new
.[FISHING_SPOT_PRESET_LAVALAND_LAVA] = lava_preset
var/datum/fish_source/chasm/chasm_preset = new
.[FISHING_SPOT_PRESET_CHASM] = chasm_preset
/// Where the fish actually come from - every fishing spot has one assigned but multiple fishing holes can share single source, ie single shared one for ocean/lavaland river
/datum/fish_source
/// Fish catch weight table - these are relative weights
@@ -87,7 +90,7 @@ GLOBAL_LIST_INIT(preset_fish_sources,init_fishing_configurations())
reward_path = FISHING_DUD //Ran out of these since rolling (multiple fishermen on same source most likely)
if(ispath(reward_path))
if(ispath(reward_path,/obj/item))
var/obj/item/reward = new reward_path
var/obj/item/reward = new reward_path(get_turf(fisherman))
if(ispath(reward_path,/obj/item/fish))
var/obj/item/fish/caught_fish = reward
caught_fish.randomize_weight_and_size()
@@ -21,6 +21,17 @@
)
catalog_description = "Fish dimension (Fishing portal generator)"
/datum/fish_source/chasm
catalog_description = "Chasm depths"
background = "fishing_background_lavaland"
fish_table = list(
FISHING_DUD = 5,
/obj/item/fish/chasm_crab = 15,
/obj/item/chasm_detritus = 30,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 5
/datum/fish_source/lavaland
catalog_description = "Lava vents"
background = "fishing_background_lavaland"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

+1
View File
@@ -2962,6 +2962,7 @@
#include "code\modules\fishing\aquarium\aquarium.dm"
#include "code\modules\fishing\aquarium\aquarium_kit.dm"
#include "code\modules\fishing\fish\_fish.dm"
#include "code\modules\fishing\fish\chasm_detritus.dm"
#include "code\modules\fishing\fish\fish_types.dm"
#include "code\modules\fishing\sources\_fish_source.dm"
#include "code\modules\fishing\sources\source_types.dm"