mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-23 03:57:48 +01:00
-Ported/continued AI Freelook. AIs can now look around like a ghost with the exception that they cannot see what cameras cannot see. Meaning if you're in maintenance, and there's no cameras near you, the AI will not know what you are doing. This also means there's no X-Ray vision cameras anymore. I've added documentation to freelook.dm which explains how the system works, if anyone is interested in adding anything. More information here: http://nanotrasen.com/phpBB3/viewtopic.php?f=7&t=9675
-I've added some cameras to rooms that should have cameras but were previously depending on other camera's using their X-Ray vision in order to see into a room. -AIs have a multitool in their contents that they can use to interact with the telecommunication machines. -Added "Follow" for observers. Works the same as AI tracking without checking if they can track. -Added a range to how far Cyborgs can interact with machines. This is to stop Cyborgs from interacting with machines through cameras, which could be used to easily disable the AI. git-svn-id: http://tgstation13.googlecode.com/svn/trunk@4531 316c924e-a436-60f5-8080-3fe189b3f50e
This commit is contained in:
+61
-120
@@ -1,39 +1,3 @@
|
||||
var/global/list/obj/machinery/camera/Cameras = list()
|
||||
|
||||
/obj/machinery/camera/New()
|
||||
Cameras += src
|
||||
..()
|
||||
|
||||
/obj/machinery/camera/Del()
|
||||
Cameras -= src
|
||||
..()
|
||||
|
||||
|
||||
// Double clicking turfs to move to nearest camera
|
||||
|
||||
/turf/proc/move_camera_by_click()
|
||||
if (usr.stat)
|
||||
return ..()
|
||||
if (world.time <= usr:lastDblClick+2)
|
||||
return ..()
|
||||
|
||||
//try to find the closest working camera in the same area, switch to it
|
||||
var/area/A = get_area(src)
|
||||
var/best_dist = INFINITY //infinity
|
||||
var/best_cam = null
|
||||
for(var/obj/machinery/camera/C in A)
|
||||
if(usr:network != C.network) continue
|
||||
if(!C.status) continue // ignore disabled cameras
|
||||
var/dist = get_dist(src, C)
|
||||
if(dist < best_dist)
|
||||
best_dist = dist
|
||||
best_cam = C
|
||||
|
||||
if(!best_cam)
|
||||
return ..()
|
||||
usr:lastDblClick = world.time
|
||||
usr:switchCamera(best_cam)
|
||||
|
||||
/mob/living/silicon/ai/proc/ai_camera_list()
|
||||
set category = "AI Commands"
|
||||
set name = "Show Camera List"
|
||||
@@ -55,26 +19,33 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
var/list/namecounts = list()
|
||||
var/list/humans = list()
|
||||
var/list/others = list()
|
||||
|
||||
for(var/mob/living/M in mob_list)
|
||||
//Cameras can't track people wearing an agent card or a ninja hood.
|
||||
var/human = 0
|
||||
if(istype(M, /mob/living/carbon/human))
|
||||
human = 1
|
||||
if(istype(M:wear_id, /obj/item/weapon/card/id/syndicate))
|
||||
continue
|
||||
if(istype(M:head, /obj/item/clothing/head/helmet/space/space_ninja)&&!M:head:canremove)
|
||||
continue
|
||||
if(!istype(M.loc, /turf)) //in a closet or something, AI can't see him anyways
|
||||
continue
|
||||
if(istype(M.loc.loc, /area/wizard_station))
|
||||
continue
|
||||
if(M.invisibility)//cloaked
|
||||
// Easy checks first.
|
||||
// Don't detect mobs on Centcom. Since the wizard den is on Centcomm, we only need this.
|
||||
if(M.loc.z == 2)
|
||||
continue
|
||||
if(M == usr)
|
||||
continue
|
||||
if(M.invisibility)//cloaked
|
||||
continue
|
||||
if(M.digitalcamo)
|
||||
continue
|
||||
if(M.loc.z == 2) // Don't detect mobs on Centcom
|
||||
|
||||
// Human check
|
||||
var/human = 0
|
||||
if(istype(M, /mob/living/carbon/human))
|
||||
human = 1
|
||||
var/mob/living/carbon/human/H = M
|
||||
//Cameras can't track people wearing an agent card or a ninja hood.
|
||||
if(istype(H.wear_id, /obj/item/weapon/card/id/syndicate))
|
||||
continue
|
||||
if(istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja))
|
||||
var/obj/item/clothing/head/helmet/space/space_ninja/hood = H.head
|
||||
if(!hood.canremove)
|
||||
continue
|
||||
// Now, are they viewable by a camera? (This is last because it's the most intensive check)
|
||||
if(!cameranet.checkCameraVis(M))
|
||||
continue
|
||||
|
||||
var/name = M.name
|
||||
@@ -104,9 +75,10 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
var/mob/living/silicon/ai/U = usr
|
||||
|
||||
U.cameraFollow = target
|
||||
U << text("Now tracking [] on camera.", target.name)
|
||||
if (U.machine == null)
|
||||
U.machine = U
|
||||
//U << text("Now tracking [] on camera.", target.name)
|
||||
//if (U.machine == null)
|
||||
// U.machine = U
|
||||
U << "Now tracking [target.name] on camera."
|
||||
|
||||
spawn (0)
|
||||
while (U.cameraFollow == target)
|
||||
@@ -117,7 +89,7 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
U << "Follow camera mode terminated."
|
||||
U.cameraFollow = null
|
||||
return
|
||||
if(istype(target:head, /obj/item/clothing/head/helmet/space/space_ninja)&&!target:head:canremove)
|
||||
if(istype(target:head, /obj/item/clothing/head/helmet/space/space_ninja) && !target:head:canremove)
|
||||
U << "Follow camera mode terminated."
|
||||
U.cameraFollow = null
|
||||
return
|
||||
@@ -132,47 +104,14 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
return
|
||||
else if (!target || !istype(target.loc, /turf)) //in a closet
|
||||
U << "Target is not on or near any active cameras on the station. We'll check again in 5 seconds (unless you use the cancel-camera verb)."
|
||||
sleep(40) //because we're sleeping another second after this (a few lines down)
|
||||
sleep(30) //because we're sleeping another second after this (a few lines down)
|
||||
continue
|
||||
else if(!cameranet.checkCameraVis(target))
|
||||
U << "Target is not on or near any active cameras on the station. We'll check again in 5 seconds (unless you use the cancel-camera verb)."
|
||||
sleep(30) //because we're sleeping another second after this (a few lines down)
|
||||
continue
|
||||
|
||||
var/obj/machinery/camera/C = U.current
|
||||
if ((C && istype(C, /obj/machinery/camera)) || C==null)
|
||||
|
||||
if(isrobot(target))
|
||||
var/mob/living/silicon/robot/R = target
|
||||
C = R.camera
|
||||
U.reset_view(C)
|
||||
else
|
||||
var/closestDist = -1
|
||||
if (C!=null)
|
||||
if (C.status)
|
||||
closestDist = get_dist(C, target)
|
||||
//U << text("Dist = [] for camera []", closestDist, C.name)
|
||||
var/zmatched = 0
|
||||
if (closestDist > 7 || closestDist == -1)
|
||||
//check other cameras
|
||||
var/obj/machinery/camera/closest = C
|
||||
for(var/obj/machinery/camera/C2 in Cameras)
|
||||
if (C2.network == src.network)
|
||||
if (C2.z == target.z)
|
||||
zmatched = 1
|
||||
if (C2.status)
|
||||
var/dist = get_dist(C2, target)
|
||||
if ((dist < closestDist) || (closestDist == -1))
|
||||
closestDist = dist
|
||||
closest = C2
|
||||
//U << text("Closest camera dist = [], for camera []", closestDist, closest.area.name)
|
||||
|
||||
if (closest != C)
|
||||
U.reset_view(closest)
|
||||
//use_power(50)
|
||||
if (zmatched == 0)
|
||||
U << "Target is not on or near any active cameras on the station. We'll check again in 5 seconds (unless you use the cancel-camera verb)."
|
||||
sleep(40) //because we're sleeping another second after this (a few lines down)
|
||||
else
|
||||
U << "Follow camera mode ended."
|
||||
U.cameraFollow = null
|
||||
|
||||
U.eyeobj.setLoc(get_turf(target))
|
||||
sleep(10)
|
||||
|
||||
/proc/camera_sort(list/L)
|
||||
@@ -191,7 +130,6 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
L.Swap(j, j + 1)
|
||||
return L
|
||||
|
||||
|
||||
/mob/living/silicon/ai/attack_ai(var/mob/user as mob)
|
||||
if (user != src)
|
||||
return
|
||||
@@ -199,13 +137,8 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
if (stat == 2)
|
||||
return
|
||||
|
||||
// If they cancel then just put them back to their old camera
|
||||
var/obj/machinery/camera/tempC = src.current
|
||||
user.machine = src
|
||||
switchCamera(null)
|
||||
|
||||
var/list/L = list()
|
||||
for (var/obj/machinery/camera/C in Cameras)
|
||||
for (var/obj/machinery/camera/C in cameranet.cameras)
|
||||
L.Add(C)
|
||||
|
||||
camera_sort(L)
|
||||
@@ -214,19 +147,15 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
D["Cancel"] = "Cancel"
|
||||
for (var/obj/machinery/camera/C in L)
|
||||
if (C.network == src.network)
|
||||
D[text("[][]", C.c_tag, (C.status ? null : " (Deactivated)"))] = C
|
||||
D[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C
|
||||
|
||||
var/t = input(user, "Which camera should you change to?") as null|anything in D
|
||||
|
||||
if (!t || t == "Cancel")
|
||||
if(tempC && tempC.status)
|
||||
switchCamera(tempC)
|
||||
else
|
||||
switchCamera(null)
|
||||
return 0
|
||||
|
||||
var/obj/machinery/camera/C = D[t]
|
||||
switchCamera(C)
|
||||
src.eyeobj.setLoc(C)
|
||||
|
||||
return
|
||||
|
||||
@@ -234,13 +163,15 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
if(prob(100/(hardened + severity)))
|
||||
icon_state = "cameraemp"
|
||||
network = null //Not the best way but it will do. I think.
|
||||
cameranet.removeCamera(src)
|
||||
stat |= EMPED
|
||||
sd_SetLuminosity(0)
|
||||
spawn(900)
|
||||
network = initial(network)
|
||||
icon_state = initial(icon_state)
|
||||
for(var/mob/living/silicon/ai/O in mob_list)
|
||||
if (O.current == src)
|
||||
O.cancel_camera()
|
||||
O << "Your connection to the camera has been lost."
|
||||
stat &= ~EMPED
|
||||
if(can_use())
|
||||
cameranet.addCamera(src)
|
||||
for(var/mob/O in mob_list)
|
||||
if (istype(O.machine, /obj/machinery/computer/security))
|
||||
var/obj/machinery/computer/security/S = O.machine
|
||||
@@ -266,9 +197,9 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
/obj/machinery/camera/attack_ai(var/mob/living/silicon/ai/user as mob)
|
||||
if (!istype(user))
|
||||
return
|
||||
if (src.network != user.network || !(src.status))
|
||||
if (!src.can_use())
|
||||
return
|
||||
user.reset_view(src)
|
||||
user.eyeobj.setLoc(get_turf(src))
|
||||
|
||||
/obj/machinery/camera/attack_paw(mob/living/carbon/alien/humanoid/user as mob)
|
||||
if(!istype(user))
|
||||
@@ -329,7 +260,7 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
case.motion = 1
|
||||
del(src)
|
||||
else if (istype(W, /obj/item/weapon/camera_bug))
|
||||
if (!src.status)
|
||||
if (!src.can_use())
|
||||
user << "\blue Camera non-functional"
|
||||
return
|
||||
if (src.bugged)
|
||||
@@ -381,10 +312,6 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
// now disconnect anyone using the camera
|
||||
//Apparently, this will disconnect anyone even if the camera was re-activated.
|
||||
//I guess that doesn't matter since they can't use it anyway?
|
||||
for(var/mob/living/silicon/ai/O in player_list)
|
||||
if (O.current == src)
|
||||
O.cancel_camera()
|
||||
O << "Your connection to the camera has been lost."
|
||||
for(var/mob/O in player_list)
|
||||
if (istype(O.machine, /obj/machinery/computer/security))
|
||||
var/obj/machinery/computer/security/S = O.machine
|
||||
@@ -393,6 +320,13 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
O.reset_view(null)
|
||||
O << "The screen bursts into static."
|
||||
|
||||
/obj/machinery/camera/proc/can_use()
|
||||
if(!status)
|
||||
return 0
|
||||
if(stat & EMPED)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/atom/proc/auto_turn()
|
||||
//Automatically turns based on nearby walls.
|
||||
var/turf/simulated/wall/T = null
|
||||
@@ -414,10 +348,17 @@ var/global/list/obj/machinery/camera/Cameras = list()
|
||||
//Return a working camera that can see a given mob
|
||||
//or null if none
|
||||
/proc/seen_by_camera(var/mob/M)
|
||||
for(var/obj/machinery/camera/C in oview(4, M))
|
||||
if(C.can_use()) // check if camera disabled
|
||||
return C
|
||||
break
|
||||
return null
|
||||
|
||||
for(var/obj/machinery/camera/C in oview(M))
|
||||
if(C.status) // check if camera disabled
|
||||
/proc/near_range_camera(var/mob/M)
|
||||
|
||||
for(var/obj/machinery/camera/C in range(4, M))
|
||||
if(C.can_use()) // check if camera disabled
|
||||
return C
|
||||
break
|
||||
|
||||
return null
|
||||
return null
|
||||
@@ -30,10 +30,11 @@
|
||||
attack_hand(var/mob/user as mob)
|
||||
if(stat & (NOPOWER|BROKEN)) return
|
||||
|
||||
user.machine = src
|
||||
if(!isAI(user))
|
||||
user.machine = src
|
||||
|
||||
var/list/L = list()
|
||||
for (var/obj/machinery/camera/C in Cameras)
|
||||
for (var/obj/machinery/camera/C in cameranet.cameras)
|
||||
L.Add(C)
|
||||
|
||||
camera_sort(L)
|
||||
@@ -56,14 +57,20 @@
|
||||
return 0
|
||||
|
||||
if(C)
|
||||
if ((get_dist(user, src) > 1 || user.machine != src || user.blinded || !( user.canmove ) || !( C.status )) && (!istype(user, /mob/living/silicon/ai)))
|
||||
if(!C.status)
|
||||
if ((get_dist(user, src) > 1 || user.machine != src || user.blinded || !( user.canmove ) || !( C.can_use() )) && (!istype(user, /mob/living/silicon/ai)))
|
||||
if(!C.can_use() && !isAI(user))
|
||||
src.current = null
|
||||
return 0
|
||||
else
|
||||
src.current = C
|
||||
use_power(50)
|
||||
spawn( 5 )
|
||||
if(isAI(user))
|
||||
var/mob/living/silicon/ai/A = user
|
||||
A.eyeobj.setLoc(get_turf(C))
|
||||
A.client.eye = A.eyeobj
|
||||
else
|
||||
src.current = C
|
||||
use_power(50)
|
||||
|
||||
spawn(5)
|
||||
attack_hand(user)
|
||||
return
|
||||
|
||||
|
||||
@@ -31,9 +31,8 @@ Possible to do for anyone motivated enough:
|
||||
/*There are pretty much only three ways to interact here.
|
||||
I don't need to check for client since they're clicking on an object.
|
||||
This may change in the future but for now will suffice.*/
|
||||
if(user.client.eye!=src)//Set client eye on the object if it's not already.
|
||||
user.current = src
|
||||
user.reset_view(src)
|
||||
if(user.eyeobj.loc != src.loc)//Set client eye on the object if it's not already.
|
||||
user.eyeobj.setLoc(get_turf(src))
|
||||
else if(!hologram)//If there is no hologram, possibly make one.
|
||||
activate_holo(user)
|
||||
else if(master==user)//If there is a hologram, remove it. But only if the user is the master. Otherwise do nothing.
|
||||
@@ -41,7 +40,7 @@ Possible to do for anyone motivated enough:
|
||||
return
|
||||
|
||||
/obj/machinery/hologram/holopad/proc/activate_holo(mob/living/silicon/ai/user)
|
||||
if(!(stat & NOPOWER)&&user.client.eye==src)//If the projector has power and client eye is on it.
|
||||
if(!(stat & NOPOWER) && user.eyeobj.loc == src.loc)//If the projector has power and client eye is on it.
|
||||
if(!hologram)//If there is not already a hologram.
|
||||
create_holo(user)//Create one.
|
||||
for(var/mob/M in viewers())
|
||||
@@ -76,6 +75,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
hologram.name = "AI hologram"//If someone decides to right click.
|
||||
sd_SetLuminosity(1)//To make the pad glowy.
|
||||
icon_state = "holopad1"
|
||||
A.current = src
|
||||
master = A//AI is the master.
|
||||
use_power = 2//Active power usage.
|
||||
return 1
|
||||
@@ -83,6 +83,8 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
/obj/machinery/hologram/holopad/proc/clear_holo()
|
||||
hologram.sd_SetLuminosity(0)//Clear lighting.
|
||||
del(hologram)//Get rid of hologram.
|
||||
if(master.current == src)
|
||||
master.current = null
|
||||
master = null//Null the master, since no-one is using it now.
|
||||
sd_SetLuminosity(0)//Clear lighting for the parent.
|
||||
icon_state = "holopad0"
|
||||
@@ -91,8 +93,8 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
|
||||
/obj/machinery/hologram/holopad/process()
|
||||
if(hologram)//If there is a hologram.
|
||||
if(master&&!master.stat&&master.client&&master.client.eye==src)//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector.
|
||||
if( !(get_dist(src,hologram.loc)>3||stat & NOPOWER) )//If the hologram is not out of bounds and the machine has power.
|
||||
if(master && !master.stat && master.client && master.eyeobj.loc == src.loc)//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector.
|
||||
if(!(stat & NOPOWER))//If the machine has power.
|
||||
return 1
|
||||
clear_holo()//If not, we want to get rid of the hologram.
|
||||
return 1
|
||||
|
||||
@@ -184,7 +184,13 @@ Class Procs:
|
||||
return 0
|
||||
|
||||
/obj/machinery/attack_ai(mob/user as mob)
|
||||
return src.attack_hand(user)
|
||||
if(isrobot(user))
|
||||
// For some reason attack_robot doesn't work
|
||||
// This is to stop robots from using cameras to remotely control machines.
|
||||
if(user.client && user.client.eye == user)
|
||||
return src.attack_hand(user)
|
||||
else
|
||||
return src.attack_hand(user)
|
||||
|
||||
/obj/machinery/attack_paw(mob/user as mob)
|
||||
return src.attack_hand(user)
|
||||
|
||||
+1881
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
/mob/living/silicon/ai/proc/ai_statuschange()
|
||||
set category = "AI Commands"
|
||||
set name = "AI Status"
|
||||
|
||||
if(usr.stat == 2)
|
||||
usr <<"You cannot change your emotional status because you are dead!"
|
||||
return
|
||||
var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Friend Computer")
|
||||
var/emote = input("Please, select a status!", "AI Status", null, null) in ai_emotions
|
||||
for (var/obj/machinery/ai_status_display/AISD in world) //change status
|
||||
spawn( 0 )
|
||||
AISD.emotion = emote
|
||||
for (var/obj/machinery/status_display/SD in world) //if Friend Computer, change ALL displays
|
||||
if(emote=="Friend Computer")
|
||||
spawn(0)
|
||||
SD.friendc = 1
|
||||
else
|
||||
spawn(0)
|
||||
SD.friendc = 0
|
||||
return
|
||||
Reference in New Issue
Block a user