Merge branch 'master' into master
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -42,9 +42,8 @@
|
||||
|
||||
|
||||
/mob/living/silicon/ai/bullet_act(obj/item/projectile/Proj)
|
||||
..(Proj)
|
||||
. = ..()
|
||||
updatehealth()
|
||||
return 2
|
||||
|
||||
/mob/living/silicon/ai/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0)
|
||||
return // no eyes, no flashing
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
/mob/living/silicon/ai/death(gibbed)
|
||||
if(stat == DEAD)
|
||||
return
|
||||
|
||||
. = ..()
|
||||
|
||||
var/old_icon = icon_state
|
||||
if("[icon_state]_dead" in icon_states(icon))
|
||||
icon_state = "[icon_state]_dead"
|
||||
else
|
||||
icon_state = "ai_dead"
|
||||
if("[old_icon]_death_transition" in icon_states(icon))
|
||||
flick("[old_icon]_death_transition", src)
|
||||
|
||||
cameraFollow = null
|
||||
|
||||
anchored = FALSE //unbolt floorbolts
|
||||
update_canmove()
|
||||
if(eyeobj)
|
||||
eyeobj.setLoc(get_turf(src))
|
||||
set_eyeobj_visible(FALSE)
|
||||
|
||||
GLOB.shuttle_caller_list -= src
|
||||
SSshuttle.autoEvac()
|
||||
|
||||
ShutOffDoomsdayDevice()
|
||||
|
||||
if(explosive)
|
||||
spawn(10)
|
||||
explosion(src.loc, 3, 6, 12, 15)
|
||||
|
||||
if(src.key)
|
||||
for(var/each in GLOB.ai_status_displays) //change status
|
||||
var/obj/machinery/status_display/ai/O = each
|
||||
O.mode = 2
|
||||
O.update()
|
||||
|
||||
if(istype(loc, /obj/item/aicard/aitater))
|
||||
loc.icon_state = "aitater-404"
|
||||
else if(istype(loc, /obj/item/aicard/aispook))
|
||||
loc.icon_state = "aispook-404"
|
||||
else if(istype(loc, /obj/item/aicard))
|
||||
loc.icon_state = "aicard-404"
|
||||
|
||||
/mob/living/silicon/ai/proc/ShutOffDoomsdayDevice()
|
||||
if(nuking)
|
||||
set_security_level("red")
|
||||
nuking = FALSE
|
||||
for(var/obj/item/pinpointer/nuke/P in GLOB.pinpointer_list)
|
||||
P.switch_mode_to(TRACK_NUKE_DISK) //Party's over, back to work, everyone
|
||||
P.alert = FALSE
|
||||
|
||||
if(doomsday_device)
|
||||
doomsday_device.timing = FALSE
|
||||
SSshuttle.clearHostileEnvironment(doomsday_device)
|
||||
qdel(doomsday_device)
|
||||
/mob/living/silicon/ai/death(gibbed)
|
||||
if(stat == DEAD)
|
||||
return
|
||||
|
||||
. = ..()
|
||||
|
||||
var/old_icon = icon_state
|
||||
if("[icon_state]_dead" in icon_states(icon))
|
||||
icon_state = "[icon_state]_dead"
|
||||
else
|
||||
icon_state = "ai_dead"
|
||||
if("[old_icon]_death_transition" in icon_states(icon))
|
||||
flick("[old_icon]_death_transition", src)
|
||||
|
||||
cameraFollow = null
|
||||
|
||||
move_resist = MOVE_FORCE_NORMAL
|
||||
update_canmove()
|
||||
if(eyeobj)
|
||||
eyeobj.setLoc(get_turf(src))
|
||||
set_eyeobj_visible(FALSE)
|
||||
|
||||
GLOB.shuttle_caller_list -= src
|
||||
SSshuttle.autoEvac()
|
||||
|
||||
ShutOffDoomsdayDevice()
|
||||
|
||||
if(explosive)
|
||||
spawn(10)
|
||||
explosion(src.loc, 3, 6, 12, 15)
|
||||
|
||||
if(src.key)
|
||||
for(var/each in GLOB.ai_status_displays) //change status
|
||||
var/obj/machinery/status_display/ai/O = each
|
||||
O.mode = 2
|
||||
O.update()
|
||||
|
||||
if(istype(loc, /obj/item/aicard/aitater))
|
||||
loc.icon_state = "aitater-404"
|
||||
else if(istype(loc, /obj/item/aicard/aispook))
|
||||
loc.icon_state = "aispook-404"
|
||||
else if(istype(loc, /obj/item/aicard))
|
||||
loc.icon_state = "aicard-404"
|
||||
|
||||
/mob/living/silicon/ai/proc/ShutOffDoomsdayDevice()
|
||||
if(nuking)
|
||||
set_security_level("red")
|
||||
nuking = FALSE
|
||||
for(var/obj/item/pinpointer/nuke/P in GLOB.pinpointer_list)
|
||||
P.switch_mode_to(TRACK_NUKE_DISK) //Party's over, back to work, everyone
|
||||
P.alert = FALSE
|
||||
|
||||
if(doomsday_device)
|
||||
doomsday_device.timing = FALSE
|
||||
SSshuttle.clearHostileEnvironment(doomsday_device)
|
||||
qdel(doomsday_device)
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/mob/living/silicon/ai/examine(mob/user)
|
||||
. = list("<span class='info'>*---------*\nThis is [icon2html(src, user)] <EM>[src]</EM>!")
|
||||
if (stat == DEAD)
|
||||
. += "<span class='deadsay'>It appears to be powered-down.</span>"
|
||||
else
|
||||
if (getBruteLoss())
|
||||
if (getBruteLoss() < 30)
|
||||
. += "<span class='warning'>It looks slightly dented.</span>"
|
||||
else
|
||||
. += "<span class='danger'>It looks severely dented!</span>"
|
||||
if (getFireLoss())
|
||||
if (getFireLoss() < 30)
|
||||
. += "<span class='warning'>It looks slightly charred.</span>"
|
||||
else
|
||||
. += "<span class='danger'>Its casing is melted and heat-warped!</span>"
|
||||
if(deployed_shell)
|
||||
. += "The wireless networking light is blinking."
|
||||
else if (!shunted && !client)
|
||||
. += "[src]Core.exe has stopped responding! NTOS is searching for a solution to the problem..."
|
||||
. += "*---------*</span>"
|
||||
|
||||
/mob/living/silicon/ai/examine(mob/user)
|
||||
. = list("<span class='info'>*---------*\nThis is [icon2html(src, user)] <EM>[src]</EM>!")
|
||||
if (stat == DEAD)
|
||||
. += "<span class='deadsay'>It appears to be powered-down.</span>"
|
||||
else
|
||||
if (getBruteLoss())
|
||||
if (getBruteLoss() < 30)
|
||||
. += "<span class='warning'>It looks slightly dented.</span>"
|
||||
else
|
||||
. += "<span class='danger'>It looks severely dented!</span>"
|
||||
if (getFireLoss())
|
||||
if (getFireLoss() < 30)
|
||||
. += "<span class='warning'>It looks slightly charred.</span>"
|
||||
else
|
||||
. += "<span class='danger'>Its casing is melted and heat-warped!</span>"
|
||||
if(deployed_shell)
|
||||
. += "The wireless networking light is blinking."
|
||||
else if (!shunted && !client)
|
||||
. += "[src]Core.exe has stopped responding! NTOS is searching for a solution to the problem..."
|
||||
. += "*---------*</span>"
|
||||
|
||||
. += ..()
|
||||
@@ -1,204 +1,204 @@
|
||||
// CAMERA NET
|
||||
//
|
||||
// The datum containing all the chunks.
|
||||
|
||||
#define CHUNK_SIZE 16 // Only chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc..
|
||||
|
||||
GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
|
||||
|
||||
/datum/cameranet
|
||||
var/name = "Camera Net" // Name to show for VV and stat()
|
||||
|
||||
// The cameras on the map, no matter if they work or not. Updated in obj/machinery/camera.dm by New() and Del().
|
||||
var/list/cameras = list()
|
||||
// The chunks of the map, mapping the areas that the cameras can see.
|
||||
var/list/chunks = list()
|
||||
var/ready = 0
|
||||
|
||||
// The object used for the clickable stat() button.
|
||||
var/obj/effect/statclick/statclick
|
||||
|
||||
// The objects used in vis_contents of obscured turfs
|
||||
var/list/vis_contents_objects
|
||||
var/obj/effect/overlay/camera_static/vis_contents_opaque
|
||||
var/obj/effect/overlay/camera_static/vis_contents_transparent
|
||||
// The image given to the effect in vis_contents on AI clients
|
||||
var/image/obscured
|
||||
var/image/obscured_transparent
|
||||
|
||||
/datum/cameranet/New()
|
||||
vis_contents_opaque = new /obj/effect/overlay/camera_static()
|
||||
vis_contents_transparent = new /obj/effect/overlay/camera_static/transparent()
|
||||
vis_contents_objects = list(vis_contents_opaque, vis_contents_transparent)
|
||||
|
||||
obscured = new('icons/effects/cameravis.dmi', vis_contents_opaque, null, CAMERA_STATIC_LAYER)
|
||||
obscured.plane = CAMERA_STATIC_PLANE
|
||||
|
||||
obscured_transparent = new('icons/effects/cameravis.dmi', vis_contents_transparent, null, CAMERA_STATIC_LAYER)
|
||||
obscured_transparent.plane = CAMERA_STATIC_PLANE
|
||||
|
||||
// Checks if a chunk has been Generated in x, y, z.
|
||||
/datum/cameranet/proc/chunkGenerated(x, y, z)
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
return chunks["[x],[y],[z]"]
|
||||
|
||||
// Returns the chunk in the x, y, z.
|
||||
// If there is no chunk, it creates a new chunk and returns that.
|
||||
/datum/cameranet/proc/getCameraChunk(x, y, z)
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
var/key = "[x],[y],[z]"
|
||||
. = chunks[key]
|
||||
if(!.)
|
||||
chunks[key] = . = new /datum/camerachunk(x, y, z)
|
||||
|
||||
// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set.
|
||||
|
||||
/datum/cameranet/proc/visibility(list/moved_eyes, client/C, list/other_eyes, use_static = USE_STATIC_OPAQUE)
|
||||
if(!islist(moved_eyes))
|
||||
moved_eyes = moved_eyes ? list(moved_eyes) : list()
|
||||
if(islist(other_eyes))
|
||||
other_eyes = (other_eyes - moved_eyes)
|
||||
else
|
||||
other_eyes = list()
|
||||
|
||||
if(C)
|
||||
switch(use_static)
|
||||
if(USE_STATIC_TRANSPARENT)
|
||||
C.images += obscured_transparent
|
||||
if(USE_STATIC_OPAQUE)
|
||||
C.images += obscured
|
||||
|
||||
for(var/V in moved_eyes)
|
||||
var/mob/camera/aiEye/eye = V
|
||||
var/list/visibleChunks = list()
|
||||
if(eye.loc)
|
||||
// 0xf = 15
|
||||
var/static_range = eye.static_visibility_range
|
||||
var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1)
|
||||
var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1)
|
||||
var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1)
|
||||
var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1)
|
||||
|
||||
|
||||
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
|
||||
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
|
||||
visibleChunks |= getCameraChunk(x, y, eye.z)
|
||||
|
||||
var/list/remove = eye.visibleCameraChunks - visibleChunks
|
||||
var/list/add = visibleChunks - eye.visibleCameraChunks
|
||||
|
||||
for(var/chunk in remove)
|
||||
var/datum/camerachunk/c = chunk
|
||||
c.remove(eye, FALSE)
|
||||
|
||||
for(var/chunk in add)
|
||||
var/datum/camerachunk/c = chunk
|
||||
c.add(eye)
|
||||
|
||||
if(!eye.visibleCameraChunks.len)
|
||||
var/client/client = eye.GetViewerClient()
|
||||
if(client)
|
||||
switch(eye.use_static)
|
||||
if(USE_STATIC_TRANSPARENT)
|
||||
client.images -= GLOB.cameranet.obscured_transparent
|
||||
if(USE_STATIC_OPAQUE)
|
||||
client.images -= GLOB.cameranet.obscured
|
||||
|
||||
// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open.
|
||||
|
||||
/datum/cameranet/proc/updateVisibility(atom/A, opacity_check = 1)
|
||||
if(!SSticker || (opacity_check && !A.opacity))
|
||||
return
|
||||
majorChunkChange(A, 2)
|
||||
|
||||
/datum/cameranet/proc/updateChunk(x, y, z)
|
||||
var/datum/camerachunk/chunk = chunkGenerated(x, y, z)
|
||||
if (!chunk)
|
||||
return
|
||||
chunk.hasChanged()
|
||||
|
||||
// Removes a camera from a chunk.
|
||||
|
||||
/datum/cameranet/proc/removeCamera(obj/machinery/camera/c)
|
||||
majorChunkChange(c, 0)
|
||||
|
||||
// Add a camera to a chunk.
|
||||
|
||||
/datum/cameranet/proc/addCamera(obj/machinery/camera/c)
|
||||
if(c.can_use())
|
||||
majorChunkChange(c, 1)
|
||||
|
||||
// Used for Cyborg cameras. Since portable cameras can be in ANY chunk.
|
||||
|
||||
/datum/cameranet/proc/updatePortableCamera(obj/machinery/camera/c)
|
||||
if(c.can_use())
|
||||
majorChunkChange(c, 1)
|
||||
|
||||
// Never access this proc directly!!!!
|
||||
// This will update the chunk and all the surrounding chunks.
|
||||
// It will also add the atom to the cameras list if you set the choice to 1.
|
||||
// Setting the choice to 0 will remove the camera from the chunks.
|
||||
// If you want to update the chunks around an object, without adding/removing a camera, use choice 2.
|
||||
|
||||
/datum/cameranet/proc/majorChunkChange(atom/c, choice)
|
||||
if(!c)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(c)
|
||||
if(T)
|
||||
var/x1 = max(0, T.x - (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/y1 = max(0, T.y - (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/x2 = min(world.maxx, T.x + (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/y2 = min(world.maxy, T.y + (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
|
||||
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
|
||||
var/datum/camerachunk/chunk = chunkGenerated(x, y, T.z)
|
||||
if(chunk)
|
||||
if(choice == 0)
|
||||
// Remove the camera.
|
||||
chunk.cameras -= c
|
||||
else if(choice == 1)
|
||||
// You can't have the same camera in the list twice.
|
||||
chunk.cameras |= c
|
||||
chunk.hasChanged()
|
||||
|
||||
// Will check if a mob is on a viewable turf. Returns 1 if it is, otherwise returns 0.
|
||||
|
||||
/datum/cameranet/proc/checkCameraVis(mob/living/target)
|
||||
var/turf/position = get_turf(target)
|
||||
return checkTurfVis(position)
|
||||
|
||||
|
||||
/datum/cameranet/proc/checkTurfVis(turf/position)
|
||||
var/datum/camerachunk/chunk = chunkGenerated(position.x, position.y, position.z)
|
||||
if(chunk)
|
||||
if(chunk.changed)
|
||||
chunk.hasChanged(1) // Update now, no matter if it's visible or not.
|
||||
if(chunk.visibleTurfs[position])
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/cameranet/proc/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
stat(name, statclick.update("Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]"))
|
||||
|
||||
/obj/effect/overlay/camera_static
|
||||
name = "static"
|
||||
icon = null
|
||||
icon_state = null
|
||||
anchored = TRUE // should only appear in vis_contents, but to be safe
|
||||
appearance_flags = RESET_TRANSFORM | TILE_BOUND
|
||||
// this combination makes the static block clicks to everything below it,
|
||||
// without appearing in the right-click menu for non-AI clients
|
||||
mouse_opacity = MOUSE_OPACITY_ICON
|
||||
invisibility = INVISIBILITY_ABSTRACT
|
||||
|
||||
layer = CAMERA_STATIC_LAYER
|
||||
plane = CAMERA_STATIC_PLANE
|
||||
|
||||
/obj/effect/overlay/camera_static/transparent
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
// CAMERA NET
|
||||
//
|
||||
// The datum containing all the chunks.
|
||||
|
||||
#define CHUNK_SIZE 16 // Only chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc..
|
||||
|
||||
GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
|
||||
|
||||
/datum/cameranet
|
||||
var/name = "Camera Net" // Name to show for VV and stat()
|
||||
|
||||
// The cameras on the map, no matter if they work or not. Updated in obj/machinery/camera.dm by New() and Del().
|
||||
var/list/cameras = list()
|
||||
// The chunks of the map, mapping the areas that the cameras can see.
|
||||
var/list/chunks = list()
|
||||
var/ready = 0
|
||||
|
||||
// The object used for the clickable stat() button.
|
||||
var/obj/effect/statclick/statclick
|
||||
|
||||
// The objects used in vis_contents of obscured turfs
|
||||
var/list/vis_contents_objects
|
||||
var/obj/effect/overlay/camera_static/vis_contents_opaque
|
||||
var/obj/effect/overlay/camera_static/vis_contents_transparent
|
||||
// The image given to the effect in vis_contents on AI clients
|
||||
var/image/obscured
|
||||
var/image/obscured_transparent
|
||||
|
||||
/datum/cameranet/New()
|
||||
vis_contents_opaque = new /obj/effect/overlay/camera_static()
|
||||
vis_contents_transparent = new /obj/effect/overlay/camera_static/transparent()
|
||||
vis_contents_objects = list(vis_contents_opaque, vis_contents_transparent)
|
||||
|
||||
obscured = new('icons/effects/cameravis.dmi', vis_contents_opaque, null, CAMERA_STATIC_LAYER)
|
||||
obscured.plane = CAMERA_STATIC_PLANE
|
||||
|
||||
obscured_transparent = new('icons/effects/cameravis.dmi', vis_contents_transparent, null, CAMERA_STATIC_LAYER)
|
||||
obscured_transparent.plane = CAMERA_STATIC_PLANE
|
||||
|
||||
// Checks if a chunk has been Generated in x, y, z.
|
||||
/datum/cameranet/proc/chunkGenerated(x, y, z)
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
return chunks["[x],[y],[z]"]
|
||||
|
||||
// Returns the chunk in the x, y, z.
|
||||
// If there is no chunk, it creates a new chunk and returns that.
|
||||
/datum/cameranet/proc/getCameraChunk(x, y, z)
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
var/key = "[x],[y],[z]"
|
||||
. = chunks[key]
|
||||
if(!.)
|
||||
chunks[key] = . = new /datum/camerachunk(x, y, z)
|
||||
|
||||
// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set.
|
||||
|
||||
/datum/cameranet/proc/visibility(list/moved_eyes, client/C, list/other_eyes, use_static = USE_STATIC_OPAQUE)
|
||||
if(!islist(moved_eyes))
|
||||
moved_eyes = moved_eyes ? list(moved_eyes) : list()
|
||||
if(islist(other_eyes))
|
||||
other_eyes = (other_eyes - moved_eyes)
|
||||
else
|
||||
other_eyes = list()
|
||||
|
||||
if(C)
|
||||
switch(use_static)
|
||||
if(USE_STATIC_TRANSPARENT)
|
||||
C.images += obscured_transparent
|
||||
if(USE_STATIC_OPAQUE)
|
||||
C.images += obscured
|
||||
|
||||
for(var/V in moved_eyes)
|
||||
var/mob/camera/aiEye/eye = V
|
||||
var/list/visibleChunks = list()
|
||||
if(eye.loc)
|
||||
// 0xf = 15
|
||||
var/static_range = eye.static_visibility_range
|
||||
var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1)
|
||||
var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1)
|
||||
var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1)
|
||||
var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1)
|
||||
|
||||
|
||||
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
|
||||
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
|
||||
visibleChunks |= getCameraChunk(x, y, eye.z)
|
||||
|
||||
var/list/remove = eye.visibleCameraChunks - visibleChunks
|
||||
var/list/add = visibleChunks - eye.visibleCameraChunks
|
||||
|
||||
for(var/chunk in remove)
|
||||
var/datum/camerachunk/c = chunk
|
||||
c.remove(eye, FALSE)
|
||||
|
||||
for(var/chunk in add)
|
||||
var/datum/camerachunk/c = chunk
|
||||
c.add(eye)
|
||||
|
||||
if(!eye.visibleCameraChunks.len)
|
||||
var/client/client = eye.GetViewerClient()
|
||||
if(client)
|
||||
switch(eye.use_static)
|
||||
if(USE_STATIC_TRANSPARENT)
|
||||
client.images -= GLOB.cameranet.obscured_transparent
|
||||
if(USE_STATIC_OPAQUE)
|
||||
client.images -= GLOB.cameranet.obscured
|
||||
|
||||
// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open.
|
||||
|
||||
/datum/cameranet/proc/updateVisibility(atom/A, opacity_check = 1)
|
||||
if(!SSticker || (opacity_check && !A.opacity))
|
||||
return
|
||||
majorChunkChange(A, 2)
|
||||
|
||||
/datum/cameranet/proc/updateChunk(x, y, z)
|
||||
var/datum/camerachunk/chunk = chunkGenerated(x, y, z)
|
||||
if (!chunk)
|
||||
return
|
||||
chunk.hasChanged()
|
||||
|
||||
// Removes a camera from a chunk.
|
||||
|
||||
/datum/cameranet/proc/removeCamera(obj/machinery/camera/c)
|
||||
majorChunkChange(c, 0)
|
||||
|
||||
// Add a camera to a chunk.
|
||||
|
||||
/datum/cameranet/proc/addCamera(obj/machinery/camera/c)
|
||||
if(c.can_use())
|
||||
majorChunkChange(c, 1)
|
||||
|
||||
// Used for Cyborg cameras. Since portable cameras can be in ANY chunk.
|
||||
|
||||
/datum/cameranet/proc/updatePortableCamera(obj/machinery/camera/c)
|
||||
if(c.can_use())
|
||||
majorChunkChange(c, 1)
|
||||
|
||||
// Never access this proc directly!!!!
|
||||
// This will update the chunk and all the surrounding chunks.
|
||||
// It will also add the atom to the cameras list if you set the choice to 1.
|
||||
// Setting the choice to 0 will remove the camera from the chunks.
|
||||
// If you want to update the chunks around an object, without adding/removing a camera, use choice 2.
|
||||
|
||||
/datum/cameranet/proc/majorChunkChange(atom/c, choice)
|
||||
if(!c)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(c)
|
||||
if(T)
|
||||
var/x1 = max(0, T.x - (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/y1 = max(0, T.y - (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/x2 = min(world.maxx, T.x + (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/y2 = min(world.maxy, T.y + (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
|
||||
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
|
||||
var/datum/camerachunk/chunk = chunkGenerated(x, y, T.z)
|
||||
if(chunk)
|
||||
if(choice == 0)
|
||||
// Remove the camera.
|
||||
chunk.cameras -= c
|
||||
else if(choice == 1)
|
||||
// You can't have the same camera in the list twice.
|
||||
chunk.cameras |= c
|
||||
chunk.hasChanged()
|
||||
|
||||
// Will check if a mob is on a viewable turf. Returns 1 if it is, otherwise returns 0.
|
||||
|
||||
/datum/cameranet/proc/checkCameraVis(mob/living/target)
|
||||
var/turf/position = get_turf(target)
|
||||
return checkTurfVis(position)
|
||||
|
||||
|
||||
/datum/cameranet/proc/checkTurfVis(turf/position)
|
||||
var/datum/camerachunk/chunk = chunkGenerated(position.x, position.y, position.z)
|
||||
if(chunk)
|
||||
if(chunk.changed)
|
||||
chunk.hasChanged(1) // Update now, no matter if it's visible or not.
|
||||
if(chunk.visibleTurfs[position])
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/cameranet/proc/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
stat(name, statclick.update("Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]"))
|
||||
|
||||
/obj/effect/overlay/camera_static
|
||||
name = "static"
|
||||
icon = null
|
||||
icon_state = null
|
||||
anchored = TRUE // should only appear in vis_contents, but to be safe
|
||||
appearance_flags = RESET_TRANSFORM | TILE_BOUND
|
||||
// this combination makes the static block clicks to everything below it,
|
||||
// without appearing in the right-click menu for non-AI clients
|
||||
mouse_opacity = MOUSE_OPACITY_ICON
|
||||
invisibility = INVISIBILITY_ABSTRACT
|
||||
|
||||
layer = CAMERA_STATIC_LAYER
|
||||
plane = CAMERA_STATIC_PLANE
|
||||
|
||||
/obj/effect/overlay/camera_static/transparent
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
|
||||
@@ -1,142 +1,142 @@
|
||||
#define UPDATE_BUFFER 25 // 2.5 seconds
|
||||
|
||||
// CAMERA CHUNK
|
||||
//
|
||||
// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed.
|
||||
// Allows the AI Eye to stream these chunks and know what it can and cannot see.
|
||||
|
||||
/datum/camerachunk
|
||||
var/list/obscuredTurfs = list()
|
||||
var/list/visibleTurfs = list()
|
||||
var/list/cameras = list()
|
||||
var/list/turfs = list()
|
||||
var/list/seenby = list()
|
||||
var/changed = 0
|
||||
var/x = 0
|
||||
var/y = 0
|
||||
var/z = 0
|
||||
|
||||
// Add an AI eye to the chunk, then update if changed.
|
||||
|
||||
/datum/camerachunk/proc/add(mob/camera/aiEye/eye)
|
||||
eye.visibleCameraChunks += src
|
||||
seenby += eye
|
||||
if(changed)
|
||||
update()
|
||||
|
||||
// Remove an AI eye from the chunk, then update if changed.
|
||||
|
||||
/datum/camerachunk/proc/remove(mob/camera/aiEye/eye, remove_static_with_last_chunk = TRUE)
|
||||
eye.visibleCameraChunks -= src
|
||||
seenby -= eye
|
||||
if(remove_static_with_last_chunk && !eye.visibleCameraChunks.len)
|
||||
var/client/client = eye.GetViewerClient()
|
||||
if(client)
|
||||
switch(eye.use_static)
|
||||
if(USE_STATIC_TRANSPARENT)
|
||||
client.images -= GLOB.cameranet.obscured_transparent
|
||||
if(USE_STATIC_OPAQUE)
|
||||
client.images -= GLOB.cameranet.obscured
|
||||
|
||||
// Called when a chunk has changed. I.E: A wall was deleted.
|
||||
|
||||
/datum/camerachunk/proc/visibilityChanged(turf/loc)
|
||||
if(!visibleTurfs[loc])
|
||||
return
|
||||
hasChanged()
|
||||
|
||||
// Updates the chunk, makes sure that it doesn't update too much. If the chunk isn't being watched it will
|
||||
// instead be flagged to update the next time an AI Eye moves near it.
|
||||
|
||||
/datum/camerachunk/proc/hasChanged(update_now = 0)
|
||||
if(seenby.len || update_now)
|
||||
addtimer(CALLBACK(src, .proc/update), UPDATE_BUFFER, TIMER_UNIQUE)
|
||||
else
|
||||
changed = 1
|
||||
|
||||
// The actual updating. It gathers the visible turfs from cameras and puts them into the appropiate lists.
|
||||
|
||||
/datum/camerachunk/proc/update()
|
||||
var/list/newVisibleTurfs = list()
|
||||
|
||||
for(var/camera in cameras)
|
||||
var/obj/machinery/camera/c = camera
|
||||
|
||||
if(!c)
|
||||
continue
|
||||
|
||||
if(!c.can_use())
|
||||
continue
|
||||
|
||||
var/turf/point = locate(src.x + (CHUNK_SIZE / 2), src.y + (CHUNK_SIZE / 2), src.z)
|
||||
if(get_dist(point, c) > CHUNK_SIZE + (CHUNK_SIZE / 2))
|
||||
continue
|
||||
|
||||
for(var/turf/t in c.can_see())
|
||||
// Possible optimization: if(turfs[t]) here, rather than &= turfs afterwards.
|
||||
// List associations use a tree or hashmap of some sort (alongside the list itself)
|
||||
// so are surprisingly fast. (significantly faster than var/thingy/x in list, in testing)
|
||||
newVisibleTurfs[t] = t
|
||||
|
||||
// Removes turf that isn't in turfs.
|
||||
newVisibleTurfs &= turfs
|
||||
|
||||
var/list/visAdded = newVisibleTurfs - visibleTurfs
|
||||
var/list/visRemoved = visibleTurfs - newVisibleTurfs
|
||||
|
||||
visibleTurfs = newVisibleTurfs
|
||||
obscuredTurfs = turfs - newVisibleTurfs
|
||||
|
||||
for(var/turf in visAdded)
|
||||
var/turf/t = turf
|
||||
t.vis_contents -= GLOB.cameranet.vis_contents_objects
|
||||
|
||||
for(var/turf in visRemoved)
|
||||
var/turf/t = turf
|
||||
if(obscuredTurfs[t] && !istype(t, /turf/open/ai_visible))
|
||||
t.vis_contents += GLOB.cameranet.vis_contents_objects
|
||||
|
||||
changed = 0
|
||||
|
||||
// Create a new camera chunk, since the chunks are made as they are needed.
|
||||
|
||||
/datum/camerachunk/New(x, y, z)
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
|
||||
src.x = x
|
||||
src.y = y
|
||||
src.z = z
|
||||
|
||||
for(var/obj/machinery/camera/c in urange(CHUNK_SIZE, locate(x + (CHUNK_SIZE / 2), y + (CHUNK_SIZE / 2), z)))
|
||||
if(c.can_use())
|
||||
cameras += c
|
||||
|
||||
for(var/turf/t in block(locate(max(x, 1), max(y, 1), z), locate(min(x + CHUNK_SIZE - 1, world.maxx), min(y + CHUNK_SIZE - 1, world.maxy), z)))
|
||||
turfs[t] = t
|
||||
|
||||
for(var/camera in cameras)
|
||||
var/obj/machinery/camera/c = camera
|
||||
if(!c)
|
||||
continue
|
||||
|
||||
if(!c.can_use())
|
||||
continue
|
||||
|
||||
for(var/turf/t in c.can_see())
|
||||
// Possible optimization: if(turfs[t]) here, rather than &= turfs afterwards.
|
||||
// List associations use a tree or hashmap of some sort (alongside the list itself)
|
||||
// so are surprisingly fast. (significantly faster than var/thingy/x in list, in testing)
|
||||
visibleTurfs[t] = t
|
||||
|
||||
// Removes turf that isn't in turfs.
|
||||
visibleTurfs &= turfs
|
||||
|
||||
obscuredTurfs = turfs - visibleTurfs
|
||||
|
||||
for(var/turf in obscuredTurfs)
|
||||
var/turf/t = turf
|
||||
t.vis_contents += GLOB.cameranet.vis_contents_objects
|
||||
|
||||
#undef UPDATE_BUFFER
|
||||
#undef CHUNK_SIZE
|
||||
#define UPDATE_BUFFER 25 // 2.5 seconds
|
||||
|
||||
// CAMERA CHUNK
|
||||
//
|
||||
// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed.
|
||||
// Allows the AI Eye to stream these chunks and know what it can and cannot see.
|
||||
|
||||
/datum/camerachunk
|
||||
var/list/obscuredTurfs = list()
|
||||
var/list/visibleTurfs = list()
|
||||
var/list/cameras = list()
|
||||
var/list/turfs = list()
|
||||
var/list/seenby = list()
|
||||
var/changed = 0
|
||||
var/x = 0
|
||||
var/y = 0
|
||||
var/z = 0
|
||||
|
||||
// Add an AI eye to the chunk, then update if changed.
|
||||
|
||||
/datum/camerachunk/proc/add(mob/camera/aiEye/eye)
|
||||
eye.visibleCameraChunks += src
|
||||
seenby += eye
|
||||
if(changed)
|
||||
update()
|
||||
|
||||
// Remove an AI eye from the chunk, then update if changed.
|
||||
|
||||
/datum/camerachunk/proc/remove(mob/camera/aiEye/eye, remove_static_with_last_chunk = TRUE)
|
||||
eye.visibleCameraChunks -= src
|
||||
seenby -= eye
|
||||
if(remove_static_with_last_chunk && !eye.visibleCameraChunks.len)
|
||||
var/client/client = eye.GetViewerClient()
|
||||
if(client)
|
||||
switch(eye.use_static)
|
||||
if(USE_STATIC_TRANSPARENT)
|
||||
client.images -= GLOB.cameranet.obscured_transparent
|
||||
if(USE_STATIC_OPAQUE)
|
||||
client.images -= GLOB.cameranet.obscured
|
||||
|
||||
// Called when a chunk has changed. I.E: A wall was deleted.
|
||||
|
||||
/datum/camerachunk/proc/visibilityChanged(turf/loc)
|
||||
if(!visibleTurfs[loc])
|
||||
return
|
||||
hasChanged()
|
||||
|
||||
// Updates the chunk, makes sure that it doesn't update too much. If the chunk isn't being watched it will
|
||||
// instead be flagged to update the next time an AI Eye moves near it.
|
||||
|
||||
/datum/camerachunk/proc/hasChanged(update_now = 0)
|
||||
if(seenby.len || update_now)
|
||||
addtimer(CALLBACK(src, .proc/update), UPDATE_BUFFER, TIMER_UNIQUE)
|
||||
else
|
||||
changed = 1
|
||||
|
||||
// The actual updating. It gathers the visible turfs from cameras and puts them into the appropiate lists.
|
||||
|
||||
/datum/camerachunk/proc/update()
|
||||
var/list/newVisibleTurfs = list()
|
||||
|
||||
for(var/camera in cameras)
|
||||
var/obj/machinery/camera/c = camera
|
||||
|
||||
if(!c)
|
||||
continue
|
||||
|
||||
if(!c.can_use())
|
||||
continue
|
||||
|
||||
var/turf/point = locate(src.x + (CHUNK_SIZE / 2), src.y + (CHUNK_SIZE / 2), src.z)
|
||||
if(get_dist(point, c) > CHUNK_SIZE + (CHUNK_SIZE / 2))
|
||||
continue
|
||||
|
||||
for(var/turf/t in c.can_see())
|
||||
// Possible optimization: if(turfs[t]) here, rather than &= turfs afterwards.
|
||||
// List associations use a tree or hashmap of some sort (alongside the list itself)
|
||||
// so are surprisingly fast. (significantly faster than var/thingy/x in list, in testing)
|
||||
newVisibleTurfs[t] = t
|
||||
|
||||
// Removes turf that isn't in turfs.
|
||||
newVisibleTurfs &= turfs
|
||||
|
||||
var/list/visAdded = newVisibleTurfs - visibleTurfs
|
||||
var/list/visRemoved = visibleTurfs - newVisibleTurfs
|
||||
|
||||
visibleTurfs = newVisibleTurfs
|
||||
obscuredTurfs = turfs - newVisibleTurfs
|
||||
|
||||
for(var/turf in visAdded)
|
||||
var/turf/t = turf
|
||||
t.vis_contents -= GLOB.cameranet.vis_contents_objects
|
||||
|
||||
for(var/turf in visRemoved)
|
||||
var/turf/t = turf
|
||||
if(obscuredTurfs[t] && !istype(t, /turf/open/ai_visible))
|
||||
t.vis_contents += GLOB.cameranet.vis_contents_objects
|
||||
|
||||
changed = 0
|
||||
|
||||
// Create a new camera chunk, since the chunks are made as they are needed.
|
||||
|
||||
/datum/camerachunk/New(x, y, z)
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
|
||||
src.x = x
|
||||
src.y = y
|
||||
src.z = z
|
||||
|
||||
for(var/obj/machinery/camera/c in urange(CHUNK_SIZE, locate(x + (CHUNK_SIZE / 2), y + (CHUNK_SIZE / 2), z)))
|
||||
if(c.can_use())
|
||||
cameras += c
|
||||
|
||||
for(var/turf/t in block(locate(max(x, 1), max(y, 1), z), locate(min(x + CHUNK_SIZE - 1, world.maxx), min(y + CHUNK_SIZE - 1, world.maxy), z)))
|
||||
turfs[t] = t
|
||||
|
||||
for(var/camera in cameras)
|
||||
var/obj/machinery/camera/c = camera
|
||||
if(!c)
|
||||
continue
|
||||
|
||||
if(!c.can_use())
|
||||
continue
|
||||
|
||||
for(var/turf/t in c.can_see())
|
||||
// Possible optimization: if(turfs[t]) here, rather than &= turfs afterwards.
|
||||
// List associations use a tree or hashmap of some sort (alongside the list itself)
|
||||
// so are surprisingly fast. (significantly faster than var/thingy/x in list, in testing)
|
||||
visibleTurfs[t] = t
|
||||
|
||||
// Removes turf that isn't in turfs.
|
||||
visibleTurfs &= turfs
|
||||
|
||||
obscuredTurfs = turfs - visibleTurfs
|
||||
|
||||
for(var/turf in obscuredTurfs)
|
||||
var/turf/t = turf
|
||||
t.vis_contents += GLOB.cameranet.vis_contents_objects
|
||||
|
||||
#undef UPDATE_BUFFER
|
||||
#undef CHUNK_SIZE
|
||||
|
||||
@@ -1,206 +1,206 @@
|
||||
// AI EYE
|
||||
//
|
||||
// An invisible (no icon) mob that the AI controls to look around the station with.
|
||||
// It streams chunks as it moves around, which will show it what the AI can and cannot see.
|
||||
|
||||
/mob/camera/aiEye
|
||||
name = "Inactive AI Eye"
|
||||
|
||||
icon_state = "ai_camera"
|
||||
icon = 'icons/mob/cameramob.dmi'
|
||||
invisibility = INVISIBILITY_MAXIMUM
|
||||
hud_possible = list(ANTAG_HUD, AI_DETECT_HUD = HUD_LIST_LIST)
|
||||
var/list/visibleCameraChunks = list()
|
||||
var/mob/living/silicon/ai/ai = null
|
||||
var/relay_speech = FALSE
|
||||
var/use_static = USE_STATIC_OPAQUE
|
||||
var/static_visibility_range = 16
|
||||
var/ai_detector_visible = TRUE
|
||||
var/ai_detector_color = COLOR_RED
|
||||
|
||||
/mob/camera/aiEye/Initialize()
|
||||
. = ..()
|
||||
GLOB.aiEyes += src
|
||||
update_ai_detect_hud()
|
||||
setLoc(loc, TRUE)
|
||||
|
||||
/mob/camera/aiEye/proc/update_ai_detect_hud()
|
||||
var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
|
||||
var/list/old_images = hud_list[AI_DETECT_HUD]
|
||||
if(!ai_detector_visible)
|
||||
hud.remove_from_hud(src)
|
||||
QDEL_LIST(old_images)
|
||||
return
|
||||
|
||||
if(!hud.hudusers.len)
|
||||
//no one is watching, do not bother updating anything
|
||||
return
|
||||
hud.remove_from_hud(src)
|
||||
|
||||
var/static/list/vis_contents_objects = list()
|
||||
var/obj/effect/overlay/ai_detect_hud/hud_obj = vis_contents_objects[ai_detector_color]
|
||||
if(!hud_obj)
|
||||
hud_obj = new /obj/effect/overlay/ai_detect_hud()
|
||||
hud_obj.color = ai_detector_color
|
||||
vis_contents_objects[ai_detector_color] = hud_obj
|
||||
|
||||
var/list/new_images = list()
|
||||
var/list/turfs = get_visible_turfs()
|
||||
for(var/T in turfs)
|
||||
var/image/I = (old_images.len > new_images.len) ? old_images[new_images.len + 1] : image(null, T)
|
||||
I.loc = T
|
||||
I.vis_contents += hud_obj
|
||||
new_images += I
|
||||
for(var/i in (new_images.len + 1) to old_images.len)
|
||||
qdel(old_images[i])
|
||||
hud_list[AI_DETECT_HUD] = new_images
|
||||
hud.add_to_hud(src)
|
||||
|
||||
/mob/camera/aiEye/proc/get_visible_turfs()
|
||||
if(!isturf(loc))
|
||||
return list()
|
||||
var/client/C = GetViewerClient()
|
||||
var/view = C ? getviewsize(C.view) : getviewsize(world.view)
|
||||
var/turf/lowerleft = locate(max(1, x - (view[1] - 1)/2), max(1, y - (view[2] - 1)/2), z)
|
||||
var/turf/upperright = locate(min(world.maxx, lowerleft.x + (view[1] - 1)), min(world.maxy, lowerleft.y + (view[2] - 1)), lowerleft.z)
|
||||
return block(lowerleft, upperright)
|
||||
|
||||
// Use this when setting the aiEye's location.
|
||||
// It will also stream the chunk that the new loc is in.
|
||||
|
||||
/mob/camera/aiEye/proc/setLoc(T, force_update = FALSE, dir)
|
||||
if(ai)
|
||||
if(!isturf(ai.loc))
|
||||
return
|
||||
T = get_turf(T)
|
||||
if(!force_update && (T == get_turf(src)) )
|
||||
return //we are already here!
|
||||
if (T)
|
||||
forceMove(T)
|
||||
else
|
||||
moveToNullspace()
|
||||
if(use_static != USE_STATIC_NONE)
|
||||
ai.camera_visibility(src)
|
||||
if(ai.client && !ai.multicam_on)
|
||||
ai.client.eye = src
|
||||
update_ai_detect_hud()
|
||||
update_parallax_contents()
|
||||
//Holopad
|
||||
if(istype(ai.current, /obj/machinery/holopad))
|
||||
var/obj/machinery/holopad/H = ai.current
|
||||
H.move_hologram(ai, T, dir)
|
||||
if(ai.camera_light_on)
|
||||
ai.light_cameras()
|
||||
if(ai.master_multicam)
|
||||
ai.master_multicam.refresh_view()
|
||||
|
||||
/mob/camera/aiEye/Move()
|
||||
return 0
|
||||
|
||||
/mob/camera/aiEye/proc/GetViewerClient()
|
||||
if(ai)
|
||||
return ai.client
|
||||
return null
|
||||
|
||||
/mob/camera/aiEye/Destroy()
|
||||
if(ai)
|
||||
ai.all_eyes -= src
|
||||
ai = null
|
||||
for(var/V in visibleCameraChunks)
|
||||
var/datum/camerachunk/c = V
|
||||
c.remove(src)
|
||||
GLOB.aiEyes -= src
|
||||
if(ai_detector_visible)
|
||||
var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
|
||||
hud.remove_from_hud(src)
|
||||
var/list/L = hud_list[AI_DETECT_HUD]
|
||||
QDEL_LIST(L)
|
||||
return ..()
|
||||
|
||||
/atom/proc/move_camera_by_click()
|
||||
if(isAI(usr))
|
||||
var/mob/living/silicon/ai/AI = usr
|
||||
if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj)) && (AI.eyeobj.z == z))
|
||||
AI.cameraFollow = null
|
||||
if (isturf(loc) || isturf(src))
|
||||
AI.eyeobj.setLoc(src)
|
||||
|
||||
// This will move the AIEye. It will also cause lights near the eye to light up, if toggled.
|
||||
// This is handled in the proc below this one.
|
||||
|
||||
/client/proc/AIMove(n, direct, mob/living/silicon/ai/user)
|
||||
|
||||
var/initial = initial(user.sprint)
|
||||
var/max_sprint = 50
|
||||
|
||||
if(user.cooldown && user.cooldown < world.timeofday) // 3 seconds
|
||||
user.sprint = initial
|
||||
|
||||
for(var/i = 0; i < max(user.sprint, initial); i += 20)
|
||||
var/turf/step = get_turf(get_step(user.eyeobj, direct))
|
||||
if(step)
|
||||
user.eyeobj.setLoc(step, null, direct)
|
||||
|
||||
user.cooldown = world.timeofday + 5
|
||||
if(user.acceleration)
|
||||
user.sprint = min(user.sprint + 0.5, max_sprint)
|
||||
else
|
||||
user.sprint = initial
|
||||
|
||||
if(!user.tracking)
|
||||
user.cameraFollow = null
|
||||
|
||||
// Return to the Core.
|
||||
/mob/living/silicon/ai/proc/view_core()
|
||||
if(istype(current,/obj/machinery/holopad))
|
||||
var/obj/machinery/holopad/H = current
|
||||
H.clear_holo(src)
|
||||
else
|
||||
current = null
|
||||
cameraFollow = null
|
||||
unset_machine()
|
||||
|
||||
if(isturf(loc) && (QDELETED(eyeobj) || !eyeobj.loc))
|
||||
to_chat(src, "ERROR: Eyeobj not found. Creating new eye...")
|
||||
create_eye()
|
||||
|
||||
eyeobj?.setLoc(loc)
|
||||
|
||||
/mob/living/silicon/ai/proc/create_eye()
|
||||
if(eyeobj)
|
||||
return
|
||||
eyeobj = new /mob/camera/aiEye()
|
||||
all_eyes += eyeobj
|
||||
eyeobj.ai = src
|
||||
eyeobj.setLoc(loc)
|
||||
eyeobj.name = "[name] (AI Eye)"
|
||||
set_eyeobj_visible(TRUE)
|
||||
|
||||
/mob/living/silicon/ai/proc/set_eyeobj_visible(state = TRUE)
|
||||
if(!eyeobj)
|
||||
return
|
||||
eyeobj.mouse_opacity = state ? MOUSE_OPACITY_ICON : initial(eyeobj.mouse_opacity)
|
||||
eyeobj.invisibility = state ? INVISIBILITY_OBSERVER : initial(eyeobj.invisibility)
|
||||
|
||||
/mob/living/silicon/ai/verb/toggle_acceleration()
|
||||
set category = "AI Commands"
|
||||
set name = "Toggle Camera Acceleration"
|
||||
|
||||
if(incapacitated())
|
||||
return
|
||||
acceleration = !acceleration
|
||||
to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].")
|
||||
|
||||
/mob/camera/aiEye/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
if(relay_speech && speaker && ai && !radio_freq && speaker != ai && near_camera(speaker))
|
||||
ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
|
||||
|
||||
/obj/effect/overlay/ai_detect_hud
|
||||
name = ""
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
icon = 'icons/effects/alphacolors.dmi'
|
||||
icon_state = ""
|
||||
alpha = 100
|
||||
layer = ABOVE_ALL_MOB_LAYER
|
||||
plane = GAME_PLANE
|
||||
// AI EYE
|
||||
//
|
||||
// An invisible (no icon) mob that the AI controls to look around the station with.
|
||||
// It streams chunks as it moves around, which will show it what the AI can and cannot see.
|
||||
|
||||
/mob/camera/aiEye
|
||||
name = "Inactive AI Eye"
|
||||
|
||||
icon_state = "ai_camera"
|
||||
icon = 'icons/mob/cameramob.dmi'
|
||||
invisibility = INVISIBILITY_MAXIMUM
|
||||
hud_possible = list(ANTAG_HUD, AI_DETECT_HUD = HUD_LIST_LIST)
|
||||
var/list/visibleCameraChunks = list()
|
||||
var/mob/living/silicon/ai/ai = null
|
||||
var/relay_speech = FALSE
|
||||
var/use_static = USE_STATIC_OPAQUE
|
||||
var/static_visibility_range = 16
|
||||
var/ai_detector_visible = TRUE
|
||||
var/ai_detector_color = COLOR_RED
|
||||
|
||||
/mob/camera/aiEye/Initialize()
|
||||
. = ..()
|
||||
GLOB.aiEyes += src
|
||||
update_ai_detect_hud()
|
||||
setLoc(loc, TRUE)
|
||||
|
||||
/mob/camera/aiEye/proc/update_ai_detect_hud()
|
||||
var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
|
||||
var/list/old_images = hud_list[AI_DETECT_HUD]
|
||||
if(!ai_detector_visible)
|
||||
hud.remove_from_hud(src)
|
||||
QDEL_LIST(old_images)
|
||||
return
|
||||
|
||||
if(!hud.hudusers.len)
|
||||
//no one is watching, do not bother updating anything
|
||||
return
|
||||
hud.remove_from_hud(src)
|
||||
|
||||
var/static/list/vis_contents_objects = list()
|
||||
var/obj/effect/overlay/ai_detect_hud/hud_obj = vis_contents_objects[ai_detector_color]
|
||||
if(!hud_obj)
|
||||
hud_obj = new /obj/effect/overlay/ai_detect_hud()
|
||||
hud_obj.color = ai_detector_color
|
||||
vis_contents_objects[ai_detector_color] = hud_obj
|
||||
|
||||
var/list/new_images = list()
|
||||
var/list/turfs = get_visible_turfs()
|
||||
for(var/T in turfs)
|
||||
var/image/I = (old_images.len > new_images.len) ? old_images[new_images.len + 1] : image(null, T)
|
||||
I.loc = T
|
||||
I.vis_contents += hud_obj
|
||||
new_images += I
|
||||
for(var/i in (new_images.len + 1) to old_images.len)
|
||||
qdel(old_images[i])
|
||||
hud_list[AI_DETECT_HUD] = new_images
|
||||
hud.add_to_hud(src)
|
||||
|
||||
/mob/camera/aiEye/proc/get_visible_turfs()
|
||||
if(!isturf(loc))
|
||||
return list()
|
||||
var/client/C = GetViewerClient()
|
||||
var/view = C ? getviewsize(C.view) : getviewsize(world.view)
|
||||
var/turf/lowerleft = locate(max(1, x - (view[1] - 1)/2), max(1, y - (view[2] - 1)/2), z)
|
||||
var/turf/upperright = locate(min(world.maxx, lowerleft.x + (view[1] - 1)), min(world.maxy, lowerleft.y + (view[2] - 1)), lowerleft.z)
|
||||
return block(lowerleft, upperright)
|
||||
|
||||
// Use this when setting the aiEye's location.
|
||||
// It will also stream the chunk that the new loc is in.
|
||||
|
||||
/mob/camera/aiEye/proc/setLoc(T, force_update = FALSE, dir)
|
||||
if(ai)
|
||||
if(!isturf(ai.loc))
|
||||
return
|
||||
T = get_turf(T)
|
||||
if(!force_update && (T == get_turf(src)) )
|
||||
return //we are already here!
|
||||
if (T)
|
||||
forceMove(T)
|
||||
else
|
||||
moveToNullspace()
|
||||
if(use_static != USE_STATIC_NONE)
|
||||
ai.camera_visibility(src)
|
||||
if(ai.client && !ai.multicam_on)
|
||||
ai.client.eye = src
|
||||
update_ai_detect_hud()
|
||||
update_parallax_contents()
|
||||
//Holopad
|
||||
if(istype(ai.current, /obj/machinery/holopad))
|
||||
var/obj/machinery/holopad/H = ai.current
|
||||
H.move_hologram(ai, T, dir)
|
||||
if(ai.camera_light_on)
|
||||
ai.light_cameras()
|
||||
if(ai.master_multicam)
|
||||
ai.master_multicam.refresh_view()
|
||||
|
||||
/mob/camera/aiEye/Move()
|
||||
return 0
|
||||
|
||||
/mob/camera/aiEye/proc/GetViewerClient()
|
||||
if(ai)
|
||||
return ai.client
|
||||
return null
|
||||
|
||||
/mob/camera/aiEye/Destroy()
|
||||
if(ai)
|
||||
ai.all_eyes -= src
|
||||
ai = null
|
||||
for(var/V in visibleCameraChunks)
|
||||
var/datum/camerachunk/c = V
|
||||
c.remove(src)
|
||||
GLOB.aiEyes -= src
|
||||
if(ai_detector_visible)
|
||||
var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
|
||||
hud.remove_from_hud(src)
|
||||
var/list/L = hud_list[AI_DETECT_HUD]
|
||||
QDEL_LIST(L)
|
||||
return ..()
|
||||
|
||||
/atom/proc/move_camera_by_click()
|
||||
if(isAI(usr))
|
||||
var/mob/living/silicon/ai/AI = usr
|
||||
if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj)) && (AI.eyeobj.z == z))
|
||||
AI.cameraFollow = null
|
||||
if (isturf(loc) || isturf(src))
|
||||
AI.eyeobj.setLoc(src)
|
||||
|
||||
// This will move the AIEye. It will also cause lights near the eye to light up, if toggled.
|
||||
// This is handled in the proc below this one.
|
||||
|
||||
/client/proc/AIMove(n, direct, mob/living/silicon/ai/user)
|
||||
|
||||
var/initial = initial(user.sprint)
|
||||
var/max_sprint = 50
|
||||
|
||||
if(user.cooldown && user.cooldown < world.timeofday) // 3 seconds
|
||||
user.sprint = initial
|
||||
|
||||
for(var/i = 0; i < max(user.sprint, initial); i += 20)
|
||||
var/turf/step = get_turf(get_step(user.eyeobj, direct))
|
||||
if(step)
|
||||
user.eyeobj.setLoc(step, null, direct)
|
||||
|
||||
user.cooldown = world.timeofday + 5
|
||||
if(user.acceleration)
|
||||
user.sprint = min(user.sprint + 0.5, max_sprint)
|
||||
else
|
||||
user.sprint = initial
|
||||
|
||||
if(!user.tracking)
|
||||
user.cameraFollow = null
|
||||
|
||||
// Return to the Core.
|
||||
/mob/living/silicon/ai/proc/view_core()
|
||||
if(istype(current,/obj/machinery/holopad))
|
||||
var/obj/machinery/holopad/H = current
|
||||
H.clear_holo(src)
|
||||
else
|
||||
current = null
|
||||
cameraFollow = null
|
||||
unset_machine()
|
||||
|
||||
if(isturf(loc) && (QDELETED(eyeobj) || !eyeobj.loc))
|
||||
to_chat(src, "ERROR: Eyeobj not found. Creating new eye...")
|
||||
create_eye()
|
||||
|
||||
eyeobj?.setLoc(loc)
|
||||
|
||||
/mob/living/silicon/ai/proc/create_eye()
|
||||
if(eyeobj)
|
||||
return
|
||||
eyeobj = new /mob/camera/aiEye()
|
||||
all_eyes += eyeobj
|
||||
eyeobj.ai = src
|
||||
eyeobj.setLoc(loc)
|
||||
eyeobj.name = "[name] (AI Eye)"
|
||||
set_eyeobj_visible(TRUE)
|
||||
|
||||
/mob/living/silicon/ai/proc/set_eyeobj_visible(state = TRUE)
|
||||
if(!eyeobj)
|
||||
return
|
||||
eyeobj.mouse_opacity = state ? MOUSE_OPACITY_ICON : initial(eyeobj.mouse_opacity)
|
||||
eyeobj.invisibility = state ? INVISIBILITY_OBSERVER : initial(eyeobj.invisibility)
|
||||
|
||||
/mob/living/silicon/ai/verb/toggle_acceleration()
|
||||
set category = "AI Commands"
|
||||
set name = "Toggle Camera Acceleration"
|
||||
|
||||
if(incapacitated())
|
||||
return
|
||||
acceleration = !acceleration
|
||||
to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].")
|
||||
|
||||
/mob/camera/aiEye/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
if(relay_speech && speaker && ai && !radio_freq && speaker != ai && near_camera(speaker))
|
||||
ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
|
||||
|
||||
/obj/effect/overlay/ai_detect_hud
|
||||
name = ""
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
icon = 'icons/effects/alphacolors.dmi'
|
||||
icon_state = ""
|
||||
alpha = 100
|
||||
layer = ABOVE_ALL_MOB_LAYER
|
||||
plane = GAME_PLANE
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/mob/living/silicon/ai/Login()
|
||||
..()
|
||||
if(stat != DEAD)
|
||||
for(var/each in GLOB.ai_status_displays) //change status
|
||||
var/obj/machinery/status_display/ai/O = each
|
||||
O.mode = 1
|
||||
O.emotion = emote_display
|
||||
O.update()
|
||||
set_eyeobj_visible(TRUE)
|
||||
if(multicam_on)
|
||||
end_multicam()
|
||||
view_core()
|
||||
/mob/living/silicon/ai/Login()
|
||||
..()
|
||||
if(stat != DEAD)
|
||||
for(var/each in GLOB.ai_status_displays) //change status
|
||||
var/obj/machinery/status_display/ai/O = each
|
||||
O.mode = 1
|
||||
O.emotion = emote_display
|
||||
O.update()
|
||||
set_eyeobj_visible(TRUE)
|
||||
if(multicam_on)
|
||||
end_multicam()
|
||||
view_core()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/mob/living/silicon/ai/Logout()
|
||||
..()
|
||||
for(var/each in GLOB.ai_status_displays) //change status
|
||||
var/obj/machinery/status_display/ai/O = each
|
||||
O.mode = 0
|
||||
O.update()
|
||||
set_eyeobj_visible(FALSE)
|
||||
view_core()
|
||||
/mob/living/silicon/ai/Logout()
|
||||
..()
|
||||
for(var/each in GLOB.ai_status_displays) //change status
|
||||
var/obj/machinery/status_display/ai/O = each
|
||||
O.mode = 0
|
||||
O.update()
|
||||
set_eyeobj_visible(FALSE)
|
||||
view_core()
|
||||
|
||||
@@ -1,179 +1,179 @@
|
||||
/mob/living/silicon/ai/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
|
||||
if(parent && istype(parent) && parent.stat != DEAD) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead.
|
||||
parent.say(message, language)
|
||||
return
|
||||
..(message)
|
||||
|
||||
/mob/living/silicon/ai/compose_track_href(atom/movable/speaker, namepart)
|
||||
var/mob/M = speaker.GetSource()
|
||||
if(M)
|
||||
return "<a href='?src=[REF(src)];track=[html_encode(namepart)]'>"
|
||||
return ""
|
||||
|
||||
/mob/living/silicon/ai/compose_job(atom/movable/speaker, message_langs, raw_message, radio_freq)
|
||||
//Also includes the </a> for AI hrefs, for convenience.
|
||||
return "[radio_freq ? " (" + speaker.GetJob() + ")" : ""]" + "[speaker.GetSource() ? "</a>" : ""]"
|
||||
|
||||
/mob/living/silicon/ai/IsVocal()
|
||||
return !CONFIG_GET(flag/silent_ai)
|
||||
|
||||
/mob/living/silicon/ai/radio(message, message_mode, list/spans, language)
|
||||
if(incapacitated())
|
||||
return FALSE
|
||||
if(!radio_enabled) //AI cannot speak if radio is disabled (via intellicard) or depowered.
|
||||
to_chat(src, "<span class='danger'>Your radio transmitter is offline!</span>")
|
||||
return FALSE
|
||||
..()
|
||||
|
||||
/mob/living/silicon/ai/get_message_mode(message)
|
||||
if(copytext(message, 1, 3) in list(":h", ":H", ".h", ".H", "#h", "#H"))
|
||||
return MODE_HOLOPAD
|
||||
else
|
||||
return ..()
|
||||
|
||||
//For holopads only. Usable by AI.
|
||||
/mob/living/silicon/ai/proc/holopad_talk(message, language)
|
||||
|
||||
|
||||
message = trim(message)
|
||||
|
||||
if (!message)
|
||||
return
|
||||
|
||||
var/obj/machinery/holopad/T = current
|
||||
if(istype(T) && T.masters[src])//If there is a hologram and its master is the user.
|
||||
var/turf/padturf = get_turf(T)
|
||||
var/padloc
|
||||
if(padturf)
|
||||
padloc = AREACOORD(padturf)
|
||||
else
|
||||
padloc = "(UNKNOWN)"
|
||||
src.log_talk(message, LOG_SAY, tag="HOLOPAD in [padloc]")
|
||||
send_speech(message, 7, T, "robot", message_language = language)
|
||||
to_chat(src, "<i><span class='game say'>Holopad transmitted, <span class='name'>[real_name]</span> <span class='message robot'>\"[message]\"</span></span></i>")
|
||||
else
|
||||
to_chat(src, "No holopad connected.")
|
||||
|
||||
|
||||
// Make sure that the code compiles with AI_VOX undefined
|
||||
#ifdef AI_VOX
|
||||
#define VOX_DELAY 600
|
||||
/mob/living/silicon/ai/verb/announcement_help()
|
||||
|
||||
set name = "Announcement Help"
|
||||
set desc = "Display a list of vocal words to announce to the crew."
|
||||
set category = "AI Commands"
|
||||
|
||||
if(incapacitated())
|
||||
return
|
||||
|
||||
var/dat = {"
|
||||
<font class='bad'>WARNING:</font> Misuse of the announcement system will get you job banned.<BR><BR>
|
||||
Here is a list of words you can type into the 'Announcement' button to create sentences to vocally announce to everyone on the same level at you.<BR>
|
||||
<UL><LI>You can also click on the word to PREVIEW it.</LI>
|
||||
<LI>You can only say 30 words for every announcement.</LI>
|
||||
<LI>Do not use punctuation as you would normally, if you want a pause you can use the full stop and comma characters by separating them with spaces, like so: 'Alpha . Test , Bravo'.</LI>
|
||||
<LI>Numbers are in word format, e.g. eight, sixty, etc </LI>
|
||||
<LI>Sound effects begin with an 's' before the actual word, e.g. scensor</LI>
|
||||
<LI>Use Ctrl+F to see if a word exists in the list.</LI></UL><HR>
|
||||
"}
|
||||
|
||||
var/index = 0
|
||||
for(var/word in GLOB.vox_sounds)
|
||||
index++
|
||||
dat += "<A href='?src=[REF(src)];say_word=[word]'>[capitalize(word)]</A>"
|
||||
if(index != GLOB.vox_sounds.len)
|
||||
dat += " / "
|
||||
|
||||
var/datum/browser/popup = new(src, "announce_help", "Announcement Help", 500, 400)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
|
||||
/mob/living/silicon/ai/proc/announcement()
|
||||
var/static/announcing_vox = 0 // Stores the time of the last announcement
|
||||
if(announcing_vox > world.time)
|
||||
to_chat(src, "<span class='notice'>Please wait [DisplayTimeText(announcing_vox - world.time)].</span>")
|
||||
return
|
||||
|
||||
var/message = input(src, "WARNING: Misuse of this verb can result in you being job banned. More help is available in 'Announcement Help'", "Announcement", src.last_announcement) as text
|
||||
|
||||
last_announcement = message
|
||||
|
||||
var/voxType = input(src, "Male or female VOX?", "VOX-gender") in list("male", "female")
|
||||
|
||||
if(!message || announcing_vox > world.time)
|
||||
return
|
||||
|
||||
if(incapacitated())
|
||||
return
|
||||
|
||||
if(control_disabled)
|
||||
to_chat(src, "<span class='warning'>Wireless interface disabled, unable to interact with announcement PA.</span>")
|
||||
return
|
||||
|
||||
var/list/words = splittext(trim(message), " ")
|
||||
var/list/incorrect_words = list()
|
||||
|
||||
if(words.len > 30)
|
||||
words.len = 30
|
||||
|
||||
for(var/word in words)
|
||||
word = lowertext(trim(word))
|
||||
if(!word)
|
||||
words -= word
|
||||
continue
|
||||
if(!GLOB.vox_sounds[word] && voxType == "female")
|
||||
incorrect_words += word
|
||||
if(!GLOB.vox_sounds_male[word] && voxType == "male")
|
||||
incorrect_words += word
|
||||
|
||||
if(incorrect_words.len)
|
||||
to_chat(src, "<span class='notice'>These words are not available on the announcement system: [english_list(incorrect_words)].</span>")
|
||||
return
|
||||
|
||||
announcing_vox = world.time + VOX_DELAY
|
||||
|
||||
log_game("[key_name(src)] made a vocal announcement with the following message: [message].")
|
||||
|
||||
for(var/word in words)
|
||||
play_vox_word(word, src.z, null, voxType)
|
||||
|
||||
|
||||
/proc/play_vox_word(word, z_level, mob/only_listener, voxType = "female")
|
||||
|
||||
word = lowertext(word)
|
||||
|
||||
if( (GLOB.vox_sounds[word] && voxType == "female") || (GLOB.vox_sounds_male[word] && voxType == "male") )
|
||||
|
||||
var/sound_file
|
||||
|
||||
if(voxType == "female")
|
||||
sound_file = GLOB.vox_sounds[word]
|
||||
else
|
||||
sound_file = GLOB.vox_sounds_male[word]
|
||||
var/sound/voice = sound(sound_file, wait = 1, channel = CHANNEL_VOX)
|
||||
voice.status = SOUND_STREAM
|
||||
|
||||
// If there is no single listener, broadcast to everyone in the same z level
|
||||
if(!only_listener)
|
||||
// Play voice for all mobs in the z level
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.client && M.can_hear() && (M.client.prefs.toggles & SOUND_ANNOUNCEMENTS))
|
||||
var/turf/T = get_turf(M)
|
||||
if(T.z == z_level)
|
||||
SEND_SOUND(M, voice)
|
||||
else
|
||||
SEND_SOUND(only_listener, voice)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
#undef VOX_DELAY
|
||||
#endif
|
||||
|
||||
/mob/living/silicon/ai/could_speak_in_language(datum/language/dt)
|
||||
if(is_servant_of_ratvar(src))
|
||||
// Ratvarian AIs can only speak Ratvarian
|
||||
. = ispath(dt, /datum/language/ratvar)
|
||||
else
|
||||
. = ..()
|
||||
/mob/living/silicon/ai/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
|
||||
if(parent && istype(parent) && parent.stat != DEAD) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead.
|
||||
parent.say(message, language)
|
||||
return
|
||||
..(message)
|
||||
|
||||
/mob/living/silicon/ai/compose_track_href(atom/movable/speaker, namepart)
|
||||
var/mob/M = speaker.GetSource()
|
||||
if(M)
|
||||
return "<a href='?src=[REF(src)];track=[html_encode(namepart)]'>"
|
||||
return ""
|
||||
|
||||
/mob/living/silicon/ai/compose_job(atom/movable/speaker, message_langs, raw_message, radio_freq)
|
||||
//Also includes the </a> for AI hrefs, for convenience.
|
||||
return "[radio_freq ? " (" + speaker.GetJob() + ")" : ""]" + "[speaker.GetSource() ? "</a>" : ""]"
|
||||
|
||||
/mob/living/silicon/ai/IsVocal()
|
||||
return !CONFIG_GET(flag/silent_ai)
|
||||
|
||||
/mob/living/silicon/ai/radio(message, message_mode, list/spans, language)
|
||||
if(incapacitated())
|
||||
return FALSE
|
||||
if(!radio_enabled) //AI cannot speak if radio is disabled (via intellicard) or depowered.
|
||||
to_chat(src, "<span class='danger'>Your radio transmitter is offline!</span>")
|
||||
return FALSE
|
||||
..()
|
||||
|
||||
/mob/living/silicon/ai/get_message_mode(message)
|
||||
if(copytext(message, 1, 3) in list(":h", ":H", ".h", ".H", "#h", "#H"))
|
||||
return MODE_HOLOPAD
|
||||
else
|
||||
return ..()
|
||||
|
||||
//For holopads only. Usable by AI.
|
||||
/mob/living/silicon/ai/proc/holopad_talk(message, language)
|
||||
|
||||
|
||||
message = trim(message)
|
||||
|
||||
if (!message)
|
||||
return
|
||||
|
||||
var/obj/machinery/holopad/T = current
|
||||
if(istype(T) && T.masters[src])//If there is a hologram and its master is the user.
|
||||
var/turf/padturf = get_turf(T)
|
||||
var/padloc
|
||||
if(padturf)
|
||||
padloc = AREACOORD(padturf)
|
||||
else
|
||||
padloc = "(UNKNOWN)"
|
||||
src.log_talk(message, LOG_SAY, tag="HOLOPAD in [padloc]")
|
||||
send_speech(message, 7, T, "robot", message_language = language)
|
||||
to_chat(src, "<i><span class='game say'>Holopad transmitted, <span class='name'>[real_name]</span> <span class='message robot'>\"[message]\"</span></span></i>")
|
||||
else
|
||||
to_chat(src, "No holopad connected.")
|
||||
|
||||
|
||||
// Make sure that the code compiles with AI_VOX undefined
|
||||
#ifdef AI_VOX
|
||||
#define VOX_DELAY 600
|
||||
/mob/living/silicon/ai/verb/announcement_help()
|
||||
|
||||
set name = "Announcement Help"
|
||||
set desc = "Display a list of vocal words to announce to the crew."
|
||||
set category = "AI Commands"
|
||||
|
||||
if(incapacitated())
|
||||
return
|
||||
|
||||
var/dat = {"
|
||||
<font class='bad'>WARNING:</font> Misuse of the announcement system will get you job banned.<BR><BR>
|
||||
Here is a list of words you can type into the 'Announcement' button to create sentences to vocally announce to everyone on the same level at you.<BR>
|
||||
<UL><LI>You can also click on the word to PREVIEW it.</LI>
|
||||
<LI>You can only say 30 words for every announcement.</LI>
|
||||
<LI>Do not use punctuation as you would normally, if you want a pause you can use the full stop and comma characters by separating them with spaces, like so: 'Alpha . Test , Bravo'.</LI>
|
||||
<LI>Numbers are in word format, e.g. eight, sixty, etc </LI>
|
||||
<LI>Sound effects begin with an 's' before the actual word, e.g. scensor</LI>
|
||||
<LI>Use Ctrl+F to see if a word exists in the list.</LI></UL><HR>
|
||||
"}
|
||||
|
||||
var/index = 0
|
||||
for(var/word in GLOB.vox_sounds)
|
||||
index++
|
||||
dat += "<A href='?src=[REF(src)];say_word=[word]'>[capitalize(word)]</A>"
|
||||
if(index != GLOB.vox_sounds.len)
|
||||
dat += " / "
|
||||
|
||||
var/datum/browser/popup = new(src, "announce_help", "Announcement Help", 500, 400)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
|
||||
/mob/living/silicon/ai/proc/announcement()
|
||||
var/static/announcing_vox = 0 // Stores the time of the last announcement
|
||||
if(announcing_vox > world.time)
|
||||
to_chat(src, "<span class='notice'>Please wait [DisplayTimeText(announcing_vox - world.time)].</span>")
|
||||
return
|
||||
|
||||
var/message = input(src, "WARNING: Misuse of this verb can result in you being job banned. More help is available in 'Announcement Help'", "Announcement", src.last_announcement) as text
|
||||
|
||||
last_announcement = message
|
||||
|
||||
var/voxType = input(src, "Male or female VOX?", "VOX-gender") in list("male", "female")
|
||||
|
||||
if(!message || announcing_vox > world.time)
|
||||
return
|
||||
|
||||
if(incapacitated())
|
||||
return
|
||||
|
||||
if(control_disabled)
|
||||
to_chat(src, "<span class='warning'>Wireless interface disabled, unable to interact with announcement PA.</span>")
|
||||
return
|
||||
|
||||
var/list/words = splittext(trim(message), " ")
|
||||
var/list/incorrect_words = list()
|
||||
|
||||
if(words.len > 30)
|
||||
words.len = 30
|
||||
|
||||
for(var/word in words)
|
||||
word = lowertext(trim(word))
|
||||
if(!word)
|
||||
words -= word
|
||||
continue
|
||||
if(!GLOB.vox_sounds[word] && voxType == "female")
|
||||
incorrect_words += word
|
||||
if(!GLOB.vox_sounds_male[word] && voxType == "male")
|
||||
incorrect_words += word
|
||||
|
||||
if(incorrect_words.len)
|
||||
to_chat(src, "<span class='notice'>These words are not available on the announcement system: [english_list(incorrect_words)].</span>")
|
||||
return
|
||||
|
||||
announcing_vox = world.time + VOX_DELAY
|
||||
|
||||
log_game("[key_name(src)] made a vocal announcement with the following message: [message].")
|
||||
|
||||
for(var/word in words)
|
||||
play_vox_word(word, src.z, null, voxType)
|
||||
|
||||
|
||||
/proc/play_vox_word(word, z_level, mob/only_listener, voxType = "female")
|
||||
|
||||
word = lowertext(word)
|
||||
|
||||
if( (GLOB.vox_sounds[word] && voxType == "female") || (GLOB.vox_sounds_male[word] && voxType == "male") )
|
||||
|
||||
var/sound_file
|
||||
|
||||
if(voxType == "female")
|
||||
sound_file = GLOB.vox_sounds[word]
|
||||
else
|
||||
sound_file = GLOB.vox_sounds_male[word]
|
||||
var/sound/voice = sound(sound_file, wait = 1, channel = CHANNEL_VOX)
|
||||
voice.status = SOUND_STREAM
|
||||
|
||||
// If there is no single listener, broadcast to everyone in the same z level
|
||||
if(!only_listener)
|
||||
// Play voice for all mobs in the z level
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.client && M.can_hear() && (M.client.prefs.toggles & SOUND_ANNOUNCEMENTS))
|
||||
var/turf/T = get_turf(M)
|
||||
if(T.z == z_level)
|
||||
SEND_SOUND(M, voice)
|
||||
else
|
||||
SEND_SOUND(only_listener, voice)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
#undef VOX_DELAY
|
||||
#endif
|
||||
|
||||
/mob/living/silicon/ai/could_speak_in_language(datum/language/dt)
|
||||
if(is_servant_of_ratvar(src))
|
||||
// Ratvarian AIs can only speak Ratvarian
|
||||
. = ispath(dt, /datum/language/ratvar)
|
||||
else
|
||||
. = ..()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,13 @@
|
||||
/mob/living/silicon/spawn_gibs(with_bodyparts, atom/loc_override)
|
||||
new /obj/effect/gibspawner/robot(loc_override ? loc_override.drop_location() : drop_location(), src)
|
||||
|
||||
/mob/living/silicon/spawn_dust()
|
||||
new /obj/effect/decal/remains/robot(loc)
|
||||
|
||||
/mob/living/silicon/death(gibbed)
|
||||
if(!gibbed)
|
||||
emote("deathgasp")
|
||||
diag_hud_set_status()
|
||||
diag_hud_set_health()
|
||||
update_health_hud()
|
||||
/mob/living/silicon/spawn_gibs(with_bodyparts, atom/loc_override)
|
||||
new /obj/effect/gibspawner/robot(loc_override ? loc_override.drop_location() : drop_location(), src)
|
||||
|
||||
/mob/living/silicon/spawn_dust()
|
||||
new /obj/effect/decal/remains/robot(loc)
|
||||
|
||||
/mob/living/silicon/death(gibbed)
|
||||
if(!gibbed)
|
||||
emote("deathgasp")
|
||||
diag_hud_set_status()
|
||||
diag_hud_set_health()
|
||||
update_health_hud()
|
||||
. = ..()
|
||||
@@ -1,93 +1,93 @@
|
||||
/mob/living/silicon/proc/show_laws() //Redefined in ai/laws.dm and robot/laws.dm
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/laws_sanity_check()
|
||||
if (!laws)
|
||||
make_laws()
|
||||
|
||||
/mob/living/silicon/proc/post_lawchange(announce = TRUE)
|
||||
throw_alert("newlaw", /obj/screen/alert/newlaw)
|
||||
if(announce && last_lawchange_announce != world.time)
|
||||
to_chat(src, "<b>Your laws have been changed.</b>")
|
||||
addtimer(CALLBACK(src, .proc/show_laws), 0)
|
||||
last_lawchange_announce = world.time
|
||||
|
||||
/mob/living/silicon/proc/set_law_sixsixsix(law, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.set_law_sixsixsix(law)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/set_zeroth_law(law, law_borg, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.set_zeroth_law(law, law_borg)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/add_inherent_law(law, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.add_inherent_law(law)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/clear_inherent_laws(announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_inherent_laws()
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/add_supplied_law(number, law, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.add_supplied_law(number, law)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/clear_supplied_laws(announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_supplied_laws()
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/add_ion_law(law, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.add_ion_law(law)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/add_hacked_law(law, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.add_hacked_law(law)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/replace_random_law(law, groups, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
. = laws.replace_random_law(law,groups)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/shuffle_laws(list/groups, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.shuffle_laws(groups)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/remove_law(number, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
. = laws.remove_law(number)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/clear_ion_laws(announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_ion_laws()
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/clear_hacked_laws(announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_hacked_laws()
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/make_laws()
|
||||
laws = new /datum/ai_laws
|
||||
laws.set_laws_config()
|
||||
laws.associate(src)
|
||||
|
||||
/mob/living/silicon/proc/clear_zeroth_law(force, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_zeroth_law(force)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/clear_law_sixsixsix(force, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_law_sixsixsix(force)
|
||||
post_lawchange(announce)
|
||||
/mob/living/silicon/proc/show_laws() //Redefined in ai/laws.dm and robot/laws.dm
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/laws_sanity_check()
|
||||
if (!laws)
|
||||
make_laws()
|
||||
|
||||
/mob/living/silicon/proc/post_lawchange(announce = TRUE)
|
||||
throw_alert("newlaw", /obj/screen/alert/newlaw)
|
||||
if(announce && last_lawchange_announce != world.time)
|
||||
to_chat(src, "<b>Your laws have been changed.</b>")
|
||||
addtimer(CALLBACK(src, .proc/show_laws), 0)
|
||||
last_lawchange_announce = world.time
|
||||
|
||||
/mob/living/silicon/proc/set_law_sixsixsix(law, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.set_law_sixsixsix(law)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/set_zeroth_law(law, law_borg, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.set_zeroth_law(law, law_borg)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/add_inherent_law(law, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.add_inherent_law(law)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/clear_inherent_laws(announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_inherent_laws()
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/add_supplied_law(number, law, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.add_supplied_law(number, law)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/clear_supplied_laws(announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_supplied_laws()
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/add_ion_law(law, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.add_ion_law(law)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/add_hacked_law(law, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.add_hacked_law(law)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/replace_random_law(law, groups, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
. = laws.replace_random_law(law,groups)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/shuffle_laws(list/groups, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.shuffle_laws(groups)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/remove_law(number, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
. = laws.remove_law(number)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/clear_ion_laws(announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_ion_laws()
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/clear_hacked_laws(announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_hacked_laws()
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/make_laws()
|
||||
laws = new /datum/ai_laws
|
||||
laws.set_laws_config()
|
||||
laws.associate(src)
|
||||
|
||||
/mob/living/silicon/proc/clear_zeroth_law(force, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_zeroth_law(force)
|
||||
post_lawchange(announce)
|
||||
|
||||
/mob/living/silicon/proc/clear_law_sixsixsix(force, announce = TRUE)
|
||||
laws_sanity_check()
|
||||
laws.clear_law_sixsixsix(force)
|
||||
post_lawchange(announce)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/mob/living/silicon/Login()
|
||||
if(mind && SSticker.mode)
|
||||
SSticker.mode.remove_cultist(mind, 0, 0)
|
||||
var/datum/antagonist/rev/rev = mind.has_antag_datum(/datum/antagonist/rev)
|
||||
if(rev)
|
||||
rev.remove_revolutionary(TRUE)
|
||||
var/datum/antagonist/bloodsucker/V = mind.has_antag_datum(/datum/antagonist/bloodsucker)
|
||||
if(V)
|
||||
mind.remove_antag_datum(V)
|
||||
..()
|
||||
/mob/living/silicon/Login()
|
||||
if(mind && SSticker.mode)
|
||||
SSticker.mode.remove_cultist(mind, 0, 0)
|
||||
var/datum/antagonist/rev/rev = mind.has_antag_datum(/datum/antagonist/rev)
|
||||
if(rev)
|
||||
rev.remove_revolutionary(TRUE)
|
||||
var/datum/antagonist/bloodsucker/V = mind.has_antag_datum(/datum/antagonist/bloodsucker)
|
||||
if(V)
|
||||
mind.remove_antag_datum(V)
|
||||
..()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/mob/living/silicon/pai/death(gibbed)
|
||||
if(stat == DEAD)
|
||||
return
|
||||
stat = DEAD
|
||||
canmove = 0
|
||||
update_sight()
|
||||
clear_fullscreens()
|
||||
|
||||
//New pAI's get a brand new mind to prevent meta stuff from their previous life. This new mind causes problems down the line if it's not deleted here.
|
||||
GLOB.alive_mob_list -= src
|
||||
ghostize()
|
||||
qdel(src)
|
||||
/mob/living/silicon/pai/death(gibbed)
|
||||
if(stat == DEAD)
|
||||
return
|
||||
stat = DEAD
|
||||
canmove = 0
|
||||
update_sight()
|
||||
clear_fullscreens()
|
||||
|
||||
//New pAI's get a brand new mind to prevent meta stuff from their previous life. This new mind causes problems down the line if it's not deleted here.
|
||||
GLOB.alive_mob_list -= src
|
||||
ghostize()
|
||||
qdel(src)
|
||||
|
||||
@@ -1,429 +1,429 @@
|
||||
/mob/living/silicon/pai
|
||||
name = "pAI"
|
||||
icon = 'icons/mob/pai.dmi'
|
||||
icon_state = "repairbot"
|
||||
mouse_opacity = MOUSE_OPACITY_OPAQUE
|
||||
density = FALSE
|
||||
pass_flags = PASSTABLE | PASSMOB
|
||||
mob_size = MOB_SIZE_TINY
|
||||
desc = "A generic pAI mobile hard-light holographics emitter. It seems to be deactivated."
|
||||
weather_immunities = list("ash")
|
||||
health = 500
|
||||
maxHealth = 500
|
||||
layer = BELOW_MOB_LAYER
|
||||
can_be_held = TRUE
|
||||
|
||||
var/network = "ss13"
|
||||
var/obj/machinery/camera/current = null
|
||||
|
||||
var/ram = 100 // Used as currency to purchase different abilities
|
||||
var/list/software = list()
|
||||
var/userDNA // The DNA string of our assigned user
|
||||
var/obj/item/paicard/card // The card we inhabit
|
||||
var/hacking = FALSE //Are we hacking a door?
|
||||
|
||||
var/speakStatement = "states"
|
||||
var/speakExclamation = "declares"
|
||||
var/speakDoubleExclamation = "alarms"
|
||||
var/speakQuery = "queries"
|
||||
|
||||
var/obj/item/pai_cable/cable // The cable we produce and use when door or camera jacking
|
||||
|
||||
var/master // Name of the one who commands us
|
||||
var/master_dna // DNA string for owner verification
|
||||
|
||||
// Various software-specific vars
|
||||
|
||||
var/temp // General error reporting text contained here will typically be shown once and cleared
|
||||
var/screen // Which screen our main window displays
|
||||
var/subscreen // Which specific function of the main screen is being displayed
|
||||
|
||||
var/obj/item/pda/ai/pai/pda = null
|
||||
|
||||
var/secHUD = 0 // Toggles whether the Security HUD is active or not
|
||||
var/medHUD = 0 // Toggles whether the Medical HUD is active or not
|
||||
|
||||
var/datum/data/record/medicalActive1 // Datacore record declarations for record software
|
||||
var/datum/data/record/medicalActive2
|
||||
|
||||
var/datum/data/record/securityActive1 // Could probably just combine all these into one
|
||||
var/datum/data/record/securityActive2
|
||||
|
||||
var/obj/machinery/door/hackdoor // The airlock being hacked
|
||||
var/hackprogress = 0 // Possible values: 0 - 100, >= 100 means the hack is complete and will be reset upon next check
|
||||
|
||||
var/obj/item/integrated_signaler/signaler // AI's signaller
|
||||
|
||||
var/holoform = FALSE
|
||||
var/canholo = TRUE
|
||||
var/obj/item/card/id/access_card = null
|
||||
var/chassis = "repairbot"
|
||||
var/dynamic_chassis
|
||||
var/dynamic_chassis_sit = FALSE //whether we're sitting instead of resting spritewise
|
||||
var/dynamic_chassis_bellyup = FALSE //whether we're lying down bellyup
|
||||
var/list/possible_chassis //initialized in initialize.
|
||||
var/list/dynamic_chassis_icons //ditto.
|
||||
var/list/chassis_pixel_offsets_x //stupid dogborgs
|
||||
var/static/item_head_icon = 'icons/mob/pai_item_head.dmi'
|
||||
var/static/item_lh_icon = 'icons/mob/pai_item_lh.dmi'
|
||||
var/static/item_rh_icon = 'icons/mob/pai_item_rh.dmi'
|
||||
|
||||
var/emitterhealth = 20
|
||||
var/emittermaxhealth = 20
|
||||
var/emitterregen = 0.25
|
||||
var/emitter_next_use = 0
|
||||
var/emitter_emp_cd = 300
|
||||
var/emittercd = 50
|
||||
var/emitteroverloadcd = 100
|
||||
|
||||
var/radio_short = FALSE
|
||||
var/radio_short_cooldown = 5 MINUTES
|
||||
var/radio_short_timerid
|
||||
|
||||
canmove = FALSE
|
||||
var/silent = FALSE
|
||||
var/brightness_power = 5
|
||||
|
||||
var/icon/custom_holoform_icon
|
||||
|
||||
/mob/living/silicon/pai/Destroy()
|
||||
if (loc != card)
|
||||
card.forceMove(drop_location())
|
||||
card.pai = null
|
||||
card.cut_overlays()
|
||||
card.add_overlay("pai-off")
|
||||
GLOB.pai_list -= src
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/pai/Initialize()
|
||||
var/obj/item/paicard/P = loc
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
GLOB.pai_list += src
|
||||
make_laws()
|
||||
canmove = 0
|
||||
if(!istype(P)) //when manually spawning a pai, we create a card to put it into.
|
||||
var/newcardloc = P
|
||||
P = new /obj/item/paicard(newcardloc)
|
||||
P.setPersonality(src)
|
||||
forceMove(P)
|
||||
card = P
|
||||
signaler = new(src)
|
||||
if(!radio)
|
||||
radio = new /obj/item/radio(src)
|
||||
|
||||
//PDA
|
||||
pda = new(src)
|
||||
spawn(5)
|
||||
pda.ownjob = "pAI Messenger"
|
||||
pda.owner = text("[]", src)
|
||||
pda.name = pda.owner + " (" + pda.ownjob + ")"
|
||||
|
||||
possible_chassis = typelist(NAMEOF(src, possible_chassis), list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE,
|
||||
"fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE, "borgi" = FALSE ,
|
||||
"parrot" = FALSE, "bear" = FALSE , "mushroom" = FALSE, "crow" = FALSE ,
|
||||
"fairy" = FALSE , "spiderbot" = FALSE)) //assoc value is whether it can be picked up.
|
||||
dynamic_chassis_icons = typelist(NAMEOF(src, dynamic_chassis_icons), initialize_dynamic_chassis_icons())
|
||||
chassis_pixel_offsets_x = typelist(NAMEOF(src, chassis_pixel_offsets_x), default_chassis_pixel_offsets_x())
|
||||
|
||||
. = ..()
|
||||
|
||||
var/datum/action/innate/pai/software/SW = new
|
||||
var/datum/action/innate/pai/shell/AS = new /datum/action/innate/pai/shell
|
||||
var/datum/action/innate/pai/chassis/AC = new /datum/action/innate/pai/chassis
|
||||
var/datum/action/innate/pai/rest/AR = new /datum/action/innate/pai/rest
|
||||
var/datum/action/innate/pai/light/AL = new /datum/action/innate/pai/light
|
||||
|
||||
var/datum/action/language_menu/ALM = new
|
||||
SW.Grant(src)
|
||||
AS.Grant(src)
|
||||
AC.Grant(src)
|
||||
AR.Grant(src)
|
||||
AL.Grant(src)
|
||||
ALM.Grant(src)
|
||||
emitter_next_use = world.time + 10 SECONDS
|
||||
|
||||
/mob/living/silicon/pai/Life()
|
||||
if(hacking)
|
||||
process_hack()
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/pai/proc/process_hack()
|
||||
|
||||
if(cable && cable.machine && istype(cable.machine, /obj/machinery/door) && cable.machine == hackdoor && get_dist(src, hackdoor) <= 1)
|
||||
hackprogress = CLAMP(hackprogress + 4, 0, 100)
|
||||
else
|
||||
temp = "Door Jack: Connection to airlock has been lost. Hack aborted."
|
||||
hackprogress = 0
|
||||
hacking = FALSE
|
||||
hackdoor = null
|
||||
return
|
||||
if(screen == "doorjack" && subscreen == 0) // Update our view, if appropriate
|
||||
paiInterface()
|
||||
if(hackprogress >= 100)
|
||||
hackprogress = 0
|
||||
var/obj/machinery/door/D = cable.machine
|
||||
D.open()
|
||||
hacking = FALSE
|
||||
|
||||
/mob/living/silicon/pai/make_laws()
|
||||
laws = new /datum/ai_laws/pai()
|
||||
return TRUE
|
||||
|
||||
/mob/living/silicon/pai/Login()
|
||||
..()
|
||||
usr << browse_rsc('html/paigrid.png') // Go ahead and cache the interface resources as early as possible
|
||||
if(client)
|
||||
client.perspective = EYE_PERSPECTIVE
|
||||
if(holoform)
|
||||
client.eye = src
|
||||
else
|
||||
client.eye = card
|
||||
|
||||
/mob/living/silicon/pai/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
if(!stat)
|
||||
stat(null, text("Emitter Integrity: [emitterhealth * (100/emittermaxhealth)]"))
|
||||
else
|
||||
stat(null, text("Systems nonfunctional"))
|
||||
|
||||
/mob/living/silicon/pai/restrained(ignore_grab)
|
||||
. = FALSE
|
||||
|
||||
// See software.dm for Topic()
|
||||
|
||||
/mob/living/silicon/pai/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)
|
||||
if(be_close && !in_range(M, src))
|
||||
to_chat(src, "<span class='warning'>You are too far away!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/mob/proc/makePAI(delold)
|
||||
var/obj/item/paicard/card = new /obj/item/paicard(get_turf(src))
|
||||
var/mob/living/silicon/pai/pai = new /mob/living/silicon/pai(card)
|
||||
transfer_ckey(pai)
|
||||
pai.name = name
|
||||
card.setPersonality(pai)
|
||||
if(delold)
|
||||
qdel(src)
|
||||
|
||||
/datum/action/innate/pai
|
||||
name = "PAI Action"
|
||||
icon_icon = 'icons/mob/actions/actions_silicon.dmi'
|
||||
var/mob/living/silicon/pai/P
|
||||
|
||||
/datum/action/innate/pai/Trigger()
|
||||
if(!ispAI(owner))
|
||||
return 0
|
||||
P = owner
|
||||
|
||||
/datum/action/innate/pai/software
|
||||
name = "Software Interface"
|
||||
button_icon_state = "pai"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/software/Trigger()
|
||||
..()
|
||||
P.paiInterface()
|
||||
|
||||
/datum/action/innate/pai/shell
|
||||
name = "Toggle Holoform"
|
||||
button_icon_state = "pai_holoform"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/shell/Trigger()
|
||||
..()
|
||||
if(P.holoform)
|
||||
P.fold_in(FALSE)
|
||||
else
|
||||
P.fold_out()
|
||||
|
||||
/datum/action/innate/pai/chassis
|
||||
name = "Holochassis Appearance Composite"
|
||||
button_icon_state = "pai_chassis"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/chassis/Trigger()
|
||||
..()
|
||||
P.choose_chassis()
|
||||
|
||||
/datum/action/innate/pai/rest
|
||||
name = "Rest"
|
||||
button_icon_state = "pai_rest"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/rest/Trigger()
|
||||
..()
|
||||
P.lay_down()
|
||||
|
||||
/datum/action/innate/pai/light
|
||||
name = "Toggle Integrated Lights"
|
||||
icon_icon = 'icons/mob/actions/actions_spells.dmi'
|
||||
button_icon_state = "emp"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/light/Trigger()
|
||||
..()
|
||||
P.toggle_integrated_light()
|
||||
|
||||
/mob/living/silicon/pai/Process_Spacemove(movement_dir = 0)
|
||||
. = ..()
|
||||
if(!.)
|
||||
add_movespeed_modifier(MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD, TRUE, 100, multiplicative_slowdown = 2)
|
||||
return TRUE
|
||||
remove_movespeed_modifier(MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD, TRUE)
|
||||
return TRUE
|
||||
|
||||
/mob/living/silicon/pai/examine(mob/user)
|
||||
. = ..()
|
||||
. += "A personal AI in holochassis mode. Its master ID string seems to be [master]."
|
||||
|
||||
/mob/living/silicon/pai/Life()
|
||||
if(stat == DEAD)
|
||||
return
|
||||
if(cable)
|
||||
if(get_dist(src, cable) > 1)
|
||||
var/turf/T = get_turf(src.loc)
|
||||
T.visible_message("<span class='warning'>[src.cable] rapidly retracts back into its spool.</span>", "<span class='italics'>You hear a click and the sound of wire spooling rapidly.</span>")
|
||||
qdel(src.cable)
|
||||
cable = null
|
||||
silent = max(silent - 1, 0)
|
||||
. = ..()
|
||||
|
||||
/mob/living/silicon/pai/updatehealth()
|
||||
if(status_flags & GODMODE)
|
||||
return
|
||||
health = maxHealth - getBruteLoss() - getFireLoss()
|
||||
update_stat()
|
||||
|
||||
/mob/living/silicon/pai/process()
|
||||
emitterhealth = CLAMP((emitterhealth + emitterregen), -50, emittermaxhealth)
|
||||
|
||||
/mob/living/silicon/pai/proc/short_radio()
|
||||
if(radio_short_timerid)
|
||||
deltimer(radio_short_timerid)
|
||||
radio_short = TRUE
|
||||
to_chat(src, "<span class='danger'>Your radio shorts out!</span>")
|
||||
radio_short_timerid = addtimer(CALLBACK(src, .proc/unshort_radio), radio_short_cooldown, flags = TIMER_STOPPABLE)
|
||||
|
||||
/mob/living/silicon/pai/proc/unshort_radio()
|
||||
radio_short = FALSE
|
||||
to_chat(src, "<span class='danger'>You feel your radio is operational once more.</span>")
|
||||
if(radio_short_timerid)
|
||||
deltimer(radio_short_timerid)
|
||||
|
||||
/mob/living/silicon/pai/proc/initialize_dynamic_chassis_icons()
|
||||
. = list()
|
||||
var/icon/curr //for inserts
|
||||
|
||||
//This is a horrible system and I wish I was not as lazy and did something smarter, like just generating a new icon in memory which is probably more efficient.
|
||||
|
||||
//Basic /tg/ cyborgs
|
||||
.["Cyborg - Engineering (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "engineer"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (loaderborg)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "loaderborg"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (handyeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "handyeng"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (sleekeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "sleekeng"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (marinaeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinaeng"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "medical"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (marinamed)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinamed"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (eyebotmed)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "eyebotmed"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "sec"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (sleeksec)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "sleeksec"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (marinasec)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinasec"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Clown (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "clown"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
|
||||
//Citadel dogborgs
|
||||
//Engi
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (dog - valeeng)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (dog - pupdozer)"] = curr
|
||||
//Med
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "medihound")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (dog - medihound)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (dog - medihounddark)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valemed")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (dog - valemed)"] = curr
|
||||
//Sec
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "k9")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (dog - k9)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (dog - k9dark)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valesec")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (dog - valesec)"] = curr
|
||||
//Service
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Service (dog - valeserv)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Service (dog - valeservdark)"] = curr
|
||||
//Sci
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valesci")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Science (dog - valesci)"] = curr
|
||||
//Misc
|
||||
.["Cyborg - Misc (dog - blade)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/widerobot.dmi', "blade"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
|
||||
/mob/living/silicon/pai/proc/default_chassis_pixel_offsets_x()
|
||||
. = list()
|
||||
//Engi
|
||||
.["Cyborg - Engineering (dog - valeeng)"] = -16
|
||||
.["Cyborg - Engineering (dog - pupdozer)"] = -16
|
||||
//Med
|
||||
.["Cyborg - Medical (dog - medihound)"] = -16
|
||||
.["Cyborg - Medical (dog - medihounddark)"] = -16
|
||||
.["Cyborg - Medical (dog - valemed)"] = -16
|
||||
//Sec
|
||||
.["Cyborg - Security (dog - k9)"] = -16
|
||||
.["Cyborg - Security (dog - valesec)"] = -16
|
||||
.["Cyborg - Security (dog - k9dark)"] = -16
|
||||
//Service
|
||||
.["Cyborg - Service (dog - valeserv)"] = -16
|
||||
.["Cyborg - Service (dog - valeservdark)"] = -16
|
||||
//Sci
|
||||
.["Cyborg - Security (dog - valesci)"] = -16
|
||||
//Misc
|
||||
.["Cyborg - Misc (dog - blade)"] = -16
|
||||
/mob/living/silicon/pai
|
||||
name = "pAI"
|
||||
icon = 'icons/mob/pai.dmi'
|
||||
icon_state = "repairbot"
|
||||
mouse_opacity = MOUSE_OPACITY_OPAQUE
|
||||
density = FALSE
|
||||
pass_flags = PASSTABLE | PASSMOB
|
||||
mob_size = MOB_SIZE_TINY
|
||||
desc = "A generic pAI mobile hard-light holographics emitter. It seems to be deactivated."
|
||||
weather_immunities = list("ash")
|
||||
health = 500
|
||||
maxHealth = 500
|
||||
layer = BELOW_MOB_LAYER
|
||||
can_be_held = TRUE
|
||||
|
||||
var/network = "ss13"
|
||||
var/obj/machinery/camera/current = null
|
||||
|
||||
var/ram = 100 // Used as currency to purchase different abilities
|
||||
var/list/software = list()
|
||||
var/userDNA // The DNA string of our assigned user
|
||||
var/obj/item/paicard/card // The card we inhabit
|
||||
var/hacking = FALSE //Are we hacking a door?
|
||||
|
||||
var/speakStatement = "states"
|
||||
var/speakExclamation = "declares"
|
||||
var/speakDoubleExclamation = "alarms"
|
||||
var/speakQuery = "queries"
|
||||
|
||||
var/obj/item/pai_cable/cable // The cable we produce and use when door or camera jacking
|
||||
|
||||
var/master // Name of the one who commands us
|
||||
var/master_dna // DNA string for owner verification
|
||||
|
||||
// Various software-specific vars
|
||||
|
||||
var/temp // General error reporting text contained here will typically be shown once and cleared
|
||||
var/screen // Which screen our main window displays
|
||||
var/subscreen // Which specific function of the main screen is being displayed
|
||||
|
||||
var/obj/item/pda/ai/pai/pda = null
|
||||
|
||||
var/secHUD = 0 // Toggles whether the Security HUD is active or not
|
||||
var/medHUD = 0 // Toggles whether the Medical HUD is active or not
|
||||
|
||||
var/datum/data/record/medicalActive1 // Datacore record declarations for record software
|
||||
var/datum/data/record/medicalActive2
|
||||
|
||||
var/datum/data/record/securityActive1 // Could probably just combine all these into one
|
||||
var/datum/data/record/securityActive2
|
||||
|
||||
var/obj/machinery/door/hackdoor // The airlock being hacked
|
||||
var/hackprogress = 0 // Possible values: 0 - 100, >= 100 means the hack is complete and will be reset upon next check
|
||||
|
||||
var/obj/item/integrated_signaler/signaler // AI's signaller
|
||||
|
||||
var/holoform = FALSE
|
||||
var/canholo = TRUE
|
||||
var/obj/item/card/id/access_card = null
|
||||
var/chassis = "repairbot"
|
||||
var/dynamic_chassis
|
||||
var/dynamic_chassis_sit = FALSE //whether we're sitting instead of resting spritewise
|
||||
var/dynamic_chassis_bellyup = FALSE //whether we're lying down bellyup
|
||||
var/list/possible_chassis //initialized in initialize.
|
||||
var/list/dynamic_chassis_icons //ditto.
|
||||
var/list/chassis_pixel_offsets_x //stupid dogborgs
|
||||
var/static/item_head_icon = 'icons/mob/pai_item_head.dmi'
|
||||
var/static/item_lh_icon = 'icons/mob/pai_item_lh.dmi'
|
||||
var/static/item_rh_icon = 'icons/mob/pai_item_rh.dmi'
|
||||
|
||||
var/emitterhealth = 20
|
||||
var/emittermaxhealth = 20
|
||||
var/emitterregen = 0.25
|
||||
var/emitter_next_use = 0
|
||||
var/emitter_emp_cd = 300
|
||||
var/emittercd = 50
|
||||
var/emitteroverloadcd = 100
|
||||
|
||||
var/radio_short = FALSE
|
||||
var/radio_short_cooldown = 5 MINUTES
|
||||
var/radio_short_timerid
|
||||
|
||||
canmove = FALSE
|
||||
var/silent = FALSE
|
||||
var/brightness_power = 5
|
||||
|
||||
var/icon/custom_holoform_icon
|
||||
|
||||
/mob/living/silicon/pai/Destroy()
|
||||
if (loc != card)
|
||||
card.forceMove(drop_location())
|
||||
card.pai = null
|
||||
card.cut_overlays()
|
||||
card.add_overlay("pai-off")
|
||||
GLOB.pai_list -= src
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/pai/Initialize()
|
||||
var/obj/item/paicard/P = loc
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
GLOB.pai_list += src
|
||||
make_laws()
|
||||
canmove = 0
|
||||
if(!istype(P)) //when manually spawning a pai, we create a card to put it into.
|
||||
var/newcardloc = P
|
||||
P = new /obj/item/paicard(newcardloc)
|
||||
P.setPersonality(src)
|
||||
forceMove(P)
|
||||
card = P
|
||||
signaler = new(src)
|
||||
if(!radio)
|
||||
radio = new /obj/item/radio(src)
|
||||
|
||||
//PDA
|
||||
pda = new(src)
|
||||
spawn(5)
|
||||
pda.ownjob = "pAI Messenger"
|
||||
pda.owner = text("[]", src)
|
||||
pda.name = pda.owner + " (" + pda.ownjob + ")"
|
||||
|
||||
possible_chassis = typelist(NAMEOF(src, possible_chassis), list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE,
|
||||
"fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE, "borgi" = FALSE ,
|
||||
"parrot" = FALSE, "bear" = FALSE , "mushroom" = FALSE, "crow" = FALSE ,
|
||||
"fairy" = FALSE , "spiderbot" = FALSE)) //assoc value is whether it can be picked up.
|
||||
dynamic_chassis_icons = typelist(NAMEOF(src, dynamic_chassis_icons), initialize_dynamic_chassis_icons())
|
||||
chassis_pixel_offsets_x = typelist(NAMEOF(src, chassis_pixel_offsets_x), default_chassis_pixel_offsets_x())
|
||||
|
||||
. = ..()
|
||||
|
||||
var/datum/action/innate/pai/software/SW = new
|
||||
var/datum/action/innate/pai/shell/AS = new /datum/action/innate/pai/shell
|
||||
var/datum/action/innate/pai/chassis/AC = new /datum/action/innate/pai/chassis
|
||||
var/datum/action/innate/pai/rest/AR = new /datum/action/innate/pai/rest
|
||||
var/datum/action/innate/pai/light/AL = new /datum/action/innate/pai/light
|
||||
|
||||
var/datum/action/language_menu/ALM = new
|
||||
SW.Grant(src)
|
||||
AS.Grant(src)
|
||||
AC.Grant(src)
|
||||
AR.Grant(src)
|
||||
AL.Grant(src)
|
||||
ALM.Grant(src)
|
||||
emitter_next_use = world.time + 10 SECONDS
|
||||
|
||||
/mob/living/silicon/pai/Life()
|
||||
if(hacking)
|
||||
process_hack()
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/pai/proc/process_hack()
|
||||
|
||||
if(cable && cable.machine && istype(cable.machine, /obj/machinery/door) && cable.machine == hackdoor && get_dist(src, hackdoor) <= 1)
|
||||
hackprogress = CLAMP(hackprogress + 4, 0, 100)
|
||||
else
|
||||
temp = "Door Jack: Connection to airlock has been lost. Hack aborted."
|
||||
hackprogress = 0
|
||||
hacking = FALSE
|
||||
hackdoor = null
|
||||
return
|
||||
if(screen == "doorjack" && subscreen == 0) // Update our view, if appropriate
|
||||
paiInterface()
|
||||
if(hackprogress >= 100)
|
||||
hackprogress = 0
|
||||
var/obj/machinery/door/D = cable.machine
|
||||
D.open()
|
||||
hacking = FALSE
|
||||
|
||||
/mob/living/silicon/pai/make_laws()
|
||||
laws = new /datum/ai_laws/pai()
|
||||
return TRUE
|
||||
|
||||
/mob/living/silicon/pai/Login()
|
||||
..()
|
||||
usr << browse_rsc('html/paigrid.png') // Go ahead and cache the interface resources as early as possible
|
||||
if(client)
|
||||
client.perspective = EYE_PERSPECTIVE
|
||||
if(holoform)
|
||||
client.eye = src
|
||||
else
|
||||
client.eye = card
|
||||
|
||||
/mob/living/silicon/pai/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
if(!stat)
|
||||
stat(null, text("Emitter Integrity: [emitterhealth * (100/emittermaxhealth)]"))
|
||||
else
|
||||
stat(null, text("Systems nonfunctional"))
|
||||
|
||||
/mob/living/silicon/pai/restrained(ignore_grab)
|
||||
. = FALSE
|
||||
|
||||
// See software.dm for Topic()
|
||||
|
||||
/mob/living/silicon/pai/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)
|
||||
if(be_close && !in_range(M, src))
|
||||
to_chat(src, "<span class='warning'>You are too far away!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/mob/proc/makePAI(delold)
|
||||
var/obj/item/paicard/card = new /obj/item/paicard(get_turf(src))
|
||||
var/mob/living/silicon/pai/pai = new /mob/living/silicon/pai(card)
|
||||
transfer_ckey(pai)
|
||||
pai.name = name
|
||||
card.setPersonality(pai)
|
||||
if(delold)
|
||||
qdel(src)
|
||||
|
||||
/datum/action/innate/pai
|
||||
name = "PAI Action"
|
||||
icon_icon = 'icons/mob/actions/actions_silicon.dmi'
|
||||
var/mob/living/silicon/pai/P
|
||||
|
||||
/datum/action/innate/pai/Trigger()
|
||||
if(!ispAI(owner))
|
||||
return 0
|
||||
P = owner
|
||||
|
||||
/datum/action/innate/pai/software
|
||||
name = "Software Interface"
|
||||
button_icon_state = "pai"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/software/Trigger()
|
||||
..()
|
||||
P.paiInterface()
|
||||
|
||||
/datum/action/innate/pai/shell
|
||||
name = "Toggle Holoform"
|
||||
button_icon_state = "pai_holoform"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/shell/Trigger()
|
||||
..()
|
||||
if(P.holoform)
|
||||
P.fold_in(FALSE)
|
||||
else
|
||||
P.fold_out()
|
||||
|
||||
/datum/action/innate/pai/chassis
|
||||
name = "Holochassis Appearance Composite"
|
||||
button_icon_state = "pai_chassis"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/chassis/Trigger()
|
||||
..()
|
||||
P.choose_chassis()
|
||||
|
||||
/datum/action/innate/pai/rest
|
||||
name = "Rest"
|
||||
button_icon_state = "pai_rest"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/rest/Trigger()
|
||||
..()
|
||||
P.lay_down()
|
||||
|
||||
/datum/action/innate/pai/light
|
||||
name = "Toggle Integrated Lights"
|
||||
icon_icon = 'icons/mob/actions/actions_spells.dmi'
|
||||
button_icon_state = "emp"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/light/Trigger()
|
||||
..()
|
||||
P.toggle_integrated_light()
|
||||
|
||||
/mob/living/silicon/pai/Process_Spacemove(movement_dir = 0)
|
||||
. = ..()
|
||||
if(!.)
|
||||
add_movespeed_modifier(MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD, TRUE, 100, multiplicative_slowdown = 2)
|
||||
return TRUE
|
||||
remove_movespeed_modifier(MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD, TRUE)
|
||||
return TRUE
|
||||
|
||||
/mob/living/silicon/pai/examine(mob/user)
|
||||
. = ..()
|
||||
. += "A personal AI in holochassis mode. Its master ID string seems to be [master]."
|
||||
|
||||
/mob/living/silicon/pai/Life()
|
||||
if(stat == DEAD)
|
||||
return
|
||||
if(cable)
|
||||
if(get_dist(src, cable) > 1)
|
||||
var/turf/T = get_turf(src.loc)
|
||||
T.visible_message("<span class='warning'>[src.cable] rapidly retracts back into its spool.</span>", "<span class='italics'>You hear a click and the sound of wire spooling rapidly.</span>")
|
||||
qdel(src.cable)
|
||||
cable = null
|
||||
silent = max(silent - 1, 0)
|
||||
. = ..()
|
||||
|
||||
/mob/living/silicon/pai/updatehealth()
|
||||
if(status_flags & GODMODE)
|
||||
return
|
||||
health = maxHealth - getBruteLoss() - getFireLoss()
|
||||
update_stat()
|
||||
|
||||
/mob/living/silicon/pai/process()
|
||||
emitterhealth = CLAMP((emitterhealth + emitterregen), -50, emittermaxhealth)
|
||||
|
||||
/mob/living/silicon/pai/proc/short_radio()
|
||||
if(radio_short_timerid)
|
||||
deltimer(radio_short_timerid)
|
||||
radio_short = TRUE
|
||||
to_chat(src, "<span class='danger'>Your radio shorts out!</span>")
|
||||
radio_short_timerid = addtimer(CALLBACK(src, .proc/unshort_radio), radio_short_cooldown, flags = TIMER_STOPPABLE)
|
||||
|
||||
/mob/living/silicon/pai/proc/unshort_radio()
|
||||
radio_short = FALSE
|
||||
to_chat(src, "<span class='danger'>You feel your radio is operational once more.</span>")
|
||||
if(radio_short_timerid)
|
||||
deltimer(radio_short_timerid)
|
||||
|
||||
/mob/living/silicon/pai/proc/initialize_dynamic_chassis_icons()
|
||||
. = list()
|
||||
var/icon/curr //for inserts
|
||||
|
||||
//This is a horrible system and I wish I was not as lazy and did something smarter, like just generating a new icon in memory which is probably more efficient.
|
||||
|
||||
//Basic /tg/ cyborgs
|
||||
.["Cyborg - Engineering (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "engineer"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (loaderborg)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "loaderborg"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (handyeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "handyeng"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (sleekeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "sleekeng"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (marinaeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinaeng"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "medical"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (marinamed)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinamed"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (eyebotmed)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "eyebotmed"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "sec"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (sleeksec)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "sleeksec"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (marinasec)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinasec"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Clown (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "clown"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
|
||||
//Citadel dogborgs
|
||||
//Engi
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (dog - valeeng)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Engineering (dog - pupdozer)"] = curr
|
||||
//Med
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "medihound")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (dog - medihound)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (dog - medihounddark)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valemed")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Medical (dog - valemed)"] = curr
|
||||
//Sec
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "k9")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (dog - k9)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (dog - k9dark)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valesec")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Security (dog - valesec)"] = curr
|
||||
//Service
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Service (dog - valeserv)"] = curr
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Service (dog - valeservdark)"] = curr
|
||||
//Sci
|
||||
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valesci")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-rest"), "rest")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-sit"), "sit")
|
||||
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-bellyup"), "bellyup")
|
||||
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
|
||||
.["Cyborg - Science (dog - valesci)"] = curr
|
||||
//Misc
|
||||
.["Cyborg - Misc (dog - blade)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/widerobot.dmi', "blade"), HOLOFORM_FILTER_PAI, FALSE)
|
||||
|
||||
/mob/living/silicon/pai/proc/default_chassis_pixel_offsets_x()
|
||||
. = list()
|
||||
//Engi
|
||||
.["Cyborg - Engineering (dog - valeeng)"] = -16
|
||||
.["Cyborg - Engineering (dog - pupdozer)"] = -16
|
||||
//Med
|
||||
.["Cyborg - Medical (dog - medihound)"] = -16
|
||||
.["Cyborg - Medical (dog - medihounddark)"] = -16
|
||||
.["Cyborg - Medical (dog - valemed)"] = -16
|
||||
//Sec
|
||||
.["Cyborg - Security (dog - k9)"] = -16
|
||||
.["Cyborg - Security (dog - valesec)"] = -16
|
||||
.["Cyborg - Security (dog - k9dark)"] = -16
|
||||
//Service
|
||||
.["Cyborg - Service (dog - valeserv)"] = -16
|
||||
.["Cyborg - Service (dog - valeservdark)"] = -16
|
||||
//Sci
|
||||
.["Cyborg - Security (dog - valesci)"] = -16
|
||||
//Misc
|
||||
.["Cyborg - Misc (dog - blade)"] = -16
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
if(P.stun)
|
||||
fold_in(force = TRUE)
|
||||
visible_message("<span class='warning'>The electrically-charged projectile disrupts [src]'s holomatrix, forcing [src] to fold in!</span>")
|
||||
. = ..()
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/pai/stripPanelUnequip(obj/item/what, mob/who, where) //prevents stripping
|
||||
to_chat(src, "<span class='warning'>Your holochassis stutters and warps intensely as you attempt to interact with the object, forcing you to cease lest the field fail.</span>")
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
visible_message("<span class='notice'>[src] [resting? "lays down for a moment..." : "perks up from the ground"]</span>")
|
||||
update_icon()
|
||||
|
||||
/mob/living/silicon/pai/start_pulling(atom/movable/AM, gs)
|
||||
/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
|
||||
if(ispAI(AM))
|
||||
return ..()
|
||||
return FALSE
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
/*
|
||||
name
|
||||
key
|
||||
description
|
||||
role
|
||||
comments
|
||||
ready = 0
|
||||
*/
|
||||
|
||||
/datum/paiCandidate/proc/savefile_path(mob/user)
|
||||
return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav"
|
||||
|
||||
/datum/paiCandidate/proc/savefile_save(mob/user)
|
||||
if(IsGuestKey(user.key))
|
||||
return 0
|
||||
|
||||
var/savefile/F = new /savefile(src.savefile_path(user))
|
||||
|
||||
|
||||
WRITE_FILE(F["name"], name)
|
||||
WRITE_FILE(F["description"], description)
|
||||
WRITE_FILE(F["role"], role)
|
||||
WRITE_FILE(F["comments"], comments)
|
||||
|
||||
WRITE_FILE(F["version"], 1)
|
||||
|
||||
return 1
|
||||
|
||||
// loads the savefile corresponding to the mob's ckey
|
||||
// if silent=true, report incompatible savefiles
|
||||
// returns 1 if loaded (or file was incompatible)
|
||||
// returns 0 if savefile did not exist
|
||||
|
||||
/datum/paiCandidate/proc/savefile_load(mob/user, silent = TRUE)
|
||||
if (IsGuestKey(user.key))
|
||||
return 0
|
||||
|
||||
var/path = savefile_path(user)
|
||||
|
||||
if (!fexists(path))
|
||||
return 0
|
||||
|
||||
var/savefile/F = new /savefile(path)
|
||||
|
||||
if(!F)
|
||||
return //Not everyone has a pai savefile.
|
||||
|
||||
var/version = null
|
||||
F["version"] >> version
|
||||
|
||||
if (isnull(version) || version != 1)
|
||||
fdel(path)
|
||||
if (!silent)
|
||||
alert(user, "Your savefile was incompatible with this version and was deleted.")
|
||||
return 0
|
||||
|
||||
F["name"] >> src.name
|
||||
F["description"] >> src.description
|
||||
F["role"] >> src.role
|
||||
F["comments"] >> src.comments
|
||||
return 1
|
||||
/*
|
||||
name
|
||||
key
|
||||
description
|
||||
role
|
||||
comments
|
||||
ready = 0
|
||||
*/
|
||||
|
||||
/datum/paiCandidate/proc/savefile_path(mob/user)
|
||||
return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav"
|
||||
|
||||
/datum/paiCandidate/proc/savefile_save(mob/user)
|
||||
if(IsGuestKey(user.key))
|
||||
return 0
|
||||
|
||||
var/savefile/F = new /savefile(src.savefile_path(user))
|
||||
|
||||
|
||||
WRITE_FILE(F["name"], name)
|
||||
WRITE_FILE(F["description"], description)
|
||||
WRITE_FILE(F["role"], role)
|
||||
WRITE_FILE(F["comments"], comments)
|
||||
|
||||
WRITE_FILE(F["version"], 1)
|
||||
|
||||
return 1
|
||||
|
||||
// loads the savefile corresponding to the mob's ckey
|
||||
// if silent=true, report incompatible savefiles
|
||||
// returns 1 if loaded (or file was incompatible)
|
||||
// returns 0 if savefile did not exist
|
||||
|
||||
/datum/paiCandidate/proc/savefile_load(mob/user, silent = TRUE)
|
||||
if (IsGuestKey(user.key))
|
||||
return 0
|
||||
|
||||
var/path = savefile_path(user)
|
||||
|
||||
if (!fexists(path))
|
||||
return 0
|
||||
|
||||
var/savefile/F = new /savefile(path)
|
||||
|
||||
if(!F)
|
||||
return //Not everyone has a pai savefile.
|
||||
|
||||
var/version = null
|
||||
F["version"] >> version
|
||||
|
||||
if (isnull(version) || version != 1)
|
||||
fdel(path)
|
||||
if (!silent)
|
||||
alert(user, "Your savefile was incompatible with this version and was deleted.")
|
||||
return 0
|
||||
|
||||
F["name"] >> src.name
|
||||
F["description"] >> src.description
|
||||
F["role"] >> src.role
|
||||
F["comments"] >> src.comments
|
||||
return 1
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,35 +1,35 @@
|
||||
|
||||
/mob/living/silicon/robot/gib_animation()
|
||||
new /obj/effect/temp_visual/gib_animation(loc, "gibbed-r")
|
||||
|
||||
/mob/living/silicon/robot/dust(just_ash, drop_items, force)
|
||||
if(mmi)
|
||||
qdel(mmi)
|
||||
..()
|
||||
|
||||
/mob/living/silicon/robot/spawn_dust()
|
||||
new /obj/effect/decal/remains/robot(loc)
|
||||
|
||||
/mob/living/silicon/robot/dust_animation()
|
||||
new /obj/effect/temp_visual/dust_animation(loc, "dust-r")
|
||||
|
||||
/mob/living/silicon/robot/death(gibbed)
|
||||
if(stat == DEAD)
|
||||
return
|
||||
|
||||
. = ..()
|
||||
|
||||
locked = FALSE //unlock cover
|
||||
|
||||
update_canmove()
|
||||
if(!QDELETED(builtInCamera) && builtInCamera.status)
|
||||
builtInCamera.toggle_cam(src,0)
|
||||
update_headlamp(1) //So borg lights are disabled when killed.
|
||||
|
||||
uneq_all() // particularly to ensure sight modes are cleared
|
||||
|
||||
update_icons()
|
||||
|
||||
unbuckle_all_mobs(TRUE)
|
||||
|
||||
SSblackbox.ReportDeath(src)
|
||||
|
||||
/mob/living/silicon/robot/gib_animation()
|
||||
new /obj/effect/temp_visual/gib_animation(loc, "gibbed-r")
|
||||
|
||||
/mob/living/silicon/robot/dust(just_ash, drop_items, force)
|
||||
if(mmi)
|
||||
qdel(mmi)
|
||||
..()
|
||||
|
||||
/mob/living/silicon/robot/spawn_dust()
|
||||
new /obj/effect/decal/remains/robot(loc)
|
||||
|
||||
/mob/living/silicon/robot/dust_animation()
|
||||
new /obj/effect/temp_visual/dust_animation(loc, "dust-r")
|
||||
|
||||
/mob/living/silicon/robot/death(gibbed)
|
||||
if(stat == DEAD)
|
||||
return
|
||||
|
||||
. = ..()
|
||||
|
||||
locked = FALSE //unlock cover
|
||||
|
||||
update_canmove()
|
||||
if(!QDELETED(builtInCamera) && builtInCamera.status)
|
||||
builtInCamera.toggle_cam(src,0)
|
||||
update_headlamp(1) //So borg lights are disabled when killed.
|
||||
|
||||
uneq_all() // particularly to ensure sight modes are cleared
|
||||
|
||||
update_icons()
|
||||
|
||||
unbuckle_all_mobs(TRUE)
|
||||
|
||||
SSblackbox.ReportDeath(src)
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
/mob/living/silicon/robot/examine(mob/user)
|
||||
. = list("<span class='info'>*---------*\nThis is [icon2html(src, user)] \a <EM>[src]</EM>, a [src.module.name] unit!")
|
||||
if(desc)
|
||||
. += "[desc]"
|
||||
|
||||
var/obj/act_module = get_active_held_item()
|
||||
if(act_module)
|
||||
. += "It is holding [icon2html(act_module, user)] \a [act_module]."
|
||||
var/effects_exam = status_effect_examines()
|
||||
if(!isnull(effects_exam))
|
||||
. += effects_exam
|
||||
if (getBruteLoss())
|
||||
if (getBruteLoss() < maxHealth*0.5)
|
||||
. += "<span class='warning'>It looks slightly dented.</span>"
|
||||
else
|
||||
. += "<span class='warning'><B>It looks severely dented!</B></span>"
|
||||
if (getFireLoss() || getToxLoss())
|
||||
var/overall_fireloss = getFireLoss() + getToxLoss()
|
||||
if (overall_fireloss < maxHealth * 0.5)
|
||||
. += "<span class='warning'>It looks slightly charred.</span>"
|
||||
else
|
||||
. += "<span class='warning'>It looks slightly charred.</span>"
|
||||
if (health < -maxHealth*0.5)
|
||||
. += "<span class='warning'>It looks barely operational.</span>"
|
||||
if (fire_stacks < 0)
|
||||
. += "<span class='warning'>It's covered in water.</span>"
|
||||
else if (fire_stacks > 0)
|
||||
. += "<span class='warning'>It's coated in something flammable.</span>"
|
||||
|
||||
if(opened)
|
||||
. += "<span class='warning'>Its cover is open and the power cell is [cell ? "installed" : "missing"].</span>"
|
||||
else
|
||||
. += "Its cover is closed[locked ? "" : ", and looks unlocked"]."
|
||||
|
||||
if(cell && cell.charge <= 0)
|
||||
. += "<span class='warning'>Its battery indicator is blinking red!</span>"
|
||||
|
||||
if(is_servant_of_ratvar(src) && get_dist(user, src) <= 1 && !stat) //To counter pseudo-stealth by using headlamps
|
||||
. += "<span class='warning'>Its eyes are glowing a blazing yellow!</span>"
|
||||
|
||||
switch(stat)
|
||||
if(CONSCIOUS)
|
||||
if(shell)
|
||||
. += "It appears to be an [deployed ? "active" : "empty"] AI shell."
|
||||
else if(!client)
|
||||
. += "It appears to be in stand-by mode." //afk
|
||||
if(UNCONSCIOUS)
|
||||
. += "<span class='warning'>It doesn't seem to be responding.</span>"
|
||||
if(DEAD)
|
||||
. += "<span class='deadsay'>It looks like its system is corrupted and requires a reset.</span>"
|
||||
. += "*---------*</span>"
|
||||
|
||||
. += ..()
|
||||
/mob/living/silicon/robot/examine(mob/user)
|
||||
. = list("<span class='info'>*---------*\nThis is [icon2html(src, user)] \a <EM>[src]</EM>, a [src.module.name] unit!")
|
||||
if(desc)
|
||||
. += "[desc]"
|
||||
|
||||
var/obj/act_module = get_active_held_item()
|
||||
if(act_module)
|
||||
. += "It is holding [icon2html(act_module, user)] \a [act_module]."
|
||||
var/effects_exam = status_effect_examines()
|
||||
if(!isnull(effects_exam))
|
||||
. += effects_exam
|
||||
if (getBruteLoss())
|
||||
if (getBruteLoss() < maxHealth*0.5)
|
||||
. += "<span class='warning'>It looks slightly dented.</span>"
|
||||
else
|
||||
. += "<span class='warning'><B>It looks severely dented!</B></span>"
|
||||
if (getFireLoss() || getToxLoss())
|
||||
var/overall_fireloss = getFireLoss() + getToxLoss()
|
||||
if (overall_fireloss < maxHealth * 0.5)
|
||||
. += "<span class='warning'>It looks slightly charred.</span>"
|
||||
else
|
||||
. += "<span class='warning'>It looks slightly charred.</span>"
|
||||
if (health < -maxHealth*0.5)
|
||||
. += "<span class='warning'>It looks barely operational.</span>"
|
||||
if (fire_stacks < 0)
|
||||
. += "<span class='warning'>It's covered in water.</span>"
|
||||
else if (fire_stacks > 0)
|
||||
. += "<span class='warning'>It's coated in something flammable.</span>"
|
||||
|
||||
if(opened)
|
||||
. += "<span class='warning'>Its cover is open and the power cell is [cell ? "installed" : "missing"].</span>"
|
||||
else
|
||||
. += "Its cover is closed[locked ? "" : ", and looks unlocked"]."
|
||||
|
||||
if(cell && cell.charge <= 0)
|
||||
. += "<span class='warning'>Its battery indicator is blinking red!</span>"
|
||||
|
||||
if(is_servant_of_ratvar(src) && get_dist(user, src) <= 1 && !stat) //To counter pseudo-stealth by using headlamps
|
||||
. += "<span class='warning'>Its eyes are glowing a blazing yellow!</span>"
|
||||
|
||||
switch(stat)
|
||||
if(CONSCIOUS)
|
||||
if(shell)
|
||||
. += "It appears to be an [deployed ? "active" : "empty"] AI shell."
|
||||
else if(!client)
|
||||
. += "It appears to be in stand-by mode." //afk
|
||||
if(UNCONSCIOUS)
|
||||
. += "<span class='warning'>It doesn't seem to be responding.</span>"
|
||||
if(DEAD)
|
||||
. += "<span class='deadsay'>It looks like its system is corrupted and requires a reset.</span>"
|
||||
. += "*---------*</span>"
|
||||
|
||||
. += ..()
|
||||
|
||||
@@ -1,235 +1,235 @@
|
||||
//These procs handle putting stuff in your hand. It's probably best to use these rather than setting stuff manually
|
||||
//as they handle all relevant stuff like adding it to the player's screen and such
|
||||
|
||||
//Returns the thing in our active hand (whatever is in our active module-slot, in this case)
|
||||
/mob/living/silicon/robot/get_active_held_item()
|
||||
return module_active
|
||||
|
||||
|
||||
|
||||
/mob/living/silicon/robot/proc/uneq_module(obj/item/O)
|
||||
if(!O)
|
||||
return 0
|
||||
O.mouse_opacity = MOUSE_OPACITY_OPAQUE
|
||||
if(istype(O, /obj/item/borg/sight))
|
||||
var/obj/item/borg/sight/S = O
|
||||
sight_mode &= ~S.sight_mode
|
||||
update_sight()
|
||||
else if(istype(O, /obj/item/storage/bag/tray/))
|
||||
SEND_SIGNAL(O, COMSIG_TRY_STORAGE_QUICK_EMPTY)
|
||||
//CITADEL EDIT reee proc, Dogborg modules
|
||||
if(istype(O,/obj/item/gun/energy/laser/cyborg))
|
||||
laser = FALSE
|
||||
update_icons()
|
||||
else if(istype(O,/obj/item/gun/energy/disabler/cyborg) || istype(O,/obj/item/gun/energy/e_gun/advtaser/cyborg))
|
||||
disabler = FALSE
|
||||
update_icons() //PUT THE GUN AWAY
|
||||
else if(istype(O,/obj/item/dogborg/sleeper))
|
||||
sleeper_g = FALSE
|
||||
sleeper_r = FALSE
|
||||
update_icons()
|
||||
var/obj/item/dogborg/sleeper/S = O
|
||||
S.go_out() //this should stop edgecase deletions
|
||||
//END CITADEL EDIT
|
||||
if(client)
|
||||
client.screen -= O
|
||||
observer_screen_update(O,FALSE)
|
||||
|
||||
if(module_active == O)
|
||||
module_active = null
|
||||
if(held_items[1] == O)
|
||||
inv1.icon_state = "inv1"
|
||||
held_items[1] = null
|
||||
else if(held_items[2] == O)
|
||||
inv2.icon_state = "inv2"
|
||||
held_items[2] = null
|
||||
else if(held_items[3] == O)
|
||||
inv3.icon_state = "inv3"
|
||||
held_items[3] = null
|
||||
|
||||
if(O.item_flags & DROPDEL)
|
||||
O.item_flags &= ~DROPDEL //we shouldn't HAVE things with DROPDEL_1 in our modules, but better safe than runtiming horribly
|
||||
|
||||
O.forceMove(module) //Return item to module so it appears in its contents, so it can be taken out again.
|
||||
|
||||
hud_used.update_robot_modules_display()
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/robot/proc/activate_module(obj/item/O)
|
||||
. = FALSE
|
||||
if(!(O in module.modules))
|
||||
return
|
||||
//CITADEL EDIT Dogborg lasers
|
||||
if(istype(O,/obj/item/gun/energy/laser/cyborg))
|
||||
laser = TRUE
|
||||
update_icons() //REEEEEEACH FOR THE SKY
|
||||
if(istype(O,/obj/item/gun/energy/disabler/cyborg) || istype(O,/obj/item/gun/energy/e_gun/advtaser/cyborg))
|
||||
disabler = TRUE
|
||||
update_icons()
|
||||
//END CITADEL EDIT
|
||||
if(activated(O))
|
||||
to_chat(src, "<span class='warning'>That module is already activated.</span>")
|
||||
return
|
||||
if(!held_items[1])
|
||||
held_items[1] = O
|
||||
O.screen_loc = inv1.screen_loc
|
||||
. = TRUE
|
||||
else if(!held_items[2])
|
||||
held_items[2] = O
|
||||
O.screen_loc = inv2.screen_loc
|
||||
. = TRUE
|
||||
else if(!held_items[3])
|
||||
held_items[3] = O
|
||||
O.screen_loc = inv3.screen_loc
|
||||
. = TRUE
|
||||
else
|
||||
to_chat(src, "<span class='warning'>You need to disable a module first!</span>")
|
||||
if(.)
|
||||
O.equipped(src, SLOT_HANDS)
|
||||
O.mouse_opacity = initial(O.mouse_opacity)
|
||||
O.layer = ABOVE_HUD_LAYER
|
||||
O.plane = ABOVE_HUD_PLANE
|
||||
observer_screen_update(O,TRUE)
|
||||
O.forceMove(src)
|
||||
if(istype(O, /obj/item/borg/sight))
|
||||
var/obj/item/borg/sight/S = O
|
||||
sight_mode |= S.sight_mode
|
||||
update_sight()
|
||||
|
||||
|
||||
/mob/living/silicon/robot/proc/observer_screen_update(obj/item/I,add = TRUE)
|
||||
if(observers && observers.len)
|
||||
for(var/M in observers)
|
||||
var/mob/dead/observe = M
|
||||
if(observe.client && observe.client.eye == src)
|
||||
if(add)
|
||||
observe.client.screen += I
|
||||
else
|
||||
observe.client.screen -= I
|
||||
else
|
||||
observers -= observe
|
||||
if(!observers.len)
|
||||
observers = null
|
||||
break
|
||||
|
||||
/mob/living/silicon/robot/proc/uneq_active()
|
||||
uneq_module(module_active)
|
||||
|
||||
/mob/living/silicon/robot/proc/uneq_all()
|
||||
for(var/obj/item/I in held_items)
|
||||
uneq_module(I)
|
||||
|
||||
/mob/living/silicon/robot/proc/activated(obj/item/O)
|
||||
if(O in held_items)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//Helper procs for cyborg modules on the UI.
|
||||
//These are hackish but they help clean up code elsewhere.
|
||||
|
||||
//module_selected(module) - Checks whether the module slot specified by "module" is currently selected.
|
||||
/mob/living/silicon/robot/proc/module_selected(module) //Module is 1-3
|
||||
return module == get_selected_module()
|
||||
|
||||
//module_active(module) - Checks whether there is a module active in the slot specified by "module".
|
||||
/mob/living/silicon/robot/proc/module_active(module) //Module is 1-3
|
||||
if(module < 1 || module > 3)
|
||||
return FALSE
|
||||
|
||||
if(LAZYLEN(held_items) >= module)
|
||||
if(held_items[module])
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected.
|
||||
/mob/living/silicon/robot/proc/get_selected_module()
|
||||
if(module_active)
|
||||
return held_items.Find(module_active)
|
||||
|
||||
return 0
|
||||
|
||||
//select_module(module) - Selects the module slot specified by "module"
|
||||
/mob/living/silicon/robot/proc/select_module(module) //Module is 1-3
|
||||
if(module < 1 || module > 3)
|
||||
return
|
||||
|
||||
if(!module_active(module))
|
||||
return
|
||||
|
||||
switch(module)
|
||||
if(1)
|
||||
if(module_active != held_items[module])
|
||||
inv1.icon_state = "inv1 +a"
|
||||
inv2.icon_state = "inv2"
|
||||
inv3.icon_state = "inv3"
|
||||
if(2)
|
||||
if(module_active != held_items[module])
|
||||
inv1.icon_state = "inv1"
|
||||
inv2.icon_state = "inv2 +a"
|
||||
inv3.icon_state = "inv3"
|
||||
if(3)
|
||||
if(module_active != held_items[module])
|
||||
inv1.icon_state = "inv1"
|
||||
inv2.icon_state = "inv2"
|
||||
inv3.icon_state = "inv3 +a"
|
||||
module_active = held_items[module]
|
||||
|
||||
//deselect_module(module) - Deselects the module slot specified by "module"
|
||||
/mob/living/silicon/robot/proc/deselect_module(module) //Module is 1-3
|
||||
if(module < 1 || module > 3)
|
||||
return
|
||||
|
||||
if(!module_active(module))
|
||||
return
|
||||
|
||||
switch(module)
|
||||
if(1)
|
||||
if(module_active == held_items[module])
|
||||
inv1.icon_state = "inv1"
|
||||
if(2)
|
||||
if(module_active == held_items[module])
|
||||
inv2.icon_state = "inv2"
|
||||
if(3)
|
||||
if(module_active == held_items[module])
|
||||
inv3.icon_state = "inv3"
|
||||
module_active = null
|
||||
|
||||
//toggle_module(module) - Toggles the selection of the module slot specified by "module".
|
||||
/mob/living/silicon/robot/proc/toggle_module(module) //Module is 1-3
|
||||
if(module < 1 || module > 3)
|
||||
return
|
||||
|
||||
if(module_selected(module))
|
||||
deselect_module(module)
|
||||
else
|
||||
if(module_active(module))
|
||||
select_module(module)
|
||||
else
|
||||
deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module.
|
||||
return
|
||||
|
||||
//cycle_modules() - Cycles through the list of selected modules.
|
||||
/mob/living/silicon/robot/proc/cycle_modules()
|
||||
var/slot_start = get_selected_module()
|
||||
if(slot_start)
|
||||
deselect_module(slot_start) //Only deselect if we have a selected slot.
|
||||
|
||||
var/slot_num
|
||||
if(slot_start == 0)
|
||||
slot_num = 1
|
||||
slot_start = 4
|
||||
else
|
||||
slot_num = slot_start + 1
|
||||
|
||||
while(slot_num != slot_start) //If we wrap around without finding any free slots, just give up.
|
||||
if(module_active(slot_num))
|
||||
select_module(slot_num)
|
||||
return
|
||||
slot_num++
|
||||
if(slot_num > 4) // not >3 otherwise cycling with just one item on module 3 wouldn't work
|
||||
slot_num = 1 //Wrap around.
|
||||
|
||||
|
||||
|
||||
/mob/living/silicon/robot/swap_hand()
|
||||
cycle_modules()
|
||||
//These procs handle putting stuff in your hand. It's probably best to use these rather than setting stuff manually
|
||||
//as they handle all relevant stuff like adding it to the player's screen and such
|
||||
|
||||
//Returns the thing in our active hand (whatever is in our active module-slot, in this case)
|
||||
/mob/living/silicon/robot/get_active_held_item()
|
||||
return module_active
|
||||
|
||||
|
||||
|
||||
/mob/living/silicon/robot/proc/uneq_module(obj/item/O)
|
||||
if(!O)
|
||||
return 0
|
||||
O.mouse_opacity = MOUSE_OPACITY_OPAQUE
|
||||
if(istype(O, /obj/item/borg/sight))
|
||||
var/obj/item/borg/sight/S = O
|
||||
sight_mode &= ~S.sight_mode
|
||||
update_sight()
|
||||
else if(istype(O, /obj/item/storage/bag/tray/))
|
||||
SEND_SIGNAL(O, COMSIG_TRY_STORAGE_QUICK_EMPTY)
|
||||
//CITADEL EDIT reee proc, Dogborg modules
|
||||
if(istype(O,/obj/item/gun/energy/laser/cyborg))
|
||||
laser = FALSE
|
||||
update_icons()
|
||||
else if(istype(O,/obj/item/gun/energy/disabler/cyborg) || istype(O,/obj/item/gun/energy/e_gun/advtaser/cyborg))
|
||||
disabler = FALSE
|
||||
update_icons() //PUT THE GUN AWAY
|
||||
else if(istype(O,/obj/item/dogborg/sleeper))
|
||||
sleeper_g = FALSE
|
||||
sleeper_r = FALSE
|
||||
update_icons()
|
||||
var/obj/item/dogborg/sleeper/S = O
|
||||
S.go_out() //this should stop edgecase deletions
|
||||
//END CITADEL EDIT
|
||||
if(client)
|
||||
client.screen -= O
|
||||
observer_screen_update(O,FALSE)
|
||||
|
||||
if(module_active == O)
|
||||
module_active = null
|
||||
if(held_items[1] == O)
|
||||
inv1.icon_state = "inv1"
|
||||
held_items[1] = null
|
||||
else if(held_items[2] == O)
|
||||
inv2.icon_state = "inv2"
|
||||
held_items[2] = null
|
||||
else if(held_items[3] == O)
|
||||
inv3.icon_state = "inv3"
|
||||
held_items[3] = null
|
||||
|
||||
if(O.item_flags & DROPDEL)
|
||||
O.item_flags &= ~DROPDEL //we shouldn't HAVE things with DROPDEL_1 in our modules, but better safe than runtiming horribly
|
||||
|
||||
O.forceMove(module) //Return item to module so it appears in its contents, so it can be taken out again.
|
||||
|
||||
hud_used.update_robot_modules_display()
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/robot/proc/activate_module(obj/item/O)
|
||||
. = FALSE
|
||||
if(!(O in module.modules))
|
||||
return
|
||||
//CITADEL EDIT Dogborg lasers
|
||||
if(istype(O,/obj/item/gun/energy/laser/cyborg))
|
||||
laser = TRUE
|
||||
update_icons() //REEEEEEACH FOR THE SKY
|
||||
if(istype(O,/obj/item/gun/energy/disabler/cyborg) || istype(O,/obj/item/gun/energy/e_gun/advtaser/cyborg))
|
||||
disabler = TRUE
|
||||
update_icons()
|
||||
//END CITADEL EDIT
|
||||
if(activated(O))
|
||||
to_chat(src, "<span class='warning'>That module is already activated.</span>")
|
||||
return
|
||||
if(!held_items[1])
|
||||
held_items[1] = O
|
||||
O.screen_loc = inv1.screen_loc
|
||||
. = TRUE
|
||||
else if(!held_items[2])
|
||||
held_items[2] = O
|
||||
O.screen_loc = inv2.screen_loc
|
||||
. = TRUE
|
||||
else if(!held_items[3])
|
||||
held_items[3] = O
|
||||
O.screen_loc = inv3.screen_loc
|
||||
. = TRUE
|
||||
else
|
||||
to_chat(src, "<span class='warning'>You need to disable a module first!</span>")
|
||||
if(.)
|
||||
O.equipped(src, SLOT_HANDS)
|
||||
O.mouse_opacity = initial(O.mouse_opacity)
|
||||
O.layer = ABOVE_HUD_LAYER
|
||||
O.plane = ABOVE_HUD_PLANE
|
||||
observer_screen_update(O,TRUE)
|
||||
O.forceMove(src)
|
||||
if(istype(O, /obj/item/borg/sight))
|
||||
var/obj/item/borg/sight/S = O
|
||||
sight_mode |= S.sight_mode
|
||||
update_sight()
|
||||
|
||||
|
||||
/mob/living/silicon/robot/proc/observer_screen_update(obj/item/I,add = TRUE)
|
||||
if(observers && observers.len)
|
||||
for(var/M in observers)
|
||||
var/mob/dead/observe = M
|
||||
if(observe.client && observe.client.eye == src)
|
||||
if(add)
|
||||
observe.client.screen += I
|
||||
else
|
||||
observe.client.screen -= I
|
||||
else
|
||||
observers -= observe
|
||||
if(!observers.len)
|
||||
observers = null
|
||||
break
|
||||
|
||||
/mob/living/silicon/robot/proc/uneq_active()
|
||||
uneq_module(module_active)
|
||||
|
||||
/mob/living/silicon/robot/proc/uneq_all()
|
||||
for(var/obj/item/I in held_items)
|
||||
uneq_module(I)
|
||||
|
||||
/mob/living/silicon/robot/proc/activated(obj/item/O)
|
||||
if(O in held_items)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//Helper procs for cyborg modules on the UI.
|
||||
//These are hackish but they help clean up code elsewhere.
|
||||
|
||||
//module_selected(module) - Checks whether the module slot specified by "module" is currently selected.
|
||||
/mob/living/silicon/robot/proc/module_selected(module) //Module is 1-3
|
||||
return module == get_selected_module()
|
||||
|
||||
//module_active(module) - Checks whether there is a module active in the slot specified by "module".
|
||||
/mob/living/silicon/robot/proc/module_active(module) //Module is 1-3
|
||||
if(module < 1 || module > 3)
|
||||
return FALSE
|
||||
|
||||
if(LAZYLEN(held_items) >= module)
|
||||
if(held_items[module])
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected.
|
||||
/mob/living/silicon/robot/proc/get_selected_module()
|
||||
if(module_active)
|
||||
return held_items.Find(module_active)
|
||||
|
||||
return 0
|
||||
|
||||
//select_module(module) - Selects the module slot specified by "module"
|
||||
/mob/living/silicon/robot/proc/select_module(module) //Module is 1-3
|
||||
if(module < 1 || module > 3)
|
||||
return
|
||||
|
||||
if(!module_active(module))
|
||||
return
|
||||
|
||||
switch(module)
|
||||
if(1)
|
||||
if(module_active != held_items[module])
|
||||
inv1.icon_state = "inv1 +a"
|
||||
inv2.icon_state = "inv2"
|
||||
inv3.icon_state = "inv3"
|
||||
if(2)
|
||||
if(module_active != held_items[module])
|
||||
inv1.icon_state = "inv1"
|
||||
inv2.icon_state = "inv2 +a"
|
||||
inv3.icon_state = "inv3"
|
||||
if(3)
|
||||
if(module_active != held_items[module])
|
||||
inv1.icon_state = "inv1"
|
||||
inv2.icon_state = "inv2"
|
||||
inv3.icon_state = "inv3 +a"
|
||||
module_active = held_items[module]
|
||||
|
||||
//deselect_module(module) - Deselects the module slot specified by "module"
|
||||
/mob/living/silicon/robot/proc/deselect_module(module) //Module is 1-3
|
||||
if(module < 1 || module > 3)
|
||||
return
|
||||
|
||||
if(!module_active(module))
|
||||
return
|
||||
|
||||
switch(module)
|
||||
if(1)
|
||||
if(module_active == held_items[module])
|
||||
inv1.icon_state = "inv1"
|
||||
if(2)
|
||||
if(module_active == held_items[module])
|
||||
inv2.icon_state = "inv2"
|
||||
if(3)
|
||||
if(module_active == held_items[module])
|
||||
inv3.icon_state = "inv3"
|
||||
module_active = null
|
||||
|
||||
//toggle_module(module) - Toggles the selection of the module slot specified by "module".
|
||||
/mob/living/silicon/robot/proc/toggle_module(module) //Module is 1-3
|
||||
if(module < 1 || module > 3)
|
||||
return
|
||||
|
||||
if(module_selected(module))
|
||||
deselect_module(module)
|
||||
else
|
||||
if(module_active(module))
|
||||
select_module(module)
|
||||
else
|
||||
deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module.
|
||||
return
|
||||
|
||||
//cycle_modules() - Cycles through the list of selected modules.
|
||||
/mob/living/silicon/robot/proc/cycle_modules()
|
||||
var/slot_start = get_selected_module()
|
||||
if(slot_start)
|
||||
deselect_module(slot_start) //Only deselect if we have a selected slot.
|
||||
|
||||
var/slot_num
|
||||
if(slot_start == 0)
|
||||
slot_num = 1
|
||||
slot_start = 4
|
||||
else
|
||||
slot_num = slot_start + 1
|
||||
|
||||
while(slot_num != slot_start) //If we wrap around without finding any free slots, just give up.
|
||||
if(module_active(slot_num))
|
||||
select_module(slot_num)
|
||||
return
|
||||
slot_num++
|
||||
if(slot_num > 4) // not >3 otherwise cycling with just one item on module 3 wouldn't work
|
||||
slot_num = 1 //Wrap around.
|
||||
|
||||
|
||||
|
||||
/mob/living/silicon/robot/swap_hand()
|
||||
cycle_modules()
|
||||
|
||||
@@ -1,82 +1,82 @@
|
||||
/mob/living/silicon/robot/verb/cmd_show_laws()
|
||||
set category = "Robot Commands"
|
||||
set name = "Show Laws"
|
||||
|
||||
if(usr.stat == DEAD)
|
||||
return //won't work if dead
|
||||
show_laws()
|
||||
|
||||
/mob/living/silicon/robot/show_laws(everyone = 0)
|
||||
laws_sanity_check()
|
||||
var/who
|
||||
|
||||
if (everyone)
|
||||
who = world
|
||||
else
|
||||
who = src
|
||||
if(lawupdate)
|
||||
if (connected_ai)
|
||||
if(connected_ai.stat || connected_ai.control_disabled)
|
||||
to_chat(src, "<b>AI signal lost, unable to sync laws.</b>")
|
||||
|
||||
else
|
||||
lawsync()
|
||||
to_chat(src, "<b>Laws synced with AI, be sure to note any changes.</b>")
|
||||
else
|
||||
to_chat(src, "<b>No AI selected to sync laws with, disabling lawsync protocol.</b>")
|
||||
lawupdate = 0
|
||||
|
||||
to_chat(who, "<b>Obey these laws:</b>")
|
||||
laws.show_laws(who)
|
||||
if (shell) //AI shell
|
||||
to_chat(who, "<b>Remember, you are an AI remotely controlling your shell, other AIs can be ignored.</b>")
|
||||
else if (connected_ai)
|
||||
to_chat(who, "<b>Remember, [connected_ai.name] is your master, other AIs can be ignored.</b>")
|
||||
else if (emagged)
|
||||
to_chat(who, "<b>Remember, you are not required to listen to the AI.</b>")
|
||||
else
|
||||
to_chat(who, "<b>Remember, you are not bound to any AI, you are not required to listen to them.</b>")
|
||||
|
||||
|
||||
/mob/living/silicon/robot/proc/lawsync()
|
||||
laws_sanity_check()
|
||||
var/datum/ai_laws/master = connected_ai ? connected_ai.laws : null
|
||||
var/temp
|
||||
if (master)
|
||||
laws.devillaws.len = master.devillaws.len
|
||||
for (var/index = 1, index <= master.devillaws.len, index++)
|
||||
temp = master.devillaws[index]
|
||||
if (length(temp) > 0)
|
||||
laws.devillaws[index] = temp
|
||||
|
||||
laws.ion.len = master.ion.len
|
||||
for (var/index = 1, index <= master.ion.len, index++)
|
||||
temp = master.ion[index]
|
||||
if (length(temp) > 0)
|
||||
laws.ion[index] = temp
|
||||
|
||||
laws.hacked.len = master.hacked.len
|
||||
for (var/index = 1, index <= master.hacked.len, index++)
|
||||
temp = master.hacked[index]
|
||||
if (length(temp) > 0)
|
||||
laws.hacked[index] = temp
|
||||
|
||||
if(master.zeroth_borg) //If the AI has a defined law zero specifically for its borgs, give it that one, otherwise give it the same one. --NEO
|
||||
temp = master.zeroth_borg
|
||||
else
|
||||
temp = master.zeroth
|
||||
laws.zeroth = temp
|
||||
|
||||
laws.inherent.len = master.inherent.len
|
||||
for (var/index = 1, index <= master.inherent.len, index++)
|
||||
temp = master.inherent[index]
|
||||
if (length(temp) > 0)
|
||||
laws.inherent[index] = temp
|
||||
|
||||
laws.supplied.len = master.supplied.len
|
||||
for (var/index = 1, index <= master.supplied.len, index++)
|
||||
temp = master.supplied[index]
|
||||
if (length(temp) > 0)
|
||||
laws.supplied[index] = temp
|
||||
|
||||
picturesync()
|
||||
/mob/living/silicon/robot/verb/cmd_show_laws()
|
||||
set category = "Robot Commands"
|
||||
set name = "Show Laws"
|
||||
|
||||
if(usr.stat == DEAD)
|
||||
return //won't work if dead
|
||||
show_laws()
|
||||
|
||||
/mob/living/silicon/robot/show_laws(everyone = 0)
|
||||
laws_sanity_check()
|
||||
var/who
|
||||
|
||||
if (everyone)
|
||||
who = world
|
||||
else
|
||||
who = src
|
||||
if(lawupdate)
|
||||
if (connected_ai)
|
||||
if(connected_ai.stat || connected_ai.control_disabled)
|
||||
to_chat(src, "<b>AI signal lost, unable to sync laws.</b>")
|
||||
|
||||
else
|
||||
lawsync()
|
||||
to_chat(src, "<b>Laws synced with AI, be sure to note any changes.</b>")
|
||||
else
|
||||
to_chat(src, "<b>No AI selected to sync laws with, disabling lawsync protocol.</b>")
|
||||
lawupdate = 0
|
||||
|
||||
to_chat(who, "<b>Obey these laws:</b>")
|
||||
laws.show_laws(who)
|
||||
if (shell) //AI shell
|
||||
to_chat(who, "<b>Remember, you are an AI remotely controlling your shell, other AIs can be ignored.</b>")
|
||||
else if (connected_ai)
|
||||
to_chat(who, "<b>Remember, [connected_ai.name] is your master, other AIs can be ignored.</b>")
|
||||
else if (emagged)
|
||||
to_chat(who, "<b>Remember, you are not required to listen to the AI.</b>")
|
||||
else
|
||||
to_chat(who, "<b>Remember, you are not bound to any AI, you are not required to listen to them.</b>")
|
||||
|
||||
|
||||
/mob/living/silicon/robot/proc/lawsync()
|
||||
laws_sanity_check()
|
||||
var/datum/ai_laws/master = connected_ai ? connected_ai.laws : null
|
||||
var/temp
|
||||
if (master)
|
||||
laws.devillaws.len = master.devillaws.len
|
||||
for (var/index = 1, index <= master.devillaws.len, index++)
|
||||
temp = master.devillaws[index]
|
||||
if (length(temp) > 0)
|
||||
laws.devillaws[index] = temp
|
||||
|
||||
laws.ion.len = master.ion.len
|
||||
for (var/index = 1, index <= master.ion.len, index++)
|
||||
temp = master.ion[index]
|
||||
if (length(temp) > 0)
|
||||
laws.ion[index] = temp
|
||||
|
||||
laws.hacked.len = master.hacked.len
|
||||
for (var/index = 1, index <= master.hacked.len, index++)
|
||||
temp = master.hacked[index]
|
||||
if (length(temp) > 0)
|
||||
laws.hacked[index] = temp
|
||||
|
||||
if(master.zeroth_borg) //If the AI has a defined law zero specifically for its borgs, give it that one, otherwise give it the same one. --NEO
|
||||
temp = master.zeroth_borg
|
||||
else
|
||||
temp = master.zeroth
|
||||
laws.zeroth = temp
|
||||
|
||||
laws.inherent.len = master.inherent.len
|
||||
for (var/index = 1, index <= master.inherent.len, index++)
|
||||
temp = master.inherent[index]
|
||||
if (length(temp) > 0)
|
||||
laws.inherent[index] = temp
|
||||
|
||||
laws.supplied.len = master.supplied.len
|
||||
for (var/index = 1, index <= master.supplied.len, index++)
|
||||
temp = master.supplied[index]
|
||||
if (length(temp) > 0)
|
||||
laws.supplied[index] = temp
|
||||
|
||||
picturesync()
|
||||
|
||||
@@ -1,102 +1,102 @@
|
||||
/mob/living/silicon/robot/Life()
|
||||
set invisibility = 0
|
||||
if (src.notransform)
|
||||
return
|
||||
|
||||
..()
|
||||
adjustOxyLoss(-10) //we're a robot!
|
||||
handle_robot_hud_updates()
|
||||
handle_robot_cell()
|
||||
|
||||
/mob/living/silicon/robot/proc/handle_robot_cell()
|
||||
if(stat != DEAD)
|
||||
if(low_power_mode)
|
||||
if(cell && cell.charge)
|
||||
low_power_mode = 0
|
||||
update_headlamp()
|
||||
else if(stat == CONSCIOUS)
|
||||
use_power()
|
||||
|
||||
/mob/living/silicon/robot/proc/use_power()
|
||||
if(cell && cell.charge)
|
||||
if(cell.charge <= 100)
|
||||
uneq_all()
|
||||
var/amt = CLAMP((lamp_intensity - 2) * 2,1,cell.charge) //Always try to use at least one charge per tick, but allow it to completely drain the cell.
|
||||
cell.use(amt) //Usage table: 1/tick if off/lowest setting, 4 = 4/tick, 6 = 8/tick, 8 = 12/tick, 10 = 16/tick
|
||||
else
|
||||
uneq_all()
|
||||
low_power_mode = 1
|
||||
update_headlamp()
|
||||
diag_hud_set_borgcell()
|
||||
|
||||
/mob/living/silicon/robot/proc/handle_robot_hud_updates()
|
||||
if(!client)
|
||||
return
|
||||
|
||||
update_cell_hud_icon()
|
||||
|
||||
/mob/living/silicon/robot/update_health_hud()
|
||||
if(!client || !hud_used)
|
||||
return
|
||||
if(hud_used.healths)
|
||||
if(stat != DEAD)
|
||||
if(health >= maxHealth)
|
||||
hud_used.healths.icon_state = "health0"
|
||||
else if(health > maxHealth*0.6)
|
||||
hud_used.healths.icon_state = "health2"
|
||||
else if(health > maxHealth*0.2)
|
||||
hud_used.healths.icon_state = "health3"
|
||||
else if(health > -maxHealth*0.2)
|
||||
hud_used.healths.icon_state = "health4"
|
||||
else if(health > -maxHealth*0.6)
|
||||
hud_used.healths.icon_state = "health5"
|
||||
else
|
||||
hud_used.healths.icon_state = "health6"
|
||||
else
|
||||
hud_used.healths.icon_state = "health7"
|
||||
|
||||
/mob/living/silicon/robot/proc/update_cell_hud_icon()
|
||||
if(cell)
|
||||
var/cellcharge = cell.charge/cell.maxcharge
|
||||
switch(cellcharge)
|
||||
if(0.75 to INFINITY)
|
||||
clear_alert("charge")
|
||||
if(0.5 to 0.75)
|
||||
throw_alert("charge", /obj/screen/alert/lowcell, 1)
|
||||
if(0.25 to 0.5)
|
||||
throw_alert("charge", /obj/screen/alert/lowcell, 2)
|
||||
if(0.01 to 0.25)
|
||||
throw_alert("charge", /obj/screen/alert/lowcell, 3)
|
||||
else
|
||||
throw_alert("charge", /obj/screen/alert/emptycell)
|
||||
else
|
||||
throw_alert("charge", /obj/screen/alert/nocell)
|
||||
|
||||
//Robots on fire
|
||||
/mob/living/silicon/robot/handle_fire()
|
||||
if(..())
|
||||
return
|
||||
if(fire_stacks > 0)
|
||||
fire_stacks--
|
||||
fire_stacks = max(0, fire_stacks)
|
||||
else
|
||||
ExtinguishMob()
|
||||
|
||||
//adjustFireLoss(3)
|
||||
return
|
||||
|
||||
/mob/living/silicon/robot/update_fire()
|
||||
var/mutable_appearance/fire_overlay = mutable_appearance('icons/mob/OnFire.dmi', "Generic_mob_burning")
|
||||
if(on_fire)
|
||||
add_overlay(fire_overlay)
|
||||
else
|
||||
cut_overlay(fire_overlay)
|
||||
|
||||
/mob/living/silicon/robot/update_canmove()
|
||||
if(stat || buckled || lockcharge || resting) //CITADEL EDIT resting dogborg-os
|
||||
canmove = 0
|
||||
else
|
||||
canmove = 1
|
||||
update_transform()
|
||||
update_action_buttons_icon()
|
||||
return canmove
|
||||
/mob/living/silicon/robot/Life()
|
||||
set invisibility = 0
|
||||
if (src.notransform)
|
||||
return
|
||||
|
||||
..()
|
||||
adjustOxyLoss(-10) //we're a robot!
|
||||
handle_robot_hud_updates()
|
||||
handle_robot_cell()
|
||||
|
||||
/mob/living/silicon/robot/proc/handle_robot_cell()
|
||||
if(stat != DEAD)
|
||||
if(low_power_mode)
|
||||
if(cell && cell.charge)
|
||||
low_power_mode = 0
|
||||
update_headlamp()
|
||||
else if(stat == CONSCIOUS)
|
||||
use_power()
|
||||
|
||||
/mob/living/silicon/robot/proc/use_power()
|
||||
if(cell && cell.charge)
|
||||
if(cell.charge <= 100)
|
||||
uneq_all()
|
||||
var/amt = CLAMP((lamp_intensity - 2) * 2,1,cell.charge) //Always try to use at least one charge per tick, but allow it to completely drain the cell.
|
||||
cell.use(amt) //Usage table: 1/tick if off/lowest setting, 4 = 4/tick, 6 = 8/tick, 8 = 12/tick, 10 = 16/tick
|
||||
else
|
||||
uneq_all()
|
||||
low_power_mode = 1
|
||||
update_headlamp()
|
||||
diag_hud_set_borgcell()
|
||||
|
||||
/mob/living/silicon/robot/proc/handle_robot_hud_updates()
|
||||
if(!client)
|
||||
return
|
||||
|
||||
update_cell_hud_icon()
|
||||
|
||||
/mob/living/silicon/robot/update_health_hud()
|
||||
if(!client || !hud_used)
|
||||
return
|
||||
if(hud_used.healths)
|
||||
if(stat != DEAD)
|
||||
if(health >= maxHealth)
|
||||
hud_used.healths.icon_state = "health0"
|
||||
else if(health > maxHealth*0.6)
|
||||
hud_used.healths.icon_state = "health2"
|
||||
else if(health > maxHealth*0.2)
|
||||
hud_used.healths.icon_state = "health3"
|
||||
else if(health > -maxHealth*0.2)
|
||||
hud_used.healths.icon_state = "health4"
|
||||
else if(health > -maxHealth*0.6)
|
||||
hud_used.healths.icon_state = "health5"
|
||||
else
|
||||
hud_used.healths.icon_state = "health6"
|
||||
else
|
||||
hud_used.healths.icon_state = "health7"
|
||||
|
||||
/mob/living/silicon/robot/proc/update_cell_hud_icon()
|
||||
if(cell)
|
||||
var/cellcharge = cell.charge/cell.maxcharge
|
||||
switch(cellcharge)
|
||||
if(0.75 to INFINITY)
|
||||
clear_alert("charge")
|
||||
if(0.5 to 0.75)
|
||||
throw_alert("charge", /obj/screen/alert/lowcell, 1)
|
||||
if(0.25 to 0.5)
|
||||
throw_alert("charge", /obj/screen/alert/lowcell, 2)
|
||||
if(0.01 to 0.25)
|
||||
throw_alert("charge", /obj/screen/alert/lowcell, 3)
|
||||
else
|
||||
throw_alert("charge", /obj/screen/alert/emptycell)
|
||||
else
|
||||
throw_alert("charge", /obj/screen/alert/nocell)
|
||||
|
||||
//Robots on fire
|
||||
/mob/living/silicon/robot/handle_fire()
|
||||
if(..())
|
||||
return
|
||||
if(fire_stacks > 0)
|
||||
fire_stacks--
|
||||
fire_stacks = max(0, fire_stacks)
|
||||
else
|
||||
ExtinguishMob()
|
||||
|
||||
//adjustFireLoss(3)
|
||||
return
|
||||
|
||||
/mob/living/silicon/robot/update_fire()
|
||||
var/mutable_appearance/fire_overlay = mutable_appearance('icons/mob/OnFire.dmi', "Generic_mob_burning")
|
||||
if(on_fire)
|
||||
add_overlay(fire_overlay)
|
||||
else
|
||||
cut_overlay(fire_overlay)
|
||||
|
||||
/mob/living/silicon/robot/update_canmove()
|
||||
if(stat || buckled || lockcharge || resting) //CITADEL EDIT resting dogborg-os
|
||||
canmove = 0
|
||||
else
|
||||
canmove = 1
|
||||
update_transform()
|
||||
update_action_buttons_icon()
|
||||
return canmove
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
/mob/living/silicon/robot/Login()
|
||||
..()
|
||||
regenerate_icons()
|
||||
show_laws(0)
|
||||
|
||||
/mob/living/silicon/robot/Login()
|
||||
..()
|
||||
regenerate_icons()
|
||||
show_laws(0)
|
||||
|
||||
@@ -184,8 +184,7 @@
|
||||
adjustBruteLoss(30)
|
||||
|
||||
/mob/living/silicon/robot/bullet_act(obj/item/projectile/P, def_zone)
|
||||
..()
|
||||
. = ..()
|
||||
updatehealth()
|
||||
if(prob(75) && P.damage > 0)
|
||||
spark_system.start()
|
||||
return 2
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,54 +1,54 @@
|
||||
/mob/living/proc/robot_talk(message)
|
||||
log_talk(message, LOG_SAY)
|
||||
var/desig = "Default Cyborg" //ezmode for taters
|
||||
if(issilicon(src))
|
||||
var/mob/living/silicon/S = src
|
||||
desig = trim_left(S.designation + " " + S.job)
|
||||
var/message_a = say_quote(message)
|
||||
var/rendered = "Robotic Talk, <span class='name'>[name]</span> <span class='message'>[message_a]</span>"
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.binarycheck())
|
||||
if(isAI(M))
|
||||
var/renderedAI = "<span class='binarysay'>Robotic Talk, <a href='?src=[REF(M)];track=[html_encode(name)]'><span class='name'>[name] ([desig])</span></a> <span class='message'>[message_a]</span></span>"
|
||||
to_chat(M, renderedAI)
|
||||
else
|
||||
to_chat(M, "<span class='binarysay'>[rendered]</span>")
|
||||
if(isobserver(M))
|
||||
var/following = src
|
||||
// If the AI talks on binary chat, we still want to follow
|
||||
// it's camera eye, like if it talked on the radio
|
||||
if(isAI(src))
|
||||
var/mob/living/silicon/ai/ai = src
|
||||
following = ai.eyeobj
|
||||
var/link = FOLLOW_LINK(M, following)
|
||||
to_chat(M, "<span class='binarysay'>[link] [rendered]</span>")
|
||||
|
||||
/mob/living/silicon/binarycheck()
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/lingcheck()
|
||||
return 0 //Borged or AI'd lings can't speak on the ling channel.
|
||||
|
||||
/mob/living/silicon/radio(message, message_mode, list/spans, language)
|
||||
. = ..()
|
||||
if(. != 0)
|
||||
return .
|
||||
|
||||
if(message_mode == "robot")
|
||||
if (radio)
|
||||
radio.talk_into(src, message, , spans, language)
|
||||
return REDUCE_RANGE
|
||||
|
||||
else if(message_mode in GLOB.radiochannels)
|
||||
if(radio)
|
||||
radio.talk_into(src, message, message_mode, spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
return 0
|
||||
|
||||
/mob/living/silicon/get_message_mode(message)
|
||||
. = ..()
|
||||
if(..() == MODE_HEADSET)
|
||||
return MODE_ROBOT
|
||||
else
|
||||
return .
|
||||
/mob/living/proc/robot_talk(message)
|
||||
log_talk(message, LOG_SAY)
|
||||
var/desig = "Default Cyborg" //ezmode for taters
|
||||
if(issilicon(src))
|
||||
var/mob/living/silicon/S = src
|
||||
desig = trim_left(S.designation + " " + S.job)
|
||||
var/message_a = say_quote(message)
|
||||
var/rendered = "Robotic Talk, <span class='name'>[name]</span> <span class='message'>[message_a]</span>"
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.binarycheck())
|
||||
if(isAI(M))
|
||||
var/renderedAI = "<span class='binarysay'>Robotic Talk, <a href='?src=[REF(M)];track=[html_encode(name)]'><span class='name'>[name] ([desig])</span></a> <span class='message'>[message_a]</span></span>"
|
||||
to_chat(M, renderedAI)
|
||||
else
|
||||
to_chat(M, "<span class='binarysay'>[rendered]</span>")
|
||||
if(isobserver(M))
|
||||
var/following = src
|
||||
// If the AI talks on binary chat, we still want to follow
|
||||
// it's camera eye, like if it talked on the radio
|
||||
if(isAI(src))
|
||||
var/mob/living/silicon/ai/ai = src
|
||||
following = ai.eyeobj
|
||||
var/link = FOLLOW_LINK(M, following)
|
||||
to_chat(M, "<span class='binarysay'>[link] [rendered]</span>")
|
||||
|
||||
/mob/living/silicon/binarycheck()
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/lingcheck()
|
||||
return 0 //Borged or AI'd lings can't speak on the ling channel.
|
||||
|
||||
/mob/living/silicon/radio(message, message_mode, list/spans, language)
|
||||
. = ..()
|
||||
if(. != 0)
|
||||
return .
|
||||
|
||||
if(message_mode == "robot")
|
||||
if (radio)
|
||||
radio.talk_into(src, message, , spans, language)
|
||||
return REDUCE_RANGE
|
||||
|
||||
else if(message_mode in GLOB.radiochannels)
|
||||
if(radio)
|
||||
radio.talk_into(src, message, message_mode, spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
return 0
|
||||
|
||||
/mob/living/silicon/get_message_mode(message)
|
||||
. = ..()
|
||||
if(..() == MODE_HEADSET)
|
||||
return MODE_ROBOT
|
||||
else
|
||||
return .
|
||||
|
||||
@@ -1,420 +1,420 @@
|
||||
/mob/living/silicon
|
||||
gender = NEUTER
|
||||
has_unlimited_silicon_privilege = 1
|
||||
verb_say = "states"
|
||||
verb_ask = "queries"
|
||||
verb_exclaim = "declares"
|
||||
verb_yell = "alarms"
|
||||
initial_language_holder = /datum/language_holder/synthetic
|
||||
see_in_dark = 8
|
||||
bubble_icon = "machine"
|
||||
weather_immunities = list("ash")
|
||||
possible_a_intents = list(INTENT_HELP, INTENT_HARM)
|
||||
mob_biotypes = list(MOB_ROBOTIC)
|
||||
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
|
||||
speech_span = SPAN_ROBOT
|
||||
flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1
|
||||
no_vore = TRUE
|
||||
|
||||
var/datum/ai_laws/laws = null//Now... THEY ALL CAN ALL HAVE LAWS
|
||||
var/last_lawchange_announce = 0
|
||||
var/list/alarms_to_show = list()
|
||||
var/list/alarms_to_clear = list()
|
||||
var/designation = ""
|
||||
var/radiomod = "" //Radio character used before state laws/arrivals announce to allow department transmissions, default, or none at all.
|
||||
var/obj/item/camera/siliconcam/aicamera = null //photography
|
||||
hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_TRACK_HUD)
|
||||
|
||||
var/obj/item/radio/borg/radio = null //AIs dont use this but this is at the silicon level to advoid copypasta in say()
|
||||
|
||||
var/list/alarm_types_show = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
|
||||
var/list/alarm_types_clear = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
|
||||
|
||||
var/lawcheck[1]
|
||||
var/ioncheck[1]
|
||||
var/hackedcheck[1]
|
||||
var/devillawcheck[5]
|
||||
|
||||
var/sensors_on = 0
|
||||
var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use
|
||||
var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use
|
||||
var/d_hud = DATA_HUD_DIAGNOSTIC_BASIC //Determines the diag hud to use
|
||||
|
||||
var/law_change_counter = 0
|
||||
var/obj/machinery/camera/builtInCamera = null
|
||||
var/updating = FALSE //portable camera camerachunk update
|
||||
|
||||
var/hack_software = FALSE //Will be able to use hacking actions
|
||||
var/interaction_range = 7 //wireless control range
|
||||
|
||||
/mob/living/silicon/Initialize()
|
||||
. = ..()
|
||||
GLOB.silicon_mobs += src
|
||||
faction += "silicon"
|
||||
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
|
||||
diag_hud.add_to_hud(src)
|
||||
diag_hud_set_status()
|
||||
diag_hud_set_health()
|
||||
|
||||
/mob/living/silicon/med_hud_set_health()
|
||||
return //we use a different hud
|
||||
|
||||
/mob/living/silicon/med_hud_set_status()
|
||||
return //we use a different hud
|
||||
|
||||
/mob/living/silicon/Destroy()
|
||||
radio = null
|
||||
aicamera = null
|
||||
QDEL_NULL(builtInCamera)
|
||||
GLOB.silicon_mobs -= src
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/contents_explosion(severity, target)
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/cancelAlarm()
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/triggerAlarm()
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/queueAlarm(message, type, incoming = 1)
|
||||
var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0)
|
||||
if(incoming)
|
||||
alarms_to_show += message
|
||||
alarm_types_show[type] += 1
|
||||
else
|
||||
alarms_to_clear += message
|
||||
alarm_types_clear[type] += 1
|
||||
|
||||
if(!in_cooldown)
|
||||
spawn(3 * 10) // 3 seconds
|
||||
|
||||
if(alarms_to_show.len < 5)
|
||||
for(var/msg in alarms_to_show)
|
||||
to_chat(src, msg)
|
||||
else if(alarms_to_show.len)
|
||||
|
||||
var/msg = "--- "
|
||||
|
||||
if(alarm_types_show["Burglar"])
|
||||
msg += "BURGLAR: [alarm_types_show["Burglar"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Motion"])
|
||||
msg += "MOTION: [alarm_types_show["Motion"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Fire"])
|
||||
msg += "FIRE: [alarm_types_show["Fire"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Atmosphere"])
|
||||
msg += "ATMOSPHERE: [alarm_types_show["Atmosphere"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Power"])
|
||||
msg += "POWER: [alarm_types_show["Power"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Camera"])
|
||||
msg += "CAMERA: [alarm_types_show["Camera"]] alarms detected. - "
|
||||
|
||||
msg += "<A href=?src=[REF(src)];showalerts=1'>\[Show Alerts\]</a>"
|
||||
to_chat(src, msg)
|
||||
|
||||
if(alarms_to_clear.len < 3)
|
||||
for(var/msg in alarms_to_clear)
|
||||
to_chat(src, msg)
|
||||
|
||||
else if(alarms_to_clear.len)
|
||||
var/msg = "--- "
|
||||
|
||||
if(alarm_types_clear["Motion"])
|
||||
msg += "MOTION: [alarm_types_clear["Motion"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Fire"])
|
||||
msg += "FIRE: [alarm_types_clear["Fire"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Atmosphere"])
|
||||
msg += "ATMOSPHERE: [alarm_types_clear["Atmosphere"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Power"])
|
||||
msg += "POWER: [alarm_types_clear["Power"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_show["Camera"])
|
||||
msg += "CAMERA: [alarm_types_clear["Camera"]] alarms cleared. - "
|
||||
|
||||
msg += "<A href=?src=[REF(src)];showalerts=1'>\[Show Alerts\]</a>"
|
||||
to_chat(src, msg)
|
||||
|
||||
|
||||
alarms_to_show = list()
|
||||
alarms_to_clear = list()
|
||||
for(var/key in alarm_types_show)
|
||||
alarm_types_show[key] = 0
|
||||
for(var/key in alarm_types_clear)
|
||||
alarm_types_clear[key] = 0
|
||||
|
||||
/mob/living/silicon/can_inject(mob/user, error_msg)
|
||||
if(error_msg)
|
||||
to_chat(user, "<span class='alert'>[p_their(TRUE)] outer shell is too tough.</span>")
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/IsAdvancedToolUser()
|
||||
return TRUE
|
||||
|
||||
/proc/islinked(mob/living/silicon/robot/bot, mob/living/silicon/ai/ai)
|
||||
if(!istype(bot) || !istype(ai))
|
||||
return FALSE
|
||||
if(bot.connected_ai == ai)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/Topic(href, href_list)
|
||||
if (href_list["lawc"]) // Toggling whether or not a law gets stated by the State Laws verb --NeoFite
|
||||
var/L = text2num(href_list["lawc"])
|
||||
switch(lawcheck[L+1])
|
||||
if ("Yes")
|
||||
lawcheck[L+1] = "No"
|
||||
if ("No")
|
||||
lawcheck[L+1] = "Yes"
|
||||
checklaws()
|
||||
|
||||
if (href_list["lawi"]) // Toggling whether or not a law gets stated by the State Laws verb --NeoFite
|
||||
var/L = text2num(href_list["lawi"])
|
||||
switch(ioncheck[L])
|
||||
if ("Yes")
|
||||
ioncheck[L] = "No"
|
||||
if ("No")
|
||||
ioncheck[L] = "Yes"
|
||||
checklaws()
|
||||
|
||||
if (href_list["lawh"])
|
||||
var/L = text2num(href_list["lawh"])
|
||||
switch(hackedcheck[L])
|
||||
if ("Yes")
|
||||
hackedcheck[L] = "No"
|
||||
if ("No")
|
||||
hackedcheck[L] = "Yes"
|
||||
checklaws()
|
||||
|
||||
if (href_list["lawdevil"]) // Toggling whether or not a law gets stated by the State Laws verb --NeoFite
|
||||
var/L = text2num(href_list["lawdevil"])
|
||||
switch(devillawcheck[L])
|
||||
if ("Yes")
|
||||
devillawcheck[L] = "No"
|
||||
if ("No")
|
||||
devillawcheck[L] = "Yes"
|
||||
checklaws()
|
||||
|
||||
|
||||
if (href_list["laws"]) // With how my law selection code works, I changed statelaws from a verb to a proc, and call it through my law selection panel. --NeoFite
|
||||
statelaws()
|
||||
|
||||
return
|
||||
|
||||
|
||||
/mob/living/silicon/proc/statelaws(force = 0)
|
||||
|
||||
//"radiomod" is inserted before a hardcoded message to change if and how it is handled by an internal radio.
|
||||
say("[radiomod] Current Active Laws:")
|
||||
//laws_sanity_check()
|
||||
//laws.show_laws(world)
|
||||
var/number = 1
|
||||
sleep(10)
|
||||
|
||||
if (laws.devillaws && laws.devillaws.len)
|
||||
for(var/index = 1, index <= laws.devillaws.len, index++)
|
||||
if (force || devillawcheck[index] == "Yes")
|
||||
say("[radiomod] 666. [laws.devillaws[index]]")
|
||||
sleep(10)
|
||||
|
||||
|
||||
if (laws.zeroth)
|
||||
if (force || lawcheck[1] == "Yes")
|
||||
say("[radiomod] 0. [laws.zeroth]")
|
||||
sleep(10)
|
||||
|
||||
for (var/index = 1, index <= laws.hacked.len, index++)
|
||||
var/law = laws.hacked[index]
|
||||
var/num = ionnum()
|
||||
if (length(law) > 0)
|
||||
if (force || hackedcheck[index] == "Yes")
|
||||
say("[radiomod] [num]. [law]")
|
||||
sleep(10)
|
||||
|
||||
for (var/index = 1, index <= laws.ion.len, index++)
|
||||
var/law = laws.ion[index]
|
||||
var/num = ionnum()
|
||||
if (length(law) > 0)
|
||||
if (force || ioncheck[index] == "Yes")
|
||||
say("[radiomod] [num]. [law]")
|
||||
sleep(10)
|
||||
|
||||
for (var/index = 1, index <= laws.inherent.len, index++)
|
||||
var/law = laws.inherent[index]
|
||||
|
||||
if (length(law) > 0)
|
||||
if (force || lawcheck[index+1] == "Yes")
|
||||
say("[radiomod] [number]. [law]")
|
||||
number++
|
||||
sleep(10)
|
||||
|
||||
for (var/index = 1, index <= laws.supplied.len, index++)
|
||||
var/law = laws.supplied[index]
|
||||
|
||||
if (length(law) > 0)
|
||||
if(lawcheck.len >= number+1)
|
||||
if (force || lawcheck[number+1] == "Yes")
|
||||
say("[radiomod] [number]. [law]")
|
||||
number++
|
||||
sleep(10)
|
||||
|
||||
|
||||
/mob/living/silicon/proc/checklaws() //Gives you a link-driven interface for deciding what laws the statelaws() proc will share with the crew. --NeoFite
|
||||
|
||||
var/list = "<b>Which laws do you want to include when stating them for the crew?</b><br><br>"
|
||||
|
||||
if (laws.devillaws && laws.devillaws.len)
|
||||
for(var/index = 1, index <= laws.devillaws.len, index++)
|
||||
if (!devillawcheck[index])
|
||||
devillawcheck[index] = "No"
|
||||
list += {"<A href='byond://?src=[REF(src)];lawdevil=[index]'>[devillawcheck[index]] 666:</A> <font color='#cc5500'>[laws.devillaws[index]]</font><BR>"}
|
||||
|
||||
if (laws.zeroth)
|
||||
if (!lawcheck[1])
|
||||
lawcheck[1] = "No" //Given Law 0's usual nature, it defaults to NOT getting reported. --NeoFite
|
||||
list += {"<A href='byond://?src=[REF(src)];lawc=0'>[lawcheck[1]] 0:</A> <font color='#ff0000'><b>[laws.zeroth]</b></font><BR>"}
|
||||
|
||||
for (var/index = 1, index <= laws.hacked.len, index++)
|
||||
var/law = laws.hacked[index]
|
||||
if (length(law) > 0)
|
||||
if (!hackedcheck[index])
|
||||
hackedcheck[index] = "No"
|
||||
list += {"<A href='byond://?src=[REF(src)];lawh=[index]'>[hackedcheck[index]] [ionnum()]:</A> <font color='#660000'>[law]</font><BR>"}
|
||||
hackedcheck.len += 1
|
||||
|
||||
for (var/index = 1, index <= laws.ion.len, index++)
|
||||
var/law = laws.ion[index]
|
||||
|
||||
if (length(law) > 0)
|
||||
if (!ioncheck[index])
|
||||
ioncheck[index] = "Yes"
|
||||
list += {"<A href='byond://?src=[REF(src)];lawi=[index]'>[ioncheck[index]] [ionnum()]:</A> <font color='#547DFE'>[law]</font><BR>"}
|
||||
ioncheck.len += 1
|
||||
|
||||
var/number = 1
|
||||
for (var/index = 1, index <= laws.inherent.len, index++)
|
||||
var/law = laws.inherent[index]
|
||||
|
||||
if (length(law) > 0)
|
||||
lawcheck.len += 1
|
||||
|
||||
if (!lawcheck[number+1])
|
||||
lawcheck[number+1] = "Yes"
|
||||
list += {"<A href='byond://?src=[REF(src)];lawc=[number]'>[lawcheck[number+1]] [number]:</A> [law]<BR>"}
|
||||
number++
|
||||
|
||||
for (var/index = 1, index <= laws.supplied.len, index++)
|
||||
var/law = laws.supplied[index]
|
||||
if (length(law) > 0)
|
||||
lawcheck.len += 1
|
||||
if (!lawcheck[number+1])
|
||||
lawcheck[number+1] = "Yes"
|
||||
list += {"<A href='byond://?src=[REF(src)];lawc=[number]'>[lawcheck[number+1]] [number]:</A> <font color='#990099'>[law]</font><BR>"}
|
||||
number++
|
||||
list += {"<br><br><A href='byond://?src=[REF(src)];laws=1'>State Laws</A>"}
|
||||
|
||||
usr << browse(list, "window=laws")
|
||||
|
||||
/mob/living/silicon/proc/set_autosay() //For allowing the AI and borgs to set the radio behavior of auto announcements (state laws, arrivals).
|
||||
if(!radio)
|
||||
to_chat(src, "Radio not detected.")
|
||||
return
|
||||
|
||||
//Ask the user to pick a channel from what it has available.
|
||||
var/Autochan = input("Select a channel:") as null|anything in list("Default","None") + radio.channels
|
||||
|
||||
if(!Autochan)
|
||||
return
|
||||
if(Autochan == "Default") //Autospeak on whatever frequency to which the radio is set, usually Common.
|
||||
radiomod = ";"
|
||||
Autochan += " ([radio.frequency])"
|
||||
else if(Autochan == "None") //Prevents use of the radio for automatic annoucements.
|
||||
radiomod = ""
|
||||
else //For department channels, if any, given by the internal radio.
|
||||
for(var/key in GLOB.department_radio_keys)
|
||||
if(GLOB.department_radio_keys[key] == Autochan)
|
||||
radiomod = ":" + key
|
||||
break
|
||||
|
||||
to_chat(src, "<span class='notice'>Automatic announcements [Autochan == "None" ? "will not use the radio." : "set to [Autochan]."]</span>")
|
||||
|
||||
/mob/living/silicon/put_in_hand_check() // This check is for borgs being able to receive items, not put them in others' hands.
|
||||
return 0
|
||||
|
||||
// The src mob is trying to place an item on someone
|
||||
// But the src mob is a silicon!! Disable.
|
||||
/mob/living/silicon/stripPanelEquip(obj/item/what, mob/who, slot)
|
||||
return 0
|
||||
|
||||
|
||||
/mob/living/silicon/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) //Secbots won't hunt silicon units
|
||||
return -10
|
||||
|
||||
/mob/living/silicon/proc/remove_sensors()
|
||||
var/datum/atom_hud/secsensor = GLOB.huds[sec_hud]
|
||||
var/datum/atom_hud/medsensor = GLOB.huds[med_hud]
|
||||
var/datum/atom_hud/diagsensor = GLOB.huds[d_hud]
|
||||
secsensor.remove_hud_from(src)
|
||||
medsensor.remove_hud_from(src)
|
||||
diagsensor.remove_hud_from(src)
|
||||
|
||||
/mob/living/silicon/proc/add_sensors()
|
||||
var/datum/atom_hud/secsensor = GLOB.huds[sec_hud]
|
||||
var/datum/atom_hud/medsensor = GLOB.huds[med_hud]
|
||||
var/datum/atom_hud/diagsensor = GLOB.huds[d_hud]
|
||||
secsensor.add_hud_to(src)
|
||||
medsensor.add_hud_to(src)
|
||||
diagsensor.add_hud_to(src)
|
||||
|
||||
/mob/living/silicon/proc/toggle_sensors()
|
||||
if(incapacitated())
|
||||
return
|
||||
sensors_on = !sensors_on
|
||||
if (!sensors_on)
|
||||
to_chat(src, "Sensor overlay deactivated.")
|
||||
remove_sensors()
|
||||
return
|
||||
add_sensors()
|
||||
to_chat(src, "Sensor overlay activated.")
|
||||
|
||||
/mob/living/silicon/proc/GetPhoto(mob/user)
|
||||
if (aicamera)
|
||||
return aicamera.selectpicture(user)
|
||||
|
||||
/mob/living/silicon/proc/ai_roster()
|
||||
var/dat = "<html><head><title>Crew Roster</title></head><body><b>Crew Roster:</b><br><br>"
|
||||
|
||||
dat += GLOB.data_core.get_manifest()
|
||||
dat += "</body></html>"
|
||||
|
||||
src << browse(dat, "window=airoster")
|
||||
onclose(src, "airoster")
|
||||
|
||||
/mob/living/silicon/update_transform()
|
||||
var/matrix/ntransform = matrix(transform) //aka transform.Copy()
|
||||
var/changed = 0
|
||||
if(resize != RESIZE_DEFAULT_SIZE)
|
||||
changed++
|
||||
ntransform.Scale(resize)
|
||||
resize = RESIZE_DEFAULT_SIZE
|
||||
|
||||
if(changed)
|
||||
animate(src, transform = ntransform, time = 2,easing = EASE_IN|EASE_OUT)
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/is_literate()
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/get_inactive_held_item()
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/handle_high_gravity(gravity)
|
||||
return
|
||||
/mob/living/silicon
|
||||
gender = NEUTER
|
||||
has_unlimited_silicon_privilege = 1
|
||||
verb_say = "states"
|
||||
verb_ask = "queries"
|
||||
verb_exclaim = "declares"
|
||||
verb_yell = "alarms"
|
||||
initial_language_holder = /datum/language_holder/synthetic
|
||||
see_in_dark = 8
|
||||
bubble_icon = "machine"
|
||||
weather_immunities = list("ash")
|
||||
possible_a_intents = list(INTENT_HELP, INTENT_HARM)
|
||||
mob_biotypes = list(MOB_ROBOTIC)
|
||||
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
|
||||
speech_span = SPAN_ROBOT
|
||||
flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1
|
||||
no_vore = TRUE
|
||||
|
||||
var/datum/ai_laws/laws = null//Now... THEY ALL CAN ALL HAVE LAWS
|
||||
var/last_lawchange_announce = 0
|
||||
var/list/alarms_to_show = list()
|
||||
var/list/alarms_to_clear = list()
|
||||
var/designation = ""
|
||||
var/radiomod = "" //Radio character used before state laws/arrivals announce to allow department transmissions, default, or none at all.
|
||||
var/obj/item/camera/siliconcam/aicamera = null //photography
|
||||
hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_TRACK_HUD)
|
||||
|
||||
var/obj/item/radio/borg/radio = null //AIs dont use this but this is at the silicon level to advoid copypasta in say()
|
||||
|
||||
var/list/alarm_types_show = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
|
||||
var/list/alarm_types_clear = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
|
||||
|
||||
var/lawcheck[1]
|
||||
var/ioncheck[1]
|
||||
var/hackedcheck[1]
|
||||
var/devillawcheck[5]
|
||||
|
||||
var/sensors_on = 0
|
||||
var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use
|
||||
var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use
|
||||
var/d_hud = DATA_HUD_DIAGNOSTIC_BASIC //Determines the diag hud to use
|
||||
|
||||
var/law_change_counter = 0
|
||||
var/obj/machinery/camera/builtInCamera = null
|
||||
var/updating = FALSE //portable camera camerachunk update
|
||||
|
||||
var/hack_software = FALSE //Will be able to use hacking actions
|
||||
var/interaction_range = 7 //wireless control range
|
||||
|
||||
/mob/living/silicon/Initialize()
|
||||
. = ..()
|
||||
GLOB.silicon_mobs += src
|
||||
faction += "silicon"
|
||||
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
|
||||
diag_hud.add_to_hud(src)
|
||||
diag_hud_set_status()
|
||||
diag_hud_set_health()
|
||||
|
||||
/mob/living/silicon/med_hud_set_health()
|
||||
return //we use a different hud
|
||||
|
||||
/mob/living/silicon/med_hud_set_status()
|
||||
return //we use a different hud
|
||||
|
||||
/mob/living/silicon/Destroy()
|
||||
radio = null
|
||||
aicamera = null
|
||||
QDEL_NULL(builtInCamera)
|
||||
GLOB.silicon_mobs -= src
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/contents_explosion(severity, target)
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/cancelAlarm()
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/triggerAlarm()
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/queueAlarm(message, type, incoming = 1)
|
||||
var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0)
|
||||
if(incoming)
|
||||
alarms_to_show += message
|
||||
alarm_types_show[type] += 1
|
||||
else
|
||||
alarms_to_clear += message
|
||||
alarm_types_clear[type] += 1
|
||||
|
||||
if(!in_cooldown)
|
||||
spawn(3 * 10) // 3 seconds
|
||||
|
||||
if(alarms_to_show.len < 5)
|
||||
for(var/msg in alarms_to_show)
|
||||
to_chat(src, msg)
|
||||
else if(alarms_to_show.len)
|
||||
|
||||
var/msg = "--- "
|
||||
|
||||
if(alarm_types_show["Burglar"])
|
||||
msg += "BURGLAR: [alarm_types_show["Burglar"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Motion"])
|
||||
msg += "MOTION: [alarm_types_show["Motion"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Fire"])
|
||||
msg += "FIRE: [alarm_types_show["Fire"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Atmosphere"])
|
||||
msg += "ATMOSPHERE: [alarm_types_show["Atmosphere"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Power"])
|
||||
msg += "POWER: [alarm_types_show["Power"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Camera"])
|
||||
msg += "CAMERA: [alarm_types_show["Camera"]] alarms detected. - "
|
||||
|
||||
msg += "<A href=?src=[REF(src)];showalerts=1'>\[Show Alerts\]</a>"
|
||||
to_chat(src, msg)
|
||||
|
||||
if(alarms_to_clear.len < 3)
|
||||
for(var/msg in alarms_to_clear)
|
||||
to_chat(src, msg)
|
||||
|
||||
else if(alarms_to_clear.len)
|
||||
var/msg = "--- "
|
||||
|
||||
if(alarm_types_clear["Motion"])
|
||||
msg += "MOTION: [alarm_types_clear["Motion"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Fire"])
|
||||
msg += "FIRE: [alarm_types_clear["Fire"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Atmosphere"])
|
||||
msg += "ATMOSPHERE: [alarm_types_clear["Atmosphere"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Power"])
|
||||
msg += "POWER: [alarm_types_clear["Power"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_show["Camera"])
|
||||
msg += "CAMERA: [alarm_types_clear["Camera"]] alarms cleared. - "
|
||||
|
||||
msg += "<A href=?src=[REF(src)];showalerts=1'>\[Show Alerts\]</a>"
|
||||
to_chat(src, msg)
|
||||
|
||||
|
||||
alarms_to_show = list()
|
||||
alarms_to_clear = list()
|
||||
for(var/key in alarm_types_show)
|
||||
alarm_types_show[key] = 0
|
||||
for(var/key in alarm_types_clear)
|
||||
alarm_types_clear[key] = 0
|
||||
|
||||
/mob/living/silicon/can_inject(mob/user, error_msg)
|
||||
if(error_msg)
|
||||
to_chat(user, "<span class='alert'>[p_their(TRUE)] outer shell is too tough.</span>")
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/IsAdvancedToolUser()
|
||||
return TRUE
|
||||
|
||||
/proc/islinked(mob/living/silicon/robot/bot, mob/living/silicon/ai/ai)
|
||||
if(!istype(bot) || !istype(ai))
|
||||
return FALSE
|
||||
if(bot.connected_ai == ai)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/Topic(href, href_list)
|
||||
if (href_list["lawc"]) // Toggling whether or not a law gets stated by the State Laws verb --NeoFite
|
||||
var/L = text2num(href_list["lawc"])
|
||||
switch(lawcheck[L+1])
|
||||
if ("Yes")
|
||||
lawcheck[L+1] = "No"
|
||||
if ("No")
|
||||
lawcheck[L+1] = "Yes"
|
||||
checklaws()
|
||||
|
||||
if (href_list["lawi"]) // Toggling whether or not a law gets stated by the State Laws verb --NeoFite
|
||||
var/L = text2num(href_list["lawi"])
|
||||
switch(ioncheck[L])
|
||||
if ("Yes")
|
||||
ioncheck[L] = "No"
|
||||
if ("No")
|
||||
ioncheck[L] = "Yes"
|
||||
checklaws()
|
||||
|
||||
if (href_list["lawh"])
|
||||
var/L = text2num(href_list["lawh"])
|
||||
switch(hackedcheck[L])
|
||||
if ("Yes")
|
||||
hackedcheck[L] = "No"
|
||||
if ("No")
|
||||
hackedcheck[L] = "Yes"
|
||||
checklaws()
|
||||
|
||||
if (href_list["lawdevil"]) // Toggling whether or not a law gets stated by the State Laws verb --NeoFite
|
||||
var/L = text2num(href_list["lawdevil"])
|
||||
switch(devillawcheck[L])
|
||||
if ("Yes")
|
||||
devillawcheck[L] = "No"
|
||||
if ("No")
|
||||
devillawcheck[L] = "Yes"
|
||||
checklaws()
|
||||
|
||||
|
||||
if (href_list["laws"]) // With how my law selection code works, I changed statelaws from a verb to a proc, and call it through my law selection panel. --NeoFite
|
||||
statelaws()
|
||||
|
||||
return
|
||||
|
||||
|
||||
/mob/living/silicon/proc/statelaws(force = 0)
|
||||
|
||||
//"radiomod" is inserted before a hardcoded message to change if and how it is handled by an internal radio.
|
||||
say("[radiomod] Current Active Laws:")
|
||||
//laws_sanity_check()
|
||||
//laws.show_laws(world)
|
||||
var/number = 1
|
||||
sleep(10)
|
||||
|
||||
if (laws.devillaws && laws.devillaws.len)
|
||||
for(var/index = 1, index <= laws.devillaws.len, index++)
|
||||
if (force || devillawcheck[index] == "Yes")
|
||||
say("[radiomod] 666. [laws.devillaws[index]]")
|
||||
sleep(10)
|
||||
|
||||
|
||||
if (laws.zeroth)
|
||||
if (force || lawcheck[1] == "Yes")
|
||||
say("[radiomod] 0. [laws.zeroth]")
|
||||
sleep(10)
|
||||
|
||||
for (var/index = 1, index <= laws.hacked.len, index++)
|
||||
var/law = laws.hacked[index]
|
||||
var/num = ionnum()
|
||||
if (length(law) > 0)
|
||||
if (force || hackedcheck[index] == "Yes")
|
||||
say("[radiomod] [num]. [law]")
|
||||
sleep(10)
|
||||
|
||||
for (var/index = 1, index <= laws.ion.len, index++)
|
||||
var/law = laws.ion[index]
|
||||
var/num = ionnum()
|
||||
if (length(law) > 0)
|
||||
if (force || ioncheck[index] == "Yes")
|
||||
say("[radiomod] [num]. [law]")
|
||||
sleep(10)
|
||||
|
||||
for (var/index = 1, index <= laws.inherent.len, index++)
|
||||
var/law = laws.inherent[index]
|
||||
|
||||
if (length(law) > 0)
|
||||
if (force || lawcheck[index+1] == "Yes")
|
||||
say("[radiomod] [number]. [law]")
|
||||
number++
|
||||
sleep(10)
|
||||
|
||||
for (var/index = 1, index <= laws.supplied.len, index++)
|
||||
var/law = laws.supplied[index]
|
||||
|
||||
if (length(law) > 0)
|
||||
if(lawcheck.len >= number+1)
|
||||
if (force || lawcheck[number+1] == "Yes")
|
||||
say("[radiomod] [number]. [law]")
|
||||
number++
|
||||
sleep(10)
|
||||
|
||||
|
||||
/mob/living/silicon/proc/checklaws() //Gives you a link-driven interface for deciding what laws the statelaws() proc will share with the crew. --NeoFite
|
||||
|
||||
var/list = "<b>Which laws do you want to include when stating them for the crew?</b><br><br>"
|
||||
|
||||
if (laws.devillaws && laws.devillaws.len)
|
||||
for(var/index = 1, index <= laws.devillaws.len, index++)
|
||||
if (!devillawcheck[index])
|
||||
devillawcheck[index] = "No"
|
||||
list += {"<A href='byond://?src=[REF(src)];lawdevil=[index]'>[devillawcheck[index]] 666:</A> <font color='#cc5500'>[laws.devillaws[index]]</font><BR>"}
|
||||
|
||||
if (laws.zeroth)
|
||||
if (!lawcheck[1])
|
||||
lawcheck[1] = "No" //Given Law 0's usual nature, it defaults to NOT getting reported. --NeoFite
|
||||
list += {"<A href='byond://?src=[REF(src)];lawc=0'>[lawcheck[1]] 0:</A> <font color='#ff0000'><b>[laws.zeroth]</b></font><BR>"}
|
||||
|
||||
for (var/index = 1, index <= laws.hacked.len, index++)
|
||||
var/law = laws.hacked[index]
|
||||
if (length(law) > 0)
|
||||
if (!hackedcheck[index])
|
||||
hackedcheck[index] = "No"
|
||||
list += {"<A href='byond://?src=[REF(src)];lawh=[index]'>[hackedcheck[index]] [ionnum()]:</A> <font color='#660000'>[law]</font><BR>"}
|
||||
hackedcheck.len += 1
|
||||
|
||||
for (var/index = 1, index <= laws.ion.len, index++)
|
||||
var/law = laws.ion[index]
|
||||
|
||||
if (length(law) > 0)
|
||||
if (!ioncheck[index])
|
||||
ioncheck[index] = "Yes"
|
||||
list += {"<A href='byond://?src=[REF(src)];lawi=[index]'>[ioncheck[index]] [ionnum()]:</A> <font color='#547DFE'>[law]</font><BR>"}
|
||||
ioncheck.len += 1
|
||||
|
||||
var/number = 1
|
||||
for (var/index = 1, index <= laws.inherent.len, index++)
|
||||
var/law = laws.inherent[index]
|
||||
|
||||
if (length(law) > 0)
|
||||
lawcheck.len += 1
|
||||
|
||||
if (!lawcheck[number+1])
|
||||
lawcheck[number+1] = "Yes"
|
||||
list += {"<A href='byond://?src=[REF(src)];lawc=[number]'>[lawcheck[number+1]] [number]:</A> [law]<BR>"}
|
||||
number++
|
||||
|
||||
for (var/index = 1, index <= laws.supplied.len, index++)
|
||||
var/law = laws.supplied[index]
|
||||
if (length(law) > 0)
|
||||
lawcheck.len += 1
|
||||
if (!lawcheck[number+1])
|
||||
lawcheck[number+1] = "Yes"
|
||||
list += {"<A href='byond://?src=[REF(src)];lawc=[number]'>[lawcheck[number+1]] [number]:</A> <font color='#990099'>[law]</font><BR>"}
|
||||
number++
|
||||
list += {"<br><br><A href='byond://?src=[REF(src)];laws=1'>State Laws</A>"}
|
||||
|
||||
usr << browse(list, "window=laws")
|
||||
|
||||
/mob/living/silicon/proc/set_autosay() //For allowing the AI and borgs to set the radio behavior of auto announcements (state laws, arrivals).
|
||||
if(!radio)
|
||||
to_chat(src, "Radio not detected.")
|
||||
return
|
||||
|
||||
//Ask the user to pick a channel from what it has available.
|
||||
var/Autochan = input("Select a channel:") as null|anything in list("Default","None") + radio.channels
|
||||
|
||||
if(!Autochan)
|
||||
return
|
||||
if(Autochan == "Default") //Autospeak on whatever frequency to which the radio is set, usually Common.
|
||||
radiomod = ";"
|
||||
Autochan += " ([radio.frequency])"
|
||||
else if(Autochan == "None") //Prevents use of the radio for automatic annoucements.
|
||||
radiomod = ""
|
||||
else //For department channels, if any, given by the internal radio.
|
||||
for(var/key in GLOB.department_radio_keys)
|
||||
if(GLOB.department_radio_keys[key] == Autochan)
|
||||
radiomod = ":" + key
|
||||
break
|
||||
|
||||
to_chat(src, "<span class='notice'>Automatic announcements [Autochan == "None" ? "will not use the radio." : "set to [Autochan]."]</span>")
|
||||
|
||||
/mob/living/silicon/put_in_hand_check() // This check is for borgs being able to receive items, not put them in others' hands.
|
||||
return 0
|
||||
|
||||
// The src mob is trying to place an item on someone
|
||||
// But the src mob is a silicon!! Disable.
|
||||
/mob/living/silicon/stripPanelEquip(obj/item/what, mob/who, slot)
|
||||
return 0
|
||||
|
||||
|
||||
/mob/living/silicon/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) //Secbots won't hunt silicon units
|
||||
return -10
|
||||
|
||||
/mob/living/silicon/proc/remove_sensors()
|
||||
var/datum/atom_hud/secsensor = GLOB.huds[sec_hud]
|
||||
var/datum/atom_hud/medsensor = GLOB.huds[med_hud]
|
||||
var/datum/atom_hud/diagsensor = GLOB.huds[d_hud]
|
||||
secsensor.remove_hud_from(src)
|
||||
medsensor.remove_hud_from(src)
|
||||
diagsensor.remove_hud_from(src)
|
||||
|
||||
/mob/living/silicon/proc/add_sensors()
|
||||
var/datum/atom_hud/secsensor = GLOB.huds[sec_hud]
|
||||
var/datum/atom_hud/medsensor = GLOB.huds[med_hud]
|
||||
var/datum/atom_hud/diagsensor = GLOB.huds[d_hud]
|
||||
secsensor.add_hud_to(src)
|
||||
medsensor.add_hud_to(src)
|
||||
diagsensor.add_hud_to(src)
|
||||
|
||||
/mob/living/silicon/proc/toggle_sensors()
|
||||
if(incapacitated())
|
||||
return
|
||||
sensors_on = !sensors_on
|
||||
if (!sensors_on)
|
||||
to_chat(src, "Sensor overlay deactivated.")
|
||||
remove_sensors()
|
||||
return
|
||||
add_sensors()
|
||||
to_chat(src, "Sensor overlay activated.")
|
||||
|
||||
/mob/living/silicon/proc/GetPhoto(mob/user)
|
||||
if (aicamera)
|
||||
return aicamera.selectpicture(user)
|
||||
|
||||
/mob/living/silicon/proc/ai_roster()
|
||||
var/dat = "<html><head><title>Crew Roster</title></head><body><b>Crew Roster:</b><br><br>"
|
||||
|
||||
dat += GLOB.data_core.get_manifest()
|
||||
dat += "</body></html>"
|
||||
|
||||
src << browse(dat, "window=airoster")
|
||||
onclose(src, "airoster")
|
||||
|
||||
/mob/living/silicon/update_transform()
|
||||
var/matrix/ntransform = matrix(transform) //aka transform.Copy()
|
||||
var/changed = 0
|
||||
if(resize != RESIZE_DEFAULT_SIZE)
|
||||
changed++
|
||||
ntransform.Scale(resize)
|
||||
resize = RESIZE_DEFAULT_SIZE
|
||||
|
||||
if(changed)
|
||||
animate(src, transform = ntransform, time = 2,easing = EASE_IN|EASE_OUT)
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/is_literate()
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/get_inactive_held_item()
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/handle_high_gravity(gravity)
|
||||
return
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
unbuckle_mob(M)
|
||||
M.visible_message("<span class='boldwarning'>[M] is knocked off of [src] by the [P]!</span>")
|
||||
P.on_hit(src)
|
||||
return 2
|
||||
return BULLET_ACT_HIT
|
||||
|
||||
/mob/living/silicon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/static)
|
||||
if(affect_silicon)
|
||||
|
||||
Reference in New Issue
Block a user