Files
Alexis 21b4095dfd [MDB IGNORE] [IDB IGNORE] Upstream Sync - 04/17/2026 (#5453)
Upstream 04/17/2026

fixes https://github.com/Bubberstation/Bubberstation/issues/5549

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>
Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com>
Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com>
Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com>
Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com>
Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com>
Co-authored-by: loganuk <fakeemail123@aol.com>
Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com>
Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com>
Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com>
Co-authored-by: Lucy <lucy@absolucy.moe>
Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com>
Co-authored-by: Isratosh <Isratosh@hotmail.com>
Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com>
Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com>
Co-authored-by: Alexander V. <volas@ya.ru>
Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com>
Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Tim <timothymtorres@gmail.com>
Co-authored-by: Iamgoofball <iamgoofball@gmail.com>
Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com>
Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com>
Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com>
Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com>
Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com>
Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com>
Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com>
Co-authored-by: Josh <josh.adam.powell@gmail.com>
Co-authored-by: Josh Powell <josh.powell@softwire.com>
Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com>
Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com>
Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com>
Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
2026-05-16 00:56:00 +02:00

212 lines
7.9 KiB
Plaintext

/obj/item/wallframe/fish
name = "fish mount"
desc = "The frame of a mount for trophy fish, to show off your proudest catch."
icon = 'icons/obj/wallmounts.dmi'
icon_state = "fish_mount_item"
result_path = /obj/structure/fish_mount
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
pixel_shift = 31
///Reference to the persistent_id of the mount this was spawned from.
var/persistence_id
/obj/item/wallframe/fish/after_attach(obj/structure/fish_mount/mount)
. = ..()
mount.persistence_id = persistence_id
///A wallmounted structure on which a fish can be attached to be used as room decoration.
/obj/structure/fish_mount
name = "fish mount"
desc = "A mount for trophy fish, to show off your proudest catch."
icon = 'icons/obj/wallmounts.dmi'
icon_state = "fish_mount"
anchored = TRUE
opacity = FALSE
density = FALSE
layer = SIGN_LAYER
max_integrity = 100
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT)
armor_type = /datum/armor/structure_sign
resistance_flags = FLAMMABLE
/// The instance of the fish mounted on this.
var/obj/item/fish/mounted_fish
/// The identifier for mounts that carry the trophy between rounds.
var/persistence_id
/// Trophies from persistence have a good chance to be dusted if removal is attempted, though rarely it pays off.
var/persistence_loaded_fish = FALSE
/obj/structure/fish_mount/Initialize(mapload)
. = ..()
//Mounted fish shouldn't flop. It should also show size and weight to everyone.
add_traits(list(TRAIT_STOP_FISH_FLOPPING, TRAIT_EXAMINE_FISH), INNATE_TRAIT)
if(mapload)
find_and_mount_on_atom()
if(!persistence_id)
return
if(SSfishing.initialized)
load_trophy_fish()
else
RegisterSignal(SSfishing, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(load_trophy_fish))
/obj/structure/fish_mount/Destroy(force)
QDEL_NULL(mounted_fish)
return ..()
/obj/structure/fish_mount/proc/load_trophy_fish(datum/source)
SIGNAL_HANDLER
SSpersistence.load_trophy_fish(src)
UnregisterSignal(SSfishing, COMSIG_SUBSYSTEM_POST_INITIALIZE)
if(!mounted_fish)
add_first_fish()
/obj/structure/fish_mount/screwdriver_act(mob/living/user, obj/item/item)
. = ..()
balloon_alert(user, "removing mount...")
if(!item.use_tool(src, user, 3 SECONDS, volume = 50))
return ITEM_INTERACT_BLOCKING
playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE)
balloon_alert_to_viewers("fish mount removed")
deconstruct()
return ITEM_INTERACT_SUCCESS
/obj/structure/fish_mount/proc/add_first_fish()
var/list/valid_picks = subtypesof(/obj/item/fish) - typesof(/obj/item/fish/holo) - /obj/item/fish/starfish/chrystarfish // chrystarfish immediately shatters when placed
var/obj/item/fish/fish_path = pick(valid_picks)
if(fish_path.fish_id_redirect_path)
fish_path = fish_path.fish_id_redirect_path
var/fluff_name = pick("John Trasen III", "a nameless intern", "Pun Pun", AQUARIUM_COMPANY, "Unknown", "Central Command")
add_fish(new fish_path(loc), from_persistence = TRUE, catcher = fluff_name)
mounted_fish.randomize_size_and_weight()
mounted_fish.set_status(FISH_DEAD)
SSpersistence.save_trophy_fish(src)
/obj/structure/fish_mount/atom_deconstruct(disassembled = TRUE)
. = ..()
if(disassembled)
var/obj/item/wallframe/fish/frame = new (loc)
frame.persistence_id = persistence_id
mounted_fish?.forceMove(loc)
/obj/structure/fish_mount/item_interaction(mob/living/user, obj/item/item, list/modifiers)
if(!isfish(item) || user.combat_mode)
return ..()
if(mounted_fish)
balloon_alert(user, "remove other fish first!")
return ITEM_INTERACT_BLOCKING
if(item.flags_1 & HOLOGRAM_1)
balloon_alert(user, "fish not mountable!")
return ITEM_INTERACT_BLOCKING
balloon_alert(user, "mounting fish...")
if(!do_after(user, 3 SECONDS, src) || mounted_fish)
return ITEM_INTERACT_BLOCKING
add_fish(item, catcher = user.name)
balloon_alert_to_viewers("fish mounted")
playsound(loc, 'sound/machines/click.ogg', 30, TRUE)
return ITEM_INTERACT_SUCCESS
/obj/structure/fish_mount/IsContainedAtomAccessible(atom/contained, atom/movable/user)
return TRUE
/obj/structure/fish_mount/proc/add_fish(obj/item/fish/fish, from_persistence = FALSE, catcher)
if(QDELETED(src)) // don't ever try to add a fish to one of these that's already been deleted - and get rid of the one that was created
qdel(fish)
return
if(QDELETED(fish)) // no adding deleted fishies either
return
if(mounted_fish)
mounted_fish.forceMove(loc)
fish.forceMove(src)
vis_contents += fish
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
if(!fish.catcher_name)
fish.catcher_name = catcher
if(!fish.catch_date)
fish.catch_date = "[time2text(world.realtime, "Day, Month DD", NO_TIMEZONE)], [CURRENT_STATION_YEAR]"
AddElement(/datum/element/beauty, get_fish_beauty())
RegisterSignals(fish, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW), PROC_REF(on_fish_attack_hand))
if(from_persistence)
persistence_loaded_fish = TRUE
fish.add_traits(list(TRAIT_NO_FISHING_ACHIEVEMENT, TRAIT_FISH_LOW_PRICE), INNATE_TRAIT)
else if(persistence_id)
SSpersistence.save_trophy_fish(src)
/obj/structure/fish_mount/proc/get_fish_beauty()
var/beauty = 100 + mounted_fish.beauty * 1.2
var/datum/material/main_material = mounted_fish.get_master_material()
if(main_material)
beauty += main_material.get_property(MATERIAL_BEAUTY) * mounted_fish.weight
return round(beauty)
/obj/structure/fish_mount/proc/on_fish_attack_hand(datum/source, mob/living/user)
SIGNAL_HANDLER
INVOKE_ASYNC(src, PROC_REF(remove_fish), user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/structure/fish_mount/attack_hand_secondary(mob/living/user, list/modifiers)
. = ..()
if(!mounted_fish)
balloon_alert(user, "no fish mounted!")
else
remove_fish(user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/structure/fish_mount/proc/remove_fish(mob/living/user)
balloon_alert(user, "removing fish...")
if(!do_after(user, (persistence_loaded_fish ? 6 : 3) SECONDS, src) || !mounted_fish)
return
var/obj/item/fish/fish_reference = mounted_fish
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)
ash_type = /obj/effect/decal/cleanable/ash/large
new ash_type(loc)
visible_message("[fish_reference] turns into dust as [fish_reference.p_theyre()] removed from [src].")
else
user.put_in_hands(mounted_fish)
balloon_alert_to_viewers("fish removed")
/obj/structure/fish_mount/Exited(atom/movable/gone)
if(gone != mounted_fish)
return ..()
RemoveElement(/datum/element/beauty, get_fish_beauty())
if(!QDELETED(mounted_fish) && (!persistence_loaded_fish || roll_for_safe_removal()))
UnregisterSignal(mounted_fish, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW))
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
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"
MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fish_mount/bar, /obj/item/wallframe/fish::pixel_shift)