/mob/abstract/ghost
stat = DEAD
/// Is the ghost able to see things humans can't?
var/ghostvision = TRUE
/// This variable generally controls whether a ghost has restrictions on where it can go or not (ex. if the ghost can bypass holy places).
var/has_ghost_restrictions = TRUE
/// If the ghost has antagHUD.
var/antagHUD = FALSE
/// Necessary for seeing wires.
var/obj/item/multitool/ghost_multitool
/// The POI we're orbiting.
var/orbiting_ref
/mob/abstract/ghost/Initialize(mapload)
. = ..()
sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF
see_invisible = SEE_INVISIBLE_OBSERVER
add_verb(src, /mob/abstract/ghost/proc/dead_tele)
ghost_multitool = new(src)
update_sight()
/mob/abstract/ghost/Destroy()
QDEL_NULL(ghost_multitool)
orbiting_ref = null
return ..()
/mob/abstract/ghost/Topic(href, href_list)
if (href_list["track"])
if(istype(href_list["track"],/mob))
var/mob/target = locate(href_list["track"]) in GLOB.mob_list
if(target)
ManualFollow(target)
else
var/atom/target = locate(href_list["track"])
if(istype(target))
ManualFollow(target)
/mob/abstract/ghost/ClickOn(atom/A, params)
if(!canClick())
return
setClickCooldown(4)
// You are responsible for checking config.ghost_interaction when you override this function
// Not all of them require checking, see below
A.attack_ghost(src)
/mob/abstract/ghost/Post_Incorpmove()
orbiting?.end_orbit(src)
/mob/abstract/ghost/orbit()
set_dir(2)//reset dir so the right directional sprites show up
return ..()
/mob/abstract/ghost/verb/toggle_darkness()
set name = "Toggle Darkness"
set category = "Ghost"
var/level_message
switch(lighting_alpha)
if(LIGHTING_PLANE_ALPHA_VISIBLE)
lighting_alpha = LIGHTING_PLANE_ALPHA_SOMEWHAT_INVISIBLE
level_message = "half night vision"
if(LIGHTING_PLANE_ALPHA_SOMEWHAT_INVISIBLE)
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
level_message = "three quarters night vision"
if(LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE)
lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE
level_message = "full night vision"
if(LIGHTING_PLANE_ALPHA_INVISIBLE)
lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
level_message = "no night vision"
to_chat(src, SPAN_NOTICE("Night vision mode switched and saved to [level_message]."))
sync_lighting_plane_alpha()
/mob/abstract/ghost/verb/toggle_ghostsee()
set name = "Toggle Ghost Vision"
set category = "Ghost"
set desc = "Toggles your ability to see things only ghosts can see, like ghosts."
ghostvision = !ghostvision
update_sight()
to_chat(usr, SPAN_NOTICE("You [(ghostvision ? "now" : "no longer")] have ghost vision."))
/mob/abstract/ghost/proc/dead_tele()
set name = "Teleport"
set category = "Ghost"
set desc= "Teleport to a location."
if(!istype(usr, /mob/abstract/ghost))
to_chat(usr, SPAN_WARNING("You need to be a ghost!"))
return
var/area_name = tgui_input_list(src, "Select an area to teleport to.", "Teleport", GLOB.ghostteleportlocs)
remove_verb(usr, /mob/abstract/ghost/proc/dead_tele)
ADD_VERB_IN(usr, 30, /mob/abstract/ghost/proc/dead_tele)
var/area/thearea = GLOB.ghostteleportlocs[area_name]
if(!thearea)
return
var/list/L = list()
var/holyblock = FALSE
if(usr.invisibility <= SEE_INVISIBLE_LIVING || (usr.mind in GLOB.cult.current_antagonists))
for(var/turf/T in get_area_turfs(thearea))
if(!T.holy && has_ghost_restrictions)
L+=T
else
holyblock = TRUE
else
for(var/turf/T in get_area_turfs(thearea))
L+=T
if(!L || !L.len)
if(holyblock && has_ghost_restrictions)
to_chat(usr, SPAN_WARNING("This area has been entirely made into sacred grounds, you cannot enter it while you are in this plane of existence!"))
return
else
to_chat(usr, "No area available.")
return
var/turf/P = pick(L)
if(on_restricted_level(P.z) && has_ghost_restrictions)
to_chat(usr, "You can not teleport to this area.")
return
orbiting?.end_orbit(src)
usr.forceMove(pick(L))
/mob/abstract/ghost/verb/follow()
set name = "Follow"
set category = "Ghost"
set desc = "Follow and haunt a mob."
GLOB.follow_menu.ui_interact(src)
// This is the ghost's follow verb with an argument
/mob/abstract/ghost/proc/ManualFollow(var/atom/movable/target)
if(!target)
return FALSE
//Stops orbit if there's any; TG doesn't do this, but if you don't it breaks the orbiting reference
//if you are jumping from one mob to another, hence why we're doing it here
orbiting?.end_orbit(src)
var/list/icon_dimensions = get_icon_dimensions(target.icon)
var/orbitsize = (icon_dimensions["width"] + icon_dimensions["height"]) * 0.5
orbitsize -= (orbitsize/ICON_SIZE_ALL)*(ICON_SIZE_ALL*0.25)
var/rot_seg = 30 //Let's make it simple and it's just a circle
orbit(target,orbitsize, FALSE, 20, rot_seg)
to_chat(src, SPAN_NOTICE("Now following \the [target]."))
update_sight()
return TRUE
/mob/abstract/ghost/proc/update_sight()
//if they are on a restricted level, then set the ghost vision for them.
if(on_restricted_level())
//On the restricted level they have the same sight as the mob
set_sight(sight&(~SEE_TURFS)&(~SEE_MOBS)&(~SEE_OBJS))
else
//Outside of the restrcited level, they have enhanced vision
set_sight(sight|SEE_TURFS|SEE_MOBS|SEE_OBJS)
if(ghostvision)
set_see_invisible(SEE_INVISIBLE_OBSERVER) //overmap viewing breaks if this is lower
else
set_see_invisible(SEE_INVISIBLE_LIVING)
/mob/abstract/ghost/proc/on_restricted_level(var/check)
if(!check)
check = z
//Check if they are a staff member
if(check_rights(R_MOD|R_ADMIN|R_DEV, show_msg=FALSE, user=src))
return FALSE
//Check if the z level is in the restricted list
if (!(check in SSatlas.current_map.restricted_levels))
return FALSE
return TRUE
/mob/abstract/ghost/verb/analyze_air()
set name = "Analyze Air"
set category = "Ghost"
// Shamelessly copied from the Gas Analyzers
if(!isturf(loc))
return
var/datum/gas_mixture/environment = loc.return_air()
var/pressure = XGM_PRESSURE(environment)
var/total_moles = environment.total_moles
to_chat(src, SPAN_NOTICE("Results:"))
if(abs(pressure - ONE_ATMOSPHERE) < 10)
to_chat(src, SPAN_NOTICE("Pressure: [round(pressure,0.1)] kPa"))
else
to_chat(src, SPAN_WARNING("Pressure: [round(pressure,0.1)] kPa"))
if(total_moles)
for(var/g in environment.gas)
to_chat(src, SPAN_NOTICE("[gas_data.name[g]]: [round((environment.gas[g] / total_moles) * 100)]% ([round(environment.gas[g], 0.01)] moles)"))
to_chat(src, SPAN_NOTICE("Temperature: [round(environment.temperature-T0C,0.1)]°C ([round(environment.temperature,0.1)]K)"))
to_chat(src, SPAN_NOTICE("Heat Capacity: [round(environment.heat_capacity(),0.1)]"))
/mob/abstract/ghost/verb/view_manifest()
set name = "Show Crew Manifest"
set category = "Ghost"
SSrecords.open_manifest_tgui(usr)