mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 17:52:36 +00:00
People can now pet held mothroaches and pugs if they want to, or use items on them, hopefully without causing many issues. After all, it only took about a couple dozen lines of code to make... ...Oh, did the 527 files changed or the 850~ lines added/removed perhaps catch your eye? Made you wonder if I accidentally pushed the wrong branch? or skewed something up big time? Well, nuh uh. I just happen to be fed up with the melee attack chain still using stringized params instead of an array/list. It was frankly revolting to see how I'd have had to otherwise call `list2params` for what I'm trying to accomplish here, and make this PR another tessera to the immense stupidity of our attack chain procs calling `params2list` over and over and over instead of just using that one call instance from `ClickOn` as an argument. It's 2025, honey, wake up! I also tried to replace some of those single letter vars/args but there are just way too many of them. Improving old code. And I want to be able to pet mobroaches while holding them too. 🆑 qol: You can now interact with held mobs in more ways beside wearing them. /🆑
274 lines
9.5 KiB
Plaintext
274 lines
9.5 KiB
Plaintext
///Used to allow reaching the maximum offset range without exiting the boundaries of the game screen.
|
|
#define MOUSE_POINTER_OFFSET_MULT 1.1
|
|
|
|
///A component that allows players to use the item to zoom out. Mainly intended for firearms, but now works with other items too.
|
|
/datum/component/scope
|
|
/// How far we can extend, with modifier of 1, up to our vision edge, higher numbers multiply.
|
|
var/range_modifier = 1
|
|
/// Fullscreen object we use for tracking.
|
|
var/atom/movable/screen/fullscreen/cursor_catcher/scope/tracker
|
|
/// The owner of the tracker's ckey. For comparing with the current owner mob, in case the client has left it (e.g. ghosted).
|
|
var/tracker_owner_ckey
|
|
/// The method which we zoom in and out
|
|
var/zoom_method = ZOOM_METHOD_RIGHT_CLICK
|
|
/// if not null, an item action will be added. Redundant if the mode is ZOOM_METHOD_RIGHT_CLICK or ZOOM_METHOD_WIELD.
|
|
var/item_action_type
|
|
|
|
/datum/component/scope/Initialize(range_modifier = 1, zoom_method = ZOOM_METHOD_RIGHT_CLICK, item_action_type)
|
|
if(!isitem(parent))
|
|
return COMPONENT_INCOMPATIBLE
|
|
src.range_modifier = range_modifier
|
|
src.zoom_method = zoom_method
|
|
src.item_action_type = item_action_type
|
|
|
|
/datum/component/scope/Destroy(force)
|
|
if(tracker)
|
|
stop_zooming(tracker.owner)
|
|
return ..()
|
|
|
|
/datum/component/scope/RegisterWithParent()
|
|
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
|
|
switch(zoom_method)
|
|
if(ZOOM_METHOD_RIGHT_CLICK)
|
|
RegisterSignal(parent, COMSIG_RANGED_ITEM_INTERACTING_WITH_ATOM_SECONDARY, PROC_REF(do_secondary_zoom))
|
|
if(ZOOM_METHOD_WIELD)
|
|
RegisterSignal(parent, SIGNAL_ADDTRAIT(TRAIT_WIELDED), PROC_REF(on_wielded))
|
|
RegisterSignal(parent, SIGNAL_REMOVETRAIT(TRAIT_WIELDED), PROC_REF(on_unwielded))
|
|
if(item_action_type)
|
|
var/obj/item/parent_item = parent
|
|
var/datum/action/item_action/scope = parent_item.add_item_action(item_action_type)
|
|
RegisterSignal(scope, COMSIG_ACTION_TRIGGER, PROC_REF(on_action_trigger))
|
|
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
|
|
if(isgun(parent))
|
|
RegisterSignal(parent, COMSIG_GUN_TRY_FIRE, PROC_REF(on_gun_fire))
|
|
|
|
/datum/component/scope/UnregisterFromParent()
|
|
if(item_action_type)
|
|
var/obj/item/parent_item = parent
|
|
var/datum/action/item_action/scope = locate(item_action_type) in parent_item.actions
|
|
parent_item.remove_item_action(scope)
|
|
UnregisterSignal(parent, list(
|
|
COMSIG_MOVABLE_MOVED,
|
|
COMSIG_RANGED_ITEM_INTERACTING_WITH_ATOM_SECONDARY,
|
|
SIGNAL_ADDTRAIT(TRAIT_WIELDED),
|
|
SIGNAL_REMOVETRAIT(TRAIT_WIELDED),
|
|
COMSIG_GUN_TRY_FIRE,
|
|
COMSIG_ATOM_EXAMINE,
|
|
))
|
|
|
|
/datum/component/scope/process(seconds_per_tick)
|
|
var/mob/user_mob = tracker.owner
|
|
var/client/user_client = user_mob.client
|
|
if(!user_client)
|
|
stop_zooming(user_mob)
|
|
return
|
|
tracker.calculate_params()
|
|
if(!user_client.intended_direction)
|
|
user_mob.face_atom(tracker.given_turf)
|
|
animate(user_client, world.tick_lag, pixel_x = tracker.given_x, pixel_y = tracker.given_y)
|
|
|
|
/datum/component/scope/proc/on_move(atom/movable/source, atom/oldloc, dir, forced)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!tracker)
|
|
return
|
|
stop_zooming(tracker.owner)
|
|
|
|
/datum/component/scope/proc/do_secondary_zoom(datum/source, mob/user, atom/target, list/modifiers)
|
|
SIGNAL_HANDLER
|
|
|
|
if(tracker)
|
|
stop_zooming(user)
|
|
else
|
|
zoom(user)
|
|
return ITEM_INTERACT_BLOCKING
|
|
|
|
/datum/component/scope/proc/on_action_trigger(datum/action/source)
|
|
SIGNAL_HANDLER
|
|
var/obj/item/item = source.target
|
|
var/mob/living/user = item.loc
|
|
if(tracker)
|
|
stop_zooming(user)
|
|
else
|
|
zoom(user)
|
|
|
|
/datum/component/scope/proc/on_wielded(obj/item/source, trait)
|
|
SIGNAL_HANDLER
|
|
var/mob/living/user = source.loc
|
|
zoom(user)
|
|
|
|
/datum/component/scope/proc/on_unwielded(obj/item/source, trait)
|
|
SIGNAL_HANDLER
|
|
var/mob/living/user = source.loc
|
|
stop_zooming(user)
|
|
|
|
/datum/component/scope/proc/on_gun_fire(obj/item/gun/source, mob/living/user, atom/target, flag, params)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!tracker?.given_turf || target == get_target(tracker.given_turf))
|
|
return NONE
|
|
INVOKE_ASYNC(source, TYPE_PROC_REF(/obj/item/gun, fire_gun), get_target(tracker.given_turf), user)
|
|
return COMPONENT_CANCEL_GUN_FIRE
|
|
|
|
/datum/component/scope/proc/on_examine(datum/source, mob/user, list/examine_list)
|
|
SIGNAL_HANDLER
|
|
|
|
var/scope = isgun(parent) ? "scope in" : "zoom out"
|
|
switch(zoom_method)
|
|
if(ZOOM_METHOD_RIGHT_CLICK)
|
|
examine_list += span_notice("You can [scope] with <b>right-click</b>.")
|
|
if(ZOOM_METHOD_WIELD)
|
|
examine_list += span_notice("You can [scope] by wielding it with both hands.")
|
|
|
|
/**
|
|
* We find and return the best target to hit on a given turf.
|
|
*
|
|
* Arguments:
|
|
* * target_turf: The turf we are looking for targets on.
|
|
*/
|
|
/datum/component/scope/proc/get_target(turf/target_turf)
|
|
var/list/object_targets = list()
|
|
var/list/non_dense_targets = list()
|
|
for(var/atom/movable/possible_target in target_turf)
|
|
if(possible_target.layer <= PROJECTILE_HIT_THRESHHOLD_LAYER)
|
|
continue
|
|
if(possible_target.invisibility > tracker.owner.see_invisible)
|
|
continue
|
|
if(!possible_target.mouse_opacity)
|
|
continue
|
|
if(iseffect(possible_target))
|
|
continue
|
|
if(ismob(possible_target))
|
|
if(possible_target == tracker.owner)
|
|
continue
|
|
return possible_target
|
|
if(!possible_target.density)
|
|
non_dense_targets += possible_target
|
|
continue
|
|
object_targets += possible_target
|
|
for(var/obj/important_object as anything in object_targets)
|
|
return important_object
|
|
for(var/obj/unimportant_object as anything in non_dense_targets)
|
|
return unimportant_object
|
|
return target_turf
|
|
|
|
/**
|
|
* We start zooming by adding our tracker overlay and starting our processing.
|
|
*
|
|
* Arguments:
|
|
* * user: The mob we are starting zooming on.
|
|
*/
|
|
/datum/component/scope/proc/zoom(mob/user)
|
|
if(isnull(user.client))
|
|
return
|
|
if(HAS_TRAIT(user, TRAIT_USER_SCOPED))
|
|
user.balloon_alert(user, "already zoomed!")
|
|
return
|
|
user.playsound_local(parent, 'sound/items/weapons/scope.ogg', 75, TRUE)
|
|
tracker = user.overlay_fullscreen("scope", /atom/movable/screen/fullscreen/cursor_catcher/scope, isgun(parent))
|
|
tracker.assign_to_mob(user, range_modifier)
|
|
tracker_owner_ckey = user.ckey
|
|
if(user.is_holding(parent))
|
|
RegisterSignals(user, list(COMSIG_MOB_SWAP_HANDS, COMSIG_QDELETING), PROC_REF(stop_zooming))
|
|
RegisterSignal(user, COMSIG_ATOM_ENTERING, PROC_REF(on_enter_new_loc))
|
|
else // The item is likely worn (eg. mothic cap)
|
|
RegisterSignal(user, COMSIG_QDELETING, PROC_REF(stop_zooming))
|
|
RegisterSignal(user, COMSIG_ATOM_ENTERING, PROC_REF(on_enter_new_loc))
|
|
var/static/list/capacity_signals = list(
|
|
COMSIG_LIVING_STATUS_KNOCKDOWN,
|
|
COMSIG_LIVING_STATUS_PARALYZE,
|
|
COMSIG_LIVING_STATUS_STUN,
|
|
)
|
|
RegisterSignals(user, capacity_signals, PROC_REF(on_incapacitated))
|
|
START_PROCESSING(SSprojectiles, src)
|
|
ADD_TRAIT(user, TRAIT_USER_SCOPED, REF(src))
|
|
return TRUE
|
|
|
|
///Stop scoping if the `newloc` we move to is not a turf
|
|
/datum/component/scope/proc/on_enter_new_loc(datum/source, atom/newloc, atom/old_loc, list/atom/old_locs)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!isturf(newloc))
|
|
stop_zooming(tracker.owner)
|
|
|
|
/datum/component/scope/proc/on_incapacitated(mob/living/source, amount = 0, ignore_canstun = FALSE)
|
|
SIGNAL_HANDLER
|
|
|
|
if(amount > 0)
|
|
stop_zooming(source)
|
|
|
|
/**
|
|
* We stop zooming, canceling processing, resetting stuff back to normal and deleting our tracker.
|
|
*
|
|
* Arguments:
|
|
* * user: The mob we are canceling zooming on.
|
|
*/
|
|
/datum/component/scope/proc/stop_zooming(mob/user)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!HAS_TRAIT(user, TRAIT_USER_SCOPED))
|
|
return
|
|
|
|
STOP_PROCESSING(SSprojectiles, src)
|
|
UnregisterSignal(user, list(
|
|
COMSIG_LIVING_STATUS_KNOCKDOWN,
|
|
COMSIG_LIVING_STATUS_PARALYZE,
|
|
COMSIG_LIVING_STATUS_STUN,
|
|
COMSIG_MOB_SWAP_HANDS,
|
|
COMSIG_QDELETING,
|
|
COMSIG_ATOM_ENTERING,
|
|
))
|
|
REMOVE_TRAIT(user, TRAIT_USER_SCOPED, REF(src))
|
|
|
|
user.playsound_local(parent, 'sound/items/weapons/scope.ogg', 75, TRUE, frequency = -1)
|
|
user.clear_fullscreen("scope")
|
|
|
|
// if the client has ended up in another mob, find that mob so we can fix their cursor
|
|
var/mob/true_user
|
|
if(user.ckey != tracker_owner_ckey)
|
|
true_user = get_mob_by_ckey(tracker_owner_ckey)
|
|
|
|
if(!isnull(true_user))
|
|
user = true_user
|
|
|
|
if(user.client)
|
|
animate(user.client, 0.2 SECONDS, pixel_x = 0, pixel_y = 0)
|
|
tracker = null
|
|
tracker_owner_ckey = null
|
|
|
|
/atom/movable/screen/fullscreen/cursor_catcher/scope
|
|
icon_state = "scope"
|
|
/// Multiplier for given_X an given_y.
|
|
var/range_modifier = 1
|
|
|
|
/atom/movable/screen/fullscreen/cursor_catcher/scope/assign_to_mob(mob/new_owner, range_modifier)
|
|
src.range_modifier = range_modifier
|
|
return ..()
|
|
|
|
/atom/movable/screen/fullscreen/cursor_catcher/scope/Click(location, control, params)
|
|
if(usr == owner)
|
|
calculate_params()
|
|
return ..()
|
|
|
|
/atom/movable/screen/fullscreen/cursor_catcher/scope/calculate_params()
|
|
var/list/modifiers = params2list(mouse_params)
|
|
var/icon_x = text2num(LAZYACCESS(modifiers, VIS_X))
|
|
if(isnull(icon_x))
|
|
icon_x = text2num(LAZYACCESS(modifiers, ICON_X))
|
|
if(isnull(icon_x))
|
|
icon_x = view_list[1]*ICON_SIZE_X/2
|
|
var/icon_y = text2num(LAZYACCESS(modifiers, VIS_Y))
|
|
if(isnull(icon_y))
|
|
icon_y = text2num(LAZYACCESS(modifiers, ICON_Y))
|
|
if(isnull(icon_y))
|
|
icon_y = view_list[2]*ICON_SIZE_Y/2
|
|
var/x_cap = range_modifier * view_list[1]*ICON_SIZE_X / 2
|
|
var/y_cap = range_modifier * view_list[2]*ICON_SIZE_Y / 2
|
|
var/uncapped_x = round(range_modifier * (icon_x - view_list[1]*ICON_SIZE_X/2) * MOUSE_POINTER_OFFSET_MULT)
|
|
var/uncapped_y = round(range_modifier * (icon_y - view_list[2]*ICON_SIZE_Y/2) * MOUSE_POINTER_OFFSET_MULT)
|
|
given_x = clamp(uncapped_x, -x_cap, x_cap)
|
|
given_y = clamp(uncapped_y, -y_cap, y_cap)
|
|
given_turf = locate(owner.x+round(given_x/ICON_SIZE_X, 1),owner.y+round(given_y/ICON_SIZE_Y, 1),owner.z)
|
|
|
|
#undef MOUSE_POINTER_OFFSET_MULT
|