diff --git a/code/datums/mind/_mind.dm b/code/datums/mind/_mind.dm index 597a3c038b5..efcd0aed2c9 100644 --- a/code/datums/mind/_mind.dm +++ b/code/datums/mind/_mind.dm @@ -62,8 +62,9 @@ var/datum/atom_hud/alternate_appearance/basic/antagonist_hud/antag_hud = null var/holy_role = NONE //is this person a chaplain or admin role allowed to use bibles, Any rank besides 'NONE' allows for this. - ///If this mind's master is another mob (i.e. adamantine golems) - var/mob/living/enslaved_to + ///If this mind's master is another mob (i.e. adamantine golems). Weakref of a /living. + var/datum/weakref/enslaved_to + var/datum/language_holder/language_holder var/unconvertable = FALSE var/late_joiner = FALSE diff --git a/code/datums/mind/antag.dm b/code/datums/mind/antag.dm index a00e903aa5b..2061f934d5d 100644 --- a/code/datums/mind/antag.dm +++ b/code/datums/mind/antag.dm @@ -211,7 +211,7 @@ add_antag_datum(N,converter.nuke_team) - enslaved_to = creator + enslaved_to = WEAKREF(creator) current.faction |= creator.faction creator.faction |= current.faction diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index 9b50a259805..6ae27dabda3 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -489,7 +489,8 @@ return FALSE if(specific_cult?.is_sacrifice_target(target.mind)) return FALSE - if(target.mind.enslaved_to && !IS_CULTIST(target.mind.enslaved_to)) + var/mob/living/master = target.mind.enslaved_to?.resolve() + if(master && !IS_CULTIST(master)) return FALSE if(target.mind.unconvertable) return FALSE diff --git a/code/modules/antagonists/sentient_creature/sentient_creature.dm b/code/modules/antagonists/sentient_creature/sentient_creature.dm index 5b2d98e0697..fd168360d65 100644 --- a/code/modules/antagonists/sentient_creature/sentient_creature.dm +++ b/code/modules/antagonists/sentient_creature/sentient_creature.dm @@ -20,18 +20,18 @@ return final_icon /datum/antagonist/sentient_creature/on_gain() - if(owner.enslaved_to) - owner.current.copy_languages(owner.enslaved_to, LANGUAGE_MASTER) + var/mob/living/master = owner.enslaved_to?.resolve() + if(master) + owner.current.copy_languages(master, LANGUAGE_MASTER) owner.current.update_atom_languages() . = ..() /datum/antagonist/sentient_creature/ui_static_data(mob/user) var/list/data = list() - if(owner.enslaved_to) - data["enslaved_to"] = owner.enslaved_to.real_name - data["p_them"] = owner.enslaved_to.p_them() - data["p_their"] = owner.enslaved_to.p_their() + var/mob/living/master = owner.enslaved_to?.resolve() + if(master) + data["enslaved_to"] = master.real_name + data["p_them"] = master.p_them() + data["p_their"] = master.p_their() data["holographic"] = owner.current.flags_1 & HOLOGRAM_1 return data - - diff --git a/code/modules/mapfluff/ruins/lavaland_ruin_code.dm b/code/modules/mapfluff/ruins/lavaland_ruin_code.dm index 0157d20bfec..be1e9ea9011 100644 --- a/code/modules/mapfluff/ruins/lavaland_ruin_code.dm +++ b/code/modules/mapfluff/ruins/lavaland_ruin_code.dm @@ -38,10 +38,10 @@ icon = 'icons/obj/wizard.dmi' icon_state = "construct" desc = "The incomplete body of a golem. Add ten sheets of any mineral to finish." - var/shell_type = /obj/effect/mob_spawn/ghost_role/human/golem - var/has_owner = FALSE //if the resulting golem obeys someone w_class = WEIGHT_CLASS_BULKY + var/shell_type = /obj/effect/mob_spawn/ghost_role/human/golem + /obj/item/golem_shell/attackby(obj/item/I, mob/user, params) . = ..() var/static/list/golem_shell_species_types = list( diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index e665555b60d..addd3f87f29 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -54,7 +54,6 @@ var/list/special_names = list("Tarkus") var/human_surname_chance = 3 var/special_name_chance = 5 - var/owner //dobby is a free golem /datum/species/golem/random_name(gender,unique,lastname) var/golem_surname = pick(GLOB.golem_names) @@ -1086,10 +1085,15 @@ to_chat(H, span_warning("You do not have enough cardboard!")) return FALSE to_chat(H, span_notice("You create a new cardboard golem shell.")) - create_brother(H.loc) + create_brother(H, H.loc) + +/datum/species/golem/cardboard/proc/create_brother(mob/living/carbon/human/golem, atom/location) + var/mob/living/master = golem.mind.enslaved_to?.resolve() + if(master) + new /obj/effect/mob_spawn/ghost_role/human/golem/servant(location, /datum/species/golem/cardboard, master) + else + new /obj/effect/mob_spawn/ghost_role/human/golem(location, /datum/species/golem/cardboard) -/datum/species/golem/cardboard/proc/create_brother(location) - new /obj/effect/mob_spawn/ghost_role/human/golem/servant(location, /datum/species/golem/cardboard, owner) last_creation = world.time /datum/species/golem/leather diff --git a/code/modules/mob_spawn/ghost_roles/golem_roles.dm b/code/modules/mob_spawn/ghost_roles/golem_roles.dm index 7d3ee0f92a0..7df4e530a75 100644 --- a/code/modules/mob_spawn/ghost_roles/golem_roles.dm +++ b/code/modules/mob_spawn/ghost_roles/golem_roles.dm @@ -14,11 +14,16 @@ prompt_name = "a free golem" you_are_text = "You are a Free Golem. Your family worships The Liberator." flavour_text = "In his infinite and divine wisdom, he set your clan free to travel the stars with a single declaration: \"Yeah go do whatever.\"" - var/has_owner = FALSE - var/can_transfer = TRUE //if golems can switch bodies to this new shell - var/mob/living/owner = null //golem's owner if it has one + spawner_job_path = /datum/job/free_golem + /// If TRUE, other golems can touch us to swap into this shell. + var/can_transfer = TRUE + /// Weakref to the creator of this golem shell. + var/datum/weakref/owner_ref -/obj/effect/mob_spawn/ghost_role/human/golem/Initialize(mapload, datum/species/golem/species = null, mob/creator = null) +/obj/effect/mob_spawn/ghost_role/human/golem/Initialize(mapload, datum/species/golem/species, mob/creator) + if(creator) + name = "inert servant golem shell" + prompt_name = "servant golem" if(species) //spawners list uses object name to register so this goes before ..() name += " ([initial(species.prefix)])" mob_species = species @@ -26,44 +31,47 @@ var/area/init_area = get_area(src) if(!mapload && init_area) notify_ghosts("\A [initial(species.prefix)] golem shell has been completed in \the [init_area.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_GOLEM) - if(has_owner && creator) + if(creator) you_are_text = "You are a golem." flavour_text = "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." important_text = "Serve [creator], and assist [creator.p_them()] in completing [creator.p_their()] goals at any cost." - owner = creator + owner_ref = WEAKREF(creator) + spawner_job_path = /datum/job/servant_golem /obj/effect/mob_spawn/ghost_role/human/golem/name_mob(mob/living/spawned_mob, forced_name) if(!forced_name) var/datum/species/golem/golem_species = mob_species - if(has_owner) + if(owner_ref?.resolve()) forced_name = "[initial(golem_species.prefix)] Golem ([rand(1,999)])" else golem_species = new mob_species forced_name = golem_species.random_name() - . = ..() + return ..() /obj/effect/mob_spawn/ghost_role/human/golem/special(mob/living/new_spawn, mob/mob_possessor) . = ..() - var/datum/species/golem/X = mob_species - to_chat(new_spawn, "[initial(X.info_text)]") - if(!owner) - to_chat(new_spawn, "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.") - try_keep_home(new_spawn) + var/mob/living/real_owner = owner_ref?.resolve() + var/datum/species/golem/golem_species = mob_species + to_chat(new_spawn, "[initial(golem_species.info_text)]") + if(isnull(real_owner)) + if(!is_station_level(new_spawn.z)) + to_chat(new_spawn, "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.") + try_keep_home(new_spawn) + + else if(new_spawn.mind) + new_spawn.mind.enslave_mind_to_creator(real_owner) + else - new_spawn.mind.enslave_mind_to_creator(owner) - new_spawn.log_message("possessed a golem shell enslaved to [key_name(owner)].", LOG_GAME) - log_admin("[key_name(new_spawn)] possessed a golem shell enslaved to [key_name(owner)].") + stack_trace("[type] created a golem without a mind.") + + new_spawn.log_message("possessed a golem shell[real_owner ? " enslaved to [key_name(real_owner)]" : ""].", LOG_GAME) + log_admin("[key_name(new_spawn)] possessed a golem shell[real_owner ? " enslaved to [key_name(real_owner)]" : ""].") + if(ishuman(new_spawn)) - var/mob/living/carbon/human/H = new_spawn - if(has_owner) - var/datum/species/golem/G = H.dna.species - G.owner = owner - H.set_cloned_appearance() - if(has_owner && new_spawn.mind) - new_spawn.mind.set_assigned_role_with_greeting(SSjob.GetJobType(/datum/job/servant_golem)) - else - new_spawn.mind.set_assigned_role_with_greeting(SSjob.GetJobType(/datum/job/free_golem)) + var/mob/living/carbon/human/human_spawn = new_spawn + human_spawn.set_cloned_appearance() /obj/effect/mob_spawn/ghost_role/human/golem/proc/try_keep_home(mob/new_spawn) var/static/list/allowed_areas = typecacheof(list(/area/icemoon, /area/lavaland, /area/ruin)) + typecacheof(/area/misc/survivalpod) @@ -75,28 +83,32 @@ . = ..() if(.) return - if(isgolem(user) && can_transfer) - var/mob/living/carbon/human/H = user - var/transfer_choice = tgui_alert(usr, "Transfer your soul to [src]? (Warning, your old body will die!)",,list("Yes","No")) - if(transfer_choice != "Yes") - return - if(QDELETED(src) || uses <= 0) - return - H.log_message("golem-swapped into [src].", LOG_GAME) - H.visible_message(span_notice("A faint light leaves [H], moving to [src] and animating it!"),span_notice("You leave your old body behind, and transfer into [src]!")) - show_flavor = FALSE - var/mob/living/carbon/human/newgolem = create(newname = H.real_name) - H.transfer_quirk_datums(newgolem) - H.mind.transfer_to(newgolem) - H.death() + if(!isgolem(user) || !can_transfer) return + var/mob/living/carbon/human/golem = user + var/transfer_choice = tgui_alert(usr, "Transfer your soul to [src]? (Warning, your old body will die!)",,list("Yes","No")) + if(transfer_choice != "Yes") + return + if(QDELETED(src) || uses <= 0) + return + uses -= 1 + golem.log_message("golem-swapped into [src].", LOG_GAME) + golem.visible_message( + span_notice("A faint light leaves [golem], moving to [src] and animating it!"), + span_notice("You leave your old body behind, and transfer into [src]!"), + ) + show_flavor = FALSE + var/mob/living/carbon/human/newgolem = create(user, golem.real_name) + golem.transfer_quirk_datums(newgolem) + golem.death() + check_uses() + return TRUE + /obj/effect/mob_spawn/ghost_role/human/golem/servant - has_owner = TRUE name = "inert servant golem shell" prompt_name = "servant golem" - /obj/effect/mob_spawn/ghost_role/human/golem/adamantine name = "dust-caked free golem shell" desc = "A humanoid shape, empty, lifeless, and full of potential." diff --git a/code/modules/mob_spawn/mob_spawn.dm b/code/modules/mob_spawn/mob_spawn.dm index 6d4818bec1c..cc0a2590fb1 100644 --- a/code/modules/mob_spawn/mob_spawn.dm +++ b/code/modules/mob_spawn/mob_spawn.dm @@ -15,7 +15,7 @@ ////Human specific stuff. Don't set these if you aren't using a human, the unit tests will put a stop to your sinful hand. ///sets the human as a species, use a typepath (example: /datum/species/skeleton) - var/mob_species + var/datum/species/mob_species ///equips the human with an outfit. var/datum/outfit/outfit ///for mappers to override parts of the outfit. really only in here for secret away missions, please try to refrain from using this out of laziness @@ -181,7 +181,15 @@ /obj/effect/mob_spawn/ghost_role/special(mob/living/spawned_mob, mob/mob_possessor) . = ..() if(mob_possessor) - spawned_mob.ckey = mob_possessor.ckey + if(mob_possessor.mind) + mob_possessor.mind.transfer_to(spawned_mob, force_key_move = TRUE) + else + spawned_mob.key = mob_possessor.key + var/datum/mind/spawned_mind = spawned_mob.mind + if(spawned_mind) + spawned_mob.mind.set_assigned_role_with_greeting(SSjob.GetJobType(spawner_job_path)) + spawned_mind.name = spawned_mob.real_name + if(show_flavor) var/output_message = "[you_are_text]" if(flavour_text != "") @@ -189,10 +197,6 @@ if(important_text != "") output_message += "\n[span_userdanger("[important_text]")]" to_chat(spawned_mob, output_message) - var/datum/mind/spawned_mind = spawned_mob.mind - if(spawned_mind) - spawned_mob.mind.set_assigned_role(SSjob.GetJobType(spawner_job_path)) - spawned_mind.name = spawned_mob.real_name /// Checks if the spawner has zero uses left, if so, delete yourself... NOW! /obj/effect/mob_spawn/ghost_role/proc/check_uses()