diff --git a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql index b7e2e501ce..4978c514b8 100644 --- a/SQL/tgstation_schema.sql +++ b/SQL/tgstation_schema.sql @@ -2,9 +2,7 @@ SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL'; -CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ; CREATE SCHEMA IF NOT EXISTS `tgstation` DEFAULT CHARACTER SET latin1 ; -USE `mydb` ; USE `tgstation` ; -- ----------------------------------------------------- diff --git a/baystation12.dme b/baystation12.dme index d064eb19f4..8e69350ebe 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -161,11 +161,11 @@ #define FILE_DIR "code/unused/powerarmor" #define FILE_DIR "code/unused/spacecraft" #define FILE_DIR "code/WorkInProgress" +#define FILE_DIR "code/WorkInProgress/AI_Visibility" #define FILE_DIR "code/WorkInProgress/animusstation" #define FILE_DIR "code/WorkInProgress/Apples" #define FILE_DIR "code/WorkInProgress/Cael_Aislinn" #define FILE_DIR "code/WorkInProgress/Cael_Aislinn/BirdMan" -#define FILE_DIR "code/WorkInProgress/Cael_Aislinn/Jumper" #define FILE_DIR "code/WorkInProgress/Cael_Aislinn/Rust" #define FILE_DIR "code/WorkInProgress/Cael_Aislinn/Supermatter" #define FILE_DIR "code/WorkInProgress/Cael_Aislinn/Tajara" @@ -616,7 +616,6 @@ #include "code\game\mecha\working\firefighter.dm" #include "code\game\mecha\working\ripley.dm" #include "code\game\mecha\working\working.dm" -#include "code\game\objects\biomass.dm" #include "code\game\objects\blood.dm" #include "code\game\objects\bodybag.dm" #include "code\game\objects\bombspawner.dm" @@ -1143,10 +1142,15 @@ #include "code\modules\scripting\Scanner\Tokens.dm" #include "code\modules\security levels\keycard authentication.dm" #include "code\modules\security levels\security levels.dm" -#include "code\WorkInProgress\AI_Visibility.dm" #include "code\WorkInProgress\buildmode.dm" #include "code\WorkInProgress\explosion_particles.dm" +#include "code\WorkInProgress\AI_Visibility\ai.dm" +#include "code\WorkInProgress\AI_Visibility\cameranet.dm" +#include "code\WorkInProgress\AI_Visibility\chunk.dm" +#include "code\WorkInProgress\AI_Visibility\minimap.dm" +#include "code\WorkInProgress\AI_Visibility\util.dm" #include "code\WorkInProgress\animusstation\atm.dm" +#include "code\WorkInProgress\Cael_Aislinn\power_monitor.dm" #include "code\WorkInProgress\Cael_Aislinn\BirdMan\bird_transformation.dm" #include "code\WorkInProgress\Cael_Aislinn\BirdMan\birdman.dm" #include "code\WorkInProgress\Cael_Aislinn\Rust\core_field.dm" diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index 624a731e3e..0c8c8f4134 100755 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -108,8 +108,8 @@ Filter types: else filtered_out = null - - filtered_out.update_values() + if(filtered_out) + filtered_out.update_values() air2.merge(filtered_out) air3.merge(removed) diff --git a/code/FEA/FEA_gas_mixture.dm b/code/FEA/FEA_gas_mixture.dm index 1819129532..8edefe79d7 100644 --- a/code/FEA/FEA_gas_mixture.dm +++ b/code/FEA/FEA_gas_mixture.dm @@ -466,6 +466,8 @@ datum //Outputs: 0 if the self-check failed (local airgroup breaks?) // then -1 if sharer-check failed (sharing airgroup breaks?) // then 1 if both checks pass (share succesful?) + if(!istype(sharer)) + return var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/TRANSFER_FRACTION var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/TRANSFER_FRACTION @@ -552,6 +554,10 @@ datum //Inputs: Air datum to share with //Outputs: Amount of gas exchanged (Negative if lost air, positive if gained.) + + if(!istype(sharer)) + return + var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/TRANSFER_FRACTION var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/TRANSFER_FRACTION var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived)/TRANSFER_FRACTION diff --git a/code/FEA/FEA_turf_tile.dm b/code/FEA/FEA_turf_tile.dm index fa4081b8af..38083f52ce 100644 --- a/code/FEA/FEA_turf_tile.dm +++ b/code/FEA/FEA_turf_tile.dm @@ -378,25 +378,24 @@ turf enemy_tile.consider_pressure_difference(connection_difference, direction) else air_master.active_singletons -= src //not active if not processing! + if(air) + air.react() - air.react() + if(active_hotspot) + if (!active_hotspot.process(possible_fire_spreads)) + return 0 - if(active_hotspot) - if (!active_hotspot.process(possible_fire_spreads)) - return 0 + if(air.temperature > MINIMUM_TEMPERATURE_START_SUPERCONDUCTION) + consider_superconductivity(starting = 1) - if(air.temperature > MINIMUM_TEMPERATURE_START_SUPERCONDUCTION) - consider_superconductivity(starting = 1) + if(air.check_tile_graphic()) + update_visuals(air) - if(air.check_tile_graphic()) - update_visuals(air) - - if(air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST) -// reset_delay() //hotspots always process quickly - hotspot_expose(air.temperature, CELL_VOLUME) - for(var/atom/movable/item in src) - item.temperature_expose(air, air.temperature, CELL_VOLUME) - temperature_expose(air, air.temperature, CELL_VOLUME) + if(air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST) + hotspot_expose(air.temperature, CELL_VOLUME) + for(var/atom/movable/item in src) + item.temperature_expose(air, air.temperature, CELL_VOLUME) + temperature_expose(air, air.temperature, CELL_VOLUME) return 1 diff --git a/code/WorkInProgress/AI_Visibility.dm b/code/WorkInProgress/AI_Visibility/_old_AI_Visibility.dm similarity index 96% rename from code/WorkInProgress/AI_Visibility.dm rename to code/WorkInProgress/AI_Visibility/_old_AI_Visibility.dm index cf3eadb2b3..fb7aab4dd0 100644 --- a/code/WorkInProgress/AI_Visibility.dm +++ b/code/WorkInProgress/AI_Visibility/_old_AI_Visibility.dm @@ -1,573 +1,573 @@ -//All credit for this goes to Uristqwerty. - -//And some to me! -Mini - - - -//This file is partly designed around being able to uninclude it to go back to the old ai viewing system completely. -//(And therefore also be portable to another similar codebase simply by transferring the file and including it after the other AI code files.) -//There are probably a few parts that don't do that at the moment, but I'll fix them at some point. - - -#define MINIMAP_UPDATE_DELAY 1200 - -/turf - var/image/obscured - var/image/dim - -/turf/proc/visibilityChanged() - cameranet.updateVisibility(src) - -/turf/New() - ..() - cameranet.updateVisibility(src) -/* -/turf/Del() - ..() - cameranet.updateVisibility(src) -*/ -/datum/camerachunk - var/list/obscuredTurfs = list() - var/list/visibleTurfs = list() - var/list/dimTurfs = list() - var/list/obscured = list() - var/list/dim = list() - var/list/cameras = list() - var/list/turfs = list() - var/list/seenby = list() - var/visible = 0 - var/changed = 1 - var/updating = 0 - - var/x - var/y - var/z - - var/minimap_updating = 0 - - var/icon/minimap_icon = new('minimap.dmi', "chunk_base") - var/obj/minimap_obj/minimap_obj = new() - -/obj/minimap_obj/Click(location, control, params) - if(!istype(usr, /mob/dead) && !istype(usr, /mob/living/silicon/ai) && !(usr.client && usr.client.holder && usr.client.holder.level >= 4)) - return - - var/list/par = params2list(params) - var/screen_loc = par["screen-loc"] - - if(findtext(screen_loc, "minimap:") != 1) - return - - screen_loc = copytext(screen_loc, length("minimap:") + 1) - - var/x_text = copytext(screen_loc, 1, findtext(screen_loc, ",")) - var/y_text = copytext(screen_loc, findtext(screen_loc, ",") + 1) - - var/x = (text2num(copytext(x_text, 1, findtext(x_text, ":"))) - 1) * 16 - x += round((text2num(copytext(x_text, findtext(x_text, ":") + 1)) + 1) / 2) - - var/y = (text2num(copytext(y_text, 1, findtext(y_text, ":"))) - 1) * 16 - y += round((text2num(copytext(y_text, findtext(y_text, ":") + 1)) + 1) / 2) - - if(istype(usr, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/ai = usr - ai.freelook() - ai.eyeobj.loc = locate(max(1, x - 1), max(1, y - 1), ai.eyeobj.z) - cameranet.visibility(ai.eyeobj) - - else - usr.loc = locate(max(1, x - 1), max(1, y - 1), usr.z) - -/mob/dead/verb/Open_Minimap() - set category = "Ghost" - winshow(src, "minimapwindow", 1) - client.screen |= cameranet.minimap - - if(cameranet.generating_minimap) - cameranet.minimap_viewers += src - -/mob/living/silicon/ai/verb/Open_Minimap() - set category = "AI Commands" - winshow(src, "minimapwindow", 1) - client.screen |= cameranet.minimap - - if(cameranet.generating_minimap) - cameranet.minimap_viewers += src - -/client/proc/Open_Minimap() - set category = "Admin" - winshow(src, "minimapwindow", 1) - screen |= cameranet.minimap - - if(cameranet.generating_minimap) - cameranet.minimap_viewers += src.mob - -/datum/camerachunk/proc/update_minimap() - if(changed && !updating) - update() - - minimap_icon.Blend(rgb(255, 0, 0), ICON_MULTIPLY) - - var/list/turfs = visibleTurfs | dimTurfs - - for(var/turf/turf in turfs) - var/x = (turf.x & 0xf) * 2 - var/y = (turf.y & 0xf) * 2 - - if(turf.density) - minimap_icon.DrawBox(rgb(100, 100, 100), x + 1, y + 1, x + 2, y + 2) - continue - - else if(istype(turf, /turf/space)) - minimap_icon.DrawBox(rgb(0, 0, 0), x + 1, y + 1, x + 2, y + 2) - - else - minimap_icon.DrawBox(rgb(200, 200, 200), x + 1, y + 1, x + 2, y + 2) - - for(var/obj/structure/o in turf) - if(o.density) - if(istype(o, /obj/structure/window) && (o.dir == NORTH || o.dir == SOUTH || o.dir == EAST || o.dir == WEST)) - if(o.dir == NORTH) - minimap_icon.DrawBox(rgb(150, 150, 200), x + 1, y + 2, x + 2, y + 2) - else if(o.dir == SOUTH) - minimap_icon.DrawBox(rgb(150, 150, 200), x + 1, y + 1, x + 2, y + 1) - else if(o.dir == EAST) - minimap_icon.DrawBox(rgb(150, 150, 200), x + 3, y + 1, x + 2, y + 2) - else if(o.dir == WEST) - minimap_icon.DrawBox(rgb(150, 150, 200), x + 1, y + 1, x + 1, y + 2) - - else - minimap_icon.DrawBox(rgb(150, 150, 150), x + 1, y + 1, x + 2, y + 2) - break - - for(var/obj/machinery/door/o in turf) - if(istype(o, /obj/machinery/door/window)) - if(o.dir == NORTH) - minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 2, x + 2, y + 2) - else if(o.dir == SOUTH) - minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 1, x + 2, y + 1) - else if(o.dir == EAST) - minimap_icon.DrawBox(rgb(100, 150, 100), x + 2, y + 1, x + 2, y + 2) - else if(o.dir == WEST) - minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 1, x + 1, y + 2) - - else - minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 1, x + 2, y + 2) - break - - minimap_obj.screen_loc = "minimap:[src.x / 16],[src.y / 16]" - minimap_obj.icon = minimap_icon - -/mob/aiEye - var/list/visibleCameraChunks = list() - var/mob/ai = null - density = 0 - -/datum/camerachunk/proc/add(mob/aiEye/ai) - ai.visibleCameraChunks += src - if(ai.ai.client) - ai.ai.client.images += obscured - ai.ai.client.images += dim - visible++ - seenby += ai - if(changed && !updating) - update() - changed = 0 - -/datum/camerachunk/proc/remove(mob/aiEye/ai) - ai.visibleCameraChunks -= src - if(ai.ai.client) - ai.ai.client.images -= obscured - ai.ai.client.images -= dim - seenby -= ai - if(visible > 0) - visible-- - -/datum/camerachunk/proc/visibilityChanged(turf/loc) - if(!(loc in visibleTurfs)) - return - - hasChanged() - -/datum/camerachunk/proc/hasChanged() - if(visible) - if(!updating) - updating = 1 - spawn(10)//Batch large changes, such as many doors opening or closing at once - update() - updating = 0 - else - changed = 1 - - if(!minimap_updating) - minimap_updating = 1 - - spawn(MINIMAP_UPDATE_DELAY) - if(changed && !updating) - update() - changed = 0 - - update_minimap() - minimap_updating = 0 - -/datum/camerachunk/proc/update() - - var/list/newDimTurfs = list() - var/list/newVisibleTurfs = list() - - for(var/obj/machinery/camera/c in cameras) - var/lum = c.luminosity - c.luminosity = 7 - - newDimTurfs |= turfs & view(7, c) - newVisibleTurfs |= turfs & view(6, c) - - c.luminosity = lum - - var/list/dimAdded = newDimTurfs - dimTurfs - var/list/dimRemoved = dimTurfs - newDimTurfs - var/list/visAdded = newVisibleTurfs - visibleTurfs - var/list/visRemoved = visibleTurfs - newVisibleTurfs - - visibleTurfs = newVisibleTurfs - dimTurfs = newDimTurfs - obscuredTurfs = turfs - dimTurfs - dimTurfs -= visibleTurfs - - for(var/turf/t in dimRemoved) - if(t.dim) - dim -= t.dim - for(var/mob/aiEye/m in seenby) - if(m.ai.client) - m.ai.client.images -= t.dim - - if(!(t in visibleTurfs)) - if(!t.obscured) - t.obscured = image('cameravis.dmi', t, "black", 15) - - obscured += t.obscured - for(var/mob/aiEye/m in seenby) - if(m.ai.client) - m.ai.client.images += t.obscured - - for(var/turf/t in dimAdded) - if(!(t in visibleTurfs)) - if(!t.dim) - t.dim = image('cameravis.dmi', t, "dim", 15) - t.mouse_opacity = 0 - - dim += t.dim - for(var/mob/aiEye/m in seenby) - if(m.ai.client) - m.ai.client.images += t.dim - - if(t.obscured) - obscured -= t.obscured - for(var/mob/aiEye/m in seenby) - if(m.ai.client) - m.ai.client.images -= t.obscured - - for(var/turf/t in visAdded) - if(t.obscured) - obscured -= t.obscured - for(var/mob/aiEye/m in seenby) - if(m.ai.client) - m.ai.client.images -= t.obscured - - for(var/turf/t in visRemoved) - if(t in obscuredTurfs) - if(!t.obscured) - t.obscured = image('cameravis.dmi', t, "black", 15) - - obscured += t.obscured - for(var/mob/aiEye/m in seenby) - if(m.ai.client) - m.ai.client.images += t.obscured - - -/datum/camerachunk/New(loc, x, y, z) - x &= ~0xf - y &= ~0xf - - src.x = x - src.y = y - src.z = z - - for(var/obj/machinery/camera/c in range(16, locate(x + 8, y + 8, z))) - if(c.status) - cameras += c - - turfs = block(locate(x, y, z), locate(min(world.maxx, x + 15), min(world.maxy, y + 15), z)) - - for(var/obj/machinery/camera/c in cameras) - var/lum = c.luminosity - c.luminosity = 7 - - dimTurfs |= turfs & view(7, c) - visibleTurfs |= turfs & view(6, c) - - c.luminosity = lum - - obscuredTurfs = turfs - dimTurfs - dimTurfs -= visibleTurfs - - for(var/turf/t in obscuredTurfs) - if(!t.obscured) - t.obscured = image('cameravis.dmi', t, "black", 15) - - obscured += t.obscured - - for(var/turf/t in dimTurfs) - if(!(t in visibleTurfs)) - if(!t.dim) - t.dim = image('cameravis.dmi', t, "dim", TURF_LAYER) - t.dim.mouse_opacity = 0 - - dim += t.dim - - cameranet.minimap += minimap_obj - -var/datum/cameranet/cameranet = new() - -/datum/cameranet - var/list/cameras = list() - var/list/chunks = list() - var/network = "net1" - var/ready = 0 - - var/list/minimap = list() - - var/generating_minimap = TRUE - var/list/minimap_viewers = list() - -/datum/cameranet/New() - ..() - - spawn(200) - for(var/x = 0, x <= world.maxx, x += 16) - for(var/y = 0, y <= world.maxy, y += 16) - sleep(1) - var/datum/camerachunk/c = getCameraChunk(x, y, 1) - c.update_minimap() - - for(var/mob/m in minimap_viewers) - m.client.screen |= c.minimap_obj - - generating_minimap = FALSE - minimap_viewers = list() - -/datum/cameranet/proc/chunkGenerated(x, y, z) - var/key = "[x],[y],[z]" - return key in chunks - -/datum/cameranet/proc/getCameraChunk(x, y, z) - var/key = "[x],[y],[z]" - - if(!(key in chunks)) - chunks[key] = new /datum/camerachunk(null, x, y, z) - - return chunks[key] - -/datum/cameranet/proc/visibility(mob/aiEye/ai) - var/x1 = max(0, ai.x - 16) & ~0xf - var/y1 = max(0, ai.y - 16) & ~0xf - var/x2 = min(world.maxx, ai.x + 16) & ~0xf - var/y2 = min(world.maxy, ai.y + 16) & ~0xf - - var/list/visibleChunks = list() - - for(var/x = x1; x <= x2; x += 16) - for(var/y = y1; y <= y2; y += 16) - visibleChunks += getCameraChunk(x, y, ai.z) - - var/list/remove = ai.visibleCameraChunks - visibleChunks - var/list/add = visibleChunks - ai.visibleCameraChunks - - for(var/datum/camerachunk/c in remove) - c.remove(ai) - - for(var/datum/camerachunk/c in add) - c.add(ai) - -/datum/cameranet/proc/updateVisibility(turf/loc) - if(!chunkGenerated(loc.x & ~0xf, loc.y & ~0xf, loc.z)) - return - - var/datum/camerachunk/chunk = getCameraChunk(loc.x & ~0xf, loc.y & ~0xf, loc.z) - chunk.visibilityChanged(loc) - -/datum/cameranet/proc/addCamera(obj/machinery/camera/c) - var/x1 = max(0, c.x - 16) & ~0xf - var/y1 = max(0, c.y - 16) & ~0xf - var/x2 = min(world.maxx, c.x + 16) & ~0xf - var/y2 = min(world.maxy, c.y + 16) & ~0xf - - for(var/x = x1; x <= x2; x += 16) - for(var/y = y1; y <= y2; y += 16) - if(chunkGenerated(x, y, c.z)) - var/datum/camerachunk/chunk = getCameraChunk(x, y, c.z) - if(!(c in chunk.cameras)) - chunk.cameras += c - chunk.hasChanged() - -/datum/cameranet/proc/removeCamera(obj/machinery/camera/c) - var/x1 = max(0, c.x - 16) & ~0xf - var/y1 = max(0, c.y - 16) & ~0xf - var/x2 = min(world.maxx, c.x + 16) & ~0xf - var/y2 = min(world.maxy, c.y + 16) & ~0xf - - for(var/x = x1; x <= x2; x += 16) - for(var/y = y1; y <= y2; y += 16) - if(chunkGenerated(x, y, c.z)) - var/datum/camerachunk/chunk = getCameraChunk(x, y, c.z) - if(!c) - chunk.hasChanged() - if(c in chunk.cameras) - chunk.cameras -= c - chunk.hasChanged() - -/mob/living/silicon/ai/var/mob/aiEye/eyeobj = new() - -/mob/living/silicon/ai/New() - ..() - eyeobj.ai = src - spawn(20) - freelook() - -/mob/living/silicon/ai/death(gibbed) - if(client && client.eye == eyeobj) - for(var/datum/camerachunk/c in eyeobj.visibleCameraChunks) - c.remove(eyeobj) - client.eye = src - return ..(gibbed) - -/mob/living/silicon/ai/verb/freelook() - set category = "AI Commands" - set name = "freelook" - current = null //cancel camera view first, it causes problems - cameraFollow = null -// machine = null - if(!eyeobj) //if it got deleted somehow (like an admin trying to fix things <.<') - eyeobj = new() - eyeobj.ai = src - client.eye = eyeobj - eyeobj.loc = loc - cameranet.visibility(eyeobj) - cameraFollow = null - -/mob/aiEye/Move() - . = ..() - if(.) - cameranet.visibility(src) - -/client/AIMove(n, direct, var/mob/living/silicon/ai/user) - if(eye == user.eyeobj) - user.eyeobj.loc = get_step(user.eyeobj, direct) - cameranet.visibility(user.eyeobj) - - else - return ..() - -/* -/client/AIMoveZ(direct, var/mob/living/silicon/ai/user) - if(eye == user.eyeobj) - var/dif = 0 - if(direct == UP && user.eyeobj.z > 1) - dif = -1 - else if(direct == DOWN && user.eyeobj.z < 4) - dif = 1 - user.eyeobj.loc = locate(user.eyeobj.x, user.eyeobj.y, user.eyeobj.z + dif) - cameranet.visibility(user.eyeobj) - else - return ..() -*/ - -/turf/move_camera_by_click() - if(istype(usr, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/AI = usr - if(AI.client.eye == AI.eyeobj) - return - return ..() - - -/obj/machinery/door/update_nearby_tiles(need_rebuild) - . = ..(need_rebuild) - cameranet.updateVisibility(loc) - -/obj/machinery/camera/New() - ..() - cameranet.addCamera(src) - -/obj/machinery/camera/Del() - cameranet.removeCamera(src) - ..() - -/obj/machinery/camera/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - . = ..(W, user) - if(istype(W, /obj/item/weapon/wirecutters)) - if(status) - cameranet.addCamera(src) - else - cameranet.removeCamera(src) - -/proc/checkcameravis(atom/A) - for(var/obj/machinery/camera/C in view(A,7)) - if(!C.status || C.stat == 2) - continue - return 1 - return 0 - -/mob/living/silicon/ai/attack_ai(var/mob/user as mob) - if (user != src) - return - - if (stat == 2) - return - - var/list/L = list() - for (var/obj/machinery/camera/C in world) - L.Add(C) - - camera_sort(L) - L = camera_network_sort(L) - - var/list/D = list() - for (var/obj/machinery/camera/C in L) - if ( C.network in src.networks ) - D[text("[]: [][]", C.network, C.c_tag, (C.status ? null : " (Deactivated)"))] = C - D["Cancel"] = "Cancel" - - var/t = input(user, "Which camera should you change to?") as null|anything in D - - if (!t || t == "Cancel") - return 0 - - var/obj/machinery/camera/C = D[t] - - eyeobj.loc = C.loc - cameranet.visibility(eyeobj) - - return - -/mob/living/silicon/ai/cancel_camera() - set name = "Cancel Camera View" - set category = "OOC" - reset_view(null) - machine = null - -/mob/living/silicon/ai/reset_view(atom/A) - if (client) - if(!eyeobj) - eyeobj = new() - eyeobj.ai = src - - client.eye = eyeobj - client.perspective = EYE_PERSPECTIVE - - if (istype(A, /atom/movable)) - eyeobj.loc = locate(A.x, A.y, A.z) - - else - eyeobj.loc = locate(src.x, src.y, src.z) - +//All credit for this goes to Uristqwerty. + +//And some to me! -Mini + + + +//This file is partly designed around being able to uninclude it to go back to the old ai viewing system completely. +//(And therefore also be portable to another similar codebase simply by transferring the file and including it after the other AI code files.) +//There are probably a few parts that don't do that at the moment, but I'll fix them at some point. + + +#define MINIMAP_UPDATE_DELAY 1200 + +/turf + var/image/obscured + var/image/dim + +/turf/proc/visibilityChanged() + cameranet.updateVisibility(src) + +/turf/New() + ..() + cameranet.updateVisibility(src) +/* +/turf/Del() + ..() + cameranet.updateVisibility(src) +*/ +/datum/camerachunk + var/list/obscuredTurfs = list() + var/list/visibleTurfs = list() + var/list/dimTurfs = list() + var/list/obscured = list() + var/list/dim = list() + var/list/cameras = list() + var/list/turfs = list() + var/list/seenby = list() + var/visible = 0 + var/changed = 1 + var/updating = 0 + + var/x + var/y + var/z + + var/minimap_updating = 0 + + var/icon/minimap_icon = new('minimap.dmi', "chunk_base") + var/obj/minimap_obj/minimap_obj = new() + +/obj/minimap_obj/Click(location, control, params) + if(!istype(usr, /mob/dead) && !istype(usr, /mob/living/silicon/ai) && !(usr.client && usr.client.holder && usr.client.holder.level >= 4)) + return + + var/list/par = params2list(params) + var/screen_loc = par["screen-loc"] + + if(findtext(screen_loc, "minimap:") != 1) + return + + screen_loc = copytext(screen_loc, length("minimap:") + 1) + + var/x_text = copytext(screen_loc, 1, findtext(screen_loc, ",")) + var/y_text = copytext(screen_loc, findtext(screen_loc, ",") + 1) + + var/x = (text2num(copytext(x_text, 1, findtext(x_text, ":"))) - 1) * 16 + x += round((text2num(copytext(x_text, findtext(x_text, ":") + 1)) + 1) / 2) + + var/y = (text2num(copytext(y_text, 1, findtext(y_text, ":"))) - 1) * 16 + y += round((text2num(copytext(y_text, findtext(y_text, ":") + 1)) + 1) / 2) + + if(istype(usr, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/ai = usr + ai.freelook() + ai.eyeobj.loc = locate(max(1, x - 1), max(1, y - 1), ai.eyeobj.z) + cameranet.visibility(ai.eyeobj) + + else + usr.loc = locate(max(1, x - 1), max(1, y - 1), usr.z) + +/mob/dead/verb/Open_Minimap() + set category = "Ghost" + winshow(src, "minimapwindow", 1) + client.screen |= cameranet.minimap + + if(cameranet.generating_minimap) + cameranet.minimap_viewers += src + +/mob/living/silicon/ai/verb/Open_Minimap() + set category = "AI Commands" + winshow(src, "minimapwindow", 1) + client.screen |= cameranet.minimap + + if(cameranet.generating_minimap) + cameranet.minimap_viewers += src + +/client/proc/Open_Minimap() + set category = "Admin" + winshow(src, "minimapwindow", 1) + screen |= cameranet.minimap + + if(cameranet.generating_minimap) + cameranet.minimap_viewers += src.mob + +/datum/camerachunk/proc/update_minimap() + if(changed && !updating) + update() + + minimap_icon.Blend(rgb(255, 0, 0), ICON_MULTIPLY) + + var/list/turfs = visibleTurfs | dimTurfs + + for(var/turf/turf in turfs) + var/x = (turf.x & 0xf) * 2 + var/y = (turf.y & 0xf) * 2 + + if(turf.density) + minimap_icon.DrawBox(rgb(100, 100, 100), x + 1, y + 1, x + 2, y + 2) + continue + + else if(istype(turf, /turf/space)) + minimap_icon.DrawBox(rgb(0, 0, 0), x + 1, y + 1, x + 2, y + 2) + + else + minimap_icon.DrawBox(rgb(200, 200, 200), x + 1, y + 1, x + 2, y + 2) + + for(var/obj/structure/o in turf) + if(o.density) + if(istype(o, /obj/structure/window) && (o.dir == NORTH || o.dir == SOUTH || o.dir == EAST || o.dir == WEST)) + if(o.dir == NORTH) + minimap_icon.DrawBox(rgb(150, 150, 200), x + 1, y + 2, x + 2, y + 2) + else if(o.dir == SOUTH) + minimap_icon.DrawBox(rgb(150, 150, 200), x + 1, y + 1, x + 2, y + 1) + else if(o.dir == EAST) + minimap_icon.DrawBox(rgb(150, 150, 200), x + 3, y + 1, x + 2, y + 2) + else if(o.dir == WEST) + minimap_icon.DrawBox(rgb(150, 150, 200), x + 1, y + 1, x + 1, y + 2) + + else + minimap_icon.DrawBox(rgb(150, 150, 150), x + 1, y + 1, x + 2, y + 2) + break + + for(var/obj/machinery/door/o in turf) + if(istype(o, /obj/machinery/door/window)) + if(o.dir == NORTH) + minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 2, x + 2, y + 2) + else if(o.dir == SOUTH) + minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 1, x + 2, y + 1) + else if(o.dir == EAST) + minimap_icon.DrawBox(rgb(100, 150, 100), x + 2, y + 1, x + 2, y + 2) + else if(o.dir == WEST) + minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 1, x + 1, y + 2) + + else + minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 1, x + 2, y + 2) + break + + minimap_obj.screen_loc = "minimap:[src.x / 16],[src.y / 16]" + minimap_obj.icon = minimap_icon + +/mob/aiEye + var/list/visibleCameraChunks = list() + var/mob/ai = null + density = 0 + +/datum/camerachunk/proc/add(mob/aiEye/ai) + ai.visibleCameraChunks += src + if(ai.ai.client) + ai.ai.client.images += obscured + ai.ai.client.images += dim + visible++ + seenby += ai + if(changed && !updating) + update() + changed = 0 + +/datum/camerachunk/proc/remove(mob/aiEye/ai) + ai.visibleCameraChunks -= src + if(ai.ai.client) + ai.ai.client.images -= obscured + ai.ai.client.images -= dim + seenby -= ai + if(visible > 0) + visible-- + +/datum/camerachunk/proc/visibilityChanged(turf/loc) + if(!(loc in visibleTurfs)) + return + + hasChanged() + +/datum/camerachunk/proc/hasChanged() + if(visible) + if(!updating) + updating = 1 + spawn(10)//Batch large changes, such as many doors opening or closing at once + update() + updating = 0 + else + changed = 1 + + if(!minimap_updating) + minimap_updating = 1 + + spawn(MINIMAP_UPDATE_DELAY) + if(changed && !updating) + update() + changed = 0 + + update_minimap() + minimap_updating = 0 + +/datum/camerachunk/proc/update() + + var/list/newDimTurfs = list() + var/list/newVisibleTurfs = list() + + for(var/obj/machinery/camera/c in cameras) + var/lum = c.luminosity + c.luminosity = 7 + + newDimTurfs |= turfs & view(7, c) + newVisibleTurfs |= turfs & view(6, c) + + c.luminosity = lum + + var/list/dimAdded = newDimTurfs - dimTurfs + var/list/dimRemoved = dimTurfs - newDimTurfs + var/list/visAdded = newVisibleTurfs - visibleTurfs + var/list/visRemoved = visibleTurfs - newVisibleTurfs + + visibleTurfs = newVisibleTurfs + dimTurfs = newDimTurfs + obscuredTurfs = turfs - dimTurfs + dimTurfs -= visibleTurfs + + for(var/turf/t in dimRemoved) + if(t.dim) + dim -= t.dim + for(var/mob/aiEye/m in seenby) + if(m.ai.client) + m.ai.client.images -= t.dim + + if(!(t in visibleTurfs)) + if(!t.obscured) + t.obscured = image('cameravis.dmi', t, "black", 15) + + obscured += t.obscured + for(var/mob/aiEye/m in seenby) + if(m.ai.client) + m.ai.client.images += t.obscured + + for(var/turf/t in dimAdded) + if(!(t in visibleTurfs)) + if(!t.dim) + t.dim = image('cameravis.dmi', t, "dim", 15) + t.mouse_opacity = 0 + + dim += t.dim + for(var/mob/aiEye/m in seenby) + if(m.ai.client) + m.ai.client.images += t.dim + + if(t.obscured) + obscured -= t.obscured + for(var/mob/aiEye/m in seenby) + if(m.ai.client) + m.ai.client.images -= t.obscured + + for(var/turf/t in visAdded) + if(t.obscured) + obscured -= t.obscured + for(var/mob/aiEye/m in seenby) + if(m.ai.client) + m.ai.client.images -= t.obscured + + for(var/turf/t in visRemoved) + if(t in obscuredTurfs) + if(!t.obscured) + t.obscured = image('cameravis.dmi', t, "black", 15) + + obscured += t.obscured + for(var/mob/aiEye/m in seenby) + if(m.ai.client) + m.ai.client.images += t.obscured + + +/datum/camerachunk/New(loc, x, y, z) + x &= ~0xf + y &= ~0xf + + src.x = x + src.y = y + src.z = z + + for(var/obj/machinery/camera/c in range(16, locate(x + 8, y + 8, z))) + if(c.status) + cameras += c + + turfs = block(locate(x, y, z), locate(min(world.maxx, x + 15), min(world.maxy, y + 15), z)) + + for(var/obj/machinery/camera/c in cameras) + var/lum = c.luminosity + c.luminosity = 7 + + dimTurfs |= turfs & view(7, c) + visibleTurfs |= turfs & view(6, c) + + c.luminosity = lum + + obscuredTurfs = turfs - dimTurfs + dimTurfs -= visibleTurfs + + for(var/turf/t in obscuredTurfs) + if(!t.obscured) + t.obscured = image('cameravis.dmi', t, "black", 15) + + obscured += t.obscured + + for(var/turf/t in dimTurfs) + if(!(t in visibleTurfs)) + if(!t.dim) + t.dim = image('cameravis.dmi', t, "dim", TURF_LAYER) + t.dim.mouse_opacity = 0 + + dim += t.dim + + cameranet.minimap += minimap_obj + +var/datum/cameranet/cameranet = new() + +/datum/cameranet + var/list/cameras = list() + var/list/chunks = list() + var/network = "net1" + var/ready = 0 + + var/list/minimap = list() + + var/generating_minimap = TRUE + var/list/minimap_viewers = list() + +/datum/cameranet/New() + ..() + + spawn(200) + for(var/x = 0, x <= world.maxx, x += 16) + for(var/y = 0, y <= world.maxy, y += 16) + sleep(1) + var/datum/camerachunk/c = getCameraChunk(x, y, 1) + c.update_minimap() + + for(var/mob/m in minimap_viewers) + m.client.screen |= c.minimap_obj + + generating_minimap = FALSE + minimap_viewers = list() + +/datum/cameranet/proc/chunkGenerated(x, y, z) + var/key = "[x],[y],[z]" + return key in chunks + +/datum/cameranet/proc/getCameraChunk(x, y, z) + var/key = "[x],[y],[z]" + + if(!(key in chunks)) + chunks[key] = new /datum/camerachunk(null, x, y, z) + + return chunks[key] + +/datum/cameranet/proc/visibility(mob/aiEye/ai) + var/x1 = max(0, ai.x - 16) & ~0xf + var/y1 = max(0, ai.y - 16) & ~0xf + var/x2 = min(world.maxx, ai.x + 16) & ~0xf + var/y2 = min(world.maxy, ai.y + 16) & ~0xf + + var/list/visibleChunks = list() + + for(var/x = x1; x <= x2; x += 16) + for(var/y = y1; y <= y2; y += 16) + visibleChunks += getCameraChunk(x, y, ai.z) + + var/list/remove = ai.visibleCameraChunks - visibleChunks + var/list/add = visibleChunks - ai.visibleCameraChunks + + for(var/datum/camerachunk/c in remove) + c.remove(ai) + + for(var/datum/camerachunk/c in add) + c.add(ai) + +/datum/cameranet/proc/updateVisibility(turf/loc) + if(!chunkGenerated(loc.x & ~0xf, loc.y & ~0xf, loc.z)) + return + + var/datum/camerachunk/chunk = getCameraChunk(loc.x & ~0xf, loc.y & ~0xf, loc.z) + chunk.visibilityChanged(loc) + +/datum/cameranet/proc/addCamera(obj/machinery/camera/c) + var/x1 = max(0, c.x - 16) & ~0xf + var/y1 = max(0, c.y - 16) & ~0xf + var/x2 = min(world.maxx, c.x + 16) & ~0xf + var/y2 = min(world.maxy, c.y + 16) & ~0xf + + for(var/x = x1; x <= x2; x += 16) + for(var/y = y1; y <= y2; y += 16) + if(chunkGenerated(x, y, c.z)) + var/datum/camerachunk/chunk = getCameraChunk(x, y, c.z) + if(!(c in chunk.cameras)) + chunk.cameras += c + chunk.hasChanged() + +/datum/cameranet/proc/removeCamera(obj/machinery/camera/c) + var/x1 = max(0, c.x - 16) & ~0xf + var/y1 = max(0, c.y - 16) & ~0xf + var/x2 = min(world.maxx, c.x + 16) & ~0xf + var/y2 = min(world.maxy, c.y + 16) & ~0xf + + for(var/x = x1; x <= x2; x += 16) + for(var/y = y1; y <= y2; y += 16) + if(chunkGenerated(x, y, c.z)) + var/datum/camerachunk/chunk = getCameraChunk(x, y, c.z) + if(!c) + chunk.hasChanged() + if(c in chunk.cameras) + chunk.cameras -= c + chunk.hasChanged() + +/mob/living/silicon/ai/var/mob/aiEye/eyeobj = new() + +/mob/living/silicon/ai/New() + ..() + eyeobj.ai = src + spawn(20) + freelook() + +/mob/living/silicon/ai/death(gibbed) + if(client && client.eye == eyeobj) + for(var/datum/camerachunk/c in eyeobj.visibleCameraChunks) + c.remove(eyeobj) + client.eye = src + return ..(gibbed) + +/mob/living/silicon/ai/verb/freelook() + set category = "AI Commands" + set name = "freelook" + current = null //cancel camera view first, it causes problems + cameraFollow = null +// machine = null + if(!eyeobj) //if it got deleted somehow (like an admin trying to fix things <.<') + eyeobj = new() + eyeobj.ai = src + client.eye = eyeobj + eyeobj.loc = loc + cameranet.visibility(eyeobj) + cameraFollow = null + +/mob/aiEye/Move() + . = ..() + if(.) + cameranet.visibility(src) + +/client/AIMove(n, direct, var/mob/living/silicon/ai/user) + if(eye == user.eyeobj) + user.eyeobj.loc = get_step(user.eyeobj, direct) + cameranet.visibility(user.eyeobj) + + else + return ..() + +/* +/client/AIMoveZ(direct, var/mob/living/silicon/ai/user) + if(eye == user.eyeobj) + var/dif = 0 + if(direct == UP && user.eyeobj.z > 1) + dif = -1 + else if(direct == DOWN && user.eyeobj.z < 4) + dif = 1 + user.eyeobj.loc = locate(user.eyeobj.x, user.eyeobj.y, user.eyeobj.z + dif) + cameranet.visibility(user.eyeobj) + else + return ..() +*/ + +/turf/move_camera_by_click() + if(istype(usr, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/AI = usr + if(AI.client.eye == AI.eyeobj) + return + return ..() + + +/obj/machinery/door/update_nearby_tiles(need_rebuild) + . = ..(need_rebuild) + cameranet.updateVisibility(loc) + +/obj/machinery/camera/New() + ..() + cameranet.addCamera(src) + +/obj/machinery/camera/Del() + cameranet.removeCamera(src) + ..() + +/obj/machinery/camera/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + . = ..(W, user) + if(istype(W, /obj/item/weapon/wirecutters)) + if(status) + cameranet.addCamera(src) + else + cameranet.removeCamera(src) + +/proc/checkcameravis(atom/A) + for(var/obj/machinery/camera/C in view(A,7)) + if(!C.status || C.stat == 2) + continue + return 1 + return 0 + +/mob/living/silicon/ai/attack_ai(var/mob/user as mob) + if (user != src) + return + + if (stat == 2) + return + + var/list/L = list() + for (var/obj/machinery/camera/C in world) + L.Add(C) + + camera_sort(L) + L = camera_network_sort(L) + + var/list/D = list() + for (var/obj/machinery/camera/C in L) + if ( C.network in src.networks ) + D[text("[]: [][]", C.network, C.c_tag, (C.status ? null : " (Deactivated)"))] = C + D["Cancel"] = "Cancel" + + var/t = input(user, "Which camera should you change to?") as null|anything in D + + if (!t || t == "Cancel") + return 0 + + var/obj/machinery/camera/C = D[t] + + eyeobj.loc = C.loc + cameranet.visibility(eyeobj) + + return + +/mob/living/silicon/ai/cancel_camera() + set name = "Cancel Camera View" + set category = "OOC" + reset_view(null) + machine = null + +/mob/living/silicon/ai/reset_view(atom/A) + if (client) + if(!eyeobj) + eyeobj = new() + eyeobj.ai = src + + client.eye = eyeobj + client.perspective = EYE_PERSPECTIVE + + if (istype(A, /atom/movable)) + eyeobj.loc = locate(A.x, A.y, A.z) + + else + eyeobj.loc = locate(src.x, src.y, src.z) + cameranet.visibility(eyeobj) diff --git a/code/WorkInProgress/AI_Visibility/ai.dm b/code/WorkInProgress/AI_Visibility/ai.dm new file mode 100644 index 0000000000..7dcbf54e67 --- /dev/null +++ b/code/WorkInProgress/AI_Visibility/ai.dm @@ -0,0 +1,107 @@ + +/mob/aiEye + var/list/visibleCameraChunks = list() + var/mob/ai = null + density = 0 + +/mob/living/silicon/ai/var/mob/aiEye/eyeobj = new() + +/mob/living/silicon/ai/New() + ..() + eyeobj.ai = src + spawn(20) + freelook() + +/mob/living/silicon/ai/death(gibbed) + if(client && client.eye == eyeobj) + for(var/datum/camerachunk/c in eyeobj.visibleCameraChunks) + c.remove(eyeobj) + client.eye = src + return ..(gibbed) + +/mob/living/silicon/ai/verb/freelook() + set category = "AI Commands" + set name = "freelook" + current = null //cancel camera view first, it causes problems + cameraFollow = null + if(!eyeobj) //if it got deleted somehow (like an admin trying to fix things <.<') + eyeobj = new() + eyeobj.ai = src + client.eye = eyeobj + eyeobj.loc = loc + cameranet.visibility(eyeobj) + +/mob/aiEye/Move() + . = ..() + if(.) + cameranet.visibility(src) + +/client/AIMove(n, direct, var/mob/living/silicon/ai/user) + if(eye == user.eyeobj) + user.eyeobj.loc = get_step(user.eyeobj, direct) + cameranet.visibility(user.eyeobj) + + else + return ..() + +/turf/move_camera_by_click() + if(istype(usr, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/AI = usr + if(AI.client.eye == AI.eyeobj) + return + return ..() + +/mob/living/silicon/ai/attack_ai(var/mob/user as mob) + if (user != src) + return + + if (stat == 2) + return + + var/list/L = list() + for (var/obj/machinery/camera/C in world) + L.Add(C) + + camera_sort(L) + L = camera_network_sort(L) + + var/list/D = list() + for (var/obj/machinery/camera/C in L) + if ( C.network in src.networks ) + D[text("[]: [][]", C.network, C.c_tag, (C.status ? null : " (Deactivated)"))] = C + D["Cancel"] = "Cancel" + + var/t = input(user, "Which camera should you change to?") as null|anything in D + + if (!t || t == "Cancel") + return 0 + + var/obj/machinery/camera/C = D[t] + + eyeobj.loc = C.loc + cameranet.visibility(eyeobj) + + return + +/mob/living/silicon/ai/cancel_camera() + set name = "Cancel Camera View" + set category = "OOC" + reset_view(null) + machine = null + +/mob/living/silicon/ai/reset_view(atom/A) + if (client) + if(!eyeobj) + eyeobj = new() + eyeobj.ai = src + + client.eye = eyeobj + client.perspective = EYE_PERSPECTIVE + + if (istype(A, /atom/movable)) + eyeobj.loc = locate(A.x, A.y, A.z) + + else + eyeobj.loc = locate(src.x, src.y, src.z) + + cameranet.visibility(eyeobj) diff --git a/code/WorkInProgress/AI_Visibility/cameranet.dm b/code/WorkInProgress/AI_Visibility/cameranet.dm new file mode 100644 index 0000000000..3b46844e6b --- /dev/null +++ b/code/WorkInProgress/AI_Visibility/cameranet.dm @@ -0,0 +1,156 @@ +//------------------------------------------------------------ +// +// The Cameranet +// +// The cameranet is a single global instance of a unique +// datum, which contains logic for managing the individual +// chunks. +// +//------------------------------------------------------------ + +/datum/cameranet + var/list/cameras = list() + var/list/chunks = list() + var/network = "net1" + var/ready = 0 + + var/list/minimap = list() + + var/generating_minimap = TRUE + +var/datum/cameranet/cameranet = new() + + + +/datum/cameranet/New() + ..() + + spawn(100) + init_minimap() + + +/datum/cameranet/proc/init_minimap() + for(var/x = 0, x <= world.maxx, x += 16) + for(var/y = 0, y <= world.maxy, y += 16) + sleep(1) + getCameraChunk(x, y, 5) + getCameraChunk(x, y, 1) + + generating_minimap = FALSE + + +/datum/cameranet/proc/chunkGenerated(x, y, z) + var/key = "[x],[y],[z]" + return key in chunks + + +/datum/cameranet/proc/getCameraChunk(x, y, z) + var/key = "[x],[y],[z]" + + if(!(key in chunks)) + chunks[key] = new /datum/camerachunk(null, x, y, z) + + return chunks[key] + + + + +// This proc updates what chunks are considered seen +// by an aiEye. As part of the process, it will force +// any newly visible chunks with pending unscheduled +// updates to update, and show the correct obscuring +// and dimming image sets. If you do not call this +// after the eye has moved, it may result in the +// affected AI gaining (partial) xray, seeing through +// now-closed doors, not seeing through open doors, +// or other visibility oddities, depending on if/when +// they last visited any of the chunks in the nearby +// area. + +// It must be called manually, as there is no way to +// have a proc called automatically every time an +// object's loc changes. + +/datum/cameranet/proc/visibility(mob/aiEye/ai) + var/x1 = max(0, ai.x - 16) & ~0xf + var/y1 = max(0, ai.y - 16) & ~0xf + var/x2 = min(world.maxx, ai.x + 16) & ~0xf + var/y2 = min(world.maxy, ai.y + 16) & ~0xf + + var/list/visibleChunks = list() + + for(var/x = x1; x <= x2; x += 16) + for(var/y = y1; y <= y2; y += 16) + visibleChunks += getCameraChunk(x, y, ai.z) + + var/list/remove = ai.visibleCameraChunks - visibleChunks + var/list/add = visibleChunks - ai.visibleCameraChunks + + for(var/datum/camerachunk/c in remove) + c.remove(ai) + + for(var/datum/camerachunk/c in add) + c.add(ai) + + + + +// This proc should be called if a turf, or the contents +// of a turf, changes opacity. This includes such things +// as changing the turf, opening or closing a door, or +// anything else that would alter line of sight in the +// general area. + +/datum/cameranet/proc/updateVisibility(turf/loc) + if(!chunkGenerated(loc.x & ~0xf, loc.y & ~0xf, loc.z)) + return + + var/datum/camerachunk/chunk = getCameraChunk(loc.x & ~0xf, loc.y & ~0xf, loc.z) + chunk.visibilityChanged(loc) + + + + +// This proc updates all relevant chunks when enabling or +// creating a camera, allowing freelook and the minimap to +// respond correctly. + +/datum/cameranet/proc/addCamera(obj/machinery/camera/c) + var/x1 = max(0, c.x - 16) & ~0xf + var/y1 = max(0, c.y - 16) & ~0xf + var/x2 = min(world.maxx, c.x + 16) & ~0xf + var/y2 = min(world.maxy, c.y + 16) & ~0xf + + for(var/x = x1; x <= x2; x += 16) + for(var/y = y1; y <= y2; y += 16) + if(chunkGenerated(x, y, c.z)) + var/datum/camerachunk/chunk = getCameraChunk(x, y, c.z) + + if(!(c in chunk.cameras)) + chunk.cameras += c + chunk.hasChanged() + + + + +// This proc updates all relevant chunks when disabling or +// deleting a camera, allowing freelook and the minimap to +// respond correctly. + +/datum/cameranet/proc/removeCamera(obj/machinery/camera/c) + var/x1 = max(0, c.x - 16) & ~0xf + var/y1 = max(0, c.y - 16) & ~0xf + var/x2 = min(world.maxx, c.x + 16) & ~0xf + var/y2 = min(world.maxy, c.y + 16) & ~0xf + + for(var/x = x1; x <= x2; x += 16) + for(var/y = y1; y <= y2; y += 16) + if(chunkGenerated(x, y, c.z)) + var/datum/camerachunk/chunk = getCameraChunk(x, y, c.z) + + if(!c) + chunk.hasChanged() + + if(c in chunk.cameras) + chunk.cameras -= c + chunk.hasChanged() diff --git a/code/WorkInProgress/AI_Visibility/chunk.dm b/code/WorkInProgress/AI_Visibility/chunk.dm new file mode 100644 index 0000000000..73306facb4 --- /dev/null +++ b/code/WorkInProgress/AI_Visibility/chunk.dm @@ -0,0 +1,221 @@ +#define MINIMAP_UPDATE_DELAY 1200 + +/datum/camerachunk + var/list/turfs = list() + + var/list/obscuredTurfs = list() + var/list/visibleTurfs = list() + var/list/dimTurfs = list() + + var/list/obscured = list() + var/list/dim = list() + + var/list/cameras = list() + var/list/seenby = list() + + var/changed = 1 + var/updating = 0 + var/minimap_updating = 0 + + var/x + var/y + var/z + + + var/icon/minimap_icon = new('minimap.dmi', "chunk_base") + var/obj/minimap_obj/minimap_obj = new() + + + +/datum/camerachunk/New(loc, x, y, z) + //Round X and Y down to a multiple of 16, if nessecary + src.x = x & ~0xF + src.y = y & ~0xF + src.z = z + + rebuild_chunk() + + + +// Completely re-calculate the whole chunk. + +/datum/camerachunk/proc/rebuild_chunk() + for(var/mob/aiEye/eye in seenby) + if(!eye.ai) + seenby -= eye + continue + + if(eye.ai.client) + eye.ai.client.images -= obscured + eye.ai.client.images -= dim + + var/start = locate(x, y, z) + var/end = locate(min(x + 15, world.maxx), min(y + 15, world.maxy), z) + + turfs = block(start, end) + dimTurfs = list() + visibleTurfs = list() + obscured = list() + dim = list() + cameras = list() + + for(var/obj/machinery/camera/c in range(16, locate(x + 8, y + 8, z))) + if(c.status) + cameras += c + + for(var/obj/machinery/camera/c in cameras) + var/lum = c.luminosity + c.luminosity = 7 + + dimTurfs |= turfs & view(7, c) + visibleTurfs |= turfs & view(6, c) + + c.luminosity = lum + + obscuredTurfs = turfs - dimTurfs + dimTurfs -= visibleTurfs + + for(var/turf/t in obscuredTurfs) + if(!t.obscured) + t.obscured = image('cameravis.dmi', t, "black", 15) + + obscured += t.obscured + + for(var/turf/t in dimTurfs) + if(!t.dim) + t.dim = image('cameravis.dmi', t, "dim", TURF_LAYER) + t.dim.mouse_opacity = 0 + + dim += t.dim + + cameranet.minimap |= minimap_obj + + for(var/mob/aiEye/eye in seenby) + if(eye.ai.client) + eye.ai.client.images |= obscured + eye.ai.client.images |= dim + + + +/datum/camerachunk/proc/add(mob/aiEye/eye) + eye.visibleCameraChunks |= src + + if(eye.ai.client) + eye.ai.client.images |= obscured + eye.ai.client.images |= dim + + seenby |= eye + + if(changed && !updating) + update() + changed = 0 + + + +/datum/camerachunk/proc/remove(mob/aiEye/eye) + eye.visibleCameraChunks -= src + + if(eye.ai.client) + eye.ai.client.images -= obscured + eye.ai.client.images -= dim + + seenby -= eye + +/datum/camerachunk/proc/visibilityChanged(turf/loc) + if(!(loc in visibleTurfs)) + return + + hasChanged() + +/datum/camerachunk/proc/hasChanged() + if(length(seenby) > 0) + if(!updating) + updating = 1 + + spawn(10)//Batch large changes, such as many doors opening or closing at once + update() + updating = 0 + + else + changed = 1 + + if(!minimap_updating) + minimap_updating = 1 + + spawn(MINIMAP_UPDATE_DELAY) + if(changed && !updating) + update() + changed = 0 + + update_minimap() + minimap_updating = 0 + +/datum/camerachunk/proc/update() + + var/list/newDimTurfs = list() + var/list/newVisibleTurfs = list() + + for(var/obj/machinery/camera/c in cameras) + var/lum = c.luminosity + c.luminosity = 7 + + newDimTurfs |= turfs & view(7, c) + newVisibleTurfs |= turfs & view(6, c) + + c.luminosity = lum + + var/list/dimAdded = newDimTurfs - dimTurfs + var/list/dimRemoved = dimTurfs - newDimTurfs + var/list/visAdded = newVisibleTurfs - visibleTurfs + var/list/visRemoved = visibleTurfs - newVisibleTurfs + + visibleTurfs = newVisibleTurfs + dimTurfs = newDimTurfs + obscuredTurfs = turfs - dimTurfs + dimTurfs -= visibleTurfs + + var/list/images_added = list() + var/list/images_removed = list() + + for(var/turf/t in dimRemoved) + if(t.dim) + dim -= t.dim + images_removed += t.dim + + if(!(t in visibleTurfs)) + if(!t.obscured) + t.obscured = image('cameravis.dmi', t, "black", 15) + + obscured += t.obscured + images_added += t.obscured + + for(var/turf/t in dimAdded) + if(!(t in visibleTurfs)) + if(!t.dim) + t.dim = image('cameravis.dmi', t, "dim", 15) + t.mouse_opacity = 0 + + dim += t.dim + images_added += t.dim + + if(t.obscured) + obscured -= t.obscured + images_removed += t.obscured + + for(var/turf/t in visAdded) + if(t.obscured) + obscured -= t.obscured + images_removed += t.obscured + + for(var/turf/t in visRemoved) + if(t in obscuredTurfs) + if(!t.obscured) + t.obscured = image('cameravis.dmi', t, "black", 15) + + obscured += t.obscured + images_added += t.obscured + + for(var/mob/aiEye/eye in seenby) + if(eye.ai.client) + eye.ai.client.images -= images_removed + eye.ai.client.images |= images_added diff --git a/code/WorkInProgress/AI_Visibility/minimap.dm b/code/WorkInProgress/AI_Visibility/minimap.dm new file mode 100644 index 0000000000..c97b01e0f8 --- /dev/null +++ b/code/WorkInProgress/AI_Visibility/minimap.dm @@ -0,0 +1,137 @@ +/client/var/minimap_view_z = 1 + +/obj/minimap_obj + var/datum/camerachunk/chunk + +/obj/minimap_obj/Click(location, control, params) + if(!istype(usr, /mob/dead) && !istype(usr, /mob/living/silicon/ai) && !(usr.client && usr.client.holder && usr.client.holder.level >= 4)) + return + + var/list/par = params2list(params) + var/screen_loc = par["screen-loc"] + + if(findtext(screen_loc, "minimap:") != 1) + return + + screen_loc = copytext(screen_loc, length("minimap:") + 1) + + var/x_text = copytext(screen_loc, 1, findtext(screen_loc, ",")) + var/y_text = copytext(screen_loc, findtext(screen_loc, ",") + 1) + + var/x = chunk.x + x += round((text2num(copytext(x_text, findtext(x_text, ":") + 1)) + 1) / 2) + + var/y = chunk.y + y += round((text2num(copytext(y_text, findtext(y_text, ":") + 1)) + 1) / 2) + + if(istype(usr, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/ai = usr + ai.freelook() + ai.eyeobj.loc = locate(max(1, x - 1), max(1, y - 1), usr.client.minimap_view_z) + cameranet.visibility(ai.eyeobj) + + else + usr.loc = locate(max(1, x - 1), max(1, y - 1), usr.client.minimap_view_z) + +/mob/dead/verb/Open_Minimap() + set category = "Ghost" + cameranet.show_minimap(client) + + +/mob/living/silicon/ai/verb/Open_Minimap() + set category = "AI Commands" + cameranet.show_minimap(client) + + +/client/proc/Open_Minimap() + set category = "Admin" + cameranet.show_minimap(src) + + +/mob/verb/Open_Minimap_Z() + set hidden = 1 + + if(!istype(src, /mob/dead) && !istype(src, /mob/living/silicon/ai) && !(client && client.holder && client.holder.level >= 4)) + return + + var/level = input("Select a Z level", "Z select", null) as null | anything in cameranet.minimap + + if(level != null) + cameranet.show_minimap(client, level) + + + +/datum/cameranet/proc/show_minimap(client/client, z_level = "z-1") + if(!istype(client.mob, /mob/dead) && !istype(client.mob, /mob/living/silicon/ai) && !(client.holder && client.holder.level >= 4)) + return + + if(z_level in cameranet.minimap) + winshow(client, "minimapwindow", 1) + + for(var/key in cameranet.minimap) + client.screen -= cameranet.minimap[key] + + client.screen |= cameranet.minimap[z_level] + + if(cameranet.generating_minimap) + spawn(50) + show_minimap(client, z_level) + + client.minimap_view_z = text2num(copytext(z_level, 3)) + + +/datum/camerachunk/proc/update_minimap() + if(changed && !updating) + update() + + minimap_icon.Blend(rgb(255, 0, 0), ICON_MULTIPLY) + + var/list/turfs = visibleTurfs | dimTurfs + + for(var/turf/turf in turfs) + var/x = (turf.x & 0xf) * 2 + var/y = (turf.y & 0xf) * 2 + + if(turf.density) + minimap_icon.DrawBox(rgb(100, 100, 100), x + 1, y + 1, x + 2, y + 2) + continue + + else if(istype(turf, /turf/space)) + minimap_icon.DrawBox(rgb(0, 0, 0), x + 1, y + 1, x + 2, y + 2) + + else + minimap_icon.DrawBox(rgb(200, 200, 200), x + 1, y + 1, x + 2, y + 2) + + for(var/obj/structure/o in turf) + if(o.density) + if(istype(o, /obj/structure/window) && (o.dir == NORTH || o.dir == SOUTH || o.dir == EAST || o.dir == WEST)) + if(o.dir == NORTH) + minimap_icon.DrawBox(rgb(150, 150, 200), x + 1, y + 2, x + 2, y + 2) + else if(o.dir == SOUTH) + minimap_icon.DrawBox(rgb(150, 150, 200), x + 1, y + 1, x + 2, y + 1) + else if(o.dir == EAST) + minimap_icon.DrawBox(rgb(150, 150, 200), x + 3, y + 1, x + 2, y + 2) + else if(o.dir == WEST) + minimap_icon.DrawBox(rgb(150, 150, 200), x + 1, y + 1, x + 1, y + 2) + + else + minimap_icon.DrawBox(rgb(150, 150, 150), x + 1, y + 1, x + 2, y + 2) + break + + for(var/obj/machinery/door/o in turf) + if(istype(o, /obj/machinery/door/window)) + if(o.dir == NORTH) + minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 2, x + 2, y + 2) + else if(o.dir == SOUTH) + minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 1, x + 2, y + 1) + else if(o.dir == EAST) + minimap_icon.DrawBox(rgb(100, 150, 100), x + 2, y + 1, x + 2, y + 2) + else if(o.dir == WEST) + minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 1, x + 1, y + 2) + + else + minimap_icon.DrawBox(rgb(100, 150, 100), x + 1, y + 1, x + 2, y + 2) + break + + minimap_obj.screen_loc = "minimap:[src.x / 16],[src.y / 16]" + minimap_obj.icon = minimap_icon diff --git a/code/WorkInProgress/AI_Visibility/util.dm b/code/WorkInProgress/AI_Visibility/util.dm new file mode 100644 index 0000000000..da576cbd40 --- /dev/null +++ b/code/WorkInProgress/AI_Visibility/util.dm @@ -0,0 +1,38 @@ + +/turf + var/image/obscured + var/image/dim + +/turf/proc/visibilityChanged() + cameranet.updateVisibility(src) + +/turf/New() + ..() + cameranet.updateVisibility(src) + +/obj/machinery/door/update_nearby_tiles(need_rebuild) + . = ..(need_rebuild) + cameranet.updateVisibility(loc) + +/obj/machinery/camera/New() + ..() + cameranet.addCamera(src) + +/obj/machinery/camera/Del() + cameranet.removeCamera(src) + ..() + +/obj/machinery/camera/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + . = ..(W, user) + if(istype(W, /obj/item/weapon/wirecutters)) + if(status) + cameranet.addCamera(src) + else + cameranet.removeCamera(src) + +/proc/checkcameravis(atom/A) + for(var/obj/machinery/camera/C in view(A,7)) + if(!C.status || C.stat == 2) + continue + return 1 + return 0 diff --git a/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm b/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm index 321b835209..ad6a754ba7 100644 --- a/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm +++ b/code/WorkInProgress/Cael_Aislinn/Rust/core_field.dm @@ -131,7 +131,7 @@ Deuterium-tritium fusion: 4.5 x 10^7 K radiation = 0 //update values - var/transfer_ratio = 50 / field_strength + var/transfer_ratio = field_strength / 50 major_radius = field_strength * 0.21875// max = 8.75 minor_radius = field_strength * 0.2125// max = 8.625 volume_covered = PI * major_radius * minor_radius * 2.5 * 2.5 * 2.5 * 7 * 7 * transfer_ratio @@ -159,10 +159,12 @@ Deuterium-tritium fusion: 4.5 x 10^7 K var/datum/gas_mixture/plasma_captured = new /datum/gas_mixture() // plasma_captured.toxins = round(gas_covered.toxins * transfer_ratio) + //world << "\blue[plasma_captured.toxins] moles of plasma captured" plasma_captured.temperature = gas_covered.temperature + //plasma_captured.update_values() gas_covered.toxins -= plasma_captured.toxins - plasma_captured.update_values() - gas_covered.update_values() + //gas_covered.update_values() + // held_plasma.merge(plasma_captured) // environment.merge(gas_covered) @@ -187,7 +189,25 @@ Deuterium-tritium fusion: 4.5 x 10^7 K //if there is too much plasma in the field, lose some /*if( held_plasma.toxins > (MOLES_CELLSTANDARD * 7) * (50 / field_strength) ) LosePlasma()*/ - LosePlasma() + if(held_plasma.toxins > 1) + //lose a random amount of plasma back into the air, increased by the field strength (want to switch this over to frequency eventually) + var/loss_ratio = rand() * (0.05 + (0.05 * 50 / field_strength)) + //world << "lost [loss_ratio*100]% of held plasma" + // + var/datum/gas_mixture/plasma_lost = new + plasma_lost.temperature = held_plasma.temperature + // + plasma_lost.toxins = held_plasma.toxins * loss_ratio + //plasma_lost.update_values() + held_plasma.toxins -= held_plasma.toxins * loss_ratio + //held_plasma.update_values() + // + environment.merge(plasma_lost) + radiation += loss_ratio * mega_energy * 0.1 + mega_energy -= loss_ratio * mega_energy * 0.1 + else + held_plasma.toxins = 0 + //held_plasma.update_values() //handle some reactants formatting //helium-4 has no use at the moment, but a buttload of it is produced @@ -293,30 +313,6 @@ Deuterium-tritium fusion: 4.5 x 10^7 K catcher.UpdateSize() return changed - proc/LosePlasma() - if(held_plasma.toxins > 1) - //lose a random amount of plasma back into the air, increased by the field strength (want to switch this over to frequency eventually) - var/datum/gas_mixture/environment = loc.return_air() - var/loss_ratio = rand() * (0.05 + (0.05 * 50 / field_strength)) - //world << "lost [loss_ratio*100]% of held plasma" - // - var/datum/gas_mixture/plasma_lost = new - plasma_lost.temperature = held_plasma.temperature - // - plasma_lost.toxins = held_plasma.toxins * loss_ratio - plasma_lost.update_values() - held_plasma.toxins -= held_plasma.toxins * loss_ratio - held_plasma.update_values() - // - environment.merge(plasma_lost) - radiation += loss_ratio * mega_energy * 0.1 - mega_energy -= loss_ratio * mega_energy * 0.1 - return 1 - else - held_plasma.toxins = 0 - held_plasma.update_values() - return 0 - //the !!fun!! part //reactions have to be individually hardcoded, see AttemptReaction() below this proc/React() diff --git a/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm b/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm index 91c35089c2..da06f39cca 100644 --- a/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm +++ b/code/WorkInProgress/Cael_Aislinn/Rust/fuel_injector.dm @@ -104,11 +104,11 @@ user.machine = null user << browse(null, "window=fuel_injector") return - var/t = "Reactor Core Fuel Injector
" + var/t = "Reactor Core Fuel Injector
" t += "Stage: [stage]
" t += "Status: [injecting ? "Active \[Disable\]" : "Standby \[Enable\]"]
" - t += "Interval (sec): [rate/10] \[Modify\]" - t += "Fuel usage: [fuel_usage*100]% \[Modify\]" + t += "Interval (sec): [rate/10] \[Modify\]
" + t += "Fuel usage: [fuel_usage*100]% \[Modify\]
" /* var/t = "Reactor Core Fuel Control
" t += "Current fuel injection stage: [active_stage]
" @@ -146,6 +146,7 @@ t += "" t += "" */ + t += "
" t += "Close
" user << browse(t, "window=fuel_injector;size=500x800") user.machine = src @@ -178,26 +179,21 @@ for(var/reagent in owned_assembly_port.cur_assembly.rod_quantities) //world << "checking [reagent]" if(owned_assembly_port.cur_assembly.rod_quantities[reagent] > 0) - //world << " rods left: [owned_assembly_port.cur_assembly.rod_quantities[reagent]]] + //world << " rods left: [owned_assembly_port.cur_assembly.rod_quantities[reagent]]" var/amount = owned_assembly_port.cur_assembly.rod_quantities[reagent] * fuel_usage var/numparticles = round(amount * 1000) if(numparticles < 1) numparticles = 1 //world << " amount: [amount]" //world << " numparticles: [numparticles]" - for(var/i=0, iYou are now a Tajara." spawn(0)//To prevent the proc from returning null. del(src) diff --git a/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dm b/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dm index de50debbcf..1f80c190dd 100644 --- a/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dm +++ b/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dm @@ -9,7 +9,7 @@ universal_speak = 1 //hacky fix until someone can figure out how to make them only understand humans taj_talk_understand = 1 voice_message = "mrowls" - examine_text = "one of the cat-like Tajarans." + examine_text = "one of the cat-like Tajarans" /mob/living/carbon/human/tajaran/New() var/g = "m" @@ -28,7 +28,7 @@ ..() /mob/living/carbon/human/tajaran/update_clothing() - ..() +// ..() if (monkeyizing) return @@ -589,94 +589,3 @@ /mob/living/carbon/human/tajaran/co2overloadtime = null /mob/living/carbon/human/tajaran/temperature_resistance = T0C+70 - - -/* //This is silly. -- Erthilo -/mob/living/carbon/human/tajaran/Emissary/ - unacidable = 1 - var/aegis = 1 - -/mob/living/carbon/human/tajaran/Emissary/New() - - ..() - - reagents.add_reagent("hyperzine", 5000) //From the dark, to the light, it's a supersonic flight! - // Gotta keep it going! - if (!(mutations & HULK)) - mutations |= HULK - - if (!(mutations & LASER)) - mutations |= LASER - - if (!(mutations & XRAY)) - mutations |= XRAY - sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS) - see_in_dark = 8 - see_invisible = 2 - - if (!(mutations & COLD_RESISTANCE)) - mutations |= COLD_RESISTANCE - - if (!(mutations & TK)) - mutations |= TK - - if(!(mutations & HEAL)) - mutations |= HEAL - - spawn(0) - while(src) - adjustBruteLoss(-10) - adjustToxLoss(-10) - adjustOxyLoss(-10) - adjustFireLoss(-10) - sleep(10) - - -/mob/living/carbon/human/tajaran/Emissary/ex_act() - return - -/mob/living/carbon/human/tajaran/Emissary/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, inrange, params) - if(istype(target , /obj/machinery/door/airlock)) - if(target:locked) - target:locked = 0 - if(!target:density) - return 1 - if(target:operating > 0) - return - if(!ticker) - return 0 - if(!target:operating) - target:operating = 1 - - target:animate("opening") - target:sd_SetOpacity(0) - sleep(10) - target:layer = 2.7 - target:density = 0 - target:update_icon() - target:sd_SetOpacity(0) - target:update_nearby_tiles() - - target:operating = -1 - - user << "You force the door open, shearing the bolts and burning out the motor." - - if(target:operating) - target:operating = -1 - else if(istype(target , /obj/machinery/door/firedoor)) - target:open() - -/mob/living/carbon/human/tajaran/Emissary/Life() - - ..() - - if (!(mutations & HULK)) - mutations |= HULK - - - if((stat == 2) && aegis) - src.show_message("\red [src]'s eyes open suddenlly.", 3, "\red \"I gave a solemn vow to never die for long.\"", 2) - src.heal_overall_damage(9001,9001) - src.stat = 0 - aegis = 0 -*/ \ No newline at end of file diff --git a/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dmi b/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dmi index 5cb5c4b5c0..6e69669647 100644 Binary files a/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dmi and b/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dmi differ diff --git a/code/WorkInProgress/Cael_Aislinn/power_monitor.dm b/code/WorkInProgress/Cael_Aislinn/power_monitor.dm new file mode 100644 index 0000000000..b044e174eb --- /dev/null +++ b/code/WorkInProgress/Cael_Aislinn/power_monitor.dm @@ -0,0 +1,226 @@ +//links to a power monitor computer and transmits the amount of energy in the associated powercable network +//uses the navbeacon sprite and transmits data via magic for now + +/obj/machinery/powermonitor + + icon = 'objects.dmi' + icon_state = "navbeacon0-f" + name = "power monitor" + desc = "A monitoring device used to track power in a cable network." + level = 1 // underfloor + layer = 2.5 + anchored = 1 + + var/freq = 1427 // radio frequency + var/powernet_tag = "" //the text tag associated with this power monitor's network + var/open = 0 // true if cover is open + var/locked = 1 // true if controls are locked + var/list/codes // assoc. list of transponder codes + var/codes_txt = "" // codes as set on map: "tag1;tag2" or "tag1=value;tag2=value" + + req_access = list(access_engine) + + New() + ..() + var/turf/T = loc + hide(T.intact) + + set_codes() + + spawn(5) // must wait for map loading to finish + if(radio_controller) + radio_controller.add_object(src, freq, RADIO_POWER) + + // set the transponder codes assoc list from codes_txt + proc/set_codes() + if(!codes_txt) + return + + codes = new() + + var/list/entries = dd_text2List(codes_txt, ";") // entries are separated by semicolons + + for(var/e in entries) + var/index = findtext(e, "=") // format is "key=value" + if(index) + var/key = copytext(e, 1, index) + var/val = copytext(e, index+1) + codes[key] = val + else + codes[e] = "1" + + // called when turf state changes + // hide the object if turf is intact + hide(var/intact) + invisibility = intact ? 101 : 0 + updateicon() + + // update the icon_state + proc/updateicon() + var/state="navbeacon[open]" + if(invisibility) + icon_state = "[state]-f" // if invisible, set icon to faded version + // in case revealed by T-scanner + else + icon_state = "[state]" + + // look for a signal of the form "getpowerlevel" + // where X is any + // or the location + // or one of the set transponder keys + // if found, return a signal + receive_signal(datum/signal/signal) + + var/request = signal.data["getpowermonitor"] + if(request && (request == "any" || request == powernet_tag) ) + spawn(1) + post_signal() + + // return a signal giving the power network energy level + proc/post_signal() + + var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) + + if(!frequency) return + + var/datum/signal/signal = new() + signal.source = src + signal.transmission_method = 1 + signal.data["powerlevel"] = 1 //TODO + + for(var/key in codes) + signal.data[key] = codes[key] + + frequency.post_signal(src, signal, filter = RADIO_POWER) + + attackby(var/obj/item/I, var/mob/user) + var/turf/T = loc + if(T.intact) + return // prevent intraction when T-scanner revealed + + if(istype(I, /obj/item/weapon/screwdriver)) + open = !open + + user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") + + updateicon() + + else if (istype(I, /obj/item/weapon/card/id)||istype(I, /obj/item/device/pda)) + if(open) + if (src.allowed(user)) + src.locked = !src.locked + user << "Controls are now [src.locked ? "locked." : "unlocked."]" + else + user << "\red Access denied." + updateDialog() + else + user << "You must open the cover first!" + return + + attack_ai(var/mob/user) + interact(user, 1) + + attack_paw() + return + + attack_hand(var/mob/user) + interact(user, 0) + + proc/interact(var/mob/user, var/ai = 0) + var/turf/T = loc + if(T.intact) + return // prevent intraction when T-scanner revealed + + if(!open && !ai) // can't alter controls if not open, unless you're an AI + user << "The monitor's control cover is closed." + return + + var/dat = "Navigation Beacon
" + if(locked && !ai) + dat += "(swipe card to unlock controls)
" + else if(!ai) + dat += "(swipe card to lock controls)
" + + dat += "Frequency: " + if(!locked) + dat += "-" + dat += "-" + dat += "[format_frequency(freq)]" + if(!locked) + dat += "+" + dat += "+" + dat += "
" + + dat += "Power network tag: [powernet_tag ? powernet_tag : "(none)"]
" + dat += "Transponder Codes: