mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 01:34:01 +00:00
[MIRROR] [TM Candidate] Overhauls orbit and POI code to fix part of issue #61508 where players could observe /mob/living/new_player on the lobby. (#8339)
* [TM Candidate] Overhauls orbit and POI code to fix part of issue #61508 where players could observe /mob/living/new_player on the lobby. * E * Missed merge Co-authored-by: Timberpoes <silent_insomnia_pp@hotmail.co.uk> Co-authored-by: Gandalf <jzo123@hotmail.com>
This commit is contained in:
@@ -14,7 +14,6 @@
|
||||
/area/shuttle/supply)
|
||||
"e" = (
|
||||
/obj/machinery/light/directional/west,
|
||||
/obj/machinery/computer/cargo_shuttle_console,
|
||||
/turf/open/floor/iron/dark/blue/side{
|
||||
dir = 9
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#define COMSIG_GLOB_VAR_EDIT "!var_edit"
|
||||
/// called after an explosion happened : (epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range)
|
||||
#define COMSIG_GLOB_EXPLOSION "!explosion"
|
||||
/// mob was created somewhere : (mob)
|
||||
/// Called from base of /mob/Initialise : (mob)
|
||||
#define COMSIG_GLOB_MOB_CREATED "!mob_created"
|
||||
/// mob died somewhere : (mob/living, gibbed)
|
||||
#define COMSIG_GLOB_MOB_DEATH "!mob_death"
|
||||
@@ -1485,3 +1485,9 @@
|
||||
#define COMSIG_TICKER_ROUND_STARTING "comsig_ticker_round_starting"
|
||||
|
||||
#define COMSIG_GREYSCALE_CONFIG_REFRESHED "greyscale_config_refreshed"
|
||||
|
||||
// Point of interest signals
|
||||
/// Sent from base of /datum/controller/subsystem/points_of_interest/proc/on_poi_element_added : (atom/new_poi)
|
||||
#define COMSIG_ADDED_POINT_OF_INTEREST "added_point_of_interest"
|
||||
/// Sent from base of /datum/controller/subsystem/points_of_interest/proc/on_poi_element_removed : (atom/old_poi)
|
||||
#define COMSIG_REMOVED_POINT_OF_INTEREST "removed_point_of_interest"
|
||||
|
||||
@@ -331,10 +331,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
#define TRAIT_NOBLEED "nobleed" //This carbon doesn't bleed
|
||||
/// This atom can ignore the "is on a turf" check for simple AI datum attacks, allowing them to attack from bags or lockers as long as any other conditions are met
|
||||
#define TRAIT_AI_BAGATTACK "bagattack"
|
||||
///When people are floating from zero-grav or something, we can move around freely!
|
||||
#define TRAIT_FREE_FLOAT_MOVEMENT "free_float_movement"
|
||||
/// This mobs bodyparts are invisible but still clickable.
|
||||
#define TRAIT_INVISIBLE_MAN "invisible_man"
|
||||
/// Don't draw external organs/species features like wings, horns, frills and stuff
|
||||
#define TRAIT_HIDE_EXTERNAL_ORGANS "hide_external_organs"
|
||||
///When people are floating from zero-grav or something, we can move around freely!
|
||||
#define TRAIT_FREE_FLOAT_MOVEMENT "free_float_movement"
|
||||
// You can stare into the abyss, but it does not stare back.
|
||||
// You're immune to the hallucination effect of the supermatter, either
|
||||
// through force of will, or equipment. Present on /mob or /datum/mind
|
||||
|
||||
@@ -81,6 +81,41 @@
|
||||
};\
|
||||
} while(FALSE)
|
||||
|
||||
/**
|
||||
* Custom binary search sorted insert utilising comparison procs instead of vars.
|
||||
* INPUT: Object to be inserted
|
||||
* LIST: List to insert object into
|
||||
* TYPECONT: The typepath of the contents of the list
|
||||
* COMPARE: The object to compare against, usualy the same as INPUT
|
||||
* COMPARISON: The plaintext name of a proc on INPUT that takes a single argument to accept a single element from LIST and returns a positive, negative or zero number to perform a comparison.
|
||||
* COMPTYPE: How should the values be compared? Either COMPARE_KEY or COMPARE_VALUE.
|
||||
*/
|
||||
#define BINARY_INSERT_PROC_COMPARE(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \
|
||||
do {\
|
||||
var/list/__BIN_LIST = LIST;\
|
||||
var/__BIN_CTTL = length(__BIN_LIST);\
|
||||
if(!__BIN_CTTL) {\
|
||||
__BIN_LIST += INPUT;\
|
||||
} else {\
|
||||
var/__BIN_LEFT = 1;\
|
||||
var/__BIN_RIGHT = __BIN_CTTL;\
|
||||
var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
|
||||
var ##TYPECONT/__BIN_ITEM;\
|
||||
while(__BIN_LEFT < __BIN_RIGHT) {\
|
||||
__BIN_ITEM = COMPTYPE;\
|
||||
if(__BIN_ITEM.##COMPARISON(COMPARE) <= 0) {\
|
||||
__BIN_LEFT = __BIN_MID + 1;\
|
||||
} else {\
|
||||
__BIN_RIGHT = __BIN_MID;\
|
||||
};\
|
||||
__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
|
||||
};\
|
||||
__BIN_ITEM = COMPTYPE;\
|
||||
__BIN_MID = __BIN_ITEM.##COMPARISON(COMPARE) > 0 ? __BIN_MID : __BIN_MID + 1;\
|
||||
__BIN_LIST.Insert(__BIN_MID, INPUT);\
|
||||
};\
|
||||
} while(FALSE)
|
||||
|
||||
//Returns a list in plain english as a string
|
||||
/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" )
|
||||
var/total = length(input)
|
||||
|
||||
@@ -133,23 +133,24 @@
|
||||
SSblackbox.record_feedback("associative", "antagonists", 1, antag_info)
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/record_nuke_disk_location()
|
||||
var/obj/item/disk/nuclear/N = locate() in GLOB.poi_list
|
||||
if(N)
|
||||
var/disk_count = 1
|
||||
for(var/obj/item/disk/nuclear/nuke_disk as anything in SSpoints_of_interest.real_nuclear_disks)
|
||||
var/list/data = list()
|
||||
var/turf/T = get_turf(N)
|
||||
if(T)
|
||||
data["x"] = T.x
|
||||
data["y"] = T.y
|
||||
data["z"] = T.z
|
||||
var/atom/outer = get_atom_on_turf(N,/mob/living)
|
||||
if(outer != N)
|
||||
var/turf/disk_turf = get_turf(nuke_disk)
|
||||
if(disk_turf)
|
||||
data["x"] = disk_turf.x
|
||||
data["y"] = disk_turf.y
|
||||
data["z"] = disk_turf.z
|
||||
var/atom/outer = get_atom_on_turf(nuke_disk, /mob/living)
|
||||
if(outer != nuke_disk)
|
||||
if(isliving(outer))
|
||||
var/mob/living/L = outer
|
||||
data["holder"] = L.real_name
|
||||
var/mob/living/disk_holder = outer
|
||||
data["holder"] = disk_holder.real_name
|
||||
else
|
||||
data["holder"] = outer.name
|
||||
|
||||
SSblackbox.record_feedback("associative", "roundend_nukedisk", 1 , data)
|
||||
SSblackbox.record_feedback("associative", "roundend_nukedisk", disk_count, data)
|
||||
disk_count++
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/gather_newscaster()
|
||||
var/json_file = file("[GLOB.log_directory]/newscaster.json")
|
||||
|
||||
@@ -228,59 +228,37 @@ Turf and target are separate in case you want to teleport some distance from a t
|
||||
/proc/ionnum() //! is at the start to prevent us from changing say modes via get_message_mode()
|
||||
return "![pick("!","@","#","$","%","^","&")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]"
|
||||
|
||||
//Returns a list of all items of interest with their name
|
||||
/proc/getpois(mobs_only = FALSE, skip_mindless = FALSE, specify_dead_role = TRUE)
|
||||
var/list/mobs = sortmobs()
|
||||
var/list/namecounts = list()
|
||||
var/list/pois = list()
|
||||
for(var/mob/M in mobs)
|
||||
if(skip_mindless && (!M.mind && !M.ckey))
|
||||
if(!isbot(M) && !iscameramob(M) && !ismegafauna(M))
|
||||
continue
|
||||
if(M.client && M.client.holder && M.client.holder.fakekey) //stealthmins
|
||||
continue
|
||||
var/name = avoid_assoc_duplicate_keys(M.name, namecounts) + M.get_realname_string()
|
||||
|
||||
if(M.stat == DEAD && specify_dead_role)
|
||||
if(isobserver(M))
|
||||
name += " \[ghost\]"
|
||||
else
|
||||
name += " \[dead\]"
|
||||
pois[name] = M
|
||||
|
||||
if(!mobs_only)
|
||||
for(var/atom/A in GLOB.poi_list)
|
||||
if(!A || !A.loc)
|
||||
continue
|
||||
pois[avoid_assoc_duplicate_keys(A.name, namecounts)] = A
|
||||
|
||||
return pois
|
||||
//Orders mobs by type then by name
|
||||
/// Orders mobs by type then by name. Accepts optional arg to sort a custom list, otherwise copies GLOB.mob_list.
|
||||
/proc/sortmobs()
|
||||
var/list/moblist = list()
|
||||
var/list/sortmob = sortNames(GLOB.mob_list)
|
||||
for(var/mob/living/silicon/ai/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/camera/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/living/silicon/pai/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/living/silicon/robot/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/living/carbon/human/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/living/brain/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/living/carbon/alien/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/dead/observer/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/dead/new_player/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/living/simple_animal/slime/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/living/simple_animal/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/living/silicon/ai/mob_to_sort in sortmob)
|
||||
moblist += mob_to_sort
|
||||
for(var/mob/camera/mob_to_sort in sortmob)
|
||||
moblist += mob_to_sort
|
||||
for(var/mob/living/silicon/pai/mob_to_sort in sortmob)
|
||||
moblist += mob_to_sort
|
||||
for(var/mob/living/silicon/robot/mob_to_sort in sortmob)
|
||||
moblist += mob_to_sort
|
||||
for(var/mob/living/carbon/human/mob_to_sort in sortmob)
|
||||
moblist += mob_to_sort
|
||||
for(var/mob/living/brain/mob_to_sort in sortmob)
|
||||
moblist += mob_to_sort
|
||||
for(var/mob/living/carbon/alien/mob_to_sort in sortmob)
|
||||
moblist += mob_to_sort
|
||||
for(var/mob/dead/observer/mob_to_sort in sortmob)
|
||||
moblist += mob_to_sort
|
||||
for(var/mob/dead/new_player/mob_to_sort in sortmob)
|
||||
moblist += mob_to_sort
|
||||
for(var/mob/living/simple_animal/slime/mob_to_sort in sortmob)
|
||||
moblist += mob_to_sort
|
||||
for(var/mob/living/simple_animal/mob_to_sort in sortmob)
|
||||
// We've already added slimes.
|
||||
if(isslime(mob_to_sort))
|
||||
continue
|
||||
moblist += mob_to_sort
|
||||
for(var/mob/living/basic/mob_to_sort in sortmob)
|
||||
moblist += mob_to_sort
|
||||
return moblist
|
||||
|
||||
// Format a power value in W, kW, MW, or GW.
|
||||
|
||||
@@ -30,7 +30,6 @@ GLOBAL_LIST_EMPTY(rcd_list) //list of Rapid Construction Devices.
|
||||
GLOBAL_LIST_EMPTY(apcs_list) //list of all Area Power Controller machines, separate from machines for powernet speeeeeeed.
|
||||
GLOBAL_LIST_EMPTY(tracked_implants) //list of all current implants that are tracked to work out what sort of trek everyone is on. Sadly not on lavaworld not implemented...
|
||||
GLOBAL_LIST_EMPTY(tracked_chem_implants) //list of implants the prisoner console can track and send inject commands too
|
||||
GLOBAL_LIST_EMPTY(poi_list) //list of points of interest for observe/follow
|
||||
GLOBAL_LIST_EMPTY(pinpointer_list) //list of all pinpointers. Used to change stuff they are pointing to all at once.
|
||||
GLOBAL_LIST_EMPTY(zombie_infection_list) // A list of all zombie_infection organs, for any mass "animation"
|
||||
GLOBAL_LIST_EMPTY(meteor_list) // List of all meteors.
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
if (owner?.client?.interviewee)
|
||||
return
|
||||
var/list/buttons = subtypesof(/atom/movable/screen/lobby)
|
||||
for(var/button in buttons)
|
||||
var/atom/movable/screen/lobbyscreen = new button()
|
||||
for(var/button_type in buttons)
|
||||
var/atom/movable/screen/lobbyscreen = new button_type()
|
||||
lobbyscreen.hud = src
|
||||
static_inventory += lobbyscreen
|
||||
if(istype(lobbyscreen, /atom/movable/screen/lobby/button))
|
||||
var/atom/movable/screen/lobby/button/lobby_button = lobbyscreen
|
||||
lobby_button.owner = REF(owner)
|
||||
|
||||
/atom/movable/screen/lobby
|
||||
plane = SPLASHSCREEN_PLANE
|
||||
@@ -26,9 +29,15 @@
|
||||
var/enabled = TRUE
|
||||
///Is the button currently being hovered over with the mouse?
|
||||
var/highlighted = FALSE
|
||||
/// The ref of the mob that owns this button. Only the owner can click on it.
|
||||
var/owner
|
||||
|
||||
/atom/movable/screen/lobby/button/Click(location, control, params)
|
||||
if(owner != REF(usr))
|
||||
return
|
||||
|
||||
. = ..()
|
||||
|
||||
if(!enabled)
|
||||
return
|
||||
flick("[base_icon_state]_pressed", src)
|
||||
@@ -37,11 +46,17 @@
|
||||
return TRUE
|
||||
|
||||
/atom/movable/screen/lobby/button/MouseEntered(location,control,params)
|
||||
if(owner != REF(usr))
|
||||
return
|
||||
|
||||
. = ..()
|
||||
highlighted = TRUE
|
||||
update_appearance(UPDATE_ICON)
|
||||
|
||||
/atom/movable/screen/lobby/button/MouseExited()
|
||||
if(owner != REF(usr))
|
||||
return
|
||||
|
||||
. = ..()
|
||||
highlighted = FALSE
|
||||
update_appearance(UPDATE_ICON)
|
||||
|
||||
250
code/controllers/subsystem/points_of_interest.dm
Normal file
250
code/controllers/subsystem/points_of_interest.dm
Normal file
@@ -0,0 +1,250 @@
|
||||
/// Subsystem for managing all POIs.
|
||||
SUBSYSTEM_DEF(points_of_interest)
|
||||
name = "Points of Interest"
|
||||
|
||||
flags = SS_NO_FIRE | SS_NO_INIT
|
||||
|
||||
/// List of mob POIs. This list is automatically sorted.
|
||||
var/list/datum/point_of_interest/mob_poi/mob_points_of_interest = list()
|
||||
/// List of non-mob POIs. This list is automatically sorted.
|
||||
var/list/datum/point_of_interest/other_points_of_interest = list()
|
||||
/// List of all value:POI datums by their key:target refs.
|
||||
var/list/datum/point_of_interest/points_of_interest_by_target_ref = list()
|
||||
|
||||
/// Special helper list of all real nuke disks.
|
||||
var/list/obj/item/disk/nuclear/real_nuclear_disks = list()
|
||||
|
||||
/// Special helper list to track any Nar'sies.
|
||||
var/list/obj/narsie/narsies = list()
|
||||
|
||||
/**
|
||||
* Turns new_poi into a new point of interest by adding the /datum/element/point_of_interest element to it.
|
||||
*/
|
||||
/datum/controller/subsystem/points_of_interest/proc/make_point_of_interest(atom/new_poi)
|
||||
new_poi.AddElement(/datum/element/point_of_interest)
|
||||
|
||||
/**
|
||||
* Stops old_poi from being a point of interest by removing the /datum/element/point_of_interest element from it.
|
||||
*/
|
||||
/datum/controller/subsystem/points_of_interest/proc/remove_point_of_interest(atom/old_poi)
|
||||
old_poi.RemoveElement(/datum/element/point_of_interest)
|
||||
|
||||
/**
|
||||
* Called by [/datum/element/point_of_interest] when it gets removed from old_poi.
|
||||
*/
|
||||
/datum/controller/subsystem/points_of_interest/proc/on_poi_element_added(atom/new_poi)
|
||||
var/datum/point_of_interest/new_poi_datum
|
||||
if(ismob(new_poi))
|
||||
new_poi_datum = new /datum/point_of_interest/mob_poi(new_poi)
|
||||
BINARY_INSERT_PROC_COMPARE(new_poi_datum, mob_points_of_interest, /datum/point_of_interest/mob_poi, new_poi_datum, compare_to, COMPARE_KEY)
|
||||
points_of_interest_by_target_ref[REF(new_poi)] = new_poi_datum
|
||||
else
|
||||
new_poi_datum = new /datum/point_of_interest(new_poi)
|
||||
BINARY_INSERT_PROC_COMPARE(new_poi_datum, other_points_of_interest, /datum/point_of_interest, new_poi_datum, compare_to, COMPARE_KEY)
|
||||
points_of_interest_by_target_ref[REF(new_poi)] = new_poi_datum
|
||||
|
||||
// NUKE DISK HELPER
|
||||
if(istype(new_poi, /obj/item/disk/nuclear))
|
||||
var/obj/item/disk/nuclear/nuke_disk = new_poi
|
||||
if(!nuke_disk.fake)
|
||||
real_nuclear_disks += nuke_disk
|
||||
// NAR'SIE HELPER
|
||||
else if(istype(new_poi, /obj/narsie))
|
||||
narsies += new_poi
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_ADDED_POINT_OF_INTEREST, new_poi)
|
||||
|
||||
/**
|
||||
* Called by [/datum/element/point_of_interest] when it gets removed from old_poi.
|
||||
*/
|
||||
/datum/controller/subsystem/points_of_interest/proc/on_poi_element_removed(atom/old_poi)
|
||||
var/poi_ref = REF(old_poi)
|
||||
var/datum/point_of_interest/poi_to_remove = points_of_interest_by_target_ref[poi_ref]
|
||||
|
||||
if(!poi_to_remove)
|
||||
return
|
||||
|
||||
if(ismob(old_poi))
|
||||
mob_points_of_interest -= poi_to_remove
|
||||
else
|
||||
other_points_of_interest -= poi_to_remove
|
||||
|
||||
// NUKE DISK HELPER
|
||||
if(istype(old_poi, /obj/item/disk/nuclear))
|
||||
var/obj/item/disk/nuclear/nuke_disk = old_poi
|
||||
if(!nuke_disk.fake)
|
||||
real_nuclear_disks -= nuke_disk
|
||||
// NAR'SIE HELPER
|
||||
else if(istype(old_poi, /obj/narsie))
|
||||
narsies -= old_poi
|
||||
|
||||
points_of_interest_by_target_ref -= poi_ref
|
||||
|
||||
poi_to_remove.target = null
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_REMOVED_POINT_OF_INTEREST, old_poi)
|
||||
|
||||
/**
|
||||
* If there is a valid POI for a given reference, it returns that POI's associated atom. Otherwise, it returns null.
|
||||
*/
|
||||
/datum/controller/subsystem/points_of_interest/proc/get_poi_atom_by_ref(reference)
|
||||
return points_of_interest_by_target_ref[reference]?.target
|
||||
|
||||
/**
|
||||
* Returns a list of mob POIs with names as keys and mobs as values.
|
||||
*
|
||||
* If multiple POIs have the same name, then avoid_assoc_duplicate_keys is used alongside used_name_list to
|
||||
* tag them as Mob Name (1), Mob Name (2), Mob Name (3) etc.
|
||||
*
|
||||
* Arguments:
|
||||
* * poi_validation_override - [OPTIONAL] Callback to a proc that takes a single argument for the POI and returns TRUE if this POI should be included. Overrides standard POI validation.
|
||||
* * append_dead_role - [OPTIONAL] If TRUE, adds a ghost tag to the end of observer names and a dead tag to the end of any other mob which is not alive.
|
||||
*/
|
||||
/datum/controller/subsystem/points_of_interest/proc/get_mob_pois(datum/callback/poi_validation_override = null, append_dead_role = TRUE)
|
||||
var/list/pois = list()
|
||||
var/list/used_name_list = list()
|
||||
|
||||
for(var/datum/point_of_interest/mob_poi/mob_poi as anything in mob_points_of_interest)
|
||||
if(poi_validation_override)
|
||||
if(!poi_validation_override.Invoke(mob_poi))
|
||||
continue
|
||||
else if(!mob_poi.validate())
|
||||
continue
|
||||
|
||||
var/mob/target_mob = mob_poi.target
|
||||
var/name = avoid_assoc_duplicate_keys(target_mob.name, used_name_list) + target_mob.get_realname_string()
|
||||
|
||||
// Add the ghost/dead tag to the end of dead mob POIs.
|
||||
if(append_dead_role && target_mob.stat == DEAD)
|
||||
if(isobserver(target_mob))
|
||||
name += " \[ghost\]"
|
||||
else
|
||||
name += " \[dead\]"
|
||||
|
||||
pois[name] = target_mob
|
||||
|
||||
return pois
|
||||
|
||||
/**
|
||||
* Returns a list of non-mob POIs with names as keys and atoms as values.
|
||||
*
|
||||
* If multiple POIs have the same name, then avoid_assoc_duplicate_keys is used alongside used_name_list to
|
||||
* tag them as Object Name (1), Object Name (2), Object Name (3) etc.
|
||||
*
|
||||
* Arguments:
|
||||
* * poi_validation_override - [OPTIONAL] Callback to a proc that takes a single argument for the POI and returns TRUE if this POI should be included. Overrides standard POI validation.
|
||||
*/
|
||||
/datum/controller/subsystem/points_of_interest/proc/get_other_pois(datum/callback/poi_validation_override = null)
|
||||
var/list/pois = list()
|
||||
var/list/used_name_list = list()
|
||||
|
||||
for(var/datum/point_of_interest/other_poi as anything in other_points_of_interest)
|
||||
if(poi_validation_override)
|
||||
if(!poi_validation_override.Invoke(other_poi))
|
||||
continue
|
||||
else if(!other_poi.validate())
|
||||
continue
|
||||
|
||||
var/atom/target_poi = other_poi.target
|
||||
|
||||
pois[avoid_assoc_duplicate_keys(target_poi.name, used_name_list)] = target_poi
|
||||
|
||||
return pois
|
||||
|
||||
/// Returns TRUE if potential_poi has an associated poi_datum that validates.
|
||||
/datum/controller/subsystem/points_of_interest/proc/is_valid_poi(atom/potential_poi, datum/callback/poi_validation_override = null)
|
||||
var/datum/point_of_interest/poi_datum = points_of_interest_by_target_ref[REF(potential_poi)]
|
||||
|
||||
if(!poi_datum)
|
||||
return FALSE
|
||||
|
||||
if(poi_validation_override)
|
||||
return poi_validation_override.Invoke(poi_datum)
|
||||
|
||||
return poi_datum.validate()
|
||||
|
||||
/// Simple helper datum for points of interest.
|
||||
/datum/point_of_interest
|
||||
/// The specific point of interest this datum references. This won't hard del as the POI element will be removed from the target when it qdels, which will clear this reference.
|
||||
var/atom/target
|
||||
/// The type of POI this datum references.
|
||||
var/poi_type = /atom
|
||||
|
||||
/datum/point_of_interest/New(poi_target)
|
||||
if(!istype(poi_target, poi_type))
|
||||
CRASH("Incorrect target type provided to /datum/point_of_interest/New: Expected \[[poi_type]\]")
|
||||
|
||||
target = poi_target
|
||||
|
||||
/// Validates the POI. Returns TRUE if the POI has valid state, returns FALSE if the POI has invalid state.
|
||||
/datum/point_of_interest/proc/validate()
|
||||
// In nullspace, invalid as a POI.
|
||||
if(!target.loc)
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/// Comparison proc used to sort POIs. Override to implement logic used doing binary sort insertions.
|
||||
/datum/point_of_interest/proc/compare_to(datum/point_of_interest/rhs)
|
||||
return cmp_name_asc(target, rhs.target)
|
||||
|
||||
/datum/point_of_interest/mob_poi
|
||||
poi_type = /mob
|
||||
|
||||
/// Validation for mobs is expanded to invalidate stealthmins and /mob/dead/new_player as POIs.
|
||||
/datum/point_of_interest/mob_poi/validate()
|
||||
. = ..()
|
||||
|
||||
if(!.)
|
||||
return
|
||||
|
||||
var/mob/poi_mob = target
|
||||
|
||||
// Stealthmin, invalid as a POI.
|
||||
if(poi_mob.client?.holder?.fakekey)
|
||||
return FALSE
|
||||
|
||||
// POI is a /mob/dead/new_player, players in the lobby are invalid as POIs.
|
||||
if(isnewplayer(poi_mob))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/// Mob POIs are sorted by a simple priority list depending on their type. When their type priority is identical, they're sub-sorted by name.
|
||||
/datum/point_of_interest/mob_poi/compare_to(datum/point_of_interest/mob_poi/rhs)
|
||||
var/sort_difference = get_type_sort_priority() - rhs.get_type_sort_priority()
|
||||
|
||||
// If they're equal in priority, call parent to sort by name.
|
||||
if(sort_difference == 0)
|
||||
return ..()
|
||||
// Else sort by priority.
|
||||
else
|
||||
return sort_difference
|
||||
|
||||
/// Priority list broadly stolen from /proc/sortmobs. Lower numbers are higher priorities when sorted and appear closer to the top or start of lists.
|
||||
/datum/point_of_interest/mob_poi/proc/get_type_sort_priority()
|
||||
if(istype(target, /mob/living/silicon/ai))
|
||||
return 0
|
||||
if(istype(target, /mob/camera))
|
||||
return 1
|
||||
if(istype(target, /mob/living/silicon/pai))
|
||||
return 2
|
||||
if(istype(target, /mob/living/silicon/robot))
|
||||
return 3
|
||||
if(istype(target, /mob/living/carbon/human))
|
||||
return 4
|
||||
if(istype(target, /mob/living/brain))
|
||||
return 5
|
||||
if(istype(target, /mob/living/carbon/alien))
|
||||
return 6
|
||||
if(istype(target, /mob/dead/observer))
|
||||
return 7
|
||||
if(istype(target, /mob/dead/new_player))
|
||||
return 8
|
||||
if(istype(target, /mob/living/simple_animal/slime))
|
||||
return 9
|
||||
if(istype(target, /mob/living/simple_animal))
|
||||
return 10
|
||||
if(istype(target, /mob/living/basic))
|
||||
return 11
|
||||
return 12
|
||||
@@ -5,9 +5,16 @@
|
||||
/datum/element/point_of_interest/Attach(datum/target)
|
||||
if (!isatom(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
GLOB.poi_list += target
|
||||
|
||||
// New players are abstract mobs assigned to people who are still in the lobby screen.
|
||||
// As a result, they are not a valid POI and should never be a valid POI. If they
|
||||
// somehow get this element attached to them, there's something we need to debug.
|
||||
if(isnewplayer(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
|
||||
SSpoints_of_interest.on_poi_element_added(target)
|
||||
return ..()
|
||||
|
||||
/datum/element/point_of_interest/Detach(datum/target)
|
||||
GLOB.poi_list -= target
|
||||
SSpoints_of_interest.on_poi_element_removed(target)
|
||||
return ..()
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
if(!spawnerlist.len)
|
||||
return
|
||||
var/obj/effect/mob_spawn/mob_spawner = pick(spawnerlist)
|
||||
if(!istype(mob_spawner) || !(mob_spawner in GLOB.poi_list))
|
||||
if(!istype(mob_spawner) || !SSpoints_of_interest.get_poi_atom_by_ref(mob_spawner))
|
||||
return
|
||||
|
||||
switch(action)
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/obj/effect/anomaly/Initialize(mapload, new_lifespan, drops_core = TRUE)
|
||||
. = ..()
|
||||
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
|
||||
START_PROCESSING(SSobj, src)
|
||||
impact_area = get_area(src)
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
. = ..()
|
||||
for (var/answer in haunted_answers)
|
||||
votes[answer] = 0
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
become_hearing_sensitive()
|
||||
|
||||
/obj/item/toy/eightball/haunted/MakeHaunted()
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
/obj/item/his_grace/Initialize()
|
||||
. = ..()
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
RegisterSignal(src, COMSIG_MOVABLE_POST_THROW, .proc/move_gracefully)
|
||||
update_appearance()
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ GLOBAL_DATUM(highlander_controller, /datum/highlander_controller)
|
||||
RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, .proc/new_highlander)
|
||||
sound_to_playing_players('sound/misc/highlander.ogg')
|
||||
send_to_playing_players(span_boldannounce("<font size=6>THERE CAN BE ONLY ONE</font>"))
|
||||
for(var/obj/item/disk/nuclear/fukkendisk in GLOB.poi_list)
|
||||
var/datum/component/stationloving/component = fukkendisk.GetComponent(/datum/component/stationloving)
|
||||
for(var/obj/item/disk/nuclear/nuke_disk as anything in SSpoints_of_interest.real_nuclear_disks)
|
||||
var/datum/component/stationloving/component = nuke_disk.GetComponent(/datum/component/stationloving)
|
||||
component?.relocate() //Gets it out of bags and such
|
||||
|
||||
for(var/mob/living/carbon/human/human in GLOB.player_list)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
/obj/structure/blob/special/core/Initialize(mapload, client/new_overmind = null, placed = 0)
|
||||
GLOB.blob_cores += src
|
||||
START_PROCESSING(SSobj, src)
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
update_appearance() //so it atleast appears
|
||||
if(!placed && !overmind)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
@@ -523,7 +523,7 @@
|
||||
to_chat(user, span_warning("You try to shatter the orb, but it remains as solid as a rock!"))
|
||||
to_chat(user, span_danger(span_big("It seems that the blood cult has exhausted its ability to curse the emergency escape shuttle. It would be unwise to create more cursed orbs or to continue to try to shatter this one.")))
|
||||
return
|
||||
if(locate(/obj/narsie) in GLOB.poi_list)
|
||||
if(locate(/obj/narsie) in SSpoints_of_interest.narsies)
|
||||
to_chat(user, span_warning("Nar'Sie is already on this plane, there is no delaying the end of all things."))
|
||||
return
|
||||
|
||||
|
||||
@@ -510,7 +510,7 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
|
||||
/obj/effect/rune/narsie/Initialize(mapload, set_keyword)
|
||||
. = ..()
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
|
||||
/obj/effect/rune/narsie/conceal() //can't hide this, and you wouldn't want to
|
||||
return
|
||||
@@ -527,7 +527,7 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
if(!(place in summon_objective.summon_spots))
|
||||
to_chat(user, span_cultlarge("The Geometer can only be summoned where the veil is weak - in [english_list(summon_objective.summon_spots)]!"))
|
||||
return
|
||||
if(locate(/obj/narsie) in GLOB.poi_list)
|
||||
if(locate(/obj/narsie) in SSpoints_of_interest.narsies)
|
||||
for(var/M in invokers)
|
||||
to_chat(M, span_warning("Nar'Sie is already on this plane!"))
|
||||
log_game("Nar'Sie rune failed - already summoned")
|
||||
|
||||
@@ -41,7 +41,7 @@ GLOBAL_VAR(station_nuke_source)
|
||||
core = new /obj/item/nuke_core(src)
|
||||
STOP_PROCESSING(SSobj, core)
|
||||
update_appearance()
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
previous_level = get_security_level()
|
||||
|
||||
/obj/machinery/nuclearbomb/Destroy()
|
||||
@@ -652,7 +652,7 @@ This is here to make the tiles around the station mininuke change when it's arme
|
||||
AddElement(/datum/element/bed_tuckable, 6, -6, 0)
|
||||
|
||||
if(!fake)
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
last_disk_move = world.time
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
target = null
|
||||
switch(mode)
|
||||
if(TRACK_NUKE_DISK)
|
||||
var/obj/item/disk/nuclear/N = locate() in GLOB.poi_list
|
||||
var/obj/item/disk/nuclear/N = locate() in SSpoints_of_interest.real_nuclear_disks
|
||||
target = N
|
||||
if(TRACK_MALF_AI)
|
||||
for(var/V in GLOB.ai_list)
|
||||
|
||||
@@ -318,7 +318,7 @@
|
||||
objectives += O
|
||||
|
||||
/datum/team/nuclear/proc/disk_rescued()
|
||||
for(var/obj/item/disk/nuclear/D in GLOB.poi_list)
|
||||
for(var/obj/item/disk/nuclear/D in SSpoints_of_interest.real_nuclear_disks)
|
||||
//If emergency shuttle is in transit disk is only safe on it
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_ESCAPE)
|
||||
if(!SSshuttle.emergency.is_in_shuttle_bounds(D))
|
||||
@@ -433,7 +433,7 @@
|
||||
/datum/team/nuclear/antag_listing_entry()
|
||||
var/disk_report = "<b>Nuclear Disk(s)</b><br>"
|
||||
disk_report += "<table cellspacing=5>"
|
||||
for(var/obj/item/disk/nuclear/N in GLOB.poi_list)
|
||||
for(var/obj/item/disk/nuclear/N in SSpoints_of_interest.real_nuclear_disks)
|
||||
disk_report += "<tr><td>[N.name], "
|
||||
var/atom/disk_loc = N.loc
|
||||
while(!isturf(disk_loc))
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
if(instant || (roundstart && (mapload || (SSticker && SSticker.current_state > GAME_STATE_SETTING_UP))))
|
||||
INVOKE_ASYNC(src, .proc/create)
|
||||
else if(ghost_usable)
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
LAZYADD(GLOB.mob_spawners[name], src)
|
||||
|
||||
/obj/effect/mob_spawn/Destroy()
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
|
||||
/obj/machinery/capture_the_flag/Initialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
ctf_landmark = GLOB.ctf_spawner
|
||||
|
||||
/obj/machinery/capture_the_flag/Destroy()
|
||||
|
||||
@@ -402,14 +402,22 @@
|
||||
if (specificTarget)
|
||||
specificTarget = null
|
||||
return
|
||||
var/list/mobs = getpois()//code stolen from observer.dm
|
||||
var/inputTarget = input("Select a mob! (Smiting does this automatically)", "Target", null, null) as null|anything in mobs
|
||||
if (isnull(inputTarget))
|
||||
return
|
||||
var/mob/target = mobs[inputTarget]
|
||||
specificTarget = target///input specific tartget
|
||||
. = TRUE
|
||||
|
||||
var/list/possible_destinations = SSpoints_of_interest.get_mob_pois()
|
||||
var/target = input("Select a mob! (Smiting does this automatically)", "Target", null, null) as null|anything in possible_destinations
|
||||
|
||||
if (isnull(target))
|
||||
return
|
||||
|
||||
var/mob/mob_target = possible_destinations[target]
|
||||
|
||||
// During the break between opening the input menu and selecting our target, has this become an invalid option?
|
||||
if(!SSpoints_of_interest.is_valid_poi(mob_target))
|
||||
return
|
||||
|
||||
specificTarget = mob_target
|
||||
|
||||
. = TRUE
|
||||
////////////////////////////TIMER DELAYS//////////////////
|
||||
if("editTiming") //Change the different timers relating to the pod
|
||||
var/delay = params["timer"]
|
||||
|
||||
@@ -88,7 +88,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
|
||||
special_target = aimed_at
|
||||
loopy_rod = force_looping
|
||||
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
|
||||
RegisterSignal(src, COMSIG_ATOM_ENTERING, .proc/on_entering_atom)
|
||||
|
||||
@@ -99,7 +99,6 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
|
||||
|
||||
/obj/effect/immovablerod/Destroy(force)
|
||||
UnregisterSignal(src, COMSIG_ATOM_ENTERING)
|
||||
RemoveElement(/datum/element/point_of_interest)
|
||||
SSaugury.unregister_doom(src)
|
||||
already_hit = null //Skyrat Addition
|
||||
return ..()
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
/obj/item/greentext/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
roundend_callback = CALLBACK(src,.proc/check_winner)
|
||||
SSticker.OnRoundend(roundend_callback)
|
||||
|
||||
|
||||
@@ -630,7 +630,7 @@
|
||||
. = ..()
|
||||
spirits = list()
|
||||
START_PROCESSING(SSobj, src)
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
AddComponent(/datum/component/butchering, 150, 90)
|
||||
|
||||
/obj/item/melee/ghost_sword/Destroy()
|
||||
|
||||
@@ -59,7 +59,6 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
// Used for displaying in ghost chat, without changing the actual name
|
||||
// of the mob
|
||||
var/deadchat_name
|
||||
var/datum/orbit_menu/orbit_menu
|
||||
var/datum/spawners_menu/spawners_menu
|
||||
|
||||
/mob/dead/observer/Initialize()
|
||||
@@ -148,6 +147,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
show_data_huds()
|
||||
data_huds_on = 1
|
||||
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
|
||||
/mob/dead/observer/get_photo_description(obj/item/camera/camera)
|
||||
if(!invisibility || camera.see_ghosts)
|
||||
@@ -175,7 +175,6 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
|
||||
updateallghostimages()
|
||||
|
||||
QDEL_NULL(orbit_menu)
|
||||
QDEL_NULL(spawners_menu)
|
||||
return ..()
|
||||
|
||||
@@ -461,10 +460,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
set name = "Orbit" // "Haunt"
|
||||
set desc = "Follow and orbit a mob."
|
||||
|
||||
if(!orbit_menu)
|
||||
orbit_menu = new(src)
|
||||
|
||||
orbit_menu.ui_interact(src)
|
||||
GLOB.orbit_menu.show(src)
|
||||
|
||||
// This is the ghost's follow verb with an argument
|
||||
/mob/dead/observer/proc/ManualFollow(atom/movable/target)
|
||||
@@ -507,27 +503,31 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
set name = "Jump to Mob"
|
||||
set desc = "Teleport to a mob"
|
||||
|
||||
if(isobserver(usr)) //Make sure they're an observer!
|
||||
if(!isobserver(usr)) //Make sure they're an observer!
|
||||
return
|
||||
|
||||
var/list/possible_destinations = SSpoints_of_interest.get_mob_pois()
|
||||
var/target = null
|
||||
|
||||
var/list/dest = list() //List of possible destinations (mobs)
|
||||
var/target = null //Chosen target.
|
||||
target = input("Please, select a player!", "Jump to Mob", null, null) as null|anything in possible_destinations
|
||||
|
||||
dest += getpois(mobs_only = TRUE) //Fill list, prompt user with list
|
||||
target = input("Please, select a player!", "Jump to Mob", null, null) as null|anything in dest
|
||||
if (!target || !isobserver(usr))
|
||||
return
|
||||
|
||||
if (!target)//Make sure we actually have a target
|
||||
return
|
||||
else
|
||||
var/mob/M = dest[target] //Destination mob
|
||||
var/mob/A = src //Source mob
|
||||
var/turf/T = get_turf(M) //Turf of the destination mob
|
||||
var/mob/destination_mob = possible_destinations[target] //Destination mob
|
||||
|
||||
if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination.
|
||||
A.abstract_move(T)
|
||||
A.update_parallax_contents()
|
||||
else
|
||||
to_chat(A, span_danger("This mob is not located in the game world."))
|
||||
// During the break between opening the input menu and selecting our target, has this become an invalid option?
|
||||
if(!SSpoints_of_interest.is_valid_poi(destination_mob))
|
||||
return
|
||||
|
||||
var/mob/source_mob = src //Source mob
|
||||
var/turf/destination_turf = get_turf(destination_mob) //Turf of the destination mob
|
||||
|
||||
if(isturf(destination_turf))
|
||||
source_mob.abstract_move(destination_turf)
|
||||
source_mob.update_parallax_contents()
|
||||
else
|
||||
to_chat(source_mob, span_danger("This mob is not located in the game world."))
|
||||
|
||||
/mob/dead/observer/verb/change_view_range()
|
||||
set category = "Ghost"
|
||||
@@ -865,20 +865,33 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
set name = "Observe"
|
||||
set category = "Ghost"
|
||||
|
||||
var/list/creatures = getpois()
|
||||
if(!isobserver(usr)) //Make sure they're an observer!
|
||||
return
|
||||
|
||||
reset_perspective(null)
|
||||
|
||||
var/eye_name = null
|
||||
var/list/possible_destinations = SSpoints_of_interest.get_mob_pois()
|
||||
var/target = null
|
||||
|
||||
eye_name = input("Please, select a player!", "Observe", null, null) as null|anything in creatures
|
||||
target = input("Please, select a player!", "Jump to Mob", null, null) as null|anything in possible_destinations
|
||||
|
||||
if (!eye_name)
|
||||
if (!target || !isobserver(usr))
|
||||
return
|
||||
|
||||
do_observe(creatures[eye_name])
|
||||
var/mob/chosen_target = possible_destinations[target]
|
||||
|
||||
// During the break between opening the input menu and selecting our target, has this become an invalid option?
|
||||
if(!SSpoints_of_interest.is_valid_poi(chosen_target))
|
||||
return
|
||||
|
||||
do_observe(chosen_target)
|
||||
|
||||
/mob/dead/observer/proc/do_observe(mob/mob_eye)
|
||||
if(isnewplayer(mob_eye))
|
||||
stack_trace("/mob/dead/new_player: \[[mob_eye]\] is being observed by [key_name(src)]. This should never happen and has been blocked.")
|
||||
message_admins("[ADMIN_LOOKUPFLW(src)] attempted to observe someone in the lobby: [ADMIN_LOOKUPFLW(mob_eye)]. This should not be possible and has been blocked.")
|
||||
return
|
||||
|
||||
//Istype so we filter out points of interest that are not mobs
|
||||
if(client && mob_eye && istype(mob_eye))
|
||||
client.eye = mob_eye
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
/datum/orbit_menu
|
||||
var/mob/dead/observer/owner
|
||||
var/auto_observe = FALSE
|
||||
GLOBAL_DATUM_INIT(orbit_menu, /datum/orbit_menu, new)
|
||||
|
||||
/datum/orbit_menu/New(mob/dead/observer/new_owner)
|
||||
if(!istype(new_owner))
|
||||
qdel(src)
|
||||
owner = new_owner
|
||||
/datum/orbit_menu
|
||||
/// Serialised list of all valid POIs. Master list that holds all POIs from all other lists.
|
||||
var/list/pois = list()
|
||||
/// Serialised list of all alive POIs.
|
||||
var/list/alive = list()
|
||||
/// Serialised list of all antagonist POIs.
|
||||
var/list/antagonists = list()
|
||||
/// Serialised list of all dead mob POIs.
|
||||
var/list/dead = list()
|
||||
/// Serialised list of all observers POIS.
|
||||
var/list/ghosts = list()
|
||||
/// Serialised list of all non-mob POIs.
|
||||
var/list/misc = list()
|
||||
/// Serialised list of all POIS without a mind.
|
||||
var/list/npcs = list()
|
||||
|
||||
/datum/orbit_menu/ui_state(mob/user)
|
||||
return GLOB.observer_state
|
||||
@@ -18,85 +27,33 @@
|
||||
|
||||
/datum/orbit_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if ("orbit")
|
||||
if("orbit")
|
||||
var/ref = params["ref"]
|
||||
var/atom/movable/poi = (locate(ref) in GLOB.mob_list) || (locate(ref) in GLOB.poi_list)
|
||||
if (poi == null)
|
||||
. = TRUE
|
||||
return
|
||||
owner.ManualFollow(poi)
|
||||
owner.reset_perspective(null)
|
||||
var/auto_observe = params["auto_observe"]
|
||||
var/atom/poi = SSpoints_of_interest.get_poi_atom_by_ref(ref)
|
||||
|
||||
if((ismob(poi) && !SSpoints_of_interest.is_valid_poi(poi, CALLBACK(src, .proc/validate_mob_poi))) \
|
||||
|| !SSpoints_of_interest.is_valid_poi(poi)
|
||||
)
|
||||
to_chat(usr, span_notice("That point of interest is no longer valid."))
|
||||
return TRUE
|
||||
|
||||
var/mob/dead/observer/user = usr
|
||||
user.ManualFollow(poi)
|
||||
user.reset_perspective(null)
|
||||
if (auto_observe)
|
||||
owner.do_observe(poi)
|
||||
. = TRUE
|
||||
if ("refresh")
|
||||
update_static_data(owner, ui)
|
||||
. = TRUE
|
||||
if ("toggle_observe")
|
||||
auto_observe = !auto_observe
|
||||
if (auto_observe && owner.orbit_target)
|
||||
owner.do_observe(owner.orbit_target)
|
||||
else
|
||||
owner.reset_perspective(null)
|
||||
user.do_observe(poi)
|
||||
return TRUE
|
||||
|
||||
/datum/orbit_menu/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["auto_observe"] = auto_observe
|
||||
return data
|
||||
|
||||
/datum/orbit_menu/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/list/alive = list()
|
||||
var/list/antagonists = list()
|
||||
var/list/dead = list()
|
||||
var/list/ghosts = list()
|
||||
var/list/misc = list()
|
||||
var/list/npcs = list()
|
||||
|
||||
var/list/pois = getpois(skip_mindless = TRUE, specify_dead_role = FALSE)
|
||||
for (var/name in pois)
|
||||
var/list/serialized = list()
|
||||
serialized["name"] = name
|
||||
|
||||
var/poi = pois[name]
|
||||
|
||||
serialized["ref"] = REF(poi)
|
||||
|
||||
var/mob/M = poi
|
||||
if (istype(M))
|
||||
if (isobserver(M))
|
||||
var/number_of_orbiters = length(M.get_all_orbiters())
|
||||
if (number_of_orbiters)
|
||||
serialized["orbiters"] = number_of_orbiters
|
||||
ghosts += list(serialized)
|
||||
else if (M.stat == DEAD)
|
||||
dead += list(serialized)
|
||||
else if (M.mind == null)
|
||||
npcs += list(serialized)
|
||||
else
|
||||
var/number_of_orbiters = length(M.get_all_orbiters())
|
||||
if (number_of_orbiters)
|
||||
serialized["orbiters"] = number_of_orbiters
|
||||
|
||||
var/datum/mind/mind = M.mind
|
||||
var/was_antagonist = FALSE
|
||||
|
||||
for (var/_A in mind.antag_datums)
|
||||
var/datum/antagonist/A = _A
|
||||
if (A.show_to_ghosts)
|
||||
was_antagonist = TRUE
|
||||
serialized["antag"] = A.name
|
||||
antagonists += list(serialized)
|
||||
break
|
||||
|
||||
if (!was_antagonist)
|
||||
alive += list(serialized)
|
||||
else
|
||||
misc += list(serialized)
|
||||
update_poi_list()
|
||||
|
||||
data["alive"] = alive
|
||||
data["antagonists"] = antagonists
|
||||
@@ -104,8 +61,101 @@
|
||||
data["ghosts"] = ghosts
|
||||
data["misc"] = misc
|
||||
data["npcs"] = npcs
|
||||
|
||||
return data
|
||||
|
||||
/datum/orbit_menu/ui_assets()
|
||||
. = ..() || list()
|
||||
. += get_asset_datum(/datum/asset/simple/orbit)
|
||||
return list(
|
||||
get_asset_datum(/datum/asset/simple/orbit),
|
||||
)
|
||||
|
||||
/// Fully updates the list of POIs.
|
||||
/datum/orbit_menu/proc/update_poi_list()
|
||||
var/list/new_mob_pois = SSpoints_of_interest.get_mob_pois(CALLBACK(src, .proc/validate_mob_poi), append_dead_role = FALSE)
|
||||
var/list/new_other_pois = SSpoints_of_interest.get_other_pois()
|
||||
|
||||
pois.Cut()
|
||||
|
||||
alive.Cut()
|
||||
antagonists.Cut()
|
||||
dead.Cut()
|
||||
ghosts.Cut()
|
||||
npcs.Cut()
|
||||
|
||||
misc.Cut()
|
||||
|
||||
for(var/name in new_mob_pois)
|
||||
var/list/serialized = list()
|
||||
|
||||
var/mob/mob_poi = new_mob_pois[name]
|
||||
|
||||
var/poi_ref = REF(mob_poi)
|
||||
serialized["ref"] = poi_ref
|
||||
serialized["name"] = name
|
||||
|
||||
pois[poi_ref] = mob_poi
|
||||
|
||||
if(isobserver(mob_poi))
|
||||
var/number_of_orbiters = length(mob_poi.get_all_orbiters())
|
||||
if (number_of_orbiters)
|
||||
serialized["orbiters"] = number_of_orbiters
|
||||
ghosts += list(serialized)
|
||||
continue
|
||||
|
||||
if(mob_poi.stat == DEAD)
|
||||
dead += list(serialized)
|
||||
continue
|
||||
|
||||
if(isnull(mob_poi.mind))
|
||||
npcs += list(serialized)
|
||||
continue
|
||||
|
||||
var/number_of_orbiters = length(mob_poi.get_all_orbiters())
|
||||
if(number_of_orbiters)
|
||||
serialized["orbiters"] = number_of_orbiters
|
||||
|
||||
var/datum/mind/mind = mob_poi.mind
|
||||
var/was_antagonist = FALSE
|
||||
|
||||
for(var/datum/antagonist/antag_datum as anything in mind.antag_datums)
|
||||
if (antag_datum.show_to_ghosts)
|
||||
was_antagonist = TRUE
|
||||
serialized["antag"] = antag_datum.name
|
||||
antagonists += list(serialized)
|
||||
break
|
||||
|
||||
if(!was_antagonist)
|
||||
alive += list(serialized)
|
||||
|
||||
for(var/name in new_other_pois)
|
||||
var/list/serialized = list()
|
||||
|
||||
var/atom/atom_poi = new_other_pois[name]
|
||||
|
||||
var/poi_ref = REF(atom_poi)
|
||||
serialized["ref"] = poi_ref
|
||||
serialized["name"] = name
|
||||
|
||||
pois[poi_ref] = atom_poi
|
||||
misc += list(serialized)
|
||||
|
||||
/// Shows the UI to the specified user.
|
||||
/datum/orbit_menu/proc/show(mob/user)
|
||||
ui_interact(user)
|
||||
|
||||
/**
|
||||
* Helper POI validation function passed as a callback to various SSpoints_of_interest procs.
|
||||
*
|
||||
* Provides extended validation above and beyond standard, limiting mob POIs without minds or ckeys
|
||||
* unless they're mobs, camera mobs or megafauna.
|
||||
*
|
||||
* If they satisfy that requirement, falls back to default validation for the POI.
|
||||
*/
|
||||
/datum/orbit_menu/proc/validate_mob_poi(datum/point_of_interest/mob_poi/potential_poi)
|
||||
var/mob/potential_mob_poi = potential_poi.target
|
||||
// Skip mindless and ckeyless mobs except bots, cameramobs and megafauna.
|
||||
if(!potential_mob_poi.mind && !potential_mob_poi.ckey)
|
||||
if(!isbot(potential_mob_poi) && !iscameramob(potential_mob_poi) && !ismegafauna(potential_mob_poi))
|
||||
return FALSE
|
||||
|
||||
return potential_poi.validate()
|
||||
|
||||
@@ -617,9 +617,10 @@ generate/load female uniform sprites matching all previously decided variables
|
||||
for(var/X in bodyparts)
|
||||
var/obj/item/bodypart/BP = X
|
||||
. += "-[BP.body_zone]"
|
||||
for(var/obj/item/organ/external/organ in BP.external_organs)
|
||||
if(organ.can_draw_on_bodypart(src)) //make sure we're drawn before generating a key
|
||||
. += "([organ.cache_key])"
|
||||
if(!HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
|
||||
for(var/obj/item/organ/external/organ as anything in BP.external_organs)
|
||||
if(organ.can_draw_on_bodypart(src)) //make sure we're drawn before generating a key
|
||||
. += "([organ.cache_key])"
|
||||
//SKYRAT EDIT REMOVAL BEGIN - CUSTOMIZATION
|
||||
/*
|
||||
if(BP.status == BODYPART_ORGANIC)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
diag_hud.add_to_hud(src)
|
||||
faction += "[REF(src)]"
|
||||
GLOB.mob_living_list += src
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
|
||||
/mob/living/ComponentInitialize()
|
||||
. = ..()
|
||||
|
||||
@@ -525,7 +525,7 @@
|
||||
|
||||
/obj/machinery/anomalous_crystal/helpers/ActivationReaction(mob/user, method)
|
||||
if(..() && !ready_to_deploy)
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
ready_to_deploy = TRUE
|
||||
notify_ghosts("An anomalous crystal has been activated in [get_area(src)]! This crystal can always be used by ghosts hereafter.", enter_link = "<a href=?src=[REF(src)];ghostjoin=1>(Click to enter)</a>", ghost_sound = 'sound/effects/ghost2.ogg', source = src, action = NOTIFY_ATTACK, header = "Anomalous crystal activated")
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
///Adds a mob reference to the list of all suicided mobs
|
||||
/mob/proc/add_to_mob_suicide_list()
|
||||
GLOB.suicided_mob_list += src
|
||||
|
||||
|
||||
///Removes a mob references from the list of all suicided mobs
|
||||
/mob/proc/remove_from_mob_suicide_list()
|
||||
GLOB.suicided_mob_list -= src
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
pointercolor = "red"
|
||||
|
||||
/datum/computer_file/program/radar/fission360/find_atom()
|
||||
return locate(selected) in GLOB.poi_list
|
||||
return SSpoints_of_interest.get_poi_atom_by_ref(selected)
|
||||
|
||||
/datum/computer_file/program/radar/fission360/scan()
|
||||
if(world.time < next_scan)
|
||||
@@ -285,7 +285,7 @@
|
||||
name = nuke.name,
|
||||
)
|
||||
objects += list(nukeinfo)
|
||||
var/obj/item/disk/nuclear/disk = locate() in GLOB.poi_list
|
||||
var/obj/item/disk/nuclear/disk = locate() in SSpoints_of_interest.real_nuclear_disks
|
||||
var/list/nukeinfo = list(
|
||||
ref = REF(disk),
|
||||
name = "Nuke Auth. Disk",
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
/obj/narsie/Initialize()
|
||||
. = ..()
|
||||
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
|
||||
singularity = WEAKREF(AddComponent(
|
||||
/datum/component/singularity, \
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
//SKYRAT EDIT END
|
||||
|
||||
START_PROCESSING(SSobj, src)
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
|
||||
var/datum/component/singularity/new_component = AddComponent(
|
||||
/datum/component/singularity, \
|
||||
|
||||
@@ -230,7 +230,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
|
||||
SSair.start_processing_machine(src)
|
||||
countdown = new(src)
|
||||
countdown.start()
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
radio = new(src)
|
||||
radio.keyslot = new radio_key
|
||||
radio.listening = 0
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
/obj/item/gun/energy/pulse/prize/Initialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
message_admins("A pulse rifle prize has been created at [ADMIN_VERBOSEJMP(T)]")
|
||||
|
||||
@@ -680,6 +680,7 @@
|
||||
return
|
||||
|
||||
ADD_TRAIT(invisible_man, TRAIT_INVISIBLE_MAN, name)
|
||||
ADD_TRAIT(invisible_man, TRAIT_HIDE_EXTERNAL_ORGANS, name)
|
||||
|
||||
var/datum/dna/druggy_dna = invisible_man.has_dna()
|
||||
if(druggy_dna?.species)
|
||||
@@ -695,6 +696,7 @@
|
||||
if(HAS_TRAIT(invisible_man, TRAIT_INVISIBLE_MAN))
|
||||
invisible_man.add_to_all_human_data_huds() //Is this safe, what do you think, Floyd?
|
||||
REMOVE_TRAIT(invisible_man, TRAIT_INVISIBLE_MAN, name)
|
||||
REMOVE_TRAIT(invisible_man, TRAIT_HIDE_EXTERNAL_ORGANS, name)
|
||||
to_chat(invisible_man, span_notice("As you sober up, opacity once again returns to your body meats."))
|
||||
|
||||
var/datum/dna/druggy_dna = invisible_man.has_dna()
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
return FALSE
|
||||
|
||||
/obj/docking_port/mobile/arrivals/proc/NukeDiskCheck()
|
||||
for (var/obj/item/disk/nuclear/N in GLOB.poi_list)
|
||||
for (var/obj/item/disk/nuclear/N in SSpoints_of_interest.real_nuclear_disks)
|
||||
if (get_area(N) in areas)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
name = "phylactery of [mind.name]"
|
||||
|
||||
active_phylacteries++
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/phylactery/Destroy(force=FALSE)
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
img.pixel_y = px_y
|
||||
add_overlay(standing)
|
||||
|
||||
/obj/item/bodypart/head/get_limb_icon(dropped)
|
||||
/obj/item/bodypart/head/get_limb_icon(dropped, draw_external_organs)
|
||||
cut_overlays()
|
||||
. = ..()
|
||||
if(dropped) //certain overlays only appear when the limb is being detached from its owner.
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
/obj/structure/swarmer_beacon/Initialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
|
||||
/obj/structure/swarmer_beacon/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
|
||||
@@ -201,7 +201,7 @@
|
||||
add_scanmod()
|
||||
add_capacitor()
|
||||
START_PROCESSING(SSobj, src)
|
||||
AddElement(/datum/element/point_of_interest)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
log_message("[src.name] created.", LOG_MECHA)
|
||||
GLOB.mechas_list += src //global mech list
|
||||
prepare_huds()
|
||||
|
||||
@@ -279,12 +279,13 @@
|
||||
|
||||
//GENERATE NEW LIMBS
|
||||
var/list/new_limbs = list()
|
||||
var/draw_features = !HAS_TRAIT(src, TRAIT_INVISIBLE_MAN)
|
||||
for(var/X in bodyparts)
|
||||
var/obj/item/bodypart/BP = X
|
||||
if(is_taur && (BP.body_part == LEG_LEFT || BP.body_part == LEG_RIGHT))
|
||||
continue
|
||||
|
||||
new_limbs += BP.get_limb_icon()
|
||||
new_limbs += BP.get_limb_icon(draw_external_organs = draw_features)
|
||||
if(new_limbs.len)
|
||||
overlays_standing[BODYPARTS_LAYER] = new_limbs
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/obj/item/bodypart/proc/get_limb_icon(dropped)
|
||||
/obj/item/bodypart/proc/get_limb_icon(dropped, draw_external_organs)
|
||||
icon_state = "" //to erase the default sprite, we're building the visual aspects of the bodypart through overlays alone.
|
||||
|
||||
. = list()
|
||||
@@ -140,7 +140,10 @@
|
||||
var/mutable_appearance/aux_em_block = mutable_appearance(aux.icon, aux.icon_state, plane = EMISSIVE_PLANE, appearance_flags = KEEP_APART)
|
||||
aux_em_block.dir = image_dir
|
||||
aux_em_block.color = GLOB.em_block_color
|
||||
aux.overlays += aux_em_block
|
||||
|
||||
|
||||
if(!draw_external_organs)
|
||||
return
|
||||
//Draw external organs like horns and frills
|
||||
for(var/obj/item/organ/external/external_organ in external_organs)
|
||||
if(!dropped && !external_organ.can_draw_on_bodypart(owner))
|
||||
|
||||
@@ -404,6 +404,7 @@
|
||||
#include "code\controllers\subsystem\pathfinder.dm"
|
||||
#include "code\controllers\subsystem\persistence.dm"
|
||||
#include "code\controllers\subsystem\persistent_paintings.dm"
|
||||
#include "code\controllers\subsystem\points_of_interest.dm"
|
||||
#include "code\controllers\subsystem\profiler.dm"
|
||||
#include "code\controllers\subsystem\radiation.dm"
|
||||
#include "code\controllers\subsystem\radio.dm"
|
||||
|
||||
@@ -35,7 +35,8 @@ const compareNumberedText = (a, b) => {
|
||||
|
||||
const BasicSection = (props, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const { searchText, source, title } = props;
|
||||
const { searchText, source, title, autoObserve } = props;
|
||||
|
||||
const things = source.filter(searchFor(searchText));
|
||||
things.sort(compareNumberedText);
|
||||
return source.length > 0 && (
|
||||
@@ -46,6 +47,7 @@ const BasicSection = (props, context) => {
|
||||
content={thing.name}
|
||||
onClick={() => act("orbit", {
|
||||
ref: thing.ref,
|
||||
auto_observe: autoObserve,
|
||||
})} />
|
||||
))}
|
||||
</Section>
|
||||
@@ -54,13 +56,14 @@ const BasicSection = (props, context) => {
|
||||
|
||||
const OrbitedButton = (props, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const { color, thing } = props;
|
||||
const { color, thing, autoObserve } = props;
|
||||
|
||||
return (
|
||||
<Button
|
||||
color={color}
|
||||
onClick={() => act("orbit", {
|
||||
ref: thing.ref,
|
||||
auto_observe: autoObserve,
|
||||
})}>
|
||||
{thing.name}
|
||||
{thing.orbiters && (
|
||||
@@ -82,7 +85,6 @@ export const Orbit = (props, context) => {
|
||||
const {
|
||||
alive,
|
||||
antagonists,
|
||||
auto_observe,
|
||||
dead,
|
||||
ghosts,
|
||||
misc,
|
||||
@@ -90,6 +92,7 @@ export const Orbit = (props, context) => {
|
||||
} = data;
|
||||
|
||||
const [searchText, setSearchText] = useLocalState(context, "searchText", "");
|
||||
const [autoObserve, setAutoObserve] = useLocalState(context, "autoObserve", false);
|
||||
|
||||
const collatedAntagonists = {};
|
||||
for (const antagonist of antagonists) {
|
||||
@@ -113,7 +116,10 @@ export const Orbit = (props, context) => {
|
||||
.filter(searchFor(searchText))
|
||||
.sort(compareNumberedText)[0];
|
||||
if (member !== undefined) {
|
||||
act("orbit", { ref: member.ref });
|
||||
act("orbit", {
|
||||
ref: member.ref,
|
||||
auto_observe: autoObserve,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -151,16 +157,9 @@ export const Orbit = (props, context) => {
|
||||
tooltip={multiline`Toggle Auto-Observe. When active, you'll
|
||||
see the UI / full inventory of whoever you're orbiting. Neat!`}
|
||||
tooltipPosition="bottom-start"
|
||||
selected={auto_observe}
|
||||
icon={auto_observe ? "toggle-on" : "toggle-off"}
|
||||
onClick={() => act("toggle_observe")} />
|
||||
<Button
|
||||
inline
|
||||
color="transparent"
|
||||
tooltip="Refresh"
|
||||
tooltipPosition="bottom-start"
|
||||
icon="sync-alt"
|
||||
onClick={() => act("refresh")} />
|
||||
selected={autoObserve}
|
||||
icon={autoObserve ? "toggle-on" : "toggle-off"}
|
||||
onClick={() => setAutoObserve(!autoObserve)} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
@@ -176,6 +175,7 @@ export const Orbit = (props, context) => {
|
||||
key={antag.name}
|
||||
color="bad"
|
||||
thing={antag}
|
||||
autoObserve={autoObserve}
|
||||
/>
|
||||
))}
|
||||
</Section>
|
||||
@@ -191,7 +191,8 @@ export const Orbit = (props, context) => {
|
||||
<OrbitedButton
|
||||
key={thing.name}
|
||||
color="good"
|
||||
thing={thing} />
|
||||
thing={thing}
|
||||
autoObserve={autoObserve} />
|
||||
))}
|
||||
</Section>
|
||||
|
||||
@@ -203,7 +204,8 @@ export const Orbit = (props, context) => {
|
||||
<OrbitedButton
|
||||
key={thing.name}
|
||||
color="grey"
|
||||
thing={thing} />
|
||||
thing={thing}
|
||||
autoObserve={autoObserve} />
|
||||
))}
|
||||
</Section>
|
||||
|
||||
@@ -211,6 +213,7 @@ export const Orbit = (props, context) => {
|
||||
title="Dead"
|
||||
source={dead}
|
||||
searchText={searchText}
|
||||
autoObserve={autoObserve}
|
||||
/>
|
||||
|
||||
<BasicSection
|
||||
|
||||
Reference in New Issue
Block a user