diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index a7f51aa4fac..0605844c817 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -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). diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index c633b56c900..f1cf8b5ea9b 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -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, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 8a3ed07c2d1..7527af0203c 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -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, diff --git a/code/controllers/subsystem/persistence/trophy_fishes.dm b/code/controllers/subsystem/persistence/trophy_fishes.dm index 211bb849c69..df7247bd0e6 100644 --- a/code/controllers/subsystem/persistence/trophy_fishes.dm +++ b/code/controllers/subsystem/persistence/trophy_fishes.dm @@ -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 diff --git a/code/modules/cargo/bounties/assistant.dm b/code/modules/cargo/bounties/assistant.dm index 3eb8086c0a1..ca77572cb93 100644 --- a/code/modules/cargo/bounties/assistant.dm +++ b/code/modules/cargo/bounties/assistant.dm @@ -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 diff --git a/code/modules/fishing/aquarium/aquarium_kit.dm b/code/modules/fishing/aquarium/aquarium_kit.dm index 751dff2609c..a44790f4da1 100644 --- a/code/modules/fishing/aquarium/aquarium_kit.dm +++ b/code/modules/fishing/aquarium/aquarium_kit.dm @@ -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() diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index 1c4169294be..3ef0f728f09 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -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 diff --git a/code/modules/fishing/fish_mount.dm b/code/modules/fishing/fish_mount.dm index 65e2c0d3ce1..37eeb0049f2 100644 --- a/code/modules/fishing/fish_mount.dm +++ b/code/modules/fishing/fish_mount.dm @@ -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"