mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2025-12-20 07:02:30 +00:00
This makes an [OPEN] link appear on all radio messages the AI hears (to the right of the follow link). When clicked, it allows the AI to open the door nearest to the speaker (or, if it is a voice changer, the door nearest to the poor sap who had his voice stolen). AI, Open This Door! Port of https://github.com/VOREStation/VOREStation/pull/7897
284 lines
7.7 KiB
Plaintext
284 lines
7.7 KiB
Plaintext
#define TRACKING_POSSIBLE 0
|
|
#define TRACKING_NO_COVERAGE 1
|
|
#define TRACKING_TERMINATE 2
|
|
|
|
/mob/living/silicon/ai/var/max_locations = 30
|
|
/mob/living/silicon/ai/var/stored_locations[0]
|
|
|
|
/proc/InvalidPlayerTurf(turf/T as turf)
|
|
return !(T && T.z in using_map.player_levels)
|
|
|
|
/mob/living/silicon/ai/proc/get_camera_list()
|
|
if(src.stat == 2)
|
|
return
|
|
|
|
cameranet.process_sort()
|
|
|
|
var/list/T = list()
|
|
for (var/obj/machinery/camera/C in cameranet.cameras)
|
|
var/list/tempnetwork = C.network&src.network
|
|
if (tempnetwork.len)
|
|
T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C
|
|
|
|
track = new()
|
|
track.cameras = T
|
|
return T
|
|
|
|
|
|
/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list())
|
|
set category = "AI Commands"
|
|
set name = "Show Camera List"
|
|
|
|
if(check_unable())
|
|
return
|
|
|
|
if (!camera)
|
|
return 0
|
|
|
|
var/obj/machinery/camera/C = track.cameras[camera]
|
|
src.eyeobj.setLoc(C)
|
|
|
|
return
|
|
|
|
/mob/living/silicon/ai/proc/ai_store_location(loc as text)
|
|
set category = "AI Commands"
|
|
set name = "Store Camera Location"
|
|
set desc = "Stores your current camera location by the given name"
|
|
|
|
loc = sanitize(loc)
|
|
if(!loc)
|
|
to_chat(src, "<span class='warning'>Must supply a location name</span>")
|
|
return
|
|
|
|
if(stored_locations.len >= max_locations)
|
|
to_chat(src, "<span class='warning'>Cannot store additional locations. Remove one first</span>")
|
|
return
|
|
|
|
if(loc in stored_locations)
|
|
to_chat(src, "<span class='warning'>There is already a stored location by this name</span>")
|
|
return
|
|
|
|
var/L = src.eyeobj.getLoc()
|
|
if (InvalidPlayerTurf(get_turf(L)))
|
|
to_chat(src, "<span class='warning'>Unable to store this location</span>")
|
|
return
|
|
|
|
stored_locations[loc] = L
|
|
to_chat(src, "Location '[loc]' stored")
|
|
|
|
/mob/living/silicon/ai/proc/sorted_stored_locations()
|
|
return sortList(stored_locations)
|
|
|
|
/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations())
|
|
set category = "AI Commands"
|
|
set name = "Goto Camera Location"
|
|
set desc = "Returns to the selected camera location"
|
|
|
|
if (!(loc in stored_locations))
|
|
to_chat(src, "<span class='warning'>Location [loc] not found</span>")
|
|
return
|
|
|
|
var/L = stored_locations[loc]
|
|
src.eyeobj.setLoc(L)
|
|
|
|
/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations())
|
|
set category = "AI Commands"
|
|
set name = "Delete Camera Location"
|
|
set desc = "Deletes the selected camera location"
|
|
|
|
if (!(loc in stored_locations))
|
|
to_chat(src, "<span class='warning'>Location [loc] not found</span>")
|
|
return
|
|
|
|
stored_locations.Remove(loc)
|
|
to_chat(src, "Location [loc] removed")
|
|
|
|
// Used to allow the AI is write in mob names/camera name from the CMD line.
|
|
/datum/trackable
|
|
var/list/names = list()
|
|
var/list/namecounts = list()
|
|
var/list/humans = list()
|
|
var/list/others = list()
|
|
var/list/cameras = list()
|
|
|
|
/mob/living/silicon/ai/proc/trackable_mobs()
|
|
if(usr.stat == 2)
|
|
return list()
|
|
|
|
var/datum/trackable/TB = new()
|
|
for(var/mob/living/M in mob_list)
|
|
if(M == usr)
|
|
continue
|
|
if(M.tracking_status() != TRACKING_POSSIBLE)
|
|
continue
|
|
|
|
var/name = M.name
|
|
if (name in TB.names)
|
|
TB.namecounts[name]++
|
|
name = text("[] ([])", name, TB.namecounts[name])
|
|
else
|
|
TB.names.Add(name)
|
|
TB.namecounts[name] = 1
|
|
if(istype(M, /mob/living/carbon/human))
|
|
TB.humans[name] = M
|
|
else
|
|
TB.others[name] = M
|
|
|
|
var/list/targets = sortList(TB.humans) + sortList(TB.others)
|
|
src.track = TB
|
|
return targets
|
|
|
|
/mob/living/silicon/ai/proc/ai_camera_track(var/target_name in trackable_mobs())
|
|
set category = "AI Commands"
|
|
set name = "Follow With Camera"
|
|
set desc = "Select who you would like to track."
|
|
|
|
if(src.stat == 2)
|
|
to_chat(src, "You can't follow [target_name] with cameras because you are dead!")
|
|
return
|
|
if(!target_name)
|
|
src.cameraFollow = null
|
|
|
|
var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name])
|
|
src.track = null
|
|
ai_actual_track(target)
|
|
|
|
/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0)
|
|
if(!cameraFollow)
|
|
return
|
|
|
|
to_chat(src, "Follow camera mode [forced ? "terminated" : "ended"].")
|
|
cameraFollow.tracking_cancelled()
|
|
cameraFollow = null
|
|
|
|
/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target as mob)
|
|
if(!istype(target)) return FALSE
|
|
var/mob/living/silicon/ai/U = usr
|
|
|
|
if(target == U.cameraFollow)
|
|
return TRUE
|
|
|
|
if(U.cameraFollow)
|
|
U.ai_cancel_tracking()
|
|
U.cameraFollow = target
|
|
to_chat(U, "Now tracking [target.name] on camera.")
|
|
target.tracking_initiated()
|
|
|
|
spawn (0)
|
|
while (U.cameraFollow == target)
|
|
if (U.cameraFollow == null)
|
|
return
|
|
|
|
switch(target.tracking_status())
|
|
if(TRACKING_NO_COVERAGE)
|
|
to_chat(U, "Target is not near any active cameras.")
|
|
sleep(100)
|
|
continue
|
|
if(TRACKING_TERMINATE)
|
|
U.ai_cancel_tracking(1)
|
|
return
|
|
|
|
if(U.eyeobj)
|
|
U.eyeobj.setLoc(get_turf(target), 0)
|
|
else
|
|
view_core()
|
|
return
|
|
sleep(10)
|
|
|
|
return TRUE
|
|
|
|
/obj/machinery/camera/attack_ai(var/mob/living/silicon/ai/user as mob)
|
|
if (!istype(user))
|
|
return
|
|
if (!src.can_use())
|
|
return
|
|
user.eyeobj.setLoc(get_turf(src))
|
|
|
|
|
|
/mob/living/silicon/ai/attack_ai(var/mob/user as mob)
|
|
ai_camera_list()
|
|
|
|
/proc/camera_sort(list/L)
|
|
var/obj/machinery/camera/a
|
|
var/obj/machinery/camera/b
|
|
|
|
for (var/i = L.len, i > 0, i--)
|
|
for (var/j = 1 to i - 1)
|
|
a = L[j]
|
|
b = L[j + 1]
|
|
if (a.c_tag_order != b.c_tag_order)
|
|
if (a.c_tag_order > b.c_tag_order)
|
|
L.Swap(j, j + 1)
|
|
else
|
|
if (sorttext(a.c_tag, b.c_tag) < 0)
|
|
L.Swap(j, j + 1)
|
|
return L
|
|
|
|
|
|
mob/living/proc/near_camera()
|
|
if (!isturf(loc))
|
|
return 0
|
|
else if(!cameranet.checkVis(src))
|
|
return 0
|
|
return 1
|
|
|
|
/mob/living/proc/tracking_status()
|
|
// Easy checks first.
|
|
// Don't detect mobs on CentCom. Since the wizard den is on CentCom, we only need this.
|
|
var/obj/item/weapon/card/id/id = GetIdCard()
|
|
if(id && id.prevent_tracking())
|
|
return TRACKING_TERMINATE
|
|
if(InvalidPlayerTurf(get_turf(src)))
|
|
return TRACKING_TERMINATE
|
|
if(invisibility >= INVISIBILITY_LEVEL_ONE) //cloaked
|
|
return TRACKING_TERMINATE
|
|
if(digitalcamo)
|
|
return TRACKING_TERMINATE
|
|
if(alpha < 127) // For lings and possible future alpha-based cloaks.
|
|
return TRACKING_TERMINATE
|
|
if(istype(loc,/obj/effect/dummy))
|
|
return TRACKING_TERMINATE
|
|
|
|
// Now, are they viewable by a camera? (This is last because it's the most intensive check)
|
|
return near_camera() ? TRACKING_POSSIBLE : TRACKING_NO_COVERAGE
|
|
|
|
/mob/living/silicon/robot/tracking_status()
|
|
. = ..()
|
|
if(. == TRACKING_NO_COVERAGE)
|
|
return camera && camera.can_use() ? TRACKING_POSSIBLE : TRACKING_NO_COVERAGE
|
|
|
|
/mob/living/carbon/human/tracking_status()
|
|
//Cameras can't track people wearing an agent card or a ninja hood.
|
|
if(istype(head, /obj/item/clothing/head/helmet/space/rig))
|
|
var/obj/item/clothing/head/helmet/space/rig/helmet = head
|
|
if(helmet.prevent_track())
|
|
return TRACKING_TERMINATE
|
|
|
|
. = ..()
|
|
if(. == TRACKING_TERMINATE)
|
|
return
|
|
|
|
if(. == TRACKING_NO_COVERAGE)
|
|
var/turf/T = get_turf(src)
|
|
if(T && (T.z in using_map.station_levels) && hassensorlevel(src, SUIT_SENSOR_TRACKING))
|
|
return TRACKING_POSSIBLE
|
|
|
|
mob/living/proc/tracking_initiated()
|
|
|
|
mob/living/silicon/robot/tracking_initiated()
|
|
tracking_entities++
|
|
if(tracking_entities == 1 && has_zeroth_law())
|
|
to_chat(src, "<span class='warning'>Internal camera is currently being accessed.</span>")
|
|
|
|
mob/living/proc/tracking_cancelled()
|
|
|
|
mob/living/silicon/robot/tracking_initiated()
|
|
tracking_entities--
|
|
if(!tracking_entities && has_zeroth_law())
|
|
to_chat(src, "<span class='notice'>Internal camera is no longer being accessed.</span>")
|
|
|
|
|
|
#undef TRACKING_POSSIBLE
|
|
#undef TRACKING_NO_COVERAGE
|
|
#undef TRACKING_TERMINATE
|