Files
Aurora.3/code/controllers/subsystems/ghostroles.dm
T
VMSolidus 4d43d53c6f Ghostroles In Lobby Manifest (#21772)
This PR makes it so that player characters who are active in the
ghostrole list will show up on the Crew Manifest, but only if you the
player are not currently in-round. Any type of Observer, including
ghosts, aghosts, and actually being in the lobby, all allow the user to
view Off-ship characters in the manifest. If you are not any kind of
observer, the Crew Manifest will only show Horizon characters.


I have tested this PR. Behold.


https://github.com/user-attachments/assets/76ffb40a-bb67-4719-a2e7-df04f38b929e
2026-02-03 10:09:49 +00:00

309 lines
10 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
SUBSYSTEM_DEF(ghostroles)
name = "Ghost Roles"
flags = SS_NO_FIRE
init_order = INIT_ORDER_GHOSTROLES
var/list/spawnpoints = list() //List of the available spawnpoints by spawnpoint type
// -> type 1 -> spawnpoint 1
// -> spawnpoint 2
/**
* List of all available spawner datums that exist in the code.
* This includes spawner datums that might not currently exist anywhere in-game.
*/
var/list/spawners = list()
/**
* The set of all ghostroles that have been taken by players.
* As a weakref list, these are not guaranteed to factually exist, only that these did at some point exist.
* The set of all mobs here should be retrieved via SSghostroles.get_ghostrole_mobs()
*/
var/list/datum/weakref/spawned_ghostrole_mobs = list()
// For special spawners that have mobile or object spawnpoints
var/list/spawn_types = list("Golems", "Borers")
var/list/spawn_atom = list()
/datum/controller/subsystem/ghostroles/Recover()
src.spawnpoints = SSghostroles.spawnpoints
src.spawners = SSghostroles.spawners
/datum/controller/subsystem/ghostroles/Initialize(start_timeofday)
for(var/spawner in subtypesof(/datum/ghostspawner))
CHECK_TICK
var/datum/ghostspawner/G = new spawner
//Check if we have name, short_name and desc set
if(!G.short_name || !G.name || !G.desc)
log_subsystem_ghostroles("Spawner [G.type] got removed from selection because of missing data")
continue
//Check if we have a spawnpoint on the current map
if(!G.select_spawnlocation(FALSE) && G.loc_type == GS_LOC_POS)
log_subsystem_ghostroles("Spawner [G.type] got removed from selection because of missing spawnpoint")
continue
spawners[G.short_name] = G
for (var/identifier in spawnpoints)
CHECK_TICK
spawnpoints[identifier] = shuffle(spawnpoints[identifier])
update_spawnpoint_status_by_identifier(identifier)
for(var/spawn_type in spawn_types)
spawn_atom[spawn_type] = list()
return SS_INIT_SUCCESS
//Adds a spawnpoint to the spawnpoint list
/datum/controller/subsystem/ghostroles/proc/add_spawnpoints(var/obj/effect/ghostspawpoint/P)
if(!P.identifier) //If the spawnpoint has no identifier -> Abort
log_subsystem_ghostroles_error("Spawner [P] at [P.x],[P.y],[P.z] has no identifier set")
qdel(P)
return
if(!spawnpoints[P.identifier])
spawnpoints[P.identifier] = list()
spawnpoints[P.identifier] += P
// Only update the status if the round is started. During initialization that´s taken care of at the end of init.
if(ROUND_IS_STARTED)
update_spawnpoint_status(P)
/datum/controller/subsystem/ghostroles/proc/update_spawnpoint_status(var/obj/effect/ghostspawpoint/P)
if(!P || !istype(P))
return null
//If any of the spawners uses that spawnpoint and is active set the status to available
for(var/s in spawners)
var/datum/ghostspawner/G = spawners[s]
if(!G.is_enabled())
continue
if(!(P.identifier in G.spawnpoints))
continue
P.set_available()
return TRUE
P.set_unavailable()
return FALSE
/datum/controller/subsystem/ghostroles/proc/update_spawnpoint_status_by_identifier(var/identifier)
if(!identifier) //If no identifier ist set, return false
return null
if(!length(spawnpoints[identifier])) //If we have no spawnpoints for that identifier, return false
return null
for (var/spawnpoint in spawnpoints[identifier])
CHECK_TICK
var/obj/effect/ghostspawpoint/P = spawnpoint
update_spawnpoint_status(P)
/datum/controller/subsystem/ghostroles/proc/remove_spawnpoints(var/obj/effect/ghostspawpoint/G)
spawnpoints[G.identifier] -= G
return
//Returns the turf where the spawnpoint is located and updates the spawner to be used
/datum/controller/subsystem/ghostroles/proc/get_spawnpoint(var/identifier, var/use = TRUE)
if(!identifier) //If no identifier ist set, return false
return null
if(!length(spawnpoints[identifier])) //If we have no spawnpoints for that identifier, return false
return null
for (var/spawnpoint in spawnpoints[identifier])
var/obj/effect/ghostspawpoint/P = spawnpoint
if(P.is_available())
if(use)
P.set_spawned()
return get_turf(P)
/datum/controller/subsystem/ghostroles/ui_state(mob/user)
return GLOB.always_state
/datum/controller/subsystem/ghostroles/ui_status(mob/user, datum/ui_state/state)
return UI_INTERACTIVE
/datum/controller/subsystem/ghostroles/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "GhostSpawner", "Ghost Spawners")
ui.open()
/datum/controller/subsystem/ghostroles/ui_data(mob/user)
var/list/data = list()
data["spawners"] = list()
data["categories"] = list()
for(var/s in spawners)
var/datum/ghostspawner/G = spawners[s]
if(G.cant_see(user))
continue
var/cant_spawn = G.cant_spawn(user)
var/list/manifest = list()
if(LAZYLEN(G.spawned_mobs))
for(var/datum/weakref/mob_ref in G.spawned_mobs)
var/mob/spawned_mob = mob_ref.resolve()
if(spawned_mob)
manifest += spawned_mob.real_name
var/atom/spawn_overmap_location = null
if(SSatlas.current_map.use_overmap)
var/atom/spawner = G.select_spawnlocation(FALSE)
if(istype(spawner))
var/obj/effect/overmap/visitable/sector = GLOB.map_sectors["[spawner.z]"]
if(istype(sector))
spawn_overmap_location = sector.name
var/list/spawner = list(
"short_name" = G.short_name,
"name" = G.name,
"desc" = G.desc,
"desc_ooc" = G.desc_ooc,
"type" = G.type,
"cant_spawn" = cant_spawn,
"can_edit" = G.can_edit(user),
"can_jump_to" = G.can_jump_to(user),
"enabled" = G.enabled,
"count" = G.count,
"spawn_atoms" = length(G.spawn_atoms),
"spawn_overmap_location" = spawn_overmap_location,
"max_count" = G.max_count,
"tags" = G.tags,
"spawnpoints" = G.spawnpoints,
"manifest" = manifest
)
data["categories"] |= G.tags
data["spawners"] += list(spawner)
return data
/datum/controller/subsystem/ghostroles/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
switch(action)
if("spawn")
var/spawner = params["spawn"]
var/datum/ghostspawner/S = spawners[spawner]
if(!S)
return
var/cant_spawn = S.cant_spawn(usr)
if(cant_spawn)
to_chat(usr, "Unable to spawn: [cant_spawn]")
return
if(isnewplayer(usr))
var/mob/abstract/new_player/N = usr
N.close_spawn_windows()
if(!S.pre_spawn(usr))
to_chat(usr, "Unable to spawn: pre_spawn failed. Report this on GitHub")
return
var/mob/M = S.spawn_mob(usr)
if(!M)
to_chat(usr, "Unable to spawn: spawn_mob failed. Report this on GitHub")
return
if(!S.post_spawn(M))
to_chat(usr, "Unable to spawn: post_spawn failed. Report this on GitHub")
return
var/spawned_weakref = WEAKREF(M)
// Add the spawned mobs to this spawner's list.
LAZYADD(S.spawned_mobs, spawned_weakref)
// Then also add the spawned mob to the subsystem's list.
LAZYADD(spawned_ghostrole_mobs, spawned_weakref)
SSrecords.reset_manifest()
log_and_message_admins("joined as GhostRole: [S.name]", M)
SStgui.update_uis(src)
. = TRUE
if("jump_to")
var/spawner_id = params["spawner_id"]
var/datum/ghostspawner/human/spawner = spawners[spawner_id]
var/mob/abstract/ghost/observer/observer = usr
if(spawner && istype(observer) && spawner.can_jump_to(observer))
var/atom/turf = spawner.select_spawnlocation(FALSE)
if(isturf(turf))
observer.on_mob_jump()
observer.forceMove(turf)
if("follow_manifest_entry")
var/spawner_id = params["spawner_id"]
var/spawned_mob_name = params["spawned_mob_name"]
var/datum/ghostspawner/human/spawner = spawners[spawner_id]
var/mob/abstract/ghost/observer/observer = usr
if(istype(observer) && spawner.can_jump_to(observer) && spawner && LAZYLEN(spawner.spawned_mobs))
for(var/datum/weakref/mob_ref in spawner.spawned_mobs)
var/mob/spawned_mob = mob_ref.resolve()
if(spawned_mob && spawned_mob.real_name == spawned_mob_name)
observer.ManualFollow(spawned_mob)
break
if("enable")
var/datum/ghostspawner/S = spawners[params["enable"]]
if(!S)
return
if(!S.can_edit(usr))
return
if(!S.enabled)
S.enable()
to_chat(usr, SPAN_NOTICE("Ghost spawner enabled: [S.name]"))
SStgui.update_uis(src)
for(var/i in S.spawnpoints)
update_spawnpoint_status_by_identifier(i)
. = TRUE
if("disable")
var/datum/ghostspawner/S = spawners[params["disable"]]
if(!S)
return
if(!S.can_edit(usr))
return
if(S.enabled)
S.disable()
to_chat(usr, SPAN_NOTICE("Ghost spawner disabled: [S.name]"))
SStgui.update_uis(src)
for(var/i in S.spawnpoints)
update_spawnpoint_status_by_identifier(i)
. = TRUE
/datum/controller/subsystem/ghostroles/proc/add_spawn_atom(var/ghost_role_name, var/atom/spawn_atom)
if(ghost_role_name && spawn_atom)
var/datum/ghostspawner/G = spawners[ghost_role_name]
if(G && !(spawn_atom in G.spawn_atoms))
G.spawn_atoms += spawn_atom
if(G.atom_add_message)
say_dead_direct("[G.atom_add_message]<br>Spawn in as it by using the ghost spawner menu in the ghost tab, and try to be good!")
/datum/controller/subsystem/ghostroles/proc/remove_spawn_atom(var/ghost_role_name, var/atom/spawn_atom)
if(ghost_role_name && spawn_atom)
var/datum/ghostspawner/G = spawners[ghost_role_name]
if(G)
G.spawn_atoms -= spawn_atom
/datum/controller/subsystem/ghostroles/proc/get_spawn_atoms(var/ghost_role_name)
var/datum/ghostspawner/G = spawners[ghost_role_name]
if(G)
return G.spawn_atoms
return list()
//Returns the spawner with the specified (short) name or null
/datum/controller/subsystem/ghostroles/proc/get_spawner(var/spawner_name)
if(spawner_name in spawners)
return spawners[spawner_name]
return null
/**
* Returns a /list/mob containing all mobs that were created by ghostroles and currently exist.
* It will also delete any mob found to be nonexistent from the list for faster future lookups.
*/
/datum/controller/subsystem/ghostroles/proc/get_ghostrole_mobs()
var/list/mob/found_mobs = list()
for (var/datum/weakref/nullable_spawn in spawned_ghostrole_mobs)
var/possible_spawn = nullable_spawn.resolve()
if (!possible_spawn || !ismob(possible_spawn))
// Clear weakrefs from the list that are discovered to be nonexistent.
// So that future calls to this proc have fewer entries to enumerate over.
// Also do this if for whatever baffling reason a non-mob was shoved into the list.
qdel(nullable_spawn)
spawned_ghostrole_mobs.Remove(nullable_spawn)
continue
var/mob/found_spawn = possible_spawn
found_mobs.Add(found_spawn)
return found_mobs