mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2025-12-09 20:23:34 +00:00
ghostroles (#3961)
* files * obj * mass ports * ah * wack * that * todo refactor ui intieract calls jfc * all * test case * compile * compile * auxtools time * wack * debugger hook * I LOVE YOU SPACEMANIAC THE DEBUGGER JUST SAVED MY LIFE
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
environment = "vorestation.dme"
|
||||
|
||||
[langserver]
|
||||
dreamchecker = true
|
||||
|
||||
[dmdoc]
|
||||
use_typepath_names = true
|
||||
|
||||
[debugger]
|
||||
engine = "auxtools"
|
||||
|
||||
24
code/__DEFINES/auxtools.dm
Normal file
24
code/__DEFINES/auxtools.dm
Normal file
@@ -0,0 +1,24 @@
|
||||
/proc/auxtools_stack_trace(msg)
|
||||
CRASH(msg)
|
||||
|
||||
GLOBAL_LIST_EMPTY(auxtools_initialized)
|
||||
|
||||
#define AUXTOOLS_CHECK(LIB)\
|
||||
if (!GLOB.auxtools_initialized[LIB]) {\
|
||||
if (fexists(LIB)) {\
|
||||
var/string = call(LIB,"auxtools_init")();\
|
||||
if(findtext(string, "SUCCESS")) {\
|
||||
GLOB.auxtools_initialized[LIB] = TRUE;\
|
||||
} else {\
|
||||
CRASH(string);\
|
||||
}\
|
||||
} else {\
|
||||
CRASH("No file named [LIB] found!")\
|
||||
}\
|
||||
}\
|
||||
|
||||
#define AUXTOOLS_SHUTDOWN(LIB)\
|
||||
if (GLOB.auxtools_initialized[LIB] && fexists(LIB)){\
|
||||
call(LIB,"auxtools_shutdown")();\
|
||||
GLOB.auxtools_initialized[LIB] = FALSE;\
|
||||
}\
|
||||
@@ -36,7 +36,7 @@
|
||||
///? Skips the specific attack step, continuing for the next one to happen.
|
||||
////#define COMPONENT_SKIP_ATTACK (1<<1)
|
||||
/// From base of atom/attack_ghost(): (mob/dead/observer/ghost)
|
||||
////#define COMSIG_ATOM_ATTACK_GHOST "atom_attack_ghost"
|
||||
#define COMSIG_ATOM_ATTACK_GHOST "atom_attack_ghost"
|
||||
/// From base of atom/attack_hand(): (mob/user, list/modifiers)
|
||||
////#define COMSIG_ATOM_ATTACK_HAND "atom_attack_hand"
|
||||
/// From base of atom/attack_paw(): (mob/user)
|
||||
|
||||
4
code/__DEFINES/rendering/alert.dm
Normal file
4
code/__DEFINES/rendering/alert.dm
Normal file
@@ -0,0 +1,4 @@
|
||||
// Notification action types
|
||||
#define NOTIFY_JUMP "jump"
|
||||
#define NOTIFY_ATTACK "attack"
|
||||
#define NOTIFY_ORBIT "orbit"
|
||||
@@ -28,9 +28,8 @@
|
||||
#define VAR_PROTECTED var
|
||||
#endif
|
||||
|
||||
/*
|
||||
/world/proc/enable_debugger()
|
||||
var/dll = world.GetConfig("env", "EXTOOLS_DLL")
|
||||
if (dll)
|
||||
call(dll, "debug_initialize")()
|
||||
*/
|
||||
/proc/enable_debugging()
|
||||
CRASH("Auxtools not found")
|
||||
|
||||
/proc/auxtools_expr_stub()
|
||||
CRASH("Auxtools not found")
|
||||
|
||||
@@ -203,6 +203,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
#define ORGAN_TRAIT "organ"
|
||||
*/
|
||||
#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention
|
||||
#define GHOSTROLE_TRAIT "ghostrole"
|
||||
#define JOB_TRAIT "job"
|
||||
#define TRAIT_MIME "mime" //Mime trait.
|
||||
/*
|
||||
|
||||
7
code/__HELPERS/lists/json.dm
Normal file
7
code/__HELPERS/lists/json.dm
Normal file
@@ -0,0 +1,7 @@
|
||||
/proc/safe_json_encode(list/L, default = "")
|
||||
. = default
|
||||
return json_encode(L)
|
||||
|
||||
/proc/safe_json_decode(string, default = list())
|
||||
. = default
|
||||
return json_decode(string)
|
||||
@@ -167,6 +167,7 @@
|
||||
#define ui_ghost_teleport "SOUTH:6,CENTER+1:24"
|
||||
#define ui_ghost_pai "SOUTH: 6,CENTER+2:24"
|
||||
#define ui_ghost_updown "SOUTH: 6,CENTER+3:24"
|
||||
#define ui_ghost_spawners "SOUTH: 6,CENTER+4:24"
|
||||
|
||||
// Rig panel
|
||||
#define ui_rig_deco1 "WEST:-7, SOUTH+5"
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
else
|
||||
alert = new type
|
||||
|
||||
alert.owner = src
|
||||
|
||||
if(new_master)
|
||||
alert.icon_state = "itembased"
|
||||
var/image/I = image(icon = new_master.icon, icon_state = new_master.icon_state, dir = SOUTH)
|
||||
@@ -82,16 +84,19 @@
|
||||
var/severity = 0
|
||||
var/alerttooltipstyle = ""
|
||||
var/no_underlay // Don't underlay the UI style's blank template icon under this
|
||||
/// mob that owns us
|
||||
var/mob/owner
|
||||
|
||||
/atom/movable/screen/alert/Destroy()
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
/atom/movable/screen/alert/MouseEntered(location,control,params)
|
||||
openToolTip(usr, src, params, title = name, content = desc, theme = alerttooltipstyle)
|
||||
|
||||
|
||||
/atom/movable/screen/alert/MouseExited()
|
||||
closeToolTip(usr)
|
||||
|
||||
|
||||
//Gas alerts
|
||||
/atom/movable/screen/alert/not_enough_oxy
|
||||
name = "Choking (No O2)"
|
||||
@@ -373,6 +378,32 @@ so as to remain in compliance with the most up-to-date laws."
|
||||
var/mob/observer/dead/G = usr
|
||||
G.reenter_corpse()
|
||||
|
||||
/atom/movable/screen/alert/notify_action
|
||||
name = "Body created"
|
||||
desc = "A body was created. You can enter it."
|
||||
icon_state = "template"
|
||||
timeout = 300
|
||||
var/atom/target = null
|
||||
var/action = NOTIFY_JUMP
|
||||
|
||||
/atom/movable/screen/alert/notify_action/Click()
|
||||
if(!usr || !usr.client || usr != owner)
|
||||
return
|
||||
if(!target)
|
||||
return
|
||||
var/mob/observer/dead/G = usr
|
||||
if(!istype(G))
|
||||
return
|
||||
switch(action)
|
||||
if(NOTIFY_ATTACK)
|
||||
target.attack_ghost(G)
|
||||
if(NOTIFY_JUMP)
|
||||
var/turf/T = get_turf(target)
|
||||
if(T && isturf(T))
|
||||
G.forceMove(T)
|
||||
if(NOTIFY_ORBIT)
|
||||
G.ManualFollow(target)
|
||||
|
||||
// /atom/movable/screen/alert/notify_jump
|
||||
// name = "Body created"
|
||||
// desc = "A body was created. You can enter it."
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/atom/movable/screen/ghost
|
||||
icon = 'icons/mob/screen_ghost.dmi'
|
||||
icon = 'icons/hud/common/observer.dmi'
|
||||
|
||||
/atom/movable/screen/ghost/MouseEntered(location,control,params)
|
||||
flick(icon_state + "_anim", src)
|
||||
@@ -91,6 +91,16 @@
|
||||
var/mob/observer/dead/G = usr
|
||||
G.zMove(DOWN)
|
||||
|
||||
/atom/movable/screen/ghost/spawners
|
||||
name = "Ghost Roles/Spawners"
|
||||
desc = "Open the ghostrole/spawner menu."
|
||||
icon_state = "spawners"
|
||||
|
||||
/atom/movable/screen/ghost/spawners/Click()
|
||||
. = ..()
|
||||
GLOB.ghostrole_menu.ui_interact(usr)
|
||||
|
||||
// TODO; /datum/hud refactor
|
||||
/datum/hud/proc/ghost_hud(apply_to_client = TRUE)
|
||||
|
||||
var/list/adding = list()
|
||||
@@ -136,6 +146,11 @@
|
||||
using.hud = src
|
||||
adding += using
|
||||
|
||||
using = new /atom/movable/screen/ghost/spawners
|
||||
using.screen_loc = ui_ghost_spawners
|
||||
using.hud = src
|
||||
adding += using
|
||||
|
||||
if(mymob.client && apply_to_client)
|
||||
mymob.client.screen = list()
|
||||
mymob.client.screen += adding
|
||||
|
||||
@@ -43,13 +43,15 @@
|
||||
A.attack_ghost(src)
|
||||
|
||||
// Oh by the way this didn't work with old click code which is why clicking shit didn't spam you
|
||||
/atom/proc/attack_ghost(mob/observer/dead/user as mob)
|
||||
/atom/proc/attack_ghost(mob/observer/dead/user)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_GHOST, user)
|
||||
// TODO: main ai interact bay code fucking disgusts me wtf
|
||||
if(IsAdminGhost(user)) // admin AI interact
|
||||
AdminAIInteract(user)
|
||||
return
|
||||
if(user.client && user.client.inquisitive_ghost)
|
||||
user.examinate(src)
|
||||
return
|
||||
|
||||
// defaults to just attack_ai
|
||||
/atom/proc/AdminAIInteract(mob/user)
|
||||
@@ -65,19 +67,22 @@
|
||||
if(com.locked)
|
||||
user.loc = get_turf(com.locked)
|
||||
*/
|
||||
/obj/effect/portal/attack_ghost(mob/user as mob)
|
||||
/obj/effect/portal/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
if(target)
|
||||
user.loc = get_turf(target)
|
||||
user.forceMove(get_turf(target))
|
||||
|
||||
/obj/machinery/gateway/centerstation/attack_ghost(mob/user as mob)
|
||||
/obj/machinery/gateway/centerstation/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
if(awaygate)
|
||||
user.loc = awaygate.loc
|
||||
user.forceMove(awaygate.loc)
|
||||
else
|
||||
to_chat(user, "[src] has no destination.")
|
||||
|
||||
/obj/machinery/gateway/centeraway/attack_ghost(mob/user as mob)
|
||||
/obj/machinery/gateway/centeraway/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
if(stationgate)
|
||||
user.loc = stationgate.loc
|
||||
user.forceMove(stationgate.loc)
|
||||
else
|
||||
to_chat(user, "[src] has no destination.")
|
||||
|
||||
@@ -95,7 +100,8 @@
|
||||
*/
|
||||
|
||||
//VR FILE MERGE
|
||||
/obj/item/paicard/attack_ghost(mob/user as mob)
|
||||
/obj/item/paicard/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
if(src.pai != null) //Have a person in them already?
|
||||
user.examinate(src)
|
||||
return
|
||||
|
||||
@@ -483,7 +483,7 @@
|
||||
return 0
|
||||
|
||||
//Initialisation procs
|
||||
/mob/living/proc/mind_initialize()
|
||||
/mob/proc/mind_initialize()
|
||||
if(mind)
|
||||
mind.key = key
|
||||
else
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
|
||||
// /vg/: Don't let ghosts fuck with this.
|
||||
/turf/unsimulated/wall/supermatter/attack_ghost(mob/user as mob)
|
||||
. = ..()
|
||||
user.examinate(src)
|
||||
|
||||
/turf/unsimulated/wall/supermatter/attack_ai(mob/user as mob)
|
||||
|
||||
@@ -253,6 +253,7 @@
|
||||
return attack_hand(user)
|
||||
|
||||
/obj/machinery/body_scanconsole/attack_ghost(user as mob)
|
||||
. = ..()
|
||||
return attack_hand(user)
|
||||
|
||||
/obj/machinery/body_scanconsole/attack_hand(user as mob)
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
return src.attack_hand(user)
|
||||
|
||||
/obj/machinery/portable_atmospherics/powered/pump/attack_ghost(var/mob/user)
|
||||
. = ..()
|
||||
return src.attack_hand(user)
|
||||
|
||||
/obj/machinery/portable_atmospherics/powered/pump/attack_hand(var/mob/user)
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
return src.attack_hand(user)
|
||||
|
||||
/obj/machinery/portable_atmospherics/powered/scrubber/attack_ghost(var/mob/user)
|
||||
. = ..()
|
||||
return src.attack_hand(user)
|
||||
|
||||
/obj/machinery/portable_atmospherics/powered/scrubber/attack_hand(var/mob/user)
|
||||
|
||||
@@ -53,9 +53,9 @@
|
||||
return
|
||||
|
||||
/obj/machinery/computer/aiupload/attack_ghost(user as mob)
|
||||
. = ..()
|
||||
return 1
|
||||
|
||||
|
||||
/obj/machinery/computer/borgupload
|
||||
name = "cyborg upload console"
|
||||
desc = "Used to upload laws to Cyborgs."
|
||||
@@ -89,4 +89,5 @@
|
||||
return
|
||||
|
||||
/obj/machinery/computer/borgupload/attack_ghost(user as mob)
|
||||
. = ..()
|
||||
return 1
|
||||
|
||||
@@ -841,6 +841,7 @@ About the new airlock wires panel:
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/door/airlock/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/door/airlock/ui_interact(mob/user, datum/tgui/ui)
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
|
||||
|
||||
/obj/machinery/tele_pad/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
if (!computer?.active)
|
||||
return
|
||||
var/turf/T = get_turf(computer.target)
|
||||
|
||||
@@ -131,6 +131,7 @@
|
||||
attack_generic()
|
||||
|
||||
/obj/effect/catwalk_plated/attack_ghost()
|
||||
. = ..()
|
||||
attack_generic()
|
||||
|
||||
/obj/effect/catwalk_plated/attack_generic()
|
||||
|
||||
@@ -375,6 +375,7 @@
|
||||
to_chat(usr, "<span class='notice'>It won't budge!</span>")
|
||||
|
||||
/obj/structure/closet/attack_ghost(mob/ghost)
|
||||
. = ..()
|
||||
if(ghost.client && ghost.client.inquisitive_ghost)
|
||||
ghost.examinate(src)
|
||||
if (!src.opened)
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
description_info = "A ghost can click on this to return to the round as whatever is contained inside this object."
|
||||
|
||||
/obj/structure/ghost_pod/ghost_activated/attack_ghost(var/mob/observer/dead/user)
|
||||
. = ..()
|
||||
if(used)
|
||||
to_chat(user, "<span class='warning'>Another spirit appears to have gotten to \the [src] before you. Sorry.</span>")
|
||||
return
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
attack_generic()
|
||||
|
||||
/obj/effect/wingrille_spawn/attack_ghost()
|
||||
. = ..()
|
||||
attack_generic()
|
||||
|
||||
/obj/effect/wingrille_spawn/attack_generic()
|
||||
|
||||
@@ -18,12 +18,16 @@ GLOBAL_LIST(topic_status_cache)
|
||||
//This happens after the Master subsystem new(s) (it's a global datum)
|
||||
//So subsystems globals exist, but are not initialised
|
||||
/world/New()
|
||||
// enable_debugger()
|
||||
var/debug_server = world.GetConfig("env", "AUXTOOLS_DEBUG_DLL")
|
||||
if (debug_server)
|
||||
call(debug_server, "auxtools_init")()
|
||||
enable_debugging()
|
||||
|
||||
log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!")
|
||||
|
||||
var/tempfile = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set
|
||||
GLOB.config_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_map_error_log = GLOB.world_attack_log = GLOB.world_game_log = tempfile
|
||||
// citadel edit: world runtime log removed due to world.log shunt doing that for us
|
||||
GLOB.config_error_log = GLOB.world_href_log = GLOB.world_map_error_log = GLOB.world_attack_log = GLOB.world_game_log = tempfile
|
||||
|
||||
world.Profile(PROFILE_START)
|
||||
make_datum_reference_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
|
||||
@@ -300,6 +304,12 @@ GLOBAL_REAL_VAR(world_log_redirected) = FALSE
|
||||
shutdown_logging() // Past this point, no logging procs can be used, at risk of data loss.
|
||||
..()
|
||||
|
||||
/world/Del()
|
||||
var/debug_server = world.GetConfig("env", "AUXTOOLS_DEBUG_DLL")
|
||||
if (debug_server)
|
||||
call(debug_server, "auxtools_shutdown")()
|
||||
..()
|
||||
|
||||
/hook/startup/proc/loadMode()
|
||||
world.load_mode()
|
||||
return 1
|
||||
|
||||
@@ -228,6 +228,7 @@ Thus, the two variables affect pump operation are set in New():
|
||||
return
|
||||
|
||||
/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user)
|
||||
|
||||
@@ -721,7 +721,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
using_perspective = null
|
||||
if(!P)
|
||||
eye = null
|
||||
virtual_eye = null
|
||||
lazy_eye = 0
|
||||
perspective = EYE_PERSPECTIVE
|
||||
return
|
||||
|
||||
152
code/modules/ghostroles/instantiator.dm
Normal file
152
code/modules/ghostroles/instantiator.dm
Normal file
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* Handles mob creation, equip, and ckey transfer.
|
||||
*/
|
||||
/datum/ghostrole_instantiator
|
||||
/// traits to add to mob : will be made with GHOSTROLE_TRAIT source
|
||||
var/list/mob_traits
|
||||
|
||||
/datum/ghostrole_instantiator/proc/Run(client/C, atom/location, list/params)
|
||||
RETURN_TYPE(/mob)
|
||||
. = Create(C, location, params)
|
||||
if(!.)
|
||||
return
|
||||
Equip(C, ., params)
|
||||
|
||||
/datum/ghostrole_instantiator/proc/Create(client/C, atom/location, list/params)
|
||||
CRASH("Base Create() called on ghostrole instantiator datum.")
|
||||
|
||||
/datum/ghostrole_instantiator/proc/Equip(client/C, mob/M, list/params)
|
||||
CRASH("Base Equip() called on ghostrole instantiator datum.")
|
||||
|
||||
/datum/ghostrole_instantiator/simple
|
||||
var/mob_type
|
||||
|
||||
/datum/ghostrole_instantiator/simple/Create(client/C, atom/location, list/params)
|
||||
var/type_to_make = GetMobType(location)
|
||||
if(!ispath(type_to_make, /mob/living))
|
||||
CRASH("Invalid path: [type_to_make]")
|
||||
var/mob/living/L = new type_to_make(location)
|
||||
for(var/trait in mob_traits)
|
||||
ADD_TRAIT(L, trait, GHOSTROLE_TRAIT)
|
||||
return L
|
||||
|
||||
/datum/ghostrole_instantiator/simple/proc/GetMobType(client/C, atom/location, list/params)
|
||||
return params["mob"] || mob_type
|
||||
|
||||
/datum/ghostrole_instantiator/human
|
||||
/// outfit to equip
|
||||
var/equip_outfit
|
||||
|
||||
/datum/ghostrole_instantiator/human/Create(client/C, atom/location, list/params)
|
||||
var/mob/living/carbon/human/H = new(location)
|
||||
for(var/trait in mob_traits)
|
||||
ADD_TRAIT(H, trait, GHOSTROLE_TRAIT)
|
||||
return H
|
||||
|
||||
/datum/ghostrole_instantiator/human/Equip(client/C, mob/M, list/params)
|
||||
var/mob/living/carbon/human/H = M
|
||||
|
||||
// H.dna.species.before_equip_job(null, H)
|
||||
|
||||
var/datum/outfit/O = GetOutfit(C, M, params)
|
||||
if(ispath(O, /datum/outfit))
|
||||
O = new O
|
||||
O.equip(M)
|
||||
|
||||
H.species.equip_survival_gear(H, TRUE, TRUE)
|
||||
// H.dna.species.after_equip_job(null, H)
|
||||
|
||||
/**
|
||||
* Returns an outfit instance or a typepath
|
||||
*/
|
||||
/datum/ghostrole_instantiator/human/proc/GetOutfit(client/C, mob/M, list/params)
|
||||
// allow for outfit override
|
||||
if(params["outfit"])
|
||||
var/override = params["outfit"]
|
||||
if(istext(override))
|
||||
override = text2path(override)
|
||||
if(ispath(override, /datum/outfit))
|
||||
return override
|
||||
if(istype(override, /datum/outfit))
|
||||
return override
|
||||
if(ispath(equip_outfit, /datum/outfit))
|
||||
return equip_outfit
|
||||
if(istype(equip_outfit, /datum/outfit))
|
||||
return equip_outfit
|
||||
return new /datum/outfit
|
||||
|
||||
/datum/ghostrole_instantiator/human/random
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/Create(client/C, atom/location, list/params)
|
||||
var/mob/living/carbon/human/H = ..()
|
||||
Randomize(H, params)
|
||||
return H
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/proc/Randomize(mob/living/carbon/human/H, list/params)
|
||||
return // tgcode does this automatically
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species
|
||||
/// allowed species types
|
||||
var/list/possible_species = list(
|
||||
/datum/species/human,
|
||||
/datum/species/unathi,
|
||||
/datum/species/tajaran,
|
||||
/datum/species/skrell,
|
||||
/datum/species/akula,
|
||||
/datum/species/diona
|
||||
// /datum/species/lizard,
|
||||
// /datum/species/plasmaman,
|
||||
// /datum/species/jelly,
|
||||
// /datum/species/ipc
|
||||
)
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/proc/GetSpeciesPath(mob/living/carbon/human/H, list/params)
|
||||
var/override = params["species"]
|
||||
if(istext(override))
|
||||
override = text2path(override)
|
||||
if(ispath(override, /datum/species))
|
||||
return override
|
||||
return SAFEPICK(possible_species) || /datum/species/human
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/Randomize(mob/living/carbon/human/H, list/params)
|
||||
. = ..()
|
||||
var/species = pick(GetSpeciesPath(H, params))
|
||||
// todo species refactor
|
||||
var/datum/species/S = species
|
||||
H.set_species(initial(S.name))
|
||||
var/new_name = random_name(H.gender, H.species.name)
|
||||
// H.set_species(new species)
|
||||
// var/new_name
|
||||
// switch(H.dna.species.type)
|
||||
// if(/datum/species/lizard)
|
||||
// new_name = random_unique_lizard_name()
|
||||
// if(/datum/species/ethereal)
|
||||
// new_name = random_unique_ethereal_name()
|
||||
// if(/datum/species/plasmaman)
|
||||
// new_name = random_unique_plasmaman_name()
|
||||
// if(/datum/species/insect)
|
||||
// new_name = random_unique_moth_name()
|
||||
// if(/datum/species/arachnid)
|
||||
// new_name = random_unique_arachnid_name()
|
||||
// else
|
||||
// new_name = random_unique_name()
|
||||
H.fully_replace_character_name(H.real_name, new_name)
|
||||
|
||||
/datum/ghostrole_instantiator/human/player_static
|
||||
/// equip loadout
|
||||
var/equip_loadout = TRUE
|
||||
/// equip traits
|
||||
var/equip_traits = TRUE
|
||||
|
||||
/datum/ghostrole_instantiator/human/player_static/Create(client/C, atom/location, list/params)
|
||||
var/mob/living/carbon/human/H = ..()
|
||||
LoadSavefile(C, H)
|
||||
return H
|
||||
|
||||
/datum/ghostrole_instantiator/human/player_static/proc/LoadSavefile(client/C, mob/living/carbon/human/H)
|
||||
C.prefs.copy_to(H)
|
||||
job_master.EquipRank(H, USELESS_JOB)
|
||||
// if(equip_loadout)
|
||||
// SSjob.EquipLoadout(H, FALSE, null, C.prefs, C.ckey)
|
||||
// if(equip_traits && CONFIG_GET(flag/roundstart_traits))
|
||||
// SSquirks.AssignQuirks(H, C, TRUE, FALSE, null, FALSE, C)
|
||||
66
code/modules/ghostroles/menu.dm
Normal file
66
code/modules/ghostroles/menu.dm
Normal file
@@ -0,0 +1,66 @@
|
||||
GLOBAL_DATUM_INIT(ghostrole_menu, /datum/ghostrole_menu, new)
|
||||
|
||||
/datum/ghostrole_menu
|
||||
|
||||
/datum/ghostrole_menu/ui_state(mob/user)
|
||||
return GLOB.observer_state
|
||||
|
||||
/datum/ghostrole_menu/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "GhostRoleMenu")
|
||||
ui.open()
|
||||
|
||||
/datum/ghostrole_menu/ui_static_data(mob/user)
|
||||
. = ..()
|
||||
var/list/spawners = list()
|
||||
.["spawners"] = spawners
|
||||
for(var/id in GLOB.ghostroles)
|
||||
var/datum/ghostrole/role = GLOB.ghostroles[id]
|
||||
if(!istype(role))
|
||||
stack_trace("non ghostrole [role] ([id]) pruned from ghostroles list.")
|
||||
GLOB.ghostroles -= id
|
||||
continue
|
||||
var/list/data = list()
|
||||
data["id"] = role.id || role.type
|
||||
data["name"] = role.name
|
||||
data["short_desc"] = role.desc
|
||||
data["flavor_text"] = role.spawntext
|
||||
data["important_info"] = role.ImportantInfo()
|
||||
var/slots = role.SpawnsLeft(user)
|
||||
data["amount_left"] = slots == INFINITY? -1 : slots
|
||||
spawners += list(data) // wrap
|
||||
|
||||
/datum/ghostrole_menu/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
if(!isobserver(usr))
|
||||
return
|
||||
var/id = params["id"]
|
||||
var/datum/ghostrole/role = get_ghostrole_datum(id)
|
||||
if(!role)
|
||||
return
|
||||
switch(action)
|
||||
if("jump")
|
||||
if(role.spawnerless)
|
||||
to_chat(usr, "<span class='warning'>[role] is spawnerless! You won't be able to find out where you spawn until you actually spawn in!</span>")
|
||||
return
|
||||
var/atom/A = role.GetSpawnLoc(usr.client, role.GetSpawnpoint(usr.client))
|
||||
if(!A)
|
||||
to_chat(usr, "<span class='warning'>Could not find a spawnpoint for [role]. This sometimes mean it isn't loaded in until someone attempts to spawn. Alternatively, you didn't pick one!</span>")
|
||||
return
|
||||
if(!A.loc)
|
||||
to_chat(usr, "<span class='danger'>BUG: Spawnpoint was nullspace.</span>")
|
||||
return
|
||||
usr.forceMove(get_turf(A))
|
||||
if("spawn")
|
||||
var/client/C = usr.client
|
||||
var/error = role.AttemptSpawn(C)
|
||||
if(istext(error))
|
||||
to_chat(C, SPAN_DANGER(error))
|
||||
|
||||
/**
|
||||
* Call this whenever ghostrole data changes, we don't keep resending to save performance.
|
||||
*/
|
||||
/datum/ghostrole_menu/proc/queue_update()
|
||||
addtimer(CALLBACK(src, /datum/proc/update_static_data), 0, TIMER_UNIQUE | TIMER_OVERRIDE)
|
||||
215
code/modules/ghostroles/role.dm
Normal file
215
code/modules/ghostroles/role.dm
Normal file
@@ -0,0 +1,215 @@
|
||||
GLOBAL_LIST_INIT(ghostroles, init_ghostroles())
|
||||
|
||||
/proc/init_ghostroles()
|
||||
. = list()
|
||||
for(var/path in subtypesof(/datum/ghostrole))
|
||||
var/datum/ghostrole/G = path
|
||||
if(initial(G.abstract_type) == path)
|
||||
continue
|
||||
if(initial(G.lazy_init))
|
||||
continue
|
||||
.[path] = new path
|
||||
|
||||
/**
|
||||
* This intentionally supports strings for badmin purposes.
|
||||
*/
|
||||
/proc/get_ghostrole_datum(path)
|
||||
if(GLOB.ghostroles[path])
|
||||
return GLOB.ghostroles[path]
|
||||
var/is_this_a_path = ispath(path)? path : text2path(path)
|
||||
if(ispath(is_this_a_path, /datum/ghostrole))
|
||||
GLOB.ghostroles[is_this_a_path] = new is_this_a_path
|
||||
return GLOB.ghostroles[is_this_a_path]
|
||||
|
||||
/**
|
||||
* Ghostrole datums
|
||||
*/
|
||||
/datum/ghostrole
|
||||
/// name
|
||||
var/name = "Unnamed Role"
|
||||
/// **short** description - use spawntext for long one.
|
||||
var/desc = "Wow, a coder fucked up."
|
||||
/// init on server load or only when needed
|
||||
var/lazy_init = TRUE
|
||||
/// allow selecting the spawner, or random? **If the spawner gets clicked by a player, they can still spawn from it!**
|
||||
var/allow_pick_spawner = FALSE
|
||||
/// abstract type
|
||||
var/abstract_type = /datum/ghostrole
|
||||
/// /datum/ghostrole_instantiator - handles mob creation, equip, and transfer. DOES NOT greet the ghostrole with role information.
|
||||
var/datum/ghostrole_instantiator/instantiator
|
||||
/// spawn count
|
||||
var/spawns = 0
|
||||
/// max spawns
|
||||
var/slots = INFINITY
|
||||
/// default message to show on greet(), also shows in spawners menu.
|
||||
var/spawntext
|
||||
/// important rules/policy info
|
||||
var/important_info
|
||||
/// should we show the standard ghostrole greeting?
|
||||
var/show_standard_greeting = TRUE
|
||||
/// snowflake ID for if we're not to be referred to by path - dynamically created ghostolres
|
||||
var/id
|
||||
/// spawnerless - advanced users only. This isn't for "load in spawners in PreInstantiate()", this is for true spawnpoint-less ghostroles.
|
||||
var/spawnerless = FALSE
|
||||
/// assigned role. defaults to name.
|
||||
var/assigned_role
|
||||
/// jobban name/id, if any
|
||||
var/jobban_role
|
||||
/// Automatically give them an objective and custom antag datum
|
||||
var/automatic_objective
|
||||
/// inject params during spawning
|
||||
var/list/inject_params
|
||||
|
||||
/datum/ghostrole/New(_id)
|
||||
if(ispath(instantiator, /datum/ghostrole_instantiator))
|
||||
instantiator = new instantiator
|
||||
id = _id || type
|
||||
|
||||
/datum/ghostrole/proc/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
if(show_standard_greeting)
|
||||
to_chat(created, {"<center><b><font size='16px'>You have spawned as a ghostrole.</font></b></center>
|
||||
These roles are usually more roleplay oriented than standard hard-defined antagonist roles - besure to follow spawntext (if any), as well as server rules.
|
||||
Spawntext as follows;"})
|
||||
if(spawntext)
|
||||
to_chat(created, spawntext)
|
||||
if(spawnpoint.spawntext)
|
||||
to_chat(created, spawntext)
|
||||
|
||||
/datum/ghostrole/proc/ImportantInfo()
|
||||
// todo: policyconfig
|
||||
return important_info
|
||||
|
||||
/**
|
||||
* Master proc for spawning someone as this role.
|
||||
*
|
||||
* Return TRUe on success, or a string of why it failed.
|
||||
*/
|
||||
/datum/ghostrole/proc/AttemptSpawn(client/C, datum/component/ghostrole_spawnpoint/chosen_spawnpoint)
|
||||
if(BanCheck(C))
|
||||
return "You can't spawn as [src] due to an active job-ban."
|
||||
if(!AllowSpawn(C))
|
||||
return "You can't spawn as this role; Try refreshing the ghostrole/join menu."
|
||||
if(!PreInstantiate(C))
|
||||
return "PreInstantiate() failed."
|
||||
var/datum/component/ghostrole_spawnpoint/spawnpoint = spawnerless? null : (chosen_spawnpoint || GetSpawnpoint(C))
|
||||
var/list/params = islist(spawnpoint?.params)? spawnpoint.params.Copy() : list() // clone/new, because procs CAN MODIFY THIS.
|
||||
if(inject_params)
|
||||
params |= inject_params
|
||||
if(!AllowSpawn(C, params)) // check again with params
|
||||
return "The spawnpoint refused to let you spawn."
|
||||
var/atom/location = GetSpawnLoc(C, spawnpoint)
|
||||
if(!location)
|
||||
return "Couldn't get a spawn location."
|
||||
if(!instantiator)
|
||||
return "BUG: No instantiator for [src][(id !=type) && ":[id]"] ([type])"
|
||||
var/mob/created = Instantiate(C, location, params)
|
||||
if(!created)
|
||||
return "Mob instantiation failed."
|
||||
if(!Transfer(C, created))
|
||||
qdel(created)
|
||||
return "Mob transfer failed."
|
||||
PostInstantiate(created, spawnpoint, params)
|
||||
// GLOB.join_menu.queue_update()
|
||||
GLOB.ghostrole_menu.queue_update()
|
||||
return TRUE
|
||||
|
||||
/datum/ghostrole/proc/Instantiate(client/C, atom/loc, list/params)
|
||||
var/mob/living/L = instantiator.Run(C, loc, params)
|
||||
. = istype(L) && L
|
||||
if(.)
|
||||
L.mind?.assigned_role = assigned_role || name
|
||||
|
||||
/datum/ghostrole/proc/Transfer(client/C, mob/created)
|
||||
if(!isnewplayer(C.mob))
|
||||
C.mob.ghostize(TRUE, TRUE)
|
||||
created.ckey = C.ckey
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Ran before anything else is at AttemptSpawn()
|
||||
*/
|
||||
/datum/ghostrole/proc/PreInstantiate(client/C)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Checks if the client is a valid user mob and if we can allow a spawn from them
|
||||
*/
|
||||
/datum/ghostrole/proc/AllowSpawn(client/C, list/params)
|
||||
if(!isobserver(C.mob) && !isnewplayer(C.mob))
|
||||
return FALSE
|
||||
if(SpawnsLeft(C) <= 0)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/ghostrole/proc/SpawnsLeft(client/C)
|
||||
if(spawnerless)
|
||||
return max(0, slots - spawns)
|
||||
return min(max(0, slots - spawns), TallySpawnpointSlots(C))
|
||||
|
||||
/datum/ghostrole/proc/TallySpawnpointSlots(client/C)
|
||||
var/list/datum/component/ghostrole_spawnpoint/spawnpoints = GLOB.ghostrole_spawnpoints[id]
|
||||
. = 0
|
||||
for(var/datum/component/ghostrole_spawnpoint/S as anything in spawnpoints)
|
||||
. += S.SpawnsLeft(C)
|
||||
|
||||
/**
|
||||
* Gets a spawnpoint for a client
|
||||
*
|
||||
* For spawnerless ghostroles, return null.
|
||||
*/
|
||||
/datum/ghostrole/proc/GetSpawnpoint(client/C)
|
||||
if(!allow_pick_spawner)
|
||||
return SAFEPICK(GLOB.ghostrole_spawnpoints[id])
|
||||
var/list/datum/component/ghostrole_spawnpoint/spawnpoints = GLOB.ghostrole_spawnpoints[id]
|
||||
var/list/inputlist = list()
|
||||
for(var/datum/component/ghostrole_spawnpoint/spawnpoint as anything in spawnpoints)
|
||||
var/atom/A = spawnpoint.Atom()
|
||||
inputlist["[A] - [get_area(A)]"] = spawnpoint
|
||||
var/picked = tgui_input_list(C.mob, "Spawner Selection", inputlist)
|
||||
return inputlist[picked]
|
||||
|
||||
/**
|
||||
* Gets a spawn location for a client.
|
||||
*
|
||||
* spawnpoint can be null for spawnerless ghostroles.
|
||||
*/
|
||||
/datum/ghostrole/proc/GetSpawnLoc(client/C, datum/component/ghostrole_spawnpoint/spawnpoint)
|
||||
return spawnpoint?.Turf()
|
||||
|
||||
/**
|
||||
* Spawnpoint can be null here, if we're not using a spawnpoint
|
||||
*/
|
||||
/datum/ghostrole/proc/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
Greet(created, spawnpoint, params)
|
||||
if(automatic_objective)
|
||||
GiveCustomObjective(created, automatic_objective)
|
||||
spawns++
|
||||
spawnpoint?.OnSpawn(created, src)
|
||||
|
||||
/**
|
||||
* Ban check.
|
||||
*/
|
||||
/datum/ghostrole/proc/BanCheck(client/C)
|
||||
if(!jobban_role)
|
||||
return FALSE
|
||||
return jobban_isbanned(C.mob, jobban_role)
|
||||
|
||||
/datum/ghostrole/proc/GiveCustomObjective(mob/created, objective)
|
||||
created.GhostroleGiveCustomObjective(src, objective)
|
||||
|
||||
/mob/proc/GhostroleGiveCustomObjective(datum/ghostrole/R, objective)
|
||||
if(!mind)
|
||||
mind_initialize()
|
||||
if(!mind)
|
||||
CRASH("No mind.")
|
||||
|
||||
store_memory("OBJECTIVE: [objective]", TRUE)
|
||||
to_chat(src, SPAN_DANGER("An objective has been added to you by your ghostrole spawner. Remember that roleplay comes first - these are often freeform. Said objective is in your MEMORIES, due to the codebase lacking datum antagonists."))
|
||||
|
||||
// TODO: DATUM ANTAGS
|
||||
// var/datum/antagonist/custom/A = mind.has_antag_datum(/datum/antagonist/custom) || mind.add_antag_datum(/datum/antagonist/custom)
|
||||
// if(!A)
|
||||
// CRASH("Failed to locate/make custom antagonist datum.")
|
||||
// var/datum/objective/O = new(objective)
|
||||
// O.owner = mind
|
||||
// A.objectives += O
|
||||
81
code/modules/ghostroles/roles/ash_lizard.dm
Normal file
81
code/modules/ghostroles/roles/ash_lizard.dm
Normal file
@@ -0,0 +1,81 @@
|
||||
/datum/ghostrole/ashwalker
|
||||
name = "Ashwalker"
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/species/ashwalker
|
||||
desc = "You are an ash walker. Your tribe worships the Necropolis."
|
||||
spawntext = "The wastes are sacred ground, its monsters a blessed bounty. You would never willingly leave your homeland behind. \
|
||||
You have seen lights in the distance... they foreshadow the arrival of outsiders to your domain. \
|
||||
Ensure your nest remains protected at all costs."
|
||||
assigned_role = "Ash Walker"
|
||||
allow_pick_spawner = TRUE
|
||||
jobban_role = ROLE_LAVALAND
|
||||
|
||||
/datum/ghostrole/ashwalker/Greet(mob/created)
|
||||
. = ..()
|
||||
var/turf/T = get_turf(created)
|
||||
if(is_mining_level(T.z))
|
||||
to_chat(created, "<b>Drag the corpses of men and beasts to your nest. It will absorb them to create more of your kind. Glory to the Necropolis!</b>")
|
||||
to_chat(created, "<b>You can expand the weather proof area provided by your shelters by using the 'New Area' key near the bottom right of your HUD.</b>")
|
||||
else
|
||||
to_chat(created, "<span class='userdanger'>You have been born outside of your natural home! Whether you decide to return home, or make due with your new home is your own decision.</span>")
|
||||
|
||||
/datum/ghostrole/ashwalker/AllowSpawn(client/C, list/params)
|
||||
if(params && params["team"])
|
||||
var/datum/team/ashwalkers/team = params["team"]
|
||||
if(C.ckey in team.players_spawned)
|
||||
to_chat(C, span_warning("<b>You have exhausted your usefulness to the Necropolis</b>."))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/ghostrole/ashwalker/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
. = ..()
|
||||
if(params["team"])
|
||||
var/datum/team/ashwalkers/team = spawnpoint.params["team"]
|
||||
team.players_spawned += ckey(created.key)
|
||||
created.mind.add_antag_datum(/datum/antagonist/ashwalker, team)
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/ashwalker
|
||||
possible_species = list(
|
||||
/datum/species/lizard/ashwalker
|
||||
)
|
||||
equip_outfit = /datum/outfit/ashwalker
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/ashwalker/Randomize(mob/living/carbon/human/H, list/params)
|
||||
. = ..()
|
||||
H.underwear = "Nude"
|
||||
H.undershirt = "Nude"
|
||||
H.socks = "Nude"
|
||||
H.update_body()
|
||||
H.real_name = random_unique_lizard_name(H.gender)
|
||||
|
||||
/obj/structure/ghost_role_spawner/ash_walker
|
||||
name = "ash walker egg"
|
||||
desc = "A man-sized yellow egg, spawned from some unfathomable creature. A humanoid silhouette lurks within. The egg shell looks resistant to temperature but otherwise rather brittle."
|
||||
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "large_egg"
|
||||
anchored = FALSE
|
||||
move_resist = MOVE_FORCE_NORMAL
|
||||
density = FALSE
|
||||
role_type = /datum/ghostrole/ashwalker
|
||||
role_spawns = 1
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | FREEZE_PROOF
|
||||
max_integrity = 80
|
||||
var/datum/team/ashwalkers/team
|
||||
|
||||
/obj/structure/ghost_role_spawner/ash_walker/on_spawn(mob/created, datum/ghostrole/role, list/params)
|
||||
. = ..()
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/ghost_role_spawner/ash_walker/Destroy()
|
||||
var/mob/living/carbon/human/yolk = new /mob/living/carbon/human/(get_turf(src))
|
||||
yolk.fully_replace_character_name(null,random_unique_lizard_name(gender))
|
||||
yolk.set_species(/datum/species/lizard/ashwalker)
|
||||
yolk.underwear = "Nude"
|
||||
yolk.equipOutfit(/datum/outfit/ashwalker)//this is an authentic mess we're making
|
||||
yolk.update_body()
|
||||
yolk.gib()
|
||||
return ..()
|
||||
|
||||
/datum/outfit/ashwalker
|
||||
name ="Ashwalker"
|
||||
head = /obj/item/clothing/head/helmet/gladiator
|
||||
uniform = /obj/item/clothing/under/costume/gladiator/ash_walker
|
||||
101
code/modules/ghostroles/roles/cybersun_cruiser.dm
Normal file
101
code/modules/ghostroles/roles/cybersun_cruiser.dm
Normal file
@@ -0,0 +1,101 @@
|
||||
/datum/ghostrole/cybersun
|
||||
abstract_type = /datum/ghostrole/cybersun
|
||||
assigned_role = "Space Syndicate"
|
||||
|
||||
/datum/ghostrole/cybersun/ship
|
||||
name = "Cybersun Ship Operative"
|
||||
name = "Syndicate Battlecruiser Ship Operative"
|
||||
desc = "You are a crewmember aboard the syndicate flagship: the SBC Starfury."
|
||||
spawntext = "Your job is to follow your captain's orders, maintain the ship, and keep the engine running. If you are not familiar with how the supermatter engine functions: do not attempt to start it. \
|
||||
<br>Furthermore, the armory is not a candy store, and your role is not to assault the station directly, leave that work to the assault operatives."
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/cybersun/ship
|
||||
|
||||
/datum/ghostrole/cybersun/assault
|
||||
name = "Cybersun Assault Operative"
|
||||
desc = "You are an assault operative aboard the syndicate flagship: the SBC Starfury."
|
||||
spawntext = "Your job is to follow your captain's orders, keep intruders out of the ship, and assault Space Station 13. There is an armory, multiple assault ships, and beam cannons to attack the station with. \
|
||||
<br>Work as a team with your fellow operatives and work out a plan of attack. If you are overwhelmed, escape back to your ship!"
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/cybersun/assault
|
||||
|
||||
/datum/ghostrole/cybersun/captain
|
||||
name = "Cybersun Ship Captain"
|
||||
desc = "You are the captain aboard the syndicate flagship: the SBC Starfury."
|
||||
spawntext = "Your job is to oversee your crew, defend the ship, and destroy Space Station 13. The ship has an armory, multiple ships, beam cannons, and multiple crewmembers to accomplish this goal. \
|
||||
<br>As the captain, this whole operation falls on your shoulders. You do not need to nuke the station, causing sufficient damage and preventing your ship from being destroyed will be enough."
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/cybersun/captain
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/cybersun
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/cybersun/Create(client/C, atom/location)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/mob/M = .
|
||||
M.faction |= ROLE_SYNDICATE
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/cybersun/ship
|
||||
equip_outfit = /datum/outfit/syndicate_empty/SBC
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/cybersun/assault
|
||||
equip_outfit = /datum/outfit/syndicate_empty/SBC/assault
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/cybersun/captain
|
||||
equip_outfit = /datum/outfit/syndicate_empty/SBC/assault/captain
|
||||
|
||||
/obj/structure/ghost_role_spawner/syndicate
|
||||
name = "Syndicate Operative"
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
icon_state = "sleeper_s"
|
||||
qdel_on_deplete = TRUE
|
||||
role_type = null
|
||||
|
||||
/obj/structure/ghost_role_spawner/syndicate/battlecruiser
|
||||
role_type = /datum/ghostrole/cybersun/ship
|
||||
|
||||
/obj/structure/ghost_role_spawner/syndicate/battlecruiser/assault
|
||||
role_type = /datum/ghostrole/cybersun/assault
|
||||
|
||||
/obj/structure/ghost_role_spawner/syndicate/battlecruiser/captain
|
||||
role_type = /datum/ghostrole/cybersun/captain
|
||||
|
||||
/datum/outfit/syndicate_empty
|
||||
name = "Syndicate Operative Empty"
|
||||
uniform = /obj/item/clothing/under/syndicate
|
||||
shoes = /obj/item/clothing/shoes/combat
|
||||
gloves = /obj/item/clothing/gloves/tackler/combat/insulated
|
||||
ears = /obj/item/radio/headset/syndicate/alt
|
||||
back = /obj/item/storage/backpack
|
||||
implants = list(/obj/item/implant/weapons_auth)
|
||||
id = /obj/item/card/id/syndicate
|
||||
|
||||
/datum/outfit/syndicate_empty/SBC
|
||||
name = "Syndicate Battlecruiser Ship Operative"
|
||||
l_pocket = /obj/item/gun/ballistic/automatic/pistol
|
||||
r_pocket = /obj/item/kitchen/knife/combat/survival
|
||||
belt = /obj/item/storage/belt/military/assault
|
||||
|
||||
/datum/outfit/syndicate_empty/SBC/assault
|
||||
name = "Syndicate Battlecruiser Assault Operative"
|
||||
uniform = /obj/item/clothing/under/syndicate/combat
|
||||
l_pocket = /obj/item/ammo_box/magazine/m10mm
|
||||
r_pocket = /obj/item/kitchen/knife/combat/survival
|
||||
belt = /obj/item/storage/belt/military
|
||||
suit = /obj/item/clothing/suit/armor/vest
|
||||
suit_store = /obj/item/gun/ballistic/automatic/pistol
|
||||
back = /obj/item/storage/backpack/security
|
||||
mask = /obj/item/clothing/mask/gas/syndicate
|
||||
|
||||
/datum/outfit/syndicate_empty/SBC/assault/captain
|
||||
name = "Syndicate Battlecruiser Captain"
|
||||
l_pocket = /obj/item/melee/transforming/energy/sword/saber/red
|
||||
r_pocket = /obj/item/melee/classic_baton/telescopic
|
||||
suit = /obj/item/clothing/suit/armor/vest/capcarapace/syndicate
|
||||
suit_store = /obj/item/gun/ballistic/revolver/mateba
|
||||
back = /obj/item/storage/backpack/satchel/leather
|
||||
head = /obj/item/clothing/head/HoS/syndicate
|
||||
mask = /obj/item/clothing/mask/cigarette/cigar/havana
|
||||
glasses = /obj/item/clothing/glasses/thermal/eyepatch
|
||||
access_add = list(
|
||||
ACCESS_SYNDICATE,
|
||||
ACCESS_SYNDICATE_LEADER
|
||||
)
|
||||
68
code/modules/ghostroles/roles/demonic_friend.dm
Normal file
68
code/modules/ghostroles/roles/demonic_friend.dm
Normal file
@@ -0,0 +1,68 @@
|
||||
/datum/ghostrole/demonic_friend
|
||||
name = "Demonic Friend"
|
||||
desc = "You are someone's demonic friend from hell."
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/demonic_friend
|
||||
assigned_role = "SuperFriend"
|
||||
|
||||
/datum/ghostrole/demonic_friend/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
. = ..()
|
||||
if(params["spell"])
|
||||
var/obj/effect/proc_holder/spell/targeted/summon_friend/S = spawnpoint?.params["spell"]
|
||||
S.friend = created
|
||||
S.charge_counter = S.charge_max
|
||||
if(!created.mind)
|
||||
CRASH("No mind")
|
||||
created.mind.hasSoul = FALSE
|
||||
if(params["owner"])
|
||||
var/datum/mind/owner = spawnpoint.params["owner"]
|
||||
soullink(/datum/soullink/oneway, owner.current, created)
|
||||
created.name = created.real_name
|
||||
created.real_name = "[owner.name]'s best friend"
|
||||
if(QDELETED(owner.current) || owner.current.stat == DEAD)
|
||||
addtimer(CALLBACK(created, /mob/proc/dust), 15 SECONDS)
|
||||
else
|
||||
addtimer(CALLBACK(created, /mob/proc/dust), 15 SECONDS)
|
||||
|
||||
/datum/ghostrole/demonic_friend/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
. = ..()
|
||||
if(params["owner"])
|
||||
var/datum/mind/owner = spawnpoint?.params["owner"]
|
||||
to_chat(created, "You have been given a reprieve from your eternity of torment, to be [owner.name]'s friend for their short mortal coil.")
|
||||
to_chat(created, "Be aware that if you do not live up to their expectations, they can send you back to hell with a single thought. [owner.name]'s death will also return you to hell.")
|
||||
if(QDELETED(owner.current) || owner.current.stat == DEAD)
|
||||
to_chat(created, span_danger("Your owner is already dead! You will soon perish."))
|
||||
else
|
||||
GiveCustomObjective(created, "Be [owner.name]'s friend, and keep them alive, so you don't get sent back to hell.")
|
||||
else
|
||||
to_chat(created, span_danger("Your owner is already dead! You will soon perish."))
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/demonic_friend
|
||||
equip_outfit = /datum/outfit/demonic_friend
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/demonic_friend/Equip(client/C, mob/M, list/params)
|
||||
. = ..()
|
||||
var/mob/living/carbon/human/H = .
|
||||
if(!istype(H))
|
||||
return
|
||||
var/obj/item/card/id/ID = H.wear_id?.GetID()
|
||||
if(ID && params["owner"])
|
||||
var/datum/mind/_M = params["owner"]
|
||||
ID.registered_name = "[_M.name]'s best friend"
|
||||
ID.update_label()
|
||||
|
||||
/obj/structure/ghost_role_spawner/demonic_friend
|
||||
name = "Essence of friendship"
|
||||
desc = "Oh boy! Oh boy! A friend!"
|
||||
icon = 'icons/obj/cardboard_cutout.dmi'
|
||||
icon_state = "cutout_basic"
|
||||
role_type = /datum/ghostrole/demonic_friend
|
||||
|
||||
/datum/outfit/demonic_friend
|
||||
name = "Demonic Friend"
|
||||
uniform = /obj/item/clothing/under/misc/assistantformal
|
||||
shoes = /obj/item/clothing/shoes/laceup
|
||||
r_pocket = /obj/item/radio/off
|
||||
back = /obj/item/storage/backpack
|
||||
implants = list(/obj/item/implant/mindshield) //No revolutionaries, he's MY friend.
|
||||
id = /obj/item/card/id
|
||||
access_clone = /datum/job/assistant
|
||||
80
code/modules/ghostroles/roles/fugitives.dm
Normal file
80
code/modules/ghostroles/roles/fugitives.dm
Normal file
@@ -0,0 +1,80 @@
|
||||
/datum/ghostrole/fugitive_hunter
|
||||
name = "Fugitive Hunter"
|
||||
desc = "Independent bounty hunters sent after fugitives"
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/fugitive_hunter
|
||||
|
||||
/datum/ghostrole/fugitive_hunter/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
. = ..()
|
||||
var/datum/antagonist/fugitive_hunter/fughunter = new
|
||||
fughunter.backstory = params["bcakstory"]
|
||||
created.mind.add_antag_datum(fughunter)
|
||||
fughunter.greet()
|
||||
message_admins("[ADMIN_LOOKUPFLW(created)] has been made into a Fugitive Hunter by an event.")
|
||||
log_game("[key_name(created)] was spawned as a Fugitive Hunter by an event.")
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/fugitive_hunter
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/fugitive_hunter/GetOutfit(client/C, mob/M, list/params)
|
||||
switch(params["outfit"])
|
||||
if("spacepol")
|
||||
return new /datum/outfit/spacepol
|
||||
if("russian")
|
||||
return new /datum/outfit/russiancorpse/hunter
|
||||
if("bountyarmor")
|
||||
return new /datum/outfit/bountyarmor
|
||||
if("bountygrapple")
|
||||
return new /datum/outfit/bountygrapple
|
||||
if("bountyhook")
|
||||
return new /datum/outfit/bountyhook
|
||||
if("bountysynth")
|
||||
return new /datum/outfit/bountysynth
|
||||
return ..()
|
||||
|
||||
/obj/structure/ghost_role_spawner/fugitive_hunter
|
||||
role_type = /datum/ghostrole/fugitive_hunter
|
||||
var/backstory
|
||||
var/outfit
|
||||
|
||||
/obj/structure/ghost_role_spawner/fugitive_hunter/Initialize(mapload, params, spawns)
|
||||
return ..(mapload, list(
|
||||
"backstory" = backstory,
|
||||
"outfit" = outfit
|
||||
))
|
||||
|
||||
/obj/structure/ghost_role_spawner/fugitive_hunter/spacepol
|
||||
name = "police pod"
|
||||
desc = "A small sleeper typically used to put people to sleep for briefing on the mission."
|
||||
backstory = "space cop"
|
||||
outfit = "spacepol"
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
icon_state = "sleeper"
|
||||
|
||||
/obj/structure/ghost_role_spawner/fugitive_hunter/russian
|
||||
name = "russian pod"
|
||||
desc = "A small sleeper typically used to make long distance travel a bit more bearable."
|
||||
backstory = "russian"
|
||||
outfit = "russian"
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
icon_state = "sleeper"
|
||||
|
||||
/obj/structure/ghost_role_spawner/fugitive_hunter/bounty
|
||||
name = "bounty hunter pod"
|
||||
desc = "A small sleeper typically used to make long distance travel a bit more bearable."
|
||||
backstory = "bounty hunters"
|
||||
outfit = "bountyaromr"
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
icon_state = "sleeper"
|
||||
|
||||
/obj/structure/ghost_role_spawner/fugitive_hunter/bounty/Destroy()
|
||||
var/obj/structure/fluff/empty_sleeper/S = new(drop_location())
|
||||
S.setDir(dir)
|
||||
return ..()
|
||||
|
||||
/obj/structure/ghost_role_spawner/fugitive_hunter/bounty/armor
|
||||
outfit = "bountyarmor"
|
||||
|
||||
/obj/structure/ghost_role_spawner/fugitive_hunter/bounty/hook
|
||||
outfit = "bountyhook"
|
||||
|
||||
/obj/structure/ghost_role_spawner/fugitive_hunter/bounty/synth
|
||||
outfit = "bountysynth"
|
||||
167
code/modules/ghostroles/roles/ghost_cafe.dm
Normal file
167
code/modules/ghostroles/roles/ghost_cafe.dm
Normal file
@@ -0,0 +1,167 @@
|
||||
|
||||
/datum/ghostrole/ghost_cafe
|
||||
name = "Ghost Cafe Visitor"
|
||||
assigned_role = "Ghost Cafe Visitor"
|
||||
desc = "Off-station area for ghosts to roleplay in."
|
||||
spawntext = "You know one thing for sure. You aren't actually alive! Are you in a simulation?"
|
||||
jobban_role = ROLE_GHOSTCAFE
|
||||
instantiator = /datum/ghostrole_instantiator/human/player_static/ghost_cafe
|
||||
|
||||
/obj/structure/ghost_role_spawner/ghost_cafe
|
||||
name = "Ghost Cafe Sleeper"
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
icon_state = "sleeper"
|
||||
role_type = /datum/ghostrole/ghost_cafe
|
||||
role_spawns = INFINITY
|
||||
|
||||
/datum/action/toggle_dead_chat_mob
|
||||
icon_icon = 'icons/mob/mob.dmi'
|
||||
button_icon_state = "ghost"
|
||||
name = "Toggle deadchat"
|
||||
desc = "Turn off or on your ability to hear ghosts."
|
||||
|
||||
/datum/action/toggle_dead_chat_mob/Trigger()
|
||||
if(!..())
|
||||
return 0
|
||||
var/mob/M = target
|
||||
if(HAS_TRAIT_FROM(M,TRAIT_SIXTHSENSE,GHOSTROLE_TRAIT))
|
||||
REMOVE_TRAIT(M,TRAIT_SIXTHSENSE,GHOSTROLE_TRAIT)
|
||||
to_chat(M,"<span class='notice'>You're no longer hearing deadchat.</span>")
|
||||
else
|
||||
ADD_TRAIT(M,TRAIT_SIXTHSENSE,GHOSTROLE_TRAIT)
|
||||
to_chat(M,"<span class='notice'>You're once again longer hearing deadchat.</span>")
|
||||
|
||||
/datum/action/disguise
|
||||
name = "Disguise"
|
||||
button_icon_state = "ling_transform"
|
||||
icon_icon = 'icons/mob/actions/actions_changeling.dmi'
|
||||
background_icon_state = "bg_mime"
|
||||
var/currently_disguised = FALSE
|
||||
var/static/list/mob_blacklist = typecacheof(list(
|
||||
/mob/living/simple_animal/pet,
|
||||
/mob/living/simple_animal/hostile/retaliate/goose,
|
||||
/mob/living/simple_animal/hostile/poison,
|
||||
/mob/living/simple_animal/hostile/retaliate/goat,
|
||||
/mob/living/simple_animal/cow,
|
||||
/mob/living/simple_animal/chick,
|
||||
/mob/living/simple_animal/chicken,
|
||||
/mob/living/simple_animal/kiwi,
|
||||
/mob/living/simple_animal/babyKiwi,
|
||||
/mob/living/simple_animal/deer,
|
||||
/mob/living/simple_animal/parrot,
|
||||
/mob/living/simple_animal/hostile/lizard,
|
||||
/mob/living/simple_animal/crab,
|
||||
/mob/living/simple_animal/cockroach,
|
||||
/mob/living/simple_animal/butterfly,
|
||||
/mob/living/simple_animal/mouse,
|
||||
/mob/living/simple_animal/sloth,
|
||||
/mob/living/simple_animal/opossum,
|
||||
/mob/living/simple_animal/hostile/bear,
|
||||
/mob/living/simple_animal/hostile/asteroid/polarbear,
|
||||
/mob/living/simple_animal/hostile/asteroid/wolf,
|
||||
/mob/living/carbon/monkey,
|
||||
/mob/living/simple_animal/hostile/gorilla,
|
||||
/mob/living/carbon/alien/larva,
|
||||
/mob/living/simple_animal/hostile/retaliate/frog
|
||||
))
|
||||
|
||||
|
||||
/datum/action/disguise/Trigger()
|
||||
var/mob/living/carbon/human/H = owner
|
||||
if(!currently_disguised)
|
||||
var/user_object_type = input(H, "Disguising as OBJECT or MOB?") as null|anything in list("OBJECT", "MOB")
|
||||
if(user_object_type)
|
||||
var/search_term = stripped_input(H, "Enter the search term")
|
||||
if(search_term)
|
||||
var/list_to_search
|
||||
if(user_object_type == "MOB")
|
||||
list_to_search = subtypesof(/mob) - mob_blacklist
|
||||
else
|
||||
list_to_search = subtypesof(/obj)
|
||||
var/list/filtered_results = list()
|
||||
for(var/some_search_item in list_to_search)
|
||||
if(findtext("[some_search_item]", search_term))
|
||||
filtered_results += some_search_item
|
||||
if(!length(filtered_results))
|
||||
to_chat(H, "Nothing matched your search query!")
|
||||
else
|
||||
var/disguise_selection = input("Select item to disguise as") as null|anything in filtered_results
|
||||
if(disguise_selection)
|
||||
var/atom/disguise_item = disguise_selection
|
||||
var/image/I = image(icon = initial(disguise_item.icon), icon_state = initial(disguise_item.icon_state), loc = H)
|
||||
I.override = TRUE
|
||||
I.layer = ABOVE_MOB_LAYER
|
||||
H.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, "ghost_cafe_disguise", I)
|
||||
currently_disguised = TRUE
|
||||
else
|
||||
H.remove_alt_appearance("ghost_cafe_disguise")
|
||||
currently_disguised = FALSE
|
||||
|
||||
/datum/ghostrole/ghost_cafe/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
. = ..()
|
||||
to_chat(created,"<span class='boldwarning'>Ghosting is free!</span>")
|
||||
|
||||
/datum/ghostrole_instantiator/human/player_static/ghost_cafe
|
||||
equip_outfit = /datum/outfit/ghostcafe
|
||||
mob_traits = list(
|
||||
TRAIT_SIXTHSENSE,
|
||||
TRAIT_EXEMPT_HEALTH_EVENTS,
|
||||
TRAIT_NO_MIDROUND_ANTAG
|
||||
)
|
||||
|
||||
/datum/ghostrole_instantiator/human/player_static/ghost_cafe/Create(client/C, atom/location, list/params)
|
||||
. = ..()
|
||||
var/mob/living/carbon/human/H = .
|
||||
H.AddElement(/datum/element/ghost_role_eligibility, free_ghosting = TRUE)
|
||||
H.AddElement(/datum/element/dusts_on_catatonia)
|
||||
var/area/A = get_area(H)
|
||||
H.AddElement(/datum/element/dusts_on_leaving_area,list(A.type,/area/hilbertshotel))
|
||||
var/datum/action/toggle_dead_chat_mob/D = new(H)
|
||||
D.Grant(H)
|
||||
var/datum/action/disguise/disguise_action = new(H)
|
||||
disguise_action.Grant(H)
|
||||
|
||||
/datum/outfit/ghostcafe
|
||||
name = "ID, jumpsuit and shoes"
|
||||
uniform = /obj/item/clothing/under/color/random
|
||||
shoes = /obj/item/clothing/shoes/sneakers/black
|
||||
id = /obj/item/card/id/no_banking
|
||||
r_hand = /obj/item/storage/box/syndie_kit/chameleon/ghostcafe
|
||||
|
||||
|
||||
/datum/outfit/ghostcafe/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
..()
|
||||
if (isplasmaman(H))
|
||||
head = /obj/item/clothing/head/helmet/space/plasmaman
|
||||
uniform = /obj/item/clothing/under/plasmaman
|
||||
l_hand= /obj/item/tank/internals/plasmaman/belt/full
|
||||
mask = /obj/item/clothing/mask/breath
|
||||
return
|
||||
|
||||
var/suited = !preference_source || preference_source.prefs.jumpsuit_style == PREF_SUIT
|
||||
if (CONFIG_GET(flag/grey_assistants))
|
||||
uniform = suited ? /obj/item/clothing/under/color/grey : /obj/item/clothing/under/color/jumpskirt/grey
|
||||
else
|
||||
if(SSevents.holidays && SSevents.holidays[PRIDE_MONTH])
|
||||
uniform = suited ? /obj/item/clothing/under/color/rainbow : /obj/item/clothing/under/color/jumpskirt/rainbow
|
||||
else
|
||||
uniform = suited ? /obj/item/clothing/under/color/random : /obj/item/clothing/under/color/jumpskirt/random
|
||||
|
||||
/datum/outfit/ghostcafe/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
H.internal = H.get_item_for_held_index(1)
|
||||
H.update_internals_hud_icon(1)
|
||||
|
||||
/obj/item/storage/box/syndie_kit/chameleon/ghostcafe
|
||||
name = "ghost cafe costuming kit"
|
||||
desc = "Look just the way you did in life - or better!"
|
||||
|
||||
/obj/item/storage/box/syndie_kit/chameleon/ghostcafe/PopulateContents() // Doesn't contain a PDA, for isolation reasons.
|
||||
new /obj/item/clothing/under/chameleon(src)
|
||||
new /obj/item/clothing/suit/chameleon(src)
|
||||
new /obj/item/clothing/gloves/chameleon(src)
|
||||
new /obj/item/clothing/shoes/chameleon(src)
|
||||
new /obj/item/clothing/glasses/chameleon(src)
|
||||
new /obj/item/clothing/head/chameleon(src)
|
||||
new /obj/item/clothing/mask/chameleon(src)
|
||||
new /obj/item/storage/backpack/chameleon(src)
|
||||
new /obj/item/clothing/neck/cloak/chameleon(src)
|
||||
123
code/modules/ghostroles/roles/golem.dm
Normal file
123
code/modules/ghostroles/roles/golem.dm
Normal file
@@ -0,0 +1,123 @@
|
||||
/datum/ghostrole/golem
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/species/golem
|
||||
|
||||
/datum/ghostrole/golem/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
. = ..()
|
||||
var/mob/living/carbon/human/H = created
|
||||
if(!istype(H))
|
||||
return
|
||||
var/datum/species/golem/G = H.dna.species
|
||||
if(!istype(G))
|
||||
return
|
||||
to_chat(created, G.info_text)
|
||||
|
||||
/datum/ghostrole/golem/free
|
||||
name = "Free Golem"
|
||||
desc = "You are a Free Golem. Your family worships The Liberator."
|
||||
spawntext = "In his infinite and divine wisdom, he set your clan free to \
|
||||
travel the stars with a single declaration: \"Yeah go do whatever.\" Though you are bound to the one who created you, it is customary in your society to repeat those same words to newborn \
|
||||
golems, so that no golem may ever be forced to serve again."
|
||||
|
||||
/datum/ghostrole/golem/free/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
. = ..()
|
||||
to_chat(created, span_boldwarning("Build golem shells in the autolathe, and feed refined mineral sheets to the shells to bring them to life! You are generally a peaceful group unless provoked."))
|
||||
|
||||
/datum/ghostrole/golem/servant
|
||||
name = "Servant Golem"
|
||||
desc = "You are a golem."
|
||||
spawntext = "You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools."
|
||||
inject_params = list(
|
||||
"servant" = TRUE
|
||||
)
|
||||
|
||||
/datum/ghostrole/golem/servant/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
. = ..()
|
||||
var/datum/mind/creator_mind = params["creator"]
|
||||
if(!creator_mind)
|
||||
return
|
||||
var/creator_name = creator_mind.name
|
||||
log_game("[key_name(created)] possessed a golem shell enslaved to [creator_mind.name]/[creator_mind.key].")
|
||||
log_admin("[key_name(created)] possessed a golem shell enslaved to [creator_mind.name]/[creator_mind.key].")
|
||||
created.mind.store_memory( "Serve [creator_name][creator_mind.current && " (currently [creator_mind.current.name])"], and assist them in completing their goals at any cost.")
|
||||
to_chat(created, span_boldwarning("Serve [creator_name][creator_mind.current && " (currently [creator_mind.current.name])"], and assist them in completing their goals at any cost."))
|
||||
var/mob/living/carbon/human/H = created
|
||||
var/datum/species/golem/G = H.dna.species
|
||||
G.owner = creator_mind
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/golem
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/golem/GetSpeciesPath(mob/living/carbon/human/H, list/params)
|
||||
var/predestined = params["species"]
|
||||
if(istext(predestined))
|
||||
predestined = text2path(predestined)
|
||||
if(!ispath(predestined, /datum/species/golem))
|
||||
return pick(typesof(/datum/species/golem))
|
||||
return predestined
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/golem/Randomize(mob/living/carbon/human/H, list/params)
|
||||
. = ..()
|
||||
H.set_cloned_appearance()
|
||||
var/datum/species/golem/G = H.dna.species
|
||||
H.real_name = params["name"] || (params["servant"]? "[initial(G.prefix)] Golem ([rand(1,999)])" : H.dna.species.random_name())
|
||||
|
||||
//Golem shells: Spawns in Free Golem ships in lavaland. Ghosts become mineral golems and are advised to spread personal freedom.
|
||||
/obj/structure/ghost_role_spawner/golem
|
||||
name = "inert free golem shell"
|
||||
desc = "A humanoid shape, empty, lifeless, and full of potential."
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "construct"
|
||||
anchored = FALSE
|
||||
move_resist = MOVE_FORCE_NORMAL
|
||||
density = FALSE
|
||||
/// can we possibly have an owner
|
||||
var/has_owner = FALSE
|
||||
/// can golems switch bodies to this shell
|
||||
var/can_transfer = TRUE
|
||||
/// override golem species?
|
||||
var/golem_species_override
|
||||
|
||||
/obj/structure/ghost_role_spawner/golem/Initialize(mapload, datum/species/golem/species, mob/creator)
|
||||
if(golem_species_override)
|
||||
species = golem_species_override
|
||||
if(species) //spawners list uses object name to register so this goes before ..()
|
||||
name += " ([initial(species.prefix)])"
|
||||
golem_species_override = species // yes semi-circular-reference sue me
|
||||
return ..(mapload, list(
|
||||
"species" = species,
|
||||
"creator" = creator && (istype(creator, /datum/mind)? creator : creator.mind)
|
||||
), (has_owner && creator)? /datum/ghostrole/golem/servant : /datum/ghostrole/golem/free)
|
||||
|
||||
/obj/structure/ghost_role_spawner/golem/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
if(isgolem(user) && can_transfer)
|
||||
// this is a bit special
|
||||
// we want them to keep their mind, so....
|
||||
var/datum/ghostrole/G = get_ghostrole_datum(/datum/ghostrole/golem/free)
|
||||
if(!G?.instantiator)
|
||||
CRASH("Couldn't locate freegolem instantiator")
|
||||
var/datum/ghostrole_instantiator/I = G.instantiator
|
||||
var/transfer_choice = alert("Transfer your soul to [src]? (Warning, your old body will die!)",,"Yes","No")
|
||||
if(transfer_choice != "Yes" || QDELETED(src) || !user.canUseTopic(src, BE_CLOSE, NO_DEXTERY, NO_TK))
|
||||
return
|
||||
log_game("[key_name(user)] golem-swapped into [src]")
|
||||
user.visible_message("<span class='notice'>A faint light leaves [user], moving to [src] and animating it!</span>","<span class='notice'>You leave your old body behind, and transfer into [src]!</span>")
|
||||
var/mob/living/created = I.Run(user.client, loc, list(
|
||||
"species" = golem_species_override,
|
||||
"name" = user.real_name
|
||||
))
|
||||
if(!created)
|
||||
CRASH("Couldn't make a valid golem, cancelling.")
|
||||
user.mind.transfer_to(created)
|
||||
user.death()
|
||||
qdel(src)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/structure/ghost_role_spawner/golem/servant
|
||||
has_owner = TRUE
|
||||
name = "inert servant golem shell"
|
||||
|
||||
/obj/structure/ghost_role_spawner/golem/adamantine
|
||||
name = "dust-caked free golem shell"
|
||||
desc = "A humanoid shape, empty, lifeless, and full of potential."
|
||||
can_transfer = FALSE
|
||||
golem_species_override = /datum/species/golem/adamantine
|
||||
82
code/modules/ghostroles/roles/hermit.dm
Normal file
82
code/modules/ghostroles/roles/hermit.dm
Normal file
@@ -0,0 +1,82 @@
|
||||
/datum/ghostrole/hermit
|
||||
name = "Space Hermit"
|
||||
assigned_role = "Hermit"
|
||||
desc = "A stranded cryo-occupant in deep space."
|
||||
spawntext = "You've been late to awaken from your cryo slumber. Blasted machine, you set it to 10 days not 10 weeks!</span><b> Where have the others gone while we were out? Did they manage to survive?"
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/hermit
|
||||
|
||||
/datum/ghostrole/hermit/Instantiate(client/C, atom/loc, list/params)
|
||||
var/rp = rand(1, 4)
|
||||
switch(rp)
|
||||
if(1)
|
||||
params["fluff"] = "proper"
|
||||
if(2)
|
||||
params["fluff"] = "tiger"
|
||||
if(3)
|
||||
params["fluff"] = "exile"
|
||||
if(4)
|
||||
params["fluff"] = "tourist"
|
||||
return ..()
|
||||
|
||||
/datum/ghostrole/hermit/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
. = ..()
|
||||
var/flavour_text = "Each day you barely scrape by, and between the terrible conditions of your makeshift shelter, \
|
||||
the hostile creatures, and the ash drakes swooping down from the cloudless skies, all you can wish for is the feel of soft grass between your toes and \
|
||||
the fresh air of Earth. These thoughts are dispelled by yet another recollection of how you got here... "
|
||||
switch(params["fluff"])
|
||||
if("proper")
|
||||
flavour_text += "you were a [pick("arms dealer", "shipwright", "docking manager")]'s assistant on a small trading station several sectors from here. Raiders attacked, and there was \
|
||||
only one pod left when you got to the escape bay. You took it and launched it alone, and the crowd of terrified faces crowding at the airlock door as your pod's engines burst to \
|
||||
life and sent you to this hell are forever branded into your memory."
|
||||
if("tiger")
|
||||
flavour_text += "you're an exile from the Tiger Cooperative. Their technological fanaticism drove you to question the power and beliefs of the Exolitics, and they saw you as a \
|
||||
heretic and subjected you to hours of horrible torture. You were hours away from execution when a high-ranking friend of yours in the Cooperative managed to secure you a pod, \
|
||||
scrambled its destination's coordinates, and launched it. You awoke from stasis when you landed and have been surviving - barely - ever since."
|
||||
if("exile")
|
||||
flavour_text += "you were a doctor on one of Nanotrasen's space stations, but you left behind that damn corporation's tyranny and everything it stood for. From a metaphorical hell \
|
||||
to a literal one, you find yourself nonetheless missing the recycled air and warm floors of what you left behind... but you'd still rather be here than there."
|
||||
if("tourist")
|
||||
flavour_text += "you were always joked about by your friends for \"not playing with a full deck\", as they so <i>kindly</i> put it. It seems that they were right when you, on a tour \
|
||||
at one of Nanotrasen's state-of-the-art research facilities, were in one of the escape pods alone and saw the red button. It was big and shiny, and it caught your eye. You pressed \
|
||||
it, and after a terrifying and fast ride for days, you landed here. You've had time to wisen up since then, and you think that your old friends wouldn't be laughing now."
|
||||
to_chat(created, flavour_text)
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/hermit
|
||||
// mob_traits = list(
|
||||
// TRAIT_EXEMPT_HEALTH_EVENTS
|
||||
// )
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/hermit/GetOutfit(client/C, mob/M, list/params)
|
||||
var/datum/outfit/outfit = ..()
|
||||
switch(params["fluff"])
|
||||
if("proper")
|
||||
outfit.uniform = /obj/item/clothing/under/assistantformal
|
||||
outfit.shoes = /obj/item/clothing/shoes/black
|
||||
outfit.back = /obj/item/storage/backpack
|
||||
if("tiger")
|
||||
outfit.uniform = /obj/item/clothing/under/color/prison
|
||||
outfit.shoes = /obj/item/clothing/shoes/orange
|
||||
outfit.back = /obj/item/storage/backpack
|
||||
if("exile")
|
||||
outfit.uniform = /obj/item/clothing/under/rank/medical
|
||||
outfit.suit = /obj/item/clothing/suit/toggle/labcoat/paramedic
|
||||
outfit.back = /obj/item/storage/backpack/medic
|
||||
outfit.shoes = /obj/item/clothing/shoes/black
|
||||
if("tourist")
|
||||
outfit.uniform = /obj/item/clothing/under/color/grey
|
||||
outfit.shoes = /obj/item/clothing/shoes/black
|
||||
outfit.back = /obj/item/storage/backpack
|
||||
return outfit
|
||||
|
||||
//Malfunctioning cryostasis sleepers: Spawns in makeshift shelters in lavaland. Ghosts become hermits with knowledge of how they got to where they are now.
|
||||
/obj/structure/ghost_role_spawner/hermit
|
||||
name = "malfunctioning cryostasis sleeper"
|
||||
desc = "A humming sleeper with a silhouetted occupant inside. Its stasis function is broken and it's likely being used as a bed."
|
||||
icon = 'icons/obj/spawners.dmi'
|
||||
icon_state = "cryostasis_sleeper"
|
||||
role_type = /datum/ghostrole/hermit
|
||||
qdel_on_deplete = TRUE
|
||||
|
||||
/obj/structure/ghost_role_spawner/hermit/Destroy()
|
||||
// new /obj/structure/fluff/empty_cryostasis_sleeper(get_turf(src))
|
||||
return ..()
|
||||
56
code/modules/ghostroles/roles/hotel.dm
Normal file
56
code/modules/ghostroles/roles/hotel.dm
Normal file
@@ -0,0 +1,56 @@
|
||||
/datum/ghostrole/space_hotel
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/space_hotel
|
||||
name = "Space Hotel Staff"
|
||||
desc = "You are a staff member of a top-of-the-line space hotel! Cater to guests and make sure the manager doesn't fire you."
|
||||
automatic_objective = "You are a staff member of a top-of-the-line space hotel! Cater to guests and make sure the manager doesn't fire you."
|
||||
assigned_role = "Hotel Staff"
|
||||
|
||||
/datum/ghostrole/space_hotel/security
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/space_hotel/security
|
||||
name = "Space Hotel Security"
|
||||
desc = "You have been assigned to this hotel to protect the interests of the company while keeping the peace between \
|
||||
guests and the staff."
|
||||
automatic_objective = "Do not leave your assigned hotel. Try and keep the peace between staff and guests, non-lethal force heavily advised if possible."
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/space_hotel
|
||||
equip_outfit = /datum/outfit/hotelstaff
|
||||
mob_traits = list(
|
||||
TRAIT_EXEMPT_HEALTH_EVENTS
|
||||
)
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/space_hotel/security
|
||||
equip_outfit = /datum/outfit/hotelstaff/security
|
||||
|
||||
//Space Hotel Staff
|
||||
/obj/structure/ghost_role_spawner/space_hotel //not free antag u little shits
|
||||
name = "staff sleeper"
|
||||
desc = "A sleeper designed for long-term stasis between guest visits."
|
||||
role_type = /datum/ghostrole/space_hotel
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
icon_state = "sleeper_s"
|
||||
|
||||
/obj/structure/ghost_role_spawner/space_hotel/Destroy()
|
||||
new/obj/structure/fluff/empty_sleeper/syndicate(get_turf(src))
|
||||
return ..()
|
||||
|
||||
/obj/structure/ghost_role_spawner/space_hotel/security
|
||||
name = "hotel security sleeper"
|
||||
role_type = /datum/ghostrole/space_hotel/security
|
||||
|
||||
/datum/outfit/hotelstaff
|
||||
name = "Hotel Staff"
|
||||
uniform = /obj/item/clothing/under/suit/telegram
|
||||
shoes = /obj/item/clothing/shoes/laceup
|
||||
head = /obj/item/clothing/head/hotel
|
||||
r_pocket = /obj/item/radio/off
|
||||
back = /obj/item/storage/backpack
|
||||
implants = list(/obj/item/implant/mindshield)
|
||||
|
||||
/datum/outfit/hotelstaff/security
|
||||
name = "Hotel Secuirty"
|
||||
uniform = /obj/item/clothing/under/rank/security/officer/blueshirt
|
||||
shoes = /obj/item/clothing/shoes/jackboots
|
||||
suit = /obj/item/clothing/suit/armor/vest/blueshirt
|
||||
head = /obj/item/clothing/head/helmet/blueshirt
|
||||
back = /obj/item/storage/backpack/security
|
||||
belt = /obj/item/storage/belt/security/full
|
||||
38
code/modules/ghostroles/roles/lifebringer.dm
Normal file
38
code/modules/ghostroles/roles/lifebringer.dm
Normal file
@@ -0,0 +1,38 @@
|
||||
/datum/ghostrole/seed_vault
|
||||
name = "Lifebringer"
|
||||
desc = "You are a sentient ecosystem, an example of the mastery over life that your creators possessed."
|
||||
spawntext = "Your masters, benevolent as they were, created uncounted seed vaults and spread them across \
|
||||
the universe to every planet they could chart. You are in one such seed vault. \
|
||||
Your goal is to cultivate and spread life wherever it will go while waiting for contact from your creators. \
|
||||
Estimated time of last contact: Deployment, 5000 millennia ago."
|
||||
assigned_role = "Lifebringer"
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/seed_vault
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/seed_vault
|
||||
mob_traits = list(
|
||||
TRAIT_EXEMPT_HEALTH_EVENTS
|
||||
)
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/seed_vault/Randomize(mob/living/carbon/human/H, list/params)
|
||||
. = ..()
|
||||
H.set_species(new /datum/species/pod)
|
||||
var/plant_name = pick("Tomato", "Potato", "Broccoli", "Carrot", "Ambrosia", "Pumpkin", "Ivy", "Kudzu", "Banana", "Moss", "Flower", "Bloom", "Root", "Bark", "Glowshroom", "Petal", "Leaf", \
|
||||
"Venus", "Sprout","Cocoa", "Strawberry", "Citrus", "Oak", "Cactus", "Pepper", "Juniper")
|
||||
H.real_name = plant_name //why this works when moving it from one function to another is beyond me
|
||||
H.underwear = "Nude" //You're a plant, partner
|
||||
H.undershirt = "Nude" //changing underwear/shirt/socks doesn't seem to function correctly right now because of some bug elsewhere?
|
||||
H.socks = "Nude"
|
||||
H.update_body(TRUE)
|
||||
H.language_holder.selected_language = /datum/language/sylvan
|
||||
|
||||
//Preserved terrarium/seed vault: Spawns in seed vault structures in lavaland. Ghosts become plantpeople and are advised to begin growing plants in the room near them.
|
||||
/obj/structure/ghost_role_spawner/seed_vault
|
||||
name = "preserved terrarium"
|
||||
desc = "An ancient machine that seems to be used for storing plant matter. The glass is obstructed by a mat of vines."
|
||||
icon = 'icons/obj/lavaland/spawners.dmi'
|
||||
icon_state = "terrarium"
|
||||
role_type = /datum/ghostrole/seed_vault
|
||||
|
||||
/obj/structure/ghost_role_spawner/seed_vault/Destroy()
|
||||
new/obj/structure/fluff/empty_terrarium(get_turf(src))
|
||||
return ..()
|
||||
61
code/modules/ghostroles/roles/oldresearch.dm
Normal file
61
code/modules/ghostroles/roles/oldresearch.dm
Normal file
@@ -0,0 +1,61 @@
|
||||
/datum/ghostrole/old_research
|
||||
name = "Oldstation Crew"
|
||||
allow_pick_spawner = TRUE
|
||||
desc = "You were a Nanotrasen employee from an era past, stationed upon a state of the art research station. \
|
||||
You vaguely recall rushing into a cryogenics pod due to an oncoming radiation storm. \
|
||||
The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \
|
||||
your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod."
|
||||
automatic_objective = "Work as a team with your fellow survivors and do not abandon them."
|
||||
assigned_role = "Ancient Crew"
|
||||
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/old_research
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/old_research/GetOutfit(client/C, mob/M, list/params)
|
||||
. = ..()
|
||||
switch(params["role"])
|
||||
if("Officer")
|
||||
return /datum/outfit/old_research/security
|
||||
if("Engineer")
|
||||
return /datum/outfit/old_research/engineer
|
||||
if("Scientist")
|
||||
return /datum/outfit/old_research/scientist
|
||||
|
||||
/obj/structure/ghost_role_spawner/old_research
|
||||
role_type = /datum/ghostrole/old_research
|
||||
name = "old cryogenics pod"
|
||||
desc = "A humming cryo pod. You can barely recognise a security uniform underneath the built up ice. The machine is attempting to wake up its occupant."
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
icon_state = "sleeper"
|
||||
|
||||
/obj/structure/ghost_role_spawner/old_research/Destroy()
|
||||
new /obj/structure/showcase/machinery/oldpod/used(drop_location())
|
||||
return ..()
|
||||
|
||||
/obj/structure/ghost_role_spawner/old_research/security
|
||||
role_params = list("role" = "Officer")
|
||||
|
||||
/obj/structure/ghost_role_spawner/old_research/engineer
|
||||
role_params = list("role" = "Engineer")
|
||||
|
||||
/obj/structure/ghost_role_spawner/old_research/scientist
|
||||
role_params = list("role" = "Scientist")
|
||||
|
||||
/datum/outfit/old_research/security
|
||||
uniform = /obj/item/clothing/under/rank/security/officer
|
||||
shoes = /obj/item/clothing/shoes/jackboots
|
||||
id = /obj/item/card/id/away/old/sec
|
||||
r_pocket = /obj/item/restraints/handcuffs
|
||||
l_pocket = /obj/item/assembly/flash/handheld
|
||||
|
||||
/datum/outfit/old_research/engineer
|
||||
uniform = /obj/item/clothing/under/rank/engineering/engineer
|
||||
shoes = /obj/item/clothing/shoes/workboots
|
||||
id = /obj/item/card/id/away/old/eng
|
||||
gloves = /obj/item/clothing/gloves/color/fyellow/old
|
||||
l_pocket = /obj/item/tank/internals/emergency_oxygen
|
||||
|
||||
/datum/outfit/old_research/scientist
|
||||
uniform = /obj/item/clothing/under/rank/rnd/scientist
|
||||
shoes = /obj/item/clothing/shoes/laceup
|
||||
id = /obj/item/card/id/away/old/sci
|
||||
l_pocket = /obj/item/stack/medical/suture
|
||||
103
code/modules/ghostroles/roles/pirate.dm
Normal file
103
code/modules/ghostroles/roles/pirate.dm
Normal file
@@ -0,0 +1,103 @@
|
||||
/datum/ghostrole/pirate
|
||||
name = "Space Pirate"
|
||||
desc = "The station refused to pay for your protection, protect the ship, siphon the credits from the station and raid it for even more loot."
|
||||
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/species/pirate
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/pirate
|
||||
possible_species = list(
|
||||
/datum/species/skeleton/space
|
||||
)
|
||||
equip_outfit = /datum/outfit/pirate/space
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/pirate/GetOutfit(client/C, mob/M, list/params)
|
||||
. = ..()
|
||||
switch(params["rank"])
|
||||
if("Mate", "Gunner")
|
||||
return /datum/outfit/pirate/space
|
||||
if("Captain")
|
||||
return /datum/outfit/pirate/space/captain
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/pirate/Randomize(mob/living/carbon/human/H, list/params)
|
||||
. = ..()
|
||||
H.fully_replace_character_name(H.real_name,generate_pirate_name(params["rank"]))
|
||||
|
||||
/datum/ghostrole/pirate/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
. = ..()
|
||||
created.mind.add_antag_datum(/datum/antagonist/pirate)
|
||||
|
||||
/proc/generate_pirate_name(rank)
|
||||
var/beggings = strings(PIRATE_NAMES_FILE, "beginnings")
|
||||
var/endings = strings(PIRATE_NAMES_FILE, "endings")
|
||||
return "[rank] [pick(beggings)][pick(endings)]"
|
||||
|
||||
/obj/structure/ghost_role_spawner/pirate
|
||||
name = "space pirate sleeper"
|
||||
desc = "A cryo sleeper smelling faintly of rum. The sleeper looks unstable. <i>Perhaps the pirate within can be killed with the right tools...</i>"
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
icon_state = "sleeper"
|
||||
role_type = /datum/ghostrole/pirate
|
||||
role_params = list(
|
||||
"rank" = "Mate"
|
||||
)
|
||||
|
||||
/obj/structure/ghost_role_spawner/pirate/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(user.mind.has_antag_datum(/datum/antagonist/pirate))
|
||||
to_chat(user, "<span class='notice'>Your shipmate sails within their dreams for now. Perhaps they may wake up eventually.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>If you want to kill the pirate off, something to pry open the sleeper might be the best way to do it.</span>")
|
||||
|
||||
/obj/structure/ghost_role_spawner/pirate/attackby(obj/item/W, mob/user, params)
|
||||
if(W.tool_behaviour == TOOL_CROWBAR && user.a_intent != INTENT_HARM)
|
||||
if(user.mind.has_antag_datum(/datum/antagonist/pirate))
|
||||
to_chat(user,"<span class='warning'>Why would you want to do that to your shipmate? That'd kill them.</span>")
|
||||
return
|
||||
user.visible_message("<span class='warning'>[user] start to pry open [src]...</span>",
|
||||
"<span class='notice'>You start to pry open [src]...</span>",
|
||||
"<span class='italics'>You hear prying...</span>")
|
||||
W.play_tool_sound(src)
|
||||
if(do_after(user, 100*W.toolspeed, target = src))
|
||||
user.visible_message("<span class='warning'>[user] pries open [src], disrupting the sleep of the pirate within and killing them.</span>",
|
||||
"<span class='notice'>You pry open [src], disrupting the sleep of the pirate within and killing them.</span>",
|
||||
"<span class='italics'>You hear prying, followed by the death rattling of bones.</span>")
|
||||
log_game("[key_name(user)] has successfully pried open [src] and disabled a space pirate spawner.")
|
||||
W.play_tool_sound(src)
|
||||
playsound(src.loc, 'modular_citadel/sound/voice/scream_skeleton.ogg', 50, 1, 4, 1.2)
|
||||
if(role_params["rank"] == "Captain")
|
||||
new /obj/effect/mob_spawn/human/pirate/corpse/captain(get_turf(src))
|
||||
else
|
||||
new /obj/effect/mob_spawn/human/pirate/corpse(get_turf(src))
|
||||
qdel(src)
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/effect/mob_spawn/human/pirate
|
||||
mob_species = /datum/species/skeleton/space
|
||||
outfit = /datum/outfit/pirate/space
|
||||
|
||||
/obj/effect/mob_spawn/human/pirate/corpse //occurs when someone pries a pirate out of their sleeper.
|
||||
mob_name = "Dead Space Pirate"
|
||||
death = TRUE
|
||||
instant = TRUE
|
||||
random = FALSE
|
||||
|
||||
/obj/effect/mob_spawn/human/pirate/corpse/captain
|
||||
mob_name = "Dead Space Pirate Captain"
|
||||
outfit = /datum/outfit/pirate/space/captain
|
||||
|
||||
/obj/structure/ghost_role_spawner/pirate/Destroy()
|
||||
new /obj/structure/showcase/machinery/oldpod/used(drop_location())
|
||||
return ..()
|
||||
|
||||
/obj/structure/ghost_role_spawner/pirate/captain
|
||||
role_params = list(
|
||||
"rank" = "Captain"
|
||||
)
|
||||
|
||||
/obj/structure/ghost_role_spawner/pirate/gunner
|
||||
role_params = list(
|
||||
"rank" = "Gunner"
|
||||
)
|
||||
39
code/modules/ghostroles/roles/prisoner.dm
Normal file
39
code/modules/ghostroles/roles/prisoner.dm
Normal file
@@ -0,0 +1,39 @@
|
||||
/datum/ghostrole/lavaland_prisoner
|
||||
name = "Lavaland Prisoner"
|
||||
desc = "You're a prisoner, sentenced to hard work in one of Nanotrasen's labor camps, but it seems as though fate has other plans for you."
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/lavaland_prisoner
|
||||
assigned_role = "Escaped Prisoner"
|
||||
|
||||
/datum/ghostrole/lavaland_prisoner/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint)
|
||||
. = ..()
|
||||
var/list/crimes = list("murder", "larceny", "embezzlement", "unionization", "dereliction of duty", "kidnapping", "gross incompetence", "grand theft", "collaboration with the Syndicate", \
|
||||
"worship of a forbidden deity", "interspecies relations", "mutiny")
|
||||
to_chat(created, span_danger("Good. It seems as though your ship crashed. You remember that you were convicted of [pick(crimes)]. but regardless of that, it seems like your crime doesn't matter now. You don't know where you are, but you know that it's out to kill you, and you're not going \
|
||||
to lose this opportunity. Find a way to get out of this mess and back to where you rightfully belong - your [pick("house", "apartment", "spaceship", "station")]."))
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/lavaland_prisoner
|
||||
equip_outfit = /datum/outfit/lavalandprisoner
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/lavaland_prisoner/Randomize(mob/living/carbon/human/H, list/params)
|
||||
. = ..()
|
||||
H.real_name = "NTP #LL-0[rand(111,999)]" //Nanotrasen Prisoner #Lavaland-(numbers)
|
||||
H.name = H.real_name
|
||||
|
||||
//Prisoner containment sleeper: Spawns in crashed prison ships in lavaland. Ghosts become escaped prisoners and are advised to find a way out of the mess they've gotten themselves into.
|
||||
/obj/structure/ghost_role_spawner/prisoner_transport
|
||||
name = "prisoner containment sleeper"
|
||||
desc = "A sleeper designed to put its occupant into a deep coma, unbreakable until the sleeper turns off. This one's glass is cracked and you can see a pale, sleeping face staring out."
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
icon_state = "sleeper_s"
|
||||
role_type = /datum/ghostrole/lavaland_prisoner
|
||||
|
||||
/datum/outfit/lavalandprisoner
|
||||
name = "Lavaland Prisoner"
|
||||
uniform = /obj/item/clothing/under/rank/prisoner
|
||||
mask = /obj/item/clothing/mask/breath
|
||||
shoes = /obj/item/clothing/shoes/sneakers/orange
|
||||
r_pocket = /obj/item/tank/internals/emergency_oxygen
|
||||
|
||||
/obj/structure/ghost_role_spawner/prisoner_transport/Destroy()
|
||||
new/obj/structure/fluff/empty_sleeper/syndicate(get_turf(src))
|
||||
return ..()
|
||||
40
code/modules/ghostroles/roles/timeless_prison.dm
Normal file
40
code/modules/ghostroles/roles/timeless_prison.dm
Normal file
@@ -0,0 +1,40 @@
|
||||
/datum/ghostrole/timeless_prison
|
||||
name = "Timeless Prisoner"
|
||||
desc = "Years ago, you sacrificed the lives of your trusted friends and the humanity of yourself to reach the Wish Granter. Though you \
|
||||
did so, it has come at a cost: your very body rejects the light, dooming you to wander endlessly in this horrible wasteland."
|
||||
instantiator = /datum/ghostrole_instantiator/human/random/species/shadow
|
||||
assigned_role = "Exile"
|
||||
|
||||
/datum/ghostrole/timeless_prison/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
|
||||
. = ..()
|
||||
var/wish = rand(1,4)
|
||||
switch(wish)
|
||||
if(1)
|
||||
to_chat(created, "<b>You wished to kill, and kill you did. You've lost track of how many, but the spark of excitement that murder once held has winked out. You feel only regret.</b>")
|
||||
if(2)
|
||||
to_chat(created, "<b>You wished for unending wealth, but no amount of money was worth this existence. Maybe charity might redeem your soul?</b>")
|
||||
if(3)
|
||||
to_chat(created, "<b>You wished for power. Little good it did you, cast out of the light. You are the [created.gender == MALE ? "king" : "queen"] of a hell that holds no subjects. You feel only remorse.</b>")
|
||||
if(4)
|
||||
to_chat(created, "<b>You wished for immortality, even as your friends lay dying behind you. No matter how many times you cast yourself into the lava, you awaken in this room again within a few days. There is no escape.</b>")
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/shadow
|
||||
possible_species = list(
|
||||
/datum/species/shadow
|
||||
)
|
||||
|
||||
/datum/ghostrole_instantiator/human/random/species/shadow/timeless_prison/Randomize(mob/living/carbon/human/H, list/params)
|
||||
. = ..()
|
||||
H.real_name = "Wish Granter's Victim ([rand(1,999)])"
|
||||
|
||||
//Timeless prisons: Spawns in Wish Granter prisons in lavaland. Ghosts become age-old users of the Wish Granter and are advised to seek repentance for their past.
|
||||
/obj/structure/ghost_role_spawner/exile
|
||||
name = "timeless prison"
|
||||
desc = "Although this stasis pod looks medicinal, it seems as though it's meant to preserve something for a very long time."
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
icon_state = "sleeper"
|
||||
role_type = /datum/ghostrole/timeless_prison
|
||||
|
||||
/obj/structure/ghost_role_spawner/exile/Destroy()
|
||||
new/obj/structure/fluff/empty_sleeper(get_turf(src))
|
||||
return ..()
|
||||
18
code/modules/ghostroles/roles/vet.dm
Normal file
18
code/modules/ghostroles/roles/vet.dm
Normal file
@@ -0,0 +1,18 @@
|
||||
/datum/ghostrole/lavaland_vet
|
||||
name = "Lavaland Vet"
|
||||
desc = "You are a animal doctor who just woke up in lavaland"
|
||||
assigned_role = "Translocated Vet"
|
||||
spawntext = "What...? Where are you? Where are the others? This is still the animal hospital - you should know, you've been an intern here for weeks - but \
|
||||
you see them right now. So where is \
|
||||
everyone? Where did they go? What happened to the hospital? And is that <i>smoke</i> you smell? You need to find someone else. Maybe they c everyone's gone. One of the cats scratched you just a few minutes ago. That's why you were in the pod - to heal the scratch. The scabs are still fresh; an tell you what happened."
|
||||
|
||||
//Broken rejuvenation pod: Spawns in animal hospitals in lavaland. Ghosts become disoriented interns and are advised to search for help.
|
||||
/obj/structure/ghost_role_spawner/lavaland_vet
|
||||
name = "broken rejuvenation pod"
|
||||
desc = "A small sleeper typically used to instantly restore minor wounds. This one seems broken, and its occupant is comatose."
|
||||
role_type = /datum/ghostrole/lavaland_vet
|
||||
|
||||
/obj/structure/ghost_role_spawner/lavaland_vet/Destroy()
|
||||
var/obj/structure/fluff/empty_sleeper/S = new(drop_location())
|
||||
S.setDir(dir)
|
||||
return ..()
|
||||
97
code/modules/ghostroles/spawner.dm
Normal file
97
code/modules/ghostroles/spawner.dm
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Default implementation for ghost role spawners
|
||||
*/
|
||||
/obj/structure/ghost_role_spawner
|
||||
name = "Ghost Role Spawner"
|
||||
desc = "if you're seeing this a coder fucked up"
|
||||
// TODO: atom damage
|
||||
// resistance_flags = INDESTRUCTIBLE
|
||||
unacidable = TRUE
|
||||
density = TRUE
|
||||
icon = 'icons/obj/spawners.dmi'
|
||||
icon_state = "cryostasis_sleeper"
|
||||
|
||||
/// automatic handling - role type
|
||||
var/role_type
|
||||
/// automatic handling - allowed spawn count
|
||||
var/role_spawns = 1
|
||||
/// automatic handling - params. If this is a string at Init, it'll be json_decoded.
|
||||
var/list/role_params
|
||||
/// automatic handling - qdel on running out
|
||||
var/qdel_on_deplete = FALSE
|
||||
/// automatic handling - special spawntext
|
||||
var/special_spawntext
|
||||
|
||||
/obj/structure/ghost_role_spawner/Initialize(mapload, params, type, spawns, spawntext)
|
||||
. = ..()
|
||||
if(type)
|
||||
src.role_type = type
|
||||
if(params)
|
||||
role_params = params
|
||||
else if(istext(role_params))
|
||||
role_params = json_decode(role_params)
|
||||
if(spawns)
|
||||
role_spawns = spawns
|
||||
if(spawntext)
|
||||
special_spawntext = spawntext
|
||||
if(!src.role_type)
|
||||
if(mapload)
|
||||
stack_trace("No role type")
|
||||
else
|
||||
AddComponent(/datum/component/ghostrole_spawnpoint, role_type, role_spawns, role_params, /obj/structure/ghost_role_spawner/proc/on_spawn, null, special_spawntext)
|
||||
|
||||
/obj/structure/ghost_role_spawner/proc/on_spawn(mob/created, datum/ghostrole/role, list/params, datum/component/ghostrole_spawnpoint/spawnpoint)
|
||||
if(qdel_on_deplete && !spawnpoint.SpawnsLeft())
|
||||
qdel(src)
|
||||
|
||||
/**
|
||||
* for mappers
|
||||
*/
|
||||
/obj/structure/ghost_role_spawner/custom
|
||||
/// json to convert to params
|
||||
var/role_params_json
|
||||
/// next id
|
||||
var/static/id_next = 0
|
||||
/// forced id
|
||||
var/forced_id
|
||||
/// mob path
|
||||
var/mob_path = /mob/living/carbon/human
|
||||
/// forced instantiator path
|
||||
var/instantiator_path
|
||||
|
||||
/obj/structure/ghost_role_spawner/custom/Initialize(mapload, params, type, spawns, spawntext)
|
||||
role_type = "[forced_id]" || "[++id_next]"
|
||||
GenerateRole(role_type, mob_path)
|
||||
role_params = GenerateParams()
|
||||
return ..()
|
||||
|
||||
/obj/structure/ghost_role_spawner/custom/proc/GenerateRole(id = role_type, mob_path = src.mob_path)
|
||||
var/datum/ghostrole/G = get_ghostrole_datum(id)
|
||||
if(G)
|
||||
return
|
||||
G = new(id)
|
||||
if(instantiator_path)
|
||||
G.instantiator = new instantiator_path
|
||||
else
|
||||
G.instantiator = ispath(mob_path, /mob/living/carbon/human)? new /datum/ghostrole_instantiator/human/random/species : new /datum/ghostrole_instantiator/simple
|
||||
return G
|
||||
|
||||
/obj/structure/ghost_role_spawner/custom/proc/GenerateParams()
|
||||
. = list()
|
||||
if(istext(role_params_json))
|
||||
var/list/L = safe_json_decode(role_params_json)
|
||||
if(L)
|
||||
. = L
|
||||
.["mob"] = mob_path
|
||||
|
||||
/obj/structure/ghost_role_spawner/custom/human
|
||||
mob_path = /mob/living/carbon/human
|
||||
/// outfit path
|
||||
var/outfit_path = /datum/outfit/job/assistant
|
||||
/// species path
|
||||
var/species_path = /datum/species/human
|
||||
|
||||
/obj/structure/ghost_role_spawner/custom/human/GenerateParams()
|
||||
. = ..()
|
||||
.["outfit"] = outfit_path
|
||||
.["species"] = species_path
|
||||
114
code/modules/ghostroles/spawnpoint.dm
Normal file
114
code/modules/ghostroles/spawnpoint.dm
Normal file
@@ -0,0 +1,114 @@
|
||||
GLOBAL_LIST_EMPTY(ghostrole_spawnpoints)
|
||||
|
||||
/**
|
||||
* Spawnpoint for a ghostrole
|
||||
*/
|
||||
/datum/component/ghostrole_spawnpoint
|
||||
dupe_mode = COMPONENT_DUPE_ALLOWED
|
||||
can_transfer = TRUE
|
||||
/// allowed spawns
|
||||
var/max_spawns
|
||||
/// current spawns
|
||||
var/spawns = 0
|
||||
/// role type
|
||||
var/role_type
|
||||
/// params list
|
||||
var/list/params
|
||||
/// callback or proc type
|
||||
var/datum/callback/proc_to_call_or_callback
|
||||
/// custom HTML spawntext to show, if any
|
||||
var/spawntext
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/Initialize(role_type, allowed_spawns = INFINITY, list/params, datum/callback/proc_to_call_or_callback, notify_ghosts = TRUE, spawntext)
|
||||
if((. = ..()) & COMPONENT_INCOMPATIBLE)
|
||||
return
|
||||
if(!isatom(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
max_spawns = allowed_spawns
|
||||
src.role_type = role_type
|
||||
src.params = params
|
||||
src.spawntext = spawntext
|
||||
src.proc_to_call_or_callback = proc_to_call_or_callback
|
||||
if(notify_ghosts)
|
||||
var/datum/ghostrole/role = get_ghostrole_datum(role_type)
|
||||
if(!role)
|
||||
return
|
||||
notify_ghosts("Ghostrole spawner created: [role.name] - [parent] - [get_area(parent)]", source = parent, ignore_mapload = TRUE, flashwindow = FALSE)
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/RegisterWithParent()
|
||||
if(!isatom(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
. = ..()
|
||||
RegisterSignal(parent, COMSIG_ATOM_ATTACK_GHOST, .proc/GhostInteract)
|
||||
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/Examine)
|
||||
RegisterGlobal()
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/UnregisterFromParent()
|
||||
. = ..()
|
||||
UnregisterGlobal()
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/PostTransfer()
|
||||
if(ispath(proc_to_call_or_callback) && !hascall(parent, proc_to_call_or_callback))
|
||||
stack_trace("[src] [parent] had a proc to call, [proc_to_call_or_callback], that is no longer valid after a component transfer. It will be removed. Please fix this!")
|
||||
proc_to_call_or_callback = null
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/proc/RegisterGlobal()
|
||||
if(!islist(GLOB.ghostrole_spawnpoints[role_type]))
|
||||
GLOB.ghostrole_spawnpoints[role_type] = list(src)
|
||||
else
|
||||
GLOB.ghostrole_spawnpoints[role_type] |= src
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/proc/UnregisterGlobal()
|
||||
GLOB.ghostrole_spawnpoints[role_type] -= src
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/proc/Atom()
|
||||
RETURN_TYPE(/atom)
|
||||
return parent
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/proc/Turf()
|
||||
RETURN_TYPE(/turf)
|
||||
return Atom().loc
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/proc/SpawnsLeft(client/C)
|
||||
return max(0, max_spawns - spawns)
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/proc/OnSpawn(mob/created, datum/ghostrole/role)
|
||||
if(istype(proc_to_call_or_callback))
|
||||
proc_to_call_or_callback.Invoke(created, role, params, src)
|
||||
spawns++
|
||||
if(ispath(proc_to_call_or_callback))
|
||||
if(!hascall(parent, proc_to_call_or_callback))
|
||||
CRASH("Invalid proc [proc_to_call_or_callback] on [parent]")
|
||||
call(parent, proc_to_call_or_callback)(created, role, params, src)
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/vv_edit_var(var_name, var_value, massedit)
|
||||
if(var_name == NAMEOF(src, proc_to_call_or_callback))
|
||||
if(ispath(var_value) || istext(var_value))
|
||||
return FALSE // security reasons, unwrapped proccall
|
||||
. = ..()
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/proc/Examine(datum/source, list/examine_list)
|
||||
if(isobserver(source))
|
||||
var/datum/ghostrole/role = get_ghostrole_datum(role_type)
|
||||
if(!role)
|
||||
return
|
||||
examine_list += "<b>Click</> this ghostrole spawner to become a [role.name]!"
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/proc/GhostInteract(datum/source, mob/user)
|
||||
var/datum/ghostrole/role = get_ghostrole_datum(role_type)
|
||||
if(!role)
|
||||
to_chat(user, SPAN_DANGER("No ghostrole datum found: [role_type]. Contact a coder!"))
|
||||
if(!(datum_flags & DF_VAR_EDITED))
|
||||
stack_trace("Couldn't find role. Deleting self.")
|
||||
qdel(src)
|
||||
return
|
||||
role.AttemptSpawn(user.client, src)
|
||||
|
||||
/datum/component/ghostrole_spawnpoint/vv_edit_var(var_name, var_value, massedit)
|
||||
. = ..()
|
||||
if(var_name == NAMEOF(src, proc_to_call_or_callback))
|
||||
if(!istype(proc_to_call_or_callback, /datum/callback))
|
||||
if(hascall(parent, proc_to_call_or_callback))
|
||||
proc_to_call_or_callback = CALLBACK(parent, proc_to_call_or_callback)
|
||||
else
|
||||
proc_to_call_or_callback = null
|
||||
@@ -135,6 +135,7 @@
|
||||
return ..()
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/attack_ghost(var/mob/observer/dead/user)
|
||||
. = ..()
|
||||
|
||||
if(!(harvest && seed && seed.has_mob_product))
|
||||
return
|
||||
@@ -145,7 +146,6 @@
|
||||
var/response = alert(user, "Are you sure you want to harvest this [seed.display_name]?", "Living plant request", "Yes", "No")
|
||||
if(response == "Yes")
|
||||
harvest()
|
||||
return
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/attack_generic(var/mob/user)
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
interact(user)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
interact(user)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/interact(mob/user)
|
||||
|
||||
@@ -358,6 +358,38 @@ proc/is_blind(A)
|
||||
else
|
||||
say_dead_direct("<span class='name'>[name]</span> no longer [pick("skulks","lurks","prowls","creeps","stalks")] in the realm of the dead. [message]")
|
||||
|
||||
/**
|
||||
* WARNING: Proc direct ported from main
|
||||
*
|
||||
* ignore_key, ignore_dnr_observers will NOT work!
|
||||
*/
|
||||
/proc/notify_ghosts(message, ghost_sound, enter_link, atom/source, mutable_appearance/alert_overlay, action = NOTIFY_JUMP, flashwindow = TRUE, ignore_mapload = TRUE, ignore_key, ignore_dnr_observers = FALSE, header) //Easy notification of ghosts.
|
||||
if(ignore_mapload && SSatoms.subsystem_initialized != INITIALIZATION_INNEW_REGULAR) //don't notify for objects created during a map load
|
||||
return
|
||||
for(var/mob/observer/dead/O in player_list)
|
||||
if(!O.client)
|
||||
continue
|
||||
to_chat(O, "<span class='ghostalert'>[message][(enter_link) ? " [enter_link]" : ""]</span>")
|
||||
if(ghost_sound)
|
||||
SEND_SOUND(O, sound(ghost_sound))
|
||||
if(flashwindow)
|
||||
window_flash(O.client)
|
||||
if(source)
|
||||
var/atom/movable/screen/alert/notify_action/A = O.throw_alert("[REF(source)]_notify_action", /atom/movable/screen/alert/notify_action)
|
||||
if(A)
|
||||
if(O.client.prefs && O.client.prefs.UI_style)
|
||||
A.icon = ui_style2icon(O.client.prefs.UI_style)
|
||||
if (header)
|
||||
A.name = header
|
||||
A.desc = message
|
||||
A.action = action
|
||||
A.target = source
|
||||
if(!alert_overlay)
|
||||
alert_overlay = new(source)
|
||||
alert_overlay.layer = FLOAT_LAYER
|
||||
alert_overlay.plane = FLOAT_PLANE
|
||||
A.add_overlay(alert_overlay)
|
||||
|
||||
/mob/proc/switch_to_camera(var/obj/machinery/camera/C)
|
||||
if (!C.can_use() || stat || (get_dist(C, src) > 1 || machine != src || blinded || !canmove))
|
||||
return 0
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
update_uis()
|
||||
|
||||
/obj/item/modular_computer/attack_ghost(var/mob/observer/ghost/user)
|
||||
. = ..()
|
||||
if(enabled)
|
||||
nano_ui_interact(user)
|
||||
else if(check_rights(R_ADMIN, 0, user))
|
||||
|
||||
@@ -51,13 +51,13 @@
|
||||
climbLadder(M, target_ladder)
|
||||
|
||||
/obj/structure/ladder/attack_ghost(var/mob/M)
|
||||
. = ..()
|
||||
var/target_ladder = getTargetLadder(M)
|
||||
if(target_ladder)
|
||||
M.forceMove(get_turf(target_ladder))
|
||||
|
||||
/obj/structure/ladder/attack_robot(var/mob/M)
|
||||
attack_hand(M)
|
||||
return
|
||||
|
||||
/obj/structure/ladder/proc/getTargetLadder(var/mob/M)
|
||||
if((!target_up && !target_down) || (target_up && !istype(target_up.loc, /turf) || (target_down && !istype(target_down.loc,/turf))))
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
|
||||
// After a recent rework this should mostly be safe.
|
||||
/obj/machinery/computer/ship/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
interface_interact(user)
|
||||
|
||||
// If you don't call parent in this proc, you must make all appropriate checks yourself.
|
||||
|
||||
@@ -113,6 +113,7 @@ var/list/possible_cable_coil_colours = list(
|
||||
|
||||
// Ghost examining the cable -> tells him the power
|
||||
/obj/structure/cable/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
if(user.client && user.client.inquisitive_ghost)
|
||||
user.examinate(src)
|
||||
// following code taken from attackby (multitool)
|
||||
@@ -120,8 +121,6 @@ var/list/possible_cable_coil_colours = list(
|
||||
to_chat(user, "<span class='warning'>[powernet.avail]W in power network.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The cable is not powered.</span>")
|
||||
return
|
||||
|
||||
|
||||
// Rotating cables requires d1 and d2 to be rotated
|
||||
/obj/structure/cable/setDir(new_dir)
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
add_fingerprint(usr)
|
||||
|
||||
/obj/machinery/chemical_dispenser/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
if(stat & BROKEN)
|
||||
return
|
||||
ui_interact(user)
|
||||
|
||||
@@ -137,6 +137,7 @@
|
||||
. = ..()
|
||||
|
||||
/obj/structure/lift/panel/attack_ghost(var/mob/user)
|
||||
. = ..()
|
||||
return interact(user)
|
||||
|
||||
/obj/structure/lift/panel/interact(var/mob/user)
|
||||
|
||||
BIN
icons/hud/common/observer.dmi
Normal file
BIN
icons/hud/common/observer.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
icons/obj/spawners.dmi
Normal file
BIN
icons/obj/spawners.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
58
tgui/packages/tgui/interfaces/GhostRoleMenu.js
Normal file
58
tgui/packages/tgui/interfaces/GhostRoleMenu.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const GhostRoleMenu = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const spawners = data.spawners || [];
|
||||
return (
|
||||
<Window
|
||||
title="Spawners Menu"
|
||||
width={700}
|
||||
height={600}>
|
||||
<Window.Content scrollable>
|
||||
<Section>
|
||||
{spawners.map(spawner => (
|
||||
<Section
|
||||
key={spawner.name}
|
||||
title={spawner.name + ((spawner.amount_left === -1) ? '' : (' (' + spawner.amount_left + ' left)'))}
|
||||
level={2}
|
||||
buttons={(
|
||||
<>
|
||||
<Button
|
||||
content="Jump"
|
||||
onClick={() => act('jump', {
|
||||
id: spawner.id,
|
||||
})} />
|
||||
<Button
|
||||
content="Spawn"
|
||||
onClick={() => act('spawn', {
|
||||
id: spawner.id,
|
||||
})} />
|
||||
</>
|
||||
)}>
|
||||
<Box
|
||||
bold
|
||||
mb={1}
|
||||
fontSize="20px">
|
||||
{spawner.short_desc}
|
||||
</Box>
|
||||
<Box>
|
||||
{spawner.flavor_text}
|
||||
</Box>
|
||||
{!!spawner.important_info && (
|
||||
<Box
|
||||
mt={1}
|
||||
bold
|
||||
color="bad"
|
||||
fontSize="26px">
|
||||
{spawner.important_info}
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
))}
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@@ -29,6 +29,7 @@
|
||||
#include "code\__DEFINES\access.dm"
|
||||
#include "code\__DEFINES\admin.dm"
|
||||
#include "code\__DEFINES\appearance.dm"
|
||||
#include "code\__DEFINES\auxtools.dm"
|
||||
#include "code\__DEFINES\belly_modes_vr.dm"
|
||||
#include "code\__DEFINES\callbacks.dm"
|
||||
#include "code\__DEFINES\chat.dm"
|
||||
@@ -128,6 +129,7 @@
|
||||
#include "code\__DEFINES\dcs\signals\signals_turf.dm"
|
||||
#include "code\__DEFINES\dcs\signals\datums\signals_perspective.dm"
|
||||
#include "code\__DEFINES\dcs\signals\items\signals_inducer.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_attack.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_lighting.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_main.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_mouse.dm"
|
||||
@@ -142,6 +144,7 @@
|
||||
#include "code\__DEFINES\mobs\cooldowns.dm"
|
||||
#include "code\__DEFINES\mobs\intent.dm"
|
||||
#include "code\__DEFINES\mobs\stat.dm"
|
||||
#include "code\__DEFINES\rendering\alert.dm"
|
||||
#include "code\__DEFINES\rendering\atom_huds.dm"
|
||||
#include "code\__DEFINES\rendering\parallax.dm"
|
||||
#include "code\__DEFINES\research\exosuit_fab.dm"
|
||||
@@ -183,6 +186,7 @@
|
||||
#include "code\__HELPERS\lists\bitflags.dm"
|
||||
#include "code\__HELPERS\lists\copy.dm"
|
||||
#include "code\__HELPERS\lists\counter.dm"
|
||||
#include "code\__HELPERS\lists\json.dm"
|
||||
#include "code\__HELPERS\lists\misc.dm"
|
||||
#include "code\__HELPERS\lists\pick.dm"
|
||||
#include "code\__HELPERS\lists\queue.dm"
|
||||
@@ -2171,6 +2175,12 @@
|
||||
#include "code\modules\games\tarot.dm"
|
||||
#include "code\modules\games\unus.dm"
|
||||
#include "code\modules\genetics\side_effects.dm"
|
||||
#include "code\modules\ghostroles\instantiator.dm"
|
||||
#include "code\modules\ghostroles\menu.dm"
|
||||
#include "code\modules\ghostroles\role.dm"
|
||||
#include "code\modules\ghostroles\spawner.dm"
|
||||
#include "code\modules\ghostroles\spawnpoint.dm"
|
||||
#include "code\modules\ghostroles\roles\hermit.dm"
|
||||
#include "code\modules\ghosttrap\trap.dm"
|
||||
#include "code\modules\holidays\_holiday.dm"
|
||||
#include "code\modules\holidays\halloween.dm"
|
||||
|
||||
Reference in New Issue
Block a user