initial commit - cross reference with 5th port - obviously has compile errors
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* tgui external
|
||||
*
|
||||
* Contains all external tgui declarations.
|
||||
**/
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Used to open and update UIs.
|
||||
* If this proc is not implemented properly, the UI will not update correctly.
|
||||
*
|
||||
* required user mob The mob who opened/is using the UI.
|
||||
* optional ui_key string The ui_key of the UI.
|
||||
* optional ui datum/tgui The UI to be updated, if it exists.
|
||||
* optional force_open bool If the UI should be re-opened instead of updated.
|
||||
* optional master_ui datum/tgui The parent UI.
|
||||
* optional state datum/ui_state The state used to determine status.
|
||||
**/
|
||||
/datum/proc/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state)
|
||||
return -1 // Not implemented.
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Data to be sent to the UI.
|
||||
* This must be implemented for a UI to work.
|
||||
*
|
||||
* required user mob The mob interacting with the UI.
|
||||
*
|
||||
* return list Data to be sent to the UI.
|
||||
**/
|
||||
/datum/proc/ui_data(mob/user)
|
||||
return list() // Not implemented.
|
||||
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Called on a UI when the UI receieves a href.
|
||||
* Think of this as Topic().
|
||||
*
|
||||
* required action string The action/button that has been invoked by the user.
|
||||
* required params list A list of parameters attached to the button.
|
||||
*
|
||||
* return bool If the UI should be updated or not.
|
||||
**/
|
||||
/datum/proc/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
if(!ui || ui.status != UI_INTERACTIVE)
|
||||
return 1 // If UI is not interactive or usr calling Topic is not the UI user, bail.
|
||||
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* The UI's host object (usually src_object).
|
||||
* This allows modules/datums to have the UI attached to them,
|
||||
* and be a part of another object.
|
||||
**/
|
||||
/datum/proc/ui_host()
|
||||
return src // Default src.
|
||||
|
||||
/**
|
||||
* global
|
||||
*
|
||||
* Used to track the current screen.
|
||||
**/
|
||||
/datum/var/ui_screen = "home"
|
||||
|
||||
/**
|
||||
* global
|
||||
*
|
||||
* Used to track UIs for a mob.
|
||||
**/
|
||||
/mob/var/list/open_uis = list()
|
||||
|
||||
/**
|
||||
* verb
|
||||
*
|
||||
* Called by UIs when they are closed.
|
||||
* Must be a verb so winset() can call it.
|
||||
*
|
||||
* required uiref ref The UI that was closed.
|
||||
**/
|
||||
/client/verb/uiclose(ref as text)
|
||||
// Name the verb, and hide it from the user panel.
|
||||
set name = "uiclose"
|
||||
set hidden = 1
|
||||
|
||||
// Get the UI based on the ref.
|
||||
var/datum/tgui/ui = locate(ref)
|
||||
|
||||
// If we found the UI, close it.
|
||||
if(istype(ui))
|
||||
ui.close()
|
||||
// Unset machine just to be sure.
|
||||
if(src && src.mob)
|
||||
src.mob.unset_machine()
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* tgui states
|
||||
*
|
||||
* Base state and helpers for states. Just does some sanity checks, implement a state for in-depth checks.
|
||||
**/
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Checks the UI state for a mob.
|
||||
*
|
||||
* required user mob The mob who opened/is using the UI.
|
||||
* required state datum/ui_state The state to check.
|
||||
*
|
||||
* return UI_state The state of the UI.
|
||||
**/
|
||||
/datum/proc/ui_status(mob/user, datum/ui_state/state)
|
||||
var/src_object = ui_host()
|
||||
. = UI_CLOSE
|
||||
if(!state)
|
||||
return
|
||||
|
||||
if(istype(user, /mob/dead/observer))
|
||||
// If they turn on ghost AI control, admins can always interact.
|
||||
if(IsAdminGhost(user))
|
||||
. = max(., UI_INTERACTIVE)
|
||||
|
||||
// Regular ghosts can always at least view if in range.
|
||||
if(get_dist(src_object, src) < user.client.view)
|
||||
. = max(., UI_UPDATE)
|
||||
|
||||
// Check if the state allows interaction
|
||||
var/result = state.can_use_topic(src_object, user)
|
||||
. = max(., result)
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Checks if a user can use src_object's UI, and returns the state.
|
||||
* Can call a mob proc, which allows overrides for each mob.
|
||||
*
|
||||
* required src_object datum The object/datum which owns the UI.
|
||||
* required user mob The mob who opened/is using the UI.
|
||||
*
|
||||
* return UI_state The state of the UI.
|
||||
**/
|
||||
/datum/ui_state/proc/can_use_topic(src_object, mob/user)
|
||||
return UI_CLOSE // Don't allow interaction by default.
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Standard interaction/sanity checks. Different mob types may have overrides.
|
||||
*
|
||||
* return UI_state The state of the UI.
|
||||
**/
|
||||
/mob/proc/shared_ui_interaction(src_object)
|
||||
if(!client) // Close UIs if mindless.
|
||||
return UI_CLOSE
|
||||
else if(stat) // Disable UIs if unconcious.
|
||||
return UI_DISABLED
|
||||
else if(incapacitated() || lying) // Update UIs if incapicitated but concious.
|
||||
return UI_UPDATE
|
||||
return UI_INTERACTIVE
|
||||
|
||||
/mob/living/silicon/ai/shared_ui_interaction(src_object)
|
||||
if(lacks_power()) // Disable UIs if the AI is unpowered.
|
||||
return UI_DISABLED
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/robot/shared_ui_interaction(src_object)
|
||||
if(cell.charge <= 0 || lockcharge) // Disable UIs if the Borg is unpowered or locked.
|
||||
return UI_DISABLED
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Check the distance for a living mob.
|
||||
* Really only used for checks outside the context of a mob.
|
||||
* Otherwise, use shared_living_ui_distance().
|
||||
*
|
||||
* required src_object The object which owns the UI.
|
||||
* required user mob The mob who opened/is using the UI.
|
||||
*
|
||||
* return UI_state The state of the UI.
|
||||
**/
|
||||
/atom/proc/contents_ui_distance(src_object, mob/living/user)
|
||||
return user.shared_living_ui_distance(src_object) // Just call this mob's check.
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Distance versus interaction check.
|
||||
*
|
||||
* required src_object atom/movable The object which owns the UI.
|
||||
*
|
||||
* return UI_state The state of the UI.
|
||||
**/
|
||||
/mob/living/proc/shared_living_ui_distance(atom/movable/src_object)
|
||||
if(!(src_object in view(src))) // If the object is obscured, close it.
|
||||
return UI_CLOSE
|
||||
|
||||
var/dist = get_dist(src_object, src)
|
||||
if(dist <= 1) // Open and interact if 1-0 tiles away.
|
||||
return UI_INTERACTIVE
|
||||
else if(dist <= 2) // View only if 2-3 tiles away.
|
||||
return UI_UPDATE
|
||||
else if(dist <= 5) // Disable if 5 tiles away.
|
||||
return UI_DISABLED
|
||||
return UI_CLOSE // Otherwise, we got nothing.
|
||||
|
||||
/mob/living/carbon/human/shared_living_ui_distance(atom/movable/src_object)
|
||||
if(dna.check_mutation(TK) && tkMaxRangeCheck(src, src_object))
|
||||
return UI_INTERACTIVE
|
||||
return ..()
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* tgui state: admin_state
|
||||
*
|
||||
* Checks that the user is an admin, end-of-story.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/admin_state/admin_state = new()
|
||||
|
||||
/datum/ui_state/admin_state/can_use_topic(src_object, mob/user)
|
||||
if(check_rights_for(user.client, R_ADMIN))
|
||||
return UI_INTERACTIVE
|
||||
return UI_CLOSE
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
/**
|
||||
* tgui state: always_state
|
||||
*
|
||||
* Always grants the user UI_INTERACTIVE. Period.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/always_state/always_state = new()
|
||||
|
||||
/datum/ui_state/always_state/can_use_topic(src_object, mob/user)
|
||||
return UI_INTERACTIVE
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* tgui state: conscious_state
|
||||
*
|
||||
* Only checks if the user is conscious.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/conscious_state/conscious_state = new()
|
||||
|
||||
/datum/ui_state/conscious_state/can_use_topic(src_object, mob/user)
|
||||
if(user.stat == CONSCIOUS)
|
||||
return UI_INTERACTIVE
|
||||
return UI_CLOSE
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* tgui state: contained_state
|
||||
*
|
||||
* Checks that the user is inside the src_object.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/contained_state/contained_state = new()
|
||||
|
||||
/datum/ui_state/contained_state/can_use_topic(atom/src_object, mob/user)
|
||||
if(!src_object.contains(user))
|
||||
return UI_CLOSE
|
||||
return user.shared_ui_interaction(src_object)
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* tgui state: deep_inventory_state
|
||||
*
|
||||
* Checks that the src_object is in the user's deep (backpack, box, toolbox, etc) inventory.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/deep_inventory_state/deep_inventory_state = new()
|
||||
|
||||
/datum/ui_state/deep_inventory_state/can_use_topic(src_object, mob/user)
|
||||
if(!user.contains(src_object))
|
||||
return UI_CLOSE
|
||||
return user.shared_ui_interaction(src_object)
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* tgui state: default_state
|
||||
*
|
||||
* Checks a number of things -- mostly physical distance for humans and view for robots.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/default/default_state = new()
|
||||
|
||||
/datum/ui_state/default/can_use_topic(src_object, mob/user)
|
||||
return user.default_can_use_topic(src_object) // Call the individual mob-overriden procs.
|
||||
|
||||
/mob/proc/default_can_use_topic(src_object)
|
||||
return UI_CLOSE // Don't allow interaction by default.
|
||||
|
||||
/mob/living/default_can_use_topic(src_object)
|
||||
. = shared_ui_interaction(src_object)
|
||||
if(. > UI_CLOSE && loc)
|
||||
. = min(., loc.contents_ui_distance(src_object, src)) // Check the distance...
|
||||
if(. == UI_INTERACTIVE) // Non-human living mobs can only look, not touch.
|
||||
return UI_UPDATE
|
||||
|
||||
/mob/living/carbon/human/default_can_use_topic(src_object)
|
||||
. = shared_ui_interaction(src_object)
|
||||
if(. > UI_CLOSE)
|
||||
. = min(., shared_living_ui_distance(src_object)) // Check the distance...
|
||||
// Derp a bit if we have brain loss.
|
||||
if(prob(getBrainLoss()))
|
||||
return UI_UPDATE
|
||||
|
||||
/mob/living/silicon/robot/default_can_use_topic(src_object)
|
||||
. = shared_ui_interaction(src_object)
|
||||
if(. <= UI_DISABLED)
|
||||
return
|
||||
|
||||
// Robots can interact with anything they can see.
|
||||
if(get_dist(src, src_object) <= client.view)
|
||||
return UI_INTERACTIVE
|
||||
return UI_DISABLED // Otherwise they can keep the UI open.
|
||||
|
||||
/mob/living/silicon/ai/default_can_use_topic(src_object)
|
||||
. = shared_ui_interaction(src_object)
|
||||
if(. < UI_INTERACTIVE)
|
||||
return
|
||||
|
||||
// The AI can interact with anything it can see nearby, or with cameras.
|
||||
if((get_dist(src, src_object) <= client.view) || cameranet.checkTurfVis(get_turf_pixel(src_object)))
|
||||
return UI_INTERACTIVE
|
||||
return UI_CLOSE
|
||||
|
||||
/mob/living/simple_animal/drone/default_can_use_topic(src_object)
|
||||
. = shared_ui_interaction(src_object)
|
||||
if(. > UI_CLOSE)
|
||||
. = min(., shared_living_ui_distance(src_object)) // Drones can only use things they're near.
|
||||
|
||||
/mob/living/silicon/pai/default_can_use_topic(src_object)
|
||||
// pAIs can only use themselves and the owner's radio.
|
||||
if((src_object == src || src_object == radio) && !stat)
|
||||
return UI_INTERACTIVE
|
||||
else
|
||||
return ..()
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* tgui state: hands_state
|
||||
*
|
||||
* Checks that the src_object is in the user's hands.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/hands_state/hands_state = new()
|
||||
|
||||
/datum/ui_state/hands_state/can_use_topic(src_object, mob/user)
|
||||
. = user.shared_ui_interaction(src_object)
|
||||
if(. > UI_CLOSE)
|
||||
return min(., user.hands_can_use_topic(src_object))
|
||||
|
||||
/mob/proc/hands_can_use_topic(src_object)
|
||||
return UI_CLOSE
|
||||
|
||||
/mob/living/hands_can_use_topic(src_object)
|
||||
if(src_object in get_both_hands(src))
|
||||
return UI_INTERACTIVE
|
||||
return UI_CLOSE
|
||||
|
||||
/mob/living/silicon/robot/hands_can_use_topic(src_object)
|
||||
if(activated(src_object))
|
||||
return UI_INTERACTIVE
|
||||
return UI_CLOSE
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
/**
|
||||
* tgui state: human_adjacent_state
|
||||
*
|
||||
* In addition to default checks, only allows interaction for a
|
||||
* human adjacent user.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/human_adjacent_state/human_adjacent_state = new()
|
||||
|
||||
/datum/ui_state/human_adjacent_state/can_use_topic(src_object, mob/user)
|
||||
. = user.default_can_use_topic(src_object)
|
||||
|
||||
var/dist = get_dist(src_object, user)
|
||||
if((dist > 1) || (!ishuman(user)))
|
||||
// Can't be used unless adjacent and human, even with TK
|
||||
. = min(., UI_UPDATE)
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* tgui state: inventory_state
|
||||
*
|
||||
* Checks that the src_object is in the user's top-level (hand, ear, pocket, belt, etc) inventory.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/inventory_state/inventory_state = new()
|
||||
|
||||
/datum/ui_state/inventory_state/can_use_topic(src_object, mob/user)
|
||||
if(!(src_object in user))
|
||||
return UI_CLOSE
|
||||
return user.shared_ui_interaction(src_object)
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* tgui state: not_incapacitated_state
|
||||
*
|
||||
* Checks that the user isn't incapacitated
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/not_incapacitated_state/not_incapacitated_state = new()
|
||||
|
||||
/**
|
||||
* tgui state: not_incapacitated_turf_state
|
||||
*
|
||||
* Checks that the user isn't incapacitated and that their loc is a turf
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/not_incapacitated_state/not_incapacitated_turf_state = new(no_turfs = TRUE)
|
||||
|
||||
/datum/ui_state/not_incapacitated_state
|
||||
var/turf_check = FALSE
|
||||
|
||||
/datum/ui_state/not_incapacitated_state/New(loc, no_turfs = FALSE)
|
||||
..()
|
||||
turf_check = no_turfs
|
||||
|
||||
/datum/ui_state/not_incapacitated_state/can_use_topic(src_object, mob/user)
|
||||
if(user.stat)
|
||||
return UI_CLOSE
|
||||
if(user.incapacitated() || user.lying || (turf_check && !isturf(user.loc)))
|
||||
return UI_DISABLED
|
||||
return UI_INTERACTIVE
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* tgui state: notcontained_state
|
||||
*
|
||||
* Checks that the user is not inside src_object, and then makes the default checks.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/notcontained_state/notcontained_state = new()
|
||||
|
||||
/datum/ui_state/notcontained_state/can_use_topic(atom/src_object, mob/user)
|
||||
. = user.shared_ui_interaction(src_object)
|
||||
if(. > UI_CLOSE)
|
||||
return min(., user.notcontained_can_use_topic(src_object))
|
||||
|
||||
/mob/proc/notcontained_can_use_topic(src_object)
|
||||
return UI_CLOSE
|
||||
|
||||
/mob/living/notcontained_can_use_topic(atom/src_object)
|
||||
if(src_object.contains(src))
|
||||
return UI_CLOSE // Close if we're inside it.
|
||||
return default_can_use_topic(src_object)
|
||||
|
||||
/mob/living/silicon/notcontained_can_use_topic(src_object)
|
||||
return default_can_use_topic(src_object) // Silicons use default bevhavior.
|
||||
|
||||
/mob/living/simple_animal/drone/notcontained_can_use_topic(src_object)
|
||||
return default_can_use_topic(src_object) // Drones use default bevhavior.
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* tgui state: physical_state
|
||||
*
|
||||
* Short-circuits the default state to only check physical distance.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/physical/physical_state = new()
|
||||
|
||||
/datum/ui_state/physical/can_use_topic(src_object, mob/user)
|
||||
. = user.shared_ui_interaction(src_object)
|
||||
if(. > UI_CLOSE)
|
||||
return min(., user.physical_can_use_topic(src_object))
|
||||
|
||||
/mob/proc/physical_can_use_topic(src_object)
|
||||
return UI_CLOSE
|
||||
|
||||
/mob/living/physical_can_use_topic(src_object)
|
||||
return shared_living_ui_distance(src_object)
|
||||
|
||||
/mob/living/silicon/physical_can_use_topic(src_object)
|
||||
return max(UI_UPDATE, shared_living_ui_distance(src_object)) // Silicons can always see.
|
||||
|
||||
/mob/living/silicon/ai/physical_can_use_topic(src_object)
|
||||
return UI_UPDATE // AIs are not physical.
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* tgui state: self_state
|
||||
*
|
||||
* Only checks that the user and src_object are the same.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/self_state/self_state = new()
|
||||
|
||||
/datum/ui_state/self_state/can_use_topic(src_object, mob/user)
|
||||
if(src_object != user)
|
||||
return UI_CLOSE
|
||||
return user.shared_ui_interaction(src_object)
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* tgui state: z_state
|
||||
*
|
||||
* Only checks that the Z-level of the user and src_object are the same.
|
||||
**/
|
||||
|
||||
/var/global/datum/ui_state/z_state/z_state = new()
|
||||
|
||||
/datum/ui_state/z_state/can_use_topic(src_object, mob/user)
|
||||
var/turf/turf_obj = get_turf(src_object)
|
||||
var/turf/turf_usr = get_turf(user)
|
||||
if(turf_obj && turf_usr && turf_obj.z == turf_usr.z)
|
||||
return UI_INTERACTIVE
|
||||
return UI_CLOSE
|
||||
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* tgui subsystem
|
||||
*
|
||||
* Contains all tgui state and subsystem code.
|
||||
**/
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Get a open UI given a user, src_object, and ui_key and try to update it with data.
|
||||
*
|
||||
* required user mob The mob who opened/is using the UI.
|
||||
* required src_object datum The object/datum which owns the UI.
|
||||
* required ui_key string The ui_key of the UI.
|
||||
* optional ui datum/tgui The UI to be updated, if it exists.
|
||||
* optional force_open bool If the UI should be re-opened instead of updated.
|
||||
*
|
||||
* return datum/tgui The found UI.
|
||||
**/
|
||||
/datum/subsystem/tgui/proc/try_update_ui(mob/user, datum/src_object, ui_key, datum/tgui/ui, force_open = 0)
|
||||
if(isnull(ui)) // No UI was passed, so look for one.
|
||||
ui = get_open_ui(user, src_object, ui_key)
|
||||
|
||||
if(!isnull(ui))
|
||||
var/data = src_object.ui_data(user) // Get data from the src_object.
|
||||
if(!force_open) // UI is already open; update it.
|
||||
ui.push_data(data)
|
||||
else // Re-open it anyways.
|
||||
ui.reinitialize(null, data)
|
||||
return ui // We found the UI, return it.
|
||||
else
|
||||
return null // We couldn't find a UI.
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Get a open UI given a user, src_object, and ui_key.
|
||||
*
|
||||
* required user mob The mob who opened/is using the UI.
|
||||
* required src_object datum The object/datum which owns the UI.
|
||||
* required ui_key string The ui_key of the UI.
|
||||
*
|
||||
* return datum/tgui The found UI.
|
||||
**/
|
||||
/datum/subsystem/tgui/proc/get_open_ui(mob/user, datum/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))
|
||||
return null // No UIs open.
|
||||
else if(isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list))
|
||||
return null // No UIs open for this object.
|
||||
|
||||
for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) // Find UIs for this object.
|
||||
if(ui.user == user) // Make sure we have the right user
|
||||
return ui
|
||||
|
||||
return null // Couldn't find a UI!
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Update all UIs attached to src_object.
|
||||
*
|
||||
* required src_object datum The object/datum which owns the UIs.
|
||||
*
|
||||
* return int The number of UIs updated.
|
||||
**/
|
||||
/datum/subsystem/tgui/proc/update_uis(datum/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 // Couldn't find any UIs for this object.
|
||||
|
||||
var/update_count = 0
|
||||
for(var/ui_key in open_uis[src_object_key])
|
||||
for(var/datum/tgui/ui in open_uis[src_object_key][ui_key])
|
||||
if(ui && ui.src_object && ui.user && ui.src_object.ui_host()) // Check the UI is valid.
|
||||
ui.process(force = 1) // Update the UI.
|
||||
update_count++ // Count each UI we update.
|
||||
return update_count
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Close all UIs attached to src_object.
|
||||
*
|
||||
* required src_object datum The object/datum which owns the UIs.
|
||||
*
|
||||
* return int The number of UIs closed.
|
||||
**/
|
||||
/datum/subsystem/tgui/proc/close_uis(datum/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 // Couldn't find any UIs for this object.
|
||||
|
||||
var/close_count = 0
|
||||
for(var/ui_key in open_uis[src_object_key])
|
||||
for(var/datum/tgui/ui in open_uis[src_object_key][ui_key])
|
||||
if(ui && ui.src_object && ui.user && ui.src_object.ui_host()) // Check the UI is valid.
|
||||
ui.close() // Close the UI.
|
||||
close_count++ // Count each UI we close.
|
||||
return close_count
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Update all UIs belonging to a user.
|
||||
*
|
||||
* required user mob The mob who opened/is using the UI.
|
||||
* optional src_object datum If provided, only update UIs belonging this src_object.
|
||||
* optional ui_key string If provided, only update UIs with this UI key.
|
||||
*
|
||||
* return int The number of UIs updated.
|
||||
**/
|
||||
/datum/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object = null, ui_key = null)
|
||||
if(isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0)
|
||||
return 0 // Couldn't find any UIs for this user.
|
||||
|
||||
var/update_count = 0
|
||||
for(var/datum/tgui/ui in user.open_uis)
|
||||
if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key))
|
||||
ui.process(force = 1) // Update the UI.
|
||||
update_count++ // Count each UI we upadte.
|
||||
return update_count
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Close all UIs belonging to a user.
|
||||
*
|
||||
* required user mob The mob who opened/is using the UI.
|
||||
* optional src_object datum If provided, only close UIs belonging this src_object.
|
||||
* optional ui_key string If provided, only close UIs with this UI key.
|
||||
*
|
||||
* return int The number of UIs closed.
|
||||
**/
|
||||
/datum/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object = null, ui_key = null)
|
||||
if(isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0)
|
||||
return 0 // Couldn't find any UIs for this user.
|
||||
|
||||
var/close_count = 0
|
||||
for(var/datum/tgui/ui in user.open_uis)
|
||||
if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key))
|
||||
ui.close() // Close the UI.
|
||||
close_count++ // Count each UI we close.
|
||||
return close_count
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Add a UI to the list of open UIs.
|
||||
*
|
||||
* required ui datum/tgui The UI to be added.
|
||||
**/
|
||||
/datum/subsystem/tgui/proc/on_open(datum/tgui/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()) // Make a list for the ui_key and src_object.
|
||||
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() // Make a list for the ui_key.
|
||||
|
||||
// Append the UI to all the lists.
|
||||
ui.user.open_uis |= ui
|
||||
var/list/uis = open_uis[src_object_key][ui.ui_key]
|
||||
uis |= ui
|
||||
processing_uis |= ui
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Remove a UI from the list of open UIs.
|
||||
*
|
||||
* required ui datum/tgui The UI to be removed.
|
||||
*
|
||||
* return bool If the UI was removed or not.
|
||||
**/
|
||||
/datum/subsystem/tgui/proc/on_close(datum/tgui/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 // It wasn't open.
|
||||
else if(isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list))
|
||||
return 0 // It wasn't open.
|
||||
|
||||
processing_uis.Remove(ui) // Remove it from the list of processing UIs.
|
||||
if(ui.user) // If the user exists, remove it from them too.
|
||||
ui.user.open_uis.Remove(ui)
|
||||
var/list/uis = open_uis[src_object_key][ui.ui_key] // Remove it from the list of open UIs.
|
||||
uis.Remove(ui)
|
||||
return 1 // Let the caller know we did it.
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Handle client logout, by closing all their UIs.
|
||||
*
|
||||
* required user mob The mob which logged out.
|
||||
*
|
||||
* return int The number of UIs closed.
|
||||
**/
|
||||
/datum/subsystem/tgui/proc/on_logout(mob/user)
|
||||
return close_user_uis(user)
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Handle clients switching mobs, by transfering their UIs.
|
||||
*
|
||||
* required user source The client's original mob.
|
||||
* required user target The client's new mob.
|
||||
*
|
||||
* return bool If the UIs were transferred.
|
||||
**/
|
||||
/datum/subsystem/tgui/proc/on_transfer(mob/source, mob/target)
|
||||
if(!source || isnull(source.open_uis) || !istype(source.open_uis, /list) || open_uis.len == 0)
|
||||
return 0 // The old mob had no open UIs.
|
||||
|
||||
if(isnull(target.open_uis) || !istype(target.open_uis, /list))
|
||||
target.open_uis = list() // Create a list for the new mob if needed.
|
||||
|
||||
for(var/datum/tgui/ui in source.open_uis)
|
||||
ui.user = target // Inform the UIs of their new owner.
|
||||
target.open_uis.Add(ui) // Transfer all the UIs.
|
||||
|
||||
source.open_uis.Cut() // Clear the old list.
|
||||
return 1 // Let the caller know we did it.
|
||||
@@ -0,0 +1,369 @@
|
||||
/**
|
||||
* tgui
|
||||
*
|
||||
* /tg/station user interface library
|
||||
**/
|
||||
|
||||
/**
|
||||
* tgui datum (represents a UI).
|
||||
**/
|
||||
/datum/tgui
|
||||
var/mob/user // The mob who opened/is using the UI.
|
||||
var/datum/src_object // The object which owns the UI.
|
||||
var/title // The title of te UI.
|
||||
var/ui_key // The ui_key of the UI. This allows multiple UIs for one src_object.
|
||||
var/window_id // The window_id for browse() and onclose().
|
||||
var/width = 0 // The window width.
|
||||
var/height = 0 // The window height
|
||||
var/window_options = list( // Extra options to winset().
|
||||
"focus" = FALSE,
|
||||
"titlebar" = TRUE,
|
||||
"can_resize" = TRUE,
|
||||
"can_minimize" = TRUE,
|
||||
"can_maximize" = FALSE,
|
||||
"can_close" = TRUE,
|
||||
"auto_format" = FALSE
|
||||
)
|
||||
var/style = "nanotrasen" // The style to be used for this UI.
|
||||
var/interface // The interface (template) to be used for this UI.
|
||||
var/autoupdate = TRUE // Update the UI every MC tick.
|
||||
var/initialized = FALSE // If the UI has been initialized yet.
|
||||
var/list/initial_data // The data (and datastructure) used to initialize the UI.
|
||||
var/status = UI_INTERACTIVE // The status/visibility of the UI.
|
||||
var/datum/ui_state/state = null // Topic state used to determine status/interactability.
|
||||
var/datum/tgui/master_ui // The parent UI.
|
||||
var/list/datum/tgui/children = list() // Children of this UI.
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Create a new UI.
|
||||
*
|
||||
* required user mob The mob who opened/is using the UI.
|
||||
* required src_object datum The object or datum which owns the UI.
|
||||
* required ui_key string The ui_key of the UI.
|
||||
* required interface string The interface used to render the UI.
|
||||
* optional title string The title of the UI.
|
||||
* optional width int The window width.
|
||||
* optional height int The window height.
|
||||
* optional master_ui datum/tgui The parent UI.
|
||||
* optional state datum/ui_state The state used to determine status.
|
||||
*
|
||||
* return datum/tgui The requested UI.
|
||||
**/
|
||||
/datum/tgui/New(mob/user, datum/src_object, ui_key, interface, title, width = 0, height = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state)
|
||||
src.user = user
|
||||
src.src_object = src_object
|
||||
src.ui_key = ui_key
|
||||
src.window_id = "\ref[src_object]-[ui_key]"
|
||||
|
||||
set_interface(interface)
|
||||
|
||||
if(title)
|
||||
src.title = sanitize(title)
|
||||
if(width)
|
||||
src.width = width
|
||||
if(height)
|
||||
src.height = height
|
||||
|
||||
src.master_ui = master_ui
|
||||
if(master_ui)
|
||||
master_ui.children += src
|
||||
src.state = state
|
||||
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/tgui)
|
||||
assets.send(user)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Open this UI (and initialize it with data).
|
||||
**/
|
||||
/datum/tgui/proc/open()
|
||||
if(!user.client)
|
||||
return // Bail if there is no client.
|
||||
|
||||
update_status(push = 0) // Update the window status.
|
||||
if(status < UI_UPDATE)
|
||||
return // Bail if we're not supposed to open.
|
||||
|
||||
if(!initial_data)
|
||||
set_initial_data(src_object.ui_data(user)) // Get the UI data.
|
||||
|
||||
var/window_size = ""
|
||||
if(width && height) // If we have a width and height, use them.
|
||||
window_size = "size=[width]x[height];"
|
||||
|
||||
var/debugable = check_rights_for(user.client, R_DEBUG)
|
||||
user << browse(get_html(debugable), "window=[window_id];[window_size][list2params(window_options)]") // Open the window.
|
||||
winset(user, window_id, "on-close=\"uiclose \ref[src]\"") // Instruct the client to signal UI when the window is closed.
|
||||
SStgui.on_open(src)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Reinitialize the UI.
|
||||
* (Possibly with a new interface and/or data).
|
||||
*
|
||||
* optional template string The name of the new interface.
|
||||
* optional data list The new initial data.
|
||||
**/
|
||||
/datum/tgui/proc/reinitialize(interface, list/data)
|
||||
if(interface)
|
||||
set_interface(interface) // Set a new interface.
|
||||
if(data)
|
||||
set_initial_data(data) // Replace the initial_data.
|
||||
open()
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Close the UI, and all its children.
|
||||
**/
|
||||
/datum/tgui/proc/close()
|
||||
user << browse(null, "window=[window_id]") // Close the window.
|
||||
SStgui.on_close(src)
|
||||
for(var/datum/tgui/child in children) // Loop through and close all children.
|
||||
child.close()
|
||||
children.Cut()
|
||||
state = null
|
||||
master_ui = null
|
||||
qdel(src)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Sets the browse() window options for this UI.
|
||||
*
|
||||
* required window_options list The window options to set.
|
||||
**/
|
||||
/datum/tgui/proc/set_window_options(list/window_options)
|
||||
src.window_options = window_options
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Set the style for this UI.
|
||||
*
|
||||
* required style string The new UI style.
|
||||
**/
|
||||
/datum/tgui/proc/set_style(style)
|
||||
src.style = lowertext(style)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Set the interface (template) for this UI.
|
||||
*
|
||||
* required interface string The new UI interface.
|
||||
**/
|
||||
/datum/tgui/proc/set_interface(interface)
|
||||
src.interface = lowertext(interface)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Enable/disable auto-updating of the UI.
|
||||
*
|
||||
* required state bool Enable/disable auto-updating.
|
||||
**/
|
||||
/datum/tgui/proc/set_autoupdate(state = 1)
|
||||
autoupdate = state
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Set the data to initialize the UI with.
|
||||
* The datastructure cannot be changed by subsequent updates.
|
||||
*
|
||||
* optional data list The data/datastructure to initialize the UI with.
|
||||
**/
|
||||
/datum/tgui/proc/set_initial_data(list/data)
|
||||
initial_data = data
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Generate HTML for this UI.
|
||||
*
|
||||
* optional bool inline If the JSON should be inlined into the HTML (for debugging).
|
||||
*
|
||||
* return string UI HTML output.
|
||||
**/
|
||||
/datum/tgui/proc/get_html(var/inline)
|
||||
var/html
|
||||
// Poplate HTML with JSON if we're supposed to inline.
|
||||
if(inline)
|
||||
html = replacetextEx(SStgui.basehtml, "{}", get_json(initial_data))
|
||||
else
|
||||
html = SStgui.basehtml
|
||||
html = replacetextEx(html, "\[ref]", "\ref[src]")
|
||||
html = replacetextEx(html, "\[style]", style)
|
||||
return html
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Get the config data/datastructure to initialize the UI with.
|
||||
*
|
||||
* return list The config data.
|
||||
**/
|
||||
/datum/tgui/proc/get_config_data()
|
||||
var/list/config_data = list(
|
||||
"title" = title,
|
||||
"status" = status,
|
||||
"screen" = src_object.ui_screen,
|
||||
"style" = style,
|
||||
"interface" = interface,
|
||||
"fancy" = user.client.prefs.tgui_fancy,
|
||||
"locked" = user.client.prefs.tgui_lock,
|
||||
"window" = window_id,
|
||||
"ref" = "\ref[src]",
|
||||
"user" = list(
|
||||
"name" = user.name,
|
||||
"ref" = "\ref[user]"
|
||||
),
|
||||
"srcObject" = list(
|
||||
"name" = "[src_object]",
|
||||
"ref" = "\ref[src_object]"
|
||||
)
|
||||
)
|
||||
return config_data
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Package the data to send to the UI, as JSON.
|
||||
* This includes the UI data and config_data.
|
||||
*
|
||||
* return string The packaged JSON.
|
||||
**/
|
||||
/datum/tgui/proc/get_json(list/data)
|
||||
var/list/json_data = list()
|
||||
|
||||
json_data["config"] = get_config_data()
|
||||
if(!isnull(data))
|
||||
json_data["data"] = data
|
||||
|
||||
// Generate the JSON.
|
||||
var/json = json_encode(json_data)
|
||||
// Strip #255/improper.
|
||||
json = replacetext(json, "\proper", "")
|
||||
json = replacetext(json, "\improper", "")
|
||||
return json
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Handle clicks from the UI.
|
||||
* Call the src_object's ui_act() if status is UI_INTERACTIVE.
|
||||
* If the src_object's ui_act() returns 1, update all UIs attacked to it.
|
||||
**/
|
||||
/datum/tgui/Topic(href, href_list)
|
||||
if(user != usr)
|
||||
return // Something is not right here.
|
||||
|
||||
var/action = href_list["action"]
|
||||
var/params = href_list; params -= "action"
|
||||
|
||||
switch(action)
|
||||
if("tgui:initialize")
|
||||
user << output(url_encode(get_json(initial_data)), "[window_id].browser:initialize")
|
||||
initialized = TRUE
|
||||
if("tgui:view")
|
||||
if(params["screen"])
|
||||
src_object.ui_screen = params["screen"]
|
||||
SStgui.update_uis(src_object)
|
||||
if("tgui:link")
|
||||
user << link(params["url"])
|
||||
if("tgui:fancy")
|
||||
user.client.prefs.tgui_fancy = TRUE
|
||||
if("tgui:nofrills")
|
||||
user.client.prefs.tgui_fancy = FALSE
|
||||
else
|
||||
update_status(push = 0) // Update the window state.
|
||||
if(src_object.ui_act(action, params, src, state)) // Call ui_act() on the src_object.
|
||||
SStgui.update_uis(src_object) // Update if the object requested it.
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Update the UI.
|
||||
* Only updates the data if update is true, otherwise only updates the status.
|
||||
*
|
||||
* optional force bool If the UI should be forced to update.
|
||||
**/
|
||||
/datum/tgui/process(force = 0)
|
||||
var/datum/host = src_object.ui_host()
|
||||
if(!src_object || !host || !user) // If the object or user died (or something else), abort.
|
||||
close()
|
||||
return
|
||||
|
||||
if(status && (force || autoupdate))
|
||||
update() // Update the UI if the status and update settings allow it.
|
||||
else
|
||||
update_status(push = 1) // Otherwise only update status.
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Push data to an already open UI.
|
||||
*
|
||||
* required data list The data to send.
|
||||
* optional force bool If the update should be sent regardless of state.
|
||||
**/
|
||||
/datum/tgui/proc/push_data(data, force = 0)
|
||||
update_status(push = 0) // Update the window state.
|
||||
if(!initialized)
|
||||
return // Cannot update UI if it is not set up yet.
|
||||
if(status <= UI_DISABLED && !force)
|
||||
return // Cannot update UI, we have no visibility.
|
||||
|
||||
// Send the new JSON to the update() Javascript function.
|
||||
user << output(url_encode(get_json(data)), "[window_id].browser:update")
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Updates the UI by interacting with the src_object again, which will hopefully
|
||||
* call try_ui_update on it.
|
||||
*
|
||||
* optional force_open bool If force_open should be passed to ui_interact.
|
||||
**/
|
||||
/datum/tgui/proc/update(force_open = 0)
|
||||
src_object.ui_interact(user, ui_key, src, force_open, master_ui, state)
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Update the status/visibility of the UI for its user.
|
||||
*
|
||||
* optional push bool Push an update to the UI (an update is always sent for UI_DISABLED).
|
||||
**/
|
||||
/datum/tgui/proc/update_status(push = 0)
|
||||
var/status = src_object.ui_status(user, state)
|
||||
if(master_ui)
|
||||
status = min(status, master_ui.status)
|
||||
|
||||
set_status(status, push)
|
||||
if(status == UI_CLOSE)
|
||||
close()
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Set the status/visibility of the UI.
|
||||
*
|
||||
* required status int The status to set (UI_CLOSE/UI_DISABLED/UI_UPDATE/UI_INTERACTIVE).
|
||||
* optional push bool Push an update to the UI (an update is always sent for UI_DISABLED).
|
||||
**/
|
||||
/datum/tgui/proc/set_status(status, push = 0)
|
||||
if(src.status != status) // Only update if status has changed.
|
||||
if(src.status == UI_DISABLED)
|
||||
src.status = status
|
||||
if(push)
|
||||
update()
|
||||
else
|
||||
src.status = status
|
||||
if(status == UI_DISABLED || push) // Update if the UI just because disabled, or a push is requested.
|
||||
push_data(null, force = 1)
|
||||
Reference in New Issue
Block a user