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
@@ -119,6 +119,9 @@
/// From base of area/Exited(): (area/left, direction)
#define COMSIG_MOVABLE_EXITED_AREA "movable_exited_area"
///from base of /datum/component/splat/splat: (hit_atom)
#define COMSIG_MOVABLE_SPLAT "movable_splat"
///from base of /atom/movable/point_at: (atom/A, obj/effect/temp_visual/point/point)
#define COMSIG_MOVABLE_POINTED "movable_pointed"
@@ -170,8 +170,8 @@
///Called on user, from base of /datum/strippable_item/try_(un)equip() (atom/target, obj/item/equipping?)
#define COMSIG_TRY_STRIP "try_strip"
#define COMPONENT_CANT_STRIP (1<<0)
///From /datum/component/creamed/Initialize()
#define COMSIG_MOB_CREAMED "mob_creamed"
///From /datum/component/face_decal/splat/Initialize()
#define COMSIG_MOB_HIT_BY_SPLAT "hit_by_splat"
///From /obj/item/gun/proc/check_botched()
#define COMSIG_MOB_CLUMSY_SHOOT_FOOT "mob_clumsy_shoot_foot"
///from /obj/item/hand_item/slapper/attack_atom(): (source=obj/structure/table/slammed_table, mob/living/slammer)
+15 -1
View File
@@ -67,9 +67,11 @@
#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)
///The fish will fade in and out at intervals
#define FISHING_MINIGAME_RULE_CAMO (1 << 7)
///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)
#define FISHING_MINIGAME_ACTIVE_EFFECTS (FISHING_MINIGAME_RULE_ANTIGRAV|FISHING_MINIGAME_RULE_FLIP|FISHING_MINIGAME_RULE_CAMO)
/// The default additive value for fishing hook catch weight modifiers.
#define FISHING_DEFAULT_HOOK_BONUS_ADDITIVE 0
@@ -182,3 +184,15 @@
//Minigame defines
/// The height of the minigame slider. Not in pixels, but minigame units.
#define FISHING_MINIGAME_AREA 1000
///Defines for fish properties from the collect_fish_properties proc
#define FISH_PROPERTIES_FAV_BAIT "fav_bait"
#define FISH_PROPERTIES_BAD_BAIT "bad_bait"
#define FISH_PROPERTIES_TRAITS "fish_traits"
///Define for favorite and disliked baits that aren't just item typepaths.
#define FISH_BAIT_TYPE "Type"
#define FISH_BAIT_FOODTYPE "Foodtype"
#define FISH_BAIT_REAGENT "Reagent"
#define FISH_BAIT_VALUE "Value"
#define FISH_BAIT_AMOUNT "Amount"
+14 -1
View File
@@ -748,6 +748,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NEEDS_TWO_HANDS "needstwohands"
/// Can't be catched when thrown
#define TRAIT_UNCATCHABLE "uncatchable"
/// You won't catch duds while fishing with this rod.
#define TRAIT_ROD_REMOVE_FISHING_DUD "rod_remove_fishing_dud"
/// Stuff that can go inside fish cases
#define TRAIT_FISH_CASE_COMPATIBILE "fish_case_compatibile"
/// If the item can be used as a bit.
@@ -761,7 +763,14 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Baits with this trait will ignore bait preferences and related fish traits.
#define TRAIT_OMNI_BAIT "omni_bait"
/// The bait won't be consumed when used
#define TRAIT_BAIT_UNCONSUMABLE "bait_unconsumabe"
#define TRAIT_BAIT_UNCONSUMABLE "bait_unconsumable"
/// This bait ignores environmental conditions for fishing (like low light for nocturnal fish)
#define TRAIT_BAIT_IGNORE_ENVIRONMENT "bait_ignore_environment"
/**
* This bait won't apply TRAIT_ROD_REMOVE_FISHING_DUD to the rod it's attached on,
* instead, it'll allow the fishing dud to be there unless there's at least one fish that likes the bait
*/
#define TRAIT_BAIT_ALLOW_FISHING_DUD "bait_dont_affect_fishing_dud"
/// Plants that were mutated as a result of passive instability, not a mutation threshold.
#define TRAIT_PLANT_WILDMUTATE "wildmutation"
/// If you hit an APC with exposed internals with this item it will try to shock you
@@ -959,6 +968,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_FISH_RECESSIVE "fish_recessive"
///This fish comes equipped with a stinger (increased damage and potentially venomous if also toxic)
#define TRAIT_FISH_STINGER "fish_stinger"
///This fish is currently on cooldown and cannot splash ink unto people's faces
#define TRAIT_FISH_INK_ON_COOLDOWN "fish_ink_on_cooldown"
///This fish requires two hands to carry even if smaller than FISH_SIZE_TWO_HANDS_REQUIRED, as long as it's bulky-sized.
#define TRAIT_FISH_SHOULD_TWOHANDED "should_twohanded"
/// Trait given to angelic constructs to let them purge cult runes
#define TRAIT_ANGELIC "angelic"
+7
View File
@@ -544,6 +544,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
),
/obj/item = list(
"TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING,
"TRAIT_BAIT_ALLOW_FISHING_DUD" = TRAIT_BAIT_ALLOW_FISHING_DUD,
"TRAIT_BAIT_IGNORE_ENVIRONMENT" = TRAIT_BAIT_IGNORE_ENVIRONMENT,
"TRAIT_BAIT_UNCONSUMABLE" = TRAIT_BAIT_UNCONSUMABLE,
"TRAIT_BAKEABLE" = TRAIT_BAKEABLE,
"TRAIT_BASIC_QUALITY_BAIT" = TRAIT_BASIC_QUALITY_BAIT,
@@ -604,16 +606,21 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_FISH_FED_LUBE" = TRAIT_FISH_FED_LUBE,
"TRAIT_FISH_FLOPPING" = TRAIT_FISH_FLOPPING,
"TRAIT_FISH_FROM_CASE" = TRAIT_FISH_FROM_CASE,
"TRAIT_FISH_INK_ON_COOLDOWN" = TRAIT_FISH_INK_ON_COOLDOWN,
"TRAIT_FISH_NO_HUNGER" = TRAIT_FISH_NO_HUNGER,
"TRAIT_FISH_NO_MATING" = TRAIT_FISH_NO_MATING,
"TRAIT_FISH_RECESSIVE" = TRAIT_FISH_RECESSIVE,
"TRAIT_FISH_SELF_REPRODUCE" = TRAIT_FISH_SELF_REPRODUCE,
"TRAIT_FISH_SHOULD_TWOHANDED" = TRAIT_FISH_SHOULD_TWOHANDED,
"TRAIT_FISH_STASIS" = TRAIT_FISH_STASIS,
"TRAIT_FISH_STINGER" = TRAIT_FISH_STINGER,
"TRAIT_FISH_TOXIN_IMMUNE" = TRAIT_FISH_TOXIN_IMMUNE,
"TRAIT_RESIST_EMULSIFY" = TRAIT_RESIST_EMULSIFY,
"TRAIT_YUCKY_FISH" = TRAIT_YUCKY_FISH,
),
/obj/item/fishing_rod = list(
"TRAIT_ROD_REMOVE_FISHING_DUD" = TRAIT_ROD_REMOVE_FISHING_DUD,
),
/obj/item/integrated_circuit = list(
"TRAIT_CIRCUIT_UI_OPEN" = TRAIT_CIRCUIT_UI_OPEN,
"TRAIT_CIRCUIT_UNDUPABLE" = TRAIT_CIRCUIT_UNDUPABLE,
+5
View File
@@ -339,16 +339,21 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_FISH_ELECTROGENESIS" = TRAIT_FISH_ELECTROGENESIS,
"TRAIT_FISH_FED_LUBE" = TRAIT_FISH_FED_LUBE,
"TRAIT_FISH_FROM_CASE" = TRAIT_FISH_FROM_CASE,
"TRAIT_FISH_INK_ON_COOLDOWN" = TRAIT_FISH_INK_ON_COOLDOWN,
"TRAIT_FISH_NO_HUNGER" = TRAIT_FISH_NO_HUNGER,
"TRAIT_FISH_NO_MATING" = TRAIT_FISH_NO_MATING,
"TRAIT_FISH_RECESSIVE" = TRAIT_FISH_RECESSIVE,
"TRAIT_FISH_SELF_REPRODUCE" = TRAIT_FISH_SELF_REPRODUCE,
"TRAIT_FISH_SHOULD_TWOHANDED" = TRAIT_FISH_SHOULD_TWOHANDED,
"TRAIT_FISH_STASIS" = TRAIT_FISH_STASIS,
"TRAIT_FISH_STINGER" = TRAIT_FISH_STINGER,
"TRAIT_FISH_TOXIN_IMMUNE" = TRAIT_FISH_TOXIN_IMMUNE,
"TRAIT_RESIST_EMULSIFY" = TRAIT_RESIST_EMULSIFY,
"TRAIT_YUCKY_FISH" = TRAIT_YUCKY_FISH,
),
/obj/item/fishing_rod = list(
"TRAIT_ROD_REMOVE_FISHING_DUD" = TRAIT_ROD_REMOVE_FISHING_DUD,
),
/obj/item/organ/internal/liver = list(
"TRAIT_BALLMER_SCIENTIST" = TRAIT_BALLMER_SCIENTIST,
"TRAIT_COMEDY_METABOLISM" = TRAIT_COMEDY_METABOLISM,
@@ -1,4 +1,33 @@
/// subsystem for the fishing minigame processing.
PROCESSING_SUBSYSTEM_DEF(fishing)
name = "Fishing"
flags = SS_BACKGROUND|SS_POST_FIRE_TIMING
wait = 0.05 SECONDS // If you raise it to 0.1 SECONDS, you better also modify [datum/fish_movement/move_fish()]
///Cached fish properties so we don't have to initalize fish every time
var/list/fish_properties
///A cache of fish that can be caught by each type of fishing lure
var/list/lure_catchables
/datum/controller/subsystem/processing/fishing/Initialize()
///init the properties
fish_properties = list()
for(var/fish_type in subtypesof(/obj/item/fish))
var/obj/item/fish/fish = new fish_type(null, FALSE)
fish_properties[fish_type] = list()
fish_properties[fish_type][FISH_PROPERTIES_FAV_BAIT] = fish.favorite_bait.Copy()
fish_properties[fish_type][FISH_PROPERTIES_BAD_BAIT] = fish.disliked_bait.Copy()
fish_properties[fish_type][FISH_PROPERTIES_TRAITS] = fish.fish_traits.Copy()
qdel(fish)
///init the list of things lures can catch
lure_catchables = list()
var/list/fish_types = subtypesof(/obj/item/fish)
for(var/lure_type in typesof(/obj/item/fishing_lure))
var/obj/item/fishing_lure/lure = new lure_type
lure_catchables[lure_type] = list()
for(var/obj/item/fish/fish_type as anything in fish_types)
if(lure.is_catchable_fish(fish_type, fish_properties[fish_type]))
lure_catchables[lure_type] += fish_type
qdel(lure)
return SS_INIT_SUCCESS
+2 -2
View File
@@ -27,9 +27,9 @@
return ..() //Run parent at end
///signal called by the pawn hitting something after a throw
/datum/ai_controller/cursed/proc/on_throw_hit(datum/source, atom/hit_atom, datum/thrownthing/throwingdatum)
/datum/ai_controller/cursed/proc/on_throw_hit(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
SIGNAL_HANDLER
if(!iscarbon(hit_atom))
if(caught || !iscarbon(hit_atom))
return
//equipcode has sleeps all over it.
INVOKE_ASYNC(src, PROC_REF(try_equipping_to_target_slot), hit_atom)
+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)
+3 -2
View File
@@ -45,9 +45,10 @@
shatter(source, impacted_turf)
/// Tells the parent to shatter if we are thrown and impact something
/datum/element/can_shatter/proc/on_throw_impact(datum/source, atom/hit_atom)
/datum/element/can_shatter/proc/on_throw_impact(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
SIGNAL_HANDLER
if(caught)
return
shatter(source, hit_atom)
/// Handles the actual shattering part, throwing shards of whatever is defined on the component everywhere
+2 -2
View File
@@ -27,10 +27,10 @@
/datum/element/firestacker/proc/stack_on(datum/owner, mob/living/target)
target.adjust_fire_stacks(amount)
/datum/element/firestacker/proc/impact(datum/source, atom/hit_atom, datum/thrownthing/throwingdatum)
/datum/element/firestacker/proc/impact(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
SIGNAL_HANDLER
if(isliving(hit_atom))
if(!caught && isliving(hit_atom))
stack_on(source, hit_atom)
/datum/element/firestacker/proc/item_attack(datum/source, atom/movable/target, mob/living/user)
+11 -6
View File
@@ -22,11 +22,14 @@
RegisterSignal(target, COMSIG_ITEM_ATTACK_SELF, PROC_REF(open_trash))
if(flags & FOOD_TRASH_POPABLE)
RegisterSignal(target, COMSIG_FOOD_CROSSED, PROC_REF(food_crossed))
RegisterSignal(target, COMSIG_ITEM_ON_GRIND, PROC_REF(generate_trash))
RegisterSignal(target, COMSIG_ITEM_ON_JUICE, PROC_REF(generate_trash))
RegisterSignal(target, COMSIG_ITEM_USED_AS_INGREDIENT, PROC_REF(generate_trash))
RegisterSignal(target, COMSIG_ITEM_ON_COMPOSTED, PROC_REF(generate_trash))
RegisterSignal(target, COMSIG_ITEM_SOLD_TO_CUSTOMER, PROC_REF(generate_trash))
RegisterSignals(target, list(
COMSIG_ITEM_ON_GRIND,
COMSIG_ITEM_ON_JUICE,
COMSIG_ITEM_USED_AS_INGREDIENT,
COMSIG_ITEM_ON_COMPOSTED,
COMSIG_ITEM_SOLD_TO_CUSTOMER,
COMSIG_MOVABLE_SPLAT,
), PROC_REF(generate_trash))
/datum/element/food_trash/Detach(datum/target)
. = ..()
@@ -38,7 +41,9 @@
COMSIG_ITEM_ON_JUICE,
COMSIG_ITEM_USED_AS_INGREDIENT,
COMSIG_ITEM_ON_COMPOSTED,
COMSIG_ITEM_SOLD_TO_CUSTOMER,))
COMSIG_ITEM_SOLD_TO_CUSTOMER,
COMSIG_MOVABLE_SPLAT,
))
/datum/element/food_trash/proc/generate_trash(datum/source, mob/living/eater, mob/living/feeder)
SIGNAL_HANDLER
+8 -3
View File
@@ -28,7 +28,7 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
organ.organ_flags |= ORGAN_FROZEN
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
RegisterSignal(target, COMSIG_MOVABLE_THROW_LANDED, PROC_REF(shatter_on_throw))
RegisterSignal(target, COMSIG_MOVABLE_THROW_LANDED, PROC_REF(shatter_on_landed))
RegisterSignal(target, COMSIG_MOVABLE_IMPACT, PROC_REF(shatter_on_throw))
RegisterSignal(target, COMSIG_OBJ_UNFREEZE, PROC_REF(on_unfreeze))
@@ -54,8 +54,13 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
SIGNAL_HANDLER
Detach(source)
///signal handler for COMSIG_MOVABLE_POST_THROW that shatters our target after impacting after a throw
/datum/element/frozen/proc/shatter_on_throw(datum/target, datum/thrownthing/throwingdatum)
/datum/element/frozen/proc/shatter_on_throw(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
SIGNAL_HANDLER
if(!caught)
shatter_on_landed(source, throwing_datum)
///signal handler that shatters our target after impacting after a throw.
/datum/element/frozen/proc/shatter_on_landed(datum/target, datum/thrownthing/throwingdatum)
SIGNAL_HANDLER
var/obj/obj_target = target
if(ismob(throwingdatum.thrower))
+2 -22
View File
@@ -41,15 +41,7 @@
var/datum/fish_source/fish_source = GLOB.preset_fish_sources[configuration]
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...")
@@ -60,19 +52,7 @@
return
var/datum/fish_source/fish_source = GLOB.preset_fish_sources[configuration]
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, source, examine_text)
/datum/element/lazy_fishing_spot/proc/explosive_fishing(atom/location, severity)
SIGNAL_HANDLER
+22
View File
@@ -162,6 +162,28 @@
"[protagonist_name] [mood_verb] as they lick off some of the pie",
)
/// Witnessed someone get splashed with squid ink.
/datum/memory/witnessed_inking
story_value = STORY_VALUE_OKAY
memory_flags = MEMORY_CHECK_BLINDNESS
// Protagonist - The mob that got pied
/datum/memory/witnessed_inking/get_names()
return list("The inking of [protagonist_name].")
/datum/memory/witnessed_inking/get_starts()
return list(
"[protagonist_name]'s face being covered in squid ink",
"[protagonist_name] getting squid-inked",
)
/datum/memory/witnessed_inking/get_moods()
return list(
"[protagonist_name] [mood_verb] as ink drips off their face",
"[protagonist_name] [mood_verb] because of their now expanded laundry task.",
"[protagonist_name] [mood_verb] as they wipe the ink off their face.",
)
/// Got slipped by something.
/datum/memory/was_slipped
story_value = STORY_VALUE_MEH
@@ -33,6 +33,11 @@
mood_change = -2
timeout = 3 MINUTES
/datum/mood_event/inked
description = "I've been splashed with squid ink. Tastes like salt."
mood_change = -3
timeout = 3 MINUTES
/datum/mood_event/slipped
description = "I slipped. I should be more careful next time..."
mood_change = -2
+4
View File
@@ -115,11 +115,15 @@
* Im not sure why this the case, maybe to prevent lots of hitby's if the thrown object is
* deleted shortly after hitting something (during explosions or other massive events that
* throw lots of items around - singularity being a notable example)
*
* Worth of note: If hitby returns TRUE, it means the object has been blocked or catched by src.
* So far, this is only possible for living mobs and carbons, who can hold shields and catch thrown items.
*/
/atom/proc/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
SEND_SIGNAL(src, COMSIG_ATOM_HITBY, hitting_atom, skipcatch, hitpush, blocked, throwingdatum)
if(density && !has_gravity(hitting_atom)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...).
addtimer(CALLBACK(src, PROC_REF(hitby_react), hitting_atom), 0.2 SECONDS)
return FALSE
/**
* We have have actually hit the passed in atom
+15 -9
View File
@@ -1283,16 +1283,22 @@
/atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
set waitfor = FALSE
var/hitpush = TRUE
var/impact_signal = SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_IMPACT, hit_atom, throwingdatum)
if(impact_signal & COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH)
hitpush = FALSE // hacky, tie this to something else or a proper workaround later
if(impact_signal && (impact_signal & COMPONENT_MOVABLE_IMPACT_NEVERMIND))
var/impact_flags = pre_impact(hit_atom, throwingdatum)
if(impact_flags & COMPONENT_MOVABLE_IMPACT_NEVERMIND)
return // in case a signal interceptor broke or deleted the thing before we could process our hit
if(SEND_SIGNAL(hit_atom, COMSIG_ATOM_PREHITBY, src, throwingdatum) & COMSIG_HIT_PREVENTED)
return
SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
return hit_atom.hitby(src, throwingdatum=throwingdatum, hitpush=hitpush)
if(impact_flags & COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH)
hitpush = FALSE
var/caught = hit_atom.hitby(src, throwingdatum=throwingdatum, hitpush=hitpush)
SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum, caught)
return caught
///Called before we attempt to call hitby and send the COMSIG_MOVABLE_IMPACT signal
/atom/movable/proc/pre_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
var/impact_flags = SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_IMPACT, hit_atom, throwingdatum)
var/target_flags = SEND_SIGNAL(hit_atom, COMSIG_ATOM_PREHITBY, src, throwingdatum)
if(target_flags & COMSIG_HIT_PREVENTED)
impact_flags |= COMPONENT_MOVABLE_IMPACT_NEVERMIND
return impact_flags
/atom/movable/hitby(atom/movable/hitting_atom, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum)
if(HAS_TRAIT(src, TRAIT_NO_THROW_HITPUSH))
@@ -58,3 +58,14 @@
name = "flour"
desc = "It's still good. Four second rule!"
icon_state = "flour"
/obj/effect/decal/cleanable/food/squid_ink
name = "ink smear"
desc = "a smear from some inky substance..."
icon = 'icons/mob/silicon/robots.dmi'
icon_state = "floor1"
color = COLOR_DARK
/obj/effect/decal/cleanable/food/squid_ink/Initialize(mapload, list/datum/disease/diseases)
icon_state = "floor[rand(1, 7)]"
return ..()
+55 -27
View File
@@ -831,36 +831,30 @@
. = ..()
do_drop_animation(master_storage.parent)
/obj/item/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(QDELETED(hit_atom))
return
if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_IMPACT, hit_atom, throwingdatum) & COMPONENT_MOVABLE_IMPACT_NEVERMIND)
return
if(SEND_SIGNAL(hit_atom, COMSIG_ATOM_PREHITBY, src, throwingdatum) & COMSIG_HIT_PREVENTED)
return
SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
if(get_temperature() && isliving(hit_atom))
var/mob/living/L = hit_atom
L.ignite_mob()
var/itempush = 1
/obj/item/pre_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
var/impact_flags = ..()
if(w_class < WEIGHT_CLASS_BULKY)
itempush = 0 //too light to push anything
if(isliving(hit_atom)) //Living mobs handle hit sounds differently.
var/volume = get_volume_by_throwforce_and_or_w_class()
if (throwforce > 0 || HAS_TRAIT(src, TRAIT_CUSTOM_TAP_SOUND))
if (mob_throw_hit_sound)
playsound(hit_atom, mob_throw_hit_sound, volume, TRUE, -1)
else if(hitsound)
playsound(hit_atom, hitsound, volume, TRUE, -1)
else
playsound(hit_atom, 'sound/weapons/genhit.ogg',volume, TRUE, -1)
else
playsound(hit_atom, 'sound/weapons/throwtap.ogg', 1, volume, -1)
impact_flags |= COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH
if(!(impact_flags & COMPONENT_MOVABLE_IMPACT_NEVERMIND) && get_temperature() && isliving(hit_atom))
var/mob/living/victim = hit_atom
victim.ignite_mob()
return impact_flags
else
/obj/item/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
if(!isliving(hit_atom)) //Living mobs handle hit sounds differently.
playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE)
return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum)
return
var/volume = get_volume_by_throwforce_and_or_w_class()
if (throwforce > 0 || HAS_TRAIT(src, TRAIT_CUSTOM_TAP_SOUND))
if (mob_throw_hit_sound)
playsound(hit_atom, mob_throw_hit_sound, volume, TRUE, -1)
else if(hitsound)
playsound(hit_atom, hitsound, volume, TRUE, -1)
else
playsound(hit_atom, 'sound/weapons/genhit.ogg',volume, TRUE, -1)
else
playsound(hit_atom, 'sound/weapons/throwtap.ogg', 1, volume, -1)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
if(HAS_TRAIT(src, TRAIT_NODROP))
@@ -1765,3 +1759,37 @@
RETURN_TYPE(/obj/item)
return src
/// Checks if the bait is liked by the fish type or not. Returns a multiplier that affects the chance of catching it.
/obj/item/proc/check_bait(obj/item/fish/fish_type)
if(HAS_TRAIT(src, TRAIT_OMNI_BAIT))
return 1
var/catch_multiplier = 1
var/list/properties = SSfishing.fish_properties[fish_type]
//Bait matching likes doubles the chance
var/list/fav_bait = properties[FISH_PROPERTIES_FAV_BAIT]
for(var/bait_identifer in fav_bait)
if(is_matching_bait(src, bait_identifer))
catch_multiplier *= 2
//Bait matching dislikes
var/list/disliked_bait = properties[FISH_PROPERTIES_BAD_BAIT]
for(var/bait_identifer in disliked_bait)
if(is_matching_bait(src, bait_identifer))
catch_multiplier *= 0.5
return catch_multiplier
/// Helper proc that checks if a bait matches identifier from fav/disliked bait list
/proc/is_matching_bait(obj/item/bait, identifier)
if(ispath(identifier)) //Just a path
return istype(bait, identifier)
if(!islist(identifier))
return HAS_TRAIT(bait, identifier)
var/list/special_identifier = identifier
switch(special_identifier[FISH_BAIT_TYPE])
if(FISH_BAIT_FOODTYPE)
var/obj/item/food/food_bait = bait
return istype(food_bait) && food_bait.foodtypes & special_identifier[FISH_BAIT_VALUE]
if(FISH_BAIT_REAGENT)
return bait.reagents?.has_reagent(special_identifier[FISH_BAIT_VALUE], special_identifier[FISH_BAIT_AMOUNT], check_subtypes = TRUE)
else
CRASH("Unknown bait identifier in fish favourite/disliked list")
+27
View File
@@ -805,3 +805,30 @@
foodtypes = VEGETABLES | GRAIN
w_class = WEIGHT_CLASS_TINY
crafting_complexity = FOOD_COMPLEXITY_4
///Extracted from squids, or any fish with the ink fish trait.
/obj/item/food/ink_sac
name = "ink sac"
desc = "the ink sac from some sort of fish or mollusk. It could be canned with a processor."
icon_state = "ink_sac"
food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/consumable/salt = 5)
tastes = list("seafood" = 3)
foodtypes = SEAFOOD|RAW
/obj/item/food/ink_sac/Initialize(mapload)
. = ..()
AddComponent(/datum/component/splat, \
memory_type = /datum/memory/witnessed_inking, \
smudge_type = /obj/effect/decal/cleanable/food/squid_ink, \
moodlet_type = /datum/mood_event/inked, \
splat_color = COLOR_NEARLY_ALL_BLACK, \
hit_callback = CALLBACK(src, PROC_REF(blind_em)), \
)
/obj/item/food/ink_sac/proc/blind_em(mob/living/victim, can_splat_on)
if(can_splat_on)
victim.adjust_temp_blindness_up_to(7 SECONDS, 10 SECONDS)
victim.adjust_confusion_up_to(3.5 SECONDS, 6 SECONDS)
victim.Paralyze(2 SECONDS) //splat!
victim.visible_message(span_warning("[victim] is inked by [src]!"), span_userdanger("You've been inked by [src]!"))
playsound(victim, SFX_DESECRATION, 50, TRUE)
+19
View File
@@ -156,6 +156,25 @@
tastes = list("seafood" = 7, "tin" = 1)
foodtypes = SEAFOOD
/obj/item/food/canned/squid_ink/open_can(mob/user)
. = ..()
AddComponent(/datum/component/splat, \
memory_type = /datum/memory/witnessed_inking, \
smudge_type = /obj/effect/decal/cleanable/food/squid_ink, \
moodlet_type = /datum/mood_event/inked, \
splat_color = COLOR_NEARLY_ALL_BLACK, \
hit_callback = CALLBACK(src, PROC_REF(blind_em)), \
)
ADD_TRAIT(src, TRAIT_UNCATCHABLE, INNATE_TRAIT) //good luck catching the liquid
/obj/item/food/canned/squid_ink/proc/blind_em(mob/living/victim, can_splat_on)
if(can_splat_on)
victim.adjust_temp_blindness_up_to(7 SECONDS, 10 SECONDS)
victim.adjust_confusion_up_to(3.5 SECONDS, 6 SECONDS)
victim.Paralyze(2 SECONDS) //splat!
victim.visible_message(span_warning("[victim] is inked by [src]!"), span_userdanger("You've been inked by [src]!"))
playsound(victim, SFX_DESECRATION, 50, TRUE)
/obj/item/food/canned/chap
name = "can of CHAP"
desc = "CHAP: Chopped Ham And Pork. The classic American canned meat product that won a world war, then sent millions of servicemen home with heart congestion."
+9 -24
View File
@@ -74,32 +74,17 @@
var/stunning = TRUE
crafting_complexity = FOOD_COMPLEXITY_3
/obj/item/food/pie/cream/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
/obj/item/food/pie/cream/Initialize(mapload)
. = ..()
if(!.) //if we're not being caught
splat(hit_atom)
AddComponent(/datum/component/splat, hit_callback = CALLBACK(src, PROC_REF(stun_and_blur)))
/obj/item/food/pie/cream/proc/splat(atom/movable/hit_atom)
if(isliving(loc)) //someone caught us!
return
var/turf/hit_turf = get_turf(hit_atom)
new/obj/effect/decal/cleanable/food/pie_smudge(hit_turf)
if(reagents?.total_volume)
reagents.expose(hit_atom, TOUCH)
var/is_creamable = TRUE
if(isliving(hit_atom))
var/mob/living/living_target_getting_hit = hit_atom
if(stunning)
living_target_getting_hit.Paralyze(2 SECONDS) //splat!
if(iscarbon(living_target_getting_hit))
is_creamable = !!(living_target_getting_hit.get_bodypart(BODY_ZONE_HEAD))
if(is_creamable)
living_target_getting_hit.adjust_eye_blur(2 SECONDS)
living_target_getting_hit.visible_message(span_warning("[living_target_getting_hit] is creamed by [src]!"), span_userdanger("You've been creamed by [src]!"))
playsound(living_target_getting_hit, SFX_DESECRATION, 50, TRUE)
if(is_creamable && is_type_in_typecache(hit_atom, GLOB.creamable))
hit_atom.AddComponent(/datum/component/face_decal/creampie, "creampie", EXTERNAL_FRONT)
qdel(src)
/obj/item/food/pie/cream/proc/stun_and_blur(mob/living/victim, can_splat_on)
if(stunning)
victim.Paralyze(2 SECONDS) //splat!
if(can_splat_on)
victim.adjust_eye_blur(2 SECONDS)
victim.visible_message(span_warning("[victim] is creamed by [src]!"), span_userdanger("You've been creamed by [src]!"))
playsound(victim, SFX_DESECRATION, 50, TRUE)
/obj/item/food/pie/cream/nostun
stunning = FALSE
+7 -1
View File
@@ -237,6 +237,12 @@
cost = PAYCHECK_CREW
contains = list(/obj/item/storage/box/fishing_lines)
/datum/supply_pack/goody/fishing_lure_set
name = "Fishing Lures Set"
desc = "A set of bite-resistant fishing lures to fish all (most) sort of fish. Beat randomness to a curb today!"
cost = PAYCHECK_CREW * 8
contains = list(/obj/item/storage/box/fishing_lures)
/datum/supply_pack/goody/fishing_hook_rescue
name = "Rescue Fishing Hook Single-Pack"
desc = "For when your fellow miner has inevitably fallen into a chasm, and it's up to you to save them."
@@ -252,7 +258,7 @@
/datum/supply_pack/goody/fish_feed
name = "Can of Fish Food Single-Pack"
desc = "For keeping your little friends fed and alive."
cost = PAYCHECK_CREW * 1
cost = PAYCHECK_CREW
contains = list(/obj/item/fish_feed)
/datum/supply_pack/goody/naturalbait
+1 -1
View File
@@ -51,7 +51,7 @@ ADMIN_VERB(fishing_calculator, R_DEBUG, "Fishing Calculator", "A calculator... f
temporary_rod.set_slot(new line_type(temporary_rod), ROD_SLOT_LINE)
var/result_table = list()
var/modified_table = spot.get_modified_fish_table(temporary_rod,user)
var/modified_table = spot.get_modified_fish_table(temporary_rod, user, null)
for(var/result_type in spot.fish_table) // through this not modified to display 0 chance ones too
var/list/info = list()
info["result"] = result_type
+234 -16
View File
@@ -61,21 +61,239 @@
bait_type = /obj/item/food/bait/doughball/synthetic/super
uses_left = 12
/obj/item/fishing_lure
name = "artificial minnow"
desc = "A fishing lure meant to attract smaller omnivore fish."
icon = 'icons/obj/fishing.dmi'
icon_state = "minnow"
w_class = WEIGHT_CLASS_SMALL
/**
* A list with two keys delimiting the spinning interval in which a mouse click has to be pressed while fishing.
* This is passed down to the fishing rod, and then to the lure during the minigame.
*/
var/spin_frequency = list(2 SECONDS, 3 SECONDS)
/// Helper proc that checks if a bait matches identifier from fav/disliked bait list
/proc/is_matching_bait(obj/item/bait, identifier)
if(ispath(identifier)) //Just a path
return istype(bait, identifier)
if(islist(identifier))
var/list/special_identifier = identifier
switch(special_identifier["Type"])
if("Foodtype")
var/obj/item/food/food_bait = bait
return istype(food_bait) && food_bait.foodtypes & special_identifier["Value"]
if("Reagent")
return bait.reagents?.has_reagent(special_identifier["Value"], special_identifier["Amount"], check_subtypes = TRUE)
else
CRASH("Unknown bait identifier in fish favourite/disliked list")
else
return HAS_TRAIT(bait, identifier)
/obj/item/fishing_lure/Initialize(mapload)
. = ..()
add_traits(list(TRAIT_FISHING_BAIT, TRAIT_BAIT_ALLOW_FISHING_DUD, TRAIT_OMNI_BAIT, TRAIT_BAIT_UNCONSUMABLE), INNATE_TRAIT)
RegisterSignal(src, COMSIG_FISHING_EQUIPMENT_SLOTTED, PROC_REF(lure_equipped))
/obj/item/fishing_lure/proc/lure_equipped(datum/source, obj/item/fishing_rod/rod)
SIGNAL_HANDLER
rod.spin_frequency = spin_frequency
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_removed))
/obj/item/fishing_lure/proc/on_removed(atom/movable/source, obj/item/fishing_rod/rod, dir, forced)
SIGNAL_HANDLER
UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
rod.spin_frequency = null
///Called for every fish subtype by the fishing subsystem when initializing, to populate the list of fish that can be catched with this lure.
/obj/item/fishing_lure/proc/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/avg_size = initial(fish_type.average_size)
var/intermediate_size = FISH_SIZE_SMALL_MAX + (FISH_SIZE_NORMAL_MAX - FISH_SIZE_SMALL_MAX)
if(!ISINRANGE(avg_size, FISH_SIZE_TINY_MAX * 0.5, intermediate_size))
return FALSE
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
return FALSE
return TRUE
/obj/item/fishing_lure/examine(mob/user)
. = ..()
. += span_info("It has to be spun with a frequency of [spin_frequency[1] * 0.1] to [spin_frequency[2] * 0.1] seconds while fishing.")
if(HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT))
. += span_tinynotice("Thanks to your experience, you can examine it again to get a list of fish you can catch with it.")
/obj/item/fishing_lure/examine_more(mob/user)
. = ..()
if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT))
return
var/list/known_fishes = list()
for(var/obj/item/fish/fish_type as anything in SSfishing.lure_catchables[type])
if(initial(fish_type.show_in_catalog))
known_fishes += initial(fish_type.name)
if(!length(known_fishes))
return
. += span_info("You can catch the following fish with this lure: [english_list(known_fishes)].")
///Check if the fish is in the list of catchable fish for this fishing lure. Return value is a multiplier.
/obj/item/fishing_lure/check_bait(obj/item/fish/fish_type)
var/multiplier = 0
if(is_type_in_list(/obj/item/fishing_lure, SSfishing.fish_properties[fish_type][FISH_PROPERTIES_FAV_BAIT]))
multiplier += 2
if(fish_type in SSfishing.lure_catchables[type])
multiplier += 10
return multiplier
/obj/item/fishing_lure/plug
name = "big plug lure"
desc = "A fishing lure used to catch larger omnivore fish."
icon_state = "plug"
/obj/item/fishing_lure/plug/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/avg_size = initial(fish_type.average_size)
if(avg_size <= FISH_SIZE_SMALL_MAX)
return FALSE
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
return FALSE
return TRUE
/obj/item/fishing_lure/dropping
name = "plastic dropping"
desc = "A fishing lure to catch all sort of slimy, ratty, disgusting and/or junk-loving fish."
icon_state = "dropping"
spin_frequency = list(1.5 SECONDS, 2.5 SECONDS)
/obj/item/fishing_lure/dropping/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/sources = list(/datum/fish_source/toilet, /datum/fish_source/moisture_trap)
for(var/datum/fish_source/source as anything in sources)
var/datum/fish_source/instance = GLOB.preset_fish_sources[/datum/fish_source/toilet]
if(fish_type in instance.fish_table)
return TRUE
var/list/fav_baits = fish_properties[FISH_PROPERTIES_FAV_BAIT]
for(var/list/identifier in fav_baits)
if(identifier[FISH_BAIT_TYPE] == FISH_BAIT_FOODTYPE && (identifier[FISH_BAIT_VALUE] & (JUNKFOOD|GROSS|TOXIC)))
return TRUE
if(initial(fish_type.beauty) <= FISH_BEAUTY_DISGUSTING)
return TRUE
return FALSE
/obj/item/fishing_lure/spoon
name = "\improper Indy spoon lure"
desc = "A lustrous piece of metal mimicking the scales of a fish. Good for catching small to medium freshwater omnivore fish."
icon_state = "spoon"
spin_frequency = list(1.25 SECONDS, 2.1 SECONDS)
/obj/item/fishing_lure/spoon/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/avg_size = initial(fish_type.average_size)
if(!ISINRANGE(avg_size, FISH_SIZE_TINY_MAX + 1, FISH_SIZE_NORMAL_MAX))
return FALSE
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
return FALSE
var/fluid_type = initial(fish_type.required_fluid_type)
if(fluid_type == AQUARIUM_FLUID_FRESHWATER || fluid_type == AQUARIUM_FLUID_ANADROMOUS || fluid_type == AQUARIUM_FLUID_ANY_WATER)
return TRUE
if((/datum/fish_trait/amphibious in fish_traits) && fluid_type == AQUARIUM_FLUID_AIR)
return TRUE
return FALSE
/obj/item/fishing_lure/artificial_fly
name = "\improper Silkbuzz artificial fly"
desc = "A fishing lure resembling a large wooly fly. Good for catching all sort of picky fish."
icon_state = "artificial_fly"
spin_frequency = list(1.1 SECONDS, 1.9 SECONDS)
/obj/item/fishing_lure/artificial_fly/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(/datum/fish_trait/picky_eater in fish_traits)
return TRUE
return FALSE
/obj/item/fishing_lure/led
name = "\improper LED fishing lure"
desc = "A heavy, waterproof and fish-looking LED stick, used to catch abyssal and demersal fish alike."
icon_state = "led"
spin_frequency = list(3 SECONDS, 3.75 SECONDS)
/obj/item/fishing_lure/led/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_BAIT_IGNORE_ENVIRONMENT, INNATE_TRAIT)
update_appearance(UPDATE_OVERLAYS)
/obj/item/fishing_lure/led/update_overlays()
. = ..()
. += emissive_appearance(icon, "led_emissive", src)
/obj/item/fishing_lure/led/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(length(list(/datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
return TRUE
return FALSE
/obj/item/fishing_lure/lucky_coin
name = "\improper Maneki-Coin lure"
desc = "A faux-gold lure used to attract shiny-loving fish."
icon_state = "lucky_coin"
spin_frequency = list(1.5 SECONDS, 2.5 SECONDS)
/obj/item/fishing_lure/lucky_coin/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(/datum/fish_trait/shiny_lover in fish_traits)
return TRUE
return FALSE
/obj/item/fishing_lure/algae
name = "plastic algae lure"
desc = "A soft clump of fake algae used to attract herbivore water critters."
icon_state = "algae"
spin_frequency = list(3 SECONDS, 5 SECONDS)
/obj/item/fishing_lure/algae/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(/datum/fish_trait/vegan in fish_traits)
return TRUE
return FALSE
/obj/item/fishing_lure/grub
name = "\improper Twister Worm lure"
desc = "A soft plastic lure with the body of a grub and a twisting tail. Good for panfish and other small omnivore fish."
icon_state = "grub"
spin_frequency = list(1 SECONDS, 2.5 SECONDS)
/obj/item/fishing_lure/grub/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
if(initial(fish_type.average_size) >= FISH_SIZE_SMALL_MAX)
return FALSE
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater) & fish_traits))
return FALSE
return TRUE
/obj/item/fishing_lure/buzzbait
name = "\improper Electric-Buzz lure"
desc = "A metallic, colored clanked attached to a series of cables that somehow attract shock-worthy fish."
icon_state = "buzzbait"
spin_frequency = list(0.8 SECONDS, 1.6 SECONDS)
/obj/item/fishing_lure/buzzbait/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(/datum/fish_trait/electrogenesis in fish_traits)
return TRUE
return FALSE
/obj/item/fishing_lure/spinnerbait
name = "spinnerbait lure"
desc = "A versatile lure, good for catching all sort of predatory freshwater fish."
icon_state = "spinnerbait"
spin_frequency = list(2 SECONDS, 4 SECONDS)
/obj/item/fishing_lure/spinnerbait/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(!(/datum/fish_trait/predator in fish_traits))
return FALSE
var/init_fluid_type = initial(fish_type.required_fluid_type)
if(init_fluid_type == AQUARIUM_FLUID_FRESHWATER || init_fluid_type == AQUARIUM_FLUID_ANADROMOUS || init_fluid_type == AQUARIUM_FLUID_ANY_WATER)
return TRUE
if((/datum/fish_trait/amphibious in fish_traits) && init_fluid_type == AQUARIUM_FLUID_AIR) //fluid type is changed to freshwater on init
return TRUE
return FALSE
/obj/item/fishing_lure/daisy_chain
name = "daisy chain lure"
desc = "A lure resembling a small school of fish, good for catching several saltwater predators."
icon_state = "daisy_chain"
spin_frequency = list(2 SECONDS, 4 SECONDS)
/obj/item/fishing_lure/daisy_chain/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(!(/datum/fish_trait/predator in fish_traits))
return FALSE
var/init_fluid_type = initial(fish_type.required_fluid_type)
if(init_fluid_type == AQUARIUM_FLUID_SALTWATER || init_fluid_type == AQUARIUM_FLUID_ANADROMOUS || init_fluid_type == AQUARIUM_FLUID_ANY_WATER)
return TRUE
return FALSE
+3 -1
View File
@@ -208,6 +208,8 @@
if(HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISH))
. += span_notice("It's [size] cm long.")
. += span_notice("It weighs [weight] g.")
if(HAS_TRAIT(src, TRAIT_FISHING_BAIT))
. += span_smallnoticeital("It can be used as a fishing bait.")
///Randomizes weight and size.
/obj/item/fish/proc/randomize_size_and_weight(base_size = average_size, base_weight = average_weight, deviation = weight_size_deviation)
@@ -255,7 +257,7 @@
inhand_icon_state = "fish_huge"
update_weight_class(WEIGHT_CLASS_GIGANTIC)
if(size > FISH_SIZE_TWO_HANDS_REQUIRED)
if(size > FISH_SIZE_TWO_HANDS_REQUIRED || (HAS_TRAIT(src, TRAIT_FISH_SHOULD_TWOHANDED) && w_class >= WEIGHT_CLASS_BULKY))
inhand_icon_state = "[inhand_icon_state]_wielded"
AddComponent(/datum/component/two_handed, require_twohands = TRUE)
+100 -27
View File
@@ -46,7 +46,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
return list(ADDITIVE_FISHING_MOD = 0, MULTIPLICATIVE_FISHING_MOD = 1)
/// Catch weight table modifier from this mod, needs to return a list with two values
/datum/fish_trait/proc/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman)
/datum/fish_trait/proc/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
SHOULD_CALL_PARENT(TRUE)
return list(ADDITIVE_FISHING_MOD = 0, MULTIPLICATIVE_FISHING_MOD = 1)
@@ -113,8 +113,8 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
var/live_amount = max(round((source.weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR) * live_mult, 0.1), live_mult)
var/dead_amount = max(round((source.weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR) * dead_mult, 0.1), dead_mult)
var/is_dead = source.status == FISH_DEAD
source.RemoveElement(/datum/element/venomous, venom_path, is_dead ? live_amount : dead_amount)
source.AddElement(/datum/element/venomous, venom_path, is_dead ? dead_amount : live_amount)
source.RemoveElement(/datum/element/venomous, venom_path, is_dead ? live_amount : dead_amount, thrown_effect = TRUE)
source.AddElement(/datum/element/venomous, venom_path, is_dead ? dead_amount : live_amount, thrown_effect = TRUE)
/datum/fish_trait/wary
name = "Wary"
@@ -138,27 +138,30 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/datum/fish_trait/picky_eater
name = "Picky Eater"
catalog_description = "This fish is very picky and will ignore low quality bait."
catalog_description = "This fish is very picky and will ignore low quality bait (unless it's amongst its favorites)."
/datum/fish_trait/picky_eater/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman)
/datum/fish_trait/picky_eater/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
. = ..()
if(!rod.bait)
.[MULTIPLICATIVE_FISHING_MOD] = 0
return
if(HAS_TRAIT(rod.bait, TRAIT_OMNI_BAIT))
return
if(is_matching_bait(rod.bait, SSfishing.fish_properties[fish_type][FISH_PROPERTIES_FAV_BAIT])) //we like this bait anyway
return
if(!HAS_TRAIT(rod.bait, TRAIT_GOOD_QUALITY_BAIT) && !HAS_TRAIT(rod.bait, TRAIT_GREAT_QUALITY_BAIT))
.[MULTIPLICATIVE_FISHING_MOD] = 0
/datum/fish_trait/nocturnal
name = "Nocturnal"
catalog_description = "This fish avoids bright lights, fishing and storing in darkness recommended."
/datum/fish_trait/nocturnal/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman)
/datum/fish_trait/nocturnal/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
. = ..()
var/turf/turf = get_turf(fisherman)
var/light_amount = turf.get_lumcount()
if(rod.bait && HAS_TRAIT(rod.bait, TRAIT_BAIT_IGNORE_ENVIRONMENT))
return
var/turf/turf = get_turf(location)
var/light_amount = turf?.get_lumcount()
if(light_amount > SHADOW_SPECIES_LIGHT_THRESHOLD)
.[MULTIPLICATIVE_FISHING_MOD] = 0
@@ -195,7 +198,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
mob.apply_status_effect(/datum/status_effect/shadow_regeneration)
/datum/fish_trait/heavy
name = "Heavy"
name = "Demersal"
catalog_description = "This fish tends to stay near the waterbed."
/datum/fish_trait/heavy/apply_to_mob(mob/living/basic/mob)
@@ -215,7 +218,28 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "This fish can only be baited with meat."
incompatible_traits = list(/datum/fish_trait/vegan)
/datum/fish_trait/carnivore/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman)
/datum/fish_trait/carnivore/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
. = ..()
if(!rod.bait)
.[MULTIPLICATIVE_FISHING_MOD] = 0
return
if(HAS_TRAIT(rod.bait, TRAIT_OMNI_BAIT))
return
if(isfish(rod.bait))
return
if(!istype(rod.bait, /obj/item/food))
.[MULTIPLICATIVE_FISHING_MOD] = 0
return
var/obj/item/food/food_bait = rod.bait
if(!(food_bait.foodtypes & (MEAT|SEAFOOD|BUGS)))
.[MULTIPLICATIVE_FISHING_MOD] = 0
/datum/fish_trait/vegan
name = "Herbivore"
catalog_description = "This fish can only be baited with fresh produce."
incompatible_traits = list(/datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/necrophage)
/datum/fish_trait/vegan/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
. = ..()
if(!rod.bait)
.[MULTIPLICATIVE_FISHING_MOD] = 0
@@ -225,23 +249,10 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
if(!istype(rod.bait, /obj/item/food))
.[MULTIPLICATIVE_FISHING_MOD] = 0
return
if(istype(rod.bait, /obj/item/food/grown))
return
var/obj/item/food/food_bait = rod.bait
if(!(food_bait.foodtypes & MEAT))
.[MULTIPLICATIVE_FISHING_MOD] = 0
/datum/fish_trait/vegan
name = "Herbivore"
catalog_description = "This fish can only be baited with fresh produce."
incompatible_traits = list(/datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/necrophage)
/datum/fish_trait/vegan/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman)
. = ..()
if(!rod.bait)
.[MULTIPLICATIVE_FISHING_MOD] = 0
return
if(HAS_TRAIT(rod.bait, TRAIT_OMNI_BAIT))
return
if(!istype(rod.bait, /obj/item/food/grown))
if(food_bait.foodtypes & (MEAT|SEAFOOD|GORE|BUGS|DAIRY) || !(food_bait.foodtypes & (VEGETABLES|FRUIT)))
.[MULTIPLICATIVE_FISHING_MOD] = 0
/datum/fish_trait/emulsijack
@@ -371,6 +382,11 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "It's a predatory fish. It'll hunt down and eat live fishes of smaller size when hungry."
incompatible_traits = list(/datum/fish_trait/vegan)
/datum/fish_trait/predator/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
. = ..()
if(isfish(rod.bait))
.[MULTIPLICATIVE_FISHING_MOD] *= 2
/datum/fish_trait/predator/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(eat_fishes))
@@ -647,3 +663,60 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
if(!HAS_TRAIT(source, TRAIT_FISH_STINGER))
return
change_venom_on_death(source, /datum/reagent/toxin/venom, 0.7, 0.3)
/datum/fish_trait/ink
name = "Ink Production"
catalog_description = "This fish possess a sac that produces ink."
diff_traits_inheritability = 70
spontaneous_manifest_types = list(/obj/item/fish/squid = 35)
/datum/fish_trait/ink/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_ATOM_PROCESSED, PROC_REF(on_process))
RegisterSignal(fish, COMSIG_ITEM_ATTACK_ZONE, PROC_REF(attacked_someone))
/datum/fish_trait/ink/proc/attacked_someone(obj/item/fish/source, mob/living/target, mob/living/user, zone)
SIGNAL_HANDLER
if(HAS_TRAIT(source, TRAIT_FISH_INK_ON_COOLDOWN) || source.status == FISH_DEAD)
return
if(!iscarbon(target) || target.get_bodypart(BODY_ZONE_HEAD))
target.adjust_temp_blindness_up_to(4 SECONDS, 8 SECONDS)
target.adjust_confusion_up_to(1.5 SECONDS, 4 SECONDS)
target.AddComponent(/datum/component/face_decal/splat, \
color = COLOR_NEARLY_ALL_BLACK, \
memory_type = /datum/memory/witnessed_inking, \
mood_event_type = /datum/mood_event/inked, \
)
target.visible_message(span_warning("[target] is inked by [source]!"), span_userdanger("You've been inked by [source]!"))
playsound(target, SFX_DESECRATION, 50, TRUE)
ADD_TRAIT(source, TRAIT_FISH_INK_ON_COOLDOWN, FISH_TRAIT_DATUM)
addtimer(TRAIT_CALLBACK_REMOVE(source, TRAIT_FISH_INK_ON_COOLDOWN, FISH_TRAIT_DATUM), 9 SECONDS)
/datum/fish_trait/ink/proc/on_process(obj/item/fish/source, mob/living/user, obj/item/process_item, list/results)
SIGNAL_HANDLER
new /obj/item/food/ink_sac(source.drop_location())
/datum/fish_trait/camouflage
name = "Camouflage"
catalog_description = "This fish possess the ability to blend with its surroundings."
spontaneous_manifest_types = list(/obj/item/fish/squid = 35)
/datum/fish_trait/camouflage/minigame_mod(obj/item/fishing_rod/rod, mob/fisherman, datum/fishing_challenge/minigame)
minigame.special_effects |= FISHING_MINIGAME_RULE_CAMO
/datum/fish_trait/camouflage/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(fade_out))
RegisterSignals(fish, list(COMSIG_MOVABLE_MOVED, COMSIG_FISH_STATUS_CHANGED), PROC_REF(reset_alpha))
/datum/fish_trait/camouflage/proc/fade_out(obj/item/fish/source, seconds_per_tick)
SIGNAL_HANDLER
if(source.status == FISH_DEAD || source.last_move + 5 SECONDS >= world.time)
return
source.alpha = max(source.alpha - 10 * seconds_per_tick, 10)
/datum/fish_trait/camouflage/proc/reset_alpha(obj/item/fish/source)
SIGNAL_HANDLER
var/init_alpha = initial(source.alpha)
if(init_alpha != source.alpha)
animate(source.alpha, alpha = init_alpha, time = 1.2 SECONDS, easing = CIRCULAR_EASING|EASE_OUT)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,95 @@
/obj/item/fish/sand_surfer
name = "sand surfer"
desc = "A bronze alien \"fish\" living and swimming underneath faraway sandy places."
icon_state = "sand_surfer"
sprite_height = 6
sprite_width = 6
stable_population = 5
average_size = 65
average_weight = 1100
weight_size_deviation = 0.35
random_case_rarity = FISH_RARITY_RARE
required_fluid_type = AQUARIUM_FLUID_AIR
required_temperature_min = MIN_AQUARIUM_TEMP+25
required_temperature_max = MIN_AQUARIUM_TEMP+60
fish_movement_type = /datum/fish_movement/plunger
fishing_difficulty_modifier = 5
fish_traits = list(/datum/fish_trait/shiny_lover)
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/sand_crab
name = "burrower crab"
desc = "A sand-dwelling crustacean. It looks like a crab and tastes like a crab, but waddles like a fish."
icon_state = "crab"
dedicated_in_aquarium_icon_state = "crab_small"
sprite_height = 6
sprite_width = 10
average_size = 60
average_weight = 1000
weight_size_deviation = 0.1
required_fluid_type = AQUARIUM_FLUID_SALTWATER
required_temperature_min = MIN_AQUARIUM_TEMP+20
required_temperature_max = MIN_AQUARIUM_TEMP+40
fillet_type = /obj/item/food/meat/slab/rawcrab
fish_traits = list(/datum/fish_trait/amphibious, /datum/fish_trait/shiny_lover, /datum/fish_trait/carnivore)
fish_movement_type = /datum/fish_movement/slow
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = SEAFOOD,
),
)
/obj/item/fish/bumpy
name = "bump-fish"
desc = "An misshapen fish-thing all covered in stubby little tendrils"
icon_state = "bumpy"
sprite_height = 4
sprite_width = 5
stable_population = 4
required_fluid_type = AQUARIUM_FLUID_ANY_WATER
required_temperature_min = MIN_AQUARIUM_TEMP+15
required_temperature_max = MIN_AQUARIUM_TEMP+40
beauty = FISH_BEAUTY_BAD
fish_traits = list(/datum/fish_trait/amphibious, /datum/fish_trait/vegan)
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = VEGETABLES,
),
)
/obj/item/fish/starfish
name = "cosmostarfish"
desc = "A peculiar, gravity-defying, echinoderm-looking critter from hyperspace."
icon_state = "starfish"
icon_state_dead = "starfish_dead"
sprite_height = 3
sprite_width = 4
average_size = 30
average_weight = 300
stable_population = 3
required_fluid_type = AQUARIUM_FLUID_AIR
random_case_rarity = FISH_RARITY_NOPE
required_temperature_min = 0
required_temperature_max = INFINITY
safe_air_limits = null
min_pressure = 0
max_pressure = INFINITY
grind_results = list(/datum/reagent/bluespace = 10)
fillet_type = null
fish_traits = list(/datum/fish_trait/antigrav, /datum/fish_trait/mixotroph)
beauty = FISH_BEAUTY_GREAT
/obj/item/fish/starfish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
update_appearance(UPDATE_OVERLAYS)
/obj/item/fish/starfish/update_overlays()
. = ..()
if(status == FISH_ALIVE)
. += emissive_appearance(icon, "starfish_emissive", src)
///It spins, and dimly glows in the dark.
/obj/item/fish/starfish/flop_animation()
DO_FLOATING_ANIM(src)
@@ -0,0 +1,62 @@
/obj/item/fish/sockeye_salmon
name = "sockeye salmon"
desc = "A fairly common and iconic salmon endemic of the Pacific Ocean. At some point imported into outer space, where we're now."
icon_state = "sockeye"
sprite_width = 6
sprite_height = 4
stable_population = 6
required_temperature_min = MIN_AQUARIUM_TEMP+3
required_temperature_max = MIN_AQUARIUM_TEMP+19
required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
fillet_type = /obj/item/food/fishmeat/salmon
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/arctic_char
name = "arctic char"
desc = "A cold-water anadromous fish widespread around the Northern Hemisphere of Earth, yet it has somehow found a way here."
icon_state = "arctic_char"
sprite_width = 7
sprite_height = 4
stable_population = 6
average_size = 60
average_weight = 1200
weight_size_deviation = 0.5 // known for their size dismophism
required_temperature_min = MIN_AQUARIUM_TEMP+3
required_temperature_max = MIN_AQUARIUM_TEMP+19
required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
/obj/item/fish/pike
name = "pike"
desc = "A long-bodied predator with a snout that almost looks like a beak. Definitely not a weapon to swing around."
icon = 'icons/obj/aquarium/wide.dmi'
icon_state = "pike"
inhand_icon_state = "pike"
base_pixel_x = -16
pixel_x = -16
stable_population = 4
sprite_width = 10
sprite_height = 3
average_size = 100
average_weight = 2000
breeding_timeout = 4 MINUTES
health = 150
beauty = FISH_BEAUTY_GOOD
required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
random_case_rarity = FISH_RARITY_RARE
fish_movement_type = /datum/fish_movement/plunger
fishing_difficulty_modifier = 10
required_temperature_min = MIN_AQUARIUM_TEMP+12
required_temperature_max = MIN_AQUARIUM_TEMP+27
fish_traits = list(/datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/aggressive)
compatible_types = list(/obj/item/fish/pike/armored)
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = SEAFOOD|MEAT,
),
/obj/item/fish,
)
/obj/item/fish/pike/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_FISH_SHOULD_TWOHANDED, INNATE_TRAIT)
@@ -0,0 +1,213 @@
/obj/item/fish/goldfish
name = "goldfish"
desc = "Despite common belief, goldfish do not have three-second memories. \
They can actually remember things that happened up to three months ago."
icon_state = "goldfish"
dedicated_in_aquarium_icon_state = "fish_greyscale"
aquarium_vc_color = "#D8540D"
sprite_width = 5
sprite_height = 3
stable_population = 9
average_size = 20
average_weight = 200
weight_size_deviation = 0.35
favorite_bait = list(/obj/item/food/bait/worm)
required_temperature_min = MIN_AQUARIUM_TEMP+18
required_temperature_max = MIN_AQUARIUM_TEMP+26
evolution_types = list(/datum/fish_evolution/three_eyes, /datum/fish_evolution/chainsawfish)
compatible_types = list(/obj/item/fish/goldfish/gill, /obj/item/fish/goldfish/three_eyes, /obj/item/fish/goldfish/three_eyes/gill)
/obj/item/fish/goldfish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
add_traits(list(TRAIT_FISHING_BAIT, TRAIT_GOOD_QUALITY_BAIT), INNATE_TRAIT)
/obj/item/fish/goldfish/gill
name = "McGill"
desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case."
stable_population = 1
random_case_rarity = FISH_RARITY_NOPE
show_in_catalog = FALSE
beauty = FISH_BEAUTY_GOOD
compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/three_eyes)
fish_traits = list(/datum/fish_trait/recessive)
/obj/item/fish/goldfish/three_eyes
name = "three-eyed goldfish"
desc = "A goldfish with an extra half a pair of eyes. You wonder what it's been feeding on lately..."
icon_state = "three_eyes"
stable_population = 4
fish_traits = list(/datum/fish_trait/recessive, /datum/fish_trait/shiny_lover)
compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/gill, /obj/item/fish/goldfish/three_eyes/gill)
beauty = FISH_BEAUTY_GOOD
fishing_difficulty_modifier = 10
random_case_rarity = FISH_RARITY_VERY_RARE
food = /datum/reagent/toxin/mutagen
favorite_bait = list(
list(
"Type" = "Reagent",
"Value" = /datum/reagent/toxin/mutagen,
"Amount" = 3,
),
)
/obj/item/fish/goldfish/three_eyes/gill
name = "McGill"
desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case. It looks kinda different today..."
compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/three_eyes)
beauty = FISH_BEAUTY_GREAT
show_in_catalog = FALSE
stable_population = 1
random_case_rarity = FISH_RARITY_NOPE
/obj/item/fish/angelfish
name = "angelfish"
desc = "Young Angelfish often live in groups, while adults prefer solitary life. They become territorial and aggressive toward other fish when they reach adulthood."
icon_state = "angelfish"
sprite_width = 4
sprite_height = 7
average_size = 30
average_weight = 500
stable_population = 3
fish_traits = list(/datum/fish_trait/aggressive)
required_temperature_min = MIN_AQUARIUM_TEMP+22
required_temperature_max = MIN_AQUARIUM_TEMP+30
/obj/item/fish/guppy
name = "guppy"
desc = "Guppy is also known as rainbow fish because of the brightly colored body and fins."
icon_state = "guppy"
sprite_width = 5
sprite_height = 2
sprite_width = 8
sprite_height = 5
average_size = 30
average_weight = 500
stable_population = 6
required_temperature_min = MIN_AQUARIUM_TEMP+20
required_temperature_max = MIN_AQUARIUM_TEMP+28
/obj/item/fish/plasmatetra
name = "plasma tetra"
desc = "Due to their small size, tetras are prey to many predators in their watery world, including eels, crustaceans, and invertebrates."
icon_state = "plastetra"
sprite_width = 4
sprite_height = 2
average_size = 30
average_weight = 500
stable_population = 3
required_temperature_min = MIN_AQUARIUM_TEMP+20
required_temperature_max = MIN_AQUARIUM_TEMP+28
/obj/item/fish/catfish
name = "catfish"
desc = "A catfish has about 100,000 taste buds, and their bodies are covered with them to help detect chemicals present in the water and also to respond to touch."
icon_state = "catfish"
sprite_width = 8
sprite_height = 4
average_size = 80
average_weight = 1600
weight_size_deviation = 0.35
stable_population = 3
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = JUNKFOOD
)
)
required_temperature_min = MIN_AQUARIUM_TEMP+12
required_temperature_max = MIN_AQUARIUM_TEMP+30
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/zipzap
name = "anxious zipzap"
desc = "A fish overflowing with crippling anxiety and electric potential. Worried about the walls of its tank closing in constantly. Both literally and as a general metaphorical unease about life's direction."
icon_state = "zipzap"
icon_state_dead = "zipzap_dead"
sprite_width = 6
sprite_height = 3
stable_population = 3
average_size = 30
average_weight = 500
random_case_rarity = FISH_RARITY_VERY_RARE
favorite_bait = list(/obj/item/stock_parts/power_store/cell/lead)
required_temperature_min = MIN_AQUARIUM_TEMP+18
required_temperature_max = MIN_AQUARIUM_TEMP+26
fish_traits = list(
/datum/fish_trait/no_mating,
/datum/fish_trait/wary,
/datum/fish_trait/anxiety,
/datum/fish_trait/electrogenesis,
)
//anxiety naturally limits the amount of zipzaps per tank, so they are stronger alone
electrogenesis_power = 20 MEGA JOULES
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/tadpole
name = "tadpole"
desc = "The larval spawn of an amphibian. A very minuscle, round creature with a long tail it uses to swim around."
icon_state = "tadpole"
average_size = 3
average_weight = 10
sprite_width = 3
sprite_height = 1
health = 50
feeding_frequency = 1.5 MINUTES
required_temperature_min = MIN_AQUARIUM_TEMP+15
required_temperature_max = MIN_AQUARIUM_TEMP+20
fillet_type = null
fish_traits = list(/datum/fish_trait/no_mating) //They grow into frogs and that's it.
beauty = FISH_BEAUTY_NULL
random_case_rarity = FISH_RARITY_NOPE //Why would you want generic frog tadpoles you get from ponds inside fish cases?
/// Once dead, tadpoles disappear after a dozen seconds, since you can get infinite tadpoles.
var/del_timerid
/obj/item/fish/tadpole/Initialize(mapload, apply_qualities = TRUE)
. = ..()
AddComponent(/datum/component/fish_growth, /mob/living/basic/frog, 100 / rand(2.5, 3 MINUTES) * 10)
RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(growth_checks))
RegisterSignal(src, COMSIG_FISH_FINISH_GROWING, PROC_REF(on_growth))
/obj/item/fish/tadpole/set_status(new_status, silent = FALSE)
. = ..()
if(status == FISH_DEAD)
del_timerid = QDEL_IN_STOPPABLE(src, 12 SECONDS)
else
deltimer(del_timerid)
/obj/item/fish/tadpole/proc/growth_checks(datum/source, seconds_per_tick)
SIGNAL_HANDLER
var/hunger = CLAMP01((world.time - last_feeding) / feeding_frequency)
if(hunger >= 0.7) //too hungry to grow
return COMPONENT_DONT_GROW
var/obj/structure/aquarium/aquarium = loc
if(!aquarium.allow_breeding) //the aquarium has breeding disabled
return COMPONENT_DONT_GROW
/obj/item/fish/tadpole/proc/on_growth(datum/source, mob/living/basic/frog/result)
SIGNAL_HANDLER
playsound(result, result.attack_sound, 50, TRUE) // reeeeeeeeeeeeeee...
/obj/item/fish/tadpole/get_export_price(price, percent)
return 2 //two credits. Tadpoles aren't really that valueable.
/obj/item/fish/perch
name = "perch"
desc = "An all around popular panfish, game fish and unfortunate prey to other, bigger predators."
icon_state = "perch"
dedicated_in_aquarium_icon_state = "fish_greyscale"
aquarium_vc_color = "#9D8C64"
sprite_width = 5
sprite_height = 3
stable_population = 7
average_size = 25
average_weight = 400
required_temperature_min = MIN_AQUARIUM_TEMP+5
required_temperature_max = MIN_AQUARIUM_TEMP+26
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = BUGS,
),
/obj/item/fish,
/obj/item/fishing_lure, //they love lures in general.
)
@@ -0,0 +1,108 @@
/obj/item/fish/holo
name = "holographic goldfish"
desc = "A holographic representation of a common goldfish, slowly flickering out, removed from its holo-habitat."
icon_state = /obj/item/fish/goldfish::icon_state
show_in_catalog = FALSE
random_case_rarity = FISH_RARITY_NOPE
dedicated_in_aquarium_icon_state = /obj/item/fish/goldfish::dedicated_in_aquarium_icon_state
aquarium_vc_color = /obj/item/fish/goldfish::aquarium_vc_color
sprite_width = /obj/item/fish/goldfish::sprite_width
sprite_height = /obj/item/fish/goldfish::sprite_height
stable_population = 1
average_size = /obj/item/fish/goldfish::average_size
average_weight = /obj/item/fish/goldfish::average_weight
required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
grind_results = null
fillet_type = null
death_text = "%SRC gently disappears."
fish_traits = list(/datum/fish_trait/no_mating) //just to be sure, these shouldn't reproduce
experisci_scannable = FALSE
beauty = /obj/item/fish/goldfish::beauty
/obj/item/fish/holo/Initialize(mapload, apply_qualities = TRUE)
. = ..()
var/area/station/holodeck/holo_area = get_area(src)
if(!istype(holo_area))
addtimer(CALLBACK(src, PROC_REF(set_status), FISH_DEAD), 1 MINUTES)
return
holo_area.linked.add_to_spawned(src)
/obj/item/fish/holo/set_status(new_status, silent = FALSE)
. = ..()
if(status == FISH_DEAD)
animate(src, alpha = 0, 3 SECONDS, easing = SINE_EASING)
QDEL_IN(src, 3 SECONDS)
/obj/item/fish/holo/crab
name = "holographic crab"
desc = "A holographic represantion of a soul-crushingly soulless crab, unlike the cuter ones occasionally roaming around. It stares at you, with empty, beady eyes."
icon_state = "crab"
dedicated_in_aquarium_icon_state = null
aquarium_vc_color = null
average_size = 30
average_weight = 1000
sprite_height = 6
sprite_width = 10
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/holo/puffer
name = "holographic pufferfish"
desc ="A holographic representation of 100% safe-to-eat pufferfish... that is, if holographic fishes were even edible."
icon_state = /obj/item/fish/pufferfish::icon_state
dedicated_in_aquarium_icon_state = /obj/item/fish/pufferfish::dedicated_in_aquarium_icon_state
aquarium_vc_color = /obj/item/fish/pufferfish::aquarium_vc_color
average_size = /obj/item/fish/pufferfish::average_size
average_weight = /obj/item/fish/pufferfish::average_weight
sprite_height = /obj/item/fish/pufferfish::sprite_height
sprite_width = /obj/item/fish/pufferfish::sprite_width
beauty = /obj/item/fish/pufferfish::beauty
/obj/item/fish/holo/angel
name = "holographic angelfish"
desc = "A holographic representation of a angelfish. I got nothing snarky to say about this one."
icon_state = /obj/item/fish/angelfish::icon_state
dedicated_in_aquarium_icon_state = /obj/item/fish/angelfish::dedicated_in_aquarium_icon_state
aquarium_vc_color = /obj/item/fish/angelfish::aquarium_vc_color
average_size = /obj/item/fish/angelfish::average_size
average_weight = /obj/item/fish/angelfish::average_weight
sprite_height = /obj/item/fish/angelfish::sprite_height
sprite_width = /obj/item/fish/angelfish::sprite_width
beauty = /obj/item/fish/angelfish::beauty
/obj/item/fish/holo/clown
name = "holographic clownfish"
icon_state = "holo_clownfish"
desc = "A holographic representation of a clownfish, or at least how they used to look like five centuries ago."
dedicated_in_aquarium_icon_state = null
aquarium_vc_color = /obj/item/fish/clownfish::aquarium_vc_color
average_size = /obj/item/fish/clownfish::average_size
average_weight = /obj/item/fish/clownfish::average_weight
sprite_height = /obj/item/fish/clownfish::sprite_height
sprite_width = /obj/item/fish/clownfish::sprite_width
required_fluid_type = /obj/item/fish/clownfish::required_fluid_type
beauty = /obj/item/fish/clownfish::beauty
/obj/item/fish/holo/checkered
name = "unrendered holographic fish"
desc = "A checkered silhoutte of searing purple and pitch black presents itself before your eyes, like a tear in fabric of reality. It hurts to watch."
icon_state = "checkered" //it's a meta joke, buddy.
dedicated_in_aquarium_icon_state = null
aquarium_vc_color = null
average_size = 30
average_weight = 500
sprite_width = 4
sprite_height = 3
beauty = FISH_BEAUTY_NULL
/obj/item/fish/holo/halffish
name = "holographic half-fish"
desc = "A holographic representation of... a fish reduced to all bones, except for its head. Isn't it supposed to be dead? Ehr, holo-dead?"
icon_state = "half_fish"
dedicated_in_aquarium_icon_state = null
aquarium_vc_color = null
sprite_height = 4
sprite_width = 10
average_size = 50
average_weight = 500
beauty = FISH_BEAUTY_UGLY
+198
View File
@@ -0,0 +1,198 @@
/// Commonly found on the mining fishing spots. Can be grown into lobstrosities
/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"
sprite_height = 9
sprite_width = 8
stable_population = 4
feeding_frequency = 10 MINUTES
random_case_rarity = FISH_RARITY_RARE
fillet_type = /obj/item/food/meat/slab/rawcrab
required_temperature_min = MIN_AQUARIUM_TEMP+9
required_temperature_max = LAVALAND_MAX_TEMPERATURE+50
min_pressure = HAZARD_LOW_PRESSURE
safe_air_limits = list(
/datum/gas/oxygen = list(2, 100),
/datum/gas/nitrogen,
/datum/gas/carbon_dioxide = list(0, 20),
/datum/gas/water_vapor,
/datum/gas/plasma = list(0, 5),
/datum/gas/bz = list(0, 5),
/datum/gas/miasma = list(0, 5),
)
evolution_types = list(/datum/fish_evolution/ice_chrab)
compatible_types = list(/obj/item/fish/chasm_crab/ice)
beauty = FISH_BEAUTY_GOOD
favorite_bait = list(/obj/item/fish/lavaloop)
///This value represents how much the crab needs aren't being met. Higher values translate to a more likely hostile lobstrosity.
var/anger = 0
///The lobstrosity type this matures into
var/lob_type = /mob/living/basic/mining/lobstrosity/juvenile/lava
///at which rate the crab gains maturation
var/growth_rate = 100 / (10 MINUTES) * 10
/obj/item/fish/chasm_crab/Initialize(mapload, apply_qualities = TRUE)
. = ..()
RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(growth_checks))
RegisterSignal(src, COMSIG_FISH_FINISH_GROWING, PROC_REF(on_growth))
///A chasm crab growth speed is determined by its initial weight and size, ergo bigger crabs for faster lobstrosities
/obj/item/fish/chasm_crab/update_size_and_weight(new_size = average_size, new_weight = average_weight)
. = ..()
var/multiplier = 1
switch(size)
if(0 to FISH_SIZE_TINY_MAX)
multiplier -= 0.2
if(FISH_SIZE_SMALL_MAX to FISH_SIZE_NORMAL_MAX)
multiplier += 0.2
if(FISH_SIZE_NORMAL_MAX to FISH_SIZE_BULKY_MAX)
multiplier += 0.5
if(FISH_SIZE_BULKY_MAX to INFINITY)
multiplier += 0.8
if(weight <= 800)
multiplier -= 0.1 * round((1000 - weight) / 200)
else if(weight >= 1500)
multiplier += min(0.1 * round((weight - 1000) / 500), 2)
AddComponent(/datum/component/fish_growth, lob_type, initial(growth_rate) * multiplier)
/obj/item/fish/chasm_crab/proc/growth_checks(datum/source, seconds_per_tick)
SIGNAL_HANDLER
var/hunger = CLAMP01((world.time - last_feeding) / feeding_frequency)
if(health <= initial(health) * 0.6 || hunger >= 0.6) //if too hurt or hungry, don't grow.
anger += growth_rate * 2 * seconds_per_tick
return COMPONENT_DONT_GROW
if(hunger >= 0.4) //I'm hungry and angry
anger += growth_rate * 0.6 * seconds_per_tick
if(!isaquarium(loc))
return
var/obj/structure/aquarium/aquarium = loc
if(!aquarium.allow_breeding) //the aquarium has breeding disabled
return COMPONENT_DONT_GROW
if(!locate(/obj/item/aquarium_prop) in aquarium) //the aquarium deco is quite barren
anger += growth_rate * 0.25 * seconds_per_tick
var/fish_count = length(aquarium.get_fishes())
if(!ISINRANGE(fish_count, 3, AQUARIUM_MAX_BREEDING_POPULATION * 0.5)) //too lonely or overcrowded
anger += growth_rate * 0.3 * seconds_per_tick
if(fish_count > AQUARIUM_MAX_BREEDING_POPULATION * 0.5) //check if there's enough room to maturate.
return COMPONENT_DONT_GROW
/obj/item/fish/chasm_crab/proc/on_growth(datum/source, mob/living/basic/mining/lobstrosity/juvenile/result)
SIGNAL_HANDLER
if(!prob(anger))
result.AddElement(/datum/element/ai_retaliate)
qdel(result.ai_controller)
result.ai_controller = new /datum/ai_controller/basic_controller/lobstrosity/juvenile/calm(result)
else if(anger < 30) //not really that mad, just a bit unstable.
qdel(result.ai_controller)
result.ai_controller = new /datum/ai_controller/basic_controller/lobstrosity/juvenile/capricious(result)
/obj/item/fish/chasm_crab/ice
name = "arctic chrab"
desc = "A subspecies of chasm chrabs that has adapted to the cold climate and lack of abysmal holes of the icemoon."
icon_state = "arctic_chrab"
required_temperature_min = ICEBOX_MIN_TEMPERATURE-20
required_temperature_max = MIN_AQUARIUM_TEMP+15
evolution_types = list(/datum/fish_evolution/chasm_chrab)
compatible_types = list(/obj/item/fish/chasm_crab)
beauty = FISH_BEAUTY_GREAT
lob_type = /mob/living/basic/mining/lobstrosity/juvenile
/obj/item/fish/boned
name = "unmarine bonemass"
desc = "What one could mistake for fish remains, is in reality a species that chose to discard its weak flesh a long time ago. A living fossil, in its most literal sense."
icon_state = "bonemass"
sprite_width = 10
sprite_height = 7
fish_movement_type = /datum/fish_movement/zippy
random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
required_fluid_type = AQUARIUM_FLUID_ANY_WATER
min_pressure = HAZARD_LOW_PRESSURE
health = 150
stable_population = 3
grind_results = list(/datum/reagent/bone_dust = 10)
fillet_type = /obj/item/stack/sheet/bone
num_fillets = 2
fish_traits = list(/datum/fish_trait/revival, /datum/fish_trait/carnivore)
average_size = 70
average_weight = 2000
death_text = "%SRC stops moving." //It's dead... or is it?
evolution_types = list(/datum/fish_evolution/mastodon)
beauty = FISH_BEAUTY_UGLY
/obj/item/fish/lavaloop
name = "lavaloop fish"
desc = "Due to its curvature, it can be used as make-shift boomerang."
icon_state = "lava_loop"
sprite_width = 3
sprite_height = 5
average_size = 30
average_weight = 500
resistance_flags = FIRE_PROOF | LAVA_PROOF
required_fluid_type = AQUARIUM_FLUID_ANY_WATER //if we can survive hot lava and freezing plasrivers, we can survive anything
fish_movement_type = /datum/fish_movement/zippy
min_pressure = HAZARD_LOW_PRESSURE
required_temperature_min = MIN_AQUARIUM_TEMP+30
required_temperature_max = MIN_AQUARIUM_TEMP+35
fish_traits = list(
/datum/fish_trait/carnivore,
/datum/fish_trait/heavy,
)
hitsound = null
throwforce = 5
beauty = FISH_BEAUTY_GOOD
///maximum bonus damage when winded up
var/maximum_bonus = 25
/obj/item/fish/lavaloop/Initialize(mapload, apply_qualities = TRUE)
. = ..()
add_traits(list(TRAIT_FISHING_BAIT, TRAIT_GOOD_QUALITY_BAIT, TRAIT_BYPASS_RANGED_ARMOR), INNATE_TRAIT)
AddComponent(/datum/component/boomerang, throw_range, TRUE)
AddComponent(\
/datum/component/throwbonus_on_windup,\
maximum_bonus = maximum_bonus,\
windup_increment_speed = 2,\
throw_text = "starts cooking in your hands, it may explode soon!",\
pass_maximum_callback = CALLBACK(src, PROC_REF(explode_on_user)),\
apply_bonus_callback = CALLBACK(src, PROC_REF(on_fish_land)),\
sound_on_success = 'sound/weapons/parry.ogg',\
effect_on_success = /obj/effect/temp_visual/guardian/phase,\
)
/obj/item/fish/lavaloop/proc/explode_on_user(mob/living/user)
var/obj/item/bodypart/arm/active_arm = user.get_active_hand()
active_arm?.dismember()
to_chat(user, span_warning("[src] explodes!"))
playsound(src, 'sound/effects/explosion1.ogg', 40, TRUE)
user.flash_act(1, 1)
qdel(src)
/obj/item/fish/lavaloop/proc/on_fish_land(mob/living/target, bonus_value)
if(!istype(target))
return FALSE
return (target.mob_size >= MOB_SIZE_LARGE)
/obj/item/fish/lavaloop/plasma_river
maximum_bonus = 30
/obj/item/fish/lavaloop/plasma_river/explode_on_user(mob/living/user)
playsound(src, 'sound/effects/explosion1.ogg', 40, TRUE)
user.flash_act(1, 1)
user.apply_status_effect(/datum/status_effect/ice_block_talisman, 5 SECONDS)
qdel(src)
/obj/item/fish/lavaloop/plasma_river/on_fish_land(mob/living/target, bonus_value)
if(!istype(target))
return FALSE
if(target.mob_size < MOB_SIZE_LARGE)
return FALSE
var/freeze_timer = (bonus_value * 0.1)
if(freeze_timer <= 0)
return FALSE
target.apply_status_effect(/datum/status_effect/ice_block_talisman, freeze_timer SECONDS)
return FALSE
+73
View File
@@ -0,0 +1,73 @@
///From oil puddles from the elephant graveyard. Also an evolution of the "unmarine bonemass"
/obj/item/fish/mastodon
name = "unmarine mastodon"
desc = "A monster of exposed muscles and innards, wrapped in a fish-like skeleton. You don't remember ever seeing it on the catalog."
icon = 'icons/obj/aquarium/wide.dmi'
icon_state = "mastodon"
base_pixel_x = -16
pixel_x = -16
sprite_width = 12
sprite_height = 7
show_in_catalog = FALSE
random_case_rarity = FISH_RARITY_NOPE
fishing_difficulty_modifier = 30
required_fluid_type = AQUARIUM_FLUID_ANY_WATER
min_pressure = HAZARD_LOW_PRESSURE
health = 300
stable_population = 1 //This means they can only crossbreed.
grind_results = list(/datum/reagent/bone_dust = 5, /datum/reagent/consumable/liquidgibs = 5)
fillet_type = /obj/item/stack/sheet/bone
num_fillets = 2
feeding_frequency = 2 MINUTES
breeding_timeout = 5 MINUTES
average_size = 180
average_weight = 5000
death_text = "%SRC stops moving."
fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/amphibious, /datum/fish_trait/revival, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/aggressive)
beauty = FISH_BEAUTY_BAD
///From the cursed spring
/obj/item/fish/soul
name = "soulfish"
desc = "A distant yet vaguely close critter, like a long lost relative. You feel your soul rejuvenated just from looking at it... Also, what the fuck is this shit?!"
icon_state = "soulfish"
sprite_width = 7
sprite_height = 6
average_size = 60
average_weight = 1200
stable_population = 4
show_in_catalog = FALSE
beauty = FISH_BEAUTY_EXCELLENT
fish_movement_type = /datum/fish_movement/choppy //Glideless legacy movement? in my fishing minigame?
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = JUNKFOOD|FRIED,
),
)
fillet_type = /obj/item/food/meat/cutlet/plain/human
required_temperature_min = MIN_AQUARIUM_TEMP+3
required_temperature_max = MIN_AQUARIUM_TEMP+38
random_case_rarity = FISH_RARITY_NOPE
///From the cursed spring
/obj/item/fish/skin_crab
name = "skin crab"
desc = "<i>\"And on the eighth day, a demential mockery of both humanity and crabity was made.\"<i> Fascinating."
icon_state = "skin_crab"
sprite_width = 7
sprite_height = 6
average_size = 40
average_weight = 750
stable_population = 5
show_in_catalog = FALSE
beauty = FISH_BEAUTY_GREAT
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = JUNKFOOD|FRIED
),
)
fillet_type = /obj/item/food/meat/slab/rawcrab
random_case_rarity = FISH_RARITY_NOPE
@@ -0,0 +1,275 @@
/obj/item/fish/clownfish
name = "clownfish"
desc = "Clownfish catch prey by swimming onto the reef, attracting larger fish, and luring them back to the anemone. The anemone will sting and eat the larger fish, leaving the remains for the clownfish."
icon_state = "clownfish"
required_fluid_type = AQUARIUM_FLUID_SALTWATER
sprite_width = 7
sprite_height = 4
average_size = 30
average_weight = 500
stable_population = 4
fish_traits = list(/datum/fish_trait/picky_eater)
evolution_types = list(/datum/fish_evolution/lubefish)
compatible_types = list(/obj/item/fish/clownfish/lube)
required_temperature_min = MIN_AQUARIUM_TEMP+22
required_temperature_max = MIN_AQUARIUM_TEMP+30
/obj/item/fish/clownfish/lube
name = "lubefish"
desc = "A clownfish exposed to cherry-flavored lube for far too long. First discovered the days following a cargo incident around the seas of Europa, when thousands of thousands of thousands..."
icon_state = "lubefish"
random_case_rarity = FISH_RARITY_VERY_RARE
fish_traits = list(/datum/fish_trait/picky_eater, /datum/fish_trait/lubed)
evolution_types = null
compatible_types = list(/obj/item/fish/clownfish)
food = /datum/reagent/lube
fishing_difficulty_modifier = 5
beauty = FISH_BEAUTY_GREAT
/obj/item/fish/cardinal
name = "cardinalfish"
desc = "Cardinalfish are often found near sea urchins, where the fish hide when threatened."
icon_state = "cardinalfish"
sprite_width = 6
sprite_height = 3
required_fluid_type = AQUARIUM_FLUID_SALTWATER
average_size = 30
average_weight = 500
stable_population = 4
fish_traits = list(/datum/fish_trait/vegan)
required_temperature_min = MIN_AQUARIUM_TEMP+22
required_temperature_max = MIN_AQUARIUM_TEMP+30
/obj/item/fish/greenchromis
name = "green chromis"
desc = "The Chromis can vary in color from blue to green depending on the lighting and distance from the lights."
icon_state = "greenchromis"
sprite_width = 5
sprite_height = 3
required_fluid_type = AQUARIUM_FLUID_SALTWATER
average_size = 30
average_weight = 500
stable_population = 5
required_temperature_min = MIN_AQUARIUM_TEMP+23
required_temperature_max = MIN_AQUARIUM_TEMP+28
fishing_difficulty_modifier = 5 // Bit harder
/obj/item/fish/firefish
name = "firefish goby"
desc = "To communicate in the wild, the firefish uses its dorsal fin to alert others of potential danger."
icon_state = "firefish"
sprite_width = 5
sprite_height = 3
required_fluid_type = AQUARIUM_FLUID_SALTWATER
average_size = 30
average_weight = 500
stable_population = 3
disliked_bait = list(/obj/item/food/bait/worm, /obj/item/food/bait/doughball)
fish_movement_type = /datum/fish_movement/zippy
required_temperature_min = MIN_AQUARIUM_TEMP+23
required_temperature_max = MIN_AQUARIUM_TEMP+28
/obj/item/fish/pufferfish
name = "pufferfish"
desc = "They say that one pufferfish contains enough toxins to kill 30 people, although in the last few decades they've been genetically engineered en masse to be less poisonous."
icon_state = "pufferfish"
required_fluid_type = AQUARIUM_FLUID_SALTWATER
sprite_width = 8
sprite_height = 6
average_size = 60
average_weight = 1000
stable_population = 3
required_temperature_min = MIN_AQUARIUM_TEMP+23
required_temperature_max = MIN_AQUARIUM_TEMP+28
fillet_type = /obj/item/food/fishmeat/quality //Too bad they're poisonous
fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/toxic)
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/lanternfish
name = "lanternfish"
desc = "Typically found in areas below 6600 feet below the surface of the ocean, they live in complete darkness."
icon_state = "lanternfish"
required_fluid_type = AQUARIUM_FLUID_SALTWATER
random_case_rarity = FISH_RARITY_VERY_RARE
sprite_width = 6
sprite_height = 5
average_size = 50
average_weight = 1000
stable_population = 3
fish_traits = list(/datum/fish_trait/nocturnal)
required_temperature_min = MIN_AQUARIUM_TEMP+2 //My source is that the water at a depth 6600 feet is pretty darn cold.
required_temperature_max = MIN_AQUARIUM_TEMP+18
beauty = FISH_BEAUTY_NULL
/obj/item/fish/stingray
name = "stingray"
desc = "A type of ray, most known for its venomous stinger. Despite that, They're normally docile, if not a bit easily frightened."
icon_state = "stingray"
stable_population = 4
sprite_height = 7
sprite_width = 8
average_size = 60
average_weight = 700
beauty = FISH_BEAUTY_GREAT
random_case_rarity = FISH_RARITY_RARE
required_fluid_type = AQUARIUM_FLUID_SALTWATER //Someone ought to add river rays later I guess.
fish_traits = list(/datum/fish_trait/stinger, /datum/fish_trait/toxic_barbs, /datum/fish_trait/wary, /datum/fish_trait/carnivore, /datum/fish_trait/predator)
/obj/item/fish/swordfish
name = "swordfish"
desc = "A large billfish, most famous for its elongated bill, while also fairly popular for cooking, and as a fearsome weapon in the hands of a veteran spess-fisherman."
icon = 'icons/obj/aquarium/wide.dmi'
icon_state = "swordfish"
inhand_icon_state = "swordfish"
force = 18
sharpness = SHARP_EDGED
attack_verb_continuous = list("slashes", "cuts", "pierces")
attack_verb_simple = list("slash", "cut", "pierce")
block_sound = 'sound/weapons/parry.ogg'
hitsound = 'sound/weapons/rapierhit.ogg'
demolition_mod = 0.75
attack_speed = 1 SECONDS
block_chance = 50
wound_bonus = 10
bare_wound_bonus = 20
armour_penetration = 75
base_pixel_x = -18
pixel_x = -18
sprite_width = 13
sprite_height = 6
stable_population = 3
average_size = 140
average_weight = 4000
breeding_timeout = 4.5 MINUTES
feeding_frequency = 4 MINUTES
health = 180
beauty = FISH_BEAUTY_EXCELLENT
random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
required_fluid_type = AQUARIUM_FLUID_SALTWATER
fish_movement_type = /datum/fish_movement/plunger
fishing_difficulty_modifier = 25
fillet_type = /obj/item/food/fishmeat/quality
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = SEAFOOD,
),
/obj/item/fish,
)
fish_traits = list(/datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/stinger)
/obj/item/fish/swordfish/get_force_rank()
switch(w_class)
if(WEIGHT_CLASS_TINY)
force -= 11
attack_speed -= 0.4 SECONDS
block_chance -= 45
armour_penetration -= 20
wound_bonus -= 15
bare_wound_bonus -= 20
if(WEIGHT_CLASS_SMALL)
force -= 8
attack_speed -= 0.3 SECONDS
block_chance -= 30
armour_penetration -= 15
wound_bonus -= 10
bare_wound_bonus -= 20
if(WEIGHT_CLASS_NORMAL)
force -= 5
attack_speed -= 0.2 SECONDS
block_chance -= 20
armour_penetration -= 10
wound_bonus -= 10
bare_wound_bonus -= 15
if(WEIGHT_CLASS_BULKY)
force -= 3
attack_speed -= 0.1 SECONDS
block_chance -= 10
armour_penetration -= 5
wound_bonus -= 5
bare_wound_bonus -= 10
if(WEIGHT_CLASS_GIGANTIC)
force += 5
attack_speed += 0.2 SECONDS
demolition_mod += 0.15
block_chance += 10
armour_penetration += 5
wound_bonus += 5
bare_wound_bonus += 10
if(status == FISH_DEAD)
force -= 4 + w_class
block_chance -= 25
armour_penetration -= 30
wound_bonus -= 10
bare_wound_bonus -= 10
/obj/item/fish/swordfish/calculate_fish_force_bonus(bonus_malus)
. = ..()
armour_penetration += bonus_malus * 5
wound_bonus += bonus_malus * 3
bare_wound_bonus += bonus_malus * 5
block_chance += bonus_malus * 7
/obj/item/fish/squid
name = "squid"
desc = "An elongated mollusk with eight tentacles, natural camouflage and ink clouds to spray at predators. One of the most intelligent, well-equipped invertebrates out there."
icon_state = "squid"
sprite_width = 4
sprite_height = 5
stable_population = 6
weight_size_deviation = 0.5 // They vary greatly in size.
average_weight = 500 //They're quite lighter than they're long.
required_fluid_type = AQUARIUM_FLUID_SALTWATER
beauty = FISH_BEAUTY_GOOD
required_temperature_min = MIN_AQUARIUM_TEMP+5
required_temperature_max = MIN_AQUARIUM_TEMP+26
fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/ink, /datum/fish_trait/camouflage, /datum/fish_trait/wary)
/obj/item/fish/monkfish
name = "monkfish"
desc = "A member of the Lophiid family of anglerfish. It goes by several different names, however none of them will make it look any prettier, nor any less delicious."
icon_state = "monkfish"
required_fluid_type = AQUARIUM_FLUID_SALTWATER
sprite_height = 7
sprite_width = 7
beauty = FISH_BEAUTY_UGLY
required_temperature_min = MIN_AQUARIUM_TEMP+2
required_temperature_max = MIN_AQUARIUM_TEMP+23
average_size = 60
average_weight = 1400
stable_population = 4
fish_traits = list(/datum/fish_trait/heavy)
fillet_type = /obj/item/food/fishmeat/quality
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = SEAFOOD|BUGS,
),
)
/obj/item/fish/monkfish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
name = pick("monkfish", "fishing-frog", "frog-fish", "sea-devil", "goosefish")
/obj/item/fish/plaice
name = "plaice"
desc = "Perhaps the most prominent flatfish in the space-market. Nature really pulled out the rolling pin on this one."
icon_state = "plaice"
sprite_height = 7
sprite_width = 6
required_fluid_type = AQUARIUM_FLUID_SALTWATER
required_temperature_min = MIN_AQUARIUM_TEMP+2
required_temperature_max = MIN_AQUARIUM_TEMP+18
average_size = 40
average_weight = 700
stable_population = 5
fish_traits = list(/datum/fish_trait/heavy)
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = SEAFOOD|BUGS,
),
)
@@ -0,0 +1,80 @@
/obj/item/fish/ratfish
name = "ratfish"
desc = "A rat exposed to the murky waters of maintenance too long. Any higher power, if it revealed itself, would state that the ratfish's continued existence is extremely unwelcome."
icon_state = "ratfish"
sprite_width = 7
sprite_height = 5
random_case_rarity = FISH_RARITY_RARE
required_fluid_type = AQUARIUM_FLUID_FRESHWATER
stable_population = 10 //set by New, but this is the default config value
fillet_type = /obj/item/food/meat/slab/human/mutant/zombie //eww...
fish_traits = list(/datum/fish_trait/necrophage)
required_temperature_min = MIN_AQUARIUM_TEMP+15
required_temperature_max = MIN_AQUARIUM_TEMP+35
fish_movement_type = /datum/fish_movement/zippy
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = DAIRY
)
)
beauty = FISH_BEAUTY_DISGUSTING
/obj/item/fish/ratfish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
//stable pop reflects the config for how many mice migrate. powerful...
stable_population = CONFIG_GET(number/mice_roundstart)
/obj/item/fish/sludgefish
name = "sludgefish"
desc = "A misshapen, fragile, loosely fish-like living goop, the only thing that'd ever thrive in the acidic and claustrophobic cavities of the station's organic waste disposal system."
icon_state = "sludgefish"
sprite_width = 7
sprite_height = 6
required_fluid_type = AQUARIUM_FLUID_SULPHWATEVER
stable_population = 8
average_size = 20
average_weight = 400
health = 50
breeding_timeout = 2.5 MINUTES
fish_traits = list(/datum/fish_trait/parthenogenesis, /datum/fish_trait/no_mating)
required_temperature_min = MIN_AQUARIUM_TEMP+10
required_temperature_max = MIN_AQUARIUM_TEMP+40
evolution_types = list(/datum/fish_evolution/purple_sludgefish)
beauty = FISH_BEAUTY_NULL
/obj/item/fish/sludgefish/purple
name = "purple sludgefish"
desc = "A misshapen, fragile, loosely fish-like living goop. This one has developed sexual reproduction mechanisms, and a purple tint to boot."
icon_state = "sludgefish_purple"
random_case_rarity = FISH_RARITY_NOPE
fish_traits = list(/datum/fish_trait/parthenogenesis)
/obj/item/fish/slimefish
name = "acquatic slime"
desc = "Kids, this is what happens when a slime overcomes its hydrophobic nature. It goes glug glug."
icon_state = "slimefish"
icon_state_dead = "slimefish_dead"
sprite_width = 7
sprite_height = 7
do_flop_animation = FALSE //it already has a cute bouncy wiggle. :3
random_case_rarity = FISH_RARITY_VERY_RARE
required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
stable_population = 4
health = 150
fillet_type = /obj/item/slime_extract/grey
grind_results = list(/datum/reagent/toxin/slimejelly = 10)
fish_traits = list(/datum/fish_trait/toxin_immunity, /datum/fish_trait/crossbreeder)
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = TOXIC,
),
list(
FISH_BAIT_TYPE = FISH_BAIT_REAGENT,
FISH_BAIT_VALUE = /datum/reagent/toxin,
FISH_BAIT_AMOUNT = 5,
),
)
required_temperature_min = MIN_AQUARIUM_TEMP+20
beauty = FISH_BEAUTY_GREAT
@@ -0,0 +1,256 @@
///Contains fish that can be found in the syndicate fishing portal setting as well as the ominous fish case.
/obj/item/fish/emulsijack
name = "toxic emulsijack"
desc = "Ah, the terrifying emulsijack. Created in a laboratory, the only real use of this slimey, scaleless fish is for completely ruining a tank."
icon_state = "emulsijack"
random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
stable_population = 3
sprite_width = 7
sprite_height = 3
fish_traits = list(/datum/fish_trait/emulsijack)
required_temperature_min = MIN_AQUARIUM_TEMP+5
required_temperature_max = MIN_AQUARIUM_TEMP+40
beauty = FISH_BEAUTY_BAD
/obj/item/fish/donkfish
name = "donk co. company patent donkfish"
desc = "A lab-grown donkfish. Its invention was an accident for the most part, as it was intended to be consumed in donk pockets. Unfortunately, it tastes horrible, so it has now become a pseudo-mascot."
icon_state = "donkfish"
random_case_rarity = FISH_RARITY_VERY_RARE
stable_population = 4
sprite_width = 5
sprite_height = 4
fillet_type = /obj/item/food/fishmeat/donkfish
fish_traits = list(/datum/fish_trait/yucky)
required_temperature_min = MIN_AQUARIUM_TEMP+15
required_temperature_max = MIN_AQUARIUM_TEMP+28
beauty = FISH_BEAUTY_EXCELLENT
/obj/item/fish/jumpercable
name = "monocloning jumpercable"
desc = "A surprisingly useful if nasty looking creation from the syndicate fish labs. Drop one in a tank, and \
watch it self-feed and multiply. Generates more and more power as a growing swarm!"
icon_state = "jumpercable"
sprite_width = 16
sprite_height = 5
stable_population = 12
average_size = 110
average_weight = 6000
random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
required_temperature_min = MIN_AQUARIUM_TEMP+10
required_temperature_max = MIN_AQUARIUM_TEMP+30
favorite_bait = list(/obj/item/stock_parts/power_store/cell/lead)
fish_traits = list(
/datum/fish_trait/parthenogenesis,
/datum/fish_trait/mixotroph,
/datum/fish_trait/electrogenesis,
)
beauty = FISH_BEAUTY_UGLY
/obj/item/fish/chainsawfish
name = "chainsawfish"
desc = "A very, very angry bioweapon, whose sole purpose is to rip and tear."
icon = 'icons/obj/aquarium/wide.dmi'
icon_state = "chainsawfish"
inhand_icon_state = "chainsawfish"
icon_state_dead = "chainsawfish_dead"
force = 22
demolition_mod = 1.5
block_chance = 15
attack_verb_continuous = list("saws", "tears", "lacerates", "cuts", "chops", "dices")
attack_verb_simple = list("saw", "tear", "lacerate", "cut", "chop", "dice")
hitsound = 'sound/weapons/chainsawhit.ogg'
sharpness = SHARP_EDGED
tool_behaviour = TOOL_SAW
toolspeed = 0.5
base_pixel_x = -16
pixel_x = -16
sprite_width = 8
sprite_height = 5
stable_population = 3
average_size = 85
average_weight = 2500
breeding_timeout = 4.25 MINUTES
feeding_frequency = 3 MINUTES
health = 180
beauty = FISH_BEAUTY_GREAT
random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
required_fluid_type = AQUARIUM_FLUID_FRESHWATER
fish_movement_type = /datum/fish_movement/accelerando
fishing_difficulty_modifier = 30
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = GORE,
),
)
fish_traits = list(/datum/fish_trait/aggressive, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/stinger)
required_temperature_min = MIN_AQUARIUM_TEMP+18
required_temperature_max = MIN_AQUARIUM_TEMP+26
/obj/item/fish/chainsawfish/Initialize(mapload)
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
/obj/item/fish/chainsawfish/update_icon_state()
if(status == FISH_DEAD)
inhand_icon_state = "chainsawfish_dead"
else
inhand_icon_state = "chainsawfish"
if(HAS_TRAIT(src, TRAIT_WIELDED))
inhand_icon_state = "[inhand_icon_state]_wielded"
return ..()
/obj/item/fish/chainsawfish/get_force_rank()
switch(w_class)
if(WEIGHT_CLASS_TINY)
force -= 10
attack_speed -= 0.2 SECONDS
demolition_mod -= 0.4
block_chance -= 15
armour_penetration -= 10
wound_bonus -= 10
bare_wound_bonus -= 10
toolspeed += 0.6
if(WEIGHT_CLASS_SMALL)
force -= 8
attack_speed -= 0.1 SECONDS
demolition_mod -= 0.3
block_chance -= 10
armour_penetration -= 10
wound_bonus -= 10
bare_wound_bonus -= 10
toolspeed += 0.4
if(WEIGHT_CLASS_NORMAL)
force -= 5
demolition_mod -= 0.15
block_chance -= 5
armour_penetration -= 5
wound_bonus -= 5
bare_wound_bonus -= 5
toolspeed += 0.2
if(WEIGHT_CLASS_HUGE)
force += 2
attack_speed += 0.2 SECONDS
demolition_mod += 0.15
armour_penetration += 10
block_chance += 10
wound_bonus += 10
bare_wound_bonus += 5
if(WEIGHT_CLASS_GIGANTIC)
force += 4
attack_speed += 0.4 SECONDS
demolition_mod += 0.3
block_chance += 20
armour_penetration += 20
wound_bonus += 15
bare_wound_bonus += 10
toolspeed -= 0.1
if(status == FISH_DEAD)
force -= 8 + w_class
hitsound = SFX_SWING_HIT
block_chance -= 25
demolition_mod -= 0.3
armour_penetration -= 15
wound_bonus -= 5
bare_wound_bonus -= 5
toolspeed += 1
/obj/item/fish/chainsawfish/calculate_fish_force_bonus(bonus_malus)
. = ..()
armour_penetration += bonus_malus * 3
wound_bonus += bonus_malus * 2
bare_wound_bonus += bonus_malus * 3
block_chance += bonus_malus * 2
toolspeed -= bonus_malus * 0.1
/obj/item/fish/pike/armored
name = "armored pike"
desc = "A long-bodied, metal-clad predator with a snout that almost looks like an halberd. Definitely a weapon to swing around."
icon_state = "armored_pike"
inhand_icon_state = "armored_pike"
attack_verb_continuous = list("attacks", "pokes", "jabs", "tears", "lacerates", "gores")
attack_verb_simple = list("attack", "poke", "jab", "tear", "lacerate", "gore")
hitsound = 'sound/weapons/bladeslice.ogg'
block_sound = 'sound/weapons/parry.ogg'
force = 20
sharpness = SHARP_EDGED
wound_bonus = -15
attack_speed = 1 SECONDS
block_chance = 25
bare_wound_bonus = 15
demolition_mod = 0.8
armour_penetration = 10
stable_population = 3
average_weight = 3000
breeding_timeout = 5 MINUTES
feeding_frequency = 4 MINUTES
health = 180
random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
beauty = FISH_BEAUTY_GREAT
fishing_difficulty_modifier = 20
fish_traits = list(/datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/aggressive, /datum/fish_trait/picky_eater, /datum/fish_trait/stinger)
compatible_types = list(/obj/item/fish/pike)
favorite_bait = list(
list(
FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
FISH_BAIT_VALUE = SEAFOOD,
),
/obj/item/fish,
)
/obj/item/fish/swordfish/get_force_rank()
switch(w_class)
if(WEIGHT_CLASS_TINY)
force -= 11
attack_speed -= 0.4 SECONDS
block_chance -= 25
armour_penetration -= 15
wound_bonus -= 15
bare_wound_bonus -= 30
if(WEIGHT_CLASS_SMALL)
force -= 6
attack_speed -= 0.3 SECONDS
block_chance -= 20
armour_penetration -= 10
wound_bonus -= 10
bare_wound_bonus -= 25
if(WEIGHT_CLASS_NORMAL)
force -= 4
attack_speed -= 0.2 SECONDS
block_chance -= 20
armour_penetration -= 5
wound_bonus -= 10
bare_wound_bonus -= 15
if(WEIGHT_CLASS_HUGE)
force += 3
attack_speed += 0.2 SECONDS
block_chance += 10
demolition_mod += 0.1
armour_penetration += 5
wound_bonus += 10
bare_wound_bonus += 5
if(WEIGHT_CLASS_GIGANTIC)
force += 7
attack_speed += 0.3 SECONDS
demolition_mod += 0.2
block_chance += 20
armour_penetration += 10
wound_bonus += 15
bare_wound_bonus += 10
if(status == FISH_DEAD)
force -= 5 + w_class
block_chance -= 15
armour_penetration -= 10
wound_bonus -= 5
bare_wound_bonus -= 15
/obj/item/fish/pike/armored/calculate_fish_force_bonus(bonus_malus)
. = ..()
armour_penetration += bonus_malus * 3
wound_bonus += bonus_malus * 2
bare_wound_bonus += bonus_malus * 4
block_chance += bonus_malus * 4
@@ -0,0 +1,69 @@
//Tiziran Fish.
/obj/item/fish/dwarf_moonfish
name = "dwarf moonfish"
desc = "Ordinarily in the wild, the Zagoskian moonfish is around the size of a tuna, however through selective breeding a smaller breed suitable for being kept as an aquarium pet has been created."
icon_state = "dwarf_moonfish"
sprite_height = 6
sprite_width = 6
required_fluid_type = AQUARIUM_FLUID_SALTWATER
stable_population = 2
fillet_type = /obj/item/food/fishmeat/moonfish
average_size = 60
average_weight = 1000
required_temperature_min = MIN_AQUARIUM_TEMP+20
required_temperature_max = MIN_AQUARIUM_TEMP+30
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/gunner_jellyfish
name = "gunner jellyfish"
desc = "So called due to their resemblance to an artillery shell, the gunner jellyfish is native to Tizira, where it is enjoyed as a delicacy. Produces a mild hallucinogen that is destroyed by cooking."
icon_state = "gunner_jellyfish"
sprite_height = 4
sprite_width = 5
required_fluid_type = AQUARIUM_FLUID_SALTWATER
stable_population = 4
fillet_type = /obj/item/food/fishmeat/gunner_jellyfish
required_temperature_min = MIN_AQUARIUM_TEMP+24
required_temperature_max = MIN_AQUARIUM_TEMP+32
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/needlefish
name = "needlefish"
desc = "A tiny, transparent fish which resides in large schools in the oceans of Tizira. A common food for other, larger fish."
icon_state = "needlefish"
sprite_height = 3
sprite_width = 7
required_fluid_type = AQUARIUM_FLUID_SALTWATER
stable_population = 12
breeding_timeout = 1 MINUTES
fillet_type = null
average_size = 20
average_weight = 300
fish_traits = list(/datum/fish_trait/carnivore)
required_temperature_min = MIN_AQUARIUM_TEMP+10
required_temperature_max = MIN_AQUARIUM_TEMP+32
/obj/item/fish/needlefish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
add_traits(list(TRAIT_FISHING_BAIT, TRAIT_GOOD_QUALITY_BAIT), INNATE_TRAIT)
/obj/item/fish/armorfish
name = "armorfish"
desc = "A small shellfish native to Tizira's oceans, known for its exceptionally hard shell. Consumed similarly to prawns."
icon_state = "armorfish"
sprite_height = 5
sprite_width = 6
average_size = 25
average_weight = 350
required_fluid_type = AQUARIUM_FLUID_SALTWATER
stable_population = 10
breeding_timeout = 1.25 MINUTES
fillet_type = /obj/item/food/fishmeat/armorfish
fish_movement_type = /datum/fish_movement/slow
required_temperature_min = MIN_AQUARIUM_TEMP+10
required_temperature_max = MIN_AQUARIUM_TEMP+32
/obj/item/fish/armorfish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
add_traits(list(TRAIT_FISHING_BAIT, TRAIT_GOOD_QUALITY_BAIT), INNATE_TRAIT)
+1 -1
View File
@@ -90,7 +90,7 @@
if(source.catalog_description && (fish_type in source.fish_table))
spot_descriptions += source.catalog_description
.["spots"] = english_list(spot_descriptions, nothing_text = "Unknown")
var/list/fish_list_properties = collect_fish_properties()
var/list/fish_list_properties = SSfishing.fish_properties
var/list/fav_bait = fish_list_properties[fishy][NAMEOF(fishy, favorite_bait)]
var/list/disliked_bait = fish_list_properties[fishy][NAMEOF(fishy, disliked_bait)]
var/list/bait_list = list()
+52 -2
View File
@@ -52,7 +52,7 @@
*/
/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."
desc = "A fishing line that automatically spins lures and begins reeling in fish the moment it bites. Also good for hurling things towards you."
icon_state = "reel_auto"
fishing_line_traits = FISHING_LINE_AUTOREEL
line_color = "#F88414"
@@ -163,6 +163,20 @@
rod_overlay_icon_state = "hook_treasure_overlay"
chasm_detritus_type = /datum/chasm_detritus/restricted/objects
/obj/item/fishing_hook/magnet/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_FISHING_EQUIPMENT_SLOTTED, PROC_REF(hook_equipped))
///We make sure that the fishng rod doesn't need a bait to reliably catch non-fish loot.
/obj/item/fishing_hook/magnet/proc/hook_equipped(datum/source, obj/item/fishing_rod/rod)
SIGNAL_HANDLER
ADD_TRAIT(rod, TRAIT_ROD_REMOVE_FISHING_DUD, type)
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_removed))
/obj/item/fishing_hook/magnet/proc/on_removed(atom/movable/source, atom/old_loc, dir, forced)
SIGNAL_HANDLER
REMOVE_TRAIT(old_loc, TRAIT_ROD_REMOVE_FISHING_DUD, type)
UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
/obj/item/fishing_hook/magnet/get_hook_bonus_multiplicative(fish_type, datum/fish_source/source)
if(fish_type == FISHING_DUD || ispath(fish_type, /obj/item/fish))
@@ -171,7 +185,6 @@
// We multiply the odds by five for everything that's not a fish nor a dud
return MAGNET_HOOK_BONUS_MULTIPLIER
/obj/item/fishing_hook/shiny
name = "shiny lure hook"
icon_state = "gold_shiny"
@@ -364,5 +377,42 @@
new /obj/item/storage/fish_case(src)
new /obj/item/storage/fish_case(src)
/obj/item/storage/box/fishing_lures
name = "fishing lures set"
desc = "A small tackle box containing all the fishing lures you will ever need to curb randomness."
icon_state = "plasticbox"
foldable_result = null
illustration = "fish"
/obj/item/storage/box/fishing_lures/PopulateContents()
new /obj/item/paper/lures_instructions(src)
var/list/typesof = typesof(/obj/item/fishing_lure)
for(var/type in typesof)
new type (src)
atom_storage.set_holdable(/obj/item/fishing_lure) //can only hold lures
//adds an extra slot, so we can put back the lures even if we didn't take out the instructions.
atom_storage.max_slots = length(typesof) + 1
atom_storage.max_total_storage = WEIGHT_CLASS_SMALL * (atom_storage.max_slots + 1)
/obj/item/paper/lures_instructions
name = "instructions paper"
icon_state = "slipfull"
show_written_words = FALSE
desc = "A piece of grey paper with an how-to for dummies about fishing lures printed on it. Smells cheap."
default_raw_text = "<b>Thank you for buying this set.</b><br>\
This a simple non-exhaustive set of instructions on how to use fishing lures, some information may \
be slightly incorrect or oversimplified.<br><br>\
First and foremost, fishing lures are <b>inedible, artificia baits</b>, fairly sturdy so that \
they won't be destroyed by the hungry fish. However, they need to be <b>spun at intervals</b> to replicate \
the motion of a prey or organic bait to tempt the fish, since a piece of plastic and metal ins't \
by itself all that tasty. <b>Different lures</b> can be used to catch <b>different fish</b>.<br><br>\
To help you, each lure comes with <b>a small light</b> that's attached to the <b>float</b> of your fishing rod. \
For those who don't know it, the float is basically the thing bobbing up'n'down above the fishing spot. \
The light will flash <b>green<b> and a sound cue will be played when the bait is <b>ready<b> to be spun. \
Do <b>not</b> spin while the light is still <b>red</b>.<br><br>\
That's all, best of luck to your angling journey.<br><br>"
#undef MAGNET_HOOK_BONUS_MULTIPLIER
#undef RESCUE_HOOK_FISH_MULTIPLIER
+134 -33
View File
@@ -1,4 +1,4 @@
// Lure bobbing
// float bobbing
#define WAIT_PHASE 1
// Click now to start tgui part
#define BITING_PHASE 2
@@ -53,12 +53,14 @@
var/phase = WAIT_PHASE
// Timer for the next phase
var/next_phase_timer
// The last time we clicked during the baiting phase
var/last_baiting_click
/// Fishing mob
var/mob/user
/// Rod that is used for the challenge
var/obj/item/fishing_rod/used_rod
/// Lure visual
var/obj/effect/fishing_lure/lure
/// float visual
var/obj/effect/fishing_float/float
/// Background icon state from fishing_hud.dmi
var/background = "background_default"
/// Fish icon state from fishing_hud.dmi
@@ -110,7 +112,8 @@
src.reward_path = reward_path
src.used_rod = rod
var/atom/spot = comp.parent
lure = new(get_turf(spot), spot)
float = new(get_turf(spot), spot)
float.spin_frequency = rod.spin_frequency
RegisterSignal(spot, COMSIG_QDELETING, PROC_REF(on_spot_gone))
RegisterSignal(comp.fish_source, COMSIG_FISHING_SOURCE_INTERRUPT_CHALLENGE, PROC_REF(interrupt_challenge))
comp.fish_source.RegisterSignal(src, COMSIG_FISHING_CHALLENGE_COMPLETED, TYPE_PROC_REF(/datum/fish_source, on_challenge_completed))
@@ -122,7 +125,7 @@
var/movement_path = initial(fish.fish_movement_type)
mover = new movement_path(src)
// Apply fish trait modifiers
var/list/fish_list_properties = collect_fish_properties()
var/list/fish_list_properties = SSfishing.fish_properties
var/list/fish_traits = fish_list_properties[fish][NAMEOF(fish, fish_traits)]
for(var/fish_trait in fish_traits)
var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait]
@@ -178,8 +181,7 @@
//Stops the line snapped message from appearing everytime the minigame is over.
UnregisterSignal(fishing_line, COMSIG_QDELETING)
QDEL_NULL(fishing_line)
if(lure)
QDEL_NULL(lure)
QDEL_NULL(float)
SStgui.close_uis(src)
user = null
used_rod = null
@@ -187,8 +189,8 @@
return ..()
/datum/fishing_challenge/proc/send_alert(message)
var/turf/lure_turf = get_turf(lure)
lure_turf?.balloon_alert(user, message)
var/turf/float_turf = get_turf(float)
float_turf?.balloon_alert(user, message)
/datum/fishing_challenge/proc/on_spot_gone(datum/source)
SIGNAL_HANDLER
@@ -204,11 +206,14 @@
/datum/fishing_challenge/proc/start(mob/living/user)
/// Create fishing line visuals
if(!used_rod.internal)
fishing_line = used_rod.create_fishing_line(lure, user, target_py = 5)
fishing_line = used_rod.create_fishing_line(float, user, target_py = 5)
if(isnull(fishing_line)) //couldn't create a fishing line, probably because we don't have a good line of sight.
qdel(src)
return
RegisterSignal(fishing_line, COMSIG_QDELETING, PROC_REF(on_line_deleted))
else //if the rod doesnt have a fishing line, then it ends when they move away
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_lure_or_user_move))
RegisterSignal(lure, COMSIG_MOVABLE_MOVED, PROC_REF(on_lure_or_user_move))
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_float_or_user_move))
RegisterSignal(float, COMSIG_MOVABLE_MOVED, PROC_REF(on_float_or_user_move))
RegisterSignal(user, SIGNAL_ADDTRAIT(TRAIT_HANDS_BLOCKED), PROC_REF(on_hands_blocked))
RegisterSignal(user, SIGNAL_REMOVETRAIT(TRAIT_PROFOUND_FISHER), PROC_REF(no_longer_fishing))
active_effects = bitfield_to_list(special_effects & FISHING_MINIGAME_ACTIVE_EFFECTS)
@@ -219,19 +224,49 @@
RegisterSignal(user, COMSIG_MOB_CLICKON, PROC_REF(handle_click))
start_baiting_phase()
to_chat(user, span_notice("You start fishing..."))
playsound(lure, 'sound/effects/splash.ogg', 100)
playsound(float, 'sound/effects/splash.ogg', 100)
///Set the timers for lure that need to be spun at intervals.
/datum/fishing_challenge/proc/set_lure_timers()
float.spin_ready = FALSE
addtimer(CALLBACK(src, PROC_REF(set_lure_ready)), float.spin_frequency[1], TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_DELETE_ME)
addtimer(CALLBACK(src, PROC_REF(missed_lure)), float.spin_frequency[2], TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_DELETE_ME)
float.update_appearance(UPDATE_OVERLAYS)
/datum/fishing_challenge/proc/set_lure_ready()
if(phase != WAIT_PHASE)
return
float.spin_ready = TRUE
float.update_appearance(UPDATE_OVERLAYS)
if(special_effects & FISHING_MINIGAME_AUTOREEL)
addtimer(CALLBACK(src, PROC_REF(auto_spin)), 0.2 SECONDS)
playsound(float, 'sound/machines/ping.ogg', 10, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
/datum/fishing_challenge/proc/auto_spin()
if(phase != WAIT_PHASE || !float.spin_ready)
return
float.spin_ready = FALSE
float.update_appearance(UPDATE_OVERLAYS)
set_lure_timers()
send_alert("spun")
/datum/fishing_challenge/proc/missed_lure()
if(phase != WAIT_PHASE)
return
send_alert("miss!")
start_baiting_phase(TRUE) //Add in another 3 to 5 seconds for not spinning the lure.
/datum/fishing_challenge/proc/on_line_deleted(datum/source)
SIGNAL_HANDLER
fishing_line = null
///The lure may be out of sight if the user has moed around a corner, so the message should be displayed over him instead.
///The float may be out of sight if the user has moed around a corner, so the message should be displayed over him instead.
user.balloon_alert(user, user.is_holding(used_rod) ? "line snapped" : "rod dropped")
interrupt()
/datum/fishing_challenge/proc/on_lure_or_user_move(datum/source)
/datum/fishing_challenge/proc/on_float_or_user_move(datum/source)
SIGNAL_HANDLER
if(!user.CanReach(lure))
if(!user.CanReach(float))
user.balloon_alert(user, "too far!")
interrupt()
@@ -258,8 +293,20 @@
if(!HAS_TRAIT(source, TRAIT_PROFOUND_FISHER) && source.get_active_held_item() != used_rod)
return
if(phase == WAIT_PHASE)
send_alert("miss!")
start_baiting_phase(TRUE) //Add in another 3 to 5 seconds for that blunder.
if(world.time < last_baiting_click + 0.25 SECONDS)
return //Don't punish players if they accidentally double clicked.
if(float.spin_frequency)
if(!float.spin_ready)
send_alert("too early!")
start_baiting_phase(TRUE) //Add in another 3 to 5 seconds for that blunder.
else
send_alert("spun")
last_baiting_click = world.time
float.spin_ready = FALSE
set_lure_timers()
else
send_alert("miss!")
start_baiting_phase(TRUE) //Add in another 3 to 5 seconds for that blunder.
else if(phase == BITING_PHASE)
start_minigame_phase()
return COMSIG_MOB_CANCEL_CLICKON
@@ -298,30 +345,33 @@
user.client?.give_award(/datum/award/achievement/skill/legendary_fisher, user)
if(win)
if(reward_path != FISHING_DUD)
playsound(lure, 'sound/effects/bigsplash.ogg', 100)
playsound(float, 'sound/effects/bigsplash.ogg', 100)
SEND_SIGNAL(src, COMSIG_FISHING_CHALLENGE_COMPLETED, user, win)
if(!QDELETED(src))
qdel(src)
/datum/fishing_challenge/proc/start_baiting_phase(penalty = FALSE)
var/wait_time
last_baiting_click = world.time
if(penalty)
wait_time = min(timeleft(next_phase_timer) + rand(3 SECONDS, 5 SECONDS), 30 SECONDS)
else
wait_time = rand(3 SECONDS, 25 SECONDS)
wait_time = float.spin_frequency ? rand(11 SECONDS, 17 SECONDS) : rand(3 SECONDS, 25 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(float, pixel_y = 1, time = 1 SECONDS, loop = -1, flags = ANIMATION_RELATIVE)
animate(pixel_y = -1, time = 1 SECONDS, flags = ANIMATION_RELATIVE)
next_phase_timer = addtimer(CALLBACK(src, PROC_REF(start_biting_phase)), wait_time, TIMER_STOPPABLE)
next_phase_timer = addtimer(CALLBACK(src, PROC_REF(start_biting_phase)), wait_time, TIMER_STOPPABLE|TIMER_DELETE_ME)
if(float.spin_frequency)
set_lure_timers()
/datum/fishing_challenge/proc/start_biting_phase()
phase = BITING_PHASE
// Trashing animation
playsound(lure, 'sound/effects/fish_splash.ogg', 100)
playsound(float, 'sound/effects/fish_splash.ogg', 100)
if(HAS_MIND_TRAIT(user, TRAIT_REVEAL_FISH))
switch(fish_icon)
if(FISH_ICON_DEF)
@@ -356,13 +406,19 @@
send_alert("bottle!!!")
else
send_alert("!!!")
animate(lure, pixel_y = 3, time = 5, loop = -1, flags = ANIMATION_RELATIVE)
animate(float, 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
addtimer(CALLBACK(src, PROC_REF(automatically_start_minigame)), 0.2 SECONDS)
// Setup next phase
next_phase_timer = addtimer(CALLBACK(src, PROC_REF(start_baiting_phase)), BITING_TIME_WINDOW, TIMER_STOPPABLE)
next_phase_timer = addtimer(CALLBACK(src, PROC_REF(start_baiting_phase)), BITING_TIME_WINDOW, TIMER_STOPPABLE|TIMER_DELETE_ME)
///If we're using a lure, we want the float to show a little green light during the minigame phase and not a red one.
float.spin_ready = TRUE
float.update_appearance(UPDATE_OVERLAYS)
/datum/fishing_challenge/proc/automatically_start_minigame()
if(phase == BITING_PHASE)
start_minigame_phase(auto_reel = TRUE)
///The damage dealt per second to the fish when FISHING_MINIGAME_RULE_KILL is active.
#define FISH_DAMAGE_PER_SECOND 2
@@ -403,6 +459,8 @@
var/diff_dist = 100 + difficulty
bait_position = clamp(round(fish_position + rand(-diff_dist, diff_dist) - bait_height * 0.5), 0, FISHING_MINIGAME_AREA - bait_height)
if(!prepare_minigame_hud())
get_stack_trace("couldn't prepare minigame hud for a fishing challenge.") //just to be sure. This shouldn't happen.
qdel(src)
return
ADD_TRAIT(user, TRAIT_ACTIVELY_FISHING, WEAKREF(src))
phase = MINIGAME_PHASE
@@ -410,10 +468,26 @@
if((FISHING_MINIGAME_RULE_KILL in special_effects) && ispath(reward_path,/obj/item/fish))
var/obj/item/fish/fish = reward_path
var/wait_time = (initial(fish.health) / FISH_DAMAGE_PER_SECOND) SECONDS
addtimer(CALLBACK(src, PROC_REF(win_anyway)), wait_time)
addtimer(CALLBACK(src, PROC_REF(win_anyway)), wait_time, TIMER_DELETE_ME)
start_time = world.time
experience_multiplier += difficulty * FISHING_SKILL_DIFFIULTY_EXP_MULT
///Throws a stack with prefixed text.
/datum/fishing_challenge/proc/get_stack_trace(init_text)
var/text = "[init_text] "
text += "used rod: [used_rod || "null"], "
if(used_rod)
text += "bait: [used_rod.bait || "null"], "
text += "reward: [reward_path || "null"], "
text += "user: [user || "null"]"
if(user)
if(QDELING(user))
text += ", user qdeling"
else if(!user.client)
text += ", user clientless"
text += "."
stack_trace(text)
#undef FISH_DAMAGE_PER_SECOND
///Initialize the minigame hud and register some signals to make it work.
@@ -475,6 +549,11 @@
fishing_hud.transform = fishing_hud.transform.Scale(1, -1)
SEND_SOUND(user, sound('sound/effects/boing.ogg'))
COOLDOWN_START(src, active_effect_cd, rand(5, 6) SECONDS)
if(FISHING_MINIGAME_RULE_CAMO)
fishing_hud.icon_state = "background_camo"
SEND_SOUND(user, sound('sound/effects/nightmare_poof.ogg', volume = 15))
COOLDOWN_START(src, active_effect_cd, rand(6, 8) SECONDS)
animate(fishing_hud.hud_fish, alpha = 7, time = 2 SECONDS)
return
///go back to normal
@@ -487,6 +566,10 @@
if(FISHING_MINIGAME_RULE_FLIP)
fishing_hud.transform = fishing_hud.transform.Scale(1, -1)
COOLDOWN_START(src, active_effect_cd, rand(8, 12) SECONDS)
if(FISHING_MINIGAME_RULE_CAMO)
COOLDOWN_START(src, active_effect_cd, rand(9, 16) SECONDS)
SEND_SOUND(user, sound('sound/effects/nightmare_reappear.ogg', volume = 15))
animate(fishing_hud.hud_fish, alpha = 255, time = 1.2 SECONDS)
fishing_hud.icon_state = background
current_active_effect = null
@@ -647,21 +730,39 @@
icon_state = "completion_[FLOOR(challenge.completion, 5)]"
/// The visual that appears over the fishing spot
/obj/effect/fishing_lure
/obj/effect/fishing_float
name = "float"
icon = 'icons/obj/fishing.dmi'
icon_state = "lure_idle"
icon_state = "float"
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/**
* A list with two keys delimiting the spinning interval in which a mouse click has to be pressed while fishing.
* If set, an emissive overlay will be added, colored green when the lure is ready to be spun, otherwise red.
*/
var/list/spin_frequency
///Is the bait ready to be spun?
var/spin_ready = FALSE
/obj/effect/fishing_lure/Initialize(mapload, atom/spot)
/obj/effect/fishing_float/Initialize(mapload, atom/spot)
. = ..()
if(ismovable(spot)) // we want the lure and therefore the fishing line to stay connected with the fishing spot.
if(ismovable(spot)) // we want the float and therefore the fishing line to stay connected with the fishing spot.
RegisterSignal(spot, COMSIG_MOVABLE_MOVED, PROC_REF(follow_movable))
/obj/effect/fishing_lure/proc/follow_movable(atom/movable/source)
/obj/effect/fishing_float/proc/follow_movable(atom/movable/source)
SIGNAL_HANDLER
set_glide_size(source.glide_size)
forceMove(source.loc)
/obj/effect/fishing_float/update_overlays()
. = ..()
if(!spin_frequency)
return
var/mutable_appearance/overlay = mutable_appearance(icon, "lure_light")
overlay.color = spin_ready ? COLOR_GREEN : COLOR_RED
. += overlay
. += emissive_appearance(icon, "lure_light_emissive", src, alpha = src.alpha)
#undef WAIT_PHASE
#undef BITING_PHASE
#undef MINIGAME_PHASE
+30 -16
View File
@@ -42,6 +42,12 @@
///The name of the icon state of the reel overlay
var/reel_overlay = "reel_overlay"
/**
* A list with two keys delimiting the spinning interval in which a mouse click has to be pressed while fishing.
* Inherited from baits, passed down to the minigame lure.
*/
var/list/spin_frequency
///Prevents spamming the line casting, without affecting the player's click cooldown.
COOLDOWN_DECLARE(casting_cd)
@@ -69,9 +75,11 @@
/obj/item/fishing_rod/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
. = ..()
if(currently_hooked)
context[SCREENTIP_CONTEXT_LMB] = "Reel in"
context[SCREENTIP_CONTEXT_RMB] = "Unhook"
var/gone_fishing = HAS_TRAIT(user, TRAIT_GONE_FISHING)
if(currently_hooked || gone_fishing)
context[SCREENTIP_CONTEXT_LMB] = (gone_fishing && spin_frequency) ? "Spin" : "Reel in"
if(!gone_fishing)
context[SCREENTIP_CONTEXT_RMB] = "Unhook"
return CONTEXTUAL_SCREENTIP_SET
return NONE
@@ -170,6 +178,8 @@
RegisterSignal(fishing_line, COMSIG_BEAM_BEFORE_DRAW, PROC_REF(check_los))
RegisterSignal(fishing_line, COMSIG_QDELETING, PROC_REF(clear_line))
INVOKE_ASYNC(fishing_line, TYPE_PROC_REF(/datum/beam/, Start))
if(QDELETED(fishing_line))
return null
firer.update_held_items()
return fishing_line
@@ -419,23 +429,20 @@
if(user.transferItemToLoc(new_item,src))
set_slot(new_item, slot)
balloon_alert(user, "[slot] installed")
else
balloon_alert(user, "stuck to your hands!")
return
/// Trying to swap item
else if(new_item && current_item)
if(!slot_check(new_item,slot))
return
if(user.transferItemToLoc(new_item,src))
switch(slot)
if(ROD_SLOT_BAIT)
bait = new_item
if(ROD_SLOT_HOOK)
hook = new_item
if(ROD_SLOT_LINE)
line = new_item
user.put_in_hands(current_item)
balloon_alert(user, "[slot] swapped")
if(new_item)
SEND_SIGNAL(new_item, COMSIG_FISHING_EQUIPMENT_SLOTTED, src)
if(user.transferItemToLoc(new_item, src))
user.put_in_hands(current_item)
set_slot(new_item, slot)
balloon_alert(user, "[slot] swapped")
else
balloon_alert(user, "stuck to your hands!")
return
update_icon()
playsound(src, 'sound/items/click.ogg', 50, TRUE)
@@ -445,16 +452,23 @@
switch(slot)
if(ROD_SLOT_BAIT)
bait = equipment
if(!HAS_TRAIT(bait, TRAIT_BAIT_ALLOW_FISHING_DUD))
ADD_TRAIT(src, TRAIT_ROD_REMOVE_FISHING_DUD, INNATE_TRAIT)
if(ROD_SLOT_HOOK)
hook = equipment
if(ROD_SLOT_LINE)
line = equipment
cast_range += FISHING_ROD_REEL_CAST_RANGE
else
CRASH("set_slot called with an undefined slot: [slot]")
SEND_SIGNAL(equipment, COMSIG_FISHING_EQUIPMENT_SLOTTED, src)
/obj/item/fishing_rod/Exited(atom/movable/gone, direction)
. = ..()
if(gone == bait)
bait = null
REMOVE_TRAIT(src, TRAIT_ROD_REMOVE_FISHING_DUD, INNATE_TRAIT)
if(gone == line)
cast_range -= FISHING_ROD_REEL_CAST_RANGE
line = null
+74 -53
View File
@@ -31,6 +31,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
/obj/item/fish/jumpercable = FISH_ICON_ELECTRIC,
/obj/item/fish/lavaloop = FISH_ICON_WEAPON,
/obj/item/fish/mastodon = FISH_ICON_BONE,
/obj/item/fish/pike/armored = FISH_ICON_WEAPON,
/obj/item/fish/pufferfish = FISH_ICON_CHUNKY,
/obj/item/fish/sand_crab = FISH_ICON_CRAB,
/obj/item/fish/skin_crab = FISH_ICON_CRAB,
@@ -139,8 +140,8 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
// In the future non-fish rewards can have variable difficulty calculated here
return
var/list/fish_list_properties = collect_fish_properties()
var/obj/item/fish/caught_fish = result
var/list/fish_properties = SSfishing.fish_properties[caught_fish]
// Baseline fish difficulty
. += initial(caught_fish.fishing_difficulty_modifier)
@@ -148,18 +149,18 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
if(rod.bait)
var/obj/item/bait = rod.bait
//Fav bait makes it easier
var/list/fav_bait = fish_list_properties[caught_fish][NAMEOF(caught_fish, favorite_bait)]
var/list/fav_bait = fish_properties[FISH_PROPERTIES_FAV_BAIT]
for(var/bait_identifer in fav_bait)
if(is_matching_bait(bait, bait_identifer))
. += FAV_BAIT_DIFFICULTY_MOD
//Disliked bait makes it harder
var/list/disliked_bait = fish_list_properties[caught_fish][NAMEOF(caught_fish, disliked_bait)]
var/list/disliked_bait = fish_properties[FISH_PROPERTIES_BAD_BAIT]
for(var/bait_identifer in disliked_bait)
if(is_matching_bait(bait, bait_identifer))
. += DISLIKED_BAIT_DIFFICULTY_MOD
// Matching/not matching fish traits and equipment
var/list/fish_traits = fish_list_properties[caught_fish][NAMEOF(caught_fish, fish_traits)]
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
var/additive_mod = 0
var/multiplicative_mod = 1
@@ -173,8 +174,8 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
. *= multiplicative_mod
/// In case you want more complex rules for specific spots
/datum/fish_source/proc/roll_reward(obj/item/fishing_rod/rod, mob/fisherman)
return pick_weight(get_modified_fish_table(rod,fisherman))
/datum/fish_source/proc/roll_reward(obj/item/fishing_rod/rod, mob/fisherman, atom/location)
return pick_weight(get_modified_fish_table(rod, fisherman, location)) || FISHING_DUD
/**
* Used to register signals or add traits and the such right after conditions have been cleared
@@ -198,7 +199,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
return
var/obj/item/fish/caught = source.reward_path
user.add_mob_memory(/datum/memory/caught_fish, protagonist = user, deuteragonist = initial(caught.name))
var/turf/fishing_spot = get_turf(source.lure)
var/turf/fishing_spot = get_turf(source.float)
var/atom/movable/reward = dispense_reward(source.reward_path, user, fishing_spot)
if(source.used_rod)
SEND_SIGNAL(source.used_rod, COMSIG_FISHING_ROD_CAUGHT_FISH, reward, user)
@@ -259,23 +260,6 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
caught_fish.randomize_size_and_weight()
return reward
/// Cached fish list properties so we don't have to initalize fish every time, init deffered
GLOBAL_LIST(fishing_property_cache)
/// Awful workaround around initial(x.list_variable) not being a thing while trying to keep some semblance of being structured
/proc/collect_fish_properties()
if(GLOB.fishing_property_cache == null)
var/list/fish_property_table = list()
for(var/fish_type in subtypesof(/obj/item/fish))
var/obj/item/fish/fish = new fish_type(null, FALSE)
fish_property_table[fish_type] = list()
fish_property_table[fish_type][NAMEOF(fish, favorite_bait)] = fish.favorite_bait.Copy()
fish_property_table[fish_type][NAMEOF(fish, disliked_bait)] = fish.disliked_bait.Copy()
fish_property_table[fish_type][NAMEOF(fish, fish_traits)] = fish.fish_traits.Copy()
QDEL_NULL(fish)
GLOB.fishing_property_cache = fish_property_table
return GLOB.fishing_property_cache
/// Returns the fish table, with with the unavailable items from fish_counts removed.
/datum/fish_source/proc/get_fish_table()
var/list/table = fish_table.Copy()
@@ -285,7 +269,7 @@ GLOBAL_LIST(fishing_property_cache)
return table
/// Builds a fish weights table modified by bait/rod/user properties
/datum/fish_source/proc/get_modified_fish_table(obj/item/fishing_rod/rod, mob/fisherman)
/datum/fish_source/proc/get_modified_fish_table(obj/item/fishing_rod/rod, mob/fisherman, atom/location)
var/obj/item/bait = rod.bait
///An exponent used to level out the difference in probabilities between fishes/mobs on the table depending on bait quality.
var/leveling_exponent = 0
@@ -305,9 +289,9 @@ GLOBAL_LIST(fishing_property_cache)
else if(HAS_TRAIT(bait, TRAIT_BASIC_QUALITY_BAIT))
result_multiplier = 2
leveling_exponent = 0.1
final_table -= FISHING_DUD
var/list/fish_list_properties = collect_fish_properties()
if(HAS_TRAIT(rod, TRAIT_ROD_REMOVE_FISHING_DUD))
final_table -= FISHING_DUD
if(HAS_TRAIT(fisherman, TRAIT_PROFOUND_FISHER) && !fisherman.client)
@@ -317,37 +301,17 @@ GLOBAL_LIST(fishing_property_cache)
final_table[result] += rod.hook?.get_hook_bonus_additive(result)//Decide on order here so it can be multiplicative
if(ispath(result, /obj/item/fish))
//Modify fish roll chance
var/obj/item/fish/caught_fish = result
if(bait)
final_table[result] = round(final_table[result] * result_multiplier, 1)
if(!HAS_TRAIT(bait, TRAIT_OMNI_BAIT))
//Bait matching likes doubles the chance
var/list/fav_bait = fish_list_properties[result][NAMEOF(caught_fish, favorite_bait)]
for(var/bait_identifer in fav_bait)
if(is_matching_bait(bait, bait_identifer))
final_table[result] *= 2
//Bait matching dislikes
var/list/disliked_bait = fish_list_properties[result][NAMEOF(caught_fish, disliked_bait)]
for(var/bait_identifer in disliked_bait)
if(is_matching_bait(bait, bait_identifer))
final_table[result] = round(final_table[result] * 0.5, 1)
var/mult = bait.check_bait(result)
final_table[result] = round(final_table[result] * mult, 1)
if(mult > 1 && HAS_TRAIT(bait, TRAIT_BAIT_ALLOW_FISHING_DUD))
final_table -= FISHING_DUD
else
final_table[result] = round(final_table[result] * 0.15, 1) //Fishing without bait is not going to be easy
// Apply fish trait modifiers
var/list/fish_traits = fish_list_properties[caught_fish][NAMEOF(caught_fish, fish_traits)]
var/additive_mod = 0
var/multiplicative_mod = 1
for(var/fish_trait in fish_traits)
var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait]
var/list/mod = trait.catch_weight_mod(rod, fisherman)
additive_mod += mod[ADDITIVE_FISHING_MOD]
multiplicative_mod *= mod[MULTIPLICATIVE_FISHING_MOD]
final_table[result] += additive_mod
final_table[result] = round(final_table[result] * multiplicative_mod, 1)
final_table[result] = get_fish_trait_catch_mods(final_table[result], result, rod, fisherman, location)
if(final_table[result] <= 0)
final_table -= result
@@ -371,6 +335,63 @@ GLOBAL_LIST(fishing_property_cache)
return final_table
/datum/fish_source/proc/get_fish_trait_catch_mods(weight, obj/item/fish/fish, obj/item/fishing_rod/rod, mob/user, atom/location)
if(!ispath(fish, /obj/item/fish))
return weight
var/multiplier = 1
for(var/fish_trait in SSfishing.fish_properties[fish][FISH_PROPERTIES_TRAITS])
var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait]
var/list/mod = trait.catch_weight_mod(rod, user, location, fish)
weight += mod[ADDITIVE_FISHING_MOD]
multiplier *= mod[MULTIPLICATIVE_FISHING_MOD]
return round(weight * multiplier, 1)
///returns true if this fishing spot has fish that are shown in the catalog.
/datum/fish_source/proc/has_known_fishes()
for(var/reward in fish_table)
if(!ispath(reward, /obj/item/fish))
continue
var/obj/item/fish/prototype = reward
if(initial(prototype.show_in_catalog))
return TRUE
return FALSE
///Add a string with the names of catchable fishes to the examine text.
/datum/fish_source/proc/get_catchable_fish_names(mob/user, atom/location, list/examine_text)
var/list/known_fishes = list()
var/obj/item/fishing_rod/rod = user.get_active_held_item()
if(!istype(rod))
rod = null
for(var/reward in fish_table)
if(!ispath(reward, /obj/item/fish))
continue
var/obj/item/fish/prototype = reward
if(initial(prototype.show_in_catalog))
var/init_name = initial(prototype.name)
if(rod)
var/init_weight = fish_table[reward]
var/weight = (rod.bait ? rod.bait.check_bait(prototype) : 1)
weight = get_fish_trait_catch_mods(weight, reward, rod, user, location)
if(weight > init_weight)
init_name = span_bold(init_name)
if(weight/init_weight >= 3.5)
init_name = "<u>init_name</u>"
else if(weight < init_weight)
init_name = span_small(reward)
known_fishes += init_name
if(!length(known_fishes))
return
var/info = "You can catch the following fish here"
if(rod)
info = span_tooltip("boldened are the fish you're more likely to catch with your current setup. The opposite is true for smaller names", info)
examine_text += span_info("[info]: [english_list(known_fishes)].")
/datum/fish_source/proc/spawn_reward_from_explosion(atom/location, severity)
if(!explosive_malus)
explosive_spawn(location, severity)
@@ -388,7 +409,7 @@ GLOBAL_LIST(fishing_property_cache)
explosive_spawn(turf, exploded_turfs[turf], multiplier)
exploded_turfs = null
/datum/fish_source/proc/explosive_spawn(location, severity, multiplier = 1)
/datum/fish_source/proc/explosive_spawn(atom/location, severity, multiplier = 1)
for(var/i in 1 to (severity + 2))
if(!prob((100 + 100 * severity)/i * multiplier))
continue
+28 -7
View File
@@ -1,17 +1,22 @@
/datum/fish_source/ocean
fish_table = list(
FISHING_DUD = 11,
FISHING_DUD = 10,
/obj/effect/spawner/message_in_a_bottle = 4,
/obj/item/coin/gold = 7,
/obj/item/fish/clownfish = 15,
/obj/item/fish/pufferfish = 15,
/obj/item/fish/cardinal = 15,
/obj/item/fish/greenchromis = 15,
/obj/item/coin/gold = 6,
/obj/item/fish/clownfish = 11,
/obj/item/fish/pufferfish = 11,
/obj/item/fish/cardinal = 11,
/obj/item/fish/greenchromis = 11,
/obj/item/fish/squid = 11,
/obj/item/fish/stingray = 8,
/obj/item/fish/plaice = 8,
/obj/item/fish/monkfish = 5,
/obj/item/fish/stingray = 10,
/obj/item/fish/lanternfish = 7,
/obj/item/fish/zipzap = 7,
/obj/item/fish/clownfish/lube = 5,
/obj/item/fish/swordfish = 5,
/obj/item/fish/swordfish = 3,
/obj/structure/mystery_box/fishing = 2,
)
fish_counts = list(
@@ -46,13 +51,22 @@
FISHING_DUD = 4,
/obj/item/fish/goldfish = 5,
/obj/item/fish/guppy = 5,
/obj/item/fish/perch = 4,
/obj/item/fish/angelfish = 4,
/obj/item/fish/catfish = 4,
/obj/item/fish/perch = 5,
/obj/item/fish/slimefish = 2,
/obj/item/fish/sockeye_salmon = 1,
/obj/item/fish/arctic_char = 1,
/obj/item/fish/pike = 1,
/obj/item/fish/goldfish/three_eyes = 1,
)
fish_counts = list(
/obj/item/fish/pike = 3,
)
fish_count_regen = list(
/obj/item/fish/pike = 4 MINUTES,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 5
/datum/fish_source/sand
@@ -85,6 +99,7 @@
/obj/item/fish/goldfish = 10,
/obj/item/fish/guppy = 10,
/obj/item/fish/angelfish = 10,
/obj/item/fish/perch = 5,
/obj/item/fish/goldfish/three_eyes = 3,
)
catalog_description = "Aquarium dimension (Fishing portal generator)"
@@ -103,6 +118,8 @@
/obj/item/fish/pufferfish = 10,
/obj/item/fish/cardinal = 10,
/obj/item/fish/greenchromis = 10,
/obj/item/fish/squid = 8,
/obj/item/fish/plaice = 8,
)
catalog_description = "Beach dimension (Fishing portal generator)"
radial_name = "Beach"
@@ -141,6 +158,7 @@
/obj/item/fish/armorfish = 5,
/obj/item/fish/zipzap = 5,
/obj/item/fish/stingray = 4,
/obj/item/fish/monkfish = 4,
/obj/item/fish/swordfish = 3,
)
fish_counts = list(
@@ -185,13 +203,16 @@
/obj/item/fish/donkfish = 5,
/obj/item/fish/emulsijack = 5,
/obj/item/fish/jumpercable = 5,
/obj/item/fish/chainsawfish = 3,
/obj/item/fish/chainsawfish = 2,
/obj/item/fish/pike/armored = 2,
)
fish_counts = list(
/obj/item/fish/chainsawfish = 1,
/obj/item/fish/pike/armored = 1,
)
fish_count_regen = list(
/obj/item/fish/chainsawfish = 7 MINUTES,
/obj/item/fish/pike/armored = 7 MINUTES,
)
catalog_description = "Syndicate dimension (Fishing portal generator)"
radial_name = "Syndicate"
@@ -139,3 +139,7 @@
output = /obj/item/popsicle_stick
food_multiplier = 3
preserve_materials = FALSE
/datum/food_processor_process/canned_ink
input = /obj/item/food/ink_sac
output = /obj/item/food/canned/squid_ink
+5 -1
View File
@@ -202,7 +202,7 @@
return
RegisterSignal(our_plant, COMSIG_PLANT_ON_SLIP, PROC_REF(squash_plant))
RegisterSignal(our_plant, COMSIG_MOVABLE_IMPACT, PROC_REF(squash_plant))
RegisterSignal(our_plant, COMSIG_MOVABLE_IMPACT, PROC_REF(squash_plant_if_not_caught))
RegisterSignal(our_plant, COMSIG_ITEM_ATTACK_SELF, PROC_REF(squash_plant))
/*
@@ -239,6 +239,10 @@
qdel(our_plant)
/datum/plant_gene/trait/squash/proc/squash_plant_if_not_caught(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
if(!caught)
squash_plant(source, hit_atom)
/*
* Makes plant slippery, unless it has a grown-type trash. Then the trash gets slippery.
* Applies other trait effects (teleporting, etc) to the target by signal.
+1 -1
View File
@@ -91,7 +91,7 @@
/obj/item/paperplane/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(iscarbon(hit_atom) && HAS_TRAIT(hit_atom, TRAIT_PAPER_MASTER))
var/mob/living/carbon/hit_carbon = hit_atom
if(hit_carbon.can_catch_item(TRUE))
if(hit_carbon.can_catch_item(src, skip_throw_mode_check = TRUE))
hit_carbon.throw_mode_on(THROW_MODE_TOGGLE)
. = ..()
@@ -1012,7 +1012,7 @@
/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."
desc = "An advanced line reel which can be used speed up fishing or casually snag 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)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 75 KiB

+3 -1
View File
@@ -39,4 +39,6 @@ The sludgefish from the toilets can be used as a steady supply of cheap fish and
In a jiffy, you can scoop tadpoles from ponds with your bare hands, place them inside aquariums and quickly raise them into frogs.
The legendary fishing hat isn't just cosmetic. Space carps (as well as young lobstrosities and frogs) do truly fear those who wear it.
Have you ever heard a lobster or crab talk? Well, neither have I, but they say they're quite the fishy punsters.
You can get an experiscanner from science to perform fish scanning experiments, which can unlock more modules for the fishing portal, as well as fishing technology nodes (better equipment) for research.
You can get an experiscanner from science to perform fish scanning experiments, which can unlock more modules for the fishing portal, as well as fishing technology nodes (better equipment) for research.
If you have enough credits, you can buy a set of fishing lures from cargo. Each lure allows you to catch different species of fish and won't get consumed, however they need to be spun at intervals to work.
This may sound silly, but squids and their ink sacs can be used as weapons to temporarily blind people.
+11 -1
View File
@@ -1233,6 +1233,7 @@
#include "code\datums\components\spin2win.dm"
#include "code\datums\components\spinny.dm"
#include "code\datums\components\spirit_holding.dm"
#include "code\datums\components\splat.dm"
#include "code\datums\components\splattercasting.dm"
#include "code\datums\components\squashable.dm"
#include "code\datums\components\squeak.dm"
@@ -4165,7 +4166,16 @@
#include "code\modules\fishing\fish\chasm_detritus.dm"
#include "code\modules\fishing\fish\fish_evolution.dm"
#include "code\modules\fishing\fish\fish_traits.dm"
#include "code\modules\fishing\fish\fish_types.dm"
#include "code\modules\fishing\fish\types\air_space.dm"
#include "code\modules\fishing\fish\types\anadromous.dm"
#include "code\modules\fishing\fish\types\freshwater.dm"
#include "code\modules\fishing\fish\types\holographic.dm"
#include "code\modules\fishing\fish\types\mining.dm"
#include "code\modules\fishing\fish\types\ruins.dm"
#include "code\modules\fishing\fish\types\saltwater.dm"
#include "code\modules\fishing\fish\types\station.dm"
#include "code\modules\fishing\fish\types\syndicate.dm"
#include "code\modules\fishing\fish\types\tiziran.dm"
#include "code\modules\fishing\sources\_fish_source.dm"
#include "code\modules\fishing\sources\source_types.dm"
#include "code\modules\flufftext\Dreaming.dm"