mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-21 21:10:30 +01:00
4d43d53c6f
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
309 lines
10 KiB
Plaintext
309 lines
10 KiB
Plaintext
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
|