Refactors NanoUI interaction (again)

Takes NanoUI interaction code and splits it into more manageable modules.

The default handler that checks distances, camera coverage, etc., etc.
Admin NanoUI windows now only check for admin rights, ignoring sight, distance, and anything else.
ERT/mercenary appearance changer now remains open for as long as the mob remains on the admin Z-level.
PDAs and uplinks now require that you keep the item somewhere in your main inventory (or contents to be precise). Hands or ears are fine, inside bags are not.
Rig suits also check that they are in the operator's inventory or that the synthetic operator is somewhere within the suit's contents (recursively).
This commit is contained in:
PsiOmega
2015-04-10 11:03:00 +02:00
parent 71a8ddf50e
commit e37c5c22d4
25 changed files with 146 additions and 105 deletions

View File

@@ -1267,10 +1267,15 @@
#include "code\modules\nano\JSON Reader.dm" #include "code\modules\nano\JSON Reader.dm"
#include "code\modules\nano\JSON Writer.dm" #include "code\modules\nano\JSON Writer.dm"
#include "code\modules\nano\nanoexternal.dm" #include "code\modules\nano\nanoexternal.dm"
#include "code\modules\nano\nanointeraction.dm"
#include "code\modules\nano\nanomanager.dm" #include "code\modules\nano\nanomanager.dm"
#include "code\modules\nano\nanomapgen.dm" #include "code\modules\nano\nanomapgen.dm"
#include "code\modules\nano\nanoui.dm" #include "code\modules\nano\nanoui.dm"
#include "code\modules\nano\interaction\admin.dm"
#include "code\modules\nano\interaction\base.dm"
#include "code\modules\nano\interaction\contained.dm"
#include "code\modules\nano\interaction\default.dm"
#include "code\modules\nano\interaction\inventory.dm"
#include "code\modules\nano\interaction\zlevel.dm"
#include "code\modules\nano\modules\alarm_monitor.dm" #include "code\modules\nano\modules\alarm_monitor.dm"
#include "code\modules\nano\modules\crew_monitor.dm" #include "code\modules\nano\modules\crew_monitor.dm"
#include "code\modules\nano\modules\human_appearance.dm" #include "code\modules\nano\modules\human_appearance.dm"

View File

@@ -34,7 +34,7 @@
return 1 return 1
if(flags & ANTAG_SET_APPEARANCE) if(flags & ANTAG_SET_APPEARANCE)
player.change_appearance(APPEARANCE_ALL, player, player, valid_species) player.change_appearance(APPEARANCE_ALL, player.loc, player, valid_species, state = z_state)
/datum/antagonist/proc/unequip(var/mob/living/carbon/human/player) /datum/antagonist/proc/unequip(var/mob/living/carbon/human/player)
if(!istype(player)) if(!istype(player))

View File

@@ -469,14 +469,14 @@
ui_interact(user) ui_interact(user)
wires.Interact(user) wires.Interact(user)
/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/custom_state = null) /obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
var/data[0] var/data[0]
var/remote_connection = 0 var/remote_connection = 0
var/remote_access = 0 var/remote_access = 0
if(custom_state) if(state)
var/list/state = custom_state.href_list(user) var/list/href = state.href_list(user)
remote_connection = state["remote_connection"] // Remote connection means we're non-adjacent/connecting from another computer remote_connection = href["remote_connection"] // Remote connection means we're non-adjacent/connecting from another computer
remote_access = state["remote_access"] // Remote access means we also have the privilege to alter the air alarm. remote_access = href["remote_access"] // Remote access means we also have the privilege to alter the air alarm.
data["locked"] = locked && !user.isSilicon() data["locked"] = locked && !user.isSilicon()
data["remote_connection"] = remote_connection data["remote_connection"] = remote_connection
@@ -491,7 +491,7 @@
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui) if(!ui)
ui = new(user, src, ui_key, "air_alarm.tmpl", src.name, 325, 625, master_ui = master_ui, custom_state = custom_state) ui = new(user, src, ui_key, "air_alarm.tmpl", src.name, 325, 625, master_ui = master_ui, state = state)
ui.set_initial_data(data) ui.set_initial_data(data)
ui.open() ui.open()
ui.set_auto_update(1) ui.set_auto_update(1)
@@ -616,7 +616,7 @@
data["thresholds"] = thresholds data["thresholds"] = thresholds
/obj/machinery/alarm/CanUseTopic(var/mob/user, href_list, var/datum/topic_state/custom_state) /obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/topic_state/state, var/href_list = list())
if(buildstage != 2) if(buildstage != 2)
return STATUS_CLOSE return STATUS_CLOSE
@@ -627,17 +627,15 @@
. = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE
if(. == STATUS_INTERACTIVE) if(. == STATUS_INTERACTIVE)
var/extra_href = custom_state.href_list(usr) var/extra_href = state.href_list(usr)
// Prevent remote users from altering RCON settings unless they already have access (I realize the risks) // Prevent remote users from altering RCON settings unless they already have access
if(href_list["rcon"] && extra_href["remote_connection"] && !extra_href["remote_access"]) if(href_list["rcon"] && extra_href["remote_connection"] && !extra_href["remote_access"])
. = STATUS_UPDATE . = STATUS_UPDATE
//TODO: Move the rest of if(!locked || extra_href["remote_access"] || usr.isAI()) and hrefs here
return min(..(), .) return min(..(), .)
/obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/custom_state) /obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state)
if(..(href, href_list, nowindow, custom_state)) if(..(href, href_list, nowindow, state))
return 1 return 1
// hrefs that can always be called -walter0o // hrefs that can always be called -walter0o
@@ -666,7 +664,7 @@
return 1 return 1
// hrefs that need the AA unlocked -walter0o // hrefs that need the AA unlocked -walter0o
var/extra_href = custom_state.href_list(usr) var/extra_href = state.href_list(usr)
if(!(locked && !extra_href["remote_connection"]) || extra_href["remote_access"] || usr.isSilicon()) if(!(locked && !extra_href["remote_connection"]) || extra_href["remote_access"] || usr.isSilicon())
if(href_list["command"]) if(href_list["command"])
var/device_id = href_list["id_tag"] var/device_id = href_list["id_tag"]

View File

@@ -13,7 +13,7 @@ var/global/list/minor_air_alarms = list()
/obj/machinery/computer/atmos_alert/New() /obj/machinery/computer/atmos_alert/New()
..() ..()
atmosphere_alarm.register(src, /obj/machinery/computer/station_alert/update_icon) atmosphere_alarm.register(src, /obj/machinery/computer/station_alert/update_icon)
/obj/machinery/computer/atmos_alert/Del() /obj/machinery/computer/atmos_alert/Del()
atmosphere_alarm.unregister(src) atmosphere_alarm.unregister(src)
..() ..()
@@ -68,16 +68,13 @@ var/global/list/minor_air_alarms = list()
var/obj/machinery/alarm/air_alarm = alarm_source.source var/obj/machinery/alarm/air_alarm = alarm_source.source
if(istype(air_alarm)) if(istype(air_alarm))
var/list/new_ref = list("atmos_reset" = 1) var/list/new_ref = list("atmos_reset" = 1)
air_alarm.Topic(href, new_ref, custom_state = atmos_alert_topic) air_alarm.Topic(href, new_ref, state = air_alarm_topic)
return 1 return 1
var/datum/topic_state/atmos_alert/atmos_alert_topic = new() var/datum/topic_state/air_alarm_topic/air_alarm_topic = new()
/datum/topic_state/atmos_alert /datum/topic_state/air_alarm_topic/href_list(var/mob/user)
flags = NANO_IGNORE_DISTANCE
/datum/topic_state/air_alarm/href_list(var/mob/user)
var/list/extra_href = list() var/list/extra_href = list()
extra_href["remote_connection"] = 1 extra_href["remote_connection"] = 1
extra_href["remote_access"] = 1 extra_href["remote_access"] = 1

View File

@@ -76,7 +76,7 @@
var/obj/machinery/alarm/alarm = locate(href_list["alarm"]) in (monitored_alarms ? monitored_alarms : machines) var/obj/machinery/alarm/alarm = locate(href_list["alarm"]) in (monitored_alarms ? monitored_alarms : machines)
if(alarm) if(alarm)
var/datum/topic_state/TS = generate_state(alarm) var/datum/topic_state/TS = generate_state(alarm)
alarm.ui_interact(usr, master_ui = ui_ref, custom_state = TS) alarm.ui_interact(usr, master_ui = ui_ref, state = TS)
return 1 return 1
/obj/machinery/computer/atmoscontrol/proc/generate_state(var/alarm) /obj/machinery/computer/atmoscontrol/proc/generate_state(var/alarm)
@@ -86,7 +86,6 @@
return state return state
/datum/topic_state/air_alarm /datum/topic_state/air_alarm
flags = NANO_IGNORE_DISTANCE
var/obj/machinery/computer/atmoscontrol/atmos_control = null var/obj/machinery/computer/atmoscontrol/atmos_control = null
var/obj/machinery/alarm/air_alarm = null var/obj/machinery/alarm/air_alarm = null

View File

@@ -610,7 +610,7 @@ About the new airlock wires panel:
..(user) ..(user)
return return
/obj/machinery/door/airlock/CanUseTopic(var/mob/user, href_list) /obj/machinery/door/airlock/CanUseTopic(var/mob/user)
if(!user.isSilicon()) if(!user.isSilicon())
return STATUS_CLOSE return STATUS_CLOSE

View File

@@ -185,7 +185,7 @@ Class Procs:
/obj/machinery/proc/inoperable(var/additional_flags = 0) /obj/machinery/proc/inoperable(var/additional_flags = 0)
return (stat & (NOPOWER|BROKEN|additional_flags)) return (stat & (NOPOWER|BROKEN|additional_flags))
/obj/machinery/CanUseTopic(var/mob/user, var/be_close) /obj/machinery/CanUseTopic(var/mob/user)
if(!interact_offline && (stat & (NOPOWER|BROKEN))) if(!interact_offline && (stat & (NOPOWER|BROKEN)))
return STATUS_CLOSE return STATUS_CLOSE

View File

@@ -542,7 +542,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if (!ui) if (!ui)
// the ui does not exist, so we'll create a new() one // the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "pda.tmpl", title, 520, 400) ui = new(user, src, ui_key, "pda.tmpl", title, 520, 400, state = inventory_state)
// when the ui is first opened this is the data it will use // when the ui is first opened this is the data it will use
ui.load_cached_data(ManifestJSON) ui.load_cached_data(ManifestJSON)

View File

@@ -53,6 +53,9 @@ datum/nano_item_lists
var/uplink_owner = null//text-only var/uplink_owner = null//text-only
var/used_TC = 0 var/used_TC = 0
/obj/item/device/uplink/nano_host()
return loc
/obj/item/device/uplink/New() /obj/item/device/uplink/New()
..() ..()
welcome = ticker.mode.uplink_welcome welcome = ticker.mode.uplink_welcome
@@ -219,7 +222,7 @@ datum/nano_item_lists
if (!ui) if (!ui)
// the ui does not exist, so we'll create a new() one // the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "uplink.tmpl", title, 450, 600) ui = new(user, src, ui_key, "uplink.tmpl", title, 450, 600, state = inventory_state)
// when the ui is first opened this is the data it will use // when the ui is first opened this is the data it will use
ui.set_initial_data(data) ui.set_initial_data(data)
// open the new ui window // open the new ui window

View File

@@ -16,15 +16,14 @@
var/damtype = "brute" var/damtype = "brute"
var/force = 0 var/force = 0
/obj/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/custom_state = default_state) /obj/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state = default_state)
// Calling Topic without a corresponding window open causes runtime errors // Calling Topic without a corresponding window open causes runtime errors
if(!nowindow && ..()) if(!nowindow && ..())
return 1 return 1
// In the far future no checks are made in an overriding Topic() beyond if(..()) return // In the far future no checks are made in an overriding Topic() beyond if(..()) return
// Instead any such checks are made in CanUseTopic() // Instead any such checks are made in CanUseTopic()
var/obj/host = nano_host() if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE)
if(host.CanUseTopic(usr, href_list, custom_state) == STATUS_INTERACTIVE)
CouldUseTopic(usr) CouldUseTopic(usr)
return 0 return 0

View File

@@ -724,7 +724,8 @@ var/list/admin_verbs_mentor = list(
return return
if(holder) if(holder)
S.subsystem_law_manager() var/obj/nano_module/law_manager/L = new(S)
L.ui_interact(usr, state = admin_state)
admin_log_and_message_admins("has opened [S]'s law manager.") admin_log_and_message_admins("has opened [S]'s law manager.")
feedback_add_details("admin_verb","MSL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! feedback_add_details("admin_verb","MSL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -738,7 +739,7 @@ var/list/admin_verbs_mentor = list(
if(holder) if(holder)
admin_log_and_message_admins("is altering the appearance of [H].") admin_log_and_message_admins("is altering the appearance of [H].")
H.change_appearance(APPEARANCE_ALL, usr, usr, check_species_whitelist = 0) H.change_appearance(APPEARANCE_ALL, usr, usr, check_species_whitelist = 0, state = admin_state)
feedback_add_details("admin_verb","CHAA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! feedback_add_details("admin_verb","CHAA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/change_human_appearance_self(mob/living/carbon/human/H in mob_list) /client/proc/change_human_appearance_self(mob/living/carbon/human/H in mob_list)

View File

@@ -459,7 +459,7 @@
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui) if (!ui)
ui = new(user, src, ui_key, ((src.loc != user) ? ai_interface_path : interface_path), interface_title, 480, 550) ui = new(user, src, ui_key, ((src.loc != user) ? ai_interface_path : interface_path), interface_title, 480, 550, data["ai"] ? contained_state : inventory_state)
ui.set_initial_data(data) ui.set_initial_data(data)
ui.open() ui.open()
ui.set_auto_update(1) ui.set_auto_update(1)

View File

@@ -1,7 +1,7 @@
/mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR, var/location = src, var/mob/user = src, var/check_species_whitelist = 1, var/list/species_whitelist = list(), var/list/species_blacklist = list()) /mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR, var/location = src, var/mob/user = src, var/check_species_whitelist = 1, var/list/species_whitelist = list(), var/list/species_blacklist = list(), var/datum/topic_state/state = default_state)
var/obj/nano_module/appearance_changer/AC = new(location, src, check_species_whitelist, species_whitelist, species_blacklist) var/obj/nano_module/appearance_changer/AC = new(location, src, check_species_whitelist, species_whitelist, species_blacklist)
AC.flags = flags AC.flags = flags
AC.ui_interact(user) AC.ui_interact(user, state = state)
/mob/living/carbon/human/proc/change_species(var/new_species) /mob/living/carbon/human/proc/change_species(var/new_species)
if(!new_species) if(!new_species)

View File

@@ -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) ? STATUS_INTERACTIVE : STATUS_CLOSE

View File

@@ -0,0 +1,19 @@
/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 STATUS_INTERACTIVE
/mob/proc/shared_nano_interaction()
if (src.stat || !client)
return STATUS_CLOSE // no updates, close the interface
else if (restrained() || lying || stat || stunned || weakened)
return STATUS_UPDATE // update only (orange visibility)
return STATUS_INTERACTIVE

View File

@@ -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(src))
return STATUS_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)

View File

@@ -1,41 +1,36 @@
/atom/proc/nano_host() /var/global/datum/topic_state/default_state = new()
return src
/obj/nano_module/nano_host() /datum/topic_state/default/href_list(var/mob/user)
return loc return list()
/datum/topic_state/default/can_use_topic(var/src_object, var/mob/user)
return user.default_can_use_topic(src_object)
/atom/proc/CanUseTopic(var/mob/user, href_list, var/datum/topic_state/custom_state) /mob/proc/default_can_use_topic(var/src_object)
return user.can_use_topic(nano_host(), custom_state)
/mob/proc/can_use_topic(var/mob/user, var/datum/topic_state/custom_state)
return STATUS_CLOSE // By default no mob can do anything with NanoUI return STATUS_CLOSE // By default no mob can do anything with NanoUI
/mob/dead/observer/can_use_topic() /mob/dead/observer/default_can_use_topic()
if(check_rights(R_ADMIN, 0, src)) if(check_rights(R_ADMIN, 0, src))
return STATUS_INTERACTIVE // Admins are more equal return STATUS_INTERACTIVE // Admins are more equal
return STATUS_UPDATE // Ghosts can view updates return STATUS_UPDATE // Ghosts can view updates
/mob/living/silicon/pai/can_use_topic(var/src_object) /mob/living/silicon/pai/default_can_use_topic(var/src_object)
if(src_object == src && !stat) if(src_object == src && !stat)
return STATUS_INTERACTIVE return STATUS_INTERACTIVE
else else
return ..() return ..()
/mob/living/silicon/robot/can_use_topic(var/src_object, var/datum/topic_state/custom_state) /mob/living/silicon/robot/default_can_use_topic(var/src_object)
if(stat || !client) if(stat || !client)
return STATUS_CLOSE return STATUS_CLOSE
if(lockcharge || stunned || weakened) if(lockcharge || stunned || weakened)
return STATUS_DISABLED return STATUS_DISABLED
if(custom_state.flags & NANO_IGNORE_DISTANCE)
return STATUS_INTERACTIVE
// robots can interact with things they can see within their view range // 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) if((src_object in view(src)) && get_dist(src_object, src) <= src.client.view)
return STATUS_INTERACTIVE // interactive (green visibility) return STATUS_INTERACTIVE // interactive (green visibility)
return STATUS_DISABLED // no updates, completely disabled (red visibility) return STATUS_DISABLED // no updates, completely disabled (red visibility)
/mob/living/silicon/robot/syndicate/can_use_topic(var/src_object) /mob/living/silicon/robot/syndicate/default_can_use_topic(var/src_object)
. = ..() . = ..()
if(. != STATUS_INTERACTIVE) if(. != STATUS_INTERACTIVE)
return return
@@ -50,7 +45,7 @@
return STATUS_INTERACTIVE return STATUS_INTERACTIVE
return STATUS_UPDATE return STATUS_UPDATE
/mob/living/silicon/ai/can_use_topic(var/src_object) /mob/living/silicon/ai/default_can_use_topic(var/src_object)
if(!client || check_unable(1)) if(!client || check_unable(1))
return STATUS_CLOSE return STATUS_CLOSE
// Prevents the AI from using Topic on admin levels (by for example viewing through the court/thunderdome cameras) // Prevents the AI from using Topic on admin levels (by for example viewing through the court/thunderdome cameras)
@@ -71,26 +66,13 @@
return apc_override ? STATUS_INTERACTIVE : STATUS_CLOSE return apc_override ? STATUS_INTERACTIVE : STATUS_CLOSE
return STATUS_INTERACTIVE return STATUS_INTERACTIVE
return STATUS_CLOSE return STATUS_CLOSE
/mob/living/proc/shared_living_nano_interaction(var/src_object)
if (src.stat != CONSCIOUS)
return STATUS_CLOSE // no updates, close the interface
else if (restrained() || lying || stat || stunned || weakened)
return STATUS_UPDATE // update only (orange visibility)
return STATUS_INTERACTIVE
//Some atoms such as vehicles might have special rules for how mobs inside them interact with NanoUI. //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) /atom/proc/contents_nano_distance(var/src_object, var/mob/living/user)
return user.shared_living_nano_distance(src_object) return user.shared_living_nano_distance(src_object)
/mob/living/proc/shared_living_nano_distance(var/atom/movable/src_object) /mob/living/proc/shared_living_nano_distance(var/atom/movable/src_object)
if(!isturf(src_object.loc))
if(src_object.loc == src) // Item in the inventory
return STATUS_INTERACTIVE
if(src.contents.Find(src_object.loc)) // A hidden uplink inside an item
return STATUS_INTERACTIVE
if (!(src_object in view(4, src))) // If the src object is not in visable, disable updates if (!(src_object in view(4, src))) // If the src object is not in visable, disable updates
return STATUS_CLOSE return STATUS_CLOSE
@@ -103,27 +85,17 @@
return STATUS_DISABLED // no updates, completely disabled (red visibility) return STATUS_DISABLED // no updates, completely disabled (red visibility)
return STATUS_CLOSE return STATUS_CLOSE
/mob/living/can_use_topic(var/src_object, var/datum/topic_state/custom_state) /mob/living/default_can_use_topic(var/src_object)
. = shared_living_nano_interaction(src_object) . = shared_nano_interaction(src_object)
if(. == STATUS_INTERACTIVE && !(custom_state.flags & NANO_IGNORE_DISTANCE)) if(. == STATUS_INTERACTIVE)
if(loc) if(loc)
. = loc.contents_nano_distance(src_object, src) . = loc.contents_nano_distance(src_object, src)
else
. = shared_living_nano_distance(src_object)
if(STATUS_INTERACTIVE) if(STATUS_INTERACTIVE)
return STATUS_UPDATE return STATUS_UPDATE
/mob/living/carbon/human/can_use_topic(var/src_object, var/datum/topic_state/custom_state) /mob/living/carbon/human/default_can_use_topic(var/src_object)
. = shared_living_nano_interaction(src_object) . = shared_nano_interaction(src_object)
if(. == STATUS_INTERACTIVE && !(custom_state.flags & NANO_IGNORE_DISTANCE)) if(. == STATUS_INTERACTIVE)
. = shared_living_nano_distance(src_object) . = shared_living_nano_distance(src_object)
if(. == STATUS_UPDATE && (TK in mutations)) // If we have telekinesis and remain close enough, allow interaction. if(. == STATUS_UPDATE && (TK in mutations)) // If we have telekinesis and remain close enough, allow interaction.
return STATUS_INTERACTIVE return STATUS_INTERACTIVE
/var/global/datum/topic_state/default_state = new()
/datum/topic_state
var/flags = 0
/datum/topic_state/proc/href_list(var/mob/user)
return list()

View File

@@ -0,0 +1,10 @@
/*
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 src))
return STATUS_CLOSE
return user.shared_nano_interaction()

View File

@@ -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 STATUS_CLOSE
return turf_obj.z == turf_usr.z ? STATUS_INTERACTIVE : STATUS_CLOSE

View File

@@ -18,7 +18,7 @@
src.whitelist = species_whitelist src.whitelist = species_whitelist
src.blacklist = species_blacklist src.blacklist = species_blacklist
/obj/nano_module/appearance_changer/Topic(ref, href_list) /obj/nano_module/appearance_changer/Topic(ref, href_list, var/nowindow, var/datum/topic_state/state = default_state)
if(..()) if(..())
return 1 return 1
@@ -35,13 +35,13 @@
if(href_list["skin_tone"]) if(href_list["skin_tone"])
if(can_change_skin_tone()) if(can_change_skin_tone())
var/new_s_tone = input(usr, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Skin Tone", owner.s_tone) as num|null var/new_s_tone = input(usr, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Skin Tone", owner.s_tone) as num|null
if(isnum(new_s_tone) && CanUseTopic(usr) == STATUS_INTERACTIVE) if(isnum(new_s_tone) && can_still_topic(state))
new_s_tone = 35 - max(min( round(new_s_tone), 220),1) new_s_tone = 35 - max(min( round(new_s_tone), 220),1)
return owner.change_skin_tone(new_s_tone) return owner.change_skin_tone(new_s_tone)
if(href_list["skin_color"]) if(href_list["skin_color"])
if(can_change_skin_color()) if(can_change_skin_color())
var/new_skin = input(usr, "Choose your character's skin colour: ", "Skin Color", rgb(owner.r_skin, owner.g_skin, owner.b_skin)) as color|null var/new_skin = input(usr, "Choose your character's skin colour: ", "Skin Color", rgb(owner.r_skin, owner.g_skin, owner.b_skin)) as color|null
if(new_skin && can_still_topic()) if(new_skin && can_still_topic(state))
var/r_skin = hex2num(copytext(new_skin, 2, 4)) var/r_skin = hex2num(copytext(new_skin, 2, 4))
var/g_skin = hex2num(copytext(new_skin, 4, 6)) var/g_skin = hex2num(copytext(new_skin, 4, 6))
var/b_skin = hex2num(copytext(new_skin, 6, 8)) var/b_skin = hex2num(copytext(new_skin, 6, 8))
@@ -56,7 +56,7 @@
if(href_list["hair_color"]) if(href_list["hair_color"])
if(can_change(APPEARANCE_HAIR_COLOR)) if(can_change(APPEARANCE_HAIR_COLOR))
var/new_hair = input("Please select hair color.", "Hair Color", rgb(owner.r_hair, owner.g_hair, owner.b_hair)) as color|null var/new_hair = input("Please select hair color.", "Hair Color", rgb(owner.r_hair, owner.g_hair, owner.b_hair)) as color|null
if(new_hair && can_still_topic()) if(new_hair && can_still_topic(state))
var/r_hair = hex2num(copytext(new_hair, 2, 4)) var/r_hair = hex2num(copytext(new_hair, 2, 4))
var/g_hair = hex2num(copytext(new_hair, 4, 6)) var/g_hair = hex2num(copytext(new_hair, 4, 6))
var/b_hair = hex2num(copytext(new_hair, 6, 8)) var/b_hair = hex2num(copytext(new_hair, 6, 8))
@@ -71,7 +71,7 @@
if(href_list["facial_hair_color"]) if(href_list["facial_hair_color"])
if(can_change(APPEARANCE_FACIAL_HAIR_COLOR)) if(can_change(APPEARANCE_FACIAL_HAIR_COLOR))
var/new_facial = input("Please select facial hair color.", "Facial Hair Color", rgb(owner.r_facial, owner.g_facial, owner.b_facial)) as color|null var/new_facial = input("Please select facial hair color.", "Facial Hair Color", rgb(owner.r_facial, owner.g_facial, owner.b_facial)) as color|null
if(new_facial && can_still_topic()) if(new_facial && can_still_topic(state))
var/r_facial = hex2num(copytext(new_facial, 2, 4)) var/r_facial = hex2num(copytext(new_facial, 2, 4))
var/g_facial = hex2num(copytext(new_facial, 4, 6)) var/g_facial = hex2num(copytext(new_facial, 4, 6))
var/b_facial = hex2num(copytext(new_facial, 6, 8)) var/b_facial = hex2num(copytext(new_facial, 6, 8))
@@ -81,7 +81,7 @@
if(href_list["eye_color"]) if(href_list["eye_color"])
if(can_change(APPEARANCE_EYE_COLOR)) if(can_change(APPEARANCE_EYE_COLOR))
var/new_eyes = input("Please select eye color.", "Eye Color", rgb(owner.r_eyes, owner.g_eyes, owner.b_eyes)) as color|null var/new_eyes = input("Please select eye color.", "Eye Color", rgb(owner.r_eyes, owner.g_eyes, owner.b_eyes)) as color|null
if(new_eyes && can_still_topic()) if(new_eyes && can_still_topic(state))
var/r_eyes = hex2num(copytext(new_eyes, 2, 4)) var/r_eyes = hex2num(copytext(new_eyes, 2, 4))
var/g_eyes = hex2num(copytext(new_eyes, 4, 6)) var/g_eyes = hex2num(copytext(new_eyes, 4, 6))
var/b_eyes = hex2num(copytext(new_eyes, 6, 8)) var/b_eyes = hex2num(copytext(new_eyes, 6, 8))
@@ -91,7 +91,7 @@
return 0 return 0
/obj/nano_module/appearance_changer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) /obj/nano_module/appearance_changer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
generate_data(check_whitelist, whitelist, blacklist) generate_data(check_whitelist, whitelist, blacklist)
var/data[0] var/data[0]
@@ -128,7 +128,7 @@
data["change_facial_hair_color"] = can_change(APPEARANCE_FACIAL_HAIR_COLOR) data["change_facial_hair_color"] = can_change(APPEARANCE_FACIAL_HAIR_COLOR)
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui) if (!ui)
ui = new(user, src, ui_key, "appearance_changer.tmpl", "[src.name]", 800, 450) ui = new(user, src, ui_key, "appearance_changer.tmpl", "[src.name]", 800, 450, state = state)
ui.set_initial_data(data) ui.set_initial_data(data)
ui.open() ui.open()
ui.set_auto_update(1) ui.set_auto_update(1)

View File

@@ -152,7 +152,7 @@
return 0 return 0
/obj/nano_module/law_manager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) /obj/nano_module/law_manager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
var/data[0] var/data[0]
owner.lawsync() owner.lawsync()
@@ -189,7 +189,7 @@
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui) if (!ui)
ui = new(user, src, ui_key, "law_manager.tmpl", sanitize("[src] - [owner]"), 800, is_malf(user) ? 600 : 400) ui = new(user, src, ui_key, "law_manager.tmpl", sanitize("[src] - [owner]"), 800, is_malf(user) ? 600 : 400, state = state)
ui.set_initial_data(data) ui.set_initial_data(data)
ui.open() ui.open()
ui.set_auto_update(1) ui.set_auto_update(1)

View File

@@ -1,2 +1,5 @@
/obj/nano_module/proc/can_still_topic() /obj/nano_module/nano_host()
return CanUseTopic(usr, list(), default_state) == STATUS_INTERACTIVE return loc
/obj/nano_module/proc/can_still_topic(var/datum/topic_state/state = default_state)
return CanUseTopic(usr, state) == STATUS_INTERACTIVE

View File

@@ -37,7 +37,7 @@
* *
* @return nothing * @return nothing
*/ */
/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/custom_state = 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 return
// Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob // Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob

View File

@@ -55,7 +55,7 @@ nanoui is used to open and update nano browser uis
// Relationship between a master interface and its children. Used in update_status // Relationship between a master interface and its children. Used in update_status
var/datum/nanoui/master_ui var/datum/nanoui/master_ui
var/list/datum/nanoui/children = list() var/list/datum/nanoui/children = list()
var/datum/topic_state/custom_state = null var/datum/topic_state/state = null
var/cached_data = null var/cached_data = null
@@ -73,7 +73,7 @@ nanoui is used to open and update nano browser uis
* *
* @return /nanoui new nanoui object * @return /nanoui new nanoui object
*/ */
/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/custom_state = default_state) /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 user = nuser
src_object = nsrc_object src_object = nsrc_object
ui_key = nui_key ui_key = nui_key
@@ -82,7 +82,7 @@ nanoui is used to open and update nano browser uis
src.master_ui = master_ui src.master_ui = master_ui
if(master_ui) if(master_ui)
master_ui.children += src master_ui.children += src
src.custom_state = custom_state src.state = state
// add the passed template filename as the "main" template, this is required // add the passed template filename as the "main" template, this is required
add_template("main", ntemplate_filename) add_template("main", ntemplate_filename)
@@ -142,8 +142,7 @@ nanoui is used to open and update nano browser uis
* @return nothing * @return nothing
*/ */
/datum/nanoui/proc/update_status(var/push_update = 0) /datum/nanoui/proc/update_status(var/push_update = 0)
var/atom/movable/host = src_object.nano_host() var/new_status = src_object.CanUseTopic(user, state)
var/new_status = host.CanUseTopic(user, list(), custom_state)
if(master_ui) if(master_ui)
new_status = min(new_status, master_ui.status) new_status = min(new_status, master_ui.status)
@@ -483,7 +482,7 @@ nanoui is used to open and update nano browser uis
set_map_z_level(text2num(href_list["mapZLevel"])) set_map_z_level(text2num(href_list["mapZLevel"]))
map_update = 1 map_update = 1
if ((src_object && src_object.Topic(href, href_list, 0, custom_state)) || map_update) if ((src_object && src_object.Topic(href, href_list, 0, state)) || map_update)
nanomanager.update_uis(src_object) // update all UIs attached to src_object nanomanager.update_uis(src_object) // update all UIs attached to src_object
/** /**
@@ -510,4 +509,4 @@ nanoui is used to open and update nano browser uis
* @return nothing * @return nothing
*/ */
/datum/nanoui/proc/update(var/force_open = 0) /datum/nanoui/proc/update(var/force_open = 0)
src_object.ui_interact(user, ui_key, src, force_open, master_ui, custom_state) src_object.ui_interact(user, ui_key, src, force_open, master_ui, state)

View File

@@ -779,8 +779,6 @@ var/list/be_special_flags = list(
//General-purpose life speed define for plants. //General-purpose life speed define for plants.
#define HYDRO_SPEED_MULTIPLIER 1 #define HYDRO_SPEED_MULTIPLIER 1
#define NANO_IGNORE_DISTANCE 1
// Robot AI notifications // Robot AI notifications
#define ROBOT_NOTIFICATION_NEW_UNIT 1 #define ROBOT_NOTIFICATION_NEW_UNIT 1
#define ROBOT_NOTIFICATION_NEW_NAME 2 #define ROBOT_NOTIFICATION_NEW_NAME 2