mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-12 08:27:13 +01:00
e4192f9b8f
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
310 lines
10 KiB
Plaintext
310 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
|
||
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
|