Fishing expansion 2: one-year later boogaloo (#85252)

## About The Pull Request
This PR mainly adds more fish and more fishing spots to the game, while
refactoring a few aspects of the fishing minigame.

Listing out with the new fish:
- Arctic char: mainly filler content for the ice hole fishing spot
- Sockeye Salmon: ditto but also provides better fillets that boost the
quality of resulting food items when cooked or used in recipes
- Soulfish: joke content, found by the cursed spring ruin
- Skin Crab: also a joke found by the cursed spring
- Bump-Fish: filler for the sand fishing spot
- Burrower Crab: ditto, reusing a fish sprite I made last year
- Sand Surfer: ditto
- Three-Eyed Goldfish: It's a reference, doh
- Stingray: A modestly weaponizable fish (whoops I've forgot to set the
hit sounds), it possess a few traits that make it deliver bits of venom
each time you hit someone with it
- Swordfish: Huge-ass fish that may require two hands to wield (or not,
if the RNG wants to make it smaller). Stats-wise, it's more or less the
equivalent of the captain sabre, if not stronger (and more unwieldy due
to size and weight). Becomes weaker when dead. Also gives better quality
fillets.
- Chainsawfish: A mutation of the goldfish with some size, weight and
traits requirements, but can also be found on emagged fishing portals.
Stronger than the swordfish, it behaves sort of like a chainsaw, with
the similar tool behaviour and var values. Also becomes weaker when
dead.

As for the fishing spots, you can now fish on sand turfs, at the cursed
springs or on ice. Rivers/jungle water now has its own fishing spot
datum, and no longer uses the generic fishing portal one. To fish on
ice, you first have to carve a hole with a pick or a shovel.

I've also refactored the fish "AI" hardcoded stuff used in the fishing
minigame into their own datums, which let me add a few fancier ways to
how the fish moves during the minigame (i.e. the soulfish moving at 1
FPS or the chainsawfish getting faster and faster).

As for the sword and chainsaw fish, their potential strength is balanced
out by the need of keeping them alive, as well as the potential
cumbersomeness, two-handed wielding and potential slowdown from the
excessive weight of the fish (Thank you Big Slappy for the inspiration).

Other minor changes include: Pufferfish giving better quality fillets
(too bad they're poisonous, I'll go and make a skillchip to let cooks
safely separate the poisonous liver from the fillets); McGill The
lawyer's goldfish) having a 15% of being three-eyed; the aforementioned
slowdown from fish weight and two-handed carry from fish size; a couple
new fish icons (the ones that hint you on what you're trying to catch)
for the fishing minigame; a few adjustments to prevent self-reproducing
fish from ignoring the population cap and let fish with a stable
population of 1 to crossbreed (also gotta make a different PR to let it
happen rarely without the crossbreeding trait).

This PR is still a WIP, gotta test it several times.

## Why It's Good For The Game
Fishing is something I've been working on for about a year now, but
there are still a few places where it's kinda lackluster, like there's
not enough diverse fishing spots or useful fish (I'll be working on a
separate PR to make the logistic of a carrying a fish around without
letting it die a tad easier). Also, look at these sprites:

![new
fish](https://github.com/user-attachments/assets/49c55065-32b0-44ec-ac12-74b3a1763f50)

Can you guess which is which?

## Changelog

For the sake of not dumping players with niche information 90% of the
players won't understand, I'll keep the CL pretty generic

🆑
add: Added twelve new fish types to the game. Some are cool, other are
not, some come with their own special traits and some are straight-up
weapons.
add: Added more fishing spots to the game. Sand, ice, rivers, the cursed
spring...
balance: A few fish like salmon, swordfish and pufferfish (poisonous
btw) now give better quality fillets when butchered, which can improve
the quality of food that uses them even further.
balance: Excessive fish weight will make the fish slowier to carry,
while excessive size may make it require two hands.
balance: Adjusted size, weight and cooldowns of several fish, for the
better.
/🆑
This commit is contained in:
Ghom
2024-08-20 06:46:11 +02:00
committed by GitHub
parent 5a39dd9aac
commit 659e6f0ca2
41 changed files with 1389 additions and 298 deletions
+6 -2
View File
@@ -399,9 +399,13 @@
new /obj/item/aquarium_prop/sand(src)
new /obj/item/aquarium_prop/seaweed(src)
new /obj/item/fish/goldfish/gill(src)
if(prob(85))
new /obj/item/fish/goldfish/gill(src)
reagents.add_reagent(/datum/reagent/consumable/nutriment, 2)
else
new /obj/item/fish/three_eyes/gill(src)
reagents.add_reagent(/datum/reagent/toxin/mutagen, 2) //three eyes goldfish feed on mutagen.
reagents.add_reagent(/datum/reagent/consumable/nutriment, 2)
/obj/structure/aquarium/prefilled
anchored = TRUE
@@ -58,7 +58,7 @@
name = "ominous fish case"
/obj/item/storage/fish_case/syndicate/get_fish_type()
return pick(/obj/item/fish/donkfish, /obj/item/fish/emulsijack, /obj/item/fish/jumpercable)
return pick(/obj/item/fish/donkfish, /obj/item/fish/emulsijack, /obj/item/fish/jumpercable, /obj/item/fish/chainsawfish)
/obj/item/storage/fish_case/tiziran
name = "imported fish case"
+18
View File
@@ -61,3 +61,21 @@
bait_type = /obj/item/food/bait/doughball/synthetic/super
uses_left = 12
/// 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)
+148 -42
View File
@@ -6,17 +6,19 @@
icon_state = "bugfish"
lefthand_file = 'icons/mob/inhands/fish_lefthand.dmi'
righthand_file = 'icons/mob/inhands/fish_righthand.dmi'
inhand_icon_state = "fish_normal"
force = 6
throwforce = 6
throw_range = 8
attack_verb_continuous = list("slaps", "whacks")
attack_verb_simple = list("slap", "whack")
hitsound = 'sound/weapons/slap.ogg'
///The grind results of the fish. They scale with the weight of the fish.
grind_results = list(/datum/reagent/blood = 5, /datum/reagent/consumable/liquidgibs = 5)
obj_flags = UNIQUE_RENAME
item_flags = IMMUTABLE_SLOW|SLOWS_WHILE_IN_HAND
/// Resulting width of aquarium visual icon - default size of "fish_greyscale" state
var/sprite_width = 3
var/sprite_width = 5
/// Resulting height of aquarium visual icon - default size of "fish_greyscale" state
var/sprite_height = 3
@@ -93,8 +95,8 @@
*/
var/list/fish_traits = list()
/// Fishing behaviour
var/fish_ai_type = FISH_AI_DUMB
/// path to datums that dictate how the fish moves during the fishing minigame
var/fish_movement_type = /datum/fish_movement
/// Base additive modifier to fishing difficulty
var/fishing_difficulty_modifier = 0
@@ -121,6 +123,9 @@
/// Average weight for this fish type in grams
var/average_weight = 1000
///The general deviation from the average weight and size this fish has in the wild
var/weight_size_deviation = 0.2
/// When outside of an aquarium, these gases that are checked (as well as pressure and temp) to assert if the environment is safe or not.
var/list/safe_air_limits = list(
/datum/gas/oxygen = list(12, 100),
@@ -203,7 +208,7 @@
. += span_notice("It weighs [weight] g.")
///Randomizes weight and size.
/obj/item/fish/proc/randomize_size_and_weight(base_size = average_size, base_weight = average_weight, deviation = 0.2)
/obj/item/fish/proc/randomize_size_and_weight(base_size = average_size, base_weight = average_weight, deviation = weight_size_deviation)
var/size_deviation = 0.2 * base_size
var/new_size = round(clamp(gaussian(base_size, size_deviation), average_size * 1/MAX_FISH_DEVIATION_COEFF, average_size * MAX_FISH_DEVIATION_COEFF))
@@ -214,38 +219,125 @@
///Updates weight and size, along with weight class, number of fillets you can get and grind results.
/obj/item/fish/proc/update_size_and_weight(new_size = average_size, new_weight = average_weight)
if(size && fillet_type)
RemoveElement(/datum/element/processable, TOOL_KNIFE, fillet_type, num_fillets, 0.5 SECONDS, screentip_verb = "Cut")
SEND_SIGNAL(src, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, new_size, new_weight)
if(size)
if(fillet_type)
RemoveElement(/datum/element/processable, TOOL_KNIFE, fillet_type, num_fillets, 0.5 SECONDS * num_fillets, screentip_verb = "Cut")
if(size > FISH_SIZE_TWO_HANDS_REQUIRED)
qdel(GetComponent(/datum/component/two_handed))
size = new_size
var/init_icon_state = initial(inhand_icon_state)
switch(size)
if(0 to FISH_SIZE_TINY_MAX)
update_weight_class(WEIGHT_CLASS_TINY)
inhand_icon_state = "fish_small"
if(!init_icon_state)
inhand_icon_state = "fish_small"
if(FISH_SIZE_TINY_MAX to FISH_SIZE_SMALL_MAX)
inhand_icon_state = "fish_small"
if(!init_icon_state)
inhand_icon_state = "fish_small"
update_weight_class(WEIGHT_CLASS_SMALL)
if(FISH_SIZE_SMALL_MAX to FISH_SIZE_NORMAL_MAX)
inhand_icon_state = "fish_normal"
if(!init_icon_state)
inhand_icon_state = "fish_normal"
update_weight_class(WEIGHT_CLASS_NORMAL)
if(FISH_SIZE_NORMAL_MAX to FISH_SIZE_BULKY_MAX)
inhand_icon_state = "fish_bulky"
if(!init_icon_state)
inhand_icon_state = "fish_bulky"
update_weight_class(WEIGHT_CLASS_BULKY)
if(FISH_SIZE_BULKY_MAX to INFINITY)
inhand_icon_state = "fish_huge"
if(FISH_SIZE_BULKY_MAX to FISH_SIZE_HUGE_MAX)
if(!init_icon_state)
inhand_icon_state = "fish_huge"
update_weight_class(WEIGHT_CLASS_HUGE)
if(FISH_SIZE_HUGE_MAX to INFINITY)
if(!init_icon_state)
inhand_icon_state = "fish_huge"
update_weight_class(WEIGHT_CLASS_GIGANTIC)
if(size > FISH_SIZE_TWO_HANDS_REQUIRED)
inhand_icon_state = "[inhand_icon_state]_wielded"
AddComponent(/datum/component/two_handed, require_twohands = TRUE)
if(fillet_type)
var/init_fillets = initial(num_fillets)
var/amount = max(round(init_fillets * size / FISH_FILLET_NUMBER_SIZE_DIVISOR, 1), 1)
num_fillets = amount
AddElement(/datum/element/processable, TOOL_KNIFE, fillet_type, num_fillets, 0.5 SECONDS, screentip_verb = "Cut")
AddElement(/datum/element/processable, TOOL_KNIFE, fillet_type, num_fillets, 0.5 SECONDS * num_fillets, screentip_verb = "Cut")
if(weight)
for(var/reagent_type in grind_results)
grind_results[reagent_type] /= FLOOR(weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR, 0.1)
weight = new_weight
if(weight >= FISH_WEIGHT_SLOWDOWN)
slowdown = round(((weight/FISH_WEIGHT_SLOWDOWN_DIVISOR)**FISH_WEIGHT_SLOWDOWN_EXPONENT)-1.3, 0.1)
drag_slowdown = round(slowdown * 0.5, 1)
else
slowdown = 0
drag_slowdown = 0
if(ismob(loc))
var/mob/mob = loc
mob.update_equipment_speed_mods()
for(var/reagent_type in grind_results)
grind_results[reagent_type] *= FLOOR(weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR, 0.1)
update_fish_force()
///Reset weapon-related variables of this items and recalculates those values based on the fish weight and size.
/obj/item/fish/proc/update_fish_force()
force = initial(force)
throwforce = initial(throwforce)
throw_range = initial(throw_range)
demolition_mod = initial(demolition_mod)
attack_verb_continuous = initial(attack_verb_continuous)
attack_verb_simple = initial(attack_verb_simple)
hitsound = initial(hitsound)
damtype = initial(damtype)
attack_speed = initial(attack_speed)
block_chance = initial(block_chance)
armour_penetration = initial(armour_penetration)
wound_bonus = initial(wound_bonus)
bare_wound_bonus = initial(bare_wound_bonus)
toolspeed = initial(toolspeed)
var/weight_rank = max(round(1 + log(2, weight/FISH_WEIGHT_FORCE_DIVISOR), 1), 1)
throw_range -= weight_rank
get_force_rank()
var/bonus_malus = weight_rank - w_class
if(bonus_malus)
calculate_fish_force_bonus(bonus_malus)
throwforce = force
SEND_SIGNAL(src, COMSIG_FISH_FORCE_UPDATED, weight_rank, bonus_malus)
///A proc that makes the fish slightly stronger or weaker if there's a noticeable discrepancy between size and weight.
/obj/item/fish/proc/calculate_fish_force_bonus(bonus_malus)
demolition_mod += bonus_malus * 0.1
attack_speed += bonus_malus * 0.1
force = round(force * (1 + bonus_malus * 0.1), 0.1)
/obj/item/fish/proc/get_force_rank()
switch(w_class)
if(WEIGHT_CLASS_TINY)
force -= 3
attack_speed -= 0.1 SECONDS
if(WEIGHT_CLASS_NORMAL)
force += 2
if(WEIGHT_CLASS_BULKY)
force += 5
attack_speed += 0.1 SECONDS
if(WEIGHT_CLASS_HUGE)
force += 9
attack_speed += 0.2 SECONDS
demolition_mod += 0.2
if(WEIGHT_CLASS_GIGANTIC)
force += 13
attack_speed += 0.4 SECONDS
demolition_mod += 0.4
/**
* This proc has fish_traits list populated with fish_traits paths from three different lists:
* traits from x_traits and y_traits are compared, and inserted if conditions are met;
@@ -370,7 +462,7 @@
SEND_SIGNAL(src, COMSIG_FISH_LIFE, seconds_per_tick)
/obj/item/fish/proc/set_status(new_status)
/obj/item/fish/proc/set_status(new_status, silent = FALSE)
if(status == new_status)
return
switch(new_status)
@@ -384,12 +476,14 @@
status = FISH_DEAD
STOP_PROCESSING(SSobj, src)
stop_flopping()
var/message = span_notice(replacetext(death_text, "%SRC", "[src]"))
if(isaquarium(loc))
loc.visible_message(message)
else
visible_message(message)
if(!silent)
var/message = span_notice(replacetext(death_text, "%SRC", "[src]"))
if(isaquarium(loc))
loc.visible_message(message)
else
visible_message(message)
update_appearance()
update_fish_force()
SEND_SIGNAL(src, COMSIG_FISH_STATUS_CHANGED)
/obj/item/fish/expose_reagents(list/reagents, datum/reagents/source, methods = TOUCH, volume_modifier = 1, show_message = TRUE)
@@ -476,7 +570,7 @@
return FALSE
if(!being_targeted && length(aquarium.get_fishes()) >= AQUARIUM_MAX_BREEDING_POPULATION)
return FALSE
return aquarium.allow_breeding && health >= initial(health) * 0.8 && stable_population > 1 && world.time >= breeding_wait
return aquarium.allow_breeding && health >= initial(health) * 0.8 && stable_population >= 1 && world.time >= breeding_wait
/obj/item/fish/proc/try_to_reproduce()
var/obj/structure/aquarium/aquarium = loc
@@ -511,36 +605,48 @@
second_fish = other_fish
break
if(!second_fish && !HAS_TRAIT(src, TRAIT_FISH_SELF_REPRODUCE))
return FALSE
if(!second_fish)
if(!HAS_TRAIT(src, TRAIT_FISH_SELF_REPRODUCE))
return FALSE
if(length(aquarium.tracked_fish_by_type[type]) >= stable_population)
return FALSE
if(PERFORM_ALL_TESTS(fish_breeding) && second_fish && !length(evolution_types))
return create_offspring(second_fish.type, second_fish)
var/chosen_type
var/datum/fish_evolution/chosen_evolution
if(PERFORM_ALL_TESTS(fish_breeding) && second_fish && !length(evolution_types))
chosen_type = second_fish.type
else
var/list/possible_evolutions = list()
for(var/evolution_type in evolution_types)
var/list/possible_evolutions = list()
for(var/evolution_type in evolution_types)
var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evolution_type]
if(evolution.check_conditions(src, second_fish, aquarium))
possible_evolutions += evolution
if(second_fish?.evolution_types)
var/secondary_evolutions = (second_fish.evolution_types - evolution_types)
for(var/evolution_type in secondary_evolutions)
var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evolution_type]
if(evolution.check_conditions(src, second_fish, aquarium))
if(evolution.check_conditions(second_fish, src, aquarium))
possible_evolutions += evolution
if(second_fish?.evolution_types)
var/secondary_evolutions = (second_fish.evolution_types - evolution_types)
for(var/evolution_type in secondary_evolutions)
var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evolution_type]
if(evolution.check_conditions(second_fish, src, aquarium))
possible_evolutions += evolution
if(length(possible_evolutions))
chosen_evolution = pick(possible_evolutions)
chosen_type = chosen_evolution.new_fish_type
else if(second_fish)
if(length(aquarium.tracked_fish_by_type[type]) >= stable_population)
if(length(possible_evolutions))
chosen_evolution = pick(possible_evolutions)
chosen_type = chosen_evolution.new_fish_type
else if(second_fish)
var/recessive = HAS_TRAIT(src, TRAIT_FISH_RECESSIVE)
var/recessive_partner = HAS_TRAIT(second_fish, TRAIT_FISH_RECESSIVE)
if(length(aquarium.tracked_fish_by_type[type]) >= stable_population)
if(recessive_partner && !recessive)
return FALSE
chosen_type = second_fish.type
else
if(recessive && !recessive_partner)
chosen_type = second_fish.type
else if(recessive_partner && !recessive)
chosen_type = type
else
chosen_type = pick(second_fish.type, type)
else
chosen_type = type
else
chosen_type = type
return create_offspring(chosen_type, second_fish, chosen_evolution)
+20 -1
View File
@@ -78,6 +78,7 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio
/datum/fish_evolution/purple_sludgefish
probability = 5
new_fish_type = /obj/item/fish/sludgefish/purple
new_traits = list(/datum/fish_trait/recessive)
removed_traits = list(/datum/fish_trait/no_mating)
/datum/fish_evolution/mastodon
@@ -85,7 +86,7 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio
probability = 40
new_fish_type = /obj/item/fish/mastodon
new_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/amphibious, /datum/fish_trait/predator, /datum/fish_trait/aggressive)
conditions_note = "The fish (and its mate) need to be unusually big both in size and weight."
conditions_note = "The fish (and its mate) needs to be unusually big both in size and weight."
/datum/fish_evolution/mastodon/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium)
if((source.size < 120 || source.weight < 3000) || (mate && (mate.size < 120 || mate.weight < 3000)))
@@ -103,3 +104,21 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio
new_fish_type = /obj/item/fish/chasm_crab/ice
required_temperature_min = MIN_AQUARIUM_TEMP+9
required_temperature_max = MIN_AQUARIUM_TEMP+10
/datum/fish_evolution/three_eyes
name = "Three-eyed Goldfish"
probability = 3
new_fish_type = /obj/item/fish/three_eyes
new_traits = list(/datum/fish_trait/recessive)
/datum/fish_evolution/chainsawfish
name = "Chainsawfish"
probability = 30
new_fish_type = /obj/item/fish/chainsawfish
new_traits = list(/datum/fish_trait/predator, /datum/fish_trait/aggressive)
conditions_note = "The fish needs to be unusually big and aggressive"
/datum/fish_evolution/chainsawfish/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium)
if(source.size >= 60 && source.size >= 1000 && (/datum/fish_trait/aggressive in source.fish_traits))
return ..()
return FALSE
+130 -29
View File
@@ -37,6 +37,8 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
var/list/fish_whitelist
/// Depending on the value, fish with trait will be reported as more or less difficult in the catalog.
var/added_difficulty = 0
/// Reagents added to the fish when gained
var/list/reagents_to_add
/// Difficulty modifier from this mod, needs to return a list with two values
/datum/fish_trait/proc/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman)
@@ -54,7 +56,11 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/// Applies some special qualities to the fish that has been spawned
/datum/fish_trait/proc/apply_to_fish(obj/item/fish/fish)
return
SHOULD_CALL_PARENT(TRUE)
if(reagents_to_add)
for(var/reagent in reagents_to_add)
add_to_reagents(fish, reagent, reagents_to_add[reagent])
RegisterSignal(fish, COMSIG_ATOM_PROCESSED, PROC_REF(process_reagents))
/// Applies some special qualities to basic mobs generated by fish (i.e. chasm chrab --> young lobstrosity --> lobstrosity).
/datum/fish_trait/proc/apply_to_mob(mob/living/basic/mob)
@@ -79,6 +85,37 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
fish.grind_results.Insert(1, reagent_type)
fish.grind_results[reagent_type] = amount
/// Proc that handles adding reagents from the trait to the fillets from butchered fish.
/datum/fish_trait/proc/process_reagents(obj/item/fish/source, mob/living/user, obj/item/process_item, list/results)
SIGNAL_HANDLER
var/results_with_reagents = 0
for(var/atom/result as anything in results)
if(result.reagents)
results_with_reagents++
if(!results_with_reagents)
return
for(var/reagent in reagents_to_add)
var/amount = round(source.grind_results[reagent] / results_with_reagents, 0.1)
for(var/atom/result as anything in results)
result.reagents?.add_reagent(reagent, amount)
/// Proc that adds or changes the venomous when the fish size and/or weight are updated
/datum/fish_trait/proc/add_venom(obj/item/fish/source, venom_path, new_weight, mult = 0.25)
if(source.size)
var/old_amount = max(round((source.weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR) * mult, 0.1), mult)
source.RemoveElement(/datum/element/venomous, venom_path, old_amount)
var/new_amount = max(round((new_weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR) * mult, 0.1), mult)
source.AddElement(/datum/element/venomous, venom_path, new_amount)
/// Proc that changes the venomous element based on if the fish is alive or dead (basically dead fish are weaker).
/datum/fish_trait/proc/change_venom_on_death(obj/item/fish/source, venom_path, live_mult, dead_mult)
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)
/datum/fish_trait/wary
name = "Wary"
catalog_description = "This fish will avoid visible fish lines, cloaked line recommended."
@@ -110,7 +147,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
return
if(HAS_TRAIT(rod.bait, TRAIT_OMNI_BAIT))
return
if(HAS_TRAIT(rod.bait, TRAIT_GOOD_QUALITY_BAIT) || HAS_TRAIT(rod.bait, TRAIT_GREAT_QUALITY_BAIT))
if(!HAS_TRAIT(rod.bait, TRAIT_GOOD_QUALITY_BAIT) && !HAS_TRAIT(rod.bait, TRAIT_GREAT_QUALITY_BAIT))
.[MULTIPLICATIVE_FISHING_MOD] = 0
@@ -126,6 +163,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
.[MULTIPLICATIVE_FISHING_MOD] = 0
/datum/fish_trait/nocturnal/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(check_light))
/datum/fish_trait/nocturnal/proc/check_light(obj/item/fish/source, seconds_per_tick)
@@ -170,7 +208,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
mob.obj_damage *= 1.3
/datum/fish_trait/heavy/minigame_mod(obj/item/fishing_rod/rod, mob/fisherman, datum/fishing_challenge/minigame)
minigame.fish_idle_velocity -= 10
minigame.mover.fish_idle_velocity -= 10
/datum/fish_trait/carnivore
name = "Carnivore"
@@ -211,6 +249,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "This fish emits an invisible toxin that emulsifies other fish for it to feed on."
/datum/fish_trait/emulsijack/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(emulsify))
ADD_TRAIT(fish, TRAIT_RESIST_EMULSIFY, FISH_TRAIT_DATUM)
@@ -250,6 +289,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
incompatible_traits = list(/datum/fish_trait/vegan)
/datum/fish_trait/necrophage/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(eat_dead_fishes))
/datum/fish_trait/necrophage/proc/eat_dead_fishes(obj/item/fish/source, seconds_per_tick)
@@ -269,6 +309,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
diff_traits_inheritability = 25
/datum/fish_trait/parthenogenesis/apply_to_fish(obj/item/fish/fish)
. = ..()
ADD_TRAIT(fish, TRAIT_FISH_SELF_REPRODUCE, FISH_TRAIT_DATUM)
/**
@@ -282,8 +323,19 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
incompatible_traits = list(/datum/fish_trait/crossbreeder)
/datum/fish_trait/no_mating/apply_to_fish(obj/item/fish/fish)
. = ..()
ADD_TRAIT(fish, TRAIT_FISH_NO_MATING, FISH_TRAIT_DATUM)
///Prevent offsprings of fish with this trait from being of the same type (unless self-mating or the partner also has the trait)
/datum/fish_trait/recessive
name = "Recessive"
catalog_description = "If crossbred, offsprings will always be of the mate species, unless it also possess the trait."
diff_traits_inheritability = 0
/datum/fish_trait/no_mating/apply_to_fish(obj/item/fish/fish)
. = ..()
ADD_TRAIT(fish, TRAIT_FISH_RECESSIVE, FISH_TRAIT_DATUM)
/datum/fish_trait/revival
diff_traits_inheritability = 15
name = "Self-Revival"
@@ -291,6 +343,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
spontaneous_manifest_types = list(/obj/item/fish/boned = 100, /obj/item/fish/mastodon = 100)
/datum/fish_trait/revival/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_FISH_STATUS_CHANGED, PROC_REF(check_status))
/datum/fish_trait/revival/proc/check_status(obj/item/fish/source)
@@ -319,6 +372,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
incompatible_traits = list(/datum/fish_trait/vegan)
/datum/fish_trait/predator/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(eat_fishes))
/datum/fish_trait/predator/proc/eat_fishes(obj/item/fish/source, seconds_per_tick)
@@ -327,7 +381,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
return
var/obj/structure/aquarium/aquarium = source.loc
for(var/obj/item/fish/victim in aquarium.get_fishes(TRUE, source))
if(victim.size < source.size * 0.75) // It's a big fish eat small fish world
if(victim.size < source.size * 0.7) // It's a big fish eat small fish world
continue
if(victim.status != FISH_ALIVE || victim == source || HAS_TRAIT(victim, TRAIT_YUCKY_FISH) || SPT_PROB(80, seconds_per_tick))
continue
@@ -337,31 +391,35 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/datum/fish_trait/yucky
name = "Yucky"
catalog_description = "This fish tastes so repulsive, other fishes won't try to eat it."
reagents_to_add = list(/datum/reagent/yuck = 3)
/datum/fish_trait/yucky/apply_to_fish(obj/item/fish/fish)
RegisterSignal(fish, COMSIG_ATOM_PROCESSED, PROC_REF(add_yuck))
. = ..()
ADD_TRAIT(fish, TRAIT_YUCKY_FISH, FISH_TRAIT_DATUM)
add_to_reagents(fish, /datum/reagent/yuck, 3)
/datum/fish_trait/yucky/proc/add_yuck(obj/item/fish/source, mob/living/user, obj/item/process_item, list/results)
var/amount = source.grind_results[/datum/reagent/yuck] / length(results)
for(var/atom/result as anything in results)
result.reagents?.add_reagent(/datum/reagent/yuck, amount)
/datum/fish_trait/toxic
name = "Toxic"
catalog_description = "This fish contains toxins in its liver. Feeding it to predatory fishes or people is not reccomended."
catalog_description = "This fish contains toxins. Feeding it to predatory fishes or people is not reccomended."
diff_traits_inheritability = 25
reagents_to_add = list(/datum/reagent/toxin/tetrodotoxin = 2.5)
/datum/fish_trait/toxic/apply_to_fish(obj/item/fish/fish)
RegisterSignal(fish, COMSIG_ATOM_PROCESSED, PROC_REF(add_toxin))
. = ..()
RegisterSignal(fish, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, PROC_REF(make_venomous))
RegisterSignal(fish, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_status_change))
RegisterSignal(fish, COMSIG_FISH_EATEN_BY_OTHER_FISH, PROC_REF(on_eaten))
add_to_reagents(fish, /datum/reagent/toxin/tetrodotoxin, 2.5)
/datum/fish_trait/toxic/proc/add_toxin(obj/item/fish/source, mob/living/user, obj/item/process_item, list/results)
var/amount = source.grind_results[ /datum/reagent/toxin/tetrodotoxin] / length(results)
for(var/atom/result as anything in results)
result.reagents?.add_reagent(/datum/reagent/toxin/tetrodotoxin, amount)
/datum/fish_trait/toxic/proc/make_venomous(obj/item/fish/source, new_size, new_weight)
SIGNAL_HANDLER
if(!HAS_TRAIT(source, TRAIT_FISH_STINGER))
return
add_venom(source, /datum/reagent/toxin/tetrodotoxin, new_weight, mult = source.status == FISH_DEAD ? 0.1 : 0.25)
/datum/fish_trait/toxic/proc/on_status_change(obj/item/fish/source)
SIGNAL_HANDLER
if(!HAS_TRAIT(source, TRAIT_FISH_STINGER))
return
change_venom_on_death(source, /datum/reagent/toxin/tetrodotoxin, 0.25, 0.1)
/datum/fish_trait/toxic/proc/on_eaten(obj/item/fish/source, obj/item/fish/predator)
if(HAS_TRAIT(predator, TRAIT_FISH_TOXIN_IMMUNE))
@@ -388,6 +446,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
diff_traits_inheritability = 40
/datum/fish_trait/toxin_immunity/apply_to_fish(obj/item/fish/fish)
. = ..()
ADD_TRAIT(fish, TRAIT_FISH_TOXIN_IMMUNE, FISH_TRAIT_DATUM)
/datum/fish_trait/crossbreeder
@@ -398,6 +457,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
incompatible_traits = list(/datum/fish_trait/no_mating)
/datum/fish_trait/crossbreeder/apply_to_fish(obj/item/fish/fish)
. = ..()
ADD_TRAIT(fish, TRAIT_FISH_CROSSBREEDER, FISH_TRAIT_DATUM)
/datum/fish_trait/aggressive
@@ -407,6 +467,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "This fish is aggressively territorial, and may attack fish that come close to it."
/datum/fish_trait/aggressive/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(try_attack_fish))
/datum/fish_trait/aggressive/proc/try_attack_fish(obj/item/fish/source, seconds_per_tick)
@@ -431,6 +492,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
added_difficulty = 5
/datum/fish_trait/lubed/apply_to_fish(obj/item/fish/fish)
. = ..()
fish.AddComponent(/datum/component/slippery, 8 SECONDS, SLIDE|GALOSHES_DONT_HELP)
/datum/fish_trait/lubed/apply_to_mob(mob/living/basic/mob)
@@ -448,6 +510,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "This fish has developed a primitive adaptation to life on both land and water."
/datum/fish_trait/amphibious/apply_to_fish(obj/item/fish/fish)
. = ..()
ADD_TRAIT(fish, TRAIT_FISH_AMPHIBIOUS, FISH_TRAIT_DATUM)
if(fish.required_fluid_type == AQUARIUM_FLUID_AIR)
fish.required_fluid_type = AQUARIUM_FLUID_FRESHWATER
@@ -460,6 +523,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
incompatible_traits = list(/datum/fish_trait/predator, /datum/fish_trait/necrophage)
/datum/fish_trait/mixotroph/apply_to_fish(obj/item/fish/fish)
. = ..()
ADD_TRAIT(fish, TRAIT_FISH_NO_HUNGER, FISH_TRAIT_DATUM)
/datum/fish_trait/antigrav
@@ -467,12 +531,13 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
inheritability = 75
diff_traits_inheritability = 25
catalog_description = "This fish will invert the gravity of the bait at random. May fall upward outside after being caught."
added_difficulty = 15
added_difficulty = 20
/datum/fish_trait/antigrav/minigame_mod(obj/item/fishing_rod/rod, mob/fisherman, datum/fishing_challenge/minigame)
minigame.special_effects |= FISHING_MINIGAME_RULE_ANTIGRAV
/datum/fish_trait/antigrav/apply_to_fish(obj/item/fish/fish)
. = ..()
fish.AddElement(/datum/element/forced_gravity, NEGATIVE_GRAVITY)
/datum/fish_trait/antigrav/apply_to_mob(mob/living/basic/mob)
@@ -489,6 +554,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "This fish tends to die of stress when forced to be around too many other fish."
/datum/fish_trait/anxiety/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(on_fish_life))
///signal sent when the anxiety fish is fed, killing it if sharing contents with too many fish.
@@ -511,24 +577,18 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "This fish is electroreceptive, and will generate electric fields. Can be harnessed inside a bioelectric generator."
/datum/fish_trait/electrogenesis/apply_to_fish(obj/item/fish/fish)
. = ..()
ADD_TRAIT(fish, TRAIT_FISH_ELECTROGENESIS, FISH_TRAIT_DATUM)
RegisterSignal(fish, COMSIG_ITEM_ATTACK, PROC_REF(on_item_attack))
RegisterSignal(fish, COMSIG_FISH_FORCE_UPDATED, PROC_REF(on_force_updated))
/datum/fish_trait/electrogenesis/proc/on_item_attack(obj/item/fish/fish, mob/living/target, mob/living/user)
/datum/fish_trait/electrogenesis/proc/on_force_updated(obj/item/fish/fish, weight_rank, bonus_or_malus)
SIGNAL_HANDLER
if(fish.status == FISH_ALIVE)
fish.force = 16
fish.force += 10 - fish.w_class
fish.damtype = BURN
fish.attack_verb_continuous = list("shocks", "zaps")
fish.attack_verb_simple = list("shock", "zap")
fish.hitsound = 'sound/effects/sparks4.ogg'
else
fish.force = fish::force
fish.damtype = fish::damtype
fish.attack_verb_continuous = fish::attack_verb_continuous
fish.attack_verb_simple = fish::attack_verb_simple
fish.hitsound = fish::hitsound
/datum/fish_trait/electrogenesis/apply_to_mob(mob/living/basic/mob)
. = ..()
@@ -546,3 +606,44 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/datum/fish_trait/stunted/apply_to_mob(mob/living/basic/mob)
. = ..()
qdel(mob.GetComponent(/datum/component/growth_and_differentiation))
/datum/fish_trait/stinger
name = "Stinger"
inheritability = 80
diff_traits_inheritability = 35
catalog_description = "This fish is equipped with a sharp stringer or bill capable of delivering damage and toxins."
spontaneous_manifest_types = list(/obj/item/fish/stingray = 100, /obj/item/fish/swordfish = 100, /obj/item/fish/chainsawfish = 100)
/datum/fish_trait/stinger/apply_to_fish(obj/item/fish/fish)
. = ..()
ADD_TRAIT(fish, TRAIT_FISH_STINGER, FISH_TRAIT_DATUM)
RegisterSignal(fish, COMSIG_FISH_FORCE_UPDATED, PROC_REF(on_force_updated))
/datum/fish_trait/stinger/proc/on_force_updated(obj/item/fish/fish, weight_rank, bonus_or_malus)
SIGNAL_HANDLER
fish.force += 1 + fish.w_class + bonus_or_malus
/datum/fish_trait/toxic_barbs
name = "Toxic Barbs"
catalog_description = "This fish' stinger, bill or otherwise, is coated with simple, yet effetive venom."
spontaneous_manifest_types = list(/obj/item/fish/stingray = 35)
/datum/fish_trait/toxic_barbs/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, PROC_REF(make_venomous))
RegisterSignal(fish, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_status_change))
/datum/fish_trait/toxic_barbs/proc/make_venomous(obj/item/fish/source, new_size, new_weight)
SIGNAL_HANDLER
if(!HAS_TRAIT(source, TRAIT_FISH_STINGER))
///Remove the trait from the fish so it doesn't show on the analyzer as it doesn't do anything on stingerless ones.
source.fish_traits -= type
UnregisterSignal(source, list(COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, COMSIG_FISH_STATUS_CHANGED))
return
add_venom(source, /datum/reagent/toxin/venom, new_weight, mult = source.status == FISH_DEAD ? 0.3 : 0.7)
/datum/fish_trait/toxic_barbs/proc/on_status_change(obj/item/fish/source)
SIGNAL_HANDLER
if(!HAS_TRAIT(source, TRAIT_FISH_STINGER))
return
change_venom_on_death(source, /datum/reagent/toxin/venom, 0.7, 0.3)
+430 -18
View File
@@ -10,9 +10,12 @@
stable_population = 3
average_size = 30
average_weight = 500
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/three_eyes, /obj/item/fish/three_eyes/gill)
/obj/item/fish/goldfish/gill
name = "McGill"
@@ -21,6 +24,8 @@
random_case_rarity = FISH_RARITY_NOPE
show_in_catalog = FALSE
beauty = FISH_BEAUTY_GOOD
compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/three_eyes)
fish_traits = list(/datum/fish_trait/recessive)
/obj/item/fish/angelfish
name = "angelfish"
@@ -63,13 +68,14 @@
required_temperature_max = MIN_AQUARIUM_TEMP+28
/obj/item/fish/catfish
name = "cory 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"
dedicated_in_aquarium_icon_state = "fish_greyscale"
aquarium_vc_color = "#907420"
average_size = 100
average_weight = 2000
average_size = 80
average_weight = 1600
weight_size_deviation = 0.35
stable_population = 3
favorite_bait = list(
list(
@@ -107,7 +113,7 @@
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)
/obj/item/fish/tadpole/set_status(new_status, silent = FALSE)
. = ..()
if(status == FISH_DEAD)
del_timerid = QDEL_IN_STOPPABLE(src, 12 SECONDS)
@@ -159,6 +165,7 @@
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
@@ -200,7 +207,7 @@
average_weight = 500
stable_population = 3
disliked_bait = list(/obj/item/food/bait/worm, /obj/item/food/bait/doughball)
fish_ai_type = FISH_AI_ZIPPY
fish_movement_type = /datum/fish_movement/zippy
required_temperature_min = MIN_AQUARIUM_TEMP+23
required_temperature_max = MIN_AQUARIUM_TEMP+28
@@ -216,10 +223,10 @@
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."
@@ -230,8 +237,8 @@
source_height = 21
sprite_width = 8
sprite_height = 8
average_size = 100
average_weight = 1500
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.
@@ -246,8 +253,8 @@
required_fluid_type = AQUARIUM_FLUID_SALTWATER
stable_population = 2
fillet_type = /obj/item/food/fishmeat/moonfish
average_size = 100
average_weight = 2000
average_size = 60
average_weight = 1000
required_temperature_min = MIN_AQUARIUM_TEMP+20
required_temperature_max = MIN_AQUARIUM_TEMP+30
beauty = FISH_BEAUTY_GOOD
@@ -271,6 +278,7 @@
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
@@ -285,10 +293,13 @@
dedicated_in_aquarium_icon_state = "armorfish_small"
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_ai_type = FISH_AI_SLOW
fish_movement_type = /datum/fish_movement/slow
required_temperature_min = MIN_AQUARIUM_TEMP+10
required_temperature_max = MIN_AQUARIUM_TEMP+32
@@ -456,7 +467,7 @@
fish_traits = list(/datum/fish_trait/necrophage)
required_temperature_min = MIN_AQUARIUM_TEMP+15
required_temperature_max = MIN_AQUARIUM_TEMP+35
fish_ai_type = FISH_AI_ZIPPY
fish_movement_type = /datum/fish_movement/zippy
favorite_bait = list(
list(
"Type" = "Foodtype",
@@ -534,7 +545,7 @@
dedicated_in_aquarium_icon_state = "bonemass_small"
sprite_width = 10
sprite_height = 7
fish_ai_type = FISH_AI_ZIPPY
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
@@ -563,16 +574,16 @@
sprite_height = 7
show_in_catalog = FALSE
random_case_rarity = FISH_RARITY_NOPE
fishing_difficulty_modifier = 5
fishing_difficulty_modifier = 30
required_fluid_type = AQUARIUM_FLUID_ANY_WATER
min_pressure = HAZARD_LOW_PRESSURE
health = 300
stable_population = 2 //This means they can only crossbreed.
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 = 10 MINUTES
breeding_timeout = 5 MINUTES
average_size = 180
average_weight = 5000
death_text = "%SRC stops moving."
@@ -605,7 +616,7 @@
return
holo_area.linked.add_to_spawned(src)
/obj/item/fish/holo/set_status(new_status)
/obj/item/fish/holo/set_status(new_status, silent = FALSE)
. = ..()
if(status == FISH_DEAD)
animate(src, alpha = 0, 3 SECONDS, easing = SINE_EASING)
@@ -709,7 +720,7 @@
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_ai_type = FISH_AI_ZIPPY
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
@@ -795,3 +806,404 @@
//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/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"
dedicated_in_aquarium_icon_state = "sockeye_small"
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"
dedicated_in_aquarium_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/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"
dedicated_in_aquarium_icon_state = "stingray_small"
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/sand_surfer
name = "sand surfer"
desc = "A bronze alien \"fish\" living and swimming underneath faraway sandy places."
icon_state = "sand_surfer"
dedicated_in_aquarium_icon_state = "sand_surfer_small"
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(
"Type" = "Foodtype",
"Value" = SEAFOOD,
),
)
/obj/item/fish/bumpy
name = "bump-fish"
desc = "An misshapen fish-thing all covered in stubby little tendrils"
icon_state = "bumpy"
dedicated_in_aquarium_icon_state = "bumpy_small"
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(
"Type" = "Foodtype",
"Value" = VEGETABLES,
),
)
/obj/item/fish/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"
sprite_width = 8
sprite_height = 8
average_size = 30
average_weight = 500
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/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/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/three_eyes)
beauty = FISH_BEAUTY_GREAT
show_in_catalog = FALSE
stable_population = 1
random_case_rarity = FISH_RARITY_NOPE
/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/structures/aquarium/wide.dmi'
icon_state = "swordfish"
inhand_icon_state = "swordfish"
dedicated_in_aquarium_icon = 'icons/obj/structures/aquarium/fish.dmi'
dedicated_in_aquarium_icon_state = "swordfish_small"
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(
"Type" = "Foodtype",
"Value" = SEAFOOD,
),
)
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/chainsawfish
name = "chainsawfish"
desc = "A very, very angry bioweapon, whose sole purpose is to rip and tear."
icon = 'icons/obj/structures/aquarium/wide.dmi'
icon_state = "chainsawfish"
inhand_icon_state = "chainsawfish"
icon_state_dead = "chainsawfish_dead"
dedicated_in_aquarium_icon = 'icons/obj/structures/aquarium/fish.dmi'
dedicated_in_aquarium_icon_state = "chainsaw_small"
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(
"Type" = "Foodtype",
"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/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"
dedicated_in_aquarium_icon_state = "soul_small"
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(
"Type" = "Foodtype",
"Value" = 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
/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"
dedicated_in_aquarium_icon_state = "skin_crab_small"
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(
"Type" = "Foodtype",
"Value" = FRIED
),
)
fillet_type = /obj/item/food/meat/slab/rawcrab
random_case_rarity = FISH_RARITY_NOPE
+214
View File
@@ -0,0 +1,214 @@
/// Any lower than this, and the target position of the fish is considered null
#define FISH_TARGET_MIN_DISTANCE 6
/// The friction applied to fish jumps, so that it decelerates over time
#define FISH_FRICTION_MULT 0.9
/// Used to decide whether the fish can jump in a certain direction
#define FISH_SHORT_JUMP_MIN_DISTANCE 100
/// The maximum distance for a short jump
#define FISH_SHORT_JUMP_MAX_DISTANCE 200
///Fish movements are simple datums, generated by the fishing minigame, that represent how the fish moves suring the minigame.
/datum/fish_movement
/// The minigame that spawned us
var/datum/fishing_challenge/master
/// How many times move_fish() has been called
var/times_fired = 0
/// How likely the fish is to perform a standard jump, then multiplied by difficulty
var/short_jump_chance = 2.25
/// How likely the fish is to perform a long jump, then multiplied by difficulty
var/long_jump_chance = 0.0625
/// The speed limit for the short jump
var/short_jump_velocity_limit = 400
/// The speed limit for the long jump
var/long_jump_velocity_limit = 200
/// The current speed limit used
var/current_velocity_limit
/// The base velocity of the fish, which may affect jump distances and falling speed.
var/fish_idle_velocity = 0
/// A position on the slider the fish wants to get to
var/target_position
/// If true, the fish can jump while a target position is set, thus overriding it
var/can_interrupt_move = TRUE
/// The current speed the fish is moving at
var/fish_velocity = 0
/datum/fish_movement/New(datum/fishing_challenge/master)
src.master = master
/**
* Proc that adjusts movement values to the difficulty of the minigame.
* The operations can be a tad complex, but basically it ensures that jump
* chances with a probability higher than 1% increase in a smooth curve so that
* they still reach 100% prob when the difficulty peakes.
*/
/datum/fish_movement/proc/adjust_to_difficulty()
var/square_angle_rad = TORADIANS(90)
var/zero_one_difficulty = master.difficulty/100
if(short_jump_chance > 1)
short_jump_chance = (zero_one_difficulty**(square_angle_rad-TORADIANS(arctan(short_jump_chance * 1/square_angle_rad))))*100
else
short_jump_chance *= master.difficulty
if(long_jump_chance > 1)
long_jump_chance = (zero_one_difficulty**(square_angle_rad-TORADIANS(arctan(long_jump_chance * 1/square_angle_rad))))*100
else
long_jump_chance *= master.difficulty
///The main proc, called by minigame every SSfishing tick while it's in the 'active' phase.
/datum/fish_movement/proc/move_fish(seconds_per_tick)
times_fired++
/**
* The jump chances are meant to run every odd tick (each every decisecond)
* We cannot do it every tick because the fish would be jumpier than intended
* and we cannot cut the chances in half to fit on each tick, because the maximum probability
* would go from 100% to 75%.
*/
var/can_roll = times_fired % 2
var/long_chance = long_jump_chance * seconds_per_tick * (1/seconds_per_tick)
var/short_chance = short_jump_chance * seconds_per_tick * (1/seconds_per_tick)
// If we have the target but we're close enough, mark as target reached
if(abs(target_position - master.fish_position) < FISH_TARGET_MIN_DISTANCE)
target_position = null
// Switching to new long jump target can interrupt any other
if(can_roll && (can_interrupt_move || isnull(target_position)) && prob(long_chance))
/**
* Move at least 0.75 to full of the availible bar in given direction,
* and more likely to move in the direction where there's more space
*/
var/distance_from_top = FISHING_MINIGAME_AREA - master.fish_position - master.fish_height
var/distance_from_bottom = master.fish_position
var/top_chance
if(distance_from_top < FISH_SHORT_JUMP_MIN_DISTANCE)
top_chance = 0
else
top_chance = (distance_from_top/max(distance_from_bottom, 1)) * 100
var/new_target = master.fish_position
if(prob(top_chance))
new_target += distance_from_top * rand(75, 100)/100
else
new_target -= distance_from_bottom * rand(75, 100)/100
target_position = round(new_target)
current_velocity_limit = long_jump_velocity_limit
// Move towards target
if(!isnull(target_position))
var/distance = target_position - master.fish_position
// about 5 at diff 15 , 10 at diff 30, 30 at diff 100
var/acceleration_mult = get_acceleration(seconds_per_tick)
var/target_acceleration = distance * acceleration_mult * seconds_per_tick
fish_velocity = fish_velocity * FISH_FRICTION_MULT + target_acceleration
else if(can_roll && prob(short_chance))
var/distance_from_top = FISHING_MINIGAME_AREA - master.fish_position - master.fish_height
var/distance_from_bottom = master.fish_position
var/jump_length
if(distance_from_top >= FISH_SHORT_JUMP_MIN_DISTANCE)
jump_length = rand(FISH_SHORT_JUMP_MIN_DISTANCE, FISH_SHORT_JUMP_MAX_DISTANCE)
if(distance_from_bottom >= FISH_SHORT_JUMP_MIN_DISTANCE && (!jump_length || prob(50)))
jump_length = -rand(FISH_SHORT_JUMP_MIN_DISTANCE, FISH_SHORT_JUMP_MAX_DISTANCE)
target_position = clamp(master.fish_position + jump_length, 0, FISHING_MINIGAME_AREA - master.fish_height)
current_velocity_limit = short_jump_velocity_limit
fish_velocity = clamp(fish_velocity + fish_idle_velocity, -current_velocity_limit, current_velocity_limit)
set_fish_position(seconds_per_tick)
///Proc that returns the acceleration of the fish during the minigame.
/datum/fish_movement/proc/get_acceleration(seconds_per_tick)
return 0.3 * master.difficulty + 0.5
///Called at the end of move_fish(), for updating the position of the fish in the fishing minigame.
/datum/fish_movement/proc/set_fish_position(seconds_per_tick)
master.fish_position = clamp(master.fish_position + fish_velocity * seconds_per_tick, 0, FISHING_MINIGAME_AREA - master.fish_height)
///Generic fish movement datum that only performs slow, uninterrupted long jumps
/datum/fish_movement/slow
short_jump_chance = 0
long_jump_chance = 1.5
long_jump_velocity_limit = 150
can_interrupt_move = FALSE
///Generic fish movement datum with triple the short jump chance.
/datum/fish_movement/zippy
short_jump_chance = parent_type::short_jump_chance * 3
///fish movement datum that progressively gets faster until acceleration and velocity are double the starting ones.
/datum/fish_movement/accelerando
///The jump velocity to add each tick
var/short_jump_vel_add
///The long jump velocity to add each tick
var/long_jump_vel_add
///Time to reach full speed, in seconds.
var/accel_time_cap = 30
/datum/fish_movement/accelerando/move_fish(seconds_per_tick)
var/seconds_elapsed = (times_fired * seconds_per_tick)
if(seconds_elapsed >= accel_time_cap)
return ..()
if(!times_fired) //First tick, cache the initial jump velocities
short_jump_vel_add = short_jump_velocity_limit/accel_time_cap
long_jump_vel_add = long_jump_velocity_limit/accel_time_cap
return ..()
if(current_velocity_limit)
var/vel_add = current_velocity_limit == short_jump_velocity_limit ? short_jump_vel_add : long_jump_vel_add
current_velocity_limit += round(vel_add * seconds_per_tick, 0.01)
short_jump_velocity_limit += round(short_jump_vel_add * seconds_per_tick, 0.01)
long_jump_velocity_limit += round(long_jump_vel_add * seconds_per_tick, 0.01)
return ..()
/datum/fish_movement/accelerando/get_acceleration(seconds_per_tick)
var/acceleration = ..()
return acceleration + min(acceleration, acceleration * times_fired * seconds_per_tick / accel_time_cap)
/datum/fish_movement/accelerando/set_fish_position(seconds_per_tick)
fish_velocity = round(fish_velocity)
return ..()
///Fish movement datum that updates the fish position twice per second.
/datum/fish_movement/choppy
///We keep of the theorical fish position to eventually use
var/faux_position = 0
/datum/fish_movement/choppy/set_fish_position(seconds_per_tick)
faux_position = clamp(faux_position + fish_velocity * seconds_per_tick, 0, FISHING_MINIGAME_AREA - master.fish_height)
if(!((times_fired * SSfishing.wait) % (0.5 SECONDS)))
master.fish_position = faux_position
///Fish movement datum that weakly pushes the fish up and then down with greater force once it reaches the top of the minigame.
/datum/fish_movement/plunger
///Is the fish plunging to the bottom of the minigame area, or should it swim up?
var/is_plunging = TRUE
///The added idle velocity when plunging
var/plunging_speed = -22
/datum/fish_movement/plunger/adjust_to_difficulty()
. = ..()
//Adjust the fleeing velocity, up to five times the initial value.
plunging_speed += round(plunging_speed * master.difficulty * 0.03)
fish_idle_velocity += plunging_speed //so it can be safely subtracted if the fish starts at the bottom.
/datum/fish_movement/plunger/move_fish(seconds_per_tick)
var/fish_area = FISHING_MINIGAME_AREA - master.fish_height
if(is_plunging)
if(target_position > master.fish_position) //nothing should stop us from plunging.
target_position = null
var/dist_bot_percent = master.fish_position/fish_area
if(dist_bot_percent <= 0.04)
fish_idle_velocity -= plunging_speed
is_plunging = FALSE
else
var/dist_top_percent = (fish_area - master.fish_position)/fish_area
if(dist_top_percent <= 0.04)
fish_idle_velocity += plunging_speed
is_plunging = TRUE
return ..()
#undef FISH_TARGET_MIN_DISTANCE
#undef FISH_FRICTION_MULT
#undef FISH_SHORT_JUMP_MIN_DISTANCE
#undef FISH_SHORT_JUMP_MAX_DISTANCE
+31 -120
View File
@@ -5,16 +5,6 @@
// UI minigame phase
#define MINIGAME_PHASE 3
/// The height of the minigame slider. Not in pixels, but minigame units.
#define FISHING_MINIGAME_AREA 1000
/// Any lower than this, and the target position of the fish is considered null
#define FISH_TARGET_MIN_DISTANCE 6
/// The friction applied to fish jumps, so that it decelerates over time
#define FISH_FRICTION_MULT 0.9
/// Used to decide whether the fish can jump in a certain direction
#define FISH_SHORT_JUMP_MIN_DISTANCE 100
/// The maximum distance for a short jump
#define FISH_SHORT_JUMP_MAX_DISTANCE 200
// Acceleration mod when bait is over fish
#define FISH_ON_BAIT_ACCELERATION_MULT 0.6
/// The minimum velocity required for the bait to bounce
@@ -47,8 +37,6 @@
var/start_time
/// Is it finished (either by win/lose or window closing)
var/completed = FALSE
/// Fish AI type to use
var/fish_ai = FISH_AI_DUMB
/// Rule modifiers (eg weighted bait)
var/special_effects = NONE
/// A list of possible active minigame effects. If not empty, one will be picked from time to time.
@@ -93,8 +81,6 @@
var/fish_position = 0
/// The position of the bait on the minigame slider
var/bait_position = 0
/// The current speed the fish is moving at
var/fish_velocity = 0
/// The current speed the bait is moving at
var/bait_velocity = 0
@@ -105,22 +91,7 @@
/// How much completion is gained per second when the bait area is intersecting with the fish's
var/completion_gain = 5
/// How likely the fish is to perform a standard jump, then multiplied by difficulty
var/short_jump_chance = 2.25
/// How likely the fish is to perform a long jump, then multiplied by difficulty
var/long_jump_chance = 0.0625
/// The speed limit for the short jump
var/short_jump_velocity_limit = 400
/// The speed limit for the long jump
var/long_jump_velocity_limit = 200
/// The current speed limit used
var/current_velocity_limit = 200
/// The base velocity of the fish, which may affect jump distances and falling speed.
var/fish_idle_velocity = 0
/// A position on the slider the fish wants to get to
var/target_position
/// If true, the fish can jump while a target position is set, thus overriding it
var/can_interrupt_move = TRUE
var/datum/fish_movement/mover
/// Whether the bait is idle or reeling up or down (left and right click)
var/reeling_state = REELING_STATE_IDLE
@@ -144,24 +115,21 @@
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))
background = comp.fish_source.background
/// Fish minigame properties
if(ispath(reward_path,/obj/item/fish))
var/obj/item/fish/fish = reward_path
fish_ai = initial(fish.fish_ai_type)
switch(fish_ai)
if(FISH_AI_ZIPPY) // Keeps on jumping
short_jump_chance *= 3
if(FISH_AI_SLOW) // Only does long jump, and doesn't change direction until it gets there
short_jump_chance = 0
long_jump_chance = 1.5
long_jump_velocity_limit = 150
long_jump_velocity_limit = FALSE
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_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]
trait.minigame_mod(rod, user, src)
else
mover = new /datum/fish_movement(src)
/// Enable special parameters
if(rod.line)
completion_gain += 1 // Any fishing line will provide a small boost by default
@@ -198,22 +166,7 @@
if(HAS_MIND_TRAIT(user, TRAIT_REVEAL_FISH))
fish_icon = GLOB.specific_fish_icons[reward_path] || "fish"
/**
* If the chances are higher than 1% (100% at maximum difficulty), they'll scale
* less than proportionally (exponent less than 1) instead.
* This way we ensure fish with high jump chances won't get TOO jumpy until
* they near the maximum difficulty, at which they hit 100%
*/
var/square_angle_rad = TORADIANS(90)
var/zero_one_difficulty = difficulty/100
if(short_jump_chance > 1)
short_jump_chance = (zero_one_difficulty**(square_angle_rad-TORADIANS(arctan(short_jump_chance * 1/square_angle_rad))))*100
else
short_jump_chance *= difficulty
if(long_jump_chance > 1)
long_jump_chance = (zero_one_difficulty**(square_angle_rad-TORADIANS(arctan(long_jump_chance * 1/square_angle_rad))))*100
else
long_jump_chance *= difficulty
mover.adjust_to_difficulty()
bait_height -= round(difficulty * BAIT_HEIGHT_DIFFICULTY_MALUS)
bait_pixel_height = round(MINIGAME_BAIT_HEIGHT * (bait_height/initial(bait_height)), 1)
@@ -230,6 +183,7 @@
SStgui.close_uis(src)
user = null
used_rod = null
QDEL_NULL(mover)
return ..()
/datum/fishing_challenge/proc/send_alert(message)
@@ -369,6 +323,14 @@
send_alert("crustacean!!!")
if(FISH_ICON_BONE)
send_alert("bones!!!")
if(FISH_ICON_ELECTRIC)
send_alert("zappy!!!")
if(FISH_ICON_WEAPON)
send_alert("weapon!!!")
if(FISH_ICON_CRITTER)
send_alert("critter!!!")
if(FISH_ICON_SEED)
send_alert("seed!!!")
else
send_alert("!!!")
animate(lure, pixel_y = 3, time = 5, loop = -1, flags = ANIMATION_RELATIVE)
@@ -413,6 +375,10 @@
completion *= 1.2
if(BITING_TIME_WINDOW - 0.5 SECONDS to BITING_TIME_WINDOW)
completion *= 1.4
//randomize the position of the fish a little
fish_position = rand(0, (FISHING_MINIGAME_AREA - fish_height) * 0.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())
return
phase = MINIGAME_PHASE
@@ -465,7 +431,7 @@
/datum/fishing_challenge/process(seconds_per_tick)
if(length(active_effects) && COOLDOWN_FINISHED(src, active_effect_cd))
select_active_effect()
move_fish(seconds_per_tick)
mover.move_fish(seconds_per_tick)
move_bait(seconds_per_tick)
if(!QDELETED(fishing_hud))
update_visuals()
@@ -501,58 +467,6 @@
fishing_hud.icon_state = background
current_active_effect = null
///The proc that moves the fish around, just like in the old TGUI, mostly.
/datum/fishing_challenge/proc/move_fish(seconds_per_tick)
var/long_chance = long_jump_chance * seconds_per_tick * 10
var/short_chance = short_jump_chance * seconds_per_tick * 10
// If we have the target but we're close enough, mark as target reached
if(abs(target_position - fish_position) < FISH_TARGET_MIN_DISTANCE)
target_position = null
// Switching to new long jump target can interrupt any other
if((can_interrupt_move || isnull(target_position)) && prob(long_chance))
/**
* Move at least 0.75 to full of the availible bar in given direction,
* and more likely to move in the direction where there's more space
*/
var/distance_from_top = FISHING_MINIGAME_AREA - fish_position - fish_height
var/distance_from_bottom = fish_position
var/top_chance
if(distance_from_top < FISH_SHORT_JUMP_MIN_DISTANCE)
top_chance = 0
else
top_chance = (distance_from_top/max(distance_from_bottom, 1)) * 100
var/new_target = fish_position
if(prob(top_chance))
new_target += distance_from_top * rand(75, 100)/100
else
new_target -= distance_from_bottom * rand(75, 100)/100
target_position = round(new_target)
current_velocity_limit = long_jump_velocity_limit
// Move towards target
if(!isnull(target_position))
var/distance = target_position - fish_position
// about 5 at diff 15 , 10 at diff 30, 30 at diff 100
var/acceleration_mult = 0.3 * difficulty + 0.5
var/target_acceleration = distance * acceleration_mult * seconds_per_tick
fish_velocity = fish_velocity * FISH_FRICTION_MULT + target_acceleration
else if(prob(short_chance))
var/distance_from_top = FISHING_MINIGAME_AREA - fish_position - fish_height
var/distance_from_bottom = fish_position
var/jump_length
if(distance_from_top >= FISH_SHORT_JUMP_MIN_DISTANCE)
jump_length = rand(FISH_SHORT_JUMP_MIN_DISTANCE, FISH_SHORT_JUMP_MAX_DISTANCE)
if(distance_from_bottom >= FISH_SHORT_JUMP_MIN_DISTANCE && (!jump_length || prob(50)))
jump_length = -rand(FISH_SHORT_JUMP_MIN_DISTANCE, FISH_SHORT_JUMP_MAX_DISTANCE)
target_position = clamp(fish_position + jump_length, 0, FISHING_MINIGAME_AREA - fish_height)
current_velocity_limit = short_jump_velocity_limit
fish_velocity = clamp(fish_velocity + fish_idle_velocity, -current_velocity_limit, current_velocity_limit)
fish_position = clamp(fish_position + fish_velocity * seconds_per_tick, 0, FISHING_MINIGAME_AREA - fish_height)
///The proc that moves the bait around, just like in the old TGUI, mostly.
/datum/fishing_challenge/proc/move_bait(seconds_per_tick)
var/should_bounce = abs(bait_velocity) > BAIT_MIN_VELOCITY_BOUNCE
@@ -613,9 +527,7 @@
bait_velocity += velocity_change
//check that the fish area is still intersecting the bait now that it has moved
fish_on_bait = (fish_position + fish_height >= bait_position) && (bait_position + bait_height >= fish_position)
if(fish_on_bait)
if(is_fish_on_bait())
completion += completion_gain * seconds_per_tick
if(completion >= 100)
complete(TRUE)
@@ -627,6 +539,10 @@
completion = clamp(completion, 0, 100)
///Returns TRUE if the fish and the bait are intersecting
/datum/fishing_challenge/proc/is_fish_on_bait()
return (fish_position + fish_height >= bait_position) && (bait_position + bait_height >= fish_position)
///update the vertical pixel position of both fish and bait, and the icon state of the completion bar
/datum/fishing_challenge/proc/update_visuals()
var/bait_offset_mult = bait_position/FISHING_MINIGAME_AREA
@@ -726,15 +642,6 @@
#undef BITING_PHASE
#undef MINIGAME_PHASE
#undef FISHING_MINIGAME_AREA
#undef FISH_TARGET_MIN_DISTANCE
#undef FISH_FRICTION_MULT
#undef FISH_SHORT_JUMP_MIN_DISTANCE
#undef FISH_SHORT_JUMP_MAX_DISTANCE
#undef FISH_ON_BAIT_ACCELERATION_MULT
#undef BAIT_MIN_VELOCITY_BOUNCE
#undef BAIT_DECELERATION_MULT
#undef MINIGAME_SLIDER_HEIGHT
#undef MINIGAME_BAIT_HEIGHT
#undef MINIGAME_FISH_HEIGHT
@@ -745,5 +652,9 @@
#undef REELING_STATE_UP
#undef REELING_STATE_DOWN
#undef FISH_ON_BAIT_ACCELERATION_MULT
#undef BAIT_MIN_VELOCITY_BOUNCE
#undef BAIT_DECELERATION_MULT
#undef MAX_FISH_COMPLETION_MALUS
#undef BITING_TIME_WINDOW
+13 -2
View File
@@ -107,8 +107,19 @@
var/mob/living/caught_mob = reward
if(caught_mob.stat == DEAD)
return
else if(!isfish(reward))
return
else
if(!isfish(reward))
return
var/obj/item/fish/fish = reward
if(HAS_TRAIT(bait, TRAIT_POISONOUS_BAIT) && !HAS_TRAIT(fish, TRAIT_FISH_TOXIN_IMMUNE))
var/kill_fish = TRUE
for(var/bait_identifer in fish.favorite_bait)
if(is_matching_bait(bait, bait_identifer))
kill_fish = FALSE
break
if(kill_fish)
fish.set_status(FISH_DEAD, silent = TRUE)
QDEL_NULL(bait)
update_icon()
+69 -46
View File
@@ -8,30 +8,48 @@ GLOBAL_LIST_INIT(preset_fish_sources, init_subtypes_w_path_keys(/datum/fish_sour
* A lot of the icons here may be a tad inaccurate, but since we're limited to the free font awesome icons we
* have access to, we got to make do.
*/
GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
/mob/living/basic/carp = FISH_ICON_DEF,
/mob/living/basic/mining = FISH_ICON_HOSTILE,
/obj/effect/decal/remains = FISH_ICON_BONE,
/obj/effect/mob_spawn/corpse = FISH_ICON_BONE,
/obj/item/coin = FISH_ICON_COIN,
/obj/item/fish = FISH_ICON_DEF,
/obj/item/fish/armorfish = FISH_ICON_CRAB,
/obj/item/fish/boned = FISH_ICON_BONE,
/obj/item/fish/chasm_crab = FISH_ICON_CRAB,
/obj/item/fish/gunner_jellyfish = FISH_ICON_JELLYFISH,
/obj/item/fish/holo/crab = FISH_ICON_CRAB,
/obj/item/fish/holo/puffer = FISH_ICON_CHUNKY,
/obj/item/fish/mastodon = FISH_ICON_BONE,
/obj/item/fish/pufferfish = FISH_ICON_CHUNKY,
/obj/item/fish/slimefish = FISH_ICON_SLIME,
/obj/item/fish/sludgefish = FISH_ICON_SLIME,
/obj/item/fish/starfish = FISH_ICON_STAR,
/obj/item/storage/wallet = FISH_ICON_COIN,
/obj/item/stack/sheet/bone = FISH_ICON_BONE,
/obj/item/stack/sheet/mineral = FISH_ICON_GEM,
/obj/item/stack/ore = FISH_ICON_GEM,
/obj/structure/closet/crate = FISH_ICON_COIN,
)))
GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
/proc/generate_specific_fish_icons()
var/list/return_list = zebra_typecacheof(list(
/mob/living/basic/axolotl = FISH_ICON_CRITTER,
/mob/living/basic/frog = FISH_ICON_CRITTER,
/mob/living/basic/carp = FISH_ICON_DEF,
/mob/living/basic/mining = FISH_ICON_HOSTILE,
/obj/effect/decal/remains = FISH_ICON_BONE,
/obj/effect/mob_spawn/corpse = FISH_ICON_BONE,
/obj/item/coin = FISH_ICON_COIN,
/obj/item/fish = FISH_ICON_DEF,
/obj/item/fish/armorfish = FISH_ICON_CRAB,
/obj/item/fish/boned = FISH_ICON_BONE,
/obj/item/fish/chainsawfish = FISH_ICON_WEAPON,
/obj/item/fish/chasm_crab = FISH_ICON_CRAB,
/obj/item/fish/gunner_jellyfish = FISH_ICON_JELLYFISH,
/obj/item/fish/holo/crab = FISH_ICON_CRAB,
/obj/item/fish/holo/puffer = FISH_ICON_CHUNKY,
/obj/item/fish/jumpercable = FISH_ICON_ELECTRIC,
/obj/item/fish/lavaloop = FISH_ICON_WEAPON,
/obj/item/fish/mastodon = FISH_ICON_BONE,
/obj/item/fish/pufferfish = FISH_ICON_CHUNKY,
/obj/item/fish/sand_crab = FISH_ICON_CRAB,
/obj/item/fish/skin_crab = FISH_ICON_CRAB,
/obj/item/fish/slimefish = FISH_ICON_SLIME,
/obj/item/fish/sludgefish = FISH_ICON_SLIME,
/obj/item/fish/starfish = FISH_ICON_STAR,
/obj/item/fish/stingray = FISH_ICON_WEAPON,
/obj/item/fish/swordfish = FISH_ICON_WEAPON,
/obj/item/fish/zipzap = FISH_ICON_ELECTRIC,
/obj/item/seeds/grass = FISH_ICON_SEED,
/obj/item/seeds/random = FISH_ICON_SEED,
/obj/item/storage/wallet = FISH_ICON_COIN,
/obj/item/stack/sheet/bone = FISH_ICON_BONE,
/obj/item/stack/sheet/mineral = FISH_ICON_GEM,
/obj/item/stack/ore = FISH_ICON_GEM,
/obj/structure/closet/crate = FISH_ICON_COIN,
))
return_list[FISHING_RANDOM_SEED] = FISH_ICON_SEED
return return_list
/**
* Where the fish actually come from - every fishing spot has one assigned but multiple fishing holes
@@ -45,6 +63,10 @@ GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
var/list/fish_table = list()
/// If a key from fish_table is present here, that fish is availible in limited quantity and is reduced by one on successful fishing
var/list/fish_counts = list()
/// Any limited quantity stuff in this list will be readded to the counts after a while
var/list/fish_count_regen
/// A list of stuff that's currently waiting to be readded to fish_counts
var/list/currently_on_regen
/// Text shown as baloon alert when you roll a dud in the table
var/duds = list("it was nothing", "the hook is empty")
/// Baseline difficulty for fishing in this spot
@@ -198,16 +220,26 @@ GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
/datum/fish_source/proc/simple_dispense_reward(reward_path, atom/spawn_location, turf/fishing_spot)
if(isnull(reward_path))
return null
if((reward_path in fish_counts)) // This is limited count result
if(reward_path in fish_counts) // This is limited count result
fish_counts[reward_path] -= 1
if(!fish_counts[reward_path])
fish_counts -= reward_path //Ran out of these since rolling (multiple fishermen on same source most likely)
fish_table -= reward_path
var/regen_time = fish_count_regen?[reward_path]
if(regen_time)
LAZYADDASSOC(currently_on_regen, reward_path, 1)
if(currently_on_regen[reward_path] == 1)
addtimer(CALLBACK(src, PROC_REF(regen_count), reward_path), regen_time)
var/atom/movable/reward = spawn_reward(reward_path, spawn_location, fishing_spot)
SEND_SIGNAL(src, COMSIG_FISH_SOURCE_REWARD_DISPENSED, reward)
return reward
/datum/fish_source/proc/regen_count(reward_path, regen_time)
fish_counts[reward_path] += 1
currently_on_regen[reward_path] -= 1
if(!currently_on_regen[reward_path])
LAZYREMOVE(currently_on_regen, reward_path)
else
addtimer(CALLBACK(src, PROC_REF(regen_count), reward_path), regen_time)
/// Spawns a reward from a atom path right where the fisherman is. Part of the dispense_reward() logic.
/datum/fish_source/proc/spawn_reward(reward_path, atom/spawn_location, turf/fishing_spot)
if(reward_path == FISHING_DUD)
@@ -239,22 +271,13 @@ GLOBAL_LIST(fishing_property_cache)
GLOB.fishing_property_cache = fish_property_table
return GLOB.fishing_property_cache
/// Checks if bait matches identifier from fav/disliked bait list
/datum/fish_source/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)
/// 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()
for(var/result in table)
if(fish_counts[result] == 0)
table -= result
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)
@@ -364,13 +387,13 @@ GLOBAL_LIST(fishing_property_cache)
for(var/i in 1 to (severity + 2))
if(!prob((100 + 100 * severity)/i * multiplier))
continue
var/reward_loot = pick_weight(fish_table)
var/reward_loot = pick_weight(get_fish_table())
var/atom/movable/reward = simple_dispense_reward(reward_loot, location, location)
if(isnull(reward))
continue
if(isfish(reward))
var/obj/item/fish/fish = reward
fish.set_status(FISH_DEAD)
fish.set_status(FISH_DEAD, silent = TRUE)
if(isitem(reward))
reward.pixel_x = rand(-9, 9)
reward.pixel_y = rand(-9, 9)
+86 -11
View File
@@ -1,32 +1,90 @@
/datum/fish_source/ocean
fish_table = list(
FISHING_DUD = 15,
/obj/item/coin/gold = 5,
/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/fish/lanternfish = 5,
/obj/item/fish/zipzap = 5,
/obj/item/fish/clownfish/lube = 3,
/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/structure/mystery_box/fishing = 1,
)
fish_counts = list(
/obj/item/fish/clownfish/lube = 2,
/obj/item/fish/swordfish = 2,
/obj/structure/mystery_box/fishing = 1,
)
fish_count_regen = list(
/obj/item/fish/clownfish/lube = 3 MINUTES,
/obj/item/fish/swordfish = 5 MINUTES,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 5
explosive_malus = TRUE
/datum/fish_source/ocean/beach
catalog_description = "Beach shore water"
/datum/fish_source/ice_fishing
catalog_description = "Ice-covered water"
fish_table = list(
FISHING_DUD = 4,
/obj/item/fish/arctic_char = 5,
/obj/item/fish/sockeye_salmon = 5,
/obj/item/fish/chasm_crab/ice = 2,
/obj/item/fish/boned = 1,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 20
/datum/fish_source/river
catalog_description = "River water"
fish_table = list(
FISHING_DUD = 4,
/obj/item/fish/goldfish = 5,
/obj/item/fish/guppy = 5,
/obj/item/fish/angelfish = 4,
/obj/item/fish/catfish = 4,
/obj/item/fish/slimefish = 2,
/obj/item/fish/sockeye_salmon = 1,
/obj/item/fish/arctic_char = 1,
/obj/item/fish/three_eyes = 1,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 5
/datum/fish_source/sand
catalog_description = "Sand"
fish_table = list(
FISHING_DUD = 8,
/obj/item/fish/sand_crab = 10,
/obj/item/fish/sand_surfer = 10,
/obj/item/fish/bumpy = 10,
/obj/item/coin/gold = 3,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 20
/datum/fish_source/cursed_spring
catalog_description = null //it's a secret (sorta, I know you're reading this)
fish_table = list(
FISHING_DUD = 2,
/obj/item/fish/soul = 3,
/obj/item/fish/skin_crab = 3,
/obj/item/fishing_rod/telescopic/master = 1,
)
fish_counts = list(
/obj/item/fishing_rod/telescopic/master = 1,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 25
/datum/fish_source/portal
fish_table = list(
FISHING_DUD = 7,
/obj/item/fish/goldfish = 10,
/obj/item/fish/guppy = 10,
/obj/item/fish/angelfish = 10,
/obj/item/fish/three_eyes = 3,
)
catalog_description = "Aquarium dimension (Fishing portal generator)"
///The name of this option shown in the radial menu on the fishing portal generator
@@ -73,6 +131,14 @@
/obj/item/fish/needlefish = 5,
/obj/item/fish/armorfish = 5,
/obj/item/fish/zipzap = 5,
/obj/item/fish/stingray = 4,
/obj/item/fish/swordfish = 3,
)
fish_counts = list(
/obj/item/fish/swordfish = 2,
)
fish_count_regen = list(
/obj/item/fish/swordfish = 5 MINUTES,
)
catalog_description = "Ocean dimension (Fishing portal generator)"
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 10
@@ -104,6 +170,13 @@
/obj/item/fish/donkfish = 5,
/obj/item/fish/emulsijack = 5,
/obj/item/fish/jumpercable = 5,
/obj/item/fish/chainsawfish = 3,
)
fish_counts = list(
/obj/item/fish/chainsawfish = 1,
)
fish_count_regen = list(
/obj/item/fish/chainsawfish = 7 MINUTES,
)
catalog_description = "Syndicate dimension (Fishing portal generator)"
radial_name = "Syndicate"
@@ -130,6 +203,7 @@
///rewards not found in other fishing portals
fish_table = list(
/obj/item/fish/three_eyes = 3,
/obj/item/fish/holo/checkered = 1,
)
@@ -155,8 +229,8 @@
challenge.bait_bounce_mult = clamp(challenge.bait_bounce_mult + (rand(-3, 3) * 0.1), 0.1, 1)
challenge.completion_loss = max(challenge.completion_loss + rand(-2, 2), 0)
challenge.completion_gain = max(challenge.completion_gain + rand(-1, 1), 2)
challenge.short_jump_velocity_limit += rand(-100, 100)
challenge.long_jump_velocity_limit += rand(-100, 100)
challenge.mover.short_jump_velocity_limit += rand(-100, 100)
challenge.mover.long_jump_velocity_limit += rand(-100, 100)
var/static/list/active_effects = bitfield_to_list(FISHING_MINIGAME_ACTIVE_EFFECTS)
for(var/effect in active_effects)
if(prob(30))
@@ -319,16 +393,19 @@
/obj/item/clothing/gloves/bracer = 2,
/obj/effect/decal/remains/human = 2,
/obj/item/fish/mastodon = 1,
/obj/item/fishing_rod/telescopic/master = 1,
)
fish_counts = list(
/obj/item/clothing/gloves/bracer = 1,
/obj/effect/decal/remains/human = 1,
/obj/item/fish/mastodon = 1,
/obj/item/fishing_rod/telescopic/master = 1,
)
fish_count_regen = list(
/obj/item/fish/mastodon = 8 MINUTES,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 15
#define RANDOM_SEED "Random seed"
/datum/fish_source/hydro_tray
catalog_description = "Hydroponics trays"
fish_table = list(
@@ -372,7 +449,7 @@
return ..()
/datum/fish_source/hydro_tray/spawn_reward(reward_path, mob/fisherman, turf/fishing_spot)
if(reward_path != RANDOM_SEED)
if(reward_path != FISHING_RANDOM_SEED)
var/mob/living/created_reward = ..()
if(istype(created_reward))
created_reward.name = "small [created_reward.name]"
@@ -393,5 +470,3 @@
var/picked_path = pick(seeds_to_draw_from)
return new picked_path(get_turf(fishing_spot))
#undef RANDOM_SEED