diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 4c8d060708..c9e97726b3 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -282,11 +282,13 @@ return /mob/living/carbon/MiddleClickOn(atom/A) - if(!src.stat && src.mind && src.mind.changeling && src.mind.changeling.chosen_sting && (iscarbon(A)) && (A != src)) - next_click = world.time + 5 - mind.changeling.chosen_sting.try_to_sting(src, A) - else - swap_hand() + if(!stat && mind && iscarbon(A) && A != src) + var/datum/antagonist/changeling/C = mind.has_antag_datum(/datum/antagonist/changeling) + if(C && C.chosen_sting) + C.chosen_sting.try_to_sting(src,A) + next_click = world.time + 5 + return + swap_hand() /mob/living/simple_animal/drone/MiddleClickOn(atom/A) swap_hand() @@ -344,11 +346,13 @@ return /mob/living/carbon/AltClickOn(atom/A) - if(!src.stat && src.mind && src.mind.changeling && src.mind.changeling.chosen_sting && (iscarbon(A)) && (A != src)) - next_click = world.time + 5 - mind.changeling.chosen_sting.try_to_sting(src, A) - else - ..() + if(!stat && mind && iscarbon(A) && A != src) + var/datum/antagonist/changeling/C = mind.has_antag_datum(/datum/antagonist/changeling) + if(C && C.chosen_sting) + C.chosen_sting.try_to_sting(src,A) + next_click = world.time + 5 + return + ..() /atom/proc/AltClick(mob/user) SendSignal(COMSIG_CLICK_ALT, user) diff --git a/code/datums/antagonists/changeling.dm b/code/datums/antagonists/changeling.dm index 7a3dba2cf3..d72cb64b81 100644 --- a/code/datums/antagonists/changeling.dm +++ b/code/datums/antagonists/changeling.dm @@ -80,7 +80,7 @@ . = ..() /datum/antagonist/changeling/on_removal() - remove_changeling_powers() + remove_changeling_powers(FALSE) owner.objectives -= objectives . = ..() @@ -102,11 +102,11 @@ chem_recharge_slowdown = initial(chem_recharge_slowdown) mimicing = "" -/datum/antagonist/changeling/proc/remove_changeling_powers() +/datum/antagonist/changeling/proc/remove_changeling_powers(keep_free_powers=0) if(ishuman(owner.current) || ismonkey(owner.current)) reset_properties() for(var/obj/effect/proc_holder/changeling/p in purchasedpowers) - if(p.always_keep) + if((p.dna_cost == 0 && keep_free_powers) || p.always_keep) continue purchasedpowers -= p p.on_refund(owner.current) @@ -118,13 +118,13 @@ /datum/antagonist/changeling/proc/reset_powers() if(purchasedpowers) - remove_changeling_powers() - //Repurchase free powers. + remove_changeling_powers(TRUE) + //Purchase free powers. for(var/path in all_powers) var/obj/effect/proc_holder/changeling/S = new path() if(!S.dna_cost) if(!has_sting(S)) - purchasedpowers += S + purchasedpowers+=S S.on_purchase(owner.current,TRUE) /datum/antagonist/changeling/proc/has_sting(obj/effect/proc_holder/changeling/power) @@ -480,4 +480,4 @@ /datum/antagonist/changeling/xenobio name = "Xenobio Changeling" give_objectives = FALSE - you_are_greet = FALSE \ No newline at end of file + you_are_greet = FALSE diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 284841676d..bd5742ad36 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -192,15 +192,10 @@ qdel(O) /datum/mind/proc/remove_changeling() - if(src in SSticker.mode.changelings) - SSticker.mode.changelings -= src - current.remove_changeling_powers() - if(changeling) - qdel(changeling) - changeling = null - special_role = null - remove_antag_equip() - SSticker.mode.update_changeling_icons_removed(src) + var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling) + if(C) + remove_antag_datum(/datum/antagonist/changeling) + special_role = null /datum/mind/proc/remove_traitor() if(src in SSticker.mode.traitors) @@ -255,7 +250,6 @@ remove_wizard() remove_cultist() remove_rev() - SSticker.mode.update_changeling_icons_removed(src) SSticker.mode.update_traitor_icons_removed(src) SSticker.mode.update_cult_icons_removed(src) @@ -448,17 +442,12 @@ if (SSticker.mode.config_tag=="changeling" || SSticker.mode.config_tag=="traitorchan") text = uppertext(text) text = "[text]: " - if ((src in SSticker.mode.changelings) && special_role) - text += "YES | no" + var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling) + if(C) + text += "[C.name] | No" if (objectives.len==0) text += "
Objectives are empty! Randomize!" - if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) - text += "
Transform to initial appearance." - else if(src in SSticker.mode.changelings) //Station Aligned Changeling - text += "YES (but not an antag) | no" - if (objectives.len==0) - text += "
Objectives are empty! Randomize!" - if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) + if(C.stored_profiles.len && (current.real_name != C.first_prof.name) ) text += "
Transform to initial appearance." else text += "yes | NO" @@ -1050,30 +1039,29 @@ else if (href_list["changeling"]) switch(href_list["changeling"]) if("clear") - remove_changeling() + remove_antag_datum(/datum/antagonist/changeling) + special_role = null to_chat(current, "You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!") message_admins("[key_name_admin(usr)] has de-changeling'ed [current].") log_admin("[key_name(usr)] has de-changeling'ed [current].") if("changeling") - if(!(src in SSticker.mode.changelings)) - SSticker.mode.changelings += src - current.make_changeling() - special_role = "Changeling" - to_chat(current, "Your powers are awoken. A flash of memory returns to us...we are [changeling.changelingID], a changeling!") - message_admins("[key_name_admin(usr)] has changeling'ed [current].") - log_admin("[key_name(usr)] has changeling'ed [current].") - SSticker.mode.update_changeling_icons_added(src) + var/datum/antagonist/changeling/C = make_Changling() + to_chat(current, "Your powers are awoken. A flash of memory returns to us...we are [C.changelingID], a changeling!") + message_admins("[key_name_admin(usr)] has changeling'ed [current].") + log_admin("[key_name(usr)] has changeling'ed [current].") if("autoobjectives") - SSticker.mode.forge_changeling_objectives(src) + var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling) + if(C) + C.forge_objectives() to_chat(usr, "The objectives for changeling [key] have been generated. You can edit them and anounce manually.") - if("initialdna") - if( !changeling || !changeling.stored_profiles.len || !iscarbon(current)) + var/datum/antagonist/changeling/ling = has_antag_datum(/datum/antagonist/changeling) + if( !ling || !ling.stored_profiles.len || !iscarbon(current)) to_chat(usr, "Resetting DNA failed!") else var/mob/living/carbon/C = current - changeling.first_prof.dna.transfer_identity(C, transfer_SE=1) - C.real_name = changeling.first_prof.name + ling.first_prof.dna.transfer_identity(C, transfer_SE=1) + C.real_name = ling.first_prof.name C.updateappearance(mutcolor_update=1) C.domutcheck() @@ -1408,13 +1396,11 @@ current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" /datum/mind/proc/make_Changling() - if(!(src in SSticker.mode.changelings)) - SSticker.mode.changelings += src - current.make_changeling() + var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling) + if(!C) + C = add_antag_datum(/datum/antagonist/changeling) special_role = "Changeling" - SSticker.mode.forge_changeling_objectives(src) - SSticker.mode.greet_changeling(src) - SSticker.mode.update_changeling_icons_added(src) + return C /datum/mind/proc/make_Wizard() if(!has_antag_datum(/datum/antagonist/wizard)) diff --git a/code/game/gamemodes/changeling/cellular_emporium.dm b/code/game/gamemodes/changeling/cellular_emporium.dm index aaba09c87c..df92212181 100644 --- a/code/game/gamemodes/changeling/cellular_emporium.dm +++ b/code/game/gamemodes/changeling/cellular_emporium.dm @@ -3,7 +3,7 @@ /datum/cellular_emporium var/name = "cellular emporium" - var/datum/changeling/changeling + var/datum/antagonist/changeling/changeling /datum/cellular_emporium/New(my_changeling) . = ..() @@ -32,7 +32,7 @@ var/list/abilities = list() - for(var/path in subtypesof(/obj/effect/proc_holder/changeling)) + for(var/path in changeling.all_powers) var/obj/effect/proc_holder/changeling/ability = path var/dna_cost = initial(ability.dna_cost) @@ -61,10 +61,10 @@ switch(action) if("readapt") if(changeling.canrespec) - changeling.lingRespec(usr) + changeling.readapt() if("evolve") var/sting_name = params["name"] - changeling.purchasePower(usr, sting_name) + changeling.purchase_power(sting_name) /datum/action/innate/cellular_emporium name = "Cellular Emporium" diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 653beed98c..e84477b2cb 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -1,15 +1,8 @@ -#define LING_FAKEDEATH_TIME 400 //40 seconds -#define LING_DEAD_GENETICDAMAGE_HEAL_CAP 50 //The lowest value of geneticdamage handle_changeling() can take it to while dead. -#define LING_ABSORB_RECENT_SPEECH 8 //The amount of recent spoken lines to gain on absorbing a mob - GLOBAL_LIST_INIT(possible_changeling_IDs, list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega")) GLOBAL_LIST_INIT(slots, list("head", "wear_mask", "back", "wear_suit", "w_uniform", "shoes", "belt", "gloves", "glasses", "ears", "wear_id", "s_store")) GLOBAL_LIST_INIT(slot2slot, list("head" = slot_head, "wear_mask" = slot_wear_mask, "neck" = slot_neck, "back" = slot_back, "wear_suit" = slot_wear_suit, "w_uniform" = slot_w_uniform, "shoes" = slot_shoes, "belt" = slot_belt, "gloves" = slot_gloves, "glasses" = slot_glasses, "ears" = slot_ears, "wear_id" = slot_wear_id, "s_store" = slot_s_store)) GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "wear_mask" = /obj/item/clothing/mask/changeling, "back" = /obj/item/changeling, "wear_suit" = /obj/item/clothing/suit/changeling, "w_uniform" = /obj/item/clothing/under/changeling, "shoes" = /obj/item/clothing/shoes/changeling, "belt" = /obj/item/changeling, "gloves" = /obj/item/clothing/gloves/changeling, "glasses" = /obj/item/clothing/glasses/changeling, "ears" = /obj/item/changeling, "wear_id" = /obj/item/changeling, "s_store" = /obj/item/changeling)) - - -/datum/game_mode - var/list/datum/mind/changelings = list() +GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our this objective to all lings /datum/game_mode/changeling @@ -29,26 +22,8 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w Changelings: Accomplish the objectives assigned to you.\n\ Crew: Root out and eliminate the changeling menace." - var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time - var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target - var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target - - var/const/prob_int_item = 50 // intercept names the theft target half the time - var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target - var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target - - var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time - var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target - var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target - - var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative - var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative - var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly - var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly - var/const/changeling_amount = 4 //hard limit on changelings if scaling is turned off - - var/changeling_team_objective_type = null //If this is not null, we hand our this objective to all lings + var/list/changelings = list() /datum/game_mode/changeling/pre_setup() @@ -80,7 +55,6 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w return 0 /datum/game_mode/changeling/post_setup() - //Decide if it's ok for the lings to have a team objective //And then set it up to be handed out in forge_changeling_objectives var/list/team_objectives = subtypesof(/datum/objective/changeling_team_objective) @@ -92,116 +66,27 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w possible_team_objectives += T if(possible_team_objectives.len && prob(20*changelings.len)) - changeling_team_objective_type = pick(possible_team_objectives) + GLOB.changeling_team_objective_type = pick(possible_team_objectives) for(var/datum/mind/changeling in changelings) log_game("[changeling.key] (ckey) has been selected as a changeling") - changeling.current.make_changeling() - forge_changeling_objectives(changeling) - greet_changeling(changeling) - SSticker.mode.update_changeling_icons_added(changeling) + var/datum/antagonist/changeling/new_antag = new(changeling) + new_antag.team_mode = TRUE + changeling.add_antag_datum(new_antag) ..() /datum/game_mode/changeling/make_antag_chance(mob/living/carbon/human/character) //Assigns changeling to latejoiners var/csc = CONFIG_GET(number/changeling_scaling_coeff) var/changelingcap = min(round(GLOB.joined_player_list.len / (csc * 2)) + 2, round(GLOB.joined_player_list.len / csc)) - if(SSticker.mode.changelings.len >= changelingcap) //Caps number of latejoin antagonists + if(changelings.len >= changelingcap) //Caps number of latejoin antagonists return - if(SSticker.mode.changelings.len <= (changelingcap - 2) || prob(100 - (csc * 2))) + if(changelings.len <= (changelingcap - 2) || prob(100 - (csc * 2))) if(ROLE_CHANGELING in character.client.prefs.be_special) if(!jobban_isbanned(character, ROLE_CHANGELING) && !jobban_isbanned(character, "Syndicate")) if(age_check(character.client)) if(!(character.job in restricted_jobs)) character.mind.make_Changling() - -/datum/game_mode/proc/forge_changeling_objectives(datum/mind/changeling, var/team_mode = 0) - //OBJECTIVES - random traitor objectives. Unique objectives "steal brain" and "identity theft". - //No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting - //If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone - - var/escape_objective_possible = TRUE - - //if there's a team objective, check if it's compatible with escape objectives - for(var/datum/objective/changeling_team_objective/CTO in changeling.objectives) - if(!CTO.escape_objective_compatible) - escape_objective_possible = FALSE - break - - var/datum/objective/absorb/absorb_objective = new - absorb_objective.owner = changeling - absorb_objective.gen_amount_goal(6, 8) - changeling.objectives += absorb_objective - - if(prob(60)) - if(prob(85)) - var/datum/objective/steal/steal_objective = new - steal_objective.owner = changeling - steal_objective.find_target() - changeling.objectives += steal_objective - else - var/datum/objective/download/download_objective = new - download_objective.owner = changeling - download_objective.gen_amount_goal() - changeling.objectives += download_objective - - var/list/active_ais = active_ais() - if(active_ais.len && prob(100/GLOB.joined_player_list.len)) - var/datum/objective/destroy/destroy_objective = new - destroy_objective.owner = changeling - destroy_objective.find_target() - changeling.objectives += destroy_objective - else - if(prob(70)) - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = changeling - if(team_mode) //No backstabbing while in a team - kill_objective.find_target_by_role(role = "Changeling", role_type = 1, invert = 1) - else - kill_objective.find_target() - changeling.objectives += kill_objective - else - var/datum/objective/maroon/maroon_objective = new - maroon_objective.owner = changeling - if(team_mode) - maroon_objective.find_target_by_role(role = "Changeling", role_type = 1, invert = 1) - else - maroon_objective.find_target() - changeling.objectives += maroon_objective - - if (!(locate(/datum/objective/escape) in changeling.objectives) && escape_objective_possible) - var/datum/objective/escape/escape_with_identity/identity_theft = new - identity_theft.owner = changeling - identity_theft.target = maroon_objective.target - identity_theft.update_explanation_text() - changeling.objectives += identity_theft - escape_objective_possible = FALSE - - if (!(locate(/datum/objective/escape) in changeling.objectives) && escape_objective_possible) - if(prob(50)) - var/datum/objective/escape/escape_objective = new - escape_objective.owner = changeling - changeling.objectives += escape_objective - else - var/datum/objective/escape/escape_with_identity/identity_theft = new - identity_theft.owner = changeling - if(team_mode) - identity_theft.find_target_by_role(role = "Changeling", role_type = 1, invert = 1) - else - identity_theft.find_target() - changeling.objectives += identity_theft - escape_objective_possible = FALSE - - - -/datum/game_mode/changeling/forge_changeling_objectives(datum/mind/changeling) - if(changeling_team_objective_type) - var/datum/objective/changeling_team_objective/team_objective = new changeling_team_objective_type - team_objective.owner = changeling - changeling.objectives += team_objective - - ..(changeling,1) - else - ..(changeling,0) + changelings += character.mind /datum/game_mode/changeling/generate_report() return "The Gorlex Marauders have announced the successful raid and destruction of Central Command containment ship #S-[rand(1111, 9999)]. This ship housed only a single prisoner - \ @@ -209,29 +94,12 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w of the Thing being sent to a station in this sector is highly likely. It may be in the guise of any crew member. Trust nobody - suspect everybody. Do not announce this to the crew, \ as paranoia may spread and inhibit workplace efficiency." -/datum/game_mode/proc/greet_changeling(datum/mind/changeling, you_are=1) - if (you_are) - to_chat(changeling.current, "You are [changeling.changeling.changelingID], a changeling! You have absorbed and taken the form of a human.") - to_chat(changeling.current, "Use say \":g message\" to communicate with your fellow changelings.") - to_chat(changeling.current, "You must complete the following tasks:") - changeling.current.playsound_local(get_turf(changeling.current), 'sound/ambience/antag/ling_aler.ogg', 100, FALSE, pressure_affected = FALSE) - - if (changeling.current.mind) - var/mob/living/carbon/human/H = changeling.current - if(H.mind.assigned_role == "Clown") - to_chat(H, "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself.") - H.dna.remove_mutation(CLOWNMUT) - - var/obj_count = 1 - for(var/datum/objective/objective in changeling.objectives) - to_chat(changeling.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - return - /datum/game_mode/proc/auto_declare_completion_changeling() + var/list/changelings = get_antagonists(/datum/antagonist/changeling,TRUE) //Only real lings get a mention if(changelings.len) var/text = "
The changelings were:" for(var/datum/mind/changeling in changelings) + var/datum/antagonist/changeling/ling = changeling.has_antag_datum(/datum/antagonist/changeling) var/changelingwin = 1 if(!changeling.current) changelingwin = 0 @@ -239,8 +107,8 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w text += printplayer(changeling) //Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed. - text += "
Changeling ID: [changeling.changeling.changelingID]." - text += "
Genomes Extracted: [changeling.changeling.absorbedcount]" + text += "
Changeling ID: [ling.changelingID]." + text += "
Genomes Extracted: [ling.absorbedcount]" if(changeling.objectives.len) var/count = 1 @@ -272,176 +140,8 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w to_chat(world, text) - return 1 -/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) - var/list/stored_profiles = list() //list of datum/changelingprofile - var/datum/changelingprofile/first_prof = null - //var/list/absorbed_dna = list() - //var/list/protected_dna = list() //dna that is not lost when capacity is otherwise full - var/dna_max = 6 //How many extra DNA strands the changeling can store for transformation. - var/absorbedcount = 0 - var/chem_charges = 20 - var/chem_storage = 75 - var/chem_recharge_rate = 1 - var/chem_recharge_slowdown = 0 - var/sting_range = 2 - var/changelingID = "Changeling" - var/geneticdamage = 0 - var/isabsorbing = 0 - var/islinking = 0 - var/geneticpoints = 10 - var/purchasedpowers = list() - var/mimicing = "" - var/canrespec = 0 - var/changeling_speak = 0 - var/datum/dna/chosen_dna - var/obj/effect/proc_holder/changeling/sting/chosen_sting - var/datum/cellular_emporium/cellular_emporium - var/datum/action/innate/cellular_emporium/emporium_action - -/datum/changeling/New(var/gender=FEMALE) - ..() - var/honorific - if(gender == FEMALE) - honorific = "Ms." - else - honorific = "Mr." - if(GLOB.possible_changeling_IDs.len) - changelingID = pick(GLOB.possible_changeling_IDs) - GLOB.possible_changeling_IDs -= changelingID - changelingID = "[honorific] [changelingID]" - else - changelingID = "[honorific] [rand(1,999)]" - - cellular_emporium = new(src) - emporium_action = new(cellular_emporium) - -/datum/changeling/Destroy() - qdel(cellular_emporium) - cellular_emporium = null - qdel(emporium_action) - emporium_action = null - . = ..() - -/datum/changeling/proc/regenerate(var/mob/living/carbon/the_ling) - if(istype(the_ling)) - emporium_action.Grant(the_ling) - if(the_ling.stat == DEAD) - chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), (chem_storage*0.5)) - geneticdamage = max(LING_DEAD_GENETICDAMAGE_HEAL_CAP,geneticdamage-1) - else //not dead? no chem/geneticdamage caps. - chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage) - geneticdamage = max(0, geneticdamage-1) - - -/datum/changeling/proc/get_dna(dna_owner) - for(var/datum/changelingprofile/prof in stored_profiles) - if(dna_owner == prof.name) - return prof - -/datum/changeling/proc/has_dna(datum/dna/tDNA) - for(var/datum/changelingprofile/prof in stored_profiles) - if(tDNA.is_same_as(prof.dna)) - return 1 - return 0 - -/datum/changeling/proc/can_absorb_dna(mob/living/carbon/user, mob/living/carbon/human/target, var/verbose=1) - if(stored_profiles.len) - var/datum/changelingprofile/prof = stored_profiles[1] - if(prof.dna == user.dna && stored_profiles.len >= dna_max)//If our current DNA is the stalest, we gotta ditch it. - if(verbose) - to_chat(user, "We have reached our capacity to store genetic information! We must transform before absorbing more.") - return - if(!target) - return - if(NO_DNA_COPY in target.dna.species.species_traits) - if(verbose) - to_chat(user, "[target] is not compatible with our biology.") - return - if((target.disabilities & NOCLONE) || (target.disabilities & HUSK)) - if(verbose) - to_chat(user, "DNA of [target] is ruined beyond usability!") - return - if(!ishuman(target))//Absorbing monkeys is entirely possible, but it can cause issues with transforming. That's what lesser form is for anyway! - if(verbose) - to_chat(user, "We could gain no benefit from absorbing a lesser creature.") - return - if(has_dna(target.dna)) - if(verbose) - to_chat(user, "We already have this DNA in storage!") - return - if(!target.has_dna()) - if(verbose) - to_chat(user, "[target] is not compatible with our biology.") - return - return 1 - -/datum/changeling/proc/create_profile(mob/living/carbon/human/H, mob/living/carbon/human/user, protect = 0) - var/datum/changelingprofile/prof = new - - H.dna.real_name = H.real_name //Set this again, just to be sure that it's properly set. - var/datum/dna/new_dna = new H.dna.type - H.dna.copy_dna(new_dna) - prof.dna = new_dna - prof.name = H.real_name - prof.protected = protect - - prof.underwear = H.underwear - prof.undershirt = H.undershirt - prof.socks = H.socks - - var/list/slots = list("head", "wear_mask", "back", "wear_suit", "w_uniform", "shoes", "belt", "gloves", "glasses", "ears", "wear_id", "s_store") - for(var/slot in slots) - if(slot in H.vars) - var/obj/item/I = H.vars[slot] - if(!I) - continue - prof.name_list[slot] = I.name - prof.appearance_list[slot] = I.appearance - prof.flags_cover_list[slot] = I.flags_cover - prof.item_color_list[slot] = I.item_color - prof.item_state_list[slot] = I.item_state - prof.exists_list[slot] = 1 - else - continue - - return prof - -/datum/changeling/proc/add_profile(datum/changelingprofile/prof) - if(stored_profiles.len > dna_max) - if(!push_out_profile()) - return - - stored_profiles += prof - absorbedcount++ - -/datum/changeling/proc/add_new_profile(mob/living/carbon/human/H, mob/living/carbon/human/user, protect = 0) - var/datum/changelingprofile/prof = create_profile(H, protect) - add_profile(prof) - return prof - -/datum/changeling/proc/remove_profile(mob/living/carbon/human/H, force = 0) - for(var/datum/changelingprofile/prof in stored_profiles) - if(H.real_name == prof.name) - if(prof.protected && !force) - continue - stored_profiles -= prof - qdel(prof) - -/datum/changeling/proc/get_profile_to_remove() - for(var/datum/changelingprofile/prof in stored_profiles) - if(!prof.protected) - return prof - -/datum/changeling/proc/push_out_profile() - var/datum/changelingprofile/removeprofile = get_profile_to_remove() - if(removeprofile) - stored_profiles -= removeprofile - return 1 - return 0 - /proc/changeling_transform(mob/living/carbon/human/user, datum/changelingprofile/chosen_prof) var/datum/dna/chosen_dna = chosen_prof.dna user.real_name = chosen_prof.name @@ -481,50 +181,4 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w if(equip) user.equip_to_slot_or_del(C, GLOB.slot2slot[slot]) - user.regenerate_icons() - -/datum/changelingprofile - var/name = "a bug" - - var/protected = 0 - - var/datum/dna/dna = null - var/list/name_list = list() //associative list of slotname = itemname - var/list/appearance_list = list() - var/list/flags_cover_list = list() - var/list/exists_list = list() - var/list/item_color_list = list() - var/list/item_state_list = list() - - var/underwear - var/undershirt - var/socks - -/datum/changelingprofile/Destroy() - qdel(dna) - . = ..() - -/datum/changelingprofile/proc/copy_profile(datum/changelingprofile/newprofile) - newprofile.name = name - newprofile.protected = protected - newprofile.dna = new dna.type - dna.copy_dna(newprofile.dna) - newprofile.name_list = name_list.Copy() - newprofile.appearance_list = appearance_list.Copy() - newprofile.flags_cover_list = flags_cover_list.Copy() - newprofile.exists_list = exists_list.Copy() - newprofile.item_color_list = item_color_list.Copy() - newprofile.item_state_list = item_state_list.Copy() - newprofile.underwear = underwear - newprofile.undershirt = undershirt - newprofile.socks = socks - -/datum/game_mode/proc/update_changeling_icons_added(datum/mind/changling_mind) - var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_CHANGELING] - hud.join_hud(changling_mind.current) - set_antag_hud(changling_mind.current, "changling") - -/datum/game_mode/proc/update_changeling_icons_removed(datum/mind/changling_mind) - var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_CHANGELING] - hud.leave_hud(changling_mind.current) - set_antag_hud(changling_mind.current, null) + user.regenerate_icons() \ No newline at end of file diff --git a/code/game/gamemodes/changeling/changeling_power.dm b/code/game/gamemodes/changeling/changeling_power.dm index 1abd58854b..ee6a0bee8c 100644 --- a/code/game/gamemodes/changeling/changeling_power.dm +++ b/code/game/gamemodes/changeling/changeling_power.dm @@ -26,14 +26,14 @@ /obj/effect/proc_holder/changeling/Click() var/mob/user = usr - if(!user || !user.mind || !user.mind.changeling) + if(!user || !user.mind || !user.mind.has_antag_datum(/datum/antagonist/changeling)) return try_to_sting(user) /obj/effect/proc_holder/changeling/proc/try_to_sting(mob/user, mob/target) if(!can_sting(user, target)) return - var/datum/changeling/c = user.mind.changeling + var/datum/antagonist/changeling/c = user.mind.has_antag_datum(/datum/antagonist/changeling) if(sting_action(user, target)) SSblackbox.add_details("changeling_powers",name) sting_feedback(user, target) @@ -52,7 +52,7 @@ if(req_human && !ishuman(user)) to_chat(user, "We cannot do that in this form!") return 0 - var/datum/changeling/c = user.mind.changeling + var/datum/antagonist/changeling/c = user.mind.has_antag_datum(/datum/antagonist/changeling) if(c.chem_charges < chemical_cost) to_chat(user, "We require at least [chemical_cost] unit\s of chemicals to do that!") return 0 diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm deleted file mode 100644 index 76a37f9bb1..0000000000 --- a/code/game/gamemodes/changeling/evolution_menu.dm +++ /dev/null @@ -1,112 +0,0 @@ -/datum/changeling/proc/purchasePower(mob/living/carbon/user, sting_name) - - var/obj/effect/proc_holder/changeling/thepower = null - - for(var/path in subtypesof(/obj/effect/proc_holder/changeling)) - var/obj/effect/proc_holder/changeling/S = path - if(initial(S.name) == sting_name) - thepower = new path() - break - - if(!thepower) - to_chat(user, "This is awkward. Changeling power purchase failed, please report this bug to a coder!") - return - - if(absorbedcount < thepower.req_dna) - to_chat(user, "We lack the energy to evolve this ability!") - return - - if(has_sting(thepower)) - to_chat(user, "We have already evolved this ability!") - return - - if(thepower.dna_cost < 0) - to_chat(user, "We cannot evolve this ability.") - return - - if(geneticpoints < thepower.dna_cost) - to_chat(user, "We have reached our capacity for abilities.") - return - - if(user.status_flags & FAKEDEATH)//To avoid potential exploits by buying new powers while in stasis, which clears your verblist. - to_chat(user, "We lack the energy to evolve new abilities right now.") - return - - geneticpoints -= thepower.dna_cost - purchasedpowers += thepower - thepower.on_purchase(user) - -//Reselect powers -/datum/changeling/proc/lingRespec(mob/user) - if(!ishuman(user)) - to_chat(user, "We can't remove our evolutions in this form!") - return - if(canrespec) - to_chat(user, "We have removed our evolutions from this form, and are now ready to readapt.") - user.remove_changeling_powers(1) - canrespec = 0 - user.make_changeling(TRUE) - return 1 - else - to_chat(user, "You lack the power to readapt your evolutions!") - return 0 - -/mob/proc/make_changeling(is_respec) - if(!mind) - return - if(!ishuman(src) && !ismonkey(src)) - return - if(!mind.changeling) - mind.changeling = new /datum/changeling(gender) - if(mind.changeling.purchasedpowers) - remove_changeling_powers(1) - // purchase free powers. - for(var/path in subtypesof(/obj/effect/proc_holder/changeling)) - var/obj/effect/proc_holder/changeling/S = new path() - if(!S.dna_cost) - if(!mind.changeling.has_sting(S)) - mind.changeling.purchasedpowers+=S - S.on_purchase(src, is_respec) - if(is_respec) - SSblackbox.add_details("changeling_power_purchase","Readapt") - - var/mob/living/carbon/C = src //only carbons have dna now, so we have to typecaste - if(ishuman(C)) - var/datum/changelingprofile/prof = mind.changeling.add_new_profile(C, src) - mind.changeling.first_prof = prof - - var/obj/item/organ/brain/B = C.getorganslot(ORGAN_SLOT_BRAIN) - if(B) - B.vital = FALSE - B.decoy_override = TRUE - return 1 - -/datum/changeling/proc/reset() - chosen_sting = null - geneticpoints = initial(geneticpoints) - sting_range = initial(sting_range) - chem_storage = initial(chem_storage) - chem_recharge_rate = initial(chem_recharge_rate) - chem_charges = min(chem_charges, chem_storage) - chem_recharge_slowdown = initial(chem_recharge_slowdown) - mimicing = "" - -/mob/proc/remove_changeling_powers(keep_free_powers=0) - if(ishuman(src) || ismonkey(src)) - if(mind && mind.changeling) - mind.changeling.changeling_speak = 0 - mind.changeling.reset() - for(var/obj/effect/proc_holder/changeling/p in mind.changeling.purchasedpowers) - if((p.dna_cost == 0 && keep_free_powers) || p.always_keep) - continue - mind.changeling.purchasedpowers -= p - p.on_refund(src) - if(hud_used) - hud_used.lingstingdisplay.icon_state = null - hud_used.lingstingdisplay.invisibility = INVISIBILITY_ABSTRACT - -/datum/changeling/proc/has_sting(obj/effect/proc_holder/changeling/power) - for(var/obj/effect/proc_holder/changeling/P in purchasedpowers) - if(initial(power.name) == P.name) - return 1 - return 0 diff --git a/code/game/gamemodes/changeling/powers/absorb.dm b/code/game/gamemodes/changeling/powers/absorb.dm index c7b02a3224..d7ef971406 100644 --- a/code/game/gamemodes/changeling/powers/absorb.dm +++ b/code/game/gamemodes/changeling/powers/absorb.dm @@ -9,7 +9,7 @@ if(!..()) return - var/datum/changeling/changeling = user.mind.changeling + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) if(changeling.isabsorbing) to_chat(user, "We are already absorbing!") return @@ -22,12 +22,12 @@ return var/mob/living/carbon/target = user.pulling - return changeling.can_absorb_dna(user,target) + return changeling.can_absorb_dna(target) /obj/effect/proc_holder/changeling/absorbDNA/sting_action(mob/user) - var/datum/changeling/changeling = user.mind.changeling + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) var/mob/living/carbon/human/target = user.pulling changeling.isabsorbing = 1 for(var/i in 1 to 3) @@ -52,7 +52,7 @@ to_chat(target, "You are absorbed by the changeling!") if(!changeling.has_dna(target.dna)) - changeling.add_new_profile(target, user) + changeling.add_new_profile(target) if(user.nutrition < NUTRITION_LEVEL_WELL_FED) user.nutrition = min((user.nutrition + target.nutrition), NUTRITION_LEVEL_WELL_FED) @@ -84,12 +84,14 @@ user.mind.store_memory("We have no more knowledge of [target]'s speech patterns.") to_chat(user, "We have no more knowledge of [target]'s speech patterns.") - if(target.mind.changeling)//If the target was a changeling, suck out their extra juice and objective points! - changeling.chem_charges += min(target.mind.changeling.chem_charges, changeling.chem_storage) - changeling.absorbedcount += (target.mind.changeling.absorbedcount) - target.mind.changeling.stored_profiles.len = 1 - target.mind.changeling.absorbedcount = 0 + var/datum/antagonist/changeling/target_ling = target.mind.has_antag_datum(/datum/antagonist/changeling) + if(target_ling)//If the target was a changeling, suck out their extra juice and objective points! + changeling.chem_charges += min(target_ling.chem_charges, changeling.chem_storage) + changeling.absorbedcount += (target_ling.absorbedcount) + + target_ling.stored_profiles.len = 1 + target_ling.absorbedcount = 0 changeling.chem_charges=min(changeling.chem_charges+10, changeling.chem_storage) diff --git a/code/game/gamemodes/changeling/powers/fakedeath.dm b/code/game/gamemodes/changeling/powers/fakedeath.dm index e35f04d9e7..330488f6da 100644 --- a/code/game/gamemodes/changeling/powers/fakedeath.dm +++ b/code/game/gamemodes/changeling/powers/fakedeath.dm @@ -1,35 +1,37 @@ -/obj/effect/proc_holder/changeling/fakedeath - name = "Reviving Stasis" - desc = "We fall into a stasis, allowing us to regenerate and trick our enemies." - chemical_cost = 15 - dna_cost = 0 - req_dna = 1 - req_stat = DEAD - -//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay. -/obj/effect/proc_holder/changeling/fakedeath/sting_action(mob/living/user) - to_chat(user, "We begin our stasis, preparing energy to arise once more.") - if(user.stat != DEAD) - user.emote("deathgasp") - user.tod = worldtime2text() - user.status_flags |= FAKEDEATH //play dead - user.update_stat() - user.update_canmove() - - addtimer(CALLBACK(src, .proc/ready_to_regenerate, user), LING_FAKEDEATH_TIME, TIMER_UNIQUE) - return TRUE - -/obj/effect/proc_holder/changeling/fakedeath/proc/ready_to_regenerate(mob/user) - if(user && user.mind && user.mind.changeling && user.mind.changeling.purchasedpowers) - to_chat(user, "We are ready to revive.") - user.mind.changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/revive(null) - -/obj/effect/proc_holder/changeling/fakedeath/can_sting(mob/user) - if(user.status_flags & FAKEDEATH) - to_chat(user, "We are already reviving.") - return - if(!user.stat) //Confirmation for living changelings if they want to fake their death - switch(alert("Are we sure we wish to fake our own death?",,"Yes", "No")) - if("No") - return - return ..() +/obj/effect/proc_holder/changeling/fakedeath + name = "Reviving Stasis" + desc = "We fall into a stasis, allowing us to regenerate and trick our enemies." + chemical_cost = 15 + dna_cost = 0 + req_dna = 1 + req_stat = DEAD + +//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay. +/obj/effect/proc_holder/changeling/fakedeath/sting_action(mob/living/user) + to_chat(user, "We begin our stasis, preparing energy to arise once more.") + if(user.stat != DEAD) + user.emote("deathgasp") + user.tod = worldtime2text() + user.status_flags |= FAKEDEATH //play dead + user.update_stat() + user.update_canmove() + + addtimer(CALLBACK(src, .proc/ready_to_regenerate, user), LING_FAKEDEATH_TIME, TIMER_UNIQUE) + return TRUE + +/obj/effect/proc_holder/changeling/fakedeath/proc/ready_to_regenerate(mob/user) + if(user && user.mind) + var/datum/antagonist/changeling/C = user.mind.has_antag_datum(/datum/antagonist/changeling) + if(C && C.purchasedpowers) + to_chat(user, "We are ready to revive.") + C.purchasedpowers += new /obj/effect/proc_holder/changeling/revive(null) + +/obj/effect/proc_holder/changeling/fakedeath/can_sting(mob/user) + if(user.status_flags & FAKEDEATH) + to_chat(user, "We are already reviving.") + return + if(!user.stat) //Confirmation for living changelings if they want to fake their death + switch(alert("Are we sure we wish to fake our own death?",,"Yes", "No")) + if("No") + return + return ..() diff --git a/code/game/gamemodes/changeling/powers/hivemind.dm b/code/game/gamemodes/changeling/powers/hivemind.dm index 0a78adaf33..5d79a757ec 100644 --- a/code/game/gamemodes/changeling/powers/hivemind.dm +++ b/code/game/gamemodes/changeling/powers/hivemind.dm @@ -1,93 +1,93 @@ -//HIVEMIND COMMUNICATION (:g) -/obj/effect/proc_holder/changeling/hivemind_comms - name = "Hivemind Communication" - desc = "We tune our senses to the airwaves to allow us to discreetly communicate and exchange DNA with other changelings." - helptext = "We will be able to talk with other changelings with :g. Exchanged DNA do not count towards absorb objectives." - dna_cost = 0 - chemical_cost = -1 - -/obj/effect/proc_holder/changeling/hivemind_comms/on_purchase(mob/user, is_respec) - ..() - var/datum/changeling/changeling=user.mind.changeling - changeling.changeling_speak = 1 - to_chat(user, "Use say \":g message\" to communicate with the other changelings.") - var/obj/effect/proc_holder/changeling/hivemind_upload/S1 = new - if(!changeling.has_sting(S1)) - changeling.purchasedpowers+=S1 - var/obj/effect/proc_holder/changeling/hivemind_download/S2 = new - if(!changeling.has_sting(S2)) - changeling.purchasedpowers+=S2 - -// HIVE MIND UPLOAD/DOWNLOAD DNA -GLOBAL_LIST_EMPTY(hivemind_bank) - -/obj/effect/proc_holder/changeling/hivemind_upload - name = "Hive Channel DNA" - desc = "Allows us to channel DNA in the airwaves to allow other changelings to absorb it." - chemical_cost = 10 - dna_cost = -1 - -/obj/effect/proc_holder/changeling/hivemind_upload/sting_action(var/mob/user) - var/datum/changeling/changeling = user.mind.changeling - var/list/names = list() - for(var/datum/changelingprofile/prof in changeling.stored_profiles) - if(!(prof in GLOB.hivemind_bank)) - names += prof.name - - if(names.len <= 0) - to_chat(user, "The airwaves already have all of our DNA.") - return - - var/chosen_name = input("Select a DNA to channel: ", "Channel DNA", null) as null|anything in names - if(!chosen_name) - return - - var/datum/changelingprofile/chosen_dna = changeling.get_dna(chosen_name) - if(!chosen_dna) - return - - var/datum/changelingprofile/uploaded_dna = new chosen_dna.type - chosen_dna.copy_profile(uploaded_dna) - GLOB.hivemind_bank += uploaded_dna - to_chat(user, "We channel the DNA of [chosen_name] to the air.") - return TRUE - -/obj/effect/proc_holder/changeling/hivemind_download - name = "Hive Absorb DNA" - desc = "Allows us to absorb DNA that has been channeled to the airwaves. Does not count towards absorb objectives." - chemical_cost = 10 - dna_cost = -1 - -/obj/effect/proc_holder/changeling/hivemind_download/can_sting(mob/living/carbon/user) - if(!..()) - return - var/datum/changeling/changeling = user.mind.changeling - var/datum/changelingprofile/first_prof = changeling.stored_profiles[1] - if(first_prof.name == user.real_name)//If our current DNA is the stalest, we gotta ditch it. - to_chat(user, "We have reached our capacity to store genetic information! We must transform before absorbing more.") - return - return 1 - -/obj/effect/proc_holder/changeling/hivemind_download/sting_action(mob/user) - var/datum/changeling/changeling = user.mind.changeling - var/list/names = list() - for(var/datum/changelingprofile/prof in GLOB.hivemind_bank) - if(!(prof in changeling.stored_profiles)) - names[prof.name] = prof - - if(names.len <= 0) - to_chat(user, "There's no new DNA to absorb from the air.") - return - - var/S = input("Select a DNA absorb from the air: ", "Absorb DNA", null) as null|anything in names - if(!S) - return - var/datum/changelingprofile/chosen_prof = names[S] - if(!chosen_prof) - return - - var/datum/changelingprofile/downloaded_prof = new chosen_prof.type - chosen_prof.copy_profile(downloaded_prof) - changeling.add_profile(downloaded_prof) - to_chat(user, "We absorb the DNA of [S] from the air.") - return TRUE +//HIVEMIND COMMUNICATION (:g) +/obj/effect/proc_holder/changeling/hivemind_comms + name = "Hivemind Communication" + desc = "We tune our senses to the airwaves to allow us to discreetly communicate and exchange DNA with other changelings." + helptext = "We will be able to talk with other changelings with :g. Exchanged DNA do not count towards absorb objectives." + dna_cost = 0 + chemical_cost = -1 + +/obj/effect/proc_holder/changeling/hivemind_comms/on_purchase(mob/user, is_respec) + ..() + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + changeling.changeling_speak = 1 + to_chat(user, "Use say \":g message\" to communicate with the other changelings.") + var/obj/effect/proc_holder/changeling/hivemind_upload/S1 = new + if(!changeling.has_sting(S1)) + changeling.purchasedpowers+=S1 + var/obj/effect/proc_holder/changeling/hivemind_download/S2 = new + if(!changeling.has_sting(S2)) + changeling.purchasedpowers+=S2 + +// HIVE MIND UPLOAD/DOWNLOAD DNA +GLOBAL_LIST_EMPTY(hivemind_bank) + +/obj/effect/proc_holder/changeling/hivemind_upload + name = "Hive Channel DNA" + desc = "Allows us to channel DNA in the airwaves to allow other changelings to absorb it." + chemical_cost = 10 + dna_cost = -1 + +/obj/effect/proc_holder/changeling/hivemind_upload/sting_action(var/mob/user) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/list/names = list() + for(var/datum/changelingprofile/prof in changeling.stored_profiles) + if(!(prof in GLOB.hivemind_bank)) + names += prof.name + + if(names.len <= 0) + to_chat(user, "The airwaves already have all of our DNA.") + return + + var/chosen_name = input("Select a DNA to channel: ", "Channel DNA", null) as null|anything in names + if(!chosen_name) + return + + var/datum/changelingprofile/chosen_dna = changeling.get_dna(chosen_name) + if(!chosen_dna) + return + + var/datum/changelingprofile/uploaded_dna = new chosen_dna.type + chosen_dna.copy_profile(uploaded_dna) + GLOB.hivemind_bank += uploaded_dna + to_chat(user, "We channel the DNA of [chosen_name] to the air.") + return TRUE + +/obj/effect/proc_holder/changeling/hivemind_download + name = "Hive Absorb DNA" + desc = "Allows us to absorb DNA that has been channeled to the airwaves. Does not count towards absorb objectives." + chemical_cost = 10 + dna_cost = -1 + +/obj/effect/proc_holder/changeling/hivemind_download/can_sting(mob/living/carbon/user) + if(!..()) + return + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/changelingprofile/first_prof = changeling.stored_profiles[1] + if(first_prof.name == user.real_name)//If our current DNA is the stalest, we gotta ditch it. + to_chat(user, "We have reached our capacity to store genetic information! We must transform before absorbing more.") + return + return 1 + +/obj/effect/proc_holder/changeling/hivemind_download/sting_action(mob/user) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/list/names = list() + for(var/datum/changelingprofile/prof in GLOB.hivemind_bank) + if(!(prof in changeling.stored_profiles)) + names[prof.name] = prof + + if(names.len <= 0) + to_chat(user, "There's no new DNA to absorb from the air.") + return + + var/S = input("Select a DNA absorb from the air: ", "Absorb DNA", null) as null|anything in names + if(!S) + return + var/datum/changelingprofile/chosen_prof = names[S] + if(!chosen_prof) + return + + var/datum/changelingprofile/downloaded_prof = new chosen_prof.type + chosen_prof.copy_profile(downloaded_prof) + changeling.add_profile(downloaded_prof) + to_chat(user, "We absorb the DNA of [S] from the air.") + return TRUE diff --git a/code/game/gamemodes/changeling/powers/humanform.dm b/code/game/gamemodes/changeling/powers/humanform.dm index d92e622c2d..90ddfb0115 100644 --- a/code/game/gamemodes/changeling/powers/humanform.dm +++ b/code/game/gamemodes/changeling/powers/humanform.dm @@ -1,30 +1,30 @@ -/obj/effect/proc_holder/changeling/humanform - name = "Human Form" - desc = "We change into a human." - chemical_cost = 5 - req_dna = 1 - -//Transform into a human. -/obj/effect/proc_holder/changeling/humanform/sting_action(mob/living/carbon/user) - var/datum/changeling/changeling = user.mind.changeling - var/list/names = list() - for(var/datum/changelingprofile/prof in changeling.stored_profiles) - names += "[prof.name]" - - var/chosen_name = input("Select the target DNA: ", "Target DNA", null) as null|anything in names - if(!chosen_name) - return - - var/datum/changelingprofile/chosen_prof = changeling.get_dna(chosen_name) - if(!chosen_prof) - return - if(!user || user.notransform) - return 0 - to_chat(user, "We transform our appearance.") - - changeling.purchasedpowers -= src - - var/newmob = user.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS) - - changeling_transform(newmob, chosen_prof) - return TRUE +/obj/effect/proc_holder/changeling/humanform + name = "Human Form" + desc = "We change into a human." + chemical_cost = 5 + req_dna = 1 + +//Transform into a human. +/obj/effect/proc_holder/changeling/humanform/sting_action(mob/living/carbon/user) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/list/names = list() + for(var/datum/changelingprofile/prof in changeling.stored_profiles) + names += "[prof.name]" + + var/chosen_name = input("Select the target DNA: ", "Target DNA", null) as null|anything in names + if(!chosen_name) + return + + var/datum/changelingprofile/chosen_prof = changeling.get_dna(chosen_name) + if(!chosen_prof) + return + if(!user || user.notransform) + return 0 + to_chat(user, "We transform our appearance.") + + changeling.purchasedpowers -= src + + var/newmob = user.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS) + + changeling_transform(newmob, chosen_prof) + return TRUE diff --git a/code/game/gamemodes/changeling/powers/linglink.dm b/code/game/gamemodes/changeling/powers/linglink.dm index e3b9e6c02c..7a93306785 100644 --- a/code/game/gamemodes/changeling/powers/linglink.dm +++ b/code/game/gamemodes/changeling/powers/linglink.dm @@ -8,7 +8,7 @@ /obj/effect/proc_holder/changeling/linglink/can_sting(mob/living/carbon/user) if(!..()) return - var/datum/changeling/changeling = user.mind.changeling + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) if(changeling.islinking) to_chat(user, "We have already formed a link with the victim!") return @@ -19,22 +19,23 @@ to_chat(user, "We cannot link with this creature!") return var/mob/living/carbon/target = user.pulling + if(!target.mind) to_chat(user, "The victim has no mind to link to!") return if(target.stat == DEAD) to_chat(user, "The victim is dead, you cannot link to a dead mind!") return - if(target.mind.changeling) + if(target.mind.has_antag_datum(/datum/antagonist/changeling)) to_chat(user, "The victim is already a part of the hivemind!") return if(user.grab_state <= GRAB_AGGRESSIVE) to_chat(user, "We must have a tighter grip to link with this creature!") return - return changeling.can_absorb_dna(user,target) + return changeling.can_absorb_dna(target) /obj/effect/proc_holder/changeling/linglink/sting_action(mob/user) - var/datum/changeling/changeling = user.mind.changeling + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) var/mob/living/carbon/human/target = user.pulling changeling.islinking = 1 for(var/i in 1 to 3) diff --git a/code/game/gamemodes/changeling/powers/mimic_voice.dm b/code/game/gamemodes/changeling/powers/mimic_voice.dm index f422be20a9..9c0d79e233 100644 --- a/code/game/gamemodes/changeling/powers/mimic_voice.dm +++ b/code/game/gamemodes/changeling/powers/mimic_voice.dm @@ -1,27 +1,27 @@ -/obj/effect/proc_holder/changeling/mimicvoice - name = "Mimic Voice" - desc = "We shape our vocal glands to sound like a desired voice." - helptext = "Will turn your voice into the name that you enter. We must constantly expend chemicals to maintain our form like this." - chemical_cost = 0 //constant chemical drain hardcoded - dna_cost = 1 - req_human = 1 - - -// Fake Voice -/obj/effect/proc_holder/changeling/mimicvoice/sting_action(mob/user) - var/datum/changeling/changeling=user.mind.changeling - if(changeling.mimicing) - changeling.mimicing = "" - changeling.chem_recharge_slowdown -= 0.5 - to_chat(user, "We return our vocal glands to their original position.") - return - - var/mimic_voice = stripped_input(user, "Enter a name to mimic.", "Mimic Voice", null, MAX_NAME_LEN) - if(!mimic_voice) - return - - changeling.mimicing = mimic_voice - changeling.chem_recharge_slowdown += 0.5 - to_chat(user, "We shape our glands to take the voice of [mimic_voice], this will slow down regenerating chemicals while active.") - to_chat(user, "Use this power again to return to our original voice and return chemical production to normal levels.") - return TRUE +/obj/effect/proc_holder/changeling/mimicvoice + name = "Mimic Voice" + desc = "We shape our vocal glands to sound like a desired voice." + helptext = "Will turn your voice into the name that you enter. We must constantly expend chemicals to maintain our form like this." + chemical_cost = 0 //constant chemical drain hardcoded + dna_cost = 1 + req_human = 1 + + +// Fake Voice +/obj/effect/proc_holder/changeling/mimicvoice/sting_action(mob/user) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling.mimicing) + changeling.mimicing = "" + changeling.chem_recharge_slowdown -= 0.5 + to_chat(user, "We return our vocal glands to their original position.") + return + + var/mimic_voice = stripped_input(user, "Enter a name to mimic.", "Mimic Voice", null, MAX_NAME_LEN) + if(!mimic_voice) + return + + changeling.mimicing = mimic_voice + changeling.chem_recharge_slowdown += 0.5 + to_chat(user, "We shape our glands to take the voice of [mimic_voice], this will slow down regenerating chemicals while active.") + to_chat(user, "Use this power again to return to our original voice and return chemical production to normal levels.") + return TRUE diff --git a/code/game/gamemodes/changeling/powers/mutations.dm b/code/game/gamemodes/changeling/powers/mutations.dm index f8b25afa5e..ec0f4ea025 100644 --- a/code/game/gamemodes/changeling/powers/mutations.dm +++ b/code/game/gamemodes/changeling/powers/mutations.dm @@ -83,7 +83,7 @@ //checks if we already have an organic suit and casts it off. /obj/effect/proc_holder/changeling/suit/proc/check_suit(mob/user) - var/datum/changeling/changeling = user.mind.changeling + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) if(!ishuman(user) || !changeling) return 1 var/mob/living/carbon/human/H = user @@ -122,7 +122,7 @@ user.equip_to_slot_if_possible(new suit_type(user), slot_wear_suit, 1, 1, 1) user.equip_to_slot_if_possible(new helmet_type(user), slot_head, 1, 1, 1) - var/datum/changeling/changeling = user.mind.changeling + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) changeling.chem_recharge_slowdown += recharge_slowdown return TRUE @@ -385,7 +385,7 @@ weapon_name_simple = "shield" /obj/effect/proc_holder/changeling/weapon/shield/sting_action(mob/user) - var/datum/changeling/changeling = user.mind.changeling //So we can read the absorbedcount. + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) //So we can read the absorbedcount. if(!changeling) return diff --git a/code/game/gamemodes/changeling/powers/revive.dm b/code/game/gamemodes/changeling/powers/revive.dm index 71c052c39f..716b41a837 100644 --- a/code/game/gamemodes/changeling/powers/revive.dm +++ b/code/game/gamemodes/changeling/powers/revive.dm @@ -1,35 +1,37 @@ -/obj/effect/proc_holder/changeling/revive - name = "Revive" - desc = "We regenerate, healing all damage from our form." - helptext = "Does not regrow lost organs or a missing head." - req_stat = DEAD - always_keep = TRUE - ignores_fakedeath = TRUE - -//Revive from revival stasis -/obj/effect/proc_holder/changeling/revive/sting_action(mob/living/carbon/user) - user.status_flags &= ~(FAKEDEATH) - user.tod = null - user.revive(full_heal = 1) - var/list/missing = user.get_missing_limbs() - missing -= "head" // headless changelings are funny - if(missing.len) - playsound(user, 'sound/magic/demon_consume.ogg', 50, 1) - user.visible_message("[user]'s missing limbs \ - reform, making a loud, grotesque sound!", - "Your limbs regrow, making a \ - loud, crunchy sound and giving you great pain!", - "You hear organic matter ripping \ - and tearing!") - user.emote("scream") - user.regenerate_limbs(0, list("head")) +/obj/effect/proc_holder/changeling/revive + name = "Revive" + desc = "We regenerate, healing all damage from our form." + helptext = "Does not regrow lost organs or a missing head." + req_stat = DEAD + always_keep = TRUE + ignores_fakedeath = TRUE + +//Revive from revival stasis +/obj/effect/proc_holder/changeling/revive/sting_action(mob/living/carbon/user) + user.status_flags &= ~(FAKEDEATH) + user.tod = null + user.revive(full_heal = 1) + var/list/missing = user.get_missing_limbs() + missing -= "head" // headless changelings are funny + if(missing.len) + playsound(user, 'sound/magic/demon_consume.ogg', 50, 1) + user.visible_message("[user]'s missing limbs \ + reform, making a loud, grotesque sound!", + "Your limbs regrow, making a \ + loud, crunchy sound and giving you great pain!", + "You hear organic matter ripping \ + and tearing!") + user.emote("scream") + user.regenerate_limbs(0, list("head")) user.regenerate_organs() - to_chat(user, "We have revived ourselves.") - user.mind.changeling.purchasedpowers -= src - return TRUE - -/obj/effect/proc_holder/changeling/revive/can_be_used_by(mob/user) - if((user.stat != DEAD) && !(user.status_flags & FAKEDEATH)) - user.mind.changeling.purchasedpowers -= src - return 0 - . = ..() + to_chat(user, "We have revived ourselves.") + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + changeling.purchasedpowers -= src + return TRUE + +/obj/effect/proc_holder/changeling/revive/can_be_used_by(mob/user) + if((user.stat != DEAD) && !(user.status_flags & FAKEDEATH)) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + changeling.purchasedpowers -= src + return 0 + . = ..() diff --git a/code/game/gamemodes/changeling/powers/shriek.dm b/code/game/gamemodes/changeling/powers/shriek.dm index dd066bf71b..667dc06daf 100644 --- a/code/game/gamemodes/changeling/powers/shriek.dm +++ b/code/game/gamemodes/changeling/powers/shriek.dm @@ -11,7 +11,7 @@ for(var/mob/living/M in get_hearers_in_view(4, user)) if(iscarbon(M)) var/mob/living/carbon/C = M - if(!C.mind || !C.mind.changeling) + if(!C.mind || !C.mind.has_antag_datum(/datum/antagonist/changeling)) C.adjustEarDamage(0, 30) C.confused += 25 C.Jitter(50) diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm index c23074c506..4fdd84040e 100644 --- a/code/game/gamemodes/changeling/powers/tiny_prick.dm +++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm @@ -5,9 +5,12 @@ /obj/effect/proc_holder/changeling/sting/Click() var/mob/user = usr - if(!user || !user.mind || !user.mind.changeling) + if(!user || !user.mind) return - if(!(user.mind.changeling.chosen_sting)) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + if(!changeling) + return + if(!changeling.chosen_sting) set_sting(user) else unset_sting(user) @@ -15,41 +18,48 @@ /obj/effect/proc_holder/changeling/sting/proc/set_sting(mob/user) to_chat(user, "We prepare our sting, use alt+click or middle mouse button on target to sting them.") - user.mind.changeling.chosen_sting = src + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + changeling.chosen_sting = src + user.hud_used.lingstingdisplay.icon_state = sting_icon user.hud_used.lingstingdisplay.invisibility = 0 /obj/effect/proc_holder/changeling/sting/proc/unset_sting(mob/user) to_chat(user, "We retract our sting, we can't sting anyone for now.") - user.mind.changeling.chosen_sting = null + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + changeling.chosen_sting = null + user.hud_used.lingstingdisplay.icon_state = null user.hud_used.lingstingdisplay.invisibility = INVISIBILITY_ABSTRACT /mob/living/carbon/proc/unset_sting() - if(mind && mind.changeling && mind.changeling.chosen_sting) - src.mind.changeling.chosen_sting.unset_sting(src) + if(mind) + var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling && changeling.chosen_sting) + changeling.chosen_sting.unset_sting(src) /obj/effect/proc_holder/changeling/sting/can_sting(mob/user, mob/target) if(!..()) return - if(!user.mind.changeling.chosen_sting) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + if(!changeling.chosen_sting) to_chat(user, "We haven't prepared our sting yet!") if(!iscarbon(target)) return if(!isturf(user.loc)) return - if(!AStar(user, target.loc, /turf/proc/Distance, user.mind.changeling.sting_range, simulated_only = 0)) + if(!AStar(user, target.loc, /turf/proc/Distance, changeling.sting_range, simulated_only = 0)) return - if(target.mind && target.mind.changeling) + if(target.mind && target.mind.has_antag_datum(/datum/antagonist/changeling)) sting_feedback(user, target) - user.mind.changeling.chem_charges -= chemical_cost + changeling.chem_charges -= chemical_cost //?? return 1 /obj/effect/proc_holder/changeling/sting/sting_feedback(mob/user, mob/target) if(!target) return to_chat(user, "We stealthily sting [target.name].") - if(target.mind && target.mind.changeling) + if(target.mind && target.mind.has_antag_datum(/datum/antagonist/changeling)) to_chat(target, "You feel a tiny prick.") return 1 @@ -65,7 +75,7 @@ /obj/effect/proc_holder/changeling/sting/transformation/Click() var/mob/user = usr - var/datum/changeling/changeling = user.mind.changeling + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) if(changeling.chosen_sting) unset_sting(user) return @@ -163,12 +173,14 @@ /obj/effect/proc_holder/changeling/sting/extract_dna/can_sting(mob/user, mob/target) if(..()) - return user.mind.changeling.can_absorb_dna(user, target) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + return changeling.can_absorb_dna(target) /obj/effect/proc_holder/changeling/sting/extract_dna/sting_action(mob/user, mob/living/carbon/human/target) add_logs(user, target, "stung", "extraction sting") - if(!(user.mind.changeling.has_dna(target.dna))) - user.mind.changeling.add_new_profile(target, user) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + if(!(changeling.has_dna(target.dna))) + changeling.add_new_profile(target) return TRUE /obj/effect/proc_holder/changeling/sting/mute diff --git a/code/game/gamemodes/changeling/powers/transform.dm b/code/game/gamemodes/changeling/powers/transform.dm index 6b627870a4..a977bab3e0 100644 --- a/code/game/gamemodes/changeling/powers/transform.dm +++ b/code/game/gamemodes/changeling/powers/transform.dm @@ -1,126 +1,129 @@ -/obj/effect/proc_holder/changeling/transform - name = "Transform" - desc = "We take on the appearance and voice of one we have absorbed." - chemical_cost = 5 - dna_cost = 0 - req_dna = 1 - req_human = 1 - -/obj/item/clothing/glasses/changeling - name = "flesh" +/obj/effect/proc_holder/changeling/transform + name = "Transform" + desc = "We take on the appearance and voice of one we have absorbed." + chemical_cost = 5 + dna_cost = 0 + req_dna = 1 + req_human = 1 + +/obj/item/clothing/glasses/changeling + name = "flesh" flags_1 = NODROP_1 - -/obj/item/clothing/glasses/changeling/attack_hand(mob/user) - if(loc == user && user.mind && user.mind.changeling) - to_chat(user, "You reabsorb [src] into your body.") - qdel(src) - return - ..() - -/obj/item/clothing/under/changeling - name = "flesh" + +/obj/item/clothing/glasses/changeling/attack_hand(mob/user) + if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + to_chat(user, "You reabsorb [src] into your body.") + qdel(src) + return + ..() + +/obj/item/clothing/under/changeling + name = "flesh" flags_1 = NODROP_1 - -/obj/item/clothing/under/changeling/attack_hand(mob/user) - if(loc == user && user.mind && user.mind.changeling) - to_chat(user, "You reabsorb [src] into your body.") - qdel(src) - return - ..() - -/obj/item/clothing/suit/changeling - name = "flesh" + +/obj/item/clothing/under/changeling/attack_hand(mob/user) + if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + to_chat(user, "You reabsorb [src] into your body.") + qdel(src) + return + ..() + +/obj/item/clothing/suit/changeling + name = "flesh" flags_1 = NODROP_1 - allowed = list(/obj/item/changeling) - -/obj/item/clothing/suit/changeling/attack_hand(mob/user) - if(loc == user && user.mind && user.mind.changeling) - to_chat(user, "You reabsorb [src] into your body.") - qdel(src) - return - ..() - -/obj/item/clothing/head/changeling - name = "flesh" + allowed = list(/obj/item/changeling) + +/obj/item/clothing/suit/changeling/attack_hand(mob/user) + if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + to_chat(user, "You reabsorb [src] into your body.") + qdel(src) + return + ..() + +/obj/item/clothing/head/changeling + name = "flesh" flags_1 = NODROP_1 - -/obj/item/clothing/head/changeling/attack_hand(mob/user) - if(loc == user && user.mind && user.mind.changeling) - to_chat(user, "You reabsorb [src] into your body.") - qdel(src) - return - ..() - -/obj/item/clothing/shoes/changeling - name = "flesh" + +/obj/item/clothing/head/changeling/attack_hand(mob/user) + if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + to_chat(user, "You reabsorb [src] into your body.") + qdel(src) + return + ..() + +/obj/item/clothing/shoes/changeling + name = "flesh" flags_1 = NODROP_1 - -/obj/item/clothing/shoes/changeling/attack_hand(mob/user) - if(loc == user && user.mind && user.mind.changeling) - to_chat(user, "You reabsorb [src] into your body.") - qdel(src) - return - ..() - -/obj/item/clothing/gloves/changeling - name = "flesh" + +/obj/item/clothing/shoes/changeling/attack_hand(mob/user) + if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + to_chat(user, "You reabsorb [src] into your body.") + qdel(src) + return + ..() + +/obj/item/clothing/gloves/changeling + name = "flesh" flags_1 = NODROP_1 - -/obj/item/clothing/gloves/changeling/attack_hand(mob/user) - if(loc == user && user.mind && user.mind.changeling) - to_chat(user, "You reabsorb [src] into your body.") - qdel(src) - return - ..() - -/obj/item/clothing/mask/changeling - name = "flesh" + +/obj/item/clothing/gloves/changeling/attack_hand(mob/user) + if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + to_chat(user, "You reabsorb [src] into your body.") + qdel(src) + return + ..() + +/obj/item/clothing/mask/changeling + name = "flesh" flags_1 = NODROP_1 - -/obj/item/clothing/mask/changeling/attack_hand(mob/user) - if(loc == user && user.mind && user.mind.changeling) - to_chat(user, "You reabsorb [src] into your body.") - qdel(src) - return - ..() - -/obj/item/changeling - name = "flesh" + +/obj/item/clothing/mask/changeling/attack_hand(mob/user) + if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + to_chat(user, "You reabsorb [src] into your body.") + qdel(src) + return + ..() + +/obj/item/changeling + name = "flesh" flags_1 = NODROP_1 - slot_flags = ALL - allowed = list(/obj/item/changeling) - -/obj/item/changeling/attack_hand(mob/user) - if(loc == user && user.mind && user.mind.changeling) - to_chat(user, "You reabsorb [src] into your body.") - qdel(src) - return - ..() - -//Change our DNA to that of somebody we've absorbed. -/obj/effect/proc_holder/changeling/transform/sting_action(mob/living/carbon/human/user) - var/datum/changeling/changeling = user.mind.changeling - var/datum/changelingprofile/chosen_prof = changeling.select_dna("Select the target DNA: ", "Target DNA", user) - - if(!chosen_prof) - return - - changeling_transform(user, chosen_prof) - return TRUE - -/datum/changeling/proc/select_dna(var/prompt, var/title, var/mob/living/carbon/user) - var/list/names = list("Drop Flesh Disguise") - for(var/datum/changelingprofile/prof in stored_profiles) - names += "[prof.name]" - - var/chosen_name = input(prompt, title, null) as null|anything in names - if(!chosen_name) - return - - if(chosen_name == "Drop Flesh Disguise") - for(var/slot in GLOB.slots) - if(istype(user.vars[slot], GLOB.slot2type[slot])) - qdel(user.vars[slot]) - - var/datum/changelingprofile/prof = get_dna(chosen_name) - return prof + slot_flags = ALL + allowed = list(/obj/item/changeling) + +/obj/item/changeling/attack_hand(mob/user) + if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + to_chat(user, "You reabsorb [src] into your body.") + qdel(src) + return + ..() + +//Change our DNA to that of somebody we've absorbed. +/obj/effect/proc_holder/changeling/transform/sting_action(mob/living/carbon/human/user) + var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/changelingprofile/chosen_prof = changeling.select_dna("Select the target DNA: ", "Target DNA") + + if(!chosen_prof) + return + + changeling_transform(user, chosen_prof) + return TRUE + +/datum/antagonist/changeling/proc/select_dna(var/prompt, var/title) + var/mob/living/carbon/user = owner.current + if(!istype(user)) + return + var/list/names = list("Drop Flesh Disguise") + for(var/datum/changelingprofile/prof in stored_profiles) + names += "[prof.name]" + + var/chosen_name = input(prompt, title, null) as null|anything in names + if(!chosen_name) + return + + if(chosen_name == "Drop Flesh Disguise") + for(var/slot in GLOB.slots) + if(istype(user.vars[slot], GLOB.slot2type[slot])) + qdel(user.vars[slot]) + + var/datum/changelingprofile/prof = get_dna(chosen_name) + return prof diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm index 8da88e18d7..6287e707f2 100644 --- a/code/game/gamemodes/changeling/traitor_chan.dm +++ b/code/game/gamemodes/changeling/traitor_chan.dm @@ -10,6 +10,7 @@ reroll_friendly = 1 var/list/possible_changelings = list() + var/list/changelings = list() var/const/changeling_amount = 1 //hard limit on changelings if scaling is turned off /datum/game_mode/traitor/changeling/announce() @@ -57,25 +58,22 @@ /datum/game_mode/traitor/changeling/post_setup() for(var/datum/mind/changeling in changelings) - changeling.current.make_changeling() - forge_changeling_objectives(changeling) - greet_changeling(changeling) - SSticker.mode.update_changeling_icons_added(changeling) - ..() - return + changeling.add_antag_datum(/datum/antagonist/changeling) + return ..() /datum/game_mode/traitor/changeling/make_antag_chance(mob/living/carbon/human/character) //Assigns changeling to latejoiners var/csc = CONFIG_GET(number/changeling_scaling_coeff) var/changelingcap = min( round(GLOB.joined_player_list.len / (csc * 4)) + 2, round(GLOB.joined_player_list.len / (csc * 2))) - if(SSticker.mode.changelings.len >= changelingcap) //Caps number of latejoin antagonists + if(changelings.len >= changelingcap) //Caps number of latejoin antagonists ..() return - if(SSticker.mode.changelings.len <= (changelingcap - 2) || prob(100 / (csc * 4))) + if(changelings.len <= (changelingcap - 2) || prob(100 / (csc * 4))) if(ROLE_CHANGELING in character.client.prefs.be_special) if(!jobban_isbanned(character, ROLE_CHANGELING) && !jobban_isbanned(character, "Syndicate")) if(age_check(character.client)) if(!(character.job in restricted_jobs)) character.mind.make_Changling() + changelings += character.mind ..() /datum/game_mode/traitor/changeling/generate_report() diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm index 8fead855b2..f136abdfcb 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm @@ -477,7 +477,7 @@ Congratulations! You are now trained for invasive xenobiology research!"} if(ishuman(L)) var/mob/living/carbon/human/H = L species = "[H.dna.species.name]" - if(L.mind && L.mind.changeling) + if(L.mind && L.mind.has_antag_datum(/datum/antagonist/changeling)) species = "Changeling lifeform" var/obj/item/organ/heart/gland/temp = locate() in H.internal_organs if(temp) diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index cd3b2ba2b2..d1c8d0193d 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -586,7 +586,7 @@ GLOBAL_LIST_EMPTY(possible_items_special) n_p ++ else if (SSticker.IsRoundInProgress()) for(var/mob/living/carbon/human/P in GLOB.player_list) - if(P.client && !(P.mind in SSticker.mode.changelings) && !(P.mind in owners)) + if(P.client && !(P.mind.has_antag_datum(/datum/antagonist/changeling)) && !(P.mind in owners)) n_p ++ target_amount = min(target_amount, n_p) @@ -597,9 +597,12 @@ GLOBAL_LIST_EMPTY(possible_items_special) var/list/datum/mind/owners = get_owners() var/absorbedcount = 0 for(var/datum/mind/M in owners) - if(!owner || !owner.changeling || !owner.changeling.stored_profiles) + if(!M) continue - absorbedcount += M.changeling.absorbedcount + var/datum/antagonist/changeling/changeling = M.has_antag_datum(/datum/antagonist/changeling) + if(!changeling || !changeling.stored_profiles) + continue + absorbedcount += changeling.absorbedcount return absorbedcount >= target_amount @@ -696,10 +699,11 @@ GLOBAL_LIST_EMPTY(possible_items_special) if("Chief Medical Officer") department_string = "medical" - var/ling_count = SSticker.mode.changelings + var/list/lings = get_antagonists(/datum/antagonist/changeling,TRUE) + var/ling_count = lings.len for(var/datum/mind/M in SSticker.minds) - if(M in SSticker.mode.changelings) + if(M in lings) continue if(department_head in get_department_heads(M.assigned_role)) if(ling_count) @@ -723,13 +727,13 @@ GLOBAL_LIST_EMPTY(possible_items_special) //Needed heads is between min_lings and the maximum possible amount of command roles //So at the time of writing, rand(3,6), it's also capped by the amount of lings there are //Because you can't fill 6 head roles with 3 lings - + var/list/lings = get_antagonists(/datum/antagonist/changeling,TRUE) var/needed_heads = rand(min_lings,GLOB.command_positions.len) - needed_heads = min(SSticker.mode.changelings.len,needed_heads) + needed_heads = min(lings.len,needed_heads) var/list/heads = SSjob.get_living_heads() for(var/datum/mind/head in heads) - if(head in SSticker.mode.changelings) //Looking at you HoP. + if(head in lings) //Looking at you HoP. continue if(needed_heads) department_minds += head @@ -789,7 +793,7 @@ GLOBAL_LIST_EMPTY(possible_items_special) //Check each department member's mind to see if any of them made it to centcom alive, if they did it's an automatic fail for(var/datum/mind/M in department_minds) - if(M in SSticker.mode.changelings) //Lings aren't picked for this, but let's be safe + if(M.has_antag_datum(/datum/antagonist/changeling)) //Lings aren't picked for this, but let's be safe continue if(M.current) @@ -800,7 +804,7 @@ GLOBAL_LIST_EMPTY(possible_items_special) //Check each staff member has been replaced, by cross referencing changeling minds, changeling current dna, the staff minds and their original DNA names var/success = 0 changelings: - for(var/datum/mind/changeling in SSticker.mode.changelings) + for(var/datum/mind/changeling in get_antagonists(/datum/antagonist/changeling,TRUE)) if(success >= department_minds.len) //We did it, stop here! return 1 if(ishuman(changeling.current)) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 865a7f4efa..9b35f06acc 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -162,11 +162,6 @@ var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) - if(clonemind.changeling) - var/obj/item/organ/brain/B = H.getorganslot(ORGAN_SLOT_BRAIN) - B.vital = FALSE - B.decoy_override = TRUE - H.hardset_dna(ui, se, H.real_name, null, mrace, features) if(efficiency > 2) diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 26bfb80860..fa6e184ab4 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -442,12 +442,15 @@ dat += "PM" dat += "" - if(SSticker.mode.changelings.len > 0) + + var/list/lings = get_antagonists(/datum/antagonist/changeling) + if(lings.len > 0) dat += "
" - for(var/datum/mind/changeling in SSticker.mode.changelings) + for(var/datum/mind/changeling in lings) + var/datum/antagonist/changeling/lingantag = changeling.has_antag_datum(/datum/antagonist/changeling) var/mob/M = changeling.current if(M) - dat += "" + dat += "" dat += "" dat += "" dat += "" @@ -607,20 +610,6 @@ dat += "" dat += "
Changelings
[M.mind.changeling.changelingID] as [M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]
[lingantag.changelingID]([lingantag.name]) as [M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]PMFLWShow Objective
PM
" - - var/list/pirates = get_antagonists(/datum/antagonist/pirate) - if(pirates.len > 0) - dat += "
" - for(var/datum/mind/N in pirates) - var/mob/M = N.current - if(!M) - dat += "" - dat += "" - else - dat += "" - dat += "" - dat += "" - dat += "
Pirates
[N.name]([N.key])No body.PM
[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]PMFLW
" if(istype(SSticker.mode, /datum/game_mode/monkey)) var/datum/game_mode/monkey/mode = SSticker.mode diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index e8e643595d..9923e19803 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -106,8 +106,6 @@ /obj/item/clothing/Destroy() - if(isliving(loc)) - dropped(loc) if(pockets) qdel(pockets) pockets = null diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index a3642684db..55f0c0b681 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -23,7 +23,7 @@ name = "brain" - if(C.mind && C.mind.changeling && !no_id_transfer) //congrats, you're trapped in a body you don't control + if(C.mind && C.mind.has_antag_datum(/datum/antagonist/changeling) && !no_id_transfer) //congrats, you're trapped in a body you don't control if(brainmob && !(C.stat == DEAD || (C.status_flags & FAKEDEATH))) to_chat(brainmob, "You can't feel your body! You're still just a brain!") loc = C diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 6cb5436df8..95a134d38f 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -770,7 +770,7 @@ /mob/living/carbon/can_be_revived() . = ..() - if(!getorgan(/obj/item/organ/brain) && (!mind || !mind.changeling)) + if(!getorgan(/obj/item/organ/brain) && (!mind || !mind.has_antag_datum(/datum/antagonist/changeling))) return 0 /mob/living/carbon/harvest(mob/living/user) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index a27c9a1eb9..7d4c62d9e1 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -60,9 +60,10 @@ stat("Chemicals", B.chemicals) if(mind) - if(mind.changeling) - stat("Chemical Storage", "[mind.changeling.chem_charges]/[mind.changeling.chem_storage]") - stat("Absorbed DNA", mind.changeling.absorbedcount) + var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling) + stat("Chemical Storage", "[changeling.chem_charges]/[changeling.chem_storage]") + stat("Absorbed DNA", changeling.absorbedcount) //NINJACODE diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 1374b391ce..c601f1445c 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -39,8 +39,10 @@ return real_name else return real_name - if(mind && mind.changeling && mind.changeling.mimicing) - return mind.changeling.mimicing + if(mind) + var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling && changeling.mimicing ) + return changeling.mimicing if(GetSpecialVoice()) return GetSpecialVoice() return real_name diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index f69a1973b3..f9b13dd24f 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -251,12 +251,14 @@ if(stat != DEAD) D.stage_act() +//todo generalize this and move hud out /mob/living/carbon/proc/handle_changeling() if(mind && hud_used && hud_used.lingchemdisplay) - if(mind.changeling) - mind.changeling.regenerate(src) + var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling) + changeling.regenerate() hud_used.lingchemdisplay.invisibility = 0 - hud_used.lingchemdisplay.maptext = "
[round(mind.changeling.chem_charges)]
" + hud_used.lingchemdisplay.maptext = "
[round(changeling.chem_charges)]
" else hud_used.lingchemdisplay.invisibility = INVISIBILITY_ABSTRACT diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index 779e7f2f90..7c65e4a604 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -74,9 +74,10 @@ stat(null, "Intent: [a_intent]") stat(null, "Move Mode: [m_intent]") if(client && mind) - if(mind.changeling) - stat("Chemical Storage", "[mind.changeling.chem_charges]/[mind.changeling.chem_storage]") - stat("Absorbed DNA", mind.changeling.absorbedcount) + var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling) + stat("Chemical Storage", "[changeling.chem_charges]/[changeling.chem_storage]") + stat("Absorbed DNA", changeling.absorbedcount) return diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index c141460731..a2fdb9d2b0 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -336,8 +336,9 @@ GLOBAL_LIST_INIT(department_radio_keys, list( if(prob(40)) to_chat(M, "We can faintly sense an outsider trying to communicate through the hivemind...") if(2) - var/msg = "[mind.changeling.changelingID]: [message]" - log_talk(src,"[mind.changeling.changelingID]/[key] : [message]",LOGSAY) + var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling) + var/msg = "[changeling.changelingID]: [message]" + log_talk(src,"[changeling.changelingID]/[key] : [message]",LOGSAY) for(var/_M in GLOB.mob_list) var/mob/M = _M if(M in GLOB.dead_mob_list) @@ -413,10 +414,12 @@ GLOBAL_LIST_INIT(department_radio_keys, list( return 0 /mob/living/lingcheck() //1 is ling w/ no hivemind. 2 is ling w/hivemind. 3 is ling victim being linked into hivemind. - if(mind && mind.changeling) - if(mind.changeling.changeling_speak) - return 2 - return 1 + if(mind) + var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling) + if(changeling.changeling_speak) + return 2 + return 1 if(mind && mind.linglink) return 3 return 0 diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index 1829a85344..222369dd55 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -486,7 +486,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians if(guardians.len && !allowmultiple) to_chat(user, "You already have a [mob_name]!") return - if(user.mind && user.mind.changeling && !allowling) + if(user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling) && !allowling) to_chat(user, "[ling_failure]") return if(used == TRUE) diff --git a/code/modules/mob/living/simple_animal/hostile/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm index 2839ac5ea0..831cd471d3 100644 --- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm +++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm @@ -74,12 +74,13 @@ if(origin && origin.current && (origin.current.stat == DEAD)) origin.transfer_to(M) - if(!origin.changeling) - M.make_changeling() - if(origin.changeling.can_absorb_dna(M, owner)) - origin.changeling.add_new_profile(owner, M) + var/datum/antagonist/changeling/C = origin.has_antag_datum(/datum/antagonist/changeling) + if(!C) + C = origin.add_antag_datum(/datum/antagonist/changeling/xenobio) + if(C.can_absorb_dna(owner)) + C.add_new_profile(owner) - origin.changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null) + C.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null) M.key = origin.key owner.gib() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 0e49c8f6d4..fed9be6d75 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -651,8 +651,9 @@ if(mind) add_spells_to_statpanel(mind.spell_list) - if(mind.changeling) - add_stings_to_statpanel(mind.changeling.purchasedpowers) + var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling) + add_stings_to_statpanel(changeling.purchasedpowers) add_spells_to_statpanel(mob_spell_list) /mob/proc/add_spells_to_statpanel(list/spells) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 1aa162d92e..30bdaa6716 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -365,7 +365,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp if(M.mind in SSticker.mode.syndicates) return 2 if("changeling") - if(M.mind in SSticker.mode.changelings) + if(M.mind.has_antag_datum(/datum/antagonist/changeling,TRUE)) return 2 if("wizard") if(iswizard(M)) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 18ca605965..51ad83e758 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -94,8 +94,9 @@ if(mind) mind.transfer_to(O) - if(O.mind.changeling) - O.mind.changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null) + var/datum/antagonist/changeling/changeling = O.mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling) + changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null) for(var/X in internal_organs) var/obj/item/organ/I = X @@ -118,7 +119,7 @@ for(var/X in O.internal_organs) var/obj/item/organ/G = X if(BP.body_zone == check_zone(G.zone)) - if(mind && mind.changeling && istype(G, /obj/item/organ/brain)) + if(mind && mind.has_antag_datum(/datum/antagonist/changeling) && istype(G, /obj/item/organ/brain)) continue //so headless changelings don't lose their brain when transforming qdel(G) //we lose the organs in the missing limbs qdel(BP) @@ -126,8 +127,9 @@ //transfer mind if we didn't yet if(mind) mind.transfer_to(O) - if(O.mind.changeling) - O.mind.changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null) + var/datum/antagonist/changeling/changeling = O.mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling) + changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null) if (tr_flags & TR_DEFAULTMSG) @@ -248,9 +250,10 @@ if(mind) mind.transfer_to(O) - if(O.mind.changeling) - for(var/obj/effect/proc_holder/changeling/humanform/HF in O.mind.changeling.purchasedpowers) - O.mind.changeling.purchasedpowers -= HF + var/datum/antagonist/changeling/changeling = O.mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling) + for(var/obj/effect/proc_holder/changeling/humanform/HF in changeling.purchasedpowers) + changeling.purchasedpowers -= HF for(var/X in internal_organs) var/obj/item/organ/I = X @@ -274,16 +277,17 @@ for(var/X in O.internal_organs) var/obj/item/organ/G = X if(BP.body_zone == check_zone(G.zone)) - if(mind && mind.changeling && istype(G, /obj/item/organ/brain)) + if(mind && mind.has_antag_datum(/datum/antagonist/changeling) && istype(G, /obj/item/organ/brain)) continue //so headless changelings don't lose their brain when transforming qdel(G) //we lose the organs in the missing limbs qdel(BP) if(mind) mind.transfer_to(O) - if(O.mind.changeling) - for(var/obj/effect/proc_holder/changeling/humanform/HF in O.mind.changeling.purchasedpowers) - O.mind.changeling.purchasedpowers -= HF + var/datum/antagonist/changeling/changeling = O.mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling) + for(var/obj/effect/proc_holder/changeling/humanform/HF in changeling.purchasedpowers) + changeling.purchasedpowers -= HF O.a_intent = INTENT_HELP if (tr_flags & TR_DEFAULTMSG) diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index cc91819611..9321c6b015 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -957,9 +957,11 @@ All effects don't start immediately, but rather get worse over time; the rate is glass_desc = "A stingy drink." /datum/reagent/consumable/ethanol/changelingsting/on_mob_life(mob/living/M) - if(M.mind && M.mind.changeling) //Changeling Sting assists in the recharging of changeling chemicals. - M.mind.changeling.chem_charges += metabolization_rate - M.mind.changeling.chem_charges = Clamp(M.mind.changeling.chem_charges, 0, M.mind.changeling.chem_storage) + if(M.mind) //Changeling Sting assists in the recharging of changeling chemicals. + var/datum/antagonist/changeling/changeling = M.mind.has_antag_datum(/datum/antagonist/changeling) + if(changeling) + changeling.chem_charges += metabolization_rate + changeling.chem_charges = Clamp(changeling.chem_charges, 0, changeling.chem_storage) return ..() /datum/reagent/consumable/ethanol/irishcarbomb diff --git a/tgstation.dme b/tgstation.dme index 4a605cbd1a..b68ee744a2 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -318,6 +318,7 @@ #include "code\datums\antagonists\abductor.dm" #include "code\datums\antagonists\antag_datum.dm" #include "code\datums\antagonists\brother.dm" +#include "code\datums\antagonists\changeling.dm" #include "code\datums\antagonists\clockcult.dm" #include "code\datums\antagonists\cult.dm" #include "code\datums\antagonists\datum_traitor.dm" @@ -483,7 +484,6 @@ #include "code\game\gamemodes\changeling\cellular_emporium.dm" #include "code\game\gamemodes\changeling\changeling.dm" #include "code\game\gamemodes\changeling\changeling_power.dm" -#include "code\game\gamemodes\changeling\evolution_menu.dm" #include "code\game\gamemodes\changeling\traitor_chan.dm" #include "code\game\gamemodes\changeling\powers\absorb.dm" #include "code\game\gamemodes\changeling\powers\adrenaline.dm"