mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-08 07:52:54 +00:00
## About The Pull Request This PR refactors mind language holders into non-existence As a result, `update_atom_languages` is no longer necessary Mind-bound languages are transferred via `/mind/proc/transfer_to` Species changing no longer deletes and re-creates the mob's language holder, allowing them to keep any languages they have. Species languages are sourced from `LANGUAGE_SPECIES` now, meaning they are removed when they change species. If the mob is not a human with a species datum, these are effectively just atom level languages. Makes a bunch of unit tests to ensure language transfer over certain events works as intended ## Why It's Good For The Game Mobs with minds having two independent language holders results in a good few bugs, and simply doesn't make sense when we have sources (`LANGUAGE_MIND`). Instead of tracking two language holders, we can simply use sources better and only track one. This means that the language holder you start with is your language holder, period. It doesn't get deleted or re-instantiated or whatever. ## Changelog 🆑 Melbert refactor: Refactored language holders, making species changes not delete all of your known languages /🆑
522 lines
19 KiB
Plaintext
522 lines
19 KiB
Plaintext
/datum/brain_trauma/special/imaginary_friend
|
|
name = "Imaginary Friend"
|
|
desc = "Patient can see and hear an imaginary person."
|
|
scan_desc = "partial schizophrenia"
|
|
gain_text = span_notice("You feel in good company, for some reason.")
|
|
lose_text = span_warning("You feel lonely again.")
|
|
var/mob/camera/imaginary_friend/friend
|
|
var/friend_initialized = FALSE
|
|
|
|
/datum/brain_trauma/special/imaginary_friend/on_gain()
|
|
var/mob/living/M = owner
|
|
if(M.stat == DEAD || !M.client)
|
|
qdel(src)
|
|
return
|
|
..()
|
|
make_friend()
|
|
get_ghost()
|
|
|
|
/datum/brain_trauma/special/imaginary_friend/on_life(seconds_per_tick, times_fired)
|
|
if(get_dist(owner, friend) > 9)
|
|
friend.recall()
|
|
if(!friend)
|
|
qdel(src)
|
|
return
|
|
if(!friend.client && friend_initialized)
|
|
addtimer(CALLBACK(src, PROC_REF(reroll_friend)), 600)
|
|
|
|
/datum/brain_trauma/special/imaginary_friend/on_death()
|
|
..()
|
|
qdel(src) //friend goes down with the ship
|
|
|
|
/datum/brain_trauma/special/imaginary_friend/on_lose()
|
|
..()
|
|
QDEL_NULL(friend)
|
|
|
|
//If the friend goes afk, make a brand new friend. Plenty of fish in the sea of imagination.
|
|
/datum/brain_trauma/special/imaginary_friend/proc/reroll_friend()
|
|
if(friend.client) //reconnected
|
|
return
|
|
friend_initialized = FALSE
|
|
QDEL_NULL(friend)
|
|
make_friend()
|
|
get_ghost()
|
|
|
|
/datum/brain_trauma/special/imaginary_friend/proc/make_friend()
|
|
friend = new(get_turf(owner), owner)
|
|
|
|
/datum/brain_trauma/special/imaginary_friend/proc/get_ghost()
|
|
set waitfor = FALSE
|
|
var/list/mob/dead/observer/candidates = poll_candidates_for_mob("Do you want to play as [owner.real_name]'s imaginary friend?", ROLE_PAI, null, 7.5 SECONDS, friend, POLL_IGNORE_IMAGINARYFRIEND)
|
|
if(LAZYLEN(candidates))
|
|
var/mob/dead/observer/C = pick(candidates)
|
|
friend.key = C.key
|
|
friend_initialized = TRUE
|
|
else
|
|
qdel(src)
|
|
|
|
/mob/camera/imaginary_friend
|
|
name = "imaginary friend"
|
|
real_name = "imaginary friend"
|
|
move_on_shuttle = TRUE
|
|
desc = "A wonderful yet fake friend."
|
|
sight = NONE
|
|
mouse_opacity = MOUSE_OPACITY_ICON
|
|
see_invisible = SEE_INVISIBLE_LIVING
|
|
invisibility = INVISIBILITY_MAXIMUM
|
|
has_emotes = TRUE
|
|
var/icon/human_image
|
|
var/image/current_image
|
|
var/hidden = FALSE
|
|
var/move_delay = 0
|
|
var/mob/living/owner
|
|
var/bubble_icon = "default"
|
|
|
|
var/datum/action/innate/imaginary_join/join
|
|
var/datum/action/innate/imaginary_hide/hide
|
|
|
|
/mob/camera/imaginary_friend/Login()
|
|
. = ..()
|
|
if(!. || !client)
|
|
return FALSE
|
|
greet()
|
|
Show()
|
|
|
|
/mob/camera/imaginary_friend/proc/greet()
|
|
to_chat(src, span_notice("<b>You are the imaginary friend of [owner]!</b>"))
|
|
to_chat(src, span_notice("You are absolutely loyal to your friend, no matter what."))
|
|
to_chat(src, span_notice("You cannot directly influence the world around you, but you can see what [owner] cannot."))
|
|
|
|
/**
|
|
* Arguments:
|
|
* * imaginary_friend_owner - The living mob that owns the imaginary friend.
|
|
* * appearance_from_prefs - If this is a valid set of prefs, the appearance of the imaginary friend is based on these prefs.
|
|
*/
|
|
/mob/camera/imaginary_friend/Initialize(mapload, mob/living/imaginary_friend_owner, datum/preferences/appearance_from_prefs = null)
|
|
. = ..()
|
|
|
|
owner = imaginary_friend_owner
|
|
|
|
if(appearance_from_prefs)
|
|
INVOKE_ASYNC(src, PROC_REF(setup_friend_from_prefs), appearance_from_prefs)
|
|
else
|
|
INVOKE_ASYNC(src, PROC_REF(setup_friend))
|
|
|
|
join = new
|
|
join.Grant(src)
|
|
hide = new
|
|
hide.Grant(src)
|
|
|
|
if(!owner.imaginary_group)
|
|
owner.imaginary_group = list(owner)
|
|
owner.imaginary_group += src
|
|
|
|
/mob/camera/imaginary_friend/proc/setup_friend()
|
|
var/gender = pick(MALE, FEMALE)
|
|
real_name = random_unique_name(gender)
|
|
name = real_name
|
|
human_image = get_flat_human_icon(null, pick(SSjob.joinable_occupations))
|
|
|
|
/**
|
|
* Sets up the imaginary friend's name and look using a set of datum preferences.
|
|
*
|
|
* Arguments:
|
|
* * appearance_from_prefs - If this is a valid set of prefs, the appearance of the imaginary friend is based on the currently selected character in them. Otherwise, it's random.
|
|
*/
|
|
/mob/camera/imaginary_friend/proc/setup_friend_from_prefs(datum/preferences/appearance_from_prefs)
|
|
if(!istype(appearance_from_prefs))
|
|
stack_trace("Attempted to create imaginary friend appearance from null prefs. Using random appearance.")
|
|
setup_friend()
|
|
return
|
|
|
|
real_name = appearance_from_prefs.read_preference(/datum/preference/name/real_name)
|
|
name = real_name
|
|
|
|
// Determine what job is marked as 'High' priority.
|
|
var/datum/job/appearance_job
|
|
var/highest_pref = 0
|
|
for(var/job in appearance_from_prefs.job_preferences)
|
|
var/this_pref = appearance_from_prefs.job_preferences[job]
|
|
if(this_pref > highest_pref)
|
|
appearance_job = SSjob.GetJob(job)
|
|
highest_pref = this_pref
|
|
|
|
if(!appearance_job)
|
|
appearance_job = SSjob.GetJob(JOB_ASSISTANT)
|
|
|
|
if(istype(appearance_job, /datum/job/ai))
|
|
human_image = icon('icons/mob/silicon/ai.dmi', icon_state = resolve_ai_icon(appearance_from_prefs.read_preference(/datum/preference/choiced/ai_core_display)), dir = SOUTH)
|
|
return
|
|
|
|
if(istype(appearance_job, /datum/job/cyborg))
|
|
human_image = icon('icons/mob/silicon/robots.dmi', icon_state = "robot")
|
|
return
|
|
|
|
human_image = get_flat_human_icon(null, appearance_job, appearance_from_prefs)
|
|
|
|
/// Returns all member clients of the imaginary_group
|
|
/mob/camera/imaginary_friend/proc/group_clients()
|
|
var/group_clients = list()
|
|
for(var/mob/person as anything in owner.imaginary_group)
|
|
if(person.client)
|
|
group_clients += person.client
|
|
return group_clients
|
|
|
|
/mob/camera/imaginary_friend/proc/Show()
|
|
if(!client) //nobody home
|
|
return
|
|
|
|
var/list/friend_clients = group_clients() - src.client
|
|
//Remove old image from group
|
|
remove_image_from_clients(current_image, friend_clients)
|
|
|
|
//Generate image from the static icon and the current dir
|
|
current_image = image(human_image, src, , MOB_LAYER, dir=src.dir)
|
|
current_image.override = TRUE
|
|
current_image.name = name
|
|
if(hidden)
|
|
current_image.alpha = 150
|
|
|
|
//Add new image to owner and friend
|
|
if(!hidden)
|
|
add_image_to_clients(current_image, friend_clients)
|
|
|
|
src.client.images |= current_image
|
|
|
|
/mob/camera/imaginary_friend/Destroy()
|
|
if(owner?.client)
|
|
owner.client.images.Remove(human_image)
|
|
if(client)
|
|
client.images.Remove(human_image)
|
|
owner.imaginary_group -= src
|
|
return ..()
|
|
|
|
/mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range)
|
|
if (client?.prefs.read_preference(/datum/preference/toggle/enable_runechat) && (client.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs) || ismob(speaker)))
|
|
create_chat_message(speaker, message_language, raw_message, spans)
|
|
to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mods))
|
|
|
|
/mob/camera/imaginary_friend/send_speech(message, range = 7, obj/source = src, bubble_type = bubble_icon, list/spans = list(), datum/language/message_language = null, list/message_mods = list(), forced = null)
|
|
message = get_message_mods(message, message_mods)
|
|
message = capitalize(message)
|
|
|
|
if(message_mods[RADIO_EXTENSION] == MODE_ADMIN)
|
|
client?.cmd_admin_say(message)
|
|
return
|
|
|
|
if(message_mods[RADIO_EXTENSION] == MODE_DEADMIN)
|
|
client?.dsay(message)
|
|
return
|
|
|
|
if(check_emote(message, forced))
|
|
return
|
|
|
|
if(message_mods[MODE_SING])
|
|
var/randomnote = pick("♩", "♪", "♫")
|
|
message = "[randomnote] [capitalize(message)] [randomnote]"
|
|
spans |= SPAN_SINGING
|
|
|
|
var/eavesdrop_range = 0
|
|
var/eavesdropped_message = ""
|
|
|
|
if (message_mods[MODE_CUSTOM_SAY_ERASE_INPUT])
|
|
message = message_mods[MODE_CUSTOM_SAY_EMOTE]
|
|
log_message(message, LOG_RADIO_EMOTE)
|
|
else
|
|
if(message_mods[WHISPER_MODE] == MODE_WHISPER)
|
|
log_talk(message, LOG_WHISPER, tag="imaginary friend", forced_by = forced, custom_say_emote = message_mods[MODE_CUSTOM_SAY_EMOTE])
|
|
spans |= SPAN_ITALICS
|
|
eavesdrop_range = EAVESDROP_EXTRA_RANGE
|
|
// "This proc is dangerously laggy, avoid it or die"
|
|
// What other option do I have here? I guess I'll die
|
|
eavesdropped_message = stars(message)
|
|
else
|
|
log_talk(message, LOG_SAY, tag="imaginary friend", forced_by = forced, custom_say_emote = message_mods[MODE_CUSTOM_SAY_EMOTE])
|
|
|
|
var/quoted_message = say_quote(say_emphasis(message), spans, message_mods)
|
|
var/rendered = "[span_name("[name]")] [quoted_message]"
|
|
var/dead_rendered = "[span_name("[name] (Imaginary friend of [owner])")] [quoted_message]"
|
|
|
|
var/language = message_language || owner.get_selected_language()
|
|
Hear(rendered, src, language, message, null, spans, message_mods) // We always hear what we say
|
|
var/group = owner.imaginary_group - src // The people in our group don't, so we have to exclude ourselves not to hear twice
|
|
for(var/mob/person in group)
|
|
if(eavesdrop_range && get_dist(src, person) > 1 + eavesdrop_range)
|
|
var/new_rendered = "[span_name("[name]")] [say_quote(say_emphasis(eavesdropped_message), spans, message_mods)]"
|
|
person.Hear(new_rendered, src, language, eavesdropped_message, null, spans, message_mods)
|
|
else
|
|
person.Hear(rendered, src, language, message, null, spans, message_mods)
|
|
|
|
// Speech bubble, but only for those who have runechat off
|
|
var/list/speech_bubble_recipients = list()
|
|
for(var/mob/user as anything in (group + src)) // Add ourselves back in
|
|
if(user.client && (!user.client.prefs.read_preference(/datum/preference/toggle/enable_runechat) || (SSlag_switch.measures[DISABLE_RUNECHAT] && !HAS_TRAIT(src, TRAIT_BYPASS_MEASURES))))
|
|
speech_bubble_recipients.Add(user.client)
|
|
|
|
var/image/bubble = image('icons/mob/effects/talk.dmi', src, "[bubble_type][say_test(message)]", FLY_LAYER)
|
|
SET_PLANE_EXPLICIT(bubble, ABOVE_GAME_PLANE, src)
|
|
bubble.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
|
|
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay_global), bubble, speech_bubble_recipients, 3 SECONDS)
|
|
LAZYADD(update_on_z, bubble)
|
|
addtimer(CALLBACK(src, PROC_REF(clear_saypopup), bubble), 3.5 SECONDS)
|
|
|
|
var/turf/center_turf = get_turf(src)
|
|
if(!center_turf)
|
|
return
|
|
|
|
for(var/mob/dead_player in GLOB.dead_mob_list)
|
|
if(dead_player.z != z || get_dist(src, dead_player) > 7)
|
|
if(eavesdrop_range)
|
|
if(!(dead_player.client?.prefs.chat_toggles & CHAT_GHOSTWHISPER))
|
|
continue
|
|
else if(!(dead_player.client?.prefs.chat_toggles & CHAT_GHOSTEARS))
|
|
continue
|
|
var/link = FOLLOW_LINK(dead_player, owner)
|
|
to_chat(dead_player, "[link] [dead_rendered]")
|
|
|
|
/mob/camera/imaginary_friend/proc/clear_saypopup(image/say_popup)
|
|
LAZYREMOVE(update_on_z, say_popup)
|
|
|
|
/mob/camera/imaginary_friend/whisper(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language, ignore_spam = FALSE, forced, filterproof)
|
|
if(!message)
|
|
return
|
|
say("#[message]", bubble_type, spans, sanitize, language, ignore_spam, forced, filterproof)
|
|
|
|
/datum/emote/imaginary_friend
|
|
mob_type_allowed_typecache = /mob/camera/imaginary_friend
|
|
|
|
// We have to create our own since we can only show emotes to ourselves and our owner
|
|
/datum/emote/imaginary_friend/run_emote(mob/user, params, type_override, intentional = FALSE)
|
|
user.log_talk(message, LOG_EMOTE)
|
|
if(!can_run_emote(user, FALSE, intentional))
|
|
return FALSE
|
|
|
|
var/msg = select_message_type(user, message, intentional)
|
|
if(params && message_param)
|
|
msg = select_param(user, params)
|
|
|
|
msg = replace_pronoun(user, msg)
|
|
|
|
if(!msg)
|
|
return TRUE
|
|
|
|
var/mob/camera/imaginary_friend/friend = user
|
|
var/dchatmsg = "[span_bold("[friend] (Imaginary friend of [friend.owner])")] [msg]"
|
|
message = "[span_name("[user]")] [msg]"
|
|
|
|
var/user_turf = get_turf(user)
|
|
if (user.client)
|
|
for(var/mob/ghost as anything in GLOB.dead_mob_list)
|
|
if(!ghost.client || isnewplayer(ghost))
|
|
continue
|
|
if(ghost.client.prefs.chat_toggles & CHAT_GHOSTSIGHT && !(ghost in viewers(user_turf, null)))
|
|
ghost.show_message("[FOLLOW_LINK(ghost, user)] [dchatmsg]")
|
|
|
|
for(var/mob/person in friend.owner.imaginary_group)
|
|
to_chat(person, message)
|
|
if(person.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat))
|
|
person.create_chat_message(friend, raw_message = msg, runechat_flags = EMOTE_MESSAGE)
|
|
return TRUE
|
|
|
|
/datum/emote/imaginary_friend/point
|
|
key = "point"
|
|
key_third_person = "points"
|
|
message = "points."
|
|
message_param = "points at %t."
|
|
|
|
/datum/emote/imaginary_friend/point/run_emote(mob/camera/imaginary_friend/friend, params, type_override, intentional)
|
|
message_param = initial(message_param) // reset
|
|
return ..()
|
|
|
|
/datum/emote/imaginary_friend/custom
|
|
key = "me"
|
|
key_third_person = "custom"
|
|
message = null
|
|
|
|
/datum/emote/imaginary_friend/custom/can_run_emote(mob/user, status_check, intentional)
|
|
return ..() && intentional
|
|
|
|
/datum/emote/imaginary_friend/custom/run_emote(mob/user, params, type_override = null, intentional = FALSE)
|
|
if(!can_run_emote(user, TRUE, intentional))
|
|
return FALSE
|
|
if(is_banned_from(user.ckey, "Emote"))
|
|
to_chat(user, span_boldwarning("You cannot send custom emotes (banned)."))
|
|
return FALSE
|
|
else if(QDELETED(user))
|
|
return FALSE
|
|
else if(user.client && user.client.prefs.muted & MUTE_IC)
|
|
to_chat(user, span_boldwarning("You cannot send IC messages (muted)."))
|
|
return FALSE
|
|
else if(!params)
|
|
message = copytext(sanitize(input("Choose an emote to display.") as text|null), 1, MAX_MESSAGE_LEN)
|
|
else
|
|
message = params
|
|
. = ..()
|
|
message = null
|
|
|
|
/datum/emote/imaginary_friend/custom/replace_pronoun(mob/user, message)
|
|
return message
|
|
|
|
// Another snowflake proc, when will they end... should have refactored it differently
|
|
/mob/camera/imaginary_friend/point_at(atom/pointed_atom)
|
|
if(!isturf(loc))
|
|
return
|
|
|
|
if (pointed_atom in src)
|
|
create_point_bubble(pointed_atom)
|
|
return
|
|
|
|
var/turf/tile = get_turf(pointed_atom)
|
|
if (!tile)
|
|
return
|
|
|
|
var/turf/our_tile = get_turf(src)
|
|
var/obj/visual = image('icons/hud/screen_gen.dmi', our_tile, "arrow", FLY_LAYER)
|
|
|
|
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay_global), visual, group_clients(), 2.5 SECONDS)
|
|
animate(visual, pixel_x = (tile.x - our_tile.x) * world.icon_size + pointed_atom.pixel_x, pixel_y = (tile.y - our_tile.y) * world.icon_size + pointed_atom.pixel_y, time = 1.7, easing = EASE_OUT)
|
|
|
|
/mob/camera/imaginary_friend/create_thinking_indicator()
|
|
if(active_thinking_indicator || active_typing_indicator || !thinking_IC)
|
|
return FALSE
|
|
active_thinking_indicator = image('icons/mob/effects/talk.dmi', src, "[bubble_icon]3", TYPING_LAYER)
|
|
add_image_to_clients(active_thinking_indicator, group_clients())
|
|
|
|
/mob/camera/imaginary_friend/remove_thinking_indicator()
|
|
if(!active_thinking_indicator)
|
|
return FALSE
|
|
remove_image_from_clients(active_thinking_indicator, group_clients())
|
|
active_thinking_indicator = null
|
|
|
|
/mob/camera/imaginary_friend/create_typing_indicator()
|
|
if(active_typing_indicator || active_thinking_indicator || !thinking_IC)
|
|
return FALSE
|
|
active_typing_indicator = image('icons/mob/effects/talk.dmi', src, "[bubble_icon]0", TYPING_LAYER)
|
|
add_image_to_clients(active_typing_indicator, group_clients())
|
|
|
|
/mob/camera/imaginary_friend/remove_typing_indicator()
|
|
if(!active_typing_indicator)
|
|
return FALSE
|
|
remove_image_from_clients(active_typing_indicator, group_clients())
|
|
active_typing_indicator = null
|
|
|
|
/mob/camera/imaginary_friend/remove_all_indicators()
|
|
thinking_IC = FALSE
|
|
remove_thinking_indicator()
|
|
remove_typing_indicator()
|
|
|
|
/mob/camera/imaginary_friend/Move(NewLoc, Dir = 0)
|
|
if(world.time < move_delay)
|
|
return FALSE
|
|
setDir(Dir)
|
|
if(get_dist(src, owner) > 9)
|
|
recall()
|
|
move_delay = world.time + 10
|
|
return FALSE
|
|
abstract_move(NewLoc)
|
|
move_delay = world.time + 1
|
|
|
|
/mob/camera/imaginary_friend/setDir(newdir)
|
|
. = ..()
|
|
Show() // The image does not actually update until Show() gets called
|
|
|
|
/mob/camera/imaginary_friend/proc/recall()
|
|
if(!owner || loc == owner)
|
|
return FALSE
|
|
abstract_move(owner)
|
|
|
|
/datum/action/innate/imaginary_join
|
|
name = "Join"
|
|
desc = "Join your owner, following them from inside their mind."
|
|
button_icon = 'icons/mob/actions/actions_minor_antag.dmi'
|
|
background_icon_state = "bg_revenant"
|
|
overlay_icon_state = "bg_revenant_border"
|
|
button_icon_state = "join"
|
|
|
|
/datum/action/innate/imaginary_join/Activate()
|
|
var/mob/camera/imaginary_friend/I = owner
|
|
I.recall()
|
|
|
|
/datum/action/innate/imaginary_hide
|
|
name = "Hide"
|
|
desc = "Hide yourself from your owner's sight."
|
|
button_icon = 'icons/mob/actions/actions_minor_antag.dmi'
|
|
background_icon_state = "bg_revenant"
|
|
overlay_icon_state = "bg_revenant_border"
|
|
button_icon_state = "hide"
|
|
|
|
/datum/action/innate/imaginary_hide/proc/update_status()
|
|
var/mob/camera/imaginary_friend/I = owner
|
|
if(I.hidden)
|
|
name = "Show"
|
|
desc = "Become visible to your owner."
|
|
button_icon_state = "unhide"
|
|
else
|
|
name = "Hide"
|
|
desc = "Hide yourself from your owner's sight."
|
|
button_icon_state = "hide"
|
|
build_all_button_icons()
|
|
|
|
/datum/action/innate/imaginary_hide/Activate()
|
|
var/mob/camera/imaginary_friend/fake_friend = owner
|
|
fake_friend.hidden = !fake_friend.hidden
|
|
fake_friend.Show()
|
|
build_all_button_icons(UPDATE_BUTTON_NAME|UPDATE_BUTTON_ICON)
|
|
|
|
/datum/action/innate/imaginary_hide/update_button_name(atom/movable/screen/movable/action_button/button, force)
|
|
var/mob/camera/imaginary_friend/fake_friend = owner
|
|
if(fake_friend.hidden)
|
|
name = "Show"
|
|
desc = "Become visible to your owner."
|
|
else
|
|
name = "Hide"
|
|
desc = "Hide yourself from your owner's sight."
|
|
return ..()
|
|
|
|
/datum/action/innate/imaginary_hide/apply_button_icon(atom/movable/screen/movable/action_button/current_button, force = FALSE)
|
|
var/mob/camera/imaginary_friend/fake_friend = owner
|
|
if(fake_friend.hidden)
|
|
button_icon_state = "unhide"
|
|
else
|
|
button_icon_state = "hide"
|
|
|
|
return ..()
|
|
|
|
//down here is the trapped mind
|
|
//like imaginary friend but a lot less imagination and more like mind prison//
|
|
|
|
/datum/brain_trauma/special/imaginary_friend/trapped_owner
|
|
name = "Trapped Victim"
|
|
desc = "Patient appears to be targeted by an invisible entity."
|
|
gain_text = ""
|
|
lose_text = ""
|
|
random_gain = FALSE
|
|
|
|
/datum/brain_trauma/special/imaginary_friend/trapped_owner/make_friend()
|
|
friend = new /mob/camera/imaginary_friend/trapped(get_turf(owner), src)
|
|
|
|
/datum/brain_trauma/special/imaginary_friend/trapped_owner/reroll_friend() //no rerolling- it's just the last owner's hell
|
|
if(friend.client) //reconnected
|
|
return
|
|
friend_initialized = FALSE
|
|
QDEL_NULL(friend)
|
|
qdel(src)
|
|
|
|
/datum/brain_trauma/special/imaginary_friend/trapped_owner/get_ghost() //no randoms
|
|
return
|
|
|
|
/mob/camera/imaginary_friend/trapped
|
|
name = "figment of imagination?"
|
|
real_name = "figment of imagination?"
|
|
desc = "The previous host of this body."
|
|
|
|
/mob/camera/imaginary_friend/trapped/greet()
|
|
to_chat(src, span_notice(span_bold("You have managed to hold on as a figment of the new host's imagination!")))
|
|
to_chat(src, span_notice("All hope is lost for you, but at least you may interact with your host. You do not have to be loyal to them."))
|
|
to_chat(src, span_notice("You cannot directly influence the world around you, but you can see what the host cannot."))
|
|
|
|
/mob/camera/imaginary_friend/trapped/setup_friend()
|
|
real_name = "[owner.real_name]?"
|
|
name = real_name
|
|
human_image = icon('icons/mob/simple/lavaland/lavaland_monsters.dmi', icon_state = "curseblob")
|