Files
Bubberstation/code/datums/components/rotation.dm
AnturK 4d6a8bc537 515 Compatibility (#71161)
Makes the code compatible with 515.1594+

Few simple changes and one very painful one.
Let's start with the easy:
* puts call behind `LIBCALL` define, so call_ext is properly used in 515
* Adds `NAMEOF_STATIC(_,X)` macro for nameof in static definitions since
src is now invalid there.
* Fixes tgui and devserver. From 515 onward the tmp3333{procid} cache
directory is not appened to base path in browser controls so we don't
check for it in base js and put the dev server dummy window file in
actual directory not the byond root.
* Renames the few things that had /final/ in typepath to ultimate since
final is a new keyword

And the very painful change:
`.proc/whatever` format is no longer valid, so we're replacing it with
new nameof() function. All this wrapped in three new macros.
`PROC_REF(X)`,`TYPE_PROC_REF(TYPE,X)`,`GLOBAL_PROC_REF(X)`. Global is
not actually necessary but if we get nameof that does not allow globals
it would be nice validation.
This is pretty unwieldy but there's no real alternative.
If you notice anything weird in the commits let me know because majority
was done with regex replace.

@tgstation/commit-access Since the .proc/stuff is pretty big change.

Co-authored-by: san7890 <the@san7890.com>
Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
2022-11-15 03:50:11 +00:00

200 lines
7.5 KiB
Plaintext

/// If an object needs to be rotated with a wrench
#define ROTATION_REQUIRE_WRENCH (1<<0)
/// If ghosts can rotate an object (if the ghost config is enabled)
#define ROTATION_GHOSTS_ALLOWED (1<<1)
/// If an object will ignore anchored for rotation (used for chairs)
#define ROTATION_IGNORE_ANCHORED (1<<2)
/// If an object will omit flipping from rotation (used for pipes since they use custom handling)
#define ROTATION_NO_FLIPPING (1<<3)
/// If an object needs to have an empty spot available in target direction (used for windoors and railings)
#define ROTATION_NEEDS_ROOM (1<<4)
/// Rotate an object clockwise
#define ROTATION_CLOCKWISE -90
/// Rotate an object counterclockwise
#define ROTATION_COUNTERCLOCKWISE 90
/// Rotate an object upside down
#define ROTATION_FLIP 180
/datum/component/simple_rotation
/// Additional stuff to do after rotation
var/datum/callback/AfterRotation
/// Rotation flags for special behavior
var/rotation_flags = NONE
/**
* 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)
* * AfterRotation (optional) Callback proc that is used after the object is rotated (sound effects, balloon alerts, etc.)
**/
/datum/component/simple_rotation/Initialize(rotation_flags = NONE, AfterRotation)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
var/atom/movable/source = parent
source.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
src.rotation_flags = rotation_flags
src.AfterRotation = AfterRotation || CALLBACK(src, PROC_REF(DefaultAfterRotation))
/datum/component/simple_rotation/proc/AddSignals()
RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(RotateLeft))
RegisterSignal(parent, COMSIG_CLICK_ALT_SECONDARY, PROC_REF(RotateRight))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(ExamineMessage))
RegisterSignal(parent, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_requesting_context_from_item))
/datum/component/simple_rotation/proc/AddVerbs()
var/obj/rotated_obj = parent
rotated_obj.verbs += /atom/movable/proc/SimpleRotateClockwise
rotated_obj.verbs += /atom/movable/proc/SimpleRotateCounterclockwise
if(!(rotation_flags & ROTATION_NO_FLIPPING))
rotated_obj.verbs += /atom/movable/proc/SimpleRotateFlip
/datum/component/simple_rotation/proc/RemoveVerbs()
if(parent)
var/obj/rotated_obj = parent
rotated_obj.verbs -= /atom/movable/proc/SimpleRotateFlip
rotated_obj.verbs -= /atom/movable/proc/SimpleRotateClockwise
rotated_obj.verbs -= /atom/movable/proc/SimpleRotateCounterclockwise
/datum/component/simple_rotation/proc/RemoveSignals()
UnregisterSignal(parent, list(COMSIG_CLICK_ALT, COMSIG_CLICK_ALT_SECONDARY, COMSIG_PARENT_EXAMINE, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM))
/datum/component/simple_rotation/RegisterWithParent()
AddVerbs()
AddSignals()
. = ..()
/datum/component/simple_rotation/PostTransfer()
//Because of the callbacks which we don't track cleanly we can't transfer this
//item cleanly, better to let the new of the new item create a new rotation datum
//instead (there's no real state worth transferring)
return COMPONENT_NOTRANSFER
/datum/component/simple_rotation/UnregisterFromParent()
RemoveVerbs()
RemoveSignals()
. = ..()
/datum/component/simple_rotation/Destroy()
QDEL_NULL(AfterRotation)
//Signals + verbs removed via UnRegister
. = ..()
/datum/component/simple_rotation/ClearFromParent()
RemoveVerbs()
return ..()
/datum/component/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/component/simple_rotation/proc/RotateRight(datum/source, mob/user)
SIGNAL_HANDLER
Rotate(user, ROTATION_CLOCKWISE)
/datum/component/simple_rotation/proc/RotateLeft(datum/source, mob/user)
SIGNAL_HANDLER
Rotate(user, ROTATION_COUNTERCLOCKWISE)
/datum/component/simple_rotation/proc/Rotate(mob/user, 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(!CanBeRotated(user, degrees) || !CanUserRotate(user, degrees))
return
var/obj/rotated_obj = parent
rotated_obj.setDir(turn(rotated_obj.dir, degrees))
if(rotation_flags & ROTATION_REQUIRE_WRENCH)
playsound(rotated_obj, 'sound/items/ratchet.ogg', 50, TRUE)
AfterRotation.Invoke(user, degrees)
/datum/component/simple_rotation/proc/CanUserRotate(mob/user, degrees)
if(isliving(user) && user.canUseTopic(parent, be_close = TRUE, no_dexterity = TRUE, no_tk = FALSE, need_hands = !iscyborg(user)))
return TRUE
if((rotation_flags & ROTATION_GHOSTS_ALLOWED) && isobserver(user) && CONFIG_GET(flag/ghost_interaction))
return TRUE
return FALSE
/datum/component/simple_rotation/proc/CanBeRotated(mob/user, degrees, silent=FALSE)
var/obj/rotated_obj = parent
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)
rotated_obj.balloon_alert(user, "need a wrench!")
return FALSE
if(!(rotation_flags & ROTATION_IGNORE_ANCHORED) && rotated_obj.anchored)
if(istype(rotated_obj, /obj/structure/window) && !silent)
rotated_obj.balloon_alert(user, "need to unscrew!")
else if(!silent)
rotated_obj.balloon_alert(user, "need to unwrench!")
return FALSE
if(rotation_flags & ROTATION_NEEDS_ROOM)
var/target_dir = turn(rotated_obj.dir, degrees)
var/obj/structure/window/rotated_window = rotated_obj
var/fulltile = istype(rotated_window) ? rotated_window.fulltile : FALSE
if(!valid_window_location(rotated_obj.loc, target_dir, is_fulltile = fulltile))
if(!silent)
rotated_obj.balloon_alert(user, "can't rotate in that direction!")
return FALSE
return TRUE
/datum/component/simple_rotation/proc/DefaultAfterRotation(mob/user, degrees)
return
/atom/movable/proc/SimpleRotateClockwise()
set name = "Rotate Clockwise"
set category = "Object"
set src in oview(1)
var/datum/component/simple_rotation/rotcomp = GetComponent(/datum/component/simple_rotation)
if(rotcomp)
rotcomp.Rotate(usr, ROTATION_CLOCKWISE)
/atom/movable/proc/SimpleRotateCounterclockwise()
set name = "Rotate Counter-Clockwise"
set category = "Object"
set src in oview(1)
var/datum/component/simple_rotation/rotcomp = GetComponent(/datum/component/simple_rotation)
if(rotcomp)
rotcomp.Rotate(usr, ROTATION_COUNTERCLOCKWISE)
/atom/movable/proc/SimpleRotateFlip()
set name = "Flip"
set category = "Object"
set src in oview(1)
var/datum/component/simple_rotation/rotcomp = GetComponent(/datum/component/simple_rotation)
if(rotcomp)
rotcomp.Rotate(usr, ROTATION_FLIP)
// maybe we don't need the item context proc but instead the hand one? since we don't need to check held_item
/datum/component/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(CanBeRotated(user, ROTATION_CLOCKWISE, silent=TRUE))
context[SCREENTIP_CONTEXT_ALT_LMB] = "Rotate left"
rotation_screentip = TRUE
if(CanBeRotated(user, ROTATION_COUNTERCLOCKWISE, silent=TRUE))
context[SCREENTIP_CONTEXT_ALT_RMB] = "Rotate right"
rotation_screentip = TRUE
if(rotation_screentip)
return CONTEXTUAL_SCREENTIP_SET
return NONE