Files
Wildkins e4192f9b8f Lag War Day 5.5: are you serious dude (#21889)
we have a gorillion hard deletions and it turns out half of them are
because we set the "ALWAYS HARD DELETE THIS" flag rather than just like.
fixing the hard delete.

cool
2026-02-21 01:41:50 +00:00

310 lines
10 KiB
Plaintext
Raw Permalink 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
G.RegisterSignal(spawn_atom, COMSIG_QDELETING, TYPE_PROC_REF(/datum/ghostspawner, spawn_atom_deleted))
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