Files
Bloop cf14dcfc1a Converts 3 components into elements (#94589)
## About The Pull Request

`adjust_fishing_difficulty` is a good one to make into a bespoke element
since a number of the same clothing item are created in the wardrobe
system.

climb_walkable is also good due to how prevalent it is, and it is a bit
of a two for one since it also removes the need for the
`/component/connect_loc_behalf`

Also stops adding the on_climbable trait to literally everything in the
turf, it now only applies it to atoms with density. So no more pipes and
stuff getting trait lists created for no reason.

<details><summary> Tested + things still work as before </summary>

<img width="422" height="122" alt="image"
src="https://github.com/user-attachments/assets/b23696f5-cd16-4150-aeb3-14ea6bd256fc"
/>

</details>

## Why It's Good For The Game

Lessens overhead, fixes a bug as well.

## Changelog

🆑
fix: fixes a pushed crate not removing on_climbable trait properly off
the mob standing atop it
/🆑
2025-12-30 01:05:50 +01:00

137 lines
5.1 KiB
Plaintext

/datum/element/simple_rotation
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
/// Rotation flags for special behavior
var/rotation_flags
/// Optional proc to call()() after the thing is successfully rotated
var/post_rotation_proccall
/**
* Adds the ability to rotate an object by Alt-click or using Right-click verbs.
*
* args:
* * rotation_flags (optional) Bitflags that determine behavior for rotation (defined at the top of this file)
* * post_rotation (optional) Callback proc that is used after the object is rotated (sound effects, balloon alerts, etc.)
**/
/datum/element/simple_rotation/Attach(datum/target, rotation_flags = NONE, post_rotation_proccall)
. = ..()
if(!ismovable(target))
return ELEMENT_INCOMPATIBLE
var/atom/movable/source = target
source.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
src.rotation_flags = rotation_flags
src.post_rotation_proccall = post_rotation_proccall
RegisterSignal(target, COMSIG_CLICK_ALT, PROC_REF(rotate_left))
RegisterSignal(target, COMSIG_CLICK_ALT_SECONDARY, PROC_REF(rotate_right))
RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(ExamineMessage))
RegisterSignal(target, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_requesting_context_from_item))
/datum/element/simple_rotation/Detach(datum/source, ...)
. = ..()
UnregisterSignal(source, list(
COMSIG_CLICK_ALT,
COMSIG_CLICK_ALT_SECONDARY,
COMSIG_ATOM_EXAMINE,
COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM,
))
return ..()
/datum/element/simple_rotation/proc/ExamineMessage(datum/source, mob/user, list/examine_list)
SIGNAL_HANDLER
if(rotation_flags & ROTATION_REQUIRE_WRENCH)
examine_list += span_notice("This requires a wrench to be rotated.")
/datum/element/simple_rotation/proc/rotate_right(datum/source, mob/user)
SIGNAL_HANDLER
rotate(user, source, ROTATION_CLOCKWISE)
return CLICK_ACTION_SUCCESS
/datum/element/simple_rotation/proc/rotate_left(datum/source, mob/user)
SIGNAL_HANDLER
rotate(user, source, ROTATION_COUNTERCLOCKWISE)
return CLICK_ACTION_SUCCESS
/datum/element/simple_rotation/proc/rotate(mob/user, obj/object_to_rotate, degrees)
if(QDELETED(user))
CRASH("[src] is being rotated [user ? "with a qdeleting" : "without a"] user")
if(!istype(user))
CRASH("[src] is being rotated without a user of the wrong type: [user.type]")
if(!isnum(degrees))
CRASH("[src] is being rotated without providing the amount of degrees needed")
if(!can_be_rotated(user, object_to_rotate, degrees) || !can_user_rotate(user, object_to_rotate, degrees))
return
object_to_rotate.setDir(turn(object_to_rotate.dir, degrees))
if(rotation_flags & ROTATION_REQUIRE_WRENCH)
playsound(object_to_rotate, 'sound/items/tools/ratchet.ogg', 50, TRUE)
if(post_rotation_proccall)
call(object_to_rotate, post_rotation_proccall)(user, degrees)
/datum/element/simple_rotation/proc/can_user_rotate(mob/user, obj/object_to_rotate, degrees)
if(isliving(user) && user.can_perform_action(object_to_rotate, NEED_DEXTERITY))
return TRUE
if((rotation_flags & ROTATION_GHOSTS_ALLOWED) && isobserver(user) && CONFIG_GET(flag/ghost_interaction))
return TRUE
return FALSE
/datum/element/simple_rotation/proc/can_be_rotated(mob/user, obj/object_to_rotate, degrees, silent = FALSE)
if(!object_to_rotate.Adjacent(user))
silent = TRUE
if(rotation_flags & ROTATION_REQUIRE_WRENCH)
if(!isliving(user))
return FALSE
var/obj/item/tool = user.get_active_held_item()
if(!tool || tool.tool_behaviour != TOOL_WRENCH)
if(!silent)
object_to_rotate.balloon_alert(user, "need a wrench!")
return FALSE
if(!(rotation_flags & ROTATION_IGNORE_ANCHORED) && object_to_rotate.anchored)
if(istype(object_to_rotate, /obj/structure/window) && !silent)
object_to_rotate.balloon_alert(user, "need to unscrew!")
else if(!silent)
object_to_rotate.balloon_alert(user, "need to unwrench!")
return FALSE
if(rotation_flags & ROTATION_NEEDS_ROOM)
var/target_dir = turn(object_to_rotate.dir, degrees)
var/obj/structure/window/window_to_rotate = object_to_rotate
var/fulltile = istype(window_to_rotate) ? window_to_rotate.fulltile : FALSE
if(!valid_build_direction(object_to_rotate.loc, target_dir, is_fulltile = fulltile))
if(!silent)
object_to_rotate.balloon_alert(user, "can't rotate in that direction!")
return FALSE
if(rotation_flags & ROTATION_NEEDS_UNBLOCKED)
var/turf/rotate_turf = get_turf(object_to_rotate)
if(rotate_turf.is_blocked_turf(source_atom = object_to_rotate))
if(!silent)
object_to_rotate.balloon_alert(user, "rotation is blocked!")
return FALSE
return TRUE
// maybe we don't need the item context proc but instead the hand one? since we don't need to check held_item
/datum/element/simple_rotation/proc/on_requesting_context_from_item(atom/source, list/context, obj/item/held_item, mob/user)
SIGNAL_HANDLER
var/rotation_screentip = FALSE
if(can_be_rotated(user, source, ROTATION_CLOCKWISE, silent=TRUE))
context[SCREENTIP_CONTEXT_ALT_LMB] = "Rotate left"
rotation_screentip = TRUE
if(can_be_rotated(user, source, ROTATION_COUNTERCLOCKWISE, silent=TRUE))
context[SCREENTIP_CONTEXT_ALT_RMB] = "Rotate right"
rotation_screentip = TRUE
if(rotation_screentip)
return CONTEXTUAL_SCREENTIP_SET
return NONE