Skip to content

Commit

Permalink
Ports the scope component from TG and new scope eyes (ParadiseSS13#24288
Browse files Browse the repository at this point in the history
)

* Ports the scope component from TG.

* scope eyes

* now in scope

* fixes lwap canceling other scopes

* Update code/datums/components/scope.dm

Co-authored-by: Charlie Nolan <[email protected]>

* c-c-c-changes

* Apply suggestions from code review

Co-authored-by: Farie82 <[email protected]>

* Update scope.dm

* moves it off the gun

* Update code/datums/components/scope.dm

Co-authored-by: Burzah <[email protected]>
Signed-off-by: Qwertytoforty <[email protected]>

* fixes stealth conflict

* do_after_once

---------

Signed-off-by: Qwertytoforty <[email protected]>
Co-authored-by: Charlie Nolan <[email protected]>
Co-authored-by: Farie82 <[email protected]>
Co-authored-by: Burzah <[email protected]>
  • Loading branch information
4 people authored May 14, 2024
1 parent 23f393a commit f105159
Show file tree
Hide file tree
Showing 19 changed files with 381 additions and 158 deletions.
9 changes: 6 additions & 3 deletions code/__DEFINES/dcs/signals.dm
Original file line number Diff line number Diff line change
Expand Up @@ -717,9 +717,12 @@

// /obj/item/gun signals

/// called in /obj/item/gun/process_fire (user, target, params, zone_override)
#define COMSIG_MOB_FIRED_GUN "mob_fired_gun"
/// called in /obj/item/gun/process_fire (user, target)
///called in /obj/item/gun/fire_gun (user, target, flag, params)
#define COMSIG_GUN_TRY_FIRE "gun_try_fire"
#define COMPONENT_CANCEL_GUN_FIRE (1<<0)
///called in /obj/item/gun/afterattack (user, target, flag, params)
#define COMSIG_MOB_TRY_FIRE "mob_fired_gun"
///called in /obj/item/gun/process_fire (user, target)
#define COMSIG_GUN_FIRED "gun_fired"
/// called in /datum/component/automatic_fire/proc/on_mouse_down: (client/clicker, atom/target, turf/location, control, params)
#define COMSIG_AUTOFIRE_ONMOUSEDOWN "autofire_onmousedown"
Expand Down
1 change: 1 addition & 0 deletions code/__DEFINES/mob_defines.dm
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@
#define HEALTH_HUD_OVERRIDE_HEALTHY 3
// Eye protection
#define FLASH_PROTECTION_VERYVUNERABLE -4
#define FLASH_PROTECTION_EXTRA_SENSITIVE -2
#define FLASH_PROTECTION_SENSITIVE -1
#define FLASH_PROTECTION_NONE 0
#define FLASH_PROTECTION_FLASH 1
Expand Down
5 changes: 5 additions & 0 deletions code/__DEFINES/zoom.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
///How the scope component is toggled.
/// Wielding the object with both hands toggles the zoom. Requires the two-handed component to work.
#define ZOOM_METHOD_WIELD 1
/// Activated by clicking an item action button specified by the `item_action_type` var.
#define ZOOM_METHOD_ITEM_ACTION 2
1 change: 1 addition & 0 deletions code/__HELPERS/trait_helpers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_CAN_VIEW_HEALTH "can_view_health" // Also used for /Stat
#define TRAIT_MAGPULSE "magnetificent" // Used for anything that is magboot related
#define TRAIT_NOSLIP "noslip"
#define TRAIT_SCOPED "user_scoped"
#define TRAIT_MEPHEDRONE_ADAPTED "mephedrone_adapted" // Trait that changes the ending effects of twitch leaving your system
#define TRAIT_NOKNOCKDOWNSLOWDOWN "noknockdownslowdown" //If this person has this trait, they are not slowed via knockdown, but they can be hit by bullets like a self knockdown
#define TRAIT_CAN_STRIP "can_strip" // This mob can strip other mobs.
Expand Down
1 change: 1 addition & 0 deletions code/_globalvars/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_FORCED_STANDING" = TRAIT_FORCED_STANDING,
"TRAIT_NOSLIP" = TRAIT_NOSLIP,
"TRAIT_MAGPULSE" = TRAIT_MAGPULSE,
"TRAIT_SCOPED" = TRAIT_SCOPED,
"TRAIT_MEPHEDRONE_ADAPTED" = TRAIT_MEPHEDRONE_ADAPTED,
"TRAIT_NOKNOCKDOWNSLOWDOWN" = TRAIT_NOKNOCKDOWNSLOWDOWN,
"TRAIT_CAN_STRIP" = TRAIT_CAN_STRIP
Expand Down
3 changes: 2 additions & 1 deletion code/datums/action.dm
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
/datum/action/proc/Trigger(left_click = TRUE)
if(!IsAvailable())
return FALSE
if(SEND_SIGNAL(src, COMSIG_ACTION_TRIGGER, src) & COMPONENT_ACTION_BLOCK_TRIGGER)
return FALSE
return TRUE

/datum/action/proc/AltTrigger()
Expand Down Expand Up @@ -570,7 +572,6 @@
var/obj/item/clothing/shoes/magboots/gravity/G = target
G.dash(usr)


///prset for organ actions
/datum/action/item_action/organ_action
check_flags = AB_CHECK_CONSCIOUS
Expand Down
273 changes: 273 additions & 0 deletions code/datums/components/scope.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
///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 the view can be moved from the player. At 1, it can be moved by the player's view distance; other values scale linearly.
var/range_modifier = 1
/// Fullscreen object we use for tracking.
var/atom/movable/screen/fullscreen/stretch/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_ITEM_ACTION
/// 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
/// Time to scope up, if you want the scope to take time to boot up. Used by the LWAP
var/time_to_scope
/// Do we let the user scope and click on the middle of their screen?
var/allow_middle_click = FALSE
/// Do we have the scope cancel on move?
var/movement_cancels_scope = FALSE

/datum/component/scope/Initialize(range_modifier = 1, zoom_method = ZOOM_METHOD_ITEM_ACTION, item_action_type = /datum/action/zoom, time_to_scope = 0, allow_middle_click = FALSE, movement_cancels_scope = FALSE)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
src.range_modifier = range_modifier
src.zoom_method = zoom_method
src.item_action_type = item_action_type
src.time_to_scope = time_to_scope
src.allow_middle_click = allow_middle_click
src.movement_cancels_scope = movement_cancels_scope


/datum/component/scope/Destroy(force)
if(is_zoomed_in())
stop_zooming(tracker.owner)
return ..()

/datum/component/scope/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) //Checks for being removed for person, not mob movement
if(zoom_method == 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/scope = new item_action_type(parent)
parent_item.actions += scope
RegisterSignal(scope, COMSIG_ACTION_TRIGGER, PROC_REF(on_action_trigger))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
if(istype(parent, /obj/item/gun))
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/scope = locate(item_action_type) in parent_item.actions
parent_item.actions -= scope
UnregisterSignal(parent, list(
COMSIG_MOVABLE_MOVED,
SIGNAL_ADDTRAIT(TRAIT_WIELDED),
SIGNAL_REMOVETRAIT(TRAIT_WIELDED),
COMSIG_GUN_TRY_FIRE,
COMSIG_PARENT_EXAMINE,
))

/datum/component/scope/process()
var/mob/user_mob = tracker.owner
var/client/user_client = user_mob.client
if(!user_client)
stop_zooming(user_mob)
return
tracker.calculate_params()
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 // COMSIG_MOVABLE_MOVED

if(!is_zoomed_in())
return
if(source.loc != tracker.owner) //Dropped.
to_chat(tracker.owner, "<span class='warning'>[parent]'s scope is overloaded by movement and shuts down!</span>")
stop_zooming(tracker.owner)

/datum/component/scope/proc/on_action_trigger(datum/action/source)
SIGNAL_HANDLER // COMSIG_ACTION_TRIGGER
var/obj/item/item = source.target
var/mob/living/user = item.loc
if(is_internal_organ(item))
var/obj/item/organ/internal/O = item
user = O.owner
if(is_zoomed_in())
stop_zooming(user)
else
INVOKE_ASYNC(src, PROC_REF(zoom), user)

/datum/component/scope/proc/on_wielded(obj/item/source, trait)
SIGNAL_HANDLER // SIGNAL_ADDTRAIT(TRAIT_WIELDED)
var/mob/living/user = source.loc
INVOKE_ASYNC(src, PROC_REF(zoom), user)

/datum/component/scope/proc/on_unwielded(obj/item/source, trait)
SIGNAL_HANDLER // SIGNAL_REMOVETRAIT(TRAIT_WIELDED)
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 // COMSIG_GUN_TRY_FIRE
if(!tracker?.given_turf || target == get_target(tracker.given_turf))
return NONE
INVOKE_ASYNC(source, TYPE_PROC_REF(/obj/item, afterattack), 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 // COMSIG_PARENT_EXAMINE

var/scope = istype(parent, /obj/item/gun) ? "scope in" : "zoom out"
switch(zoom_method)
if(ZOOM_METHOD_WIELD)
examine_list += "<span class='notice'>You can [scope] by wielding it with both hands.</span>"

/**
* 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/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
return possible_target
if(length(non_dense_targets))
return non_dense_targets[1]
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_SCOPED))
to_chat(user, "<span class='warning'>You are already zoomed in!</span>")
return
if(time_to_scope)
if(!do_after_once(user, time_to_scope, target = parent))
return
user.playsound_local(parent, 'sound/weapons/scope.ogg', 75, TRUE)
tracker = user.overlay_fullscreen("scope", /atom/movable/screen/fullscreen/stretch/cursor_catcher/scope, istype(parent, /obj/item/gun))
tracker.assign_to_mob(user, range_modifier)
if(movement_cancels_scope)
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
if(allow_middle_click)
RegisterSignal(tracker, COMSIG_CLICK, PROC_REF(generic_click))
tracker_owner_ckey = user.ckey
if(user.is_holding(parent))
RegisterSignals(user, list(COMSIG_CARBON_SWAP_HANDS, COMSIG_PARENT_QDELETING), PROC_REF(stop_zooming))
else // The item is likely worn.
RegisterSignal(user, COMSIG_PARENT_QDELETING, PROC_REF(stop_zooming))
var/static/list/capacity_signals = list(
COMSIG_LIVING_STATUS_PARALYSE,
COMSIG_LIVING_STATUS_STUN,
)
RegisterSignals(user, capacity_signals, PROC_REF(on_incapacitated))
START_PROCESSING(SSprojectiles, src)
ADD_TRAIT(user, TRAIT_SCOPED, "[UID(src)]")
if(istype(parent, /obj/item/gun))
var/obj/item/gun/G = parent
G.on_scope_success(user)
return TRUE

/datum/component/scope/proc/on_incapacitated(mob/living/source, amount = 0, ignore_canstun = FALSE)
SIGNAL_HANDLER // COMSIG_LIVING_STATUS_PARALYSE, COMSIG_LIVING_STATUS_STUN

if(amount > 0)
stop_zooming(source)

/datum/component/scope/proc/generic_click(/obj/source, location, control, params)
SIGNAL_HANDLER // COMSIG_CLICK
INVOKE_ASYNC(tracker.owner, TYPE_PROC_REF(/mob, ClickOn), get_target(tracker.given_turf), params)

/**
* 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 // COMSIG_CARBON_SWAP_HANDS, COMSIG_PARENT_QDELETING

if(!HAS_TRAIT(user, TRAIT_SCOPED))
return

STOP_PROCESSING(SSprojectiles, src)
UnregisterSignal(user, list(
COMSIG_LIVING_STATUS_PARALYSE,
COMSIG_LIVING_STATUS_STUN,
COMSIG_CARBON_SWAP_HANDS,
COMSIG_PARENT_QDELETING,
))
REMOVE_TRAIT(user, TRAIT_SCOPED, "[UID(src)]")

user.playsound_local(parent, 'sound/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)
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
QDEL_NULL(tracker)
tracker_owner_ckey = null
if(istype(parent, /obj/item/gun))
var/obj/item/gun/G = parent
G.on_scope_end(user)

/datum/component/scope/proc/is_zoomed_in()
return !!tracker

/atom/movable/screen/fullscreen/stretch/cursor_catcher/scope
icon = 'icons/mob/screen_scope.dmi'
icon_state = "scope"
/// Multiplier for given_X an given_y.
var/range_modifier = 1

/atom/movable/screen/fullscreen/stretch/cursor_catcher/scope/assign_to_mob(mob/new_owner, range_modifier)
src.range_modifier = range_modifier
return ..()

/atom/movable/screen/fullscreen/stretch/cursor_catcher/scope/Click(location, control, params)
if(usr == owner)
calculate_params()
SEND_SIGNAL(src, COMSIG_CLICK, location, control, params)

return ..()

/atom/movable/screen/fullscreen/stretch/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"))
var/icon_y = text2num(LAZYACCESS(modifiers, "vis-y"))
if(isnull(icon_y))
icon_y = text2num(LAZYACCESS(modifiers, "icon-y"))
given_x = round(range_modifier * (icon_x - view_list[1] * world.icon_size / 2))
given_y = round(range_modifier * (icon_y - view_list[2] * world.icon_size / 2))
given_turf = locate(owner.x + round(given_x / world.icon_size, 1), owner.y + round(given_y / world.icon_size, 1), owner.z)


/datum/action/zoom
name = "Toggle Scope"
check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING
button_icon_state = "sniper_zoom"
19 changes: 2 additions & 17 deletions code/datums/status_effects/neutral.dm
Original file line number Diff line number Diff line change
Expand Up @@ -218,26 +218,11 @@
tick_interval = 4
/// The number of people the gun has locked on to. Caps at 10 for sanity.
var/locks = 0
/// What direction the owner was in when using the scope.
var/owner_dir = 0

/datum/status_effect/lwap_scope/on_creation(mob/living/new_owner, stored_dir = 0)
owner_dir = stored_dir
return ..()

/datum/status_effect/lwap_scope/tick()
locks = 0
var/turf/owner_turf = get_turf(owner)
var/scope_turf
for(var/turf/T in RANGE_EDGE_TURFS(7, owner_turf))
if(get_dir(owner, T) != owner_dir)
continue
if(T in range(owner, 6))
continue
scope_turf = T
break
if(scope_turf)
for(var/mob/living/L in range(10, scope_turf))
for(var/atom/movable/screen/fullscreen/stretch/cursor_catcher/scope/our_scope in owner.client.screen)
for(var/mob/living/L in range(10, our_scope.given_turf))
if(locks >= LWAP_LOCK_CAP)
return
if(L == owner || L.stat == DEAD || isslime(L) || ismonkeybasic(L)) //xenobio moment
Expand Down
10 changes: 10 additions & 0 deletions code/datums/uplink_items/uplink_general.dm
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,16 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/autosurgeon/organ/syndicate/razorwire
cost = 20

/datum/uplink_item/cyber_implants/scope_eyes
name = "Hardened Kaleido Optics Eyes Autoimplanter"
desc = "These cybernetic eye implants will let you zoom in on far away objects. \
Many users find it disorienting, and find it hard to interact with things near them when active. \
This pair has been hardened for special operations personnel."
reference = "KOE"
item = /obj/item/autosurgeon/organ/syndicate/scope_eyes
cost = 20


// POINTLESS BADASSERY

/datum/uplink_item/badass
Expand Down
Loading

0 comments on commit f105159

Please sign in to comment.