Files
SyncIt21 39a196824a Enhances algorithm for finding an atom mount (#94076)
## About The Pull Request
Depends on #94064 for the unit test but offers a better method for
finding an atom to mount on
- Finding a mount now takes into consideration the objects pixel x & y
offsets meaning diagonal mounting is now supported. Gives great
flexibility for mappers
- If you don't want to use pixel offsets but default back to using the
objects direction that behaviour is still preserved. Useful if your
object uses directional icon states(lights & cameras for now) AND don't
use offsets
- If no direction could be specified then as the last resort it defaults
back to the objects local turf for mounting

## Changelog
🆑
fix: all mounted objects on tables, fences, windows & walls should fall
of correctly when the atom it is placed on is destroyed
fix: security telescreen now falls off when their mounted wall is
destroyed
fix: defib wall mount falls off when their mounted wall is destroyed
fix: floor lights are mounted to the ground/catwalk/tram floor they are
sitting on meaning destroying it will destroy the light
fix: wall mounted plaques now fall off when their mounted wall is
destroyed
/🆑
2025-12-01 00:50:16 +01:00

117 lines
4.0 KiB
Plaintext

/obj/structure/urinal
name = "urinal"
desc = "The HU-452, an experimental urinal. Comes complete with experimental urinal cake."
icon = 'icons/obj/watercloset.dmi'
icon_state = "urinal"
density = FALSE
anchored = TRUE
/// Can you currently put an item inside
var/exposed = FALSE
/// What's in the urinal
var/obj/item/hidden_item
MAPPING_DIRECTIONAL_HELPERS(/obj/structure/urinal, 32)
/obj/structure/urinal/Initialize(mapload)
. = ..()
if(mapload)
hidden_item = new /obj/item/food/urinalcake(src)
find_and_mount_on_atom()
/obj/structure/urinal/Exited(atom/movable/gone, direction)
. = ..()
if(gone == hidden_item)
hidden_item = null
/obj/structure/urinal/attack_hand(mob/living/user, list/modifiers)
. = ..()
if(.)
return
if(user.pulling && isliving(user.pulling))
var/mob/living/grabbed_mob = user.pulling
if(user.grab_state >= GRAB_AGGRESSIVE)
if(grabbed_mob.loc != get_turf(src))
to_chat(user, span_notice("[grabbed_mob.name] needs to be on [src]."))
return
user.changeNext_move(CLICK_CD_MELEE)
user.visible_message(span_danger("[user] slams [grabbed_mob] into [src]!"), span_danger("You slam [grabbed_mob] into [src]!"))
grabbed_mob.emote("scream")
grabbed_mob.adjust_brute_loss(8)
else
to_chat(user, span_warning("You need a tighter grip!"))
return
if(exposed)
if(hidden_item)
to_chat(user, span_notice("You fish [hidden_item] out of the drain enclosure."))
user.put_in_hands(hidden_item)
else
to_chat(user, span_warning("There is nothing in the drain holder!"))
return
return ..()
/obj/structure/urinal/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
if(exposed)
if(hidden_item)
to_chat(user, span_warning("There is already something in the drain enclosure!"))
return
if(attacking_item.w_class > WEIGHT_CLASS_TINY)
to_chat(user, span_warning("[attacking_item] is too large for the drain enclosure."))
return
if(!user.transferItemToLoc(attacking_item, src))
to_chat(user, span_warning("[attacking_item] is stuck to your hand, you cannot put it in the drain enclosure!"))
return
hidden_item = attacking_item
to_chat(user, span_notice("You place [attacking_item] into the drain enclosure."))
return
return ..()
/obj/structure/urinal/screwdriver_act(mob/living/user, obj/item/I)
if(..())
return TRUE
to_chat(user, span_notice("You start to [exposed ? "screw the cap back into place" : "unscrew the cap to the drain protector"]..."))
playsound(loc, 'sound/effects/stonedoor_openclose.ogg', 50, TRUE)
if(I.use_tool(src, user, 20))
user.visible_message(span_notice("[user] [exposed ? "screws the cap back into place" : "unscrew the cap to the drain protector"]!"),
span_notice("You [exposed ? "screw the cap back into place" : "unscrew the cap on the drain"]!"),
span_hear("You hear metal and squishing noises."))
exposed = !exposed
return TRUE
/obj/structure/urinal/wrench_act_secondary(mob/living/user, obj/item/tool)
tool.play_tool_sound(user)
deconstruct(TRUE)
balloon_alert(user, "removed urinal")
return ITEM_INTERACT_SUCCESS
/obj/structure/urinal/atom_deconstruct(disassembled = TRUE)
new /obj/item/wallframe/urinal(loc)
hidden_item?.forceMove(drop_location())
/obj/item/wallframe/urinal
name = "urinal frame"
desc = "An unmounted urinal. Attach it to a wall to use."
icon = 'icons/obj/watercloset.dmi'
icon_state = "urinal"
result_path = /obj/structure/urinal
pixel_shift = 32
/obj/item/food/urinalcake
name = "urinal cake"
desc = "The noble urinal cake, protecting the station's pipes from the station's pee. Do not eat."
icon = 'icons/obj/watercloset.dmi'
icon_state = "urinalcake"
w_class = WEIGHT_CLASS_TINY
food_reagents = list(
/datum/reagent/chlorine = 3,
/datum/reagent/ammonia = 1,
)
foodtypes = TOXIC | GROSS
preserved_food = TRUE
/obj/item/food/urinalcake/attack_self(mob/living/user)
user.visible_message(span_notice("[user] squishes [src]!"), span_notice("You squish [src]."), "<i>You hear a squish.</i>")
icon_state = "urinalcake_squish"
addtimer(VARSET_CALLBACK(src, icon_state, "urinalcake"), 0.8 SECONDS)