diff --git a/code/ATMOSPHERICS/components/unary_devices/cryo.dm b/code/ATMOSPHERICS/components/unary_devices/cryo.dm index 5edaa823b99..ac816f3109b 100644 --- a/code/ATMOSPHERICS/components/unary_devices/cryo.dm +++ b/code/ATMOSPHERICS/components/unary_devices/cryo.dm @@ -42,7 +42,7 @@ T.contents += contents if(beaker) - beaker.loc = get_step(loc, SOUTH) //Beaker is carefully ejected from the wreckage of the cryotube + beaker.loc = get_step(loc, SOUTH) //Beaker is carefully fed from the wreckage of the cryotube beaker = null return ..() /obj/machinery/atmospherics/components/unary/cryo_cell/process_atmos() @@ -203,22 +203,18 @@ if(!state_open) on = 1 - if(href_list["open"]) - open_machine() - - if(href_list["close"]) - if(close_machine() == usr) - var/datum/nanoui/ui = SSnano.get_open_ui(usr, src, "main") - ui.close() - on = 1 if(href_list["switchOff"]) on = 0 + if(href_list["ejectOccupant"]) + open_machine() + if(href_list["ejectBeaker"]) if(beaker) var/obj/item/weapon/reagent_containers/glass/B = beaker B.loc = get_step(loc, SOUTH) beaker = null + update_icon() add_fingerprint(usr) return 1 // update UIs attached to this object diff --git a/code/__DEFINES/nano.dm b/code/__DEFINES/nano.dm new file mode 100644 index 00000000000..5f53a327fde --- /dev/null +++ b/code/__DEFINES/nano.dm @@ -0,0 +1,4 @@ +#define NANO_INTERACTIVE 2 // GREEN Visability +#define NANO_UPDATE 1 // ORANGE Visability +#define NANO_DISABLED 0 // RED Visability +#define NANO_CLOSE -1 // Close the interface \ No newline at end of file diff --git a/code/controllers/subsystem/nanoUI.dm b/code/controllers/subsystem/nano.dm similarity index 100% rename from code/controllers/subsystem/nanoUI.dm rename to code/controllers/subsystem/nano.dm diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index d1a86f5d3ee..bbf22fb3cf1 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -202,7 +202,7 @@ if(stat & (BROKEN|NOPOWER)) return - ui = SSnano.push_open_or_new_ui(user, src, ui_key, ui, "air_alarm.tmpl", "Air Alarm", 350, 500, 1) + ui = SSnano.push_open_or_new_ui(user, src, ui_key, ui, "air_alarm.tmpl", "Air Alarm", 440, 600, 1) /obj/machinery/alarm/get_ui_data(mob/user) var/data = list() diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 2077fd65399..c7efc862009 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -795,6 +795,7 @@ var/list/ai_list = list() if(stat == 2) return //won't work if dead + src << "Accessing Subspace Transceiver control..." if (radio) radio.interact(src) diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index cb5851e1012..93e1147d746 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -49,7 +49,7 @@ loc = T.loc if (istype(loc, /area)) //stage = 4 - if (!loc.master.power_equip && !is_type_in_list(src.loc,list(/obj/item, /obj/mecha))) + if (lacks_power()) //stage = 5 blindness = 1 @@ -92,7 +92,7 @@ src.see_in_dark = 0 src.see_invisible = SEE_INVISIBLE_LIVING - if (((!loc.master.power_equip) || istype(T, /turf/space)) && !is_type_in_list(src.loc,list(/obj/item, /obj/mecha))) + if (lacks_power()) if (src:aiRestorePowerRoutine==0) src:aiRestorePowerRoutine = 1 @@ -169,6 +169,11 @@ sleep(50) theAPC = null +/mob/living/silicon/ai/proc/lacks_power() + var/turf/T = get_turf(src) + var/area/A = get_area(src) + return ((!A.power_equip) || istype(T, /turf/space)) && !is_type_in_list(src.loc, list(/obj/item, /obj/mecha)) + /mob/living/silicon/ai/updatehealth() if(status_flags & GODMODE) health = maxHealth diff --git a/code/modules/nano/JSON Reader.dm b/code/modules/nano/JSON Reader.dm index 4c8326ec7ca..638ff937a3f 100644 --- a/code/modules/nano/JSON Reader.dm +++ b/code/modules/nano/JSON Reader.dm @@ -1,4 +1,4 @@ -/json_token +json_token var value New(v) @@ -9,7 +9,7 @@ symbol eof -/json_reader +json_reader var list string = list("'", "\"") @@ -20,185 +20,186 @@ i = 1 -/json_reader/proc/ScanJson(json) - // scanner - src.json = json - . = new/list() - src.i = 1 - while(src.i <= lentext(json)) - var/char = get_char() - if(is_whitespace(char)) - i++ - continue - if(string.Find(char)) - . += read_string(char) - else if(symbols.Find(char)) - . += new/json_token/symbol(char) - else if(is_digit(char)) - . += read_number() - else - . += read_word() - i++ - . += new/json_token/eof() - -/json_reader/proc/read_word() - var/val = "" - while(i <= lentext(json)) - var/char = get_char() - if(is_whitespace(char) || symbols.Find(char)) - i-- // let scanner handle this character - return new/json_token/word(val) - val += char - i++ - -/json_reader/proc/read_string(delim) - var - escape = FALSE - val = "" - while(++i <= lentext(json)) - var/char = get_char() - if(escape) - switch(char) - if("\\", "'", "\"", "/", "u") - val += char + proc + // scanner + ScanJson(json) + src.json = json + . = new/list() + src.i = 1 + while(src.i <= lentext(json)) + var/char = get_char() + if(is_whitespace(char)) + i++ + continue + if(string.Find(char)) + . += read_string(char) + else if(symbols.Find(char)) + . += new/json_token/symbol(char) + else if(is_digit(char)) + . += read_number() else - // TODO: support octal, hex, unicode sequences - ASSERT(sequences.Find(char)) - val += ascii2text(sequences[char]) - else - if(char == delim) - return new/json_token/text(val) - else if(char == "\\") - escape = TRUE - else + . += read_word() + i++ + . += new/json_token/eof() + + read_word() + var/val = "" + while(i <= lentext(json)) + var/char = get_char() + if(is_whitespace(char) || symbols.Find(char)) + i-- // let scanner handle this character + return new/json_token/word(val) val += char - CRASH("Unterminated string.") + i++ -/json_reader/proc/read_number() - var/val = "" - var/char = get_char() - while(is_digit(char) || char == "." || lowertext(char) == "e") - val += char - i++ - char = get_char() - i-- // allow scanner to read the first non-number character - return new/json_token/number(text2num(val)) + read_string(delim) + var + escape = FALSE + val = "" + while(++i <= lentext(json)) + var/char = get_char() + if(escape) + switch(char) + if("\\", "'", "\"", "/", "u") + val += char + else + // TODO: support octal, hex, unicode sequences + ASSERT(sequences.Find(char)) + val += ascii2text(sequences[char]) + else + if(char == delim) + return new/json_token/text(val) + else if(char == "\\") + escape = TRUE + else + val += char + CRASH("Unterminated string.") -/json_reader/proc/check_char() - ASSERT(args.Find(get_char())) + read_number() + var/val = "" + var/char = get_char() + while(is_digit(char) || char == "." || lowertext(char) == "e") + val += char + i++ + char = get_char() + i-- // allow scanner to read the first non-number character + return new/json_token/number(text2num(val)) -/json_reader/proc/get_char() - return copytext(json, i, i+1) + check_char() + ASSERT(args.Find(get_char())) -/json_reader/proc/is_whitespace(char) - return char == " " || char == "\t" || char == "\n" || text2ascii(char) == 13 + get_char() + return copytext(json, i, i+1) -/json_reader/proc/is_digit(char) - var/c = text2ascii(char) - return 48 <= c && c <= 57 || char == "+" || char == "-" + is_whitespace(char) + return char == " " || char == "\t" || char == "\n" || text2ascii(char) == 13 + + is_digit(char) + var/c = text2ascii(char) + return 48 <= c && c <= 57 || char == "+" || char == "-" -// parser -/json_reader/proc/ReadObject(list/tokens) - src.tokens = tokens - . = new/list() - i = 1 - read_token("{", /json_token/symbol) - while(i <= tokens.len) - var/json_token/K = get_token() - check_type(/json_token/word, /json_token/text) + // parser + ReadObject(list/tokens) + src.tokens = tokens + . = new/list() + i = 1 + read_token("{", /json_token/symbol) + while(i <= tokens.len) + var/json_token/K = get_token() + check_type(/json_token/word, /json_token/text) + next_token() + read_token(":", /json_token/symbol) + + .[K.value] = read_value() + + var/json_token/S = get_token() + check_type(/json_token/symbol) + switch(S.value) + if(",") + next_token() + continue + if("}") + next_token() + return + else + die() + + get_token() + return tokens[i] + next_token() - read_token(":", /json_token/symbol) + return tokens[++i] - .[K.value] = read_value() - - var/json_token/S = get_token() - check_type(/json_token/symbol) - switch(S.value) - if(",") - next_token() - continue - if("}") - next_token() - return - else - die() - -/json_reader/proc/get_token() - return tokens[i] - -/json_reader/proc/next_token() - return tokens[++i] - -/json_reader/proc/read_token(val, type) - var/json_token/T = get_token() - if(!(T.value == val && istype(T, type))) - CRASH("Expected '[val]', found '[T.value]'.") - next_token() - return T - -/json_reader/proc/check_type(...) - var/json_token/T = get_token() - for(var/type in args) - if(istype(T, type)) - return - CRASH("Bad token type: [T.type].") - -/json_reader/proc/check_value(...) - var/json_token/T = get_token() - ASSERT(args.Find(T.value)) - -/json_reader/proc/read_key() - var/char = get_char() - if(char == "\"" || char == "'") - return read_string(char) - -/json_reader/proc/read_value() - var/json_token/T = get_token() - switch(T.type) - if(/json_token/text, /json_token/number) + read_token(val, type) + var/json_token/T = get_token() + if(!(T.value == val && istype(T, type))) + CRASH("Expected '[val]', found '[T.value]'.") next_token() - return T.value - if(/json_token/word) - next_token() - switch(T.value) - if("true") - return TRUE - if("false") - return FALSE - if("null") - return null - if(/json_token/symbol) - switch(T.value) - if("\[") - return read_array() - if("{") - return ReadObject(tokens.Copy(i)) - die() + return T -/json_reader/proc/read_array() - read_token("\[", /json_token/symbol) - . = new/list() - var/list/L = . - while(i <= tokens.len) - // Avoid using Add() or += in case a list is returned. - L.len++ - L[L.len] = read_value() - var/json_token/T = get_token() - check_type(/json_token/symbol) - switch(T.value) - if(",") - next_token() - continue - if("]") - next_token() - return - else - die() - next_token() - CRASH("Unterminated array.") + check_type(...) + var/json_token/T = get_token() + for(var/type in args) + if(istype(T, type)) + return + CRASH("Bad token type: [T.type].") + + check_value(...) + var/json_token/T = get_token() + ASSERT(args.Find(T.value)) + + read_key() + var/char = get_char() + if(char == "\"" || char == "'") + return read_string(char) + + read_value() + var/json_token/T = get_token() + switch(T.type) + if(/json_token/text, /json_token/number) + next_token() + return T.value + if(/json_token/word) + next_token() + switch(T.value) + if("true") + return TRUE + if("false") + return FALSE + if("null") + return null + if(/json_token/symbol) + switch(T.value) + if("\[") + return read_array() + if("{") + return ReadObject(tokens.Copy(i)) + die() + + read_array() + read_token("\[", /json_token/symbol) + . = new/list() + var/list/L = . + while(i <= tokens.len) + // Avoid using Add() or += in case a list is returned. + L.len++ + L[L.len] = read_value() + var/json_token/T = get_token() + check_type(/json_token/symbol) + switch(T.value) + if(",") + next_token() + continue + if("]") + next_token() + return + else + die() + next_token() + CRASH("Unterminated array.") -/json_reader/proc/die(json_token/T) - if(!T) T = get_token() - CRASH("Unexpected token: [T.value].") \ No newline at end of file + die(json_token/T) + if(!T) T = get_token() + CRASH("Unexpected token: [T.value].") \ No newline at end of file diff --git a/code/modules/nano/JSON Writer.dm b/code/modules/nano/JSON Writer.dm index aa709094601..787357fb760 100644 --- a/code/modules/nano/JSON Writer.dm +++ b/code/modules/nano/JSON Writer.dm @@ -1,57 +1,58 @@ +json_writer + var + use_cache = 0 -/json_writer/proc/WriteObject(list/L) - . = "{" - var/i = 1 - for(var/k in L) - var/val = L[k] - . += {"\"[k]\":[write(val)]"} - if(i++ < L.len) - . += "," - .+= "}" + proc + WriteObject(list/L) + if(use_cache && L["__json_cache"]) + return L["__json_cache"] -/json_writer/proc/write(val) - if(isnum(val)) - return num2text(val, 100) - else if(isnull(val)) - return "null" - else if(istype(val, /list)) - if(is_associative(val)) - return WriteObject(val) - else - return write_array(val) - else - . += write_string("[val]") + . = "{" + var/i = 1 + for(var/k in L) + var/val = L[k] + . += {"\"[k]\":[write(val)]"} + if(i++ < L.len) + . += "," + . += "}" -/json_writer/proc/write_array(list/L) - . = "\[" - for(var/i = 1 to L.len) - . += write(L[i]) - if(i < L.len) - . += "," - . += "]" - -/json_writer/proc/write_string(txt) - var/static/list/json_escape = list("\\", "\"", "'", "\n") - for(var/targ in json_escape) - var/start = 1 - while(start <= lentext(txt)) - var/i = findtext(txt, targ, start) - if(!i) - break - if(targ == "\n") - txt = copytext(txt, 1, i) + "\\n" + copytext(txt, i+2) - start = i + 1 // 1 character added - if(targ == "'") - txt = copytext(txt, 1, i) + "`" + copytext(txt, i+1) // apostrophies fuck shit up... - start = i + 1 // 1 character added + write(val) + if(isnum(val)) + return num2text(val) + else if(isnull(val)) + return "null" + else if(istype(val, /list)) + if(is_associative(val)) + return WriteObject(val) + else + return write_array(val) else - txt = copytext(txt, 1, i) + "\\" + copytext(txt, i) - start = i + 2 // 2 characters added + . += write_string("[val]") - return {""[txt]""} + write_array(list/L) + . = "\[" + for(var/i = 1 to L.len) + . += write(L[i]) + if(i < L.len) + . += "," + . += "]" -/json_writer/proc/is_associative(list/L) - for(var/key in L) - // if the key is a list that means it's actually an array of lists (stupid Byond...) - if(!isnum(key) && !istype(key, /list)) - return TRUE + write_string(txt) + var/static/list/json_escape = list("\\" = "\\\\", "\"" = "\\\"", "\n" = "\\n") + for(var/targ in json_escape) + var/start = 1 + while(start <= lentext(txt)) + var/i = findtext(txt, targ, start) + if(!i) + break + var/lrep = length(json_escape[targ]) + txt = copytext(txt, 1, i) + json_escape[targ] + copytext(txt, i + length(targ)) + start = i + lrep + + return {""[txt]""} + + is_associative(list/L) + for(var/key in L) + // if the key is a list that means it's actually an array of lists (stupid Byond...) + if(!isnum(key) && !isnull(L[key]) && !istype(key, /list)) + return TRUE diff --git a/code/modules/nano/_JSON.dm b/code/modules/nano/_JSON.dm index 72cca824253..29c18b246b6 100644 --- a/code/modules/nano/_JSON.dm +++ b/code/modules/nano/_JSON.dm @@ -2,10 +2,16 @@ n_Json v11.3.21 */ -/proc/json2list(json) +proc + json2list(json) var/static/json_reader/_jsonr = new() return _jsonr.ReadObject(_jsonr.ScanJson(json)) -/proc/list2json(list/L) + list2json(list/L) var/static/json_writer/_jsonw = new() - return _jsonw.WriteObject(L) + return _jsonw.write(L) + + list2json_usecache(list/L) + var/static/json_writer/_jsonw = new() + _jsonw.use_cache = 1 + return _jsonw.write(L) diff --git a/code/modules/nano/interaction/admin.dm b/code/modules/nano/interaction/admin.dm new file mode 100644 index 00000000000..fa5baf51941 --- /dev/null +++ b/code/modules/nano/interaction/admin.dm @@ -0,0 +1,7 @@ +/* + This state checks that the user is an admin, end of story +*/ +/var/global/datum/topic_state/admin_state/admin_state = new() + +/datum/topic_state/admin_state/can_use_topic(var/src_object, var/mob/user) + return check_rights(R_ADMIN, 0, user) ? NANO_INTERACTIVE : NANO_CLOSE diff --git a/code/modules/nano/interaction/base.dm b/code/modules/nano/interaction/base.dm new file mode 100644 index 00000000000..bb7316868a1 --- /dev/null +++ b/code/modules/nano/interaction/base.dm @@ -0,0 +1,32 @@ +/atom/proc/nano_host() + return src + +/atom/proc/CanUseTopic(var/mob/user, var/datum/topic_state/state) + var/src_object = nano_host() + return state.can_use_topic(src_object, user) + +/datum/topic_state/proc/href_list(var/mob/user) + return list() + +/datum/topic_state/proc/can_use_topic(var/src_object, var/mob/user) + return NANO_CLOSE + +/mob/proc/shared_nano_interaction() + if (src.stat || !client) + return NANO_CLOSE // no updates, close the interface + else if (restrained() || lying || stat || stunned || weakened) // TODO: Change to incapaciated() on merge + return NANO_UPDATE // update only (orange visibility) + return NANO_INTERACTIVE + +/mob/living/silicon/ai/shared_nano_interaction() + if (lacks_power()) + return NANO_CLOSE + return ..() + +/mob/living/silicon/robot/shared_nano_interaction() + . = NANO_INTERACTIVE + if (cell.charge <= 0) + return NANO_CLOSE + if (lockcharge) + . = NANO_DISABLED + return min(., ..()) diff --git a/code/modules/nano/interaction/conscious.dm b/code/modules/nano/interaction/conscious.dm new file mode 100644 index 00000000000..8d93942a79b --- /dev/null +++ b/code/modules/nano/interaction/conscious.dm @@ -0,0 +1,7 @@ +/* + This state only checks if user is conscious. +*/ +/var/global/datum/topic_state/conscious_state/conscious_state = new() + +/datum/topic_state/conscious_state/can_use_topic(var/src_object, var/mob/user) + return user.stat == CONSCIOUS ? NANO_INTERACTIVE : NANO_CLOSE diff --git a/code/modules/nano/interaction/contained.dm b/code/modules/nano/interaction/contained.dm new file mode 100644 index 00000000000..8fd82ba6b33 --- /dev/null +++ b/code/modules/nano/interaction/contained.dm @@ -0,0 +1,18 @@ +/* + This state checks if user is somewhere within src_object, as well as the default NanoUI interaction. +*/ +/var/global/datum/topic_state/contained_state/contained_state = new() + +/datum/topic_state/contained_state/can_use_topic(var/atom/src_object, var/mob/user) + if(!src_object.contains(user)) + return NANO_CLOSE + + return user.shared_nano_interaction() + +/atom/proc/contains(var/atom/location) + if(!location) + return 0 + if(location == src) + return 1 + + return contains(location.loc) diff --git a/code/modules/nano/interaction/default.dm b/code/modules/nano/interaction/default.dm new file mode 100644 index 00000000000..c7c0a1429d6 --- /dev/null +++ b/code/modules/nano/interaction/default.dm @@ -0,0 +1,93 @@ +/var/global/datum/topic_state/default/default_state = new() + +/datum/topic_state/default/href_list(var/mob/user) + return list() + +/datum/topic_state/default/can_use_topic(var/src_object, var/mob/user) + return user.default_can_use_topic(src_object) + +/mob/proc/default_can_use_topic(var/src_object) + return NANO_CLOSE // By default no mob can do anything with NanoUI + +/mob/dead/observer/default_can_use_topic() + if(check_rights(R_ADMIN, 0, src)) + return NANO_INTERACTIVE // Admins are more equal + return NANO_UPDATE // Ghosts can view updates + +/mob/living/silicon/pai/default_can_use_topic(var/src_object) + if((src_object == src || src_object == radio) && !stat) + return NANO_INTERACTIVE + else + return ..() + +/mob/living/silicon/robot/default_can_use_topic(var/src_object) + . = shared_nano_interaction() + if(. <= NANO_DISABLED) + return + + // robots can interact with things they can see within their view range + if((src_object in view(src)) && get_dist(src_object, src) <= src.client.view) + return NANO_INTERACTIVE // interactive (green visibility) + return NANO_DISABLED // no updates, completely disabled (red visibility) + +/mob/living/silicon/robot/syndicate/default_can_use_topic(var/src_object) + . = ..() + if(. != NANO_INTERACTIVE) + return + + if(istype(get_area(src), /area/syndicate_mothership) || istype(get_area(src), /area/shuttle/syndicate)) // If elsewhere, they can interact with everything on the syndicate shuttle + return NANO_INTERACTIVE + if(istype(src_object, /obj/machinery)) // Otherwise they can only interact with emagged machinery + var/obj/machinery/Machine = src_object + if(Machine.emagged) + return NANO_INTERACTIVE + return NANO_UPDATE + +/mob/living/silicon/ai/default_can_use_topic(var/src_object) + . = shared_nano_interaction() + if(. != NANO_INTERACTIVE) + return + + // Prevents the AI from using Topic on admin levels (by for example viewing through the court/thunderdome cameras) + // unless it's on the same level as the object it's interacting with. + var/turf/T = get_turf(src_object) + if(!T || !(z == T.z)) + return NANO_CLOSE + + // If an object is in view then we can interact with it + if(src_object in view(client.view, src)) + return NANO_INTERACTIVE + + return NANO_CLOSE + +//Some atoms such as vehicles might have special rules for how mobs inside them interact with NanoUI. +/atom/proc/contents_nano_distance(var/src_object, var/mob/living/user) + return user.shared_living_nano_distance(src_object) + +/mob/living/proc/shared_living_nano_distance(var/atom/movable/src_object) + if (!(src_object in view(4, src))) // If the src object is not in visable, disable updates + return NANO_CLOSE + + var/dist = get_dist(src_object, src) + if (dist <= 1) + return NANO_INTERACTIVE // interactive (green visibility) + else if (dist <= 2) + return NANO_UPDATE // update only (orange visibility) + else if (dist <= 4) + return NANO_DISABLED // no updates, completely disabled (red visibility) + return NANO_CLOSE + +/mob/living/default_can_use_topic(var/src_object) + . = shared_nano_interaction(src_object) + if(. != NANO_CLOSE) + if(loc) + . = min(., loc.contents_nano_distance(src_object, src)) + if(NANO_INTERACTIVE) + return NANO_UPDATE + +/mob/living/carbon/human/default_can_use_topic(var/src_object) + . = shared_nano_interaction(src_object) + if(. != NANO_CLOSE) + . = min(., shared_living_nano_distance(src_object)) + if(. == NANO_UPDATE && dna.check_mutation(TK)) // If we have telekinesis and remain close enough, allow interaction. + return NANO_INTERACTIVE diff --git a/code/modules/nano/interaction/inventory.dm b/code/modules/nano/interaction/inventory.dm new file mode 100644 index 00000000000..5c8f8ca46c2 --- /dev/null +++ b/code/modules/nano/interaction/inventory.dm @@ -0,0 +1,11 @@ +/* + This state checks that the src_object is somewhere in the user's first-level inventory (in hands, on ear, etc.), but not further down (such as in bags). +*/ +/var/global/datum/topic_state/inventory_state/inventory_state = new() + +/datum/topic_state/inventory_state/can_use_topic(var/src_object, var/mob/user) + if(!(src_object in user)) + return NANO_CLOSE + + + return user.shared_nano_interaction() diff --git a/code/modules/nano/interaction/inventory_deep.dm b/code/modules/nano/interaction/inventory_deep.dm new file mode 100644 index 00000000000..300552e588d --- /dev/null +++ b/code/modules/nano/interaction/inventory_deep.dm @@ -0,0 +1,10 @@ +/* + This state checks if src_object is contained anywhere in the user's inventory, including bags, etc. +*/ +/var/global/datum/topic_state/deep_inventory_state/deep_inventory_state = new() + +/datum/topic_state/deep_inventory_state/can_use_topic(var/src_object, var/mob/user) + if(!user.contains(src_object)) + return NANO_CLOSE + + return user.shared_nano_interaction() diff --git a/code/modules/nano/interaction/physical.dm b/code/modules/nano/interaction/physical.dm new file mode 100644 index 00000000000..28762012d4b --- /dev/null +++ b/code/modules/nano/interaction/physical.dm @@ -0,0 +1,18 @@ +/var/global/datum/topic_state/physical/physical_state = new() + +/datum/topic_state/physical/can_use_topic(var/src_object, var/mob/user) + . = user.shared_nano_interaction(src_object) + if(. > NANO_CLOSE) + return min(., user.check_physical_distance(src_object)) + +/mob/proc/check_physical_distance(var/src_object) + return NANO_CLOSE + +/mob/dead/observer/check_physical_distance(var/src_object) + return default_can_use_topic(src_object) + +/mob/living/check_physical_distance(var/src_object) + return shared_living_nano_distance(src_object) + +/mob/living/silicon/check_physical_distance(var/src_object) + return max(NANO_UPDATE, shared_living_nano_distance(src_object)) diff --git a/code/modules/nano/interaction/self.dm b/code/modules/nano/interaction/self.dm new file mode 100644 index 00000000000..da125df17ee --- /dev/null +++ b/code/modules/nano/interaction/self.dm @@ -0,0 +1,9 @@ +/* + This state checks that the src_object is the same the as user +*/ +/var/global/datum/topic_state/self_state/self_state = new() + +/datum/topic_state/self_state/can_use_topic(var/src_object, var/mob/user) + if(src_object != user) + return NANO_CLOSE + return user.shared_nano_interaction() diff --git a/code/modules/nano/interaction/zlevel.dm b/code/modules/nano/interaction/zlevel.dm new file mode 100644 index 00000000000..e646396743a --- /dev/null +++ b/code/modules/nano/interaction/zlevel.dm @@ -0,0 +1,13 @@ +/* + This state checks that the user is on the same Z-level as src_object +*/ + +/var/global/datum/topic_state/z_state/z_state = new() + +/datum/topic_state/z_state/can_use_topic(var/src_object, var/mob/user) + var/turf/turf_obj = get_turf(src_object) + var/turf/turf_usr = get_turf(user) + if(!turf_obj || !turf_usr) + return NANO_CLOSE + + return turf_obj.z == turf_usr.z ? NANO_INTERACTIVE : NANO_CLOSE diff --git a/code/modules/nano/nanoexternal.dm b/code/modules/nano/nanoexternal.dm index 52a2fb9d3e3..3c518ad1a60 100644 --- a/code/modules/nano/nanoexternal.dm +++ b/code/modules/nano/nanoexternal.dm @@ -33,14 +33,22 @@ * @param user /mob The mob who is interacting with this ui * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui + * @param force_open boolean Force the UI to (re)open, even if it's already open * * @return nothing */ -/atom/movable/proc/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null) +/atom/movable/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/nano_ui/master_ui = null, var/datum/topic_state/state = default_state) return + /** + * Used to get data to send to the Nano UI. + * + * @param user /mob The mob who is interacting with this ui + * + * @return list + */ /atom/movable/proc/get_ui_data(mob/user) return list() -// Used by the Nano UI SubSystem (/datum/subsystem/nano) to track UIs opened by this mob +// Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob /mob/var/list/open_uis = list() diff --git a/code/modules/nano/nanomanager.dm b/code/modules/nano/nanomanager.dm index 2831ec6a2b5..369342a7a3e 100644 --- a/code/modules/nano/nanomanager.dm +++ b/code/modules/nano/nanomanager.dm @@ -1,4 +1,4 @@ - /** + /** * Get an open /nanoui ui for the current user, or create a new one. * * @param user /mob The mob who opened/owns the ui @@ -9,7 +9,7 @@ * * @return /nanoui Returns the new or found ui */ -/datum/subsystem/nano/proc/push_open_or_new_ui(mob/user, atom/movable/src_object, ui_key, datum/nanoui/ui, template, title, width, height, auto_update) +/datum/subsystem/nano/proc/push_open_or_new_ui(mob/user, atom/movable/src_object, ui_key, datum/nanoui/ui, template, title, width, height, auto_update, var/atom/ref = null, var/datum/nanoui/master_ui = null, var/datum/topic_state/state = default_state) var/list/data = src_object.get_ui_data(user) if (!data) data = list() @@ -17,7 +17,7 @@ ui = try_update_ui(user, src_object, ui_key, ui, data) if (isnull(ui)) - ui = new /datum/nanoui(user, src_object, ui_key, template, title, width, height) + ui = new/datum/nanoui(user, src_object, ui_key, template, title, width, height, ref, master_ui, state) ui.set_initial_data(data) ui.open() ui.set_auto_update(auto_update) @@ -30,17 +30,23 @@ * @param ui_key string A string key used for the ui * @param ui /datum/nanoui An existing instance of the ui (can be null) * @param data list The data to be passed to the ui, if it exists + * @param force_open boolean The ui is being forced to (re)open, so close ui if it exists (instead of updating) * * @return /nanoui Returns the found ui, for null if none exists */ -/datum/subsystem/nano/proc/try_update_ui(mob/user, src_object, ui_key, datum/nanoui/ui, data) +/datum/subsystem/nano/proc/try_update_ui(var/mob/user, src_object, ui_key, var/datum/nanoui/ui, data, var/force_open = 0) if (isnull(ui)) // no ui has been passed, so we'll search for one + { ui = get_open_ui(user, src_object, ui_key) - + } if (!isnull(ui)) - // The UI is already open so push the data to it - ui.push_data(data) - return ui + // The UI is already open + if (!force_open) + ui.push_data(data) + return ui + else + ui.reinitialise(new_initial_data=data) + return ui return null @@ -53,17 +59,20 @@ * * @return /nanoui Returns the found ui, or null if none exists */ -/datum/subsystem/nano/proc/get_open_ui(mob/user, src_object, ui_key) +/datum/subsystem/nano/proc/get_open_ui(var/mob/user, src_object, ui_key) var/src_object_key = "\ref[src_object]" if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) + //testing("subsystem/nano/get_open_ui mob [user.name] [src_object:name] [ui_key] - there are no uis open") return null else if (isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list)) + //testing("subsystem/nano/get_open_ui mob [user.name] [src_object:name] [ui_key] - there are no uis open for this object") return null for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) if (ui.user == user) return ui + //testing("subsystem/nano/get_open_ui mob [user.name] [src_object:name] [ui_key] - ui not found") return null /** @@ -81,11 +90,31 @@ var/update_count = 0 for (var/ui_key in open_uis[src_object_key]) for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) - if(ui && ui.src_object && ui.user) + if(ui && ui.src_object && ui.user && ui.src_object.nano_host()) ui.process(1) update_count++ return update_count + /** + * Close all /nanoui uis attached to src_object + * + * @param src_object /obj|/mob The obj or mob which the uis are attached to + * + * @return int The number of uis close + */ +/datum/subsystem/nano/proc/close_uis(src_object) + var/src_object_key = "\ref[src_object]" + if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) + return 0 + + var/close_count = 0 + for (var/ui_key in open_uis[src_object_key]) + for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) + if(ui && ui.src_object && ui.user && ui.src_object.nano_host()) + ui.close() + close_count++ + return close_count + /** * Update /nanoui uis belonging to user * @@ -95,7 +124,7 @@ * * @return int The number of uis updated */ -/datum/subsystem/nano/proc/update_user_uis(mob/user, src_object = null, ui_key = null) +/datum/subsystem/nano/proc/update_user_uis(var/mob/user, src_object = null, ui_key = null) if (isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0) return 0 // has no open uis @@ -116,8 +145,9 @@ * * @return int The number of uis closed */ -/datum/subsystem/nano/proc/close_user_uis(mob/user, src_object = null, ui_key = null) +/datum/subsystem/nano/proc/close_user_uis(var/mob/user, src_object = null, ui_key = null) if (isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0) + //testing("subsystem/nano/close_user_uis mob [user.name] has no open uis") return 0 // has no open uis var/close_count = 0 @@ -126,6 +156,8 @@ ui.close() close_count++ + //testing("subsystem/nano/close_user_uis mob [user.name] closed [open_uis.len] of [close_count] uis") + return close_count /** @@ -136,17 +168,18 @@ * * @return nothing */ -/datum/subsystem/nano/proc/ui_opened(datum/nanoui/ui) +/datum/subsystem/nano/proc/ui_opened(var/datum/nanoui/ui) var/src_object_key = "\ref[ui.src_object]" if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) open_uis[src_object_key] = list(ui.ui_key = list()) else if (isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list)) open_uis[src_object_key][ui.ui_key] = list(); - ui.user.open_uis.Add(ui) + ui.user.open_uis |= ui var/list/uis = open_uis[src_object_key][ui.ui_key] - uis.Add(ui) - processing_uis.Add(ui) + uis |= ui + processing_uis |= ui + //testing("subsystem/nano/ui_opened mob [ui.user.name] [ui.src_object:name] [ui.ui_key] - user.open_uis [ui.user.open_uis.len] | uis [uis.len] | processing_uis [processing_uis.len]") /** * Remove a /nanoui ui from the list of open uis @@ -156,7 +189,7 @@ * * @return int 0 if no ui was removed, 1 if removed successfully */ -/datum/subsystem/nano/proc/ui_closed(datum/nanoui/ui) +/datum/subsystem/nano/proc/ui_closed(var/datum/nanoui/ui) var/src_object_key = "\ref[ui.src_object]" if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) return 0 // wasn't open @@ -167,7 +200,11 @@ if(ui.user) // Sanity check in case a user has been deleted (say a blown up borg watching the alarm interface) ui.user.open_uis.Remove(ui) var/list/uis = open_uis[src_object_key][ui.ui_key] - return uis.Remove(ui) + uis.Remove(ui) + + //testing("subsystem/nano/ui_closed mob [ui.user.name] [ui.src_object:name] [ui.ui_key] - user.open_uis [ui.user.open_uis.len] | uis [uis.len] | processing_uis [processing_uis.len]") + + return 1 /** * This is called on user logout @@ -179,7 +216,8 @@ */ // -/datum/subsystem/nano/proc/user_logout(mob/user) +/datum/subsystem/nano/proc/user_logout(var/mob/user) + //testing("subsystem/nano/user_logout user [user.name]") return close_user_uis(user) /** @@ -191,8 +229,10 @@ * * @return nothing */ -/datum/subsystem/nano/proc/user_transferred(mob/oldMob, mob/newMob) - if (isnull(oldMob.open_uis) || !istype(oldMob.open_uis, /list) || open_uis.len == 0) +/datum/subsystem/nano/proc/user_transferred(var/mob/oldMob, var/mob/newMob) + //testing("subsystem/nano/user_transferred from mob [oldMob.name] to mob [newMob.name]") + if (!oldMob || isnull(oldMob.open_uis) || !istype(oldMob.open_uis, /list) || open_uis.len == 0) + //testing("subsystem/nano/user_transferred mob [oldMob.name] has no open uis") return 0 // has no open uis if (isnull(newMob.open_uis) || !istype(newMob.open_uis, /list)) @@ -206,6 +246,14 @@ return 1 // success -//Send all needed nano ui files to the client + /** + * Sends all nano assets to the client + * This is called on user login + * + * @param client /client The user's client + * + * @return nothing + */ + /datum/subsystem/nano/proc/send_resources(client) getFilesSlow(client, asset_files) diff --git a/code/modules/nano/nanomapgen.dm b/code/modules/nano/nanomapgen.dm new file mode 100644 index 00000000000..dd65b7bbd08 --- /dev/null +++ b/code/modules/nano/nanomapgen.dm @@ -0,0 +1,90 @@ +// This file is a modified version of https://raw2.github.com/Baystation12/OldCode-BS12/master/code/TakePicture.dm + +#define NANOMAP_ICON_SIZE 4 +#define NANOMAP_MAX_ICON_DIMENSION 1024 + +#define NANOMAP_TILES_PER_IMAGE (NANOMAP_MAX_ICON_DIMENSION / NANOMAP_ICON_SIZE) + +#define NANOMAP_TERMINALERR 5 +#define NANOMAP_INPROGRESS 2 +#define NANOMAP_BADOUTPUT 2 +#define NANOMAP_SUCCESS 1 +#define NANOMAP_WATCHDOGSUCCESS 4 +#define NANOMAP_WATCHDOGTERMINATE 3 + + +//Call these procs to dump your world to a series of image files (!!) +//NOTE: Does not explicitly support non 32x32 icons or stuff with large pixel_* values, so don't blame me if it doesn't work perfectly + +/client/proc/nanomapgen_DumpImage() + set name = "Generate NanoUI Map" + set category = "Server" + + if(holder) + nanomapgen_DumpTile(1, 1, text2num(input(usr,"Enter the Z level to generate"))) + +/client/proc/nanomapgen_DumpTile(var/startX = 1, var/startY = 1, var/currentZ = 1, var/endX = -1, var/endY = -1) + + if (endX < 0 || endX > world.maxx) + endX = world.maxx + + if (endY < 0 || endY > world.maxy) + endY = world.maxy + + if (currentZ < 0 || currentZ > world.maxz) + usr << "NanoMapGen: ERROR: currentZ ([currentZ]) must be between 1 and [world.maxz]" + + sleep(3) + return NANOMAP_TERMINALERR + + if (startX > endX) + usr << "NanoMapGen: ERROR: startX ([startX]) cannot be greater than endX ([endX])" + + sleep(3) + return NANOMAP_TERMINALERR + + if (startY > endX) + usr << "NanoMapGen: ERROR: startY ([startY]) cannot be greater than endY ([endY])" + sleep(3) + return NANOMAP_TERMINALERR + + var/icon/Tile = icon(file("nano/mapbase1024.png")) + if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION) + world.log << "NanoMapGen: ERROR: BASE IMAGE DIMENSIONS ARE NOT [NANOMAP_MAX_ICON_DIMENSION]x[NANOMAP_MAX_ICON_DIMENSION]" + sleep(3) + return NANOMAP_TERMINALERR + + world.log << "NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])" + usr << "NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])" + + var/count = 0; + for(var/WorldX = startX, WorldX <= endX, WorldX++) + for(var/WorldY = startY, WorldY <= endY, WorldY++) + + var/atom/Turf = locate(WorldX, WorldY, currentZ) + + var/icon/TurfIcon = new(Turf.icon, Turf.icon_state) + TurfIcon.Scale(NANOMAP_ICON_SIZE, NANOMAP_ICON_SIZE) + + Tile.Blend(TurfIcon, ICON_OVERLAY, ((WorldX - 1) * NANOMAP_ICON_SIZE), ((WorldY - 1) * NANOMAP_ICON_SIZE)) + + count++ + + if (count % 8000 == 0) + world.log << "NanoMapGen: [count] tiles done" + sleep(1) + + var/mapFilename = "nanomap_z[currentZ]-new.png" + + world.log << "NanoMapGen: sending [mapFilename] to client" + + usr << browse(Tile, "window=picture;file=[mapFilename];display=0") + + world.log << "NanoMapGen: Done." + + usr << "NanoMapGen: Done. File [mapFilename] uploaded to your cache." + + if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION) + return NANOMAP_BADOUTPUT + + return NANOMAP_SUCCESS \ No newline at end of file diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index 729f0327a4b..1d7d769b8a2 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -6,11 +6,6 @@ nanoui class (or whatever Byond calls classes) nanoui is used to open and update nano browser uis **********************************************************/ - -#define STATUS_INTERACTIVE 2 // GREEN Visability -#define STATUS_UPDATE 1 // ORANGE Visability -#define STATUS_DISABLED 0 // RED Visability - /datum/nanoui // the user who opened this ui var/mob/user @@ -36,20 +31,31 @@ nanoui is used to open and update nano browser uis var/list/stylesheets = list() // the list of javascript scripts to use for this ui var/list/scripts = list() - // the list of templates to use with this ui (usually just one) + // a list of templates which can be used with this ui var/templates[0] - // the body content for this ui, do not change unless you know what you're doing - // the #mainTemplate div will contain the compiled "main" template html - var/content = "
" + // the layout key for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing) + var/layout_key = "default" + // this sets whether to re-render the ui layout with each update (default 0, turning on will break the map ui if it's in use) + var/auto_update_layout = 0 + // this sets whether to re-render the ui content with each update (default 1) + var/auto_update_content = 1 + // the default state to use for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing) + var/state_key = "default" + // show the map ui, this is used by the default layout + var/show_map = 0 + // the map z level to display + var/map_z_level = 1 // initial data, containing the full data structure, must be sent to the ui (the data structure cannot be extended later on) var/list/initial_data[0] // set to 1 to update the ui automatically every master_controller tick var/is_auto_updating = 0 // the current status/visibility of the ui - var/status = STATUS_INTERACTIVE + var/status = NANO_INTERACTIVE - // 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 + // Relationship between a master interface and its children. Used in update_status + var/datum/nanoui/master_ui + var/list/datum/nanoui/children = list() + var/datum/topic_state/state = null /** * Create a new nanoui instance. @@ -57,7 +63,7 @@ nanoui is used to open and update nano browser uis * @param nuser /mob The mob who has opened/owns this ui * @param nsrc_object /obj|/mob The obj or mob which this ui belongs to * @param nui_key string A string key to use for this ui. Allows for multiple unique uis on one src_oject - * @param ntemplate string The name of the template file from /nano/templates (e.g. "my_template.tmpl") + * @param ntemplate string The filename of the template file from /nano/templates (e.g. "my_template.tmpl") * @param ntitle string The title of this ui * @param nwidth int the width of the ui window * @param nheight int the height of the ui window @@ -65,17 +71,22 @@ nanoui is used to open and update nano browser uis * * @return /nanoui new nanoui object */ -/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null) +/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, var/datum/nanoui/master_ui = null, var/datum/topic_state/state = default_state) user = nuser src_object = nsrc_object ui_key = nui_key window_id = "[ui_key]\ref[src_object]" - // Add the passed template as the 'main' template, this is required - add_template("main", ntemplate) + src.master_ui = master_ui + if(master_ui) + master_ui.children += src + src.state = state + + // add the passed template filename as the "main" template, this is required + add_template("main", ntemplate_filename) if (ntitle) - title = ntitle + title = sanitize(ntitle) if (nwidth) width = nwidth if (nheight) @@ -86,15 +97,19 @@ nanoui is used to open and update nano browser uis add_common_assets() /** - * Use this proc to add assets which are common to all nano uis + * Use this proc to add assets which are common to (and required by) all nano uis * * @return nothing */ /datum/nanoui/proc/add_common_assets() - add_script("libraries.min.js") // The jQuery library - add_script("nano_config.js") // The NanoConfig JS, this is used to store configuration values. - add_script("nano_update.js") // The NanoUpdate JS, this is used to receive updates and apply them. - add_script("nano_base_helpers.js") // The NanoBaseHelpers JS, this is used to set up template helpers which are common to all templates + add_script("libraries.min.js") // A JS file comprising of jQuery, doT.js and jQuery Timer libraries (compressed together) + add_script("nano_utility.js") // The NanoUtility JS, this is used to store utility functions. + add_script("nano_template.js") // The NanoTemplate JS, this is used to render templates. + add_script("nano_state_manager.js") // The NanoStateManager JS, it handles updates from the server and passes data to the current state + add_script("nano_state.js") // The NanoState JS, this is the base state which all states must inherit from + add_script("nano_state_default.js") // The NanoStateDefault JS, this is the "default" state (used by all UIs by default), which inherits from NanoState + add_script("nano_base_callbacks.js") // The NanoBaseCallbacks JS, this is used to set up (before and after update) callbacks which are common to all UIs + add_script("nano_base_helpers.js") // The NanoBaseHelpers JS, this is used to set up template helpers which are common to all UIs add_stylesheet("shared.css") // this CSS sheet is common to all UIs add_stylesheet("icons.css") // this CSS sheet is common to all UIs @@ -107,12 +122,15 @@ nanoui is used to open and update nano browser uis * @return nothing */ /datum/nanoui/proc/set_status(state, push_update) - if (state != status) - status = state - if (push_update || !status) - push_data(list(), 1) // Update the UI, force the update in case the status is 0 - else - status = state + if (state != status) // Only update if it is different + if (status == NANO_DISABLED) + status = state + if (push_update) + update() + else + status = state + if (push_update || status == 0) + push_data(null, 1) // Update the UI, force the update in case the status is 0, data is null so that previous data is used /** * Update the status (visibility) of this ui based on the user's status @@ -121,38 +139,14 @@ nanoui is used to open and update nano browser uis * * @return nothing */ -/datum/nanoui/proc/update_status(push_update = 0) - if (istype(user, /mob/living/silicon/ai)) - set_status(STATUS_INTERACTIVE, push_update) // interactive (green visibility) - else if (istype(user, /mob/living/silicon/robot)) - if (src_object in view(7, user)) // robots can see and interact with things they can see within 7 tiles - set_status(STATUS_INTERACTIVE, push_update) // interactive (green visibility) - else - set_status(STATUS_DISABLED, push_update) // no updates, completely disabled (red visibility) - else - var/dist = get_dist(src_object, user) - var/isTK = 0 +/datum/nanoui/proc/update_status(var/push_update = 0) + var/new_status = src_object.CanUseTopic(user, state) + if(master_ui) + new_status = min(new_status, master_ui.status) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - isTK = H.dna.check_mutation(TK) - - if (dist > 4 && !isTK) - close() - return - - if ((allowed_user_stat > -1) && (user.stat > allowed_user_stat)) - set_status(STATUS_DISABLED, push_update) // no updates, completely disabled (red visibility) - else if (user.restrained() || user.lying) - set_status(STATUS_UPDATE, push_update) // update only (orange visibility) - else if (!(src_object in view(4, user))) // If the src object is not in visable, set status to 0 - set_status(STATUS_DISABLED, push_update) // no updates, completely disabled (red visibility) - else if (dist <= 1) - set_status(STATUS_INTERACTIVE, push_update) // interactive (green visibility) - else if (dist <= 2 || isTK) - set_status(STATUS_UPDATE, push_update) // update only (orange visibility) - else if (dist <= 4) - set_status(STATUS_DISABLED, push_update) // no updates, completely disabled (red visibility) + set_status(new_status, push_update) + if(new_status == NANO_CLOSE) + close() /** * Set the ui to auto update (every master_controller tick) @@ -161,8 +155,8 @@ nanoui is used to open and update nano browser uis * * @return nothing */ -/datum/nanoui/proc/set_auto_update(state = 1) - is_auto_updating = state +/datum/nanoui/proc/set_auto_update(nstate = 1) + is_auto_updating = nstate /** * Set the initial data for the ui. This is vital as the data structure set here cannot be changed when pushing new updates. @@ -172,21 +166,43 @@ nanoui is used to open and update nano browser uis * @return nothing */ /datum/nanoui/proc/set_initial_data(list/data) - initial_data = add_default_data(data) + initial_data = data /** - * Add default data to the data being sent to the ui. + * Get config data to sent to the ui. * - * @param data /list The list of data to be modified - * - * @return /list modified data + * @return /list config data */ -/datum/nanoui/proc/add_default_data(list/data) - data["ui"] = list( +/datum/nanoui/proc/get_config_data() + var/list/config_data = list( + "title" = title, + "srcObject" = list("name" = src_object.name), + "stateKey" = state_key, "status" = status, + "autoUpdateLayout" = auto_update_layout, + "autoUpdateContent" = auto_update_content, + "showMap" = show_map, + "mapZLevel" = map_z_level, "user" = list("name" = user.name) ) - return data + return config_data + + /** + * Get data to sent to the ui. + * + * @param data /list The list of general data for this ui (can be null to use previous data sent) + * + * @return /list data to send to the ui + */ +/datum/nanoui/proc/get_send_data(var/list/data) + var/list/config_data = get_config_data() + + var/list/send_data = list("config" = config_data) + + if (!isnull(data)) + send_data["data"] = data + + return send_data /** * Set the browser window options for this ui @@ -200,6 +216,7 @@ nanoui is used to open and update nano browser uis /** * Add a CSS stylesheet to this UI + * These must be added before the UI has been opened, adding after that will have no effect * * @param file string The name of the CSS file from /nano/css (e.g. "my_style.css") * @@ -210,6 +227,7 @@ nanoui is used to open and update nano browser uis /** * Add a JavsScript script to this UI + * These must be added before the UI has been opened, adding after that will have no effect * * @param file string The name of the JavaScript file from /nano/js (e.g. "my_script.js") * @@ -219,30 +237,81 @@ nanoui is used to open and update nano browser uis scripts.Add(file) /** - * Add a template to this UI + * Add a template for this UI * Templates are combined with the data sent to the UI to create the rendered view - * Each template needs a div in ui.content to contain the rendered content. - * The div format is '' where| t |