Camera code update, fix runtime

This commit is contained in:
Markolie
2015-02-10 15:42:47 +01:00
parent 3a4679bce7
commit c507158b8f
20 changed files with 629 additions and 242 deletions
+187 -1
View File
@@ -396,4 +396,190 @@ proc/listclearnulls(list/list)
/proc/find_record(field, value, list/L)
for(var/datum/data/record/R in L)
if(R.fields[field] == value)
return R
return R
/proc/dd_sortedObjectList(var/list/L, var/cache=list())
if(L.len < 2)
return L
var/middle = L.len / 2 + 1 // Copy is first,second-1
return dd_mergeObjectList(dd_sortedObjectList(L.Copy(0,middle), cache), dd_sortedObjectList(L.Copy(middle), cache), cache) //second parameter null = to end of list
/proc/dd_mergeObjectList(var/list/L, var/list/R, var/list/cache)
var/Li=1
var/Ri=1
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
var/LLi = L[Li]
var/RRi = R[Ri]
var/LLiV = cache[LLi]
var/RRiV = cache[RRi]
if(!LLiV)
LLiV = LLi:dd_SortValue()
cache[LLi] = LLiV
if(!RRiV)
RRiV = RRi:dd_SortValue()
cache[RRi] = RRiV
if(LLiV < RRiV)
result += L[Li++]
else
result += R[Ri++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
// Insert an object into a sorted list, preserving sortedness
/proc/dd_insertObjectList(var/list/L, var/O)
var/min = 1
var/max = L.len
var/Oval = O:dd_SortValue()
while(1)
var/mid = min+round((max-min)/2)
if(mid == max)
L.Insert(mid, O)
return
var/Lmid = L[mid]
var/midval = Lmid:dd_SortValue()
if(Oval == midval)
L.Insert(mid, O)
return
else if(Oval < midval)
max = mid
else
min = mid+1
/*
proc/dd_sortedObjectList(list/incoming)
/*
Use binary search to order by dd_SortValue().
This works by going to the half-point of the list, seeing if the node in
question is higher or lower cost, then going halfway up or down the list
and checking again. This is a very fast way to sort an item into a list.
*/
var/list/sorted_list = new()
var/low_index
var/high_index
var/insert_index
var/midway_calc
var/current_index
var/current_item
var/current_item_value
var/current_sort_object_value
var/list/list_bottom
var/current_sort_object
for (current_sort_object in incoming)
low_index = 1
high_index = sorted_list.len
while (low_index <= high_index)
// Figure out the midpoint, rounding up for fractions. (BYOND rounds down, so add 1 if necessary.)
midway_calc = (low_index + high_index) / 2
current_index = round(midway_calc)
if (midway_calc > current_index)
current_index++
current_item = sorted_list[current_index]
current_item_value = current_item:dd_SortValue()
current_sort_object_value = current_sort_object:dd_SortValue()
if (current_sort_object_value < current_item_value)
high_index = current_index - 1
else if (current_sort_object_value > current_item_value)
low_index = current_index + 1
else
// current_sort_object == current_item
low_index = current_index
break
// Insert before low_index.
insert_index = low_index
// Special case adding to end of list.
if (insert_index > sorted_list.len)
sorted_list += current_sort_object
continue
// Because BYOND lists don't support insert, have to do it by:
// 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list.
list_bottom = sorted_list.Copy(insert_index)
sorted_list.Cut(insert_index)
sorted_list += current_sort_object
sorted_list += list_bottom
return sorted_list
*/
proc/dd_sortedtextlist(list/incoming, case_sensitive = 0)
// Returns a new list with the text values sorted.
// Use binary search to order by sortValue.
// This works by going to the half-point of the list, seeing if the node in question is higher or lower cost,
// then going halfway up or down the list and checking again.
// This is a very fast way to sort an item into a list.
var/list/sorted_text = new()
var/low_index
var/high_index
var/insert_index
var/midway_calc
var/current_index
var/current_item
var/list/list_bottom
var/sort_result
var/current_sort_text
for (current_sort_text in incoming)
low_index = 1
high_index = sorted_text.len
while (low_index <= high_index)
// Figure out the midpoint, rounding up for fractions. (BYOND rounds down, so add 1 if necessary.)
midway_calc = (low_index + high_index) / 2
current_index = round(midway_calc)
if (midway_calc > current_index)
current_index++
current_item = sorted_text[current_index]
if (case_sensitive)
sort_result = sorttextEx(current_sort_text, current_item)
else
sort_result = sorttext(current_sort_text, current_item)
switch(sort_result)
if (1)
high_index = current_index - 1 // current_sort_text < current_item
if (-1)
low_index = current_index + 1 // current_sort_text > current_item
if (0)
low_index = current_index // current_sort_text == current_item
break
// Insert before low_index.
insert_index = low_index
// Special case adding to end of list.
if (insert_index > sorted_text.len)
sorted_text += current_sort_text
continue
// Because BYOND lists don't support insert, have to do it by:
// 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list.
list_bottom = sorted_text.Copy(insert_index)
sorted_text.Cut(insert_index)
sorted_text += current_sort_text
sorted_text += list_bottom
return sorted_text
proc/dd_sortedTextList(list/incoming)
var/case_sensitive = 1
return dd_sortedtextlist(incoming, case_sensitive)
datum/proc/dd_SortValue()
return "[src]"
/obj/machinery/dd_SortValue()
return "[sanitize(name)]"
/obj/machinery/camera/dd_SortValue()
return "[c_tag]"
@@ -1,6 +1,6 @@
/datum/visibility_network
var/list/viewpoints = list()
var/cameras_unsorted = 1
// the type of chunk used by this network
var/datum/visibility_chunk/ChunkType = /datum/visibility_chunk
@@ -8,6 +8,12 @@
var/list/chunks = list()
var/ready = 0
/datum/visibility_network/proc/process_sort()
if(cameras_unsorted)
viewpoints = dd_sortedObjectList(viewpoints)
cameras_unsorted = 0
// Creates a chunk key string from x,y,z coordinates
+156 -94
View File
@@ -8,17 +8,20 @@
active_power_usage = 10
layer = 5
var/datum/wires/camera/wires = null // Wires datum
var/list/network = list("SS13")
var/c_tag = null
var/c_tag_order = 999
var/status = 1.0
var/status = 1
anchored = 1.0
var/invuln = null
panel_open = 0 // 0 = Closed / 1 = Open
var/indestructible = 0
var/bugged = 0
var/obj/item/weapon/camera_assembly/assembly = null
var/watcherslist = list()
var/obj/item/device/camera_bug/hasbug = null
var/toughness = 5 //sorta fragile
// WIRES
var/datum/wires/camera/wires = null // Wires datum
//OTHER
@@ -28,15 +31,18 @@
var/light_disabled = 0
var/alarm_on = 0
var/busy = 0
var/indestructible = 0 // If set, prevents aliens from destroying it
var/obj/item/device/camera_bug/hasbug = null
/obj/machinery/camera/New()
wires = new(src)
assembly = new(src)
assembly.state = 4
invalidateCameraCache()
/* // Use this to look for cameras that have the same c_tag.
for(var/obj/machinery/camera/C in cameranet.viewpoints)
for(var/obj/machinery/camera/C in cameranet.cameras)
var/list/tempnetwork = C.network&src.network
if(C != src && C.c_tag == src.c_tag && tempnetwork.len)
world.log << "[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]"
@@ -50,51 +56,48 @@
ASSERT(src.network.len > 0)
..()
/obj/machinery/camera/Del()
if(!alarm_on)
triggerCameraAlarm()
cancelCameraAlarm()
..()
/obj/machinery/camera/emp_act(severity)
if(!isEmpProof())
if(prob(100/severity))
icon_state = "[initial(icon_state)]emp"
var/list/previous_network = network
network = list()
cameranet.removeCamera(src)
invalidateCameraCache()
stat |= EMPED
SetLuminosity(0)
kick_viewers()
triggerCameraAlarm()
update_icon()
spawn(900)
network = previous_network
icon_state = initial(icon_state)
stat &= ~EMPED
cancelCameraAlarm()
if(can_use())
cameranet.addCamera(src)
for(var/mob/O in mob_list)
if(O.client && O.client.eye == src)
O.unset_machine()
O.reset_view(null)
O << "The screen bursts into static."
update_icon()
invalidateCameraCache()
..()
/obj/machinery/camera/bullet_act(var/obj/item/projectile/P)
if(P.damage_type == BRUTE || P.damage_type == BURN)
take_damage(P.damage)
/obj/machinery/camera/ex_act(severity)
if(src.invuln)
if(indestructible)
return
else
..(severity)
return
//camera dies if an explosion touches it!
if(severity <= 2 || prob(50))
destroy()
..() //and give it the regular chance of being deleted outright
/obj/machinery/camera/blob_act()
del(src)
return
/obj/machinery/camera/proc/setViewRange(var/num = 7)
src.view_range = num
cameranet.updateVisibility(src, 0)
/obj/machinery/camera/proc/shock(var/mob/living/user)
if(!istype(user))
return
user.electrocute_act(10, src)
/obj/machinery/camera/attack_paw(mob/living/carbon/alien/humanoid/user as mob)
if(!istype(user))
return
@@ -107,10 +110,22 @@
add_hiddenprint(user)
deactivate(user,0)
/obj/machinery/camera/attackby(W as obj, mob/living/user as mob)
/obj/machinery/camera/hitby(AM as mob|obj)
..()
if (istype(AM, /obj))
var/obj/O = AM
if (O.throwforce >= src.toughness)
visible_message("<span class='warning'><B>[src] was hit by [O].</B></span>")
take_damage(O.throwforce)
/obj/machinery/camera/proc/setViewRange(var/num = 7)
src.view_range = num
cameranet.updateVisibility(src, 0)
/obj/machinery/camera/attackby(obj/W as obj, mob/living/user as mob)
invalidateCameraCache()
// DECONSTRUCTION
if(istype(W, /obj/item/weapon/screwdriver))
if(isscrewdriver(W))
//user << "<span class='notice'>You start to [panel_open ? "close" : "open"] the camera's panel.</span>"
//if(toggle_panel(user)) // No delay because no one likes screwdrivers trying to be hip and have a duration cooldown
panel_open = !panel_open
@@ -118,19 +133,21 @@
"<span class='notice'>You screw the camera's panel [panel_open ? "open" : "closed"].</span>")
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
else if((istype(W, /obj/item/weapon/wirecutters) || istype(W, /obj/item/device/multitool)) && panel_open)
wires.Interact(user)
else if((iswirecutter(W) || ismultitool(W)) && panel_open)
interact(user)
else if(istype(W, /obj/item/weapon/weldingtool) && wires.CanDeconstruct())
else if(iswelder(W) && (wires.CanDeconstruct() || (stat & BROKEN)))
if(weld(W, user))
if(assembly)
if (stat & BROKEN)
new /obj/item/stack/cable_coil(src.loc, length=2)
else if(assembly)
assembly.loc = src.loc
assembly.state = 1
new /obj/item/stack/cable_coil(src.loc, length=2)
del(src)
// OTHER
else if ((istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user))
else if (can_use() && (istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user))
var/mob/living/U = user
var/obj/item/weapon/paper/X = null
var/obj/item/device/pda/P = null
@@ -152,11 +169,12 @@
else O << "<b><a href='byond://?src=\ref[O];track2=\ref[O];track=\ref[U]'>[U]</a></b> holds \a [itemname] up to one of your cameras ..."
O << browse(text("<HTML><HEAD><TITLE>[]</TITLE></HEAD><BODY><TT>[]</TT></BODY></HTML>", itemname, info), text("window=[]", itemname))
for(var/mob/O in player_list)
if(O.client && O.client.eye == src)
O.unset_machine()
O.reset_view(null)
O << "[U] holds \a [itemname] up to the camera..."
O << browse("<HTML><HEAD><TITLE>[itemname]</TITLE></HEAD><BODY><TT>[info]</TT></BODY></HTML>","window=[itemname]")
if (istype(O.machine, /obj/machinery/computer/security))
var/obj/machinery/computer/security/S = O.machine
if (S.current == src)
O << "[U] holds \a [itemname] up to one of the cameras ..."
O << browse(text("<HTML><HEAD><TITLE>[]</TITLE></HEAD><BODY><TT>[]</TT></BODY></HTML>", itemname, info), text("window=[]", itemname))
else if (istype(W, /obj/item/device/camera_bug) && panel_open)
if (!src.can_use())
user << "\blue Camera non-functional"
@@ -165,32 +183,24 @@
user << "\blue Camera bugged."
user.drop_item(W)
hasbug = W
contents += W
if(prob(15))
spawn(30)
if(src.can_use() && hasbug)
desc += "<br>The power light on the camera is blinking"
triggerCameraAlarm()
src.bugged = 1
else if (iscrowbar(W) && panel_open && src.hasbug)
user << "\blue You retrieve \the [hasbug]"
user.put_in_hands(hasbug)
hasbug = null
deactivatebug(user)
else if(istype(W, /obj/item/weapon/melee/energy/blade))//Putting it here last since it's a special case. I wonder if there is a better way to do these than type casting.
deactivate(user,2)//Here so that you can disconnect anyone viewing the camera, regardless if it's on or off.
var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
spark_system.set_up(5, 0, loc)
spark_system.start()
playsound(loc, 'sound/weapons/blade1.ogg', 50, 1)
playsound(loc, "sparks", 50, 1)
visible_message("\blue The camera has been sliced apart by [] with an energy blade!")
del(src)
else if(istype(W, /obj/item/device/laser_pointer))
var/obj/item/device/laser_pointer/L = W
L.laser_act(src, user)
deactivatebug(user)
else if(W.damtype == BRUTE || W.damtype == BURN) //bashing cameras
if (W.force >= src.toughness)
visible_message("<span class='warning'><b>[src] has been [pick(W.attack_verb)] with [W] by [user]!</b></span>")
if (istype(W, /obj/item)) //is it even possible to get into attackby() with non-items?
var/obj/item/I = W
if (I.hitsound)
playsound(loc, I.hitsound, 50, 1, -1)
take_damage(W.force)
else
..()
return
/obj/machinery/camera/proc/deactivatebug(user as mob)
for(var/mob/O in player_list)
if(istype(O.machine, /obj/item/device/handtv))
@@ -201,50 +211,91 @@
O << "The screen bursts into static."
/obj/machinery/camera/proc/deactivate(user as mob, var/choice = 1)
if(choice==1)
status = !( src.status )
if(choice != 1)
//legacy support, if choice is != 1 then just kick viewers without changing status
kick_viewers()
else
invalidateCameraCache()
set_status( !src.status )
if (!(src.status))
if(user)
visible_message("\red [user] has deactivated [src]!")
add_hiddenprint(user)
else
visible_message("\red \The [src] deactivates!")
visible_message("\red [user] has deactivated [src]!")
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
icon_state = "[initial(icon_state)]1"
add_hiddenprint(user)
else
if(user)
visible_message("\red [user] has reactivated [src]!")
add_hiddenprint(user)
else
visible_message("\red \the [src] reactivates!")
visible_message("\red [user] has reactivated [src]!")
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
icon_state = initial(icon_state)
add_hiddenprint(user)
// now disconnect anyone using the camera
//Apparently, this will disconnect anyone even if the camera was re-activated.
//I guess that doesn't matter since they can't use it anyway?
/obj/machinery/camera/proc/take_damage(var/force, var/message)
//prob(25) gives an average of 3-4 hits
if (force >= toughness && (force > toughness*4 || prob(25)))
destroy()
//Used when someone breaks a camera
/obj/machinery/camera/proc/destroy()
invalidateCameraCache()
stat |= BROKEN
kick_viewers()
triggerCameraAlarm()
update_icon()
//sparks
var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
spark_system.set_up(5, 0, loc)
spark_system.start()
playsound(loc, "sparks", 50, 1)
/obj/machinery/camera/proc/set_status(var/newstatus)
if (status != newstatus)
status = newstatus
invalidateCameraCache()
// now disconnect anyone using the camera
//Apparently, this will disconnect anyone even if the camera was re-activated.
//I guess that doesn't matter since they couldn't use it anyway?
kick_viewers()
//This might be redundant, because of check_eye()
/obj/machinery/camera/proc/kick_viewers()
for(var/mob/O in player_list)
if(O.client && O.client.eye == src)
O.unset_machine()
O.reset_view(null)
O << "The screen bursts into static."
if (istype(O.machine, /obj/machinery/computer/security))
var/obj/machinery/computer/security/S = O.machine
if (S.current == src)
O.unset_machine()
O.reset_view(null)
O << "The screen bursts into static."
/obj/machinery/camera/update_icon()
if (!status || (stat & BROKEN))
icon_state = "[initial(icon_state)]1"
else if (stat & EMPED)
icon_state = "[initial(icon_state)]emp"
else
icon_state = initial(icon_state)
/obj/machinery/camera/proc/triggerCameraAlarm()
alarm_on = 1
if(!get_area(src))
return
for(var/mob/living/silicon/S in mob_list)
S.triggerAlarm("Camera", get_area(src), list(src), src)
/obj/machinery/camera/proc/cancelCameraAlarm()
alarm_on = 0
if(!get_area(src))
return
for(var/mob/living/silicon/S in mob_list)
S.cancelAlarm("Camera", get_area(src), list(src), src)
S.cancelAlarm("Camera", get_area(src), src)
//if false, then the camera is listed as DEACTIVATED and cannot be used
/obj/machinery/camera/proc/can_use()
if(!status)
return 0
if(stat & EMPED)
if(stat & (EMPED|BROKEN))
return 0
return 1
@@ -266,13 +317,13 @@
//If someone knows a better way to do this, let me know. -Giacom
switch(i)
if(NORTH)
src.dir = SOUTH
dir = SOUTH
if(SOUTH)
src.dir = NORTH
dir = NORTH
if(WEST)
src.dir = EAST
dir = EAST
if(EAST)
src.dir = WEST
dir = WEST
break
//Return a working camera that can see a given mob
@@ -312,3 +363,14 @@
return 1
busy = 0
return 0
/obj/machinery/camera/interact(mob/living/user as mob)
if(!panel_open || istype(user, /mob/living/silicon/ai))
return
if(stat & BROKEN)
user << "<span class='warning'>\The [src] is broken.</span>"
return
user.set_machine(src)
wires.Interact(user)
@@ -1,16 +1,15 @@
/obj/item/weapon/camera_assembly
name = "camera assembly"
desc = "The basic construction for Nanotrasen-Always-Watching-You cameras."
desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface."
icon = 'icons/obj/monitors.dmi'
icon_state = "cameracase"
w_class = 2
anchored = 0
m_amt = 700
g_amt = 300
// Motion, EMP-Proof, X-Ray
var/list/obj/item/possible_upgrades = list(/obj/item/device/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/weapon/reagent_containers/food/snacks/grown/carrot)
var/list/obj/item/possible_upgrades = list(/obj/item/device/assembly/prox_sensor, /obj/item/stack/sheet/mineral/osmium, /obj/item/weapon/stock_parts/scanning_module)
var/list/upgrades = list()
var/state = 0
var/busy = 0
@@ -59,8 +58,10 @@
if(iscoil(W))
var/obj/item/stack/cable_coil/C = W
if(C.use(2))
user << "You add wires to the assembly."
user << "<span class='notice'>You add wires to the assembly.</span>"
state = 3
else
user << "<span class='warning'>You need 2 coils of wire to wire the assembly.</span>"
return
else if(iswelder(W))
@@ -87,7 +88,8 @@
usr << "No network found please hang up and try your call again."
return
var/temptag = "[get_area(src)] ([rand(1, 999)])"
var/area/camera_area = get_area(src)
var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])"
input = strip_html(input(usr, "How would you like to name the camera?", "Set Camera Name", temptag))
state = 4
@@ -124,7 +126,7 @@
// Upgrades!
if(is_type_in_list(W, possible_upgrades) && !is_type_in_list(W, upgrades)) // Is a possible upgrade and isn't in the camera already.
user << "You attach the [W] into the assembly inner circuits."
user << "You attach \the [W] into the assembly inner circuits."
upgrades += W
user.drop_item(W)
W.loc = src
@@ -169,4 +171,4 @@
return 0
return 1
busy = 0
return 0
return 0
+8 -2
View File
@@ -8,6 +8,8 @@
/obj/machinery/camera/process()
// motion camera event loop
if (stat & (EMPED|NOPOWER))
return
if(!isMotion())
. = PROCESS_KILL
return
@@ -40,16 +42,20 @@
cancelAlarm()
/obj/machinery/camera/proc/cancelAlarm()
if (!status || (stat & NOPOWER))
return 0
if (detectTime == -1)
for (var/mob/living/silicon/aiPlayer in player_list)
if (status) aiPlayer.cancelAlarm("Motion", src.loc.loc)
aiPlayer.cancelAlarm("Motion", get_area(src), src)
detectTime = 0
return 1
/obj/machinery/camera/proc/triggerAlarm()
if (!status || (stat & NOPOWER))
return 0
if (!detectTime) return 0
for (var/mob/living/silicon/aiPlayer in player_list)
if (status) aiPlayer.triggerAlarm("Motion", src.loc.loc, src)
aiPlayer.triggerAlarm("Motion", get_area(src), list(src), src)
detectTime = -1
return 1
+19 -6
View File
@@ -53,12 +53,14 @@
// CHECKS
/obj/machinery/camera/proc/isEmpProof()
var/O = locate(/obj/item/stack/sheet/mineral/plasma) in assembly.upgrades
var/O = locate(/obj/item/stack/sheet/mineral/osmium) in assembly.upgrades
return O
/obj/machinery/camera/proc/isXRay()
var/O = locate(/obj/item/weapon/reagent_containers/food/snacks/grown/carrot) in assembly.upgrades
return O
var/obj/item/weapon/stock_parts/scanning_module/O = locate(/obj/item/weapon/stock_parts/scanning_module) in assembly.upgrades
if (O && O.rating >= 2)
return O
return null
/obj/machinery/camera/proc/isMotion()
var/O = locate(/obj/item/device/assembly/prox_sensor) in assembly.upgrades
@@ -67,11 +69,22 @@
// UPGRADE PROCS
/obj/machinery/camera/proc/upgradeEmpProof()
assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/plasma(assembly))
assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/osmium(assembly))
setPowerUsage()
/obj/machinery/camera/proc/upgradeXRay()
assembly.upgrades.Add(new /obj/item/weapon/reagent_containers/food/snacks/grown/carrot(assembly))
assembly.upgrades.Add(new /obj/item/weapon/stock_parts/scanning_module/adv(assembly))
setPowerUsage()
// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list.
/obj/machinery/camera/proc/upgradeMotion()
assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly))
assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly))
setPowerUsage()
/obj/machinery/camera/proc/setPowerUsage()
var/mult = 1
if (isXRay())
mult++
if (isMotion())
mult++
active_power_usage = mult*initial(active_power_usage)
+96 -96
View File
@@ -1,16 +1,25 @@
/mob/living/silicon/ai/var/max_locations = 10
/mob/living/silicon/ai/var/stored_locations[0]
/mob/living/silicon/ai/proc/InvalidTurf(turf/T as turf)
if(!T)
return 1
if(T.z == 2)
return 1
if(T.z > 6)
return 1
return 0
/mob/living/silicon/ai/proc/get_camera_list()
if(src.stat == 2)
return
var/list/L = list()
for (var/obj/machinery/camera/C in cameranet.viewpoints)
L.Add(C)
camera_sort(L)
cameranet.process_sort()
var/list/T = list()
T["Cancel"] = "Cancel"
for (var/obj/machinery/camera/C in L)
for (var/obj/machinery/camera/C in cameranet.viewpoints)
var/list/tempnetwork = C.network&src.network
if (tempnetwork.len)
T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C
@@ -19,20 +28,76 @@
track.cameras = T
return T
/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list())
set category = "AI Commands"
set name = "Show Camera List"
if(src.stat == 2)
src << "You can't list the cameras because you are dead!"
return
if (!camera || camera == "Cancel")
return 0
var/obj/machinery/camera/C = track.cameras[camera]
track = null
src.eyeobj.setLoc(C)
return
/mob/living/silicon/ai/proc/ai_store_location(loc as text)
set category = "AI Commands"
set name = "Store Camera Location"
set desc = "Stores your current camera location by the given name"
loc = sanitize(copytext(loc, 1, MAX_MESSAGE_LEN))
if(!loc)
src << "\red Must supply a location name"
return
if(stored_locations.len >= max_locations)
src << "\red Cannot store additional locations. Remove one first"
return
if(loc in stored_locations)
src << "\red There is already a stored location by this name"
return
var/L = src.eyeobj.getLoc()
if (InvalidTurf(get_turf(L)))
src << "\red Unable to store this location"
return
stored_locations[loc] = L
src << "Location '[loc]' stored"
/mob/living/silicon/ai/proc/sorted_stored_locations()
return sortList(stored_locations)
/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations())
set category = "AI Commands"
set name = "Goto Camera Location"
set desc = "Returns to the selected camera location"
if (!(loc in stored_locations))
src << "\red Location [loc] not found"
return
var/L = stored_locations[loc]
src.eyeobj.setLoc(L)
/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations())
set category = "AI Commands"
set name = "Delete Camera Location"
set desc = "Deletes the selected camera location"
if (!(loc in stored_locations))
src << "\red Location [loc] not found"
return
stored_locations.Remove(loc)
src << "Location [loc] removed"
// Used to allow the AI is write in mob names/camera name from the CMD line.
/datum/trackable
var/list/names = list()
@@ -50,12 +115,7 @@
for(var/mob/living/M in mob_list)
// Easy checks first.
// Don't detect mobs on Centcom. Since the wizard den is on Centcomm, we only need this.
var/turf/T = get_turf(M)
if(!T)
continue
if(T.z == 2)
continue
if(T.z > 6)
if(InvalidTurf(get_turf(M)))
continue
if(M == usr)
continue
@@ -72,10 +132,6 @@
//Cameras can't track people wearing an agent card or a ninja hood.
if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
continue
if(istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja))
var/obj/item/clothing/head/helmet/space/space_ninja/hood = H.head
if(!hood.canremove)
continue
// Now, are they viewable by a camera? (This is last because it's the most intensive check)
if(!near_camera(M))
@@ -98,6 +154,10 @@
return targets
/mob/living/silicon/ai/proc/ai_camera_track(var/target_name in trackable_mobs())
set category = "AI Commands"
set name = "Track With Camera"
set desc = "Select who you would like to track."
if(src.stat == 2)
src << "You can't track with camera because you are dead!"
return
@@ -108,61 +168,18 @@
src.track = null
ai_actual_track(target)
/mob/living/silicon/ai/proc/open_nearest_door(mob/living/target as mob)
if(!istype(target)) return
spawn(0)
if(istype(target, /mob/living/carbon/human))
var/mob/living/carbon/human/H = target
if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
src << "Unable to locate an airlock"
return
if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
src << "Unable to locate an airlock"
return
if(istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja) && !H.head.canremove)
src << "Unable to locate an airlock"
return
if(H.digitalcamo)
src << "Unable to locate an airlock"
return
if (!near_camera(target))
src << "Target is not near any active cameras."
return
var/obj/machinery/door/airlock/tobeopened
var/dist = -1
for(var/obj/machinery/door/airlock/D in range(3,target))
if(!D.density) continue
if(dist < 0)
dist = get_dist(D, target)
//world << dist
tobeopened = D
else
if(dist > get_dist(D, target))
dist = get_dist(D, target)
//world << dist
tobeopened = D
//world << "found [tobeopened.name] closer"
else
//world << "[D.name] not close enough | [get_dist(D, target)] | [dist]"
if(tobeopened)
switch(alert(src, "Do you want to open \the [tobeopened] for [target]?","Doorknob_v2a.exe","Yes","No"))
if("Yes")
var/nhref = "src=\ref[tobeopened];aiEnable=7"
tobeopened.Topic(nhref, params2list(nhref), tobeopened, 1)
src << "\blue You've opened \the [tobeopened] for [target]."
if("No")
src << "\red You deny the request."
else
src << "\red You've failed to open an airlock for [target]"
/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0)
if(!cameraFollow)
return
/mob/living/silicon/ai/proc/ai_actual_track(atom/target as mob|obj)
src << "Follow camera mode [forced ? "terminated" : "ended"]."
cameraFollow = null
/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target as mob)
if(!istype(target)) return
var/mob/living/silicon/ai/U = usr
U.cameraFollow = target
//U << text("Now tracking [] on camera.", target.name)
//if (U.machine == null)
// U.machine = U
U << "Now tracking [target.name] on camera."
spawn (0)
@@ -172,30 +189,23 @@
if (istype(target, /mob/living/carbon/human))
var/mob/living/carbon/human/H = target
if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
U << "Follow camera mode terminated."
U.cameraFollow = null
U.ai_cancel_tracking(1)
return
/* if(istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja) && !H.head.canremove)
U << "Follow camera mode terminated."
U.cameraFollow = null
return*/
if(H.digitalcamo)
U << "Follow camera mode terminated."
U.cameraFollow = null
U.ai_cancel_tracking(1)
return
if(istype(target.loc,/obj/effect/dummy))
U << "Follow camera mode ended."
U.cameraFollow = null
U.ai_cancel_tracking()
return
if (!near_camera(target))
if (!trackable(target))
U << "Target is not near any active cameras."
sleep(100)
continue
if(U.eyeobj)
U.eyeobj.setLoc(get_turf(target))
U.eyeobj.setLoc(get_turf(target), 0)
else
view_core()
return
@@ -212,6 +222,12 @@
return 0
return 1
/proc/trackable(var/mob/living/M)
var/turf/T = get_turf(M)
if(T && (T.z == 1) && hassensorlevel(M, SUIT_SENSOR_TRACKING))
return 1
return near_camera(M)
/obj/machinery/camera/attack_ai(var/mob/living/silicon/ai/user as mob)
if (!istype(user))
@@ -223,19 +239,3 @@
/mob/living/silicon/ai/attack_ai(var/mob/user as mob)
ai_camera_list()
/proc/camera_sort(list/L)
var/obj/machinery/camera/a
var/obj/machinery/camera/b
for (var/i = L.len, i > 0, i--)
for (var/j = 1 to i - 1)
a = L[j]
b = L[j + 1]
if (a.c_tag_order != b.c_tag_order)
if (a.c_tag_order > b.c_tag_order)
L.Swap(j, j + 1)
else
if (sorttext(a.c_tag, b.c_tag) < 0)
L.Swap(j, j + 1)
return L
+37 -19
View File
@@ -1,3 +1,7 @@
/proc/invalidateCameraCache()
for(var/obj/machinery/computer/security/s in world)
s.camera_cache = null
/obj/machinery/computer/security
name = "Camera Monitor"
desc = "Used to access the various cameras networks on the station."
@@ -12,6 +16,7 @@
var/list/tempnets[0]
var/list/data[0]
var/list/access[0]
var/camera_cache = null
New() // Lists existing networks and their required access. Format: networks[<name>] = list(<access>)
networks["SS13"] = list(access_hos,access_captain)
@@ -78,29 +83,38 @@
data["current"] = null
var/list/L = list()
for (var/obj/machinery/camera/C in cameranet.viewpoints)
if(can_access_camera(C))
L.Add(C)
if(isnull(camera_cache))
cameranet.process_sort()
camera_sort(L)
var/cameras[0]
for(var/obj/machinery/camera/C in cameranet.viewpoints)
var/cam[0]
cam["name"] = C.c_tag
cam["deact"] = !C.can_use()
cam["camera"] = "\ref[C]"
cam["x"] = C.x
cam["y"] = C.y
cam["z"] = C.z
var/cameras[0]
for(var/obj/machinery/camera/C in L)
var/cam[0]
cam["name"] = C.c_tag
cam["deact"] = !C.can_use()
cam["camera"] = "\ref[C]"
cam["x"] = C.x
cam["y"] = C.y
cam["z"] = C.z
cameras[++cameras.len] = cam
cameras[++cameras.len] = cam
if(C == current)
data["current"] = cam
var/list/camera_list = list("cameras" = cameras)
camera_cache=list2json(camera_list)
else
if(current)
var/cam[0]
cam["name"] = current.c_tag
cam["deact"] = !current.can_use()
cam["camera"] = "\ref[current]"
cam["x"] = current.x
cam["y"] = current.y
cam["z"] = current.z
if(C == current)
data["current"] = cam
data["cameras"] = cameras
tempnets.Cut()
if(emagged)
@@ -119,6 +133,9 @@
tempnets.Add(list(list("name" = l, "active" = 0)))
break
data["networks"] = tempnets
if(ui)
ui.load_cached_data(camera_cache)
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
@@ -128,7 +145,8 @@
ui.add_template("mapContent", "sec_camera_map_content.tmpl")
// adding a template with the key "mapHeader" replaces the map header content
ui.add_template("mapHeader", "sec_camera_map_header.tmpl")
ui.load_cached_data(camera_cache)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
+1 -1
View File
@@ -190,7 +190,7 @@
if(temp.len)
L.Add(C)
camera_sort(L)
cameranet.process_sort()
return L
verify_machine(var/obj/machinery/camera/C,var/datum/file/camnet_key/key = null)
+51 -5
View File
@@ -112,7 +112,7 @@ var/list/ai_list = list()
if (istype(loc, /turf))
verbs.Add(/mob/living/silicon/ai/proc/ai_network_change, \
/mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \
/mob/living/silicon/ai/proc/toggle_camera_light, /mob/living/silicon/ai/proc/botcall, /mob/living/silicon/ai/proc/control_integrated_radio, /mob/living/silicon/ai/proc/control_hud, /mob/living/silicon/ai/proc/change_arrival_message)
/mob/living/silicon/ai/proc/toggle_camera_light, /mob/living/silicon/ai/proc/botcall, /mob/living/silicon/ai/proc/control_integrated_radio, /mob/living/silicon/ai/proc/control_hud, /mob/living/silicon/ai/proc/change_arrival_message, /mob/living/silicon/ai/proc/ai_store_location, /mob/living/silicon/ai/proc/ai_goto_location, /mob/living/silicon/ai/proc/ai_remove_location)
if(!safety)//Only used by AIize() to successfully spawn an AI.
if (!B)//If there is no player/brain inside.
@@ -127,7 +127,7 @@ var/list/ai_list = list()
verbs.Remove(,/mob/living/silicon/ai/proc/ai_call_shuttle,/mob/living/silicon/ai/proc/ai_camera_track, \
/mob/living/silicon/ai/proc/ai_camera_list, /mob/living/silicon/ai/proc/ai_network_change, \
/mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \
/mob/living/silicon/ai/proc/toggle_camera_light,/mob/living/silicon/ai/verb/pick_icon,/mob/living/silicon/ai/proc/control_hud)
/mob/living/silicon/ai/proc/toggle_camera_light,/mob/living/silicon/ai/verb/pick_icon,/mob/living/silicon/ai/proc/control_hud, /mob/living/silicon/ai/proc/change_arrival_message)
laws = new /datum/ai_laws/alienmov
else
B.brainmob.mind.transfer_to(src)
@@ -631,8 +631,6 @@ var/list/ai_list = list()
/mob/living/silicon/ai/proc/switchCamera(var/obj/machinery/camera/C)
src.cameraFollow = null
if (!C || stat == 2) //C.can_use())
return 0
@@ -706,7 +704,6 @@ var/list/ai_list = list()
set category = "AI Commands"
set name = "Jump To Network"
unset_machine()
src.cameraFollow = null
var/cameralist[0]
if(usr.stat == 2)
@@ -912,3 +909,52 @@ var/list/ai_list = list()
src << "Accessing Subspace Transceiver control..."
if (src.aiRadio)
src.aiRadio.interact(src)
/mob/living/silicon/ai/proc/open_nearest_door(mob/living/target as mob)
if(!istype(target)) return
spawn(0)
if(istype(target, /mob/living/carbon/human))
var/mob/living/carbon/human/H = target
if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
src << "Unable to locate an airlock"
return
if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
src << "Unable to locate an airlock"
return
if(istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja) && !H.head.canremove)
src << "Unable to locate an airlock"
return
if(H.digitalcamo)
src << "Unable to locate an airlock"
return
if (!near_camera(target))
src << "Target is not near any active cameras."
return
var/obj/machinery/door/airlock/tobeopened
var/dist = -1
for(var/obj/machinery/door/airlock/D in range(3,target))
if(!D.density) continue
if(dist < 0)
dist = get_dist(D, target)
//world << dist
tobeopened = D
else
if(dist > get_dist(D, target))
dist = get_dist(D, target)
//world << dist
tobeopened = D
//world << "found [tobeopened.name] closer"
else
//world << "[D.name] not close enough | [get_dist(D, target)] | [dist]"
if(tobeopened)
switch(alert(src, "Do you want to open \the [tobeopened] for [target]?","Doorknob_v2a.exe","Yes","No"))
if("Yes")
var/nhref = "src=\ref[tobeopened];aiEnable=7"
tobeopened.Topic(nhref, params2list(nhref), tobeopened, 1)
src << "\blue You've opened \the [tobeopened] for [target]."
if("No")
src << "\red You deny the request."
else
src << "\red You've failed to open an airlock for [target]"
return
@@ -54,6 +54,14 @@
//Holopad
if(ai.holo)
ai.holo.move_hologram()
/mob/aiEye/proc/getLoc()
if(ai)
if(!isturf(ai.loc) || !ai.client)
return
return ai.eyeobj.loc
/mob/aiEye/Move()
@@ -12,12 +12,19 @@
/obj/machinery/camera/New()
..()
cameranet.viewpoints += src //Camera must be added to global list of all cameras no matter what...
//Camera must be added to global list of all cameras no matter what...
if(cameranet.cameras_unsorted || !ticker)
cameranet.viewpoints += src
cameranet.cameras_unsorted = 1
else
dd_insertObjectList(cameranet.viewpoints, src)
var/list/open_networks = difflist(network,RESTRICTED_CAMERA_NETWORKS) //...but if all of camera's networks are restricted, it only works for specific camera consoles.
if(open_networks.len) //If there is at least one open network, chunk is available for AI usage.
cameranet.addViewpoint(src)
/obj/machinery/camera/Del()
cameranet.viewpoints -= src
var/list/open_networks = difflist(network,RESTRICTED_CAMERA_NETWORKS)
if(open_networks.len)
+8
View File
@@ -161,6 +161,14 @@ proc/hasorgans(A)
/proc/hsl2rgb(h, s, l)
return
proc/hassensorlevel(A, var/level)
var/mob/living/carbon/human/H = A
if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under))
var/obj/item/clothing/under/U = H.w_uniform
return U.sensor_mode >= level
return 0
/proc/check_zone(zone)
+3 -1
View File
@@ -1,7 +1,7 @@
json_writer
proc
WriteObject(list/L)
WriteObject(list/L, cached_data = null)
. = "{"
var/i = 1
for(var/k in L)
@@ -9,6 +9,8 @@ json_writer
. += {"\"[k]\":[write(val)]"}
if(i++ < L.len)
. += ","
if(cached_data)
. = copytext(., 1, lentext(.)) + ",\"cached\":[cached_data]}"
.+= "}"
write(val)
+2 -2
View File
@@ -7,6 +7,6 @@ proc
var/static/json_reader/_jsonr = new()
return _jsonr.ReadObject(_jsonr.ScanJson(json))
list2json(list/L)
list2json(list/L, var/cached_data = null)
var/static/json_writer/_jsonw = new()
return _jsonw.WriteObject(L)
return _jsonw.WriteObject(L, cached_data)
+16 -2
View File
@@ -58,6 +58,9 @@ nanoui is used to open and update nano browser uis
var/is_auto_updating = 0
// the current status/visibility of the ui
var/status = STATUS_INTERACTIVE
var/cached_data = null
// Only allow users with a certain user.stat to get updates. Defaults to 0 (concious)
var/allowed_user_stat = 0 // -1 = ignore, 0 = alive, 1 = unconcious or alive, 2 = dead concious or alive
@@ -370,7 +373,7 @@ nanoui is used to open and update nano browser uis
template_data_json = list2json(templates)
var/list/send_data = get_send_data(initial_data)
var/initial_data_json = list2json(send_data)
var/initial_data_json = list2json(send_data, cached_data)
var/url_parameters_json = list2json(list("src" = "\ref[src]"))
@@ -450,6 +453,17 @@ nanoui is used to open and update nano browser uis
var/params = "\ref[src]"
winset(user, window_id, "on-close=\"nanoclose [params]\"")
/**
* Appends already processed json txt to the list2json proc when setting initial-data and data pushes
* Used for data that is fucking huge like manifests and camera lists that doesn't change often.
* And we only want to process them when they change.
*
* @return nothing
*/
/datum/nanoui/proc/load_cached_data(var/data)
cached_data = data
return
/**
* Push data to an already open UI window
@@ -464,7 +478,7 @@ nanoui is used to open and update nano browser uis
var/list/send_data = get_send_data(data)
//user << list2json(data) // used for debugging
user << output(list2params(list(list2json(send_data))),"[window_id].browser:receiveUpdateData")
user << output(list2params(list(list2json(send_data,cached_data))),"[window_id].browser:receiveUpdateData")
/**
* This Topic() proc is called whenever a user clicks on a link within a Nano UI
+2 -1
View File
@@ -96,6 +96,7 @@
P.internal_organs = list()
P.internal_organs += src
H.internal_organs_by_name[name] = src
H.internal_organs |= src
owner = H
return
@@ -196,7 +197,7 @@
src.damage += 0.2 * process_accuracy
//Damaged one shares the fun
else
var/datum/organ/internal/O = pick(owner.internal_organs_by_name)
var/datum/organ/internal/O = pick(owner.internal_organs)
if(O)
O.damage += 0.2 * process_accuracy
+9 -1
View File
@@ -806,6 +806,8 @@ var/list/RESTRICTED_CAMERA_NETWORKS = list( //Those networks can only be accesse
"ERT",
"NukeOps",
"Thunderdome",
"UO45",
"UO45R",
"Xeno"
)
@@ -943,4 +945,10 @@ var/list/hit_appends = list("-OOF", "-ACK", "-UGH", "-HRNK", "-HURGH", "-GLORF")
#define AUTOLATHE 4 //Uses glass/metal only.
#define CRAFTLATHE 8 //Uses fuck if I know. For use eventually.
#define MECHFAB 16 //Remember, objects utilising this flag should have construction_time and construction_cost vars.
//Note: More then one of these can be added to a design but imprinter and lathe designs are incompatable.
//Note: More then one of these can be added to a design but imprinter and lathe designs are incompatable.
// Suit sensor levels
#define SUIT_SENSOR_OFF 0
#define SUIT_SENSOR_BINARY 1
#define SUIT_SENSOR_VITAL 2
#define SUIT_SENSOR_TRACKING 3
+1 -1
View File
@@ -12,7 +12,7 @@ Used In File(s): \code\game\machinery\computer\camera.dm
<div class='itemContent'>None</div>
{{/if}}
</div>
{{for data.cameras}}
{{for data.cached.cameras}}
{{if data.current && value.name == data.current.name}}
{{:helper.link(value.name, '', {'switchTo' : value.camera}, 'selected')}}
{{else value.deact}}
+1 -1
View File
@@ -2,7 +2,7 @@
Title: Security Camera Console (Map content)
Used In File(s): \code\game\machinery\computer\camera.dm
-->
{{for data.cameras}}
{{for data.cached.cameras}}
{{if value.z == 1}}
<div class="mapIcon mapIcon16" style="left: {{:(value.x)}}px; bottom: {{:(value.y - 1)}}px;">
{{if data.current && value.name == data.current.name}}