diff --git a/code/__DEFINES/fishing.dm b/code/__DEFINES/fishing.dm index 40b4d21a9e8..8b1917f47af 100644 --- a/code/__DEFINES/fishing.dm +++ b/code/__DEFINES/fishing.dm @@ -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" diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm index b69988939b6..dc44e40e693 100644 --- a/code/datums/components/chasm.dm +++ b/code/datums/components/chasm.dm @@ -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 diff --git a/code/datums/elements/diggable.dm b/code/datums/elements/diggable.dm index e57b24fd250..d5a4811e005 100644 --- a/code/datums/elements/diggable.dm +++ b/code/datums/elements/diggable.dm @@ -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]."), diff --git a/code/game/turfs/open/asteroid.dm b/code/game/turfs/open/asteroid.dm index fdf21c7a293..5c5a357673f 100644 --- a/code/game/turfs/open/asteroid.dm +++ b/code/game/turfs/open/asteroid.dm @@ -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" diff --git a/code/game/turfs/open/floor/fancy_floor.dm b/code/game/turfs/open/floor/fancy_floor.dm index 8c8aac4bee0..5a9224433cf 100644 --- a/code/game/turfs/open/floor/fancy_floor.dm +++ b/code/game/turfs/open/floor/fancy_floor.dm @@ -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) diff --git a/code/modules/fishing/fish/chasm_detritus.dm b/code/modules/fishing/fish/chasm_detritus.dm new file mode 100644 index 00000000000..a20ab5c42df --- /dev/null +++ b/code/modules/fishing/fish/chasm_detritus.dm @@ -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 diff --git a/code/modules/fishing/fish/fish_types.dm b/code/modules/fishing/fish/fish_types.dm index f6e99c73586..c96ff4221ba 100644 --- a/code/modules/fishing/fish/fish_types.dm +++ b/code/modules/fishing/fish/fish_types.dm @@ -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" diff --git a/code/modules/fishing/fishing_equipment.dm b/code/modules/fishing/fishing_equipment.dm index 84281601a12..385936e712e 100644 --- a/code/modules/fishing/fishing_equipment.dm +++ b/code/modules/fishing/fishing_equipment.dm @@ -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" diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index 90d84a28467..d9900243ef7 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -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. \"中国航天制造\"" diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm index f27d4aed837..4e6d708c3a3 100644 --- a/code/modules/fishing/sources/_fish_source.dm +++ b/code/modules/fishing/sources/_fish_source.dm @@ -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() diff --git a/code/modules/fishing/sources/source_types.dm b/code/modules/fishing/sources/source_types.dm index 9b626de6956..d3a499d5ebd 100644 --- a/code/modules/fishing/sources/source_types.dm +++ b/code/modules/fishing/sources/source_types.dm @@ -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" diff --git a/icons/obj/aquarium.dmi b/icons/obj/aquarium.dmi index fcb2c96bf58..6927ca8104e 100644 Binary files a/icons/obj/aquarium.dmi and b/icons/obj/aquarium.dmi differ diff --git a/icons/obj/fishing.dmi b/icons/obj/fishing.dmi index 13b2409ed28..dee9f7a5c29 100644 Binary files a/icons/obj/fishing.dmi and b/icons/obj/fishing.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 1263687ff01..2000efff513 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -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"