mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-29 16:18:01 +01:00
It's now possible to take trophy fish from previous rounds off the mount (based on RNG). (#89439)
## About The Pull Request Instead of immediately dusting, there's now a chance, depending on size and weight (lower is better) as well as the fishing skill of who tries to remove it (higher is better). At no skill (or if the fish is removed indirectly), the odds of it not dusting can be as low as about 3~4% (BIG FISH) and as high as 50% (tiny feesh), while each level in the skill provides an additional 4% to the roll, up to a bonus 28% (legendary level). As always, regardless of RNG, the fish won't be removed from the persistence system if removed from the trophy mount, and only if replaced. This PR also allows trophy fish to retain traits between rounds, but keep in mind the fish starts dead and may have to be revived first. Also trying to sell it will yield a pitiful 1/20 of its theorical export value, much like for fish from fish cases. ## Why It's Good For The Game The persistent trophy fish mount at the bar seems to fare fairly well without outstanding issues so far, so maybe I should let people get their hands on rare and valuable fish from previous rounds, and give players some more practical reasons to place a fish on the mount for future rounds. As long as people don't start beelining the trophy mount by the bar on regular basis. ## Changelog 🆑 balance: It's now possible to take trophy fish from previous shifts off their mounts without turning them into dust based on a probability revolving around the size and weight of the fish against your fishing skill. /🆑
This commit is contained in:
@@ -1048,8 +1048,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
#define TRAIT_FISH_FED_LUBE "fish_fed_lube"
|
||||
#define TRAIT_FISH_WELL_COOKED "fish_well_cooked"
|
||||
#define TRAIT_FISH_NO_HUNGER "fish_no_hunger"
|
||||
///It comes from a fish case. Relevant for bounties so far.
|
||||
#define TRAIT_FISH_FROM_CASE "fish_from_case"
|
||||
///Fish with this trait only sell for 1/20 of the original price when exported. For fish cases and trophy mounts.
|
||||
#define TRAIT_FISH_LOW_PRICE "fish_from_case"
|
||||
///Fish will also occasionally fire weak tesla zaps
|
||||
#define TRAIT_FISH_ELECTROGENESIS "fish_electrogenesis"
|
||||
///Offsprings from this fish will never be of its same type (unless it's self-reproducing).
|
||||
|
||||
@@ -667,8 +667,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_FISH_ELECTROGENESIS" = TRAIT_FISH_ELECTROGENESIS,
|
||||
"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_LOW_PRICE" = TRAIT_FISH_LOW_PRICE,
|
||||
"TRAIT_FISH_MADE_OF_BONE" = TRAIT_FISH_MADE_OF_BONE,
|
||||
"TRAIT_FISH_MUTAGENIC" = TRAIT_FISH_MUTAGENIC,
|
||||
"TRAIT_FISH_NO_HUNGER" = TRAIT_FISH_NO_HUNGER,
|
||||
|
||||
@@ -360,8 +360,8 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
|
||||
"TRAIT_FISH_CROSSBREEDER" = TRAIT_FISH_CROSSBREEDER,
|
||||
"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_LOW_PRICE" = TRAIT_FISH_LOW_PRICE,
|
||||
"TRAIT_FISH_MADE_OF_BONE" = TRAIT_FISH_MADE_OF_BONE,
|
||||
"TRAIT_FISH_MUTAGENIC" = TRAIT_FISH_MUTAGENIC,
|
||||
"TRAIT_FISH_NO_HUNGER" = TRAIT_FISH_NO_HUNGER,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#define PERSISTENCE_FISH_MATERIAL "fish_material"
|
||||
#define PERSISTENCE_FISH_CATCHER "fish_catcher"
|
||||
#define PERSISTENCE_FISH_CATCH_DATE "fish_catch_date"
|
||||
#define PERSISTENCE_FISH_TRAITS "fish_traits"
|
||||
|
||||
///Instantiate a fish, then set its size, weight, eventually materials and finally add it to the mount.
|
||||
/datum/controller/subsystem/persistence/proc/load_trophy_fish(obj/structure/fish_mount/mount)
|
||||
@@ -23,7 +24,15 @@
|
||||
if(!fish_path) //the fish was removed, uh uh.
|
||||
return
|
||||
var/obj/item/fish/fish = new fish_path(mount, /* apply_qualities = */ FALSE)
|
||||
fish.fish_traits.Cut()
|
||||
var/list/traits_text = data[PERSISTENCE_FISH_TRAITS]
|
||||
if(!isnull(traits_text))
|
||||
var/list/traits = list()
|
||||
for(var/text_path in traits_text)
|
||||
var/path = text2path(text_path)
|
||||
if(path)
|
||||
traits |= path
|
||||
fish.fish_traits = traits
|
||||
fish.apply_traits()
|
||||
fish.update_size_and_weight(data[PERSISTENCE_FISH_SIZE], data[PERSISTENCE_FISH_WEIGHT])
|
||||
var/material_path = text2path(data[PERSISTENCE_FISH_MATERIAL])
|
||||
if(material_path)
|
||||
@@ -34,8 +43,8 @@
|
||||
fish.persistence_load(data)
|
||||
fish.name = data[PERSISTENCE_FISH_NAME]
|
||||
fish.set_status(FISH_DEAD, silent = TRUE)
|
||||
fish.catch_date = data[PERSISTENCE_FISH_CATCH_DATE]
|
||||
mount.add_fish(fish, from_persistence = TRUE, catcher = data[PERSISTENCE_FISH_CATCHER])
|
||||
mount.catch_date = data[PERSISTENCE_FISH_CATCH_DATE]
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/save_trophy_fish(obj/structure/fish_mount/mount)
|
||||
var/obj/item/fish/fish = mount.mounted_fish
|
||||
@@ -56,8 +65,12 @@
|
||||
data[PERSISTENCE_FISH_WEIGHT] = fish.weight / fish.material_weight_mult
|
||||
var/datum/material/material = fish.get_master_material()
|
||||
data[PERSISTENCE_FISH_MATERIAL] = "[material?.type]"
|
||||
data[PERSISTENCE_FISH_CATCHER] = mount.catcher_name
|
||||
data[PERSISTENCE_FISH_CATCH_DATE] = mount.catch_date
|
||||
data[PERSISTENCE_FISH_CATCHER] = fish.catcher_name
|
||||
data[PERSISTENCE_FISH_CATCH_DATE] = fish.catch_date
|
||||
var/list/traits = list()
|
||||
for(var/trait_path in fish.fish_traits)
|
||||
traits += "[trait_path]"
|
||||
data[PERSISTENCE_FISH_TRAITS] = traits
|
||||
|
||||
fish.persistence_save(data)
|
||||
trophy_fishes_database.set_key(mount.persistence_id, data)
|
||||
@@ -69,3 +82,4 @@
|
||||
#undef PERSISTENCE_FISH_MATERIAL
|
||||
#undef PERSISTENCE_FISH_CATCHER
|
||||
#undef PERSISTENCE_FISH_CATCH_DATE
|
||||
#undef PERSISTENCE_FISH_TRAITS
|
||||
|
||||
@@ -244,7 +244,7 @@
|
||||
var/obj/item/fish/fishie = shipped
|
||||
if(istype(shipped, /obj/item/storage/fish_case))
|
||||
fishie = locate() in shipped
|
||||
if(fishie.status == FISH_DEAD || HAS_TRAIT(fishie, TRAIT_FISH_FROM_CASE))
|
||||
if(fishie.status == FISH_DEAD || HAS_TRAIT(fishie, TRAIT_FISH_LOW_PRICE))
|
||||
reward -= shipping_penalty
|
||||
|
||||
///A subtype of the fish bounty that requires fish with a specific fluid type
|
||||
|
||||
@@ -38,8 +38,7 @@
|
||||
var/fish_type = get_fish_type()
|
||||
if(fish_type)
|
||||
var/obj/item/fish/spawned_fish = new fish_type(null)
|
||||
ADD_TRAIT(spawned_fish, TRAIT_FISH_FROM_CASE, TRAIT_GENERIC)
|
||||
ADD_TRAIT(spawned_fish, TRAIT_NO_FISHING_ACHIEVEMENT, TRAIT_GENERIC)
|
||||
spawned_fish.add_traits(list(TRAIT_NO_FISHING_ACHIEVEMENT, TRAIT_FISH_LOW_PRICE), INNATE_TRAIT)
|
||||
spawned_fish.forceMove(src) // trigger storage.handle_entered
|
||||
|
||||
/obj/item/storage/fish_case/proc/get_fish_type()
|
||||
|
||||
@@ -154,9 +154,14 @@
|
||||
/// power of the tesla zap created by the fish in a bioelectric generator. Scales with size.
|
||||
var/electrogenesis_power = 2 MEGA JOULES
|
||||
|
||||
/// The beauty this fish provides to the aquarium it's inserted in.
|
||||
/// The beauty this fish provides to the aquarium or mount it's inserted in.
|
||||
var/beauty = FISH_BEAUTY_GENERIC
|
||||
|
||||
/// Set and used by trophy mounts, this one is for the name of who mounted it (might actually not be the catcher but w/e)
|
||||
var/catcher_name
|
||||
/// Set and used by trophy mounts, this is for the day of when it was first mounted
|
||||
var/catch_date
|
||||
|
||||
/**
|
||||
* If you wonder why this isn't being tracked by the edible component instead:
|
||||
* We reset the this value when revived, and slowly chip it away as we heal.
|
||||
@@ -484,6 +489,9 @@
|
||||
|
||||
/obj/item/fish/examine(mob/user)
|
||||
. = ..()
|
||||
if(catcher_name && catch_date)
|
||||
. += span_boldnicegreen("Caught by [catcher_name] on [catch_date].")
|
||||
|
||||
if(HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISH) || HAS_TRAIT(loc, TRAIT_EXAMINE_FISH))
|
||||
. += span_notice("It's [size] cm long.")
|
||||
. += span_notice("It weighs [weight] g.")
|
||||
@@ -551,8 +559,11 @@
|
||||
fish_flags |= FISH_FLAG_UPDATING_SIZE_AND_WEIGHT
|
||||
SEND_SIGNAL(src, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, new_size, new_weight)
|
||||
|
||||
var/is_mount = istype(loc, /obj/structure/fish_mount) //used to prevent fish from getting butchered inside mounts
|
||||
|
||||
if(size)
|
||||
remove_fillet_type()
|
||||
if(!is_mount)
|
||||
remove_fillet_type()
|
||||
if(size > FISH_SIZE_TWO_HANDS_REQUIRED)
|
||||
qdel(GetComponent(/datum/component/two_handed))
|
||||
else
|
||||
@@ -591,7 +602,8 @@
|
||||
inhand_icon_state = "[inhand_icon_state]_wielded"
|
||||
AddComponent(/datum/component/two_handed, require_twohands = TRUE)
|
||||
|
||||
add_fillet_type()
|
||||
if(!is_mount)
|
||||
add_fillet_type()
|
||||
|
||||
var/make_edible = !weight
|
||||
if(weight)
|
||||
@@ -1427,7 +1439,7 @@
|
||||
if(raw_price >= FISH_PRICE_SOFT_CAP_THRESHOLD + 1)
|
||||
var/soft_cap = (raw_price - FISH_PRICE_SOFT_CAP_THRESHOLD)^FISH_PRICE_SOFT_CAP_EXPONENT
|
||||
raw_price = FISH_PRICE_SOFT_CAP_THRESHOLD + soft_cap
|
||||
if(HAS_TRAIT(src, TRAIT_FISH_FROM_CASE)) //Avoid printing money by simply ordering fish and sending it back.
|
||||
if(HAS_TRAIT(src, TRAIT_FISH_LOW_PRICE)) //Avoid printing money by simply ordering fish and sending it back.
|
||||
raw_price *= 0.05
|
||||
return raw_price * elasticity_percent
|
||||
|
||||
|
||||
@@ -31,12 +31,8 @@
|
||||
var/obj/item/fish/mounted_fish
|
||||
/// The identifier for mounts that carry the trophy between rounds.
|
||||
var/persistence_id
|
||||
/// Trophies from persistence are dusted if removed to be safe.
|
||||
/// Trophies from persistence have a good chance to be dusted if removal is attempted, though rarely it pays off.
|
||||
var/persistence_loaded_fish = FALSE
|
||||
/// String containing the name of whoever caught the fish
|
||||
var/catcher_name
|
||||
/// The date of when the fish was mounted (which should coincide with the day when it was actually caught)
|
||||
var/catch_date
|
||||
|
||||
/obj/structure/fish_mount/Initialize(mapload, floor_to_wall_dir)
|
||||
. = ..()
|
||||
@@ -121,20 +117,21 @@
|
||||
fish.vis_flags |= (VIS_INHERIT_PLANE|VIS_INHERIT_LAYER)
|
||||
fish.interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
|
||||
fish.obj_flags &= ~UNIQUE_RENAME
|
||||
fish.remove_fillet_type()
|
||||
fish.anchored = TRUE
|
||||
mounted_fish = fish
|
||||
|
||||
catcher_name = catcher
|
||||
catch_date = "[time2text(world.realtime, "Day, Month DD")], [CURRENT_STATION_YEAR]"
|
||||
if(!fish.catcher_name)
|
||||
fish.catcher_name = catcher
|
||||
if(!fish.catch_date)
|
||||
fish.catch_date = "[time2text(world.realtime, "Day, Month DD")], [CURRENT_STATION_YEAR]"
|
||||
|
||||
AddElement(/datum/element/beauty, get_fish_beauty())
|
||||
RegisterSignal(fish, COMSIG_ATOM_EXAMINE, PROC_REF(on_fish_examined))
|
||||
RegisterSignals(fish, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW), PROC_REF(on_fish_attack_hand))
|
||||
rotate_fish(dir)
|
||||
if(from_persistence)
|
||||
persistence_loaded_fish = TRUE
|
||||
fish.remove_fillet_type()
|
||||
fish.fillet_type = null
|
||||
fish.add_traits(list(TRAIT_NO_FISHING_ACHIEVEMENT, TRAIT_FISH_LOW_PRICE), INNATE_TRAIT)
|
||||
else if(persistence_id)
|
||||
SSpersistence.save_trophy_fish(src)
|
||||
|
||||
@@ -159,10 +156,6 @@
|
||||
. = ..()
|
||||
rotate_fish(dir, old_dir)
|
||||
|
||||
/obj/structure/fish_mount/proc/on_fish_examined(datum/source, mob/user, list/examine_list)
|
||||
SIGNAL_HANDLER
|
||||
examine_list += span_boldnicegreen("Caught by [catcher_name] on [catch_date].")
|
||||
|
||||
/obj/structure/fish_mount/proc/on_fish_attack_hand(datum/source, mob/living/user)
|
||||
SIGNAL_HANDLER
|
||||
INVOKE_ASYNC(src, PROC_REF(remove_fish), user)
|
||||
@@ -178,11 +171,13 @@
|
||||
|
||||
/obj/structure/fish_mount/proc/remove_fish(mob/living/user)
|
||||
balloon_alert(user, "removing fish...")
|
||||
if(!do_after(user, 3 SECONDS, src) || !mounted_fish)
|
||||
if(!do_after(user, (persistence_loaded_fish ? 6 : 3) SECONDS, src) || !mounted_fish)
|
||||
return
|
||||
|
||||
var/obj/item/fish/fish_reference = mounted_fish
|
||||
//remove it before trying to put it in hands so we don't end up with a lingering in-hand overlay if the fish is deleted.
|
||||
fish_reference.moveToNullspace()
|
||||
if(persistence_loaded_fish)
|
||||
roll_for_safe_removal(user)
|
||||
|
||||
if(QDELETED(fish_reference))
|
||||
var/ash_type = /obj/effect/decal/cleanable/ash
|
||||
if(fish_reference.w_class >= WEIGHT_CLASS_BULKY)
|
||||
@@ -197,23 +192,34 @@
|
||||
if(gone != mounted_fish)
|
||||
return ..()
|
||||
RemoveElement(/datum/element/beauty, get_fish_beauty())
|
||||
if(persistence_loaded_fish)
|
||||
if(!QDELETED(mounted_fish))
|
||||
qdel(mounted_fish)
|
||||
else
|
||||
if(!QDELETED(mounted_fish) && (!persistence_loaded_fish || roll_for_safe_removal()))
|
||||
rotate_fish(0, dir)
|
||||
UnregisterSignal(mounted_fish, list(COMSIG_ATOM_EXAMINE, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW))
|
||||
UnregisterSignal(mounted_fish, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW))
|
||||
mounted_fish.flags_1 &= ~IS_ONTOP_1
|
||||
mounted_fish.vis_flags &= ~(VIS_INHERIT_PLANE|VIS_INHERIT_LAYER)
|
||||
mounted_fish.interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP
|
||||
mounted_fish.obj_flags |= UNIQUE_RENAME
|
||||
mounted_fish.add_fillet_type()
|
||||
mounted_fish.anchored = FALSE
|
||||
persistence_loaded_fish = FALSE
|
||||
catcher_name = null
|
||||
catch_date = null
|
||||
mounted_fish = null
|
||||
return ..()
|
||||
|
||||
/obj/structure/fish_mount/proc/roll_for_safe_removal(mob/living/user)
|
||||
if(isnull(mounted_fish))
|
||||
return FALSE
|
||||
|
||||
///the base success rate is calculated considering the item inventory size and the heaviness of the fish.
|
||||
var/success_prob = 100/(mounted_fish.w_class + GET_FISH_WEIGHT_RANK(mounted_fish.weight))
|
||||
var/fishing_prowess = user?.mind?.get_skill_level(/datum/skill/fishing)
|
||||
success_prob += fishing_prowess * 4 // up to 28% fixed bonus chance to safely retrieve the trophy depending on skill.
|
||||
if(!prob(success_prob))
|
||||
qdel(mounted_fish)
|
||||
return FALSE
|
||||
|
||||
persistence_loaded_fish = FALSE //this way we don't roll again on Exited()
|
||||
return TRUE
|
||||
|
||||
/obj/structure/fish_mount/bar
|
||||
persistence_id = "Bar"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user