diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index ac1617dd711..b32c0569a80 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -70,8 +70,7 @@ var/image/current_image var/hidden = FALSE var/move_delay = 0 - var/mob/living/carbon/owner - var/datum/brain_trauma/special/imaginary_friend/trauma + var/mob/living/owner var/datum/action/innate/imaginary_join/join var/datum/action/innate/imaginary_hide/hide @@ -88,16 +87,20 @@ 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.")) -/mob/camera/imaginary_friend/Initialize(mapload, _trauma) - if(!_trauma) - stack_trace("Imaginary friend created without trauma, wtf") - return INITIALIZE_HINT_QDEL +/** + * 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) . = ..() - trauma = _trauma - owner = trauma.owner + owner = imaginary_friend_owner - INVOKE_ASYNC(src, .proc/setup_friend) + if(appearance_from_prefs) + INVOKE_ASYNC(src, .proc/setup_friend_from_prefs, appearance_from_prefs) + else + INVOKE_ASYNC(src, .proc/setup_friend) join = new join.Grant(src) @@ -110,6 +113,43 @@ 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.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("Assistant") + + if(istype(appearance_job, /datum/job/ai)) + human_image = icon('icons/mob/ai.dmi', icon_state = resolve_ai_icon(appearance_from_prefs.preferred_ai_core_display), dir = SOUTH) + return + + if(istype(appearance_job, /datum/job/cyborg)) + human_image = icon('icons/mob/robots.dmi', icon_state = "robot") + return + + human_image = get_flat_human_icon(null, appearance_job, appearance_from_prefs) + /mob/camera/imaginary_friend/proc/Show() if(!client) //nobody home return @@ -185,6 +225,7 @@ /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 @@ -192,6 +233,10 @@ abstract_move(NewLoc) move_delay = world.time + 1 +/mob/camera/imaginary_friend/keybind_face_direction(direction) + . = ..() + Show() + /mob/camera/imaginary_friend/abstract_move(atom/destination) . = ..() Show() diff --git a/code/modules/admin/smites/imaginary_friend_special.dm b/code/modules/admin/smites/imaginary_friend_special.dm new file mode 100644 index 00000000000..60ab1254417 --- /dev/null +++ b/code/modules/admin/smites/imaginary_friend_special.dm @@ -0,0 +1,56 @@ +/** + * Custom imaginary friend. + * + * Allows the admin to select the ckey to put into the imaginary friend and whether the imaginary friend looks like the + * ckey's character. + * + * Is not tied to the brain trauma and can be used on all mobs, technically. Including cyborgs and simple/basic mobs. + * + * Warranty void if used on AI eyes or other imaginary friends. Please smite responsibly. + **/ +/datum/smite/custom_imaginary_friend + name = "Imaginary Friend (Special)" + var/client/friend_candidate_client + var/random_appearance + +/datum/smite/custom_imaginary_friend/configure(client/user) + friend_candidate_client = tgui_input_list(user, "Pick the player to put in control.", "New Imaginary Friend", sortList(GLOB.clients)) + + if(QDELETED(friend_candidate_client)) + to_chat(user, span_notice("Selected player no longer has a client, aborting.")) + return FALSE + + if(isliving(friend_candidate_client.mob) && (tgui_alert(user, "This player already has a living mob ([friend_candidate_client.mob]). Do you still want to turn them into an Imaginary Friend?", "Remove player from mob?", list("Do it!", "Cancel")) == "Cancel")) + return + + if(QDELETED(friend_candidate_client)) + to_chat(user, span_notice("Selected player no longer has a client, aborting.")) + return FALSE + + if(friend_candidate_client.prefs) + random_appearance = tgui_alert(user, "Do you want the imaginary friend to look like and be named after [friend_candidate_client]'s current preferences ([friend_candidate_client.prefs.real_name])?", "Imaginary Friend Appearance?", list("Look-a-like", "Random")) == "Random" + else + random_appearance = TRUE + +/datum/smite/custom_imaginary_friend/effect(client/user, mob/living/target) + . = ..() + + if(QDELETED(target)) + to_chat(user, span_warning("The target mob no longer exists, aborting.")) + return + + if(QDELETED(friend_candidate_client)) + to_chat(user, span_warning("Imaginary friend candidate no longer has a client, aborting.")) + return + + if(isliving(friend_candidate_client.mob)) + friend_candidate_client.mob.ghostize(can_reenter_corpse = TRUE) + + var/mob/camera/imaginary_friend/friend_mob + + if(random_appearance) + friend_mob = new /mob/camera/imaginary_friend(get_turf(target), target) + else + friend_mob = new /mob/camera/imaginary_friend(get_turf(target), target, friend_candidate_client.prefs) + + friend_mob.key = friend_candidate_client.key diff --git a/tgstation.dme b/tgstation.dme index 379e9cfa9b2..c10c9530e05 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1627,6 +1627,7 @@ #include "code\modules\admin\smites\fat.dm" #include "code\modules\admin\smites\fireball.dm" #include "code\modules\admin\smites\gib.dm" +#include "code\modules\admin\smites\imaginary_friend_special.dm" #include "code\modules\admin\smites\immerse.dm" #include "code\modules\admin\smites\knot_shoes.dm" #include "code\modules\admin\smites\lightning.dm"