diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 10cc7b27384..d3918481b0f 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -368,6 +368,7 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list(
if(isobserver(mob))
//re-enter
var/mob/dead/observer/ghost = mob
+ var/old_turf = get_turf(ghost)
ghost.can_reenter_corpse = 1 //just in-case.
ghost.reenter_corpse()
log_admin("[key_name(usr)] re-entered their body")
@@ -375,6 +376,9 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list(
if(ishuman(mob))
var/mob/living/carbon/human/H = mob
H.regenerate_icons() // workaround for #13269
+ if(isAI(mob)) // client.mob, built in byond client var
+ var/mob/living/silicon/ai/ai = mob
+ ai.eyeobj.setLoc(old_turf)
else if(isnewplayer(mob))
to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or observe first.")
else
diff --git a/code/modules/admin/misc_admin_procs.dm b/code/modules/admin/misc_admin_procs.dm
index 212836ece75..005fa9861bf 100644
--- a/code/modules/admin/misc_admin_procs.dm
+++ b/code/modules/admin/misc_admin_procs.dm
@@ -889,27 +889,27 @@ GLOBAL_VAR_INIT(gamma_ship_location, 1) // 0 = station , 1 = space
//returns 1 to let the dragdrop code know we are trapping this event
//returns 0 if we don't plan to trap the event
-/datum/admins/proc/cmd_ghost_drag(mob/dead/observer/frommob, tothing)
+/datum/admins/proc/cmd_ghost_drag(mob/dead/observer/frommob, atom/tothing)
if(!istype(frommob))
return //extra sanity check to make sure only observers are shoved into things
//same as assume-direct-control perm requirements.
if(!check_rights(R_VAREDIT,0)) //no varedit, check if they have r_admin and r_debug
if(!check_rights(R_ADMIN|R_DEBUG,0)) //if they don't have r_admin and r_debug, return
- return 0 //otherwise, if they have no varedit, but do have r_admin and r_debug, execute the rest of the code
+ return FALSE //otherwise, if they have no varedit, but do have r_admin and r_debug, execute the rest of the code
if(!frommob.ckey)
- return 0
+ return FALSE
if(isitem(tothing))
var/mob/living/toitem = tothing
var/ask = alert("Are you sure you want to allow [frommob.name]([frommob.key]) to possess [toitem.name]?", "Place ghost in control of item?", "Yes", "No")
if(ask != "Yes")
- return 1
+ return TRUE
if(!frommob || !toitem) //make sure the mobs don't go away while we waited for a response
- return 1
+ return TRUE
var/mob/living/simple_animal/possessed_object/tomob = new(toitem)
@@ -931,10 +931,10 @@ GLOBAL_VAR_INIT(gamma_ship_location, 1) // 0 = station , 1 = space
var/ask = alert(question, "Place ghost in control of mob?", "Yes", "No")
if(ask != "Yes")
- return 1
+ return TRUE
if(!frommob || !tomob) //make sure the mobs don't go away while we waited for a response
- return 1
+ return TRUE
if(tomob.client) //no need to ghostize if there is no client
tomob.ghostize(0)
@@ -946,7 +946,34 @@ GLOBAL_VAR_INIT(gamma_ship_location, 1) // 0 = station , 1 = space
tomob.ckey = frommob.ckey
qdel(frommob)
- return 1
+ return TRUE
+
+ if(istype(tothing, /obj/structure/AIcore/deactivated))
+
+ var/question = "Are you sure you want to place [frommob.name]([frommob.key]) in control of an empty AI core?"
+
+ var/ask = alert(question, "Place ghost in control of an empty AI core?", "Yes", "No")
+ if(ask != "Yes")
+ return TRUE
+
+ if(QDELETED(frommob) || QDELETED(tothing)) //make sure the mobs don't go away while we waited for a response
+ return TRUE
+
+ message_admins("[key_name_admin(usr)] has put [frommob.ckey] in control of an empty AI core.")
+ log_admin("[key_name(usr)] stuffed [frommob.ckey] into an empty AI core.")
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Ghost Drag")
+
+ var/transfer_key = frommob.key // frommob is qdel'd in frommob.AIize()
+ var/mob/living/silicon/ai/ai_character = frommob.AIize()
+ ai_character.key = transfer_key // this wont occur in mind transferring if the mind is not active, which causes some weird stuff. This fixes it.
+ GLOB.empty_playable_ai_cores -= tothing
+
+ ai_character.forceMove(get_turf(tothing))
+ ai_character.view_core()
+
+ qdel(tothing)
+
+ return TRUE
// Returns a list of the number of admins in various categories
// result[1] is the number of staff that match the rank mask and are active
diff --git a/code/modules/mob/living/silicon/ai/ai_mob.dm b/code/modules/mob/living/silicon/ai/ai_mob.dm
index ae0fcb3f72b..c4edfb8b070 100644
--- a/code/modules/mob/living/silicon/ai/ai_mob.dm
+++ b/code/modules/mob/living/silicon/ai/ai_mob.dm
@@ -1518,4 +1518,11 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
stored_locations[location_number] = eyeobj.loc
return TRUE
+/mob/living/silicon/ai/ghostize(can_reenter_corpse)
+ var/old_turf = get_turf(eyeobj)
+ . = ..()
+ if(isobserver(.))
+ var/mob/dead/observer/ghost = .
+ ghost.forceMove(old_turf)
+
#undef TEXT_ANNOUNCEMENT_COOLDOWN
diff --git a/code/modules/mob/living/silicon/ai/latejoin.dm b/code/modules/mob/living/silicon/ai/latejoin.dm
index 0824a740a64..e198fc92b47 100644
--- a/code/modules/mob/living/silicon/ai/latejoin.dm
+++ b/code/modules/mob/living/silicon/ai/latejoin.dm
@@ -33,6 +33,7 @@ GLOBAL_LIST_EMPTY(empty_playable_ai_cores)
continue
O.on_target_cryo()
+ view_core()
// Ghost the current player and disallow them to return to the body
if(TOO_EARLY_TO_GHOST)
ghostize(FALSE)