feat: the AI can now point to things through the holopad (#96719)

## About The Pull Request

The AI can now send point clicks through their "camera" to be rendered
by the available holopads.


https://github.com/user-attachments/assets/427ea857-b303-4405-bc2e-91482abcbe29

Normal `point`ing timing applies, though one holopad is limited to
projecting one arrow at a time.

## Why It's Good For The Game

This is a thing I've been thinking about for a while. As the AI is
usually the "AI open" "AI where is [...]" "AI how do I [...]" kinda
role, it would make total sense there to be some way for the AI to point
to things. Didn't think it should be it's own machinery since we already
have way too much wallmount stuff.

## Changelog

🆑
add: AIs can now point to things using holopads
/🆑
This commit is contained in:
mcbalaam
2026-07-02 02:43:07 +02:00
committed by GitHub
parent 713b5bf688
commit 3337542853
4 changed files with 83 additions and 0 deletions
+12
View File
@@ -52,6 +52,9 @@
if(LAZYACCESS(modifiers, CTRL_CLICK))
CtrlShiftClickOn(A)
return
if(LAZYACCESS(modifiers, MIDDLE_CLICK))
ShiftMiddleClickOn(A)
return
ShiftClickOn(A)
return
if(LAZYACCESS(modifiers, ALT_CLICK)) // alt and alt-gr (rightalt)
@@ -269,6 +272,15 @@
togglelock(user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/mob/living/silicon/ai/ShiftMiddleClickOn(atom/A)
if(control_disabled || incapacitated)
return
if(!can_see(A))
return
if(!point_at(A, TRUE))
return
log_message("points at [A] using holopad", LOG_EMOTE)
/* AI Turrets */
/obj/machinery/turretid/ai_click_alt(mob/living/silicon/ai/user) //toggles lethal on turrets
if(ailock)
+39
View File
@@ -89,6 +89,8 @@ Possible to do for anyone motivated enough:
var/secure = FALSE
/// If we are currently calling another holopad
var/calling = FALSE
/// Whether this pad is currently projecting a pointing arrow
var/pointing = FALSE
///bitfield. used to turn on and off hearing sensitivity depending on if we can act on Hear() at all - meant for lowering the number of unessesary hearable atoms
var/can_hear_flags = NONE
@@ -738,6 +740,43 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
else
ray.transform = turn(M.Scale(1,sqrt(distx*distx+disty*disty)),newangle)
/// Project a holographic pointing arrow from this holopad toward a target
/obj/machinery/holopad/proc/holo_point(atom/target, invisibility = 0)
if(pointing)
return
var/turf/pad_turf = get_turf(src)
var/turf/target_turf = get_turf(target)
if(!pad_turf || !target_turf)
return
pointing = TRUE
var/obj/effect/temp_visual/point/holo/visual = new(pad_turf, invisibility)
var/obj/effect/overlay/holoray/ray = new(pad_turf)
var/disty = target_turf.y - pad_turf.y
var/distx = target_turf.x - pad_turf.x
var/distance = sqrt(distx*distx + disty*disty)
var/angle
if(!disty)
angle = (distx >= 0) ? 90 : 270
else
angle = arctan(distx/disty)
if(disty < 0)
angle += 180
else if(distx < 0)
angle += 360
var/matrix/M = matrix()
ray.transform = turn(M.Scale(1, distance), angle)
animate(visual, pixel_x = (target_turf.x - pad_turf.x) * ICON_SIZE_X + target.pixel_x, pixel_y = (target_turf.y - pad_turf.y) * ICON_SIZE_Y + target.pixel_y, time = 1.7, easing = EASE_OUT)
set_light(2)
icon_state = "[base_icon_state]1"
addtimer(CALLBACK(src, PROC_REF(clear_holo_point), ray), 2.5 SECONDS)
return visual
/// Called after the holo-point expires to restore the holopad's state
/obj/machinery/holopad/proc/clear_holo_point(obj/effect/overlay/holoray/ray)
qdel(ray)
pointing = FALSE
SetLightsAndPower()
// RECORDED MESSAGES
/obj/machinery/holopad/proc/setup_replay_holo(datum/holorecord/record)
+25
View File
@@ -1279,6 +1279,31 @@
. += emissive_appearance(icon, lights_state, src)
/mob/living/silicon/ai/point_at(atom/pointed_atom, intentional = FALSE)
if(pointed_atom in src)
return FALSE
var/turf/target_turf = get_turf(pointed_atom)
if(!target_turf)
return FALSE
var/obj/machinery/holopad/best_pad
for(var/obj/machinery/holopad/pad as anything in SSmachines.get_machines_by_type(/obj/machinery/holopad))
if(!pad.on_network || !pad.is_operational || pad.pointing)
continue
if(!pad.validate_location(target_turf))
continue
var/turf/pad_turf = get_turf(pad)
if(!SScameras.is_visible_by_cameras(pad_turf))
continue
if(!best_pad || get_dist(pad_turf, target_turf) < get_dist(get_turf(best_pad), target_turf))
best_pad = pad
if(!best_pad)
return FALSE
var/obj/visual = best_pad.holo_point(pointed_atom, invisibility)
if(!visual)
return FALSE
SEND_SIGNAL(src, COMSIG_MOVABLE_POINTED, pointed_atom, visual, intentional)
return TRUE
#undef HOLOGRAM_CHOICE_CHARACTER
#undef CHARACTER_TYPE_SELF
#undef CHARACTER_TYPE_CREWMEMBER
+7
View File
@@ -84,6 +84,13 @@
pixel_y = old_loc.pixel_y
SetInvisibility(set_invis)
/obj/effect/temp_visual/point/holo
icon_state = "arrow_large_white"
/obj/effect/temp_visual/point/holo/Initialize(mapload, set_invis = 0)
. = ..()
makeHologram()
#undef POINT_TIME
/**