diff --git a/code/__defines/_reagents.dm b/code/__defines/_reagents.dm index fb2a736b2b..d2257c0706 100644 --- a/code/__defines/_reagents.dm +++ b/code/__defines/_reagents.dm @@ -368,6 +368,8 @@ #define REAGENT_ID_SUPERMATTER "supermatter" #define REAGENT_ADRENALINE "Adrenaline" #define REAGENT_ID_ADRENALINE "adrenaline" +#define REAGENT_EPINEPHRINE "Epinephrine" +#define REAGENT_ID_EPINEPHRINE "epinephrine" #define REAGENT_HOLYWATER "Holy Water" #define REAGENT_ID_HOLYWATER "holywater" #define REAGENT_AMMONIA "Ammonia" diff --git a/code/__defines/changeling.dm b/code/__defines/changeling.dm new file mode 100644 index 0000000000..463589656b --- /dev/null +++ b/code/__defines/changeling.dm @@ -0,0 +1,5 @@ +#define CRYO_STING "cryo_sting" +#define ESCAPE_RESTRAINTS "escape_restraints" +#define CHANGELING_SCREECH "changeling_screech" //Used for all shriek powers. +#define FAKE_DEATH "fake_death" +#define FLESHMEND "flesh_mend" diff --git a/code/datums/components/antags/antag.dm b/code/datums/components/antags/antag.dm new file mode 100644 index 0000000000..4f4335efb1 --- /dev/null +++ b/code/datums/components/antags/antag.dm @@ -0,0 +1,31 @@ +///Component that holds the antag trait. This is the base version. +/datum/component/antag + var/mob/living/owner + dupe_mode = COMPONENT_DUPE_UNIQUE //Only one type. + +/datum/component/antag/Initialize() + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE + owner = parent + +///Should never be destroyed as these are applied to the mind. +/datum/component/antag/Destroy(force = FALSE) + if(!force) + return QDEL_HINT_LETMELIVE + owner = null + . = ..() + +///Antag datum that is held on /datum/mind. Holds all the antag data. +/datum/antag_holder + var/datum/component/antag/changeling/changeling + var/is_antag = FALSE + +/datum/antag_holder/proc/apply_antags(mob/M) + if(!M) + return + if(changeling) + M.make_changeling() + is_antag = TRUE + +/datum/antag_holder/proc/is_antag() + return is_antag diff --git a/code/datums/components/antags/changeling/changeling.dm b/code/datums/components/antags/changeling/changeling.dm new file mode 100644 index 0000000000..65aa680e1e --- /dev/null +++ b/code/datums/components/antags/changeling/changeling.dm @@ -0,0 +1,474 @@ +///Changeling component. +///Stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) +GLOBAL_LIST_INIT(possible_changeling_IDs,list("Alpha","Beta","Chi","Delta","Epsilon","Eta","Gamma","Iota","Kappa","Lambda","Mu","Nu","Omega","Omicron","Phi","Pi","Psi","Rho","Sigma","Tau","Theta","Upsilon","Xi","Zeta")) //ALPHABETICAL ORDER. +//Needs cleanup +var/list/powers = subtypesof(/datum/power/changeling) //needed for the badmin verb for now +var/list/datum/power/changeling/powerinstances = list() +/datum/power //Could be used by other antags too + var/name = "Power" + var/desc = "Placeholder" + var/helptext = "" + var/enhancedtext = "" + var/isVerb = 1 // Is it an active power, or passive? + var/verbpath // Path to a verb that contains the effects. + var/make_hud_button = TRUE // Is this ability significant enough to dedicate screen space for a HUD button? + var/ability_icon_state = null // icon_state for icons for the ability HUD. Must be in screen_spells.dmi. + +/datum/power/changeling + var/allowduringlesserform = FALSE + var/genomecost = 500000 // Cost for the changeling to evolve this power. + +/datum/component/antag/changeling + var/list/datum/absorbed_dna/absorbed_dna = list() + var/list/absorbed_languages = list() // Necessary because of set_species stuff + var/absorbedcount = 0 + var/lingabsorbedcount = 1 //Starts at one, because that's us + var/chem_charges = 20 + var/chem_recharge_rate = 0.5 + var/chem_storage = 50 + var/sting_range = 1 + var/changelingID = "Changeling" + var/geneticdamage = 0 + var/isabsorbing = FALSE + var/geneticpoints = 7 + var/max_geneticpoints = 7 + var/readapts = 1 + var/max_readapts = 2 + var/list/purchased_powers = list() + var/mimicing = "" + var/cloaked = FALSE + var/is_reviving = FALSE + var/armor_deployed = FALSE //This is only used for changeling_generic_equip_all_slots() at the moment. + var/recursive_enhancement = FALSE //Used to power up other abilities from the ling power with the same name. + var/list/purchased_powers_history = list() //Used for round-end report, includes respec uses too. + var/thermal_sight = FALSE // Is our Vision Augmented? With thermals? + var/datum/changeling_panel/power_panel //Our changeling eveolution panel. Generated the first time we try to open the panel. + dupe_mode = COMPONENT_DUPE_UNIQUE //Only the first changeling application survives! + var/cooldown_time = 1 SECOND // Sting anti-spam. + var/last_used_sting_time = 0 // world.time when we used last used a power. + var/list/changeling_cooldowns = list( + CRYO_STING = 0, + ESCAPE_RESTRAINTS = 0, + FAKE_DEATH = 0, + FLESHMEND = 0, + CHANGELING_SCREECH = 0 + ) + +///Checks if a mind or a mob is a changeling. +///Checks to see if the thing fed to it is a changeling first, then does some deeper searching. +/proc/is_changeling(mob/M) + var/datum/component/antag/changeling/changeling = (M.GetComponent(/datum/component/antag/changeling)) + if(changeling) // Whatever we fed it is a changeling. Return it. + return changeling + + //The below is what happens if we fail the above. We do some deeper searching. + if(istype(M, /datum/mind)) //Fed a mind and we failed. + var/datum/mind/our_mind = M + if(our_mind.current) + changeling = (our_mind.current.GetComponent(/datum/component/antag/changeling)) //Check to see if the mob we are currently inhabiting is a changeling. + else //Fed it a mob and we failed + if(M.mind) + changeling = M.mind.antag_holder.changeling //Check our mind's antag holder. + return changeling + + +///Handles the cooldown for the power. Returns TRUE if the cooldown has passed. FALSE if it's still on cooldown. +///This is just a general anti-spam thing and not really a true cooldown +/datum/component/antag/changeling/proc/handle_cooldown() + if(world.time > last_used_sting_time+cooldown_time) + last_used_sting_time = world.time + return TRUE + return FALSE + +/datum/component/antag/changeling/proc/get_cooldown(id) + return changeling_cooldowns[id] + +/datum/component/antag/changeling/proc/set_cooldown(id, cooldown_time) + changeling_cooldowns[id] = world.time + cooldown_time + +/datum/component/antag/changeling/proc/is_on_cooldown(id) + return (world.time < changeling_cooldowns[id]) + +/datum/component/antag/changeling/Initialize() + ..() + if(owner) + if(GLOB.possible_changeling_IDs.len) + changelingID = pick(GLOB.possible_changeling_IDs) + GLOB.possible_changeling_IDs -= changelingID + changelingID = "[changelingID]" + else + changelingID = "[rand(1,999)]" + + add_verb(owner,/mob/proc/EvolutionMenu) + add_verb(owner,/mob/proc/changeling_respec) + owner.add_language("Changeling") + +///This is a component that is referenced to by the mind, so it should never be deleted +/datum/component/antag/changeling/Destroy(force = FALSE) + if(!force) + return QDEL_HINT_LETMELIVE + return ..() + //Old code from when it did destroy itself. + /* + if(owner) + remove_verb(owner,/mob/proc/EvolutionMenu) + remove_verb(owner,/mob/proc/changeling_respec) + qdel_null(power_panel) + absorbed_dna.Cut() + absorbed_languages.Cut() + purchased_powers.Cut() + purchased_powers_history.Cut() + . = ..() + */ + +//Former /datum/changeling procs +/datum/component/antag/changeling/proc/regenerate() + chem_charges = min(max(0, chem_charges+chem_recharge_rate), chem_storage) + geneticdamage = max(0, geneticdamage-1) + +/datum/component/antag/changeling/proc/GetDNA(var/dna_owner) + for(var/datum/absorbed_dna/DNA in absorbed_dna) + if(dna_owner == DNA.name) + return DNA + +//Former /mob procs +/mob/proc/absorbDNA(var/datum/absorbed_dna/newDNA) + var/datum/component/antag/changeling/comp = is_changeling(src) + if(!comp) + return + + + for(var/language in newDNA.languages) + comp.absorbed_languages |= language + + changeling_update_languages(comp.absorbed_languages) + + if(!comp.GetDNA(newDNA.name)) // Don't duplicate - I wonder if it's possible for it to still be a different DNA? DNA code could use a rewrite + comp.absorbed_dna += newDNA + +//Restores our verbs. It will only restore verbs allowed during lesser (monkey) form if we are not human +/mob/proc/make_changeling() + + if(!mind) + return + //The current mob is made a changeling AND the mind is made a changeling. + var/datum/component/antag/changeling/comp = LoadComponent(/datum/component/antag/changeling) + mind.antag_holder.changeling = comp + var/lesser_form = !ishuman(src) + + if(!powerinstances.len) + for(var/P in powers) + powerinstances += new P() + + // Code to auto-purchase free powers. + for(var/datum/power/changeling/P in powerinstances) + if(!P.genomecost) // Is it free? + if(!(P in comp.purchased_powers)) // Do we not have it already? + comp.purchasePower(comp.owner, P.name, 0)// Purchase it. Don't remake our verbs, we're doing it after this. + + for(var/datum/power/changeling/P in comp.purchased_powers) + if(P.isVerb) + if(lesser_form && !P.allowduringlesserform) + continue + if(!(P in src.verbs)) + add_verb(src, P.verbpath) + if(P.make_hud_button) + if(!src.ability_master) + src.ability_master = new /obj/screen/movable/ability_master(src) + src.ability_master.add_ling_ability( + object_given = src, + verb_given = P.verbpath, + name_given = P.name, + ability_icon_given = P.ability_icon_state, + arguments = list() + ) + + for(var/language in languages) + comp.absorbed_languages |= language + + var/mob/living/carbon/human/H = src + if(istype(H)) + add_verb(H, /mob/living/carbon/human/proc/innate_shapeshifting) + var/saved_dna = H.dna.Clone() /// Prevent transform from breaking. + var/datum/absorbed_dna/newDNA = new(H.real_name, saved_dna, H.species.name, H.languages, H.identifying_gender, H.flavor_texts, H.modifiers) + absorbDNA(newDNA) + + //Code to make it so our BR is marked as a changeling body, so it can't be stolen. + for(var/key in SStranscore.databases) + var/datum/transcore_db/db = SStranscore.databases[key] + if(H.mind.name in db.body_scans) + var/datum/transhuman/body_record/BR = db.body_scans[H.mind.name] + BR.changeling_locked = TRUE + + return TRUE + +//removes our changeling verbs +/mob/proc/remove_changeling_powers() + if(!mind) + return + var/datum/component/antag/changeling/comp = is_changeling(src) + if(!comp) + return + for(var/datum/power/changeling/P in comp.purchased_powers) + if(P.isVerb) + remove_verb(src, P.verbpath) + var/obj/screen/ability/verb_based/changeling/C = ability_master.get_ability_by_proc_ref(P.verbpath) + if(C) + ability_master.remove_ability(C) + + +//Helper proc. Does all the checks and stuff for us to avoid copypasta +/mob/proc/changeling_power(var/required_chems=0, var/required_dna=0, var/max_genetic_damage=100, var/max_stat=0) + + if(!src.mind) return + if(!iscarbon(src)) return + + var/datum/component/antag/changeling/comp = is_changeling(src) + if(!comp) + to_world_log("[src] used a changeling verb but is not a changeling.") + return + + if(src.stat > max_stat) + to_chat(src, span_warning("We are incapacitated.")) + return + + if(comp.absorbed_dna.len < required_dna) + to_chat(src, span_warning("We require at least [required_dna] samples of compatible DNA.")) + return + + if(comp.chem_charges < required_chems) + to_chat(src, span_warning("We require at least [required_chems] units of chemicals to do that!")) + return + + if(comp.geneticdamage > max_genetic_damage) + to_chat(src, span_warning("Our genomes are still reassembling. We need time to recover first.")) + return + + return comp + +//Used to dump the languages from the changeling datum into the actual mob. +/mob/proc/changeling_update_languages(var/updated_languages) + languages = list() + for(var/language in updated_languages) + languages += language + + //This isn't strictly necessary but just to be safe... + add_language("Changeling") + + ////////// + //STINGS// //They get a pretty header because there's just so fucking many of them ;_; + ////////// + +/mob/proc/sting_can_reach(mob/M as mob, sting_range = 1) + if(M.loc == src.loc) + return 1 //target and source are in the same thing + if(!isturf(src.loc) || !isturf(M.loc)) + to_chat(src, span_warning("We cannot reach \the [M] with a sting!")) + return 0 //One is inside, the other is outside something. + // Maximum queued turfs set to 25; I don't *think* anything raises sting_range above 2, but if it does the 25 may need raising + if(!AStar(src.loc, M.loc, /turf/proc/AdjacentTurfsRangedSting, /turf/proc/Distance, max_nodes=25, max_node_depth=sting_range)) //If we can't find a path, fail + to_chat(src, span_warning("We cannot find a path to sting \the [M] by!")) + return 0 + return 1 + +//Handles the general sting code to reduce on copypasta (seeming as somebody decided to make SO MANY dumb abilities) +/mob/proc/changeling_sting(var/required_chems=0, var/verb_path) + var/datum/component/antag/changeling/comp = changeling_power(required_chems) + if(!comp) + return + if(!comp.handle_cooldown()) + to_chat(src, span_warning("We are still recovering from our last sting.")) + return + + var/list/victims = list() + for(var/mob/living/carbon/C in oview(comp.sting_range)) + victims += C + var/mob/living/carbon/T = tgui_input_list(src, "Who will we sting?", "Sting!", victims) + + if(!T) + return + if(!comp.handle_cooldown())//Check again in case we have multiple windows open at once. + to_chat(src, span_warning("We are still recovering from our last sting.")) + return + if(T.isSynthetic()) + to_chat(src, span_notice("We are unable to pierce the outer shell of [T].")) + return + if(!(T in view(comp.sting_range))) return + if(!sting_can_reach(T, comp.sting_range)) return + if(!changeling_power(required_chems)) return + + comp.chem_charges -= required_chems + comp.sting_range = 1 + + to_chat(src, span_notice("We stealthily sting [T].")) + var/datum/component/antag/changeling/target_comp = is_changeling(T) + if(!T.mind || !target_comp) + return T //T will be affected by the sting + to_chat(T, span_warning("You feel a tiny prick.")) //Stings on other lings have no effect, but they know you're a ling, too. + + return + +//Former /turf procs +/turf/proc/AdjacentTurfsRangedSting() + //Yes this is snowflakey, but I couldn't get it to work any other way.. -Luke + var/list/allowed = list( + /obj/structure/table, + /obj/structure/closet, + /obj/structure/frame, + /obj/structure/target_stake, + /obj/structure/cable, + /obj/structure/disposalpipe, + /obj/machinery, + /mob + ) + + var/L[] = new() + for(var/turf/simulated/t in oview(src,1)) + var/add = 1 + if(t.density) + add = 0 + if(add && LinkBlocked(src,t)) + add = 0 + if(add && TurfBlockedNonWindow(t)) + add = 0 + for(var/obj/O in t) + if(O.density) + add = 0 + break + if(istype(O, /obj/machinery/door)) + //not sure why this doesn't fire on LinkBlocked() + add = 0 + break + for(var/type in allowed) + if (istype(O, type)) + add = 1 + break + if(!add) + break + if(add) + L.Add(t) + return L + +/mob/proc/EvolutionMenu() + set name = "-Evolution Menu-" + set category = "Changeling" + set desc = "Adapt yourself carefully." + + var/datum/component/antag/changeling/comp = is_changeling(src) + if(!comp) + to_chat(src, "You are not a changeling!") + return + if(!powerinstances.len) + for(var/changeling_power in powers) + powerinstances += new changeling_power() + if(!comp.power_panel) + comp.power_panel = new() + comp.power_panel.comp = comp + + comp.power_panel.tgui_interact(src) + +///Purchasing a power. Called by the Evolution Panel. +/datum/component/antag/changeling/proc/purchasePower(var/mob/owner, var/Pname, var/remake_verbs = 1) + + var/datum/power/changeling/Thepower = Pname + + for (var/datum/power/changeling/P in powerinstances) + //to_world("[P] - [Pname] = [P.name == Pname ? "True" : "False"]") + if(P.name == Pname) + Thepower = P + break + + + if(Thepower == null) + to_chat(owner, "This is awkward. Changeling power purchase failed, please report this bug to a coder!") + return + + if(Thepower in purchased_powers) + to_chat(owner, "We have already evolved this ability!") + return + + + if(geneticpoints < Thepower.genomecost) + to_chat(owner, "We cannot evolve this... yet. We must acquire more DNA.") + return + + geneticpoints -= Thepower.genomecost + + purchased_powers += Thepower + + if(Thepower.genomecost > 0) + purchased_powers_history.Add("[Pname] ([Thepower.genomecost] points)") + + if(Thepower.make_hud_button && Thepower.isVerb) + if(owner.ability_master) + owner.ability_master = new /obj/screen/movable/ability_master(owner) + owner.ability_master.add_ling_ability( + object_given = owner, + verb_given = Thepower.verbpath, + name_given = Thepower.name, + ability_icon_given = Thepower.ability_icon_state, + arguments = list() + ) + + if(!Thepower.isVerb && Thepower.verbpath) + call(owner, Thepower.verbpath)() + else if(remake_verbs) + owner.make_changeling() + + +//Debug item. Here because during debugging I DO NOT want to have to open the player panel 5000 times. +/obj/item/toy/katana/changeling_debug + name = "Katana of the Changeling" + desc = "A katana imbued with special powers. It is said that those who wield it will become a changeling." +/obj/item/toy/katana/changeling_debug/attack_self(mob/user) + user.make_changeling() + +///Changeling Panel +/datum/changeling_panel + var/datum/component/antag/changeling/comp + +/datum/changeling_panel/Destroy(force) + comp = null + . = ..() + +/datum/changeling_panel/tgui_state(mob/user) + return GLOB.tgui_always_state + +/datum/changeling_panel/tgui_status(mob/user) + if(!isliving(user)) //We ghosted or something. + return STATUS_CLOSE + return ..() + +/datum/changeling_panel/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src,"ChangelingPanel", "Changeling Evolution Panel", parent_ui) + ui.open() + +/datum/changeling_panel/tgui_data(mob/living/carbon/human/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = list() + var/list/power_list = list() + + for(var/datum/power/changeling/P in powerinstances) + var/list/all_powers = list( + "power_name" = P.name, + "power_cost" = P.genomecost, + "power_purchased" = (P in comp.purchased_powers), + "power_desc" = P.desc, + ) + UNTYPED_LIST_ADD(power_list, all_powers) + + data["available_points"] = comp.geneticpoints + data["power_list"] = power_list + + return data + +/datum/changeling_panel/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("evolve_power") + comp.purchasePower(comp.owner, params["val"]) //The power must be the power's NAME. + return TRUE + return TRUE diff --git a/code/game/gamemodes/changeling/absorbed_dna.dm b/code/datums/components/antags/changeling/helpers/absorbed_dna.dm similarity index 100% rename from code/game/gamemodes/changeling/absorbed_dna.dm rename to code/datums/components/antags/changeling/helpers/absorbed_dna.dm diff --git a/code/game/gamemodes/changeling/generic_equip_procs.dm b/code/datums/components/antags/changeling/helpers/generic_equip_procs.dm similarity index 93% rename from code/game/gamemodes/changeling/generic_equip_procs.dm rename to code/datums/components/antags/changeling/helpers/generic_equip_procs.dm index 21d0b5b73b..cc8ad5a68c 100644 --- a/code/game/gamemodes/changeling/generic_equip_procs.dm +++ b/code/datums/components/antags/changeling/helpers/generic_equip_procs.dm @@ -9,7 +9,7 @@ if(istype(M.wear_suit, armor_type) || istype(M.head, helmet_type) || istype(M.shoes, boot_type)) chem_cost = 0 - var/datum/changeling/changeling = changeling_power(chem_cost, 1, 100, CONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(chem_cost, 1, 100, CONSCIOUS) if(!changeling) return @@ -44,7 +44,7 @@ var/obj/item/clothing/shoes/B = new boot_type(src) src.equip_to_slot_or_del(B, slot_shoes) - src.mind.changeling.chem_charges -= chem_cost + changeling.chem_charges -= chem_cost playsound(src, 'sound/effects/blobattack.ogg', 30, 1) M.update_inv_wear_suit() M.update_inv_head() @@ -53,7 +53,7 @@ return 1 /mob/proc/changeling_generic_equip_all_slots(var/list/stuff_to_equip, var/cost) - var/datum/changeling/changeling = changeling_power(cost,1,100,CONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(cost,1,100,CONSCIOUS) if(!changeling) return @@ -66,7 +66,7 @@ //First, check if we're already wearing the armor, and if so, take it off. - if(M.mind.changeling.armor_deployed) + if(changeling.armor_deployed) if(M.head && stuff_to_equip["head"]) if(istype(M.head, stuff_to_equip["head"])) qdel(M.head) @@ -122,7 +122,7 @@ playsound(src, 'sound/effects/splat.ogg', 30, 1) visible_message(span_warning("[src] pulls on their clothes, peeling it off along with parts of their skin attached!"), span_notice("We remove and deform our equipment.")) - M.mind.changeling.armor_deployed = 0 + changeling.armor_deployed = 0 return success else @@ -226,13 +226,13 @@ to_chat(M, span_notice("We have grown [feedback].")) if(success) - M.mind.changeling.armor_deployed = 1 - M.mind.changeling.chem_charges -= 10 + changeling.armor_deployed = 1 + changeling.chem_charges -= 10 return success //This is a generic proc that should be called by other ling weapon procs to equip them. /mob/proc/changeling_generic_weapon(var/weapon_type, var/make_sound = 1, var/cost = 20) - var/datum/changeling/changeling = changeling_power(cost,1,100,CONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(cost,1,100,CONSCIOUS) if(!changeling) return @@ -248,7 +248,7 @@ var/obj/item/W = new weapon_type(src) src.put_in_hands(W) - src.mind.changeling.chem_charges -= cost + changeling.chem_charges -= cost if(make_sound) playsound(src, 'sound/effects/blobattack.ogg', 30, 1) return 1 diff --git a/code/game/gamemodes/changeling/powers/absorb.dm b/code/datums/components/antags/changeling/powers/absorb.dm similarity index 52% rename from code/game/gamemodes/changeling/powers/absorb.dm rename to code/datums/components/antags/changeling/powers/absorb.dm index f5ca7e53fb..451c5a889b 100644 --- a/code/game/gamemodes/changeling/powers/absorb.dm +++ b/code/datums/components/antags/changeling/powers/absorb.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/absorb_dna name = "Absorb DNA" desc = "Permits us to syphon the DNA from a human. They become one with us, and we become stronger if they were of our kind." @@ -11,7 +12,7 @@ set category = "Changeling" set name = "Absorb DNA" - var/datum/changeling/changeling = changeling_power(0,0,100) + var/datum/component/antag/changeling/changeling = changeling_power(0,0,100) //Our changeling power if(!changeling) return var/obj/item/grab/G = src.get_active_hand() @@ -28,8 +29,10 @@ to_chat(src, span_warning("We do not know how to parse this creature's DNA!")) return + var/datum/component/antag/changeling/target_changeling = is_changeling(T) //If the target is a changeling + if(HUSK in T.mutations) //Lings can always absorb other lings, unless someone beat them to it first. - if(!T.mind.changeling || T.mind.changeling && T.mind.changeling.geneticpoints < 0) + if(!target_changeling || target_changeling && target_changeling.geneticpoints < 0) to_chat(src, span_warning("This creature's DNA is ruined beyond useability!")) return @@ -41,7 +44,7 @@ to_chat(src, span_warning("We are already absorbing!")) return - changeling.isabsorbing = 1 + changeling.isabsorbing = TRUE for(var/stage = 1, stage<=3, stage++) switch(stage) if(1) @@ -53,62 +56,87 @@ to_chat(src, span_notice("We stab [T] with the proboscis.")) src.visible_message(span_danger("[src] stabs [T] with the proboscis!")) to_chat(T, span_danger("You feel a sharp stabbing pain!")) - add_attack_logs(src,T,"Absorbed (changeling)") var/obj/item/organ/external/affecting = T.get_organ(src.zone_sel.selecting) if(affecting.take_damage(39,0,1,0,"large organic needle")) - T:UpdateDamageIcon() + T.UpdateDamageIcon() feedback_add_details("changeling_powers","A[stage]") if(!do_mob(src, T, 150) || G.state != GRAB_KILL) to_chat(src, span_warning("Our absorption of [T] has been interrupted!")) - changeling.isabsorbing = 0 + changeling.isabsorbing = FALSE return to_chat(src, span_notice("We have absorbed [T]!")) - src.visible_message(span_danger("[src] sucks the fluids from [T]!")) + add_attack_logs(src,T,"Absorbed (changeling)") + visible_message(span_danger("[src] sucks the fluids from [T]!")) to_chat(T, span_danger("You have been absorbed by the changeling!")) - adjust_nutrition(T.nutrition) + changeling_obtain_dna(T, changeling, target_changeling) + + changeling.isabsorbing = FALSE + T.death(FALSE) + T.Drain() + return 1 + +///Proc that does the actual 'obtaining DNA' part for changelings. Has four arguments: Our victim, our changeling component, the target's changeling component, and if we drain the victim's nutrition or not. +/mob/living/proc/changeling_obtain_dna(mob/living/carbon/human/victim, datum/component/antag/changeling/changeling, datum/component/antag/changeling/target_changeling, drain = TRUE) + if(!victim || !ishuman(victim)) //There MUST be a victim and it MUST be a human and NOT a monkey! + return + if(!target_changeling) + target_changeling = is_changeling(victim) //Let's see if the victim is a changeling or not. + if(!target_changeling && isMonkey(victim)) //No absorbing non-changeling monkeys. + return + + //If we weren't fed a changeling component, we get it ourselves. If that fails, we see if the target is a changeling. If so, they get the DNA of the person predding them. Preyling! + if(!changeling) + changeling = is_changeling(src) + if(!changeling && target_changeling) //Prey is a ling but owner is not. + changeling_obtain_dna(src, target_changeling, drain = FALSE) //Flip the tables!~ Don't steal the pred's nutrition, though! + return + else if(!changeling) //Neither pred or prey are owner. + return + + var/saved_dna = victim.dna.Clone() + var/datum/absorbed_dna/newDNA = new(victim.real_name, saved_dna, victim.species.name, victim.languages, victim.identifying_gender, victim.flavor_texts, victim.modifiers) + if(changeling.GetDNA(newDNA.name)) + qdel(newDNA) + return //No double dipping! You already ate or absorbed them once this shift, glutton! + absorbDNA(newDNA) + if(drain) + adjust_nutrition(victim.nutrition) changeling.chem_charges += 10 if(changeling.readapts <= 0) changeling.readapts = 0 //SANITYYYYYY changeling.readapts++ + //Let's give them a genetic point for absorbing someone...Because honestly if you absorb people you SHOULD get stronger. + changeling.max_geneticpoints++ + changeling.geneticpoints++ if(changeling.readapts > changeling.max_readapts) changeling.readapts = changeling.max_readapts to_chat(src, span_notice("We can now re-adapt, reverting our evolution so that we may start anew, if needed.")) - var/saved_dna = T.dna.Clone() /// Prevent transforming bugginess. - var/datum/absorbed_dna/newDNA = new(T.real_name, saved_dna, T.species.name, T.languages, T.identifying_gender, T.flavor_texts, T.modifiers) - absorbDNA(newDNA) - - if(T.mind && T.mind.changeling) - if(T.mind.changeling.absorbed_dna) - for(var/datum/absorbed_dna/dna_data in T.mind.changeling.absorbed_dna) //steal all their loot + if(victim.mind && target_changeling) + if(target_changeling.absorbed_dna) + for(var/datum/absorbed_dna/dna_data in target_changeling.absorbed_dna) //steal all their loot if(dna_data in changeling.absorbed_dna) continue absorbDNA(dna_data) changeling.absorbedcount++ - T.mind.changeling.absorbed_dna.len = 1 + target_changeling.absorbed_dna.len = 1 // This is where lings get boosts from eating eachother - if(T.mind.changeling.lingabsorbedcount) - for(var/a = 1 to T.mind.changeling.lingabsorbedcount) + if(target_changeling.lingabsorbedcount) + for(var/a = 1 to target_changeling.lingabsorbedcount) changeling.lingabsorbedcount++ changeling.geneticpoints += 4 changeling.max_geneticpoints += 4 to_chat(src, span_notice("We absorbed another changeling, and we grow stronger. Our genomes increase.")) - T.mind.changeling.chem_charges = 0 - T.mind.changeling.geneticpoints = -1 - T.mind.changeling.max_geneticpoints = -1 //To prevent revival. - T.mind.changeling.absorbedcount = 0 - T.mind.changeling.lingabsorbedcount = 0 - + target_changeling.chem_charges = 0 + target_changeling.geneticpoints = -1 + target_changeling.max_geneticpoints = -1 //To prevent revival. + target_changeling.absorbedcount = 0 + target_changeling.lingabsorbedcount = 0 changeling.absorbedcount++ - changeling.isabsorbing = 0 - - T.death(0) - T.Drain() - return 1 diff --git a/code/game/gamemodes/changeling/powers/armblade.dm b/code/datums/components/antags/changeling/powers/armblade.dm similarity index 88% rename from code/game/gamemodes/changeling/powers/armblade.dm rename to code/datums/components/antags/changeling/powers/armblade.dm index fba95bbcac..6f1c1f3fc3 100644 --- a/code/game/gamemodes/changeling/powers/armblade.dm +++ b/code/datums/components/antags/changeling/powers/armblade.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/arm_blade name = "Arm Blade" desc = "We reform one of our arms into a deadly blade." @@ -12,7 +13,10 @@ set category = "Changeling" set name = "Arm Blade (20)" - if(src.mind.changeling.recursive_enhancement) + var/datum/component/antag/changeling/comp = is_changeling(src) + if(!comp) + return + if(comp.recursive_enhancement) if(changeling_generic_weapon(/obj/item/melee/changeling/arm_blade/greater)) to_chat(src, span_notice("We prepare an extra sharp blade.")) return 1 @@ -36,8 +40,11 @@ /mob/proc/changeling_claw() set category = "Changeling" set name = "Claw (15)" + var/datum/component/antag/changeling/comp = is_changeling(src) + if(!comp) + return - if(src.mind.changeling.recursive_enhancement) + if(comp.recursive_enhancement) if(changeling_generic_weapon(/obj/item/melee/changeling/claw/greater, 1, 15)) to_chat(src, span_notice("We prepare an extra sharp claw.")) return 1 @@ -58,6 +65,8 @@ throwforce = 0 //Just to be on the safe side throw_range = 0 throw_speed = 0 + embed_chance = 0 //No embedding. + destroy_on_drop = TRUE var/mob/living/creator //This is just like ninja swords, needed to make sure dumb shit that removes the sword doesn't make it stay around. var/weapType = "weapon" var/weapLocation = "arm" @@ -67,7 +76,6 @@ /obj/item/melee/changeling/Initialize(mapload) . = ..() - START_PROCESSING(SSobj, src) if(ismob(loc)) visible_message(span_warning("A grotesque weapon forms around [loc.name]\'s arm!"), span_warning("Our arm twists and mutates, transforming it into a deadly weapon."), @@ -80,32 +88,11 @@ span_notice("We assimilate the weapon back into our body."), span_warningplain("You hear organic matter ripping and tearing!")) playsound(src, 'sound/effects/blobattack.ogg', 30, 1) - spawn(1) - if(src) - qdel(src) /obj/item/melee/changeling/Destroy() - STOP_PROCESSING(SSobj, src) creator = null . = ..() -/obj/item/melee/changeling/process() //Stolen from ninja swords. - if(!creator || loc != creator || !creator.item_is_in_hands(src)) - // Tidy up a bit. - if(isliving(loc)) - var/mob/living/carbon/human/host = loc - if(istype(host)) - for(var/obj/item/organ/external/organ in host.organs) - for(var/obj/item/O in organ.implants) - if(O == src) - organ.implants -= src - host.pinned -= src - host.embedded -= src - host.drop_from_inventory(src) - spawn(1) - if(src) - qdel(src) - /obj/item/melee/changeling/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") if(default_parry_check(user, attacker, damage_source) && prob(defend_chance)) user.visible_message(span_danger("\The [user] parries [attack_text] with \the [src]!")) diff --git a/code/game/gamemodes/changeling/powers/armor.dm b/code/datums/components/antags/changeling/powers/armor.dm similarity index 95% rename from code/game/gamemodes/changeling/powers/armor.dm rename to code/datums/components/antags/changeling/powers/armor.dm index e1e0fb032a..47cc27972e 100644 --- a/code/game/gamemodes/changeling/powers/armor.dm +++ b/code/datums/components/antags/changeling/powers/armor.dm @@ -1,9 +1,10 @@ +//Updated /datum/power/changeling/space_suit name = "Organic Space Suit" desc = "We grow an organic suit to protect ourselves from space exposure." helptext = "To remove the suit, use the ability again." ability_icon_state = "ling_space_suit" - genomecost = 1 + genomecost = 0 //Have a free spacesuit. It's not worth buying. If you want a special version, get the armor. verbpath = /mob/proc/changeling_spacesuit /mob/proc/changeling_spacesuit() @@ -37,6 +38,7 @@ desc = "A huge, bulky mass of pressure and temperature-resistant organic tissue, evolved to facilitate space travel." flags = 0 //Not THICKMATERIAL because it's organic tissue, so if somebody tries to inject something into it, //it still ends up in your blood. (also balance but muh fluff) + destroy_on_drop = TRUE allowed = list(POCKET_GENERIC, POCKET_ALL_TANKS) armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) //No armor at all. canremove = FALSE @@ -48,10 +50,6 @@ span_warning("We inflate our flesh, creating a spaceproof suit!"), span_warningplain("You hear organic matter ripping and tearing!")) -/obj/item/clothing/suit/space/changeling/dropped(mob/user) - ..() - qdel(src) - /obj/item/clothing/head/helmet/space/changeling name = "flesh mass" icon_state = "lingspacehelmet" @@ -60,10 +58,7 @@ armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) body_parts_covered = HEAD|FACE|EYES canremove = FALSE - -/obj/item/clothing/head/helmet/space/changeling/dropped(mob/user) - ..() - qdel(src) + destroy_on_drop = TRUE /obj/item/clothing/shoes/magboots/changeling desc = "A suction cupped mass of flesh, shaped like a foot." @@ -71,6 +66,7 @@ icon_state = "lingspacesuit" actions_types = list(/datum/action/item_action/toggle_grippers) canremove = FALSE + destroy_on_drop = TRUE /obj/item/clothing/shoes/magboots/changeling/set_slowdown() slowdown = shoes? max(SHOES_SLOWDOWN, shoes.slowdown): SHOES_SLOWDOWN //So you can't put on magboots to make you walk faster. @@ -91,10 +87,6 @@ force = 5 to_chat(user, "We cling to the terrain below us.") -/obj/item/clothing/shoes/magboots/changeling/dropped(mob/user) - ..() - qdel(src) - //Armor /obj/item/clothing/suit/space/changeling/armored diff --git a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm b/code/datums/components/antags/changeling/powers/augmented_eyesight.dm similarity index 71% rename from code/game/gamemodes/changeling/powers/augmented_eyesight.dm rename to code/datums/components/antags/changeling/powers/augmented_eyesight.dm index 3498e5df4d..a09870b6a1 100644 --- a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm +++ b/code/datums/components/antags/changeling/powers/augmented_eyesight.dm @@ -1,5 +1,5 @@ +//Updated //Augmented Eyesight: Gives you thermal vision. Also, higher DNA cost because of how powerful it is. - /datum/power/changeling/augmented_eyesight name = "Augmented Eyesight" desc = "Creates heat receptors in our eyes and dramatically increases light sensing ability." @@ -12,26 +12,21 @@ set category = "Changeling" set name = "Augmented Eyesight (5)" set desc = "We evolve our eyes to sense the infrared." - - var/datum/changeling/changeling = changeling_power(5,0,100,CONSCIOUS) - if(!changeling) + var/datum/component/antag/changeling/comp = changeling_power(5,0,100,CONSCIOUS) + if(!comp) return 0 var/mob/living/carbon/human/C = src - changeling.thermal_sight = !changeling.thermal_sight + comp.thermal_sight = !comp.thermal_sight - var/active = changeling.thermal_sight + var/active = comp.thermal_sight if(active) - src.mind.changeling.chem_charges -= 5 + comp.chem_charges -= 5 to_chat(C, span_notice("We feel a minute twitch in our eyes, and a hidden layer to the world is revealed.")) C.add_modifier(/datum/modifier/changeling/thermal_sight, 0, src) -// C.permanent_sight_flags |= SEE_MOBS -// C.dna.species.invis_sight = SEE_INVISIBLE_MINIMUM else to_chat(C, span_notice("Our vision dulls.")) C.remove_modifiers_of_type(/datum/modifier/changeling/thermal_sight) -// C.permanent_sight_flags &= ~SEE_MOBS -// C.dna.species.invis_sight = initial(user.dna.species.invis_sight) return 1 diff --git a/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm b/code/datums/components/antags/changeling/powers/bioelectrogenesis.dm similarity index 75% rename from code/game/gamemodes/changeling/powers/bioelectrogenesis.dm rename to code/datums/components/antags/changeling/powers/bioelectrogenesis.dm index bdbebb740d..1da904f78c 100644 --- a/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm +++ b/code/datums/components/antags/changeling/powers/bioelectrogenesis.dm @@ -15,29 +15,29 @@ set name = "Bioelectrogenesis (20 + 10/shock)" set desc = "Recharges anything in your hand, or shocks people." - var/datum/changeling/changeling = changeling_power(20,0,100,CONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(20,0,100,CONSCIOUS) var/obj/held_item = get_active_hand() if(!changeling) - return 0 + return FALSE if(held_item == null) - if(src.mind.changeling.recursive_enhancement) + if(changeling.recursive_enhancement) if(changeling_generic_weapon(/obj/item/electric_hand/efficent,0)) to_chat(src, span_notice("We will shock others more efficently.")) - return 1 + return TRUE else if(changeling_generic_weapon(/obj/item/electric_hand,0)) //Chemical cost is handled in the equip proc. - return 1 - return 0 + return TRUE + return FALSE else // Handle glove conductivity. var/obj/item/clothing/gloves/gloves = src.gloves var/siemens = 1 if(gloves) - siemens = gloves.siemens_coefficient + siemens = gloves.siemens_coefficient //Funnily enough, this means things like Knights Gloves will make you stun 2x harder and charge 2x more! //If we're grabbing someone, electrocute them. if(istype(held_item,/obj/item/grab)) @@ -55,13 +55,13 @@ span_warningplain("You hear sparks!")) else to_chat(src, span_warning("Our gloves block us from shocking \the [G.affecting].")) - src.mind.changeling.chem_charges -= 10 - return 1 + changeling.chem_charges -= 10 + return TRUE //Otherwise, charge up whatever's in their hand. else //This checks both the active hand, and the contents of the active hand's held item. - var/success = 0 + var/success = FALSE var/list/L = new() //We make a new list to avoid copypasta. //Check our hand. @@ -75,26 +75,14 @@ //Now for the actual recharging. for(var/obj/item/cell/cell in L) visible_message(span_warning("Some sparks fall out from \the [src.name]\'s [held_item]!"), - span_warning("Our hand channels raw electricity into \the [held_item]."), + span_warning("Our hand channels raw electricity into \the [held_item]. We must remain by the [held_item] to recharge it."), span_warningplain("You hear sparks!")) - var/i = 10 - if(siemens) - while(i) - cell.charge += 100 * siemens //This should be a nice compromise between recharging guns and other batteries. - if(cell.charge > cell.maxcharge) - cell.charge = cell.maxcharge - break - if(siemens) - var/T = get_turf(src) - new /obj/effect/effect/sparks(T) - held_item.update_icon() - i-- - sleep(1 SECOND) - success = 1 - if(success == 0) //If we couldn't do anything with the ability, don't deduct the chemicals. + cell.gradual_charge(10, siemens, TRUE, src) + success = TRUE + if(success == FALSE) //If we couldn't do anything with the ability, don't deduct the chemicals. to_chat(src, span_warning("We are unable to affect \the [held_item].")) else - src.mind.changeling.chem_charges -= 10 + changeling.chem_charges -= 10 return success /obj/item/electric_hand @@ -103,6 +91,7 @@ icon = 'icons/obj/weapons.dmi' icon_state = "electric_hand" show_examine = FALSE + destroy_on_drop = TRUE var/shock_cost = 10 var/agony_amount = 60 @@ -122,12 +111,6 @@ var/T = get_turf(src) new /obj/effect/effect/sparks(T) -/obj/item/electric_hand/dropped(mob/user) - ..() - spawn(1) - if(src) - qdel(src) - /obj/item/electric_hand/afterattack(var/atom/target, var/mob/living/carbon/human/user, proximity) if(!target) return @@ -141,12 +124,13 @@ siemens = gloves.siemens_coefficient //Excuse the copypasta. + var/datum/component/antag/changeling/comp = is_changeling(user) if(istype(target,/mob/living/carbon)) var/mob/living/carbon/C = target - if(user.mind.changeling.chem_charges < shock_cost) + if(comp.chem_charges < shock_cost) to_chat(src, span_warning("We require more chemicals to electrocute [C]!")) - return 0 + return FALSE C.electrocute_act(electrocute_amount * siemens,src,1.0,BP_TORSO) C.stun_effect_act(0, agony_amount * siemens, BP_TORSO, src) @@ -159,16 +143,15 @@ span_warningplain("You hear sparks!")) else to_chat(src, span_warning("Our gloves block us from shocking \the [C].")) - //qdel(src) //Since we're no longer a one hit stun, we need to stick around. - user.mind.changeling.chem_charges -= shock_cost - return 1 + comp.chem_charges -= shock_cost + return TRUE else if(istype(target,/mob/living/silicon)) var/mob/living/silicon/S = target - if(user.mind.changeling.chem_charges < 10) + if(comp.chem_charges < 10) to_chat(src, span_warning("We require more chemicals to electrocute [S]!")) - return 0 + return FALSE S.electrocute_act(60,src,0.75) //If only they had surge protectors. if(siemens) @@ -176,38 +159,22 @@ span_warning("Our hand channels raw electricity into [S]"), span_warningplain("You hear sparks!")) to_chat(S, span_danger("Warning: Electrical surge detected!")) - //qdel(src) - user.mind.changeling.chem_charges -= 10 - return 1 + comp.chem_charges -= 10 + return TRUE else if(istype(target,/obj/)) - var/success = 0 + var/success = FALSE var/obj/T = target //We can also recharge things we touch, such as APCs or hardsuits. for(var/obj/item/cell/cell in T.contents) visible_message(span_warning("Some sparks fall out from \the [target]!"), span_warning("Our hand channels raw electricity into \the [target]."), span_warningplain("You hear sparks!")) - var/i = 10 - if(siemens) - while(i) - cell.charge += 100 * siemens //This should be a nice compromise between recharging guns and other batteries. - if(cell.charge > cell.maxcharge) - cell.charge = cell.maxcharge - break //No point making sparks if the cell's full. - // if(!Adjacent(T)) - // break - if(siemens) - var/Turf = get_turf(src) - new /obj/effect/effect/sparks(Turf) - T.update_icon() - i-- - sleep(1 SECOND) - success = 1 - break - if(success == 0) + cell.gradual_charge(10, siemens, TRUE, src) + success = TRUE + if(success == FALSE) to_chat(src, span_warning("We are unable to affect \the [target].")) else qdel(src) - return 1 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/blind_sting.dm b/code/datums/components/antags/changeling/powers/blind_sting.dm similarity index 64% rename from code/game/gamemodes/changeling/powers/blind_sting.dm rename to code/datums/components/antags/changeling/powers/blind_sting.dm index 40d5f96824..9553fe3eff 100644 --- a/code/game/gamemodes/changeling/powers/blind_sting.dm +++ b/code/datums/components/antags/changeling/powers/blind_sting.dm @@ -1,30 +1,35 @@ +//Updated /datum/power/changeling/blind_sting name = "Blind Sting" desc = "We silently sting a human, completely blinding them for a short time." enhancedtext = "Duration is extended." ability_icon_state = "ling_sting_blind" genomecost = 2 - allowduringlesserform = 1 + allowduringlesserform = TRUE verbpath = /mob/proc/changeling_blind_sting /mob/proc/changeling_blind_sting() set category = "Changeling" set name = "Blind sting (20)" set desc="Sting target" - + var/datum/component/antag/changeling/comp = is_changeling(src) var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_blind_sting) if(!T) - return 0 + return FALSE add_attack_logs(src,T,"Blind sting (changeling)") to_chat(T, span_danger("Your eyes burn horrificly!")) T.disabilities |= NEARSIGHTED - var/duration = 300 - if(src.mind.changeling.recursive_enhancement) - duration = duration + 150 + var/duration = 30 SECONDS + if(comp.recursive_enhancement) + duration = duration + 15 SECONDS to_chat(src, span_notice("They will be deprived of sight for longer.")) - spawn(duration) - T.disabilities &= ~NEARSIGHTED + addtimer(CALLBACK(T, PROC_REF(nearsighted_sting_complete),T), duration, TIMER_DELETE_ME) T.Blind(10) T.eye_blurry = 20 feedback_add_details("changeling_powers","BS") - return 1 + return TRUE + +/mob/proc/nearsighted_sting_complete(mob/target, mode) + if(!target) + return + target.disabilities &= ~NEARSIGHTED diff --git a/code/game/gamemodes/changeling/powers/boost_range.dm b/code/datums/components/antags/changeling/powers/boost_range.dm similarity index 66% rename from code/game/gamemodes/changeling/powers/boost_range.dm rename to code/datums/components/antags/changeling/powers/boost_range.dm index 1b818a1f03..d710109abc 100644 --- a/code/game/gamemodes/changeling/powers/boost_range.dm +++ b/code/datums/components/antags/changeling/powers/boost_range.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/boost_range name = "Boost Range" desc = "We evolve the ability to shoot our stingers at humans, with some preperation." @@ -5,7 +6,7 @@ enhancedtext = "The range is extended to five tiles." ability_icon_state = "ling_sting_boost_range" genomecost = 1 - allowduringlesserform = 1 + allowduringlesserform = TRUE verbpath = /mob/proc/changeling_boost_range //Boosts the range of your next sting attack by 1 @@ -14,18 +15,21 @@ set name = "Ranged Sting (10)" set desc="Your next sting ability can be used against targets 2 squares away." - var/datum/changeling/changeling = changeling_power(10,0,100) + var/datum/component/antag/changeling/changeling = changeling_power(10,0,100) if(!changeling) - return 0 + return FALSE + if(!changeling.recursive_enhancement && changeling.sting_range > 1) + to_chat(src, span_notice("We have already empowered our sting!")) + return + else if(changeling.recursive_enhancement && changeling.sting_range > 2) + to_chat(src, span_notice("We have already empowered our sting!")) + return changeling.chem_charges -= 10 to_chat(src, span_notice("Your throat adjusts to launch the sting.")) var/range = 2 - if(src.mind.changeling.recursive_enhancement) + if(changeling.recursive_enhancement) range = range + 3 to_chat(src, span_notice("We can fire our next sting from five squares away.")) changeling.sting_range = range - remove_verb(src, /mob/proc/changeling_boost_range) - spawn(5) - add_verb(src, /mob/proc/changeling_boost_range) feedback_add_details("changeling_powers","RS") - return 1 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/cryo_sting.dm b/code/datums/components/antags/changeling/powers/cryo_sting.dm similarity index 59% rename from code/game/gamemodes/changeling/powers/cryo_sting.dm rename to code/datums/components/antags/changeling/powers/cryo_sting.dm index b61b5b00ed..2cc45b6a3b 100644 --- a/code/game/gamemodes/changeling/powers/cryo_sting.dm +++ b/code/datums/components/antags/changeling/powers/cryo_sting.dm @@ -13,19 +13,25 @@ set name = "Cryogenic Sting (20)" set desc = "Chills and freezes a biological creature." - var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_cryo_sting) + var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_cryo_sting, CRYO_STING) + var/datum/component/antag/changeling/comp = is_changeling(src) if(!T) - return 0 + return FALSE + if(comp.is_on_cooldown(CRYO_STING)) + to_chat(src, span_notice("We are still recovering. We will be able to sting again in [(comp.get_cooldown(CRYO_STING) - world.time)/10] seconds.")) + return + add_attack_logs(src,T,"Cryo sting (changeling)") var/inject_amount = 10 - if(src.mind.changeling.recursive_enhancement) + if(comp.recursive_enhancement) inject_amount = inject_amount * 1.5 to_chat(src, span_notice("We inject extra chemicals.")) if(T.reagents) T.reagents.add_reagent(REAGENT_ID_CRYOTOXIN, inject_amount) feedback_add_details("changeling_powers","CS") - remove_verb(src, /mob/proc/changeling_cryo_sting) - spawn(3 MINUTES) - to_chat(src, span_notice("Our cryogenic string is ready to be used once more.")) - add_verb(src, /mob/proc/changeling_cryo_sting) - return 1 + comp.set_cooldown(CRYO_STING, 3 MINUTES) //Set the cooldown to 3 minutes. + addtimer(CALLBACK(src, PROC_REF(changeling_cryo_sting_ready)), 3 MINUTES, TIMER_DELETE_ME) //Calling a proc with arguments + return TRUE + +/mob/proc/changeling_cryo_sting_ready() + to_chat(src, span_notice("Our cryogenic string is ready to be used once more.")) diff --git a/code/game/gamemodes/changeling/powers/darkvision.dm b/code/datums/components/antags/changeling/powers/darkvision.dm similarity index 89% rename from code/game/gamemodes/changeling/powers/darkvision.dm rename to code/datums/components/antags/changeling/powers/darkvision.dm index 3139a85e39..2489c551c9 100644 --- a/code/game/gamemodes/changeling/powers/darkvision.dm +++ b/code/datums/components/antags/changeling/powers/darkvision.dm @@ -11,7 +11,7 @@ set name = "Toggle Darkvision" set desc = "We are able see in the dark." - var/datum/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS) if(!changeling) return 0 diff --git a/code/game/gamemodes/changeling/powers/deaf_sting.dm b/code/datums/components/antags/changeling/powers/deaf_sting.dm similarity index 87% rename from code/game/gamemodes/changeling/powers/deaf_sting.dm rename to code/datums/components/antags/changeling/powers/deaf_sting.dm index e03205665c..9b6689eb15 100644 --- a/code/game/gamemodes/changeling/powers/deaf_sting.dm +++ b/code/datums/components/antags/changeling/powers/deaf_sting.dm @@ -4,7 +4,7 @@ enhancedtext = "Deafness duration is extended." ability_icon_state = "ling_sting_deafen" genomecost = 1 - allowduringlesserform = 1 + allowduringlesserform = TRUE verbpath = /mob/proc/changeling_deaf_sting @@ -14,10 +14,11 @@ set desc="Sting target:" var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_deaf_sting) + var/datum/component/antag/changeling/comp = is_changeling(src) if(!T) return 0 add_attack_logs(src,T,"Deaf sting (changeling)") var/duration = 300 - if(src.mind.changeling.recursive_enhancement) + if(comp.recursive_enhancement) duration = duration + 100 to_chat(src, span_notice("They will be unable to hear for a little longer.")) to_chat(T, span_danger("Your ears pop and begin ringing loudly!")) diff --git a/code/game/gamemodes/changeling/powers/delayed_toxin_sting.dm b/code/datums/components/antags/changeling/powers/delayed_toxin_sting.dm similarity index 94% rename from code/game/gamemodes/changeling/powers/delayed_toxin_sting.dm rename to code/datums/components/antags/changeling/powers/delayed_toxin_sting.dm index bbd63db65f..d2e5384617 100644 --- a/code/game/gamemodes/changeling/powers/delayed_toxin_sting.dm +++ b/code/datums/components/antags/changeling/powers/delayed_toxin_sting.dm @@ -26,11 +26,12 @@ set desc = "Injects the target with a toxin that will take effect after a few minutes." var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_delayed_toxic_sting) + var/datum/component/antag/changeling/comp = is_changeling(src) if(!T) return 0 add_attack_logs(src,T,"Delayed toxic sting (chagneling)") var/type_to_give = /datum/modifier/delayed_toxin_sting - if(src.mind.changeling.recursive_enhancement) + if(comp.recursive_enhancement) type_to_give = /datum/modifier/delayed_toxin_sting/strong to_chat(src, span_notice("Our toxin will be extra potent, when it strikes.")) diff --git a/code/game/gamemodes/changeling/powers/digital_camo.dm b/code/datums/components/antags/changeling/powers/digital_camo.dm similarity index 74% rename from code/game/gamemodes/changeling/powers/digital_camo.dm rename to code/datums/components/antags/changeling/powers/digital_camo.dm index cca4f054ae..be0e7e3766 100644 --- a/code/game/gamemodes/changeling/powers/digital_camo.dm +++ b/code/datums/components/antags/changeling/powers/digital_camo.dm @@ -1,10 +1,10 @@ -/datum/power/changeling/DigitalCamoflague +/datum/power/changeling/digital_camoflague name = "Digital Camoflauge" desc = "We evolve the ability to distort our form and proprtions, defeating common altgorthms used to detect lifeforms on cameras." helptext = "We cannot be tracked by camera while using this skill. However, humans looking at us will find us.. uncanny. We must constantly expend chemicals to maintain our form like this." ability_icon_state = "ling_digital_camo" genomecost = 1 - allowduringlesserform = 1 + allowduringlesserform = TRUE verbpath = /mob/proc/changeling_digitalcamo //Prevents AIs tracking you but makes you easily detectable to the human-eye. @@ -13,7 +13,7 @@ set name = "Toggle Digital Camoflague" set desc = "The AI can no longer track us, but we will look different if examined. Has a constant cost while active." - var/datum/changeling/changeling = changeling_power() + var/datum/component/antag/changeling/changeling = changeling_power() if(!changeling) return 0 @@ -25,12 +25,9 @@ C.digitalcamo = !C.digitalcamo spawn(0) - while(C && C.digitalcamo && C.mind && C.mind.changeling) - C.mind.changeling.chem_charges = max(C.mind.changeling.chem_charges - 1, 0) + while(C && C.digitalcamo && C.mind && changeling) + changeling.chem_charges = max(changeling.chem_charges - 1, 0) sleep(40) - remove_verb(src, /mob/proc/changeling_digitalcamo) - spawn(5) - add_verb(src, /mob/proc/changeling_digitalcamo) feedback_add_details("changeling_powers","CAM") return 1 diff --git a/code/game/gamemodes/changeling/powers/electric_lockpick.dm b/code/datums/components/antags/changeling/powers/electric_lockpick.dm similarity index 95% rename from code/game/gamemodes/changeling/powers/electric_lockpick.dm rename to code/datums/components/antags/changeling/powers/electric_lockpick.dm index 62ab50231b..cde4fcbb97 100644 --- a/code/game/gamemodes/changeling/powers/electric_lockpick.dm +++ b/code/datums/components/antags/changeling/powers/electric_lockpick.dm @@ -13,7 +13,7 @@ set name = "Electric Lockpick (5 + 10/use)" set desc = "Bruteforces open most electrical locking systems, at 10 chemicals per use." - var/datum/changeling/changeling = changeling_power(5,0,100,CONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(5,0,100,CONSCIOUS) var/obj/held_item = get_active_hand() @@ -49,11 +49,10 @@ return if(!proximity) return - if(!user.mind.changeling) + var/datum/component/antag/changeling/ling_datum = is_changeling(user) + if(!ling_datum) return - var/datum/changeling/ling_datum = user.mind.changeling - if(ling_datum.chem_charges < 10) to_chat(user, span_warning("We require more chemicals to do that.")) return diff --git a/code/game/gamemodes/changeling/powers/endoarmor.dm b/code/datums/components/antags/changeling/powers/endoarmor.dm similarity index 95% rename from code/game/gamemodes/changeling/powers/endoarmor.dm rename to code/datums/components/antags/changeling/powers/endoarmor.dm index 6ada286574..c2ca894f06 100644 --- a/code/game/gamemodes/changeling/powers/endoarmor.dm +++ b/code/datums/components/antags/changeling/powers/endoarmor.dm @@ -1,9 +1,10 @@ +//Updated /datum/power/changeling/endoarmor name = "Endoarmor" desc = "We grow hard plating underneath our skin, making us more resilient to harm by increasing our maximum health potential by 50 points." helptext = "Our maximum health is increased by 50 points." genomecost = 1 - isVerb = 0 + isVerb = FALSE verbpath = /mob/proc/changeling_endoarmor /datum/modifier/endoarmor @@ -18,5 +19,4 @@ if(ishuman(src)) var/mob/living/carbon/human/H = src H.add_modifier(/datum/modifier/endoarmor) - // H.maxHealth += 50 return 1 diff --git a/code/game/gamemodes/changeling/powers/enfeebling_string.dm b/code/datums/components/antags/changeling/powers/enfeebling_string.dm similarity index 94% rename from code/game/gamemodes/changeling/powers/enfeebling_string.dm rename to code/datums/components/antags/changeling/powers/enfeebling_string.dm index 49829aecce..05dc875fd0 100644 --- a/code/game/gamemodes/changeling/powers/enfeebling_string.dm +++ b/code/datums/components/antags/changeling/powers/enfeebling_string.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/enfeebling_string name = "Enfeebling String" desc = "We sting a biological with a potent toxin that will greatly weaken them for a short period of time." @@ -30,6 +31,7 @@ set desc = "Reduces the maximum health of a victim for a few minutes.." var/mob/living/carbon/T = changeling_sting(30,/mob/proc/changeling_enfeebling_string) + var/datum/component/antag/changeling/comp = is_changeling(src) if(!T) return 0 if(ishuman(T)) @@ -38,7 +40,7 @@ add_attack_logs(src,T,"Enfeebling sting (changeling)") var/type_to_give = /datum/modifier/enfeeble - if(src.mind.changeling.recursive_enhancement) + if(comp.recursive_enhancement) type_to_give = /datum/modifier/enfeeble/strong to_chat(src, span_notice("We make them extremely weak.")) H.add_modifier(type_to_give, 2 MINUTES) diff --git a/code/game/gamemodes/changeling/powers/engorged_glands.dm b/code/datums/components/antags/changeling/powers/engorged_glands.dm similarity index 69% rename from code/game/gamemodes/changeling/powers/engorged_glands.dm rename to code/datums/components/antags/changeling/powers/engorged_glands.dm index 8efb2cf46d..f0513f86be 100644 --- a/code/game/gamemodes/changeling/powers/engorged_glands.dm +++ b/code/datums/components/antags/changeling/powers/engorged_glands.dm @@ -1,4 +1,5 @@ -/datum/power/changeling/EngorgedGlands +//Updated +/datum/power/changeling/engorged_glands name = "Engorged Chemical Glands" desc = "Our chemical glands swell, permitting us to store more chemicals inside of them." helptext = "Allows us to store an extra 30 units of chemicals, and doubles production rate." @@ -8,6 +9,7 @@ //Increases macimum chemical storage /mob/proc/changeling_engorgedglands() - src.mind.changeling.chem_storage += 30 - src.mind.changeling.chem_recharge_rate *= 2 + var/datum/component/antag/changeling/comp = is_changeling(src) + comp.chem_storage += 30 + comp.chem_recharge_rate *= 2 return 1 diff --git a/code/game/gamemodes/changeling/powers/enrage.dm b/code/datums/components/antags/changeling/powers/enrage.dm similarity index 90% rename from code/game/gamemodes/changeling/powers/enrage.dm rename to code/datums/components/antags/changeling/powers/enrage.dm index 2336b3042a..79c024eb99 100644 --- a/code/game/gamemodes/changeling/powers/enrage.dm +++ b/code/datums/components/antags/changeling/powers/enrage.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/enrage name = "Enrage" desc = "We evolve modifications to our mind and body, allowing us to call on intense periods of rage for our benefit." @@ -8,7 +9,7 @@ enhancedtext = "The length of exhaustion after berserking is reduced to one minute, from two, and requires half as much nutrition." ability_icon_state = "ling_berserk" genomecost = 2 - allowduringlesserform = 1 + allowduringlesserform = TRUE verbpath = /mob/living/proc/changeling_berserk // Makes the ling very upset. @@ -17,12 +18,12 @@ set name = "Enrage (30)" set desc = "Causes you to go Berserk." - var/datum/changeling/changeling = changeling_power(30,0,100) + var/datum/component/antag/changeling/changeling = changeling_power(30,0,100) if(!changeling) return 0 var/modifier_to_use = /datum/modifier/berserk/changeling - if(src.mind.changeling.recursive_enhancement) + if(changeling.recursive_enhancement) modifier_to_use = /datum/modifier/berserk/changeling/recursive to_chat(src, span_notice("We optimize our levels of anger, which will avoid excessive stress on ourselves.")) diff --git a/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm b/code/datums/components/antags/changeling/powers/epinephrine_overdose.dm similarity index 81% rename from code/game/gamemodes/changeling/powers/epinephrine_overdose.dm rename to code/datums/components/antags/changeling/powers/epinephrine_overdose.dm index 5ef4de1558..38177dc956 100644 --- a/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm +++ b/code/datums/components/antags/changeling/powers/epinephrine_overdose.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/epinephrine_overdose name = "Epinephrine Overdose" desc = "We evolve additional sacs of adrenaline throughout our body." @@ -22,7 +23,7 @@ set name = "Epinephrine Overdose (30)" set desc = "Removes all stuns instantly, and reduces future stuns." - var/datum/changeling/changeling = changeling_power(30,0,100,UNCONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(30,0,100,UNCONSCIOUS) if(!changeling) return 0 changeling.chem_charges -= 30 @@ -35,23 +36,21 @@ C.SetWeakened(0) C.lying = 0 C.update_canmove() -// C.reagents.add_reagent(REAGENT_ID_TOXIN, 10) C.reagents.add_reagent("epinephrine", 20) - if(src.mind.changeling.recursive_enhancement) + if(changeling.recursive_enhancement) C.add_modifier(/datum/modifier/unstoppable, 30 SECONDS) feedback_add_details("changeling_powers","UNS") return 1 /datum/reagent/epinephrine - name = "Epinephrine" - id = "epinephrine" - description = "Reduces stun times, but causing toxicity due to high concentration." + name = REAGENT_EPINEPHRINE + id = REAGENT_ID_EPINEPHRINE + description = "A chemically naturally produced by the body while in fight-or-flight mode. Greatly increases one's strength." reagent_state = LIQUID color = "#C8A5DC" metabolism = REM * 2 - overdose = 5 //This is intentionally low, as we want the ling to take some tox damage, to discourage spamming the ability. wiki_flag = WIKI_SPOILER /datum/reagent/epinephrine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) @@ -63,5 +62,6 @@ M.AdjustParalysis(-2) M.AdjustStunned(-2) M.AdjustWeakened(-2) + M.adjustToxLoss(removed * 2.5) //It gives you 20units of epinephrine. 50 toxins damage. 1 Toxin per tick. ..() return diff --git a/code/game/gamemodes/changeling/powers/escape_restraints.dm b/code/datums/components/antags/changeling/powers/escape_restraints.dm similarity index 73% rename from code/game/gamemodes/changeling/powers/escape_restraints.dm rename to code/datums/components/antags/changeling/powers/escape_restraints.dm index 504cb884c9..09d5a0b41b 100644 --- a/code/game/gamemodes/changeling/powers/escape_restraints.dm +++ b/code/datums/components/antags/changeling/powers/escape_restraints.dm @@ -15,15 +15,15 @@ var/escape_cooldown = 5 MINUTES //This is used later to prevent spamming var/mob/living/carbon/human/C = src - var/datum/changeling/changeling = changeling_power(40,0,100,CONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(40,0,100,CONSCIOUS) if(!changeling) - return 0 - if(world.time < changeling.next_escape) - to_chat(src, span_warning("We are still recovering from our last escape...")) - return 0 + return FALSE + if(changeling.is_on_cooldown(ESCAPE_RESTRAINTS)) + to_chat(src, span_notice("We are still recovering from our last escape. We will be able to escape again in [(changeling.get_cooldown(ESCAPE_RESTRAINTS) - world.time)/10] seconds.")) + return FALSE if(!(C.handcuffed || C.legcuffed || istype(C.wear_suit,/obj/item/clothing/suit/straight_jacket))) // No need to waste chems if there's nothing to break out of to_chat(C, span_warning("We are are not restrained in a way we can escape...")) - return 0 + return FALSE changeling.chem_charges -= 40 @@ -58,10 +58,14 @@ C.wear_suit = null escape_cooldown *= 1.5 // Straight jackets are tedious compared to cuffs. - if(src.mind.changeling.recursive_enhancement) + if(changeling.recursive_enhancement) escape_cooldown *= 0.5 - changeling.next_escape = world.time + escape_cooldown //And now we set the timer + changeling.set_cooldown(ESCAPE_RESTRAINTS, escape_cooldown) + addtimer(CALLBACK(src, PROC_REF(changeling_escape_restraints_ready)), escape_cooldown, TIMER_DELETE_ME) feedback_add_details("changeling_powers","ESR") - return 1 + return TRUE + +/mob/proc/changeling_escape_restraints_ready() + to_chat(src, span_notice("We are able to escape our restraints once more.")) diff --git a/code/game/gamemodes/changeling/powers/extract_dna_sting.dm b/code/datums/components/antags/changeling/powers/extract_dna_sting.dm similarity index 83% rename from code/game/gamemodes/changeling/powers/extract_dna_sting.dm rename to code/datums/components/antags/changeling/powers/extract_dna_sting.dm index 3584f58f05..bbf8d8b11f 100644 --- a/code/game/gamemodes/changeling/powers/extract_dna_sting.dm +++ b/code/datums/components/antags/changeling/powers/extract_dna_sting.dm @@ -1,10 +1,11 @@ -/datum/power/changeling/extractdna +//Updated +/datum/power/changeling/extract_dna name = "Extract DNA" desc = "We stealthily sting a target and extract the DNA from them." helptext = "Will give you the DNA of your target, allowing you to transform into them. Does not count towards absorb objectives." ability_icon_state = "ling_sting_extract" genomecost = 0 - allowduringlesserform = 1 + allowduringlesserform = TRUE verbpath = /mob/proc/changeling_extract_dna_sting /mob/proc/changeling_extract_dna_sting() @@ -12,11 +13,11 @@ set name = "Extract DNA Sting (40)" set desc="Stealthily sting a target to extract their DNA." - var/datum/changeling/changeling = null - if(src.mind && src.mind.changeling) - changeling = src.mind.changeling + var/datum/component/antag/changeling/changeling = null + if(src.mind && changeling) + changeling = changeling if(!changeling) - return 0 + return FALSE var/mob/living/carbon/human/T = changeling_sting(40, /mob/proc/changeling_extract_dna_sting) @@ -25,15 +26,15 @@ if(!istype(T) || T.isSynthetic()) to_chat(src, span_warning("\The [T] is not compatible with our biology.")) - return 0 + return FALSE if(T.species.flags & (NO_DNA|NO_SLEEVE)) to_chat(src, span_warning("We do not know how to parse this creature's DNA!")) - return 0 + return FALSE if(HUSK in T.mutations) to_chat(src, span_warning("This creature's DNA is ruined beyond useability!")) - return 0 + return FALSE add_attack_logs(src,T,"DNA extraction sting (changeling)") @@ -42,4 +43,4 @@ absorbDNA(newDNA) feedback_add_details("changeling_powers","ED") - return 1 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/fabricate_clothing.dm b/code/datums/components/antags/changeling/powers/fabricate_clothing.dm similarity index 99% rename from code/game/gamemodes/changeling/powers/fabricate_clothing.dm rename to code/datums/components/antags/changeling/powers/fabricate_clothing.dm index 5e4979cb2d..bf52f673e2 100644 --- a/code/game/gamemodes/changeling/powers/fabricate_clothing.dm +++ b/code/datums/components/antags/changeling/powers/fabricate_clothing.dm @@ -1,3 +1,4 @@ +//Needs a BIG rework. var/global/list/changeling_fabricated_clothing = list( "w_uniform" = /obj/item/clothing/under/chameleon/changeling, "head" = /obj/item/clothing/head/chameleon/changeling, diff --git a/code/game/gamemodes/changeling/powers/fake_death.dm b/code/datums/components/antags/changeling/powers/fake_death.dm similarity index 60% rename from code/game/gamemodes/changeling/powers/fake_death.dm rename to code/datums/components/antags/changeling/powers/fake_death.dm index e8a3121eee..18a25d5ef6 100644 --- a/code/game/gamemodes/changeling/powers/fake_death.dm +++ b/code/datums/components/antags/changeling/powers/fake_death.dm @@ -1,10 +1,10 @@ -/datum/power/changeling/fakedeath +/datum/power/changeling/fake_death name = "Regenerative Stasis" desc = "We become weakened to a death-like state, where we will rise again from death." helptext = "Can be used before or after death. Duration varies greatly." ability_icon_state = "ling_regenerative_stasis" genomecost = 0 - allowduringlesserform = 1 + allowduringlesserform = TRUE verbpath = /mob/proc/changeling_fakedeath //Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay. @@ -12,7 +12,7 @@ set category = "Changeling" set name = "Regenerative Stasis (20)" - var/datum/changeling/changeling = changeling_power(CHANGELING_STASIS_COST,1,100,DEAD) + var/datum/component/antag/changeling/changeling = changeling_power(CHANGELING_STASIS_COST,1,100,DEAD) if(!changeling) return @@ -24,30 +24,29 @@ if(!C.stat && tgui_alert(src, "Are we sure we wish to regenerate? We will appear to be dead while doing so.","Revival",list("Yes","No")) != "Yes") return - to_chat(C, span_notice("We will attempt to regenerate our form.")) C.update_canmove() - C.remove_changeling_powers() changeling.chem_charges -= CHANGELING_STASIS_COST if(C.suiciding) - C.suiciding = 0 + C.suiciding = FALSE if(C.does_not_breathe) - C.does_not_breathe = 0 //This means they don't autoheal the oxy damage from the next step + C.does_not_breathe = FALSE //This means they don't autoheal the oxy damage from the next step if(C.stat != DEAD) C.adjustOxyLoss(C.getMaxHealth() * 2) C.forbid_seeing_deadchat = TRUE - spawn(rand(2 MINUTES, 4 MINUTES)) - //The ling will now be able to choose when to revive - add_verb(src, /mob/proc/changeling_revive) - - new /obj/changeling_revive_holder(src) - - to_chat(src, span_notice(span_giant("We are ready to rise. Use the Revive verb when you are ready."))) - + var/resurrection_time = rand(2 MINUTES, 4 MINUTES) + changeling.set_cooldown(FAKE_DEATH, resurrection_time) + changeling.is_reviving = TRUE + to_chat(C, span_notice("We will attempt to regenerate our form. This will take [(changeling.get_cooldown(FAKE_DEATH) - world.time)/600] minutes.")) + addtimer(CALLBACK(src, PROC_REF(finish_changeling_revive)), resurrection_time, TIMER_DELETE_ME) feedback_add_details("changeling_powers","FD") return 1 + +/mob/proc/finish_changeling_revive() + //Lets the ling know it's revive time. + to_chat(src, span_notice(span_giant("We are ready to rise. Use the Revive verb when you are ready."))) diff --git a/code/datums/components/antags/changeling/powers/fleshmend.dm b/code/datums/components/antags/changeling/powers/fleshmend.dm new file mode 100644 index 0000000000..8aa80e17a8 --- /dev/null +++ b/code/datums/components/antags/changeling/powers/fleshmend.dm @@ -0,0 +1,53 @@ +/datum/power/changeling/fleshmend + name = "Fleshmend" + desc = "Begins a slow regeneration of our form. Does not effect stuns or chemicals." + helptext = "Can be used while unconscious." + enhancedtext = "Healing is twice as effective." + ability_icon_state = "ling_fleshmend" + genomecost = 1 + verbpath = /mob/proc/changeling_fleshmend + +//Starts healing you every second for 50 seconds. Can be used whilst unconscious. +/mob/proc/changeling_fleshmend() + set category = "Changeling" + set name = "Fleshmend (10)" + set desc = "Begins a slow rengeration of our form. Does not effect stuns or chemicals." + + var/mob/living/C = src + var/datum/component/antag/changeling/changeling = changeling_power(10,0,100,UNCONSCIOUS) + if(!changeling) + return FALSE + if(C.has_modifier_of_type(/datum/modifier/fleshmend)) + to_chat(src, span_notice("We are already under the effect of fleshmend.")) + return FALSE + + changeling.chem_charges -= 10 + + if(changeling.recursive_enhancement) + to_chat(src, span_notice("We will heal much faster.")) + C.add_modifier(/datum/modifier/fleshmend/recursive, 50 SECONDS) + else + C.add_modifier(/datum/modifier/fleshmend, 50 SECONDS) + + feedback_add_details("changeling_powers","FM") + return TRUE + +/datum/modifier/fleshmend + name = "Fleshmend" + desc = "We are regenerating" + +// For changelings who bought the Recursive Enhancement evolution. +/datum/modifier/fleshmend/recursive + name = "Advanced Fleshmend" + desc = "We are regenerating more rapidly." + +//These were previously 2 or 4 per second, now it's 4 or 8 per 2 seconds +/datum/modifier/fleshmend/tick() + holder.adjustBruteLoss(-4) + holder.adjustOxyLoss(-4) + holder.adjustFireLoss(-4) + +/datum/modifier/fleshmend/recursive/tick() + holder.adjustBruteLoss(-8) + holder.adjustOxyLoss(-8) + holder.adjustFireLoss(-8) diff --git a/code/game/gamemodes/changeling/powers/hivemind.dm b/code/datums/components/antags/changeling/powers/hivemind.dm similarity index 88% rename from code/game/gamemodes/changeling/powers/hivemind.dm rename to code/datums/components/antags/changeling/powers/hivemind.dm index 44f66c8791..84e86afa9c 100644 --- a/code/game/gamemodes/changeling/powers/hivemind.dm +++ b/code/datums/components/antags/changeling/powers/hivemind.dm @@ -1,3 +1,4 @@ +//Updated // Hivemind /datum/power/changeling/hive_upload @@ -5,7 +6,7 @@ desc = "We can channel a DNA into the airwaves, allowing our fellow changelings to absorb it and transform into it as if they acquired the DNA themselves." helptext = "Allows other changelings to absorb the DNA you channel from the airwaves. Will not help them towards their absorb objectives." genomecost = 0 - make_hud_button = 0 + make_hud_button = FALSE verbpath = /mob/proc/changeling_hiveupload /datum/power/changeling/hive_download @@ -13,7 +14,7 @@ desc = "We can absorb a single DNA from the airwaves, allowing us to use more disguises with help from our fellow changelings." helptext = "Allows you to absorb a single DNA and use it. Does not count towards your absorb objective." genomecost = 0 - make_hud_button = 0 + make_hud_button = FALSE verbpath = /mob/proc/changeling_hivedownload // HIVE MIND UPLOAD/DOWNLOAD DNA @@ -25,8 +26,9 @@ var/list/datum/dna/hivemind_bank = list() set name = "Hive Channel (10)" set desc = "Allows you to channel DNA in the airwaves to allow other changelings to absorb it." - var/datum/changeling/changeling = changeling_power(10,1) - if(!changeling) return + var/datum/component/antag/changeling/changeling = changeling_power(10,1) + if(!changeling) + return var/list/names = list() for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna) @@ -38,7 +40,8 @@ var/list/datum/dna/hivemind_bank = list() return var/S = tgui_input_list(src, "Select a DNA to channel:", "Channel DNA", names) - if(!S) return + if(!S) + return var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S) if(!chosen_dna) @@ -48,15 +51,16 @@ var/list/datum/dna/hivemind_bank = list() hivemind_bank += chosen_dna to_chat(src, span_notice("We channel the DNA of [S] to the air.")) feedback_add_details("changeling_powers","HU") - return 1 + return TRUE /mob/proc/changeling_hivedownload() set category = "Changeling" set name = "Hive Absorb (20)" set desc = "Allows you to absorb DNA that is being channeled in the airwaves." - var/datum/changeling/changeling = changeling_power(20,1) - if(!changeling) return + var/datum/component/antag/changeling/changeling = changeling_power(20,1) + if(!changeling) + return var/list/names = list() for(var/datum/absorbed_dna/DNA in hivemind_bank) @@ -68,7 +72,8 @@ var/list/datum/dna/hivemind_bank = list() return var/S = tgui_input_list(src, "Select a DNA to absorb:", "Absorb DNA", names) - if(!S) return + if(!S) + return var/datum/absorbed_dna/chosen_dna = names[S] if(!chosen_dna) return @@ -77,4 +82,4 @@ var/list/datum/dna/hivemind_bank = list() absorbDNA(chosen_dna) to_chat(src, span_notice("We absorb the DNA of [S] from the air.")) feedback_add_details("changeling_powers","HD") - return 1 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/lesser_form.dm b/code/datums/components/antags/changeling/powers/lesser_form.dm similarity index 92% rename from code/game/gamemodes/changeling/powers/lesser_form.dm rename to code/datums/components/antags/changeling/powers/lesser_form.dm index be438634aa..9a6c18a695 100644 --- a/code/game/gamemodes/changeling/powers/lesser_form.dm +++ b/code/datums/components/antags/changeling/powers/lesser_form.dm @@ -9,10 +9,11 @@ set category = "Changeling" set name = "Lesser Form (1)" - var/datum/changeling/changeling = changeling_power(1,0,0) - if(!changeling) return + var/datum/component/antag/changeling/changeling = changeling_power(1,0,0) + if(!changeling) + return - if(src.has_brain_worms()) + if(has_brain_worms()) to_chat(src, span_warning("We cannot perform this ability at the present time!")) return @@ -39,7 +40,7 @@ set category = "Changeling" set name = "Transform (1)" - var/datum/changeling/changeling = changeling_power(1,1,0) + var/datum/component/antag/changeling/changeling = changeling_power(1,1,0) if(!changeling) return var/list/names = list() @@ -47,7 +48,8 @@ names += "[DNA.real_name]" var/S = tgui_input_list(src, "Select the target DNA:", "Target DNA", names) - if(!S) return + if(!S) + return var/datum/dna/chosen_dna = changeling.GetDNA(S) if(!chosen_dna) diff --git a/code/game/gamemodes/changeling/powers/lsd_sting.dm b/code/datums/components/antags/changeling/powers/lsd_sting.dm similarity index 75% rename from code/game/gamemodes/changeling/powers/lsd_sting.dm rename to code/datums/components/antags/changeling/powers/lsd_sting.dm index 9529b13280..35e59ce04a 100644 --- a/code/game/gamemodes/changeling/powers/lsd_sting.dm +++ b/code/datums/components/antags/changeling/powers/lsd_sting.dm @@ -1,5 +1,5 @@ //This only exists to be abused, so it's highly recommended to ensure this file is unchecked. -/datum/power/changeling/LSDSting +/datum/power/changeling/lsd_sting name = "Hallucination Sting" desc = "We evolve the ability to sting a target with a powerful hallunicationary chemical." helptext = "The target does not notice they have been stung. The effect occurs after 30 to 60 seconds." @@ -12,9 +12,9 @@ set desc = "Causes terror in the target." var/mob/living/carbon/T = changeling_sting(15,/mob/proc/changeling_lsdsting) - if(!T) return 0 + if(!T) + return FALSE add_attack_logs(src,T,"Hallucination sting (changeling)") - spawn(rand(300,600)) - if(T) T.hallucination += 400 + addtimer(VARSET_CALLBACK(T, hallucination, min(T.hallucination+400, 400)), rand(30 SECONDS, 60 SECONDS), TIMER_DELETE_ME) //No going ABOVE 400 hallucinations. feedback_add_details("changeling_powers","HS") - return 1 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/mimic_voice.dm b/code/datums/components/antags/changeling/powers/mimic_voice.dm similarity index 81% rename from code/game/gamemodes/changeling/powers/mimic_voice.dm rename to code/datums/components/antags/changeling/powers/mimic_voice.dm index f5d65b8ec2..4464f30618 100644 --- a/code/game/gamemodes/changeling/powers/mimic_voice.dm +++ b/code/datums/components/antags/changeling/powers/mimic_voice.dm @@ -14,7 +14,7 @@ set desc = "Shape our vocal glands to form a voice of someone we choose. We cannot regenerate chemicals when mimicing." - var/datum/changeling/changeling = changeling_power() + var/datum/component/antag/changeling/changeling = changeling_power() if(!changeling) return if(changeling.mimicing) @@ -34,8 +34,8 @@ feedback_add_details("changeling_powers","MV") spawn(0) - while(src && src.mind && src.mind.changeling && src.mind.changeling.mimicing) - src.mind.changeling.chem_charges = max(src.mind.changeling.chem_charges - 1, 0) + while(src && src.mind && changeling && changeling.mimicing) + changeling.chem_charges = max(changeling.chem_charges - 1, 0) sleep(40) - if(src && src.mind && src.mind.changeling) - src.mind.changeling.mimicing = "" + if(src && src.mind && changeling) + changeling.mimicing = "" diff --git a/code/game/gamemodes/changeling/powers/panacea.dm b/code/datums/components/antags/changeling/powers/panacea.dm similarity index 89% rename from code/game/gamemodes/changeling/powers/panacea.dm rename to code/datums/components/antags/changeling/powers/panacea.dm index 19a043669a..c7660af55f 100644 --- a/code/game/gamemodes/changeling/powers/panacea.dm +++ b/code/datums/components/antags/changeling/powers/panacea.dm @@ -13,10 +13,10 @@ set name = "Anatomic Panacea (20)" set desc = "Clense ourselves of impurities." - var/datum/changeling/changeling = changeling_power(20,0,100,UNCONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(20,0,100,UNCONSCIOUS) if(!changeling) return 0 - src.mind.changeling.chem_charges -= 20 + changeling.chem_charges -= 20 to_chat(src, span_notice("We cleanse impurities from our form.")) @@ -30,10 +30,11 @@ var/heal_amount = 5 - if(src.mind.changeling.recursive_enhancement) + if(changeling.recursive_enhancement) heal_amount = heal_amount * 2 to_chat(src, span_notice("We will heal much faster.")) + //TODO: Replace with a modifier. for(var/i = 0, i<10,i++) if(C) C.adjustToxLoss(-heal_amount) diff --git a/code/game/gamemodes/changeling/powers/para_sting.dm b/code/datums/components/antags/changeling/powers/para_sting.dm similarity index 94% rename from code/game/gamemodes/changeling/powers/para_sting.dm rename to code/datums/components/antags/changeling/powers/para_sting.dm index 2b2cf636ed..6ab8c96203 100644 --- a/code/game/gamemodes/changeling/powers/para_sting.dm +++ b/code/datums/components/antags/changeling/powers/para_sting.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/paralysis_sting name = "Paralysis Sting" desc = "We silently sting a human, paralyzing them for a short time." @@ -11,9 +12,9 @@ var/mob/living/carbon/T = changeling_sting(30,/mob/proc/changeling_paralysis_sting) if(!T) - return 0 + return FALSE add_attack_logs(src,T,"Paralysis sting (changeling)") to_chat(T, span_danger("Your muscles begin to painfully tighten.")) T.Weaken(20) feedback_add_details("changeling_powers","PS") - return 1 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/rapid_regen.dm b/code/datums/components/antags/changeling/powers/rapid_regen.dm similarity index 91% rename from code/game/gamemodes/changeling/powers/rapid_regen.dm rename to code/datums/components/antags/changeling/powers/rapid_regen.dm index b33e250312..3d371e3997 100644 --- a/code/game/gamemodes/changeling/powers/rapid_regen.dm +++ b/code/datums/components/antags/changeling/powers/rapid_regen.dm @@ -14,15 +14,15 @@ set name = "Rapid Regeneration (50)" set desc = "Heal ourselves of most injuries instantly." - var/datum/changeling/changeling = changeling_power(50,0,100,UNCONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(50,0,100,UNCONSCIOUS) if(!changeling) return 0 - src.mind.changeling.chem_charges -= 50 + changeling.chem_charges -= 50 if(ishuman(src)) var/mob/living/carbon/human/C = src var/healing_amount = 40 - if(src.mind.changeling.recursive_enhancement) + if(changeling.recursive_enhancement) healing_amount = C.getMaxHealth() to_chat(src, span_notice("We completely heal ourselves.")) spawn(0) @@ -42,6 +42,7 @@ // make the icons look correct C.regenerate_icons() + C.UpdateAppearance() // now make it obvious that we're not human (or whatever xeno race they are impersonating) playsound(src, 'sound/effects/blobattack.ogg', 30, 1) diff --git a/code/game/gamemodes/changeling/powers/recursive_enhancement.dm b/code/datums/components/antags/changeling/powers/recursive_enhancement.dm similarity index 75% rename from code/game/gamemodes/changeling/powers/recursive_enhancement.dm rename to code/datums/components/antags/changeling/powers/recursive_enhancement.dm index 21ce98bf1c..e70596d6e7 100644 --- a/code/game/gamemodes/changeling/powers/recursive_enhancement.dm +++ b/code/datums/components/antags/changeling/powers/recursive_enhancement.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/recursive_enhancement name = "Recursive Enhancement" desc = "We cause our abilities to have increased or additional effects." @@ -11,14 +12,14 @@ set category = "Changeling" set name = "Recursive Enhancement" set desc = "Empowers our abilities." - var/datum/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS) if(!changeling) - return 0 - if(src.mind.changeling.recursive_enhancement) + return FALSE + if(changeling.recursive_enhancement) to_chat(src, span_warning("We will no longer empower our abilities.")) - src.mind.changeling.recursive_enhancement = 0 - return 0 + changeling.recursive_enhancement = FALSE + return FALSE to_chat(src, span_notice("We empower ourselves. Our abilities will now be extra potent.")) - src.mind.changeling.recursive_enhancement = 1 + changeling.recursive_enhancement = TRUE feedback_add_details("changeling_powers","RE") - return 1 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/respec.dm b/code/datums/components/antags/changeling/powers/respec.dm similarity index 81% rename from code/game/gamemodes/changeling/powers/respec.dm rename to code/datums/components/antags/changeling/powers/respec.dm index 371307700e..bf98cf2937 100644 --- a/code/game/gamemodes/changeling/powers/respec.dm +++ b/code/datums/components/antags/changeling/powers/respec.dm @@ -3,22 +3,22 @@ set name = "Re-adapt" set desc = "Allows us to refund our purchased abilities." - var/datum/changeling/changeling = changeling_power(0,0,100) + var/datum/component/antag/changeling/changeling = changeling_power(0,0,100) if(!changeling) return - if(src.mind.changeling.readapts <= 0) + if(changeling.readapts <= 0) to_chat(src, span_warning("We must first absorb another compatible creature!")) - src.mind.changeling.readapts = 0 + changeling.readapts = 0 return src.remove_changeling_powers() //First, remove the verbs. - var/datum/changeling/ling_datum = src.mind.changeling + var/datum/component/antag/changeling/ling_datum = changeling ling_datum.readapts-- ling_datum.purchased_powers = list() //Then wipe all the powers we bought. ling_datum.geneticpoints = ling_datum.max_geneticpoints //Now refund our points to the maximum. ling_datum.chem_recharge_rate = 0.5 //If glands were bought, revert that upgrade. ling_datum.thermal_sight = FALSE - src.mind.changeling.recursive_enhancement = 0 //Ensures this is cleared + changeling.recursive_enhancement = 0 //Ensures this is cleared ling_datum.chem_storage = 50 if(ishuman(src)) diff --git a/code/game/gamemodes/changeling/powers/revive.dm b/code/datums/components/antags/changeling/powers/revive.dm similarity index 71% rename from code/game/gamemodes/changeling/powers/revive.dm rename to code/datums/components/antags/changeling/powers/revive.dm index d3ebd820c3..2effde42d8 100644 --- a/code/game/gamemodes/changeling/powers/revive.dm +++ b/code/datums/components/antags/changeling/powers/revive.dm @@ -1,16 +1,35 @@ +/datum/power/changeling/changeling_revive + name = "Revive" + desc = "We revive from our death-like state." + helptext = "Regeneration must first be started via Regenerative Stasis." + ability_icon_state = "ling_revive" + genomecost = 0 + allowduringlesserform = TRUE + verbpath = /mob/proc/changeling_revive + //Revive from revival stasis /mob/proc/changeling_revive() set category = "Changeling" set name = "Revive" set desc = "We are ready to revive ourselves on command." - var/datum/changeling/changeling = changeling_power(0,0,100,DEAD) + var/datum/component/antag/changeling/changeling = changeling_power(0,0,100,DEAD) if(!changeling) - return 0 + return FALSE + + if(stat != DEAD) + to_chat(src, span_danger("We are not dead.")) + return FALSE + if(!changeling.is_reviving) + to_chat(src, span_danger("We have not begun the regeneration process yet..")) + return FALSE + if(changeling.is_on_cooldown(FAKE_DEATH)) + to_chat(src, span_danger("We are still recovering. We will be able to revive again in [(changeling.get_cooldown(FAKE_DEATH) - world.time)/10] seconds.")) + return FALSE if(changeling.max_geneticpoints < 0) //Absorbed by another ling to_chat(src, span_danger("You have no genomes, not even your own, and cannot revive.")) - return 0 + return FALSE if(src.stat == DEAD) dead_mob_list -= src @@ -72,38 +91,16 @@ SJ.forceMove(H.loc) SJ.dropped(H) H.wear_suit = null + H.UpdateAppearance() C.halloss = 0 C.shock_stage = 0 //Pain to_chat(C, span_notice("We have regenerated.")) C.update_canmove() - C.mind.changeling.purchased_powers -= C feedback_add_details("changeling_powers","CR") C.set_stat(CONSCIOUS) C.forbid_seeing_deadchat = FALSE C.timeofdeath = null - remove_verb(src, /mob/proc/changeling_revive) - // re-add our changeling powers - C.make_changeling() + changeling.is_reviving = FALSE - return 1 - -//Revive from revival stasis, but one level removed, as the tab refuses to update. Placed in its own tab to avoid hyper-exploding the original tab through the same name being used. - -/obj/changeling_revive_holder - name = "strange object" - desc = "Please report this object's existence to the dev team! You shouldn't see it." - mouse_opacity = FALSE - alpha = 1 - -/obj/changeling_revive_holder/verb/ling_revive() - set src = usr.contents - set category = "Regenerate" - set name = "Revive" - set desc = "We are ready to revive ourselves on command." - - if(iscarbon(usr)) - var/mob/living/carbon/C = usr - C.changeling_revive() - - qdel(src) + return TRUE diff --git a/code/game/gamemodes/changeling/powers/self_respiration.dm b/code/datums/components/antags/changeling/powers/self_respiration.dm similarity index 77% rename from code/game/gamemodes/changeling/powers/self_respiration.dm rename to code/datums/components/antags/changeling/powers/self_respiration.dm index 4b939c8b89..ed6e9ffd90 100644 --- a/code/game/gamemodes/changeling/powers/self_respiration.dm +++ b/code/datums/components/antags/changeling/powers/self_respiration.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/self_respiration name = "Self Respiration" desc = "We evolve our body to no longer require drawing oxygen from the atmosphere." @@ -12,20 +13,20 @@ set name = "Toggle Breathing" set desc = "We choose whether or not to breathe." - var/datum/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS) + var/datum/component/antag/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS) if(!changeling) - return 0 + return FALSE if(istype(src,/mob/living/carbon)) var/mob/living/carbon/C = src if(C.suiciding) to_chat(src, "You're committing suicide, this isn't going to work.") - return 0 - if(C.does_not_breathe == 0) - C.does_not_breathe = 1 + return FALSE + if(C.does_not_breathe == FALSE) + C.does_not_breathe = TRUE to_chat(src, span_notice("We stop breathing, as we no longer need to.")) - return 1 + return TRUE else - C.does_not_breathe = 0 + C.does_not_breathe = FALSE to_chat(src, span_notice("We resume breathing, as we now need to again.")) - return 0 + return FALSE diff --git a/code/game/gamemodes/changeling/powers/shriek.dm b/code/datums/components/antags/changeling/powers/shriek.dm similarity index 67% rename from code/game/gamemodes/changeling/powers/shriek.dm rename to code/datums/components/antags/changeling/powers/shriek.dm index e5112133e1..ed2e25ae5e 100644 --- a/code/game/gamemodes/changeling/powers/shriek.dm +++ b/code/datums/components/antags/changeling/powers/shriek.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/resonant_shriek name = "Resonant Shriek" desc = "Our lungs and vocal cords shift, allowing us to briefly emit a noise that deafens and confuses the weak-minded." @@ -22,32 +23,33 @@ set name = "Resonant Shriek (20)" set desc = "Emits a high-frequency sound that confuses and deafens organics, blows out nearby lights, and overloads synthetics' sensors." - var/datum/changeling/changeling = changeling_power(20,0,100,CONSCIOUS) - if(!changeling) return 0 + var/datum/component/antag/changeling/changeling = changeling_power(20,0,100,CONSCIOUS) + if(!changeling) + return FALSE + if(changeling.is_on_cooldown(CHANGELING_SCREECH)) + to_chat(src, span_notice("We are still recovering. We will be able to screech again in [(changeling.get_cooldown(CHANGELING_SCREECH) - world.time)/10] seconds.")) + return if(is_muzzled()) to_chat(src, span_danger("Mmmf mrrfff!")) - return 0 + return FALSE if(ishuman(src)) var/mob/living/carbon/human/H = src if(H.silent) to_chat(src, span_danger("You can't speak!")) - return 0 + return FALSE - if(world.time < (changeling.last_shriek + 10 SECONDS) ) - to_chat(src, span_warning("We are still recovering from our last shriek...")) - return 0 if(!isturf(loc)) to_chat(src, span_warning("Shrieking here would be a bad idea.")) - return 0 + return FALSE src.break_cloak() //No more invisible shrieking changeling.chem_charges -= 20 var/range = 4 - if(src.mind.changeling.recursive_enhancement) + if(changeling.recursive_enhancement) range = range * 2 to_chat(src, span_notice("We are extra loud.")) @@ -55,7 +57,8 @@ var/list/affected = list() for(var/mob/living/M in range(range, src)) if(iscarbon(M)) - if(!M.mind || !M.mind.changeling) + var/datum/component/antag/changeling/m_comp = M.GetComponent(/datum/component/antag/changeling) + if(!M.mind || !m_comp) if(M.get_ear_protection() >= 2) continue to_chat(M, span_danger("You hear an extremely loud screeching sound! It \ @@ -76,14 +79,15 @@ affected += M for(var/obj/machinery/light/L in range(range, src)) - L.on = 1 + L.on = TRUE L.broken() - changeling.last_shriek = world.time + changeling.set_cooldown(CHANGELING_SCREECH, 10 SECONDS) + addtimer(CALLBACK(src, PROC_REF(changeling_screech_ready)), 10 SECONDS, TIMER_DELETE_ME) add_attack_logs(src,affected,"Used resonant shriek") feedback_add_details("changeling_powers","RS") - return 1 + return TRUE //EMP version /mob/proc/changeling_dissonant_shriek() @@ -91,28 +95,29 @@ set name = "Dissonant Shriek (20)" set desc = "We shift our vocal cords to release a high-frequency sound that overloads nearby electronics." - var/datum/changeling/changeling = changeling_power(20,0,100,CONSCIOUS) - if(!changeling) return 0 + var/datum/component/antag/changeling/changeling = changeling_power(20,0,100,CONSCIOUS) + if(!changeling) + return FALSE if(is_muzzled()) to_chat(src, span_danger("Mmmf mrrfff!")) - return 0 + return FALSE if(ishuman(src)) var/mob/living/carbon/human/H = src if(H.silent) to_chat(src, span_danger("You can't speak!")) - return 0 + return FALSE - if(world.time < (changeling.last_shriek + 10 SECONDS) ) - to_chat(src, span_warning("We are still recovering from our last shriek...")) - return 0 + if(changeling.is_on_cooldown(CHANGELING_SCREECH)) + to_chat(src, span_notice("We are still recovering. We will be able to screech again in [(changeling.get_cooldown(CHANGELING_SCREECH) - world.time)/10] seconds.")) + return if(!isturf(loc)) to_chat(src, span_warning("Shrieking here would be a bad idea.")) - return 0 + return FALSE - src.break_cloak() //No more invisible shrieking + break_cloak() //No more invisible shrieking changeling.chem_charges -= 20 @@ -120,23 +125,24 @@ var/range_med = 2 var/range_light = 4 var/range_long = 6 - if(src.mind.changeling.recursive_enhancement) + if(changeling.recursive_enhancement) range_heavy = range_heavy * 2 range_med = range_med * 2 range_light = range_light * 2 range_long = range_long * 2 to_chat(src, span_notice("We are extra loud.")) - src.mind.changeling.recursive_enhancement = 0 + changeling.recursive_enhancement = FALSE - visible_message(span_notice("[src] appears to shout.")) - - add_attack_logs(src,null,"Use dissonant shriek") - - for(var/obj/machinery/light/L in range(5, src)) - L.on = 1 + for(var/obj/machinery/light/L in range(range_light, src)) + L.on = TRUE L.broken() - empulse(get_turf(src), range_heavy, range_light, 1) + empulse(get_turf(src), range_heavy, range_med, range_light, range_long) - changeling.last_shriek = world.time + changeling.set_cooldown(CHANGELING_SCREECH, 10 SECONDS) + addtimer(CALLBACK(src, PROC_REF(changeling_screech_ready)), 10 SECONDS, TIMER_DELETE_ME) + visible_message(span_notice("[src] appears to shout.")) + add_attack_logs(src,null,"Use dissonant shriek") + return TRUE - return 1 +/mob/proc/changeling_screech_ready() + to_chat(src, span_notice("We are ready to release another screech.")) diff --git a/code/game/gamemodes/changeling/powers/silence_sting.dm b/code/datums/components/antags/changeling/powers/silence_sting.dm similarity index 83% rename from code/game/gamemodes/changeling/powers/silence_sting.dm rename to code/datums/components/antags/changeling/powers/silence_sting.dm index c94713b9b1..2ae21a6b27 100644 --- a/code/game/gamemodes/changeling/powers/silence_sting.dm +++ b/code/datums/components/antags/changeling/powers/silence_sting.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/silence_sting name = "Silence Sting" desc = "We silently sting a human, completely silencing them for a short time." @@ -5,7 +6,7 @@ enhancedtext = "Silence duration is extended." ability_icon_state = "ling_sting_mute" genomecost = 2 - allowduringlesserform = 1 + allowduringlesserform = TRUE verbpath = /mob/proc/changeling_silence_sting /mob/proc/changeling_silence_sting() @@ -14,12 +15,14 @@ set desc="Sting target" var/mob/living/carbon/T = changeling_sting(10,/mob/proc/changeling_silence_sting) - if(!T) return 0 + var/datum/component/antag/changeling/comp = is_changeling(src) + if(!T) + return FALSE add_attack_logs(src,T,"Silence sting (changeling)") var/duration = 30 - if(src.mind.changeling.recursive_enhancement) + if(comp.recursive_enhancement) duration = duration + 10 to_chat(src, span_notice("They will be unable to cry out in fear for a little longer.")) T.silent += duration feedback_add_details("changeling_powers","SS") - return 1 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/transform.dm b/code/datums/components/antags/changeling/powers/transform.dm similarity index 77% rename from code/game/gamemodes/changeling/powers/transform.dm rename to code/datums/components/antags/changeling/powers/transform.dm index 606beda59e..2925e1fb3b 100644 --- a/code/game/gamemodes/changeling/powers/transform.dm +++ b/code/datums/components/antags/changeling/powers/transform.dm @@ -10,8 +10,9 @@ set category = "Changeling" set name = "Transform (5)" - var/datum/changeling/changeling = changeling_power(5,1,0) - if(!changeling) return + var/datum/component/antag/changeling/changeling = changeling_power(5,1,0) + if(!changeling) + return if(!isturf(loc)) to_chat(src, span_warning("Transforming here would be a bad idea.")) @@ -22,7 +23,8 @@ names += "[DNA.name]" var/S = tgui_input_list(src, "Select the target DNA:", "Target DNA", names) - if(!S) return + if(!S) + return var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S) if(!chosen_dna) @@ -38,13 +40,12 @@ H.set_species(newSpecies) qdel_swap(src.dna, chosen_dna.dna.Clone()) - src.dna.b_type = "AB+" //This is needed to avoid blood rejection bugs. The fact that the blood type might not match up w/ records could be a *FEATURE* too. if(ishuman(src)) var/mob/living/carbon/human/H = src H.identifying_gender = chosen_dna.identifying_gender H.flavor_texts = chosen_dna.flavour_texts ? chosen_dna.flavour_texts.Copy() : null - src.real_name = chosen_dna.name - src.UpdateAppearance() + real_name = chosen_dna.name + UpdateAppearance() domutcheck(src, null) UpdateAppearance() changeling_update_languages(changeling.absorbed_languages) @@ -55,11 +56,7 @@ for(var/datum/modifier/mod in chosen_dna.genMods) self.modifiers.Add(mod.type) - - remove_verb(src, /mob/proc/changeling_transform) - spawn(10) - add_verb(src, /mob/proc/changeling_transform) - src.regenerate_icons() + regenerate_icons() feedback_add_details("changeling_powers","TR") - return 1 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/transform_sting.dm b/code/datums/components/antags/changeling/powers/transform_sting.dm similarity index 91% rename from code/game/gamemodes/changeling/powers/transform_sting.dm rename to code/datums/components/antags/changeling/powers/transform_sting.dm index 5b4e99f5b2..158f617383 100644 --- a/code/game/gamemodes/changeling/powers/transform_sting.dm +++ b/code/datums/components/antags/changeling/powers/transform_sting.dm @@ -12,9 +12,9 @@ set name = "Transformation sting (40)" set desc="Sting target" - var/datum/changeling/changeling = changeling_power(40) + var/datum/component/antag/changeling/changeling = changeling_power(40) if(!changeling) - return 0 + return FALSE var/list/names = list() for(var/datum/dna/DNA in changeling.absorbed_dna) @@ -30,10 +30,10 @@ var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_transformation_sting) if(!T) - return 0 + return FALSE if((HUSK in T.mutations) || (!ishuman(T) && !issmall(T))) to_chat(src, span_warning("Our sting appears ineffective against its DNA.")) - return 0 + return FALSE add_attack_logs(src,T,"Transformation sting (changeling)") T.visible_message(span_warning("[T] transforms!")) qdel_swap(T.dna, chosen_dna.Clone()) @@ -41,4 +41,4 @@ T.UpdateAppearance() domutcheck(T, null) feedback_add_details("changeling_powers","TS") - return 1 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/unfat_sting.dm b/code/datums/components/antags/changeling/powers/unfat_sting.dm similarity index 73% rename from code/game/gamemodes/changeling/powers/unfat_sting.dm rename to code/datums/components/antags/changeling/powers/unfat_sting.dm index 57321859f2..c5d2c05e07 100644 --- a/code/game/gamemodes/changeling/powers/unfat_sting.dm +++ b/code/datums/components/antags/changeling/powers/unfat_sting.dm @@ -1,3 +1,4 @@ +//Updated /datum/power/changeling/unfat_sting name = "Unfat Sting" desc = "We silently sting a human, forcing them to rapidly metabolize their fat." @@ -10,9 +11,10 @@ set desc = "Sting target" var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_unfat_sting) - if(!T) return 0 + if(!T) + return FALSE add_attack_logs(src,T,"Unfat sting (changeling)") to_chat(T, span_danger("you feel a small prick as stomach churns violently and you become to feel skinnier.")) - T.adjust_nutrition(-100) + T.adjust_nutrition(-max(100, T.nutrition/1.15)) //Decrease their nutrition by 100 or 85%, whatever is higher. Ex: 1000 nutrition becomes ~130. 6000 nutrition becomes 800. feedback_add_details("changeling_powers","US") - return 1 + return TRUE diff --git a/code/datums/components/antags/changeling/powers/visible_camouflage.dm b/code/datums/components/antags/changeling/powers/visible_camouflage.dm new file mode 100644 index 0000000000..922981b282 --- /dev/null +++ b/code/datums/components/antags/changeling/powers/visible_camouflage.dm @@ -0,0 +1,92 @@ +/datum/power/changeling/visible_camouflage + name = "Camouflage" + desc = "We rapidly shape the color of our skin and secrete easily reversible dye on our clothes, to blend in with our surroundings. \ + We are undetectable, so long as we move slowly.(Toggle)" + helptext = "Running, and performing most acts will reveal us. Our chemical regeneration is halted while we are hidden." + enhancedtext = "Can run while hidden." + ability_icon_state = "ling_camoflage" + genomecost = 3 + verbpath = /mob/proc/changeling_visible_camouflage + +//Hide us from anyone who would do us harm. +/mob/proc/changeling_visible_camouflage() + set category = "Changeling" + set name = "Visible Camouflage (10)" + set desc = "Turns yourself almost invisible, as long as you move slowly." + var/datum/component/antag/changeling/changeling = changeling_power(0,0,100,CONSCIOUS) + if(!changeling) + return + + if(ishuman(src)) + var/mob/living/carbon/human/H = src + + if(changeling.cloaked) + changeling.cloaked = FALSE + return TRUE + if(H.has_modifier_of_type(/datum/modifier/changeling_camouflage)) //If they double-clicked the button while invis. + to_chat(H, span_warning("We are already camouflaged!")) + return TRUE + + //We delay the check, so that people can uncloak without needing 10 chemicals to do so. + changeling = changeling_power(10,0,100,CONSCIOUS) + + if(!changeling) + return FALSE + changeling.chem_charges -= 10 + + to_chat(H, span_notice("We vanish from sight, and will remain hidden, so long as we move carefully.")) + changeling.cloaked = TRUE + if(changeling.recursive_enhancement) + to_chat(src, span_notice("We may move at our normal speed while hidden.")) + H.add_modifier(/datum/modifier/changeling_camouflage/recursive, 0) + else + H.add_modifier(/datum/modifier/changeling_camouflage, 0) + +/datum/modifier/changeling_camouflage + name = "Camoflauge" + desc = "We are near-impossible to see." + var/must_walk = TRUE + var/datum/component/antag/changeling/comp + var/old_regen_rate + var/mob/living/carbon/human/owner + + +/datum/modifier/changeling_camouflage/recursive + must_walk = FALSE + +/datum/modifier/changeling_camouflage/can_apply(var/mob/living/L, var/suppress_failure = FALSE) + comp = L.GetComponent(/datum/component/antag/changeling) + if(!comp) + return FALSE + +/datum/modifier/changeling_camouflage/on_applied() + comp = holder.GetComponent(/datum/component/antag/changeling) + if(must_walk) + holder.set_m_intent(I_WALK) + old_regen_rate = comp.chem_recharge_rate + comp.chem_recharge_rate = 0 + animate(holder,alpha = 255, alpha = 10, time = 10) + +/datum/modifier/changeling_camouflage/on_expire() + animate(holder,alpha = 10, alpha = 255, time = 10) + owner.invisibility = initial(owner.invisibility) + holder.visible_message(span_warning("[holder] suddenly fades in, seemingly from nowhere!"), + span_notice("We revert our camouflage, revealing ourselves.")) + holder.set_m_intent(I_RUN) + comp.cloaked = FALSE + comp.chem_recharge_rate = old_regen_rate + comp = null + owner = null + +/datum/modifier/changeling_camouflage/tick() + if(holder.m_intent != I_WALK && must_walk) // Moving too fast uncloaks you. + expire(silent = TRUE) + if(!comp.cloaked) + expire(silent = TRUE) + if(holder.stat) // Dead or unconscious lings can't stay cloaked. + expire(silent = TRUE) + if(holder.incapacitated(INCAPACITATION_DISABLED)) // Stunned lings also can't stay cloaked. + expire(silent = TRUE) + if(comp.chem_recharge_rate != 0) //Without this, there is an exploit that can be done, if one buys engorged chem sacks while cloaked. + old_regen_rate += comp.chem_recharge_rate + comp.chem_recharge_rate = 0 diff --git a/code/datums/components/waddle.dm b/code/datums/components/traits/waddle.dm similarity index 100% rename from code/datums/components/waddle.dm rename to code/datums/components/traits/waddle.dm diff --git a/code/datums/managed_browsers/changelingevolution.dm b/code/datums/managed_browsers/changelingevolution.dm deleted file mode 100644 index e09511fef5..0000000000 --- a/code/datums/managed_browsers/changelingevolution.dm +++ /dev/null @@ -1,167 +0,0 @@ -/client - var/datum/managed_browser/changelingevolution/changelingevolution = null - -/datum/managed_browser/changelingevolution - base_browser_id = "evolution_tree" - title = "Evolution Tree" - size_x = 480 - size_y = 600 - var/textbody = null - -/datum/managed_browser/changelingevolution/New(client/new_client) - if(!new_client.mob || !new_client.mob.mind || !new_client.mob.mind.changeling) - message_admins("[new_client] tried to access changeling evolutions while not changeling.") - qdel(src) - - ..() - -/datum/managed_browser/changelingevolution/Destroy() - if(my_client) - my_client.changelingevolution = null - return ..() - -/datum/managed_browser/changelingevolution/get_html() - var/list/dat = list("") - var/geneticpoints_current = my_client.mob.mind.changeling.geneticpoints - var/geneticpoints_max = my_client.mob.mind.changeling.max_geneticpoints - - dat += "
Genetic Points Available: [geneticpoints_current] / [geneticpoints_max]
" - dat += "Obtain more by feeding on your own kind.

" - dat += "What am I?

" - dat += "Inherent" - dat += "Armor" - dat += "Weapons" - dat += "Stings" - dat += "Shrieks" - dat += "Health" - dat += "Enhancements
" - if(textbody) - dat += "" - dat += "[textbody]" - dat += "
" - dat += "" - - return dat.Join() - -/datum/managed_browser/changelingevolution/Topic(href, href_list[]) - if(!my_client) - return FALSE - - if(href_list["close"]) - return - - if(href_list["inherent"]) - generate_abilitylist(CHANGELING_POWER_INHERENT) - - if(href_list["armor"]) - generate_abilitylist(CHANGELING_POWER_ARMOR) - - if(href_list["weapons"]) - generate_abilitylist(CHANGELING_POWER_WEAPONS) - - if(href_list["stings"]) - generate_abilitylist(CHANGELING_POWER_STINGS) - - if(href_list["shrieks"]) - generate_abilitylist(CHANGELING_POWER_SHRIEKS) - - if(href_list["health"]) - generate_abilitylist(CHANGELING_POWER_HEALTH) - - if(href_list["enhancements"]) - generate_abilitylist(CHANGELING_POWER_ENHANCEMENTS) - - if(href_list["evolve"]) - var/datum/mind/M = my_client.mob.mind - var/datum/changeling/C = my_client.mob.mind.changeling - var/datum/power/changeling/Thepower = href_list["evolve"] - - for (var/datum/power/changeling/P in powerinstances) - if(P.name == Thepower) - Thepower = P - break - - if(!istype(M)) - return - - if(Thepower == null) - to_chat(M.current, "Purchase failed. Inform a dev of this error.") - return - - if(Thepower in C.purchased_powers) - to_chat(M.current, "You already have this ability! Inform a dev of this error.") /// Should not be possible - return - - if(C.geneticpoints < Thepower.genomecost) - to_chat(M.current, "We cannot evolve this... yet. We must acquire more DNA.") - return - - C.purchased_powers += Thepower /// Set it to purchased - C.geneticpoints -= Thepower.genomecost - generate_abilitylist(Thepower.power_category) /// Refresh the UI - - my_client.mob.mind.changeling.purchasePower(M, Thepower) - - if(href_list["tutorial"]) - textbody = "
" + span_red("What am I?") + "

" - textbody += "" - textbody += span_white("You are a changeling, a creature empowered with genetic-based abilities that change your body in bizarre ways.") - textbody += span_white(" It's probably best the crew doesn't know about your power -- at least not right away.") + "

" - textbody += span_white("What a changeling " + span_italics("is"), + " however, is up to you. Are you a strange alien impersonating crew? Are you a") - textbody += span_white(" normal crewmember infected with a parasite? An experiment gone wrong? It's up to you to make the story.") + "

" - textbody += span_white("Of course, you need to know how it works to begin with.") + "

" - textbody += span_white("Your abilities cost chemicals that your body will slowly regenerate with varying speeds based on enhancements obtained.") - textbody += span_white(" There are a set of inherent abilities you will always have while the rest may be purchased through genomes.") + "

" - textbody += span_white("You may obtain more genomes if you find another changeling and absorb them, but this is not required. If you've found ") - textbody += span_white("your abilities aren't to your liking, you have up to two re-adapts available, and these may be refilled by absorbing anyone -- including monkeys.") + "

" - textbody += span_white("Good luck and remember, killing isn't always the end goal.") - display() - -/datum/managed_browser/changelingevolution/proc/generate_abilitylist(cat) - var/list/ability_list = list() - var/info = "" - var/catname = "" - for(var/datum/power/changeling/P in powerinstances) - if(P.power_category == cat) - ability_list[++ability_list.len] = P - switch(cat) - if(CHANGELING_POWER_INHERENT) - catname = "Inherent" - info = "These powers are inherent to your kind and will always be accessible, provided you have the chemicals to use them." - if(CHANGELING_POWER_ARMOR) - catname = "Armor" - info = "These abilities will provide you with space protection -- and potentially armor." - if(CHANGELING_POWER_WEAPONS) - catname = "Weapons" - info = "These abilities will provide you the means to fight back." - if(CHANGELING_POWER_STINGS) - catname = "Stings" - info = "These abilities provide the means to sting organic beings for various effects -- though you must be close enough, and they must have exposed flesh." - if(CHANGELING_POWER_SHRIEKS) - catname = "Shrieks" - info = "These abilities enhance your vocal chords, empowering your screams." - if(CHANGELING_POWER_HEALTH) - catname = "Health" - info = "These abilities will enhance your health or aid you in mending your wounds." - if(CHANGELING_POWER_ENHANCEMENTS) - catname = "Enhancements" - info = "These abilities enhance you in various ways." - create_textbody(ability_list, catname, info) - -/datum/managed_browser/changelingevolution/proc/create_textbody(ability_list, cat, catinfo) - textbody = "
" + span_red("[cat] Skills") + "
" - textbody += "" + span_white("[catinfo]") + "


" - for(var/A in ability_list) - var/datum/power/changeling/powerdata = A - textbody += "
" + span_red(span_bold("[initial(powerdata.name)]")) + "
" - textbody += span_white("[initial(powerdata.desc)]") + "

" - textbody += span_white(span_italics("[powerdata.helptext]")) + "
" - if(powerdata.enhancedtext != "") - textbody += span_white(span_bold("WHEN ENHANCED: ") + span_italics("[powerdata.enhancedtext]")) + "
" - if(powerdata in my_client.mob.mind.changeling.purchased_powers) - textbody += "
" + span_white(span_italics(span_bold("This ability is already evolved!"))) + "
" - else if(cat != "Inherent") - textbody += "
Cost: [powerdata.genomecost]
" - textbody += "
Evolve
" - textbody += "" - display() diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 3e3c508a37..515d1ec24a 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -31,6 +31,8 @@ var/assigned_role var/special_role + var/datum/antag_holder/antag_holder + var/role_alt_title var/datum/job/assigned_job @@ -41,7 +43,6 @@ var/has_been_rev = 0//Tracks if this mind has been a rev or not var/datum/faction/faction //associated faction - var/datum/changeling/changeling //changeling holder var/rev_cooldown = 0 var/tcrystals = 0 @@ -70,15 +71,18 @@ /datum/mind/New(var/key) src.key = key purchase_log = list() + antag_holder = new ..() /datum/mind/proc/transfer_to(mob/living/new_character, force = FALSE) if(!istype(new_character)) to_world_log("## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn") - if(current) //remove ourself from our old body's mind variable - if(changeling) + var/datum/component/antag/changeling/comp + if(current) + comp = is_changeling(current) //remove ourself from our old body's mind variable + if(comp) current.remove_changeling_powers() - remove_verb(current, /datum/changeling/proc/EvolutionMenu) + remove_verb(current, /mob/proc/EvolutionMenu) current.mind = null if(new_character.mind) //remove any mind currently in our new body's mind variable @@ -87,7 +91,7 @@ current = new_character //link ourself to our new body new_character.mind = src //and link our new body to ourself - if(changeling) + if(comp) new_character.make_changeling() if(active || force) @@ -471,7 +475,7 @@ role_alt_title = null assigned_job = null //faction = null //Uncommenting this causes a compile error due to 'undefined type', fucked if I know. - changeling = null + //changeling = null //TODO: Figure out where this is all used and move it from mind to mob. initial_account = null objectives = list() special_verbs = list() diff --git a/code/game/antagonist/outsider/wizard.dm b/code/game/antagonist/outsider/wizard.dm index 86d93cbc1a..7f5562b2b2 100644 --- a/code/game/antagonist/outsider/wizard.dm +++ b/code/game/antagonist/outsider/wizard.dm @@ -131,3 +131,7 @@ Made a proc so this is not repeated 14 (or more) times.*/ to_chat(src, span_warning("I don't feel strong enough without my hat.")) return 0 return 1 + +/datum/antagonist/wizard/remove_antagonist(datum/mind/player, show_message, implanted) + . = ..() + player.current.spellremove() diff --git a/code/game/antagonist/station/changeling.dm b/code/game/antagonist/station/changeling.dm index 6cd1f09614..d09a357dcb 100644 --- a/code/game/antagonist/station/changeling.dm +++ b/code/game/antagonist/station/changeling.dm @@ -13,12 +13,25 @@ antaghud_indicator = "hudchangeling" /datum/antagonist/changeling/get_special_objective_text(var/datum/mind/player) - return "
Changeling ID: [player.changeling.changelingID].
Genomes Absorbed: [player.changeling.absorbedcount]" + if(player.current) + var/datum/component/antag/changeling/comp = player.current.GetComponent(/datum/component/antag/changeling) + if(comp) + return "
Changeling ID: [comp.changelingID].
Genomes Absorbed: [comp.absorbedcount]" /datum/antagonist/changeling/update_antag_mob(var/datum/mind/player) ..() player.current.make_changeling() +/datum/antagonist/changeling/remove_antagonist(datum/mind/player, show_message, implanted) + . = ..() + var/datum/component/antag/changeling/comp = player.current.GetComponent(/datum/component/antag/changeling) + if(comp) + comp.owner.remove_changeling_powers() + remove_verb(comp.owner, /mob/proc/EvolutionMenu) + comp.RemoveComponent() + if(comp.owner.mind) + comp.owner.mind.antag_holder.changeling = null + /datum/antagonist/changeling/create_objectives(var/datum/mind/changeling) if(!..()) return @@ -77,12 +90,12 @@ return 1 return 0 -/datum/antagonist/changeling/print_player_full(var/datum/mind/ply) - var/text = print_player_lite(ply) +/datum/antagonist/changeling/print_player_full(var/datum/mind/player) + var/text = print_player_lite(player) - if(ply.changeling) - var/datum/changeling/ling_datum = ply.changeling - text += " (had [ling_datum.max_geneticpoints] genomes)" - text += "
Bought [english_list(ling_datum.purchased_powers_history)]." + var/datum/component/antag/changeling/comp = player.current.GetComponent(/datum/component/antag/changeling) + if(comp) + text += " (had [comp.max_geneticpoints] genomes)" + text += "
Bought [english_list(comp.purchased_powers_history)]." return text diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm deleted file mode 100644 index f394afec9c..0000000000 --- a/code/game/gamemodes/changeling/changeling_powers.dm +++ /dev/null @@ -1,244 +0,0 @@ -var/global/list/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") - -/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) - var/list/datum/absorbed_dna/absorbed_dna = list() - var/list/absorbed_languages = list() // Necessary because of set_species stuff - var/absorbedcount = 0 - var/lingabsorbedcount = 1 //Starts at one, because that's us - var/chem_charges = 20 - var/chem_recharge_rate = 0.5 - var/chem_storage = 50 - var/sting_range = 1 - var/changelingID = "Changeling" - var/geneticdamage = 0 - var/isabsorbing = 0 - var/geneticpoints = 7 - var/max_geneticpoints = 7 - var/readapts = 1 - var/max_readapts = 2 - var/list/purchased_powers = list() - var/mimicing = "" - var/cloaked = 0 - var/armor_deployed = 0 //This is only used for changeling_generic_equip_all_slots() at the moment. - var/recursive_enhancement = 0 //Used to power up other abilities from the ling power with the same name. - var/list/purchased_powers_history = list() //Used for round-end report, includes respec uses too. - var/last_shriek = null // world.time when the ling last used a shriek. - var/next_escape = 0 // world.time when the ling can next use Escape Restraints - var/thermal_sight = FALSE // Is our Vision Augmented? With thermals? - -/datum/changeling/New(var/gender=FEMALE) - ..() - if(possible_changeling_IDs.len) - changelingID = pick(possible_changeling_IDs) - possible_changeling_IDs -= changelingID - changelingID = "[changelingID]" - else - changelingID = "[rand(1,999)]" - -/datum/changeling/proc/regenerate() - chem_charges = min(max(0, chem_charges+chem_recharge_rate), chem_storage) - geneticdamage = max(0, geneticdamage-1) - -/datum/changeling/proc/GetDNA(var/dna_owner) - for(var/datum/absorbed_dna/DNA in absorbed_dna) - if(dna_owner == DNA.name) - return DNA - -/mob/proc/absorbDNA(var/datum/absorbed_dna/newDNA) - var/datum/changeling/changeling = null - if(src.mind && src.mind.changeling) - changeling = src.mind.changeling - if(!changeling) - return - - for(var/language in newDNA.languages) - changeling.absorbed_languages |= language - - changeling_update_languages(changeling.absorbed_languages) - - if(!changeling.GetDNA(newDNA.name)) // Don't duplicate - I wonder if it's possible for it to still be a different DNA? DNA code could use a rewrite - changeling.absorbed_dna += newDNA - -//Restores our verbs. It will only restore verbs allowed during lesser (monkey) form if we are not human -/mob/proc/make_changeling() - - if(!mind) return - if(!mind.changeling) mind.changeling = new /datum/changeling(gender) - - add_verb(src, /datum/changeling/proc/EvolutionMenu) - add_verb(src, /mob/proc/changeling_respec) - add_language("Changeling") - - var/lesser_form = !ishuman(src) - - if(!powerinstances.len) - for(var/P in powers) - powerinstances += new P() - - // Code to auto-purchase free powers. - for(var/datum/power/changeling/P in powerinstances) - if(!P.genomecost) // Is it free? - if(!(P in mind.changeling.purchased_powers)) // Do we not have it already? - mind.changeling.purchasePower(mind, P.name, 0)// Purchase it. Don't remake our verbs, we're doing it after this. - - for(var/datum/power/changeling/P in mind.changeling.purchased_powers) - if(P.isVerb) - if(lesser_form && !P.allowduringlesserform) continue - if(!(P in src.verbs)) - add_verb(src, P.verbpath) - if(P.make_hud_button) - if(!src.ability_master) - src.ability_master = new /obj/screen/movable/ability_master(src) - src.ability_master.add_ling_ability( - object_given = src, - verb_given = P.verbpath, - name_given = P.name, - ability_icon_given = P.ability_icon_state, - arguments = list() - ) - - for(var/language in languages) - mind.changeling.absorbed_languages |= language - - var/mob/living/carbon/human/H = src - if(istype(H)) - var/saved_dna = H.dna.Clone() /// Prevent transform from breaking. - var/datum/absorbed_dna/newDNA = new(H.real_name, saved_dna, H.species.name, H.languages, H.identifying_gender, H.flavor_texts, H.modifiers) - absorbDNA(newDNA) - - return 1 - -//removes our changeling verbs -/mob/proc/remove_changeling_powers() - if(!mind || !mind.changeling) return - for(var/datum/power/changeling/P in mind.changeling.purchased_powers) - if(P.isVerb) - remove_verb(src, P.verbpath) - var/obj/screen/ability/verb_based/changeling/C = ability_master.get_ability_by_proc_ref(P.verbpath) - if(C) - ability_master.remove_ability(C) - - -//Helper proc. Does all the checks and stuff for us to avoid copypasta -/mob/proc/changeling_power(var/required_chems=0, var/required_dna=0, var/max_genetic_damage=100, var/max_stat=0) - - if(!src.mind) return - if(!iscarbon(src)) return - - var/datum/changeling/changeling = src.mind.changeling - if(!changeling) - to_world_log("[src] has the changeling_transform() verb but is not a changeling.") - return - - if(src.stat > max_stat) - to_chat(src, span_warning("We are incapacitated.")) - return - - if(changeling.absorbed_dna.len < required_dna) - to_chat(src, span_warning("We require at least [required_dna] samples of compatible DNA.")) - return - - if(changeling.chem_charges < required_chems) - to_chat(src, span_warning("We require at least [required_chems] units of chemicals to do that!")) - return - - if(changeling.geneticdamage > max_genetic_damage) - to_chat(src, span_warning("Our genomes are still reassembling. We need time to recover first.")) - return - - return changeling - -//Used to dump the languages from the changeling datum into the actual mob. -/mob/proc/changeling_update_languages(var/updated_languages) - languages = list() - for(var/language in updated_languages) - languages += language - - //This isn't strictly necessary but just to be safe... - add_language("Changeling") - - ////////// - //STINGS// //They get a pretty header because there's just so fucking many of them ;_; - ////////// - -/turf/proc/AdjacentTurfsRangedSting() - //Yes this is snowflakey, but I couldn't get it to work any other way.. -Luke - var/list/allowed = list( - /obj/structure/table, - /obj/structure/closet, - /obj/structure/frame, - /obj/structure/target_stake, - /obj/structure/cable, - /obj/structure/disposalpipe, - /obj/machinery, - /mob - ) - - var/L[] = new() - for(var/turf/simulated/t in oview(src,1)) - var/add = 1 - if(t.density) - add = 0 - if(add && LinkBlocked(src,t)) - add = 0 - if(add && TurfBlockedNonWindow(t)) - add = 0 - for(var/obj/O in t) - if(O.density) - add = 0 - break - if(istype(O, /obj/machinery/door)) - //not sure why this doesn't fire on LinkBlocked() - add = 0 - break - for(var/type in allowed) - if (istype(O, type)) - add = 1 - break - if(!add) - break - if(add) - L.Add(t) - return L - - -/mob/proc/sting_can_reach(mob/M as mob, sting_range = 1) - if(M.loc == src.loc) - return 1 //target and source are in the same thing - if(!isturf(src.loc) || !isturf(M.loc)) - to_chat(src, span_warning("We cannot reach \the [M] with a sting!")) - return 0 //One is inside, the other is outside something. - // Maximum queued turfs set to 25; I don't *think* anything raises sting_range above 2, but if it does the 25 may need raising - if(!SSpathfinder.get_path_jps(src, get_turf(src), get_turf(M), max_path_length = 25)) //CHOMPEdit - to_chat(src, span_warning("We cannot find a path to sting \the [M] by!")) - return 0 - return 1 - -//Handles the general sting code to reduce on copypasta (seeming as somebody decided to make SO MANY dumb abilities) -/mob/proc/changeling_sting(var/required_chems=0, var/verb_path) - var/datum/changeling/changeling = changeling_power(required_chems) - if(!changeling) return - - var/list/victims = list() - for(var/mob/living/carbon/C in oview(changeling.sting_range)) - victims += C - var/mob/living/carbon/T = tgui_input_list(src, "Who will we sting?", "Sting!", victims) - - if(!T) - return - if(T.isSynthetic()) - to_chat(src, span_notice("We are unable to pierce the outer shell of [T].")) - return - if(!(T in view(changeling.sting_range))) return - if(!sting_can_reach(T, changeling.sting_range)) return - if(!changeling_power(required_chems)) return - - changeling.chem_charges -= required_chems - changeling.sting_range = 1 - remove_verb(src, verb_path) - spawn(10) add_verb(src, verb_path) - - to_chat(src, span_notice("We stealthily sting [T].")) - if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting - to_chat(T, span_warning("You feel a tiny prick.")) - return diff --git a/code/game/gamemodes/changeling/modularchangling.dm b/code/game/gamemodes/changeling/modularchangling.dm deleted file mode 100644 index 84d5294575..0000000000 --- a/code/game/gamemodes/changeling/modularchangling.dm +++ /dev/null @@ -1,359 +0,0 @@ -// READ: Don't use the apostrophe in name or desc. Causes script errors. - -//Ling power's evolution menu entry datum should be contained alongside the mob proc for the actual power, in their own file. - -var/list/powers = subtypesof(/datum/power/changeling) //needed for the badmin verb for now -var/list/datum/power/changeling/powerinstances = list() - -/datum/power //Could be used by other antags too - var/name = "Power" - var/desc = "Placeholder" - var/helptext = "" - var/enhancedtext = "" - var/isVerb = 1 // Is it an active power, or passive? - var/verbpath // Path to a verb that contains the effects. - var/make_hud_button = 1 // Is this ability significant enough to dedicate screen space for a HUD button? - var/ability_icon_state = null // icon_state for icons for the ability HUD. Must be in screen_spells.dmi. - -/datum/power/changeling - var/allowduringlesserform = 0 - var/genomecost = 500000 // Cost for the changling to evolve this power. - - -// Modularchangling, totally stolen from the new player panel. YAYY -/datum/changeling/proc/EvolutionMenu()//The new one - set name = "-Evolution Menu-" - set category = "Changeling" - set desc = "Adapt yourself carefully." - - if(!usr || !usr.mind || !usr.mind.changeling) return - src = usr.mind.changeling - - if(!powerinstances.len) - for(var/P in powers) - powerinstances += new P() - - var/dat = "Changling Evolution Menu" - - //javascript, the part that does most of the work~ - dat += {" - - - - - - - "} - - //body tag start + onload and onkeypress (onkeyup) javascript event calls - dat += "" - - //title + search bar - dat += {" - - - - - - - - -
- Changling Evolution Menu
- Hover over a power to see more information
- Current evolution points left to evolve with: [geneticpoints]
- Absorb other changelings to acquire more evolution points -

-

- Search: -
- - "} - - //player table header - dat += {" - - "} - - var/i = 1 - for(var/datum/power/changeling/P in powerinstances) - var/ownsthis = 0 - - if(P in purchased_powers) - ownsthis = 1 - - - var/color = "#e6e6e6" - if(i%2 == 0) - color = "#f2f2f2" - - - dat += {" - - - - - - "} - - i++ - - - //player table ending - dat += {" -
- - - Evolve [P] - Cost: [ownsthis ? "Purchased" : P.genomecost] - -
-
-
- - - - "} - - usr << browse(dat, "window=powers;size=900x480") - - -/datum/changeling/Topic(href, href_list) - ..() - if(!ismob(usr)) - return - - if(href_list["P"]) - var/datum/mind/M = usr.mind - if(!istype(M)) - return - purchasePower(M, href_list["P"]) - call(/datum/changeling/proc/EvolutionMenu)() - - - -/datum/changeling/proc/purchasePower(var/datum/mind/M, var/Pname, var/remake_verbs = 1) - if(!M || !M.changeling) - return - - var/datum/power/changeling/Thepower = Pname - - - for (var/datum/power/changeling/P in powerinstances) - //to_world("[P] - [Pname] = [P.name == Pname ? "True" : "False"]") - if(P.name == Pname) - Thepower = P - break - - - if(Thepower == null) - to_chat(M.current, "This is awkward. Changeling power purchase failed, please report this bug to a coder!") - return - - if(Thepower in purchased_powers) - to_chat(M.current, "We have already evolved this ability!") - return - - - if(geneticpoints < Thepower.genomecost) - to_chat(M.current, "We cannot evolve this... yet. We must acquire more DNA.") - return - - geneticpoints -= Thepower.genomecost - - purchased_powers += Thepower - - if(Thepower.genomecost > 0) - purchased_powers_history.Add("[Pname] ([Thepower.genomecost] points)") - - if(Thepower.make_hud_button && Thepower.isVerb) - if(!M.current.ability_master) - M.current.ability_master = new /obj/screen/movable/ability_master(M.current) - M.current.ability_master.add_ling_ability( - object_given = M.current, - verb_given = Thepower.verbpath, - name_given = Thepower.name, - ability_icon_given = Thepower.ability_icon_state, - arguments = list() - ) - - if(!Thepower.isVerb && Thepower.verbpath) - call(M.current, Thepower.verbpath)() - else if(remake_verbs) - M.current.make_changeling() diff --git a/code/game/gamemodes/changeling/powers/death_sting.dm b/code/game/gamemodes/changeling/powers/death_sting.dm deleted file mode 100644 index ed82ec0b35..0000000000 --- a/code/game/gamemodes/changeling/powers/death_sting.dm +++ /dev/null @@ -1,23 +0,0 @@ -/datum/power/changeling/DeathSting - name = "Death Sting" - desc = "We silently sting a human, filling him with potent chemicals. His rapid death is all but assured." - ability_icon_state = "ling_sting_death" - genomecost = 10 - verbpath = /mob/proc/changeling_DEATHsting - -/mob/proc/changeling_DEATHsting() - set category = "Changeling" - set name = "Death Sting (40)" - set desc = "Causes spasms onto death." - - var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_DEATHsting) - if(!T) - return 0 - add_attack_logs(src,T,"Death sting (changeling)") - to_chat(T, span_danger("You feel a small prick and your chest becomes tight.")) - T.silent = 10 - T.Paralyse(10) - T.make_jittery(100) - if(T.reagents) T.reagents.add_reagent(REAGENT_ID_LEXORIN, 40) - feedback_add_details("changeling_powers","DTHS") - return 1 diff --git a/code/game/gamemodes/changeling/powers/fleshmend.dm b/code/game/gamemodes/changeling/powers/fleshmend.dm deleted file mode 100644 index cf4c8d8a2a..0000000000 --- a/code/game/gamemodes/changeling/powers/fleshmend.dm +++ /dev/null @@ -1,41 +0,0 @@ -/datum/power/changeling/fleshmend - name = "Fleshmend" - desc = "Begins a slow regeneration of our form. Does not effect stuns or chemicals." - helptext = "Can be used while unconscious." - enhancedtext = "Healing is twice as effective." - ability_icon_state = "ling_fleshmend" - genomecost = 1 - verbpath = /mob/proc/changeling_fleshmend - -//Starts healing you every second for 50 seconds. Can be used whilst unconscious. -/mob/proc/changeling_fleshmend() - set category = "Changeling" - set name = "Fleshmend (10)" - set desc = "Begins a slow rengeration of our form. Does not effect stuns or chemicals." - - var/datum/changeling/changeling = changeling_power(10,0,100,UNCONSCIOUS) - if(!changeling) - return 0 - src.mind.changeling.chem_charges -= 10 - - var/mob/living/carbon/human/C = src - var/heal_amount = 2 - if(src.mind.changeling.recursive_enhancement) - heal_amount = heal_amount * 2 - to_chat(src, span_notice("We will heal much faster.")) - - spawn(0) - to_chat(src, span_notice("We begin to heal ourselves.")) - for(var/i = 0, i<50,i++) - if(C) - C.adjustBruteLoss(-heal_amount) - C.adjustOxyLoss(-heal_amount) - C.adjustFireLoss(-heal_amount) - sleep(1 SECOND) - - remove_verb(src, /mob/proc/changeling_fleshmend) - spawn(50 SECONDS) - to_chat(src, span_notice("Our regeneration has slowed to normal levels.")) - add_verb(src, /mob/proc/changeling_fleshmend) - feedback_add_details("changeling_powers","FM") - return 1 diff --git a/code/game/gamemodes/changeling/powers/visible_camouflage.dm b/code/game/gamemodes/changeling/powers/visible_camouflage.dm deleted file mode 100644 index 3692044219..0000000000 --- a/code/game/gamemodes/changeling/powers/visible_camouflage.dm +++ /dev/null @@ -1,72 +0,0 @@ -/datum/power/changeling/visible_camouflage - name = "Camouflage" - desc = "We rapidly shape the color of our skin and secrete easily reversible dye on our clothes, to blend in with our surroundings. \ - We are undetectable, so long as we move slowly.(Toggle)" - helptext = "Running, and performing most acts will reveal us. Our chemical regeneration is halted while we are hidden." - enhancedtext = "Can run while hidden." - ability_icon_state = "ling_camoflage" - genomecost = 3 - verbpath = /mob/proc/changeling_visible_camouflage - -//Hide us from anyone who would do us harm. -/mob/proc/changeling_visible_camouflage() - set category = "Changeling" - set name = "Visible Camouflage (10)" - set desc = "Turns yourself almost invisible, as long as you move slowly." - - - if(ishuman(src)) - var/mob/living/carbon/human/H = src - - if(H.mind.changeling.cloaked) - H.mind.changeling.cloaked = 0 - return 1 - - //We delay the check, so that people can uncloak without needing 10 chemicals to do so. - var/datum/changeling/changeling = changeling_power(10,0,100,CONSCIOUS) - - if(!changeling) - return 0 - changeling.chem_charges -= 10 - var/old_regen_rate = H.mind.changeling.chem_recharge_rate - - to_chat(H, span_notice("We vanish from sight, and will remain hidden, so long as we move carefully.")) - H.mind.changeling.cloaked = 1 - H.mind.changeling.chem_recharge_rate = 0 - animate(src,alpha = 255, alpha = 10, time = 10) - - var/must_walk = TRUE - if(src.mind.changeling.recursive_enhancement) - must_walk = FALSE - to_chat(src, span_notice("We may move at our normal speed while hidden.")) - - if(must_walk) - H.set_m_intent(I_WALK) - - var/remain_cloaked = TRUE - while(remain_cloaked) //This loop will keep going until the player uncloaks. - sleep(1 SECOND) // Sleep at the start so that if something invalidates a cloak, it will drop immediately after the check and not in one second. - - if(H.m_intent != I_WALK && must_walk) // Moving too fast uncloaks you. - remain_cloaked = 0 - if(!H.mind.changeling.cloaked) - remain_cloaked = 0 - if(H.stat) // Dead or unconscious lings can't stay cloaked. - remain_cloaked = 0 - if(H.incapacitated(INCAPACITATION_DISABLED)) // Stunned lings also can't stay cloaked. - remain_cloaked = 0 - - if(mind.changeling.chem_recharge_rate != 0) //Without this, there is an exploit that can be done, if one buys engorged chem sacks while cloaked. - old_regen_rate += mind.changeling.chem_recharge_rate //Unfortunately, it has to occupy this part of the proc. This fixes it while at the same time - mind.changeling.chem_recharge_rate = 0 //making sure nobody loses out on their bonus regeneration after they're done hiding. - - - - H.invisibility = initial(invisibility) - visible_message(span_warning("[src] suddenly fades in, seemingly from nowhere!"), - span_notice("We revert our camouflage, revealing ourselves.")) - H.set_m_intent(I_RUN) - H.mind.changeling.cloaked = 0 - H.mind.changeling.chem_recharge_rate = old_regen_rate - - animate(src,alpha = 10, alpha = 255, time = 10) diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 5dc5aa5c04..60d51d2372 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -614,7 +614,8 @@ GLOBAL_LIST_EMPTY(all_objectives) n_p ++ else if (ticker.current_state == GAME_STATE_PLAYING) for(var/mob/living/carbon/human/P in player_list) - if(P.client && !(P.mind.changeling) && P.mind!=owner) + var/datum/component/antag/changeling/comp = P.GetComponent(/datum/component/antag/changeling) + if(P.client && !(comp) && P.mind!=owner) n_p ++ target_amount = min(target_amount, n_p) @@ -622,8 +623,10 @@ GLOBAL_LIST_EMPTY(all_objectives) return target_amount /datum/objective/absorb/check_completion() - if(owner && owner.changeling && owner.changeling.absorbed_dna && (owner.changeling.absorbedcount >= target_amount)) - return 1 + if(owner) + var/datum/component/antag/changeling/comp = owner.GetComponent(/datum/component/antag/changeling) + if(comp && comp.absorbed_dna && (comp.absorbedcount >= target_amount)) + return 1 else return 0 diff --git a/code/game/objects/items/devices/scanners/sleevemate.dm b/code/game/objects/items/devices/scanners/sleevemate.dm index 8eb668c720..5b30c4a92e 100644 --- a/code/game/objects/items/devices/scanners/sleevemate.dm +++ b/code/game/objects/items/devices/scanners/sleevemate.dm @@ -302,6 +302,10 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob if(H.resleeve_lock && stored_mind.loaded_from_ckey != H.resleeve_lock) to_chat(usr,span_warning("\The [H] is protected from impersonation!")) return + //Changeling bodies. Only changelings can be put in them. + if(H.changeling_locked && !is_changeling(stored_mind)) + to_chat(usr,span_warning("\The [H] is too complex to put this mind into!")) + return usr.visible_message(span_warning("[usr] begins uploading someone's mind into [target]!"),span_notice("You begin uploading a mind into [target]!")) if(do_after(usr,35 SECONDS,target)) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index a0b4ff6eff..67d9614a00 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -543,6 +543,8 @@ ADMIN_VERB(respawn_character, (R_ADMIN|R_REJUVINATE), "Spawn Character", "(Re)Sp if(antag_data) antag_data.add_antagonist(new_character.mind) antag_data.place_mob(new_character) + if(new_character.mind.antag_holder) + new_character.mind.antag_holder.apply_antags(new_character) if(new_character.mind) new_character.mind.loaded_from_ckey = picked_ckey diff --git a/code/modules/examine/examine.dm b/code/modules/examine/examine.dm index 9926a15f60..3732c0116f 100644 --- a/code/modules/examine/examine.dm +++ b/code/modules/examine/examine.dm @@ -119,7 +119,15 @@ /mob/proc/update_examine_panel(var/atom/A) if(client) - var/is_antag = ((mind && mind.special_role) || isobserver(src)) //ghosts don't have minds + var/is_antag + if((mind && mind.special_role) || isobserver(src)) //todo: Phase out special_role entirely and make it check for component/antag entirely. + is_antag = TRUE + else if(isliving(src)) + var/datum/component/antag/comp = GetComponent(/datum/component/antag) + if(comp) + is_antag = TRUE + else if(mind && mind.antag_holder.is_antag()) + is_antag = TRUE client.update_description_holders(A, is_antag) SSstatpanels.set_examine_tab(client) diff --git a/code/modules/mob/_modifiers/changeling.dm b/code/modules/mob/_modifiers/changeling.dm index 739e29c8bd..7adba04093 100644 --- a/code/modules/mob/_modifiers/changeling.dm +++ b/code/modules/mob/_modifiers/changeling.dm @@ -39,8 +39,9 @@ else L = holder - - L.mind.changeling.chem_charges = between(0, L.mind.changeling.chem_charges - chem_maintenance, L.mind.changeling.chem_storage) + if(L) + var/datum/component/antag/changeling/comp = L.GetComponent(/datum/component/antag/changeling) + comp.chem_charges = between(0, comp.chem_charges - chem_maintenance, comp.chem_storage) /datum/modifier/changeling/thermal_sight name = "Thermal Adaptation" @@ -63,7 +64,7 @@ expire() return - var/datum/changeling/changeling = L.changeling_power(0,0,100,CONSCIOUS) + var/datum/component/antag/changeling/changeling = L.changeling_power(0,0,100,CONSCIOUS) if(!changeling) expire() @@ -85,7 +86,7 @@ L = holder if(L) - var/datum/changeling/changeling = L.changeling_power(0,0,100,CONSCIOUS) + var/datum/component/antag/changeling/changeling = L.changeling_power(0,0,100,CONSCIOUS) if(changeling) changeling.thermal_sight = FALSE diff --git a/code/modules/mob/language/outsider.dm b/code/modules/mob/language/outsider.dm index 2c1b95b5f5..1e1796753f 100644 --- a/code/modules/mob/language/outsider.dm +++ b/code/modules/mob/language/outsider.dm @@ -8,9 +8,9 @@ flags = RESTRICTED | HIVEMIND /datum/language/ling/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) - - if(speaker.mind && speaker.mind.changeling) - ..(speaker,message,speaker.mind.changeling.changelingID) + var/datum/component/antag/changeling/comp = speaker.GetComponent(/datum/component/antag/changeling) + if(speaker.mind && comp) + ..(speaker,message,comp.changelingID) else ..(speaker,message) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 3f345d3495..a5bce5d507 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -54,10 +54,11 @@ stop_flying() //Handle snowflake ling stuff. - if(mind && mind.changeling) + var/datum/component/antag/changeling/comp = is_changeling(src) + if(comp) // If the ling is capable of revival, don't allow them to see deadchat. - if(mind.changeling.chem_charges >= CHANGELING_STASIS_COST) - if(mind.changeling.max_geneticpoints >= 0) // Absorbed lings don't count, as they can't revive. + if(comp.chem_charges >= CHANGELING_STASIS_COST) + if(comp.max_geneticpoints >= 0) // Absorbed lings don't count, as they can't revive. forbid_seeing_deadchat = TRUE //Handle brain slugs. diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 1a0c0d49ce..87b2ab4e5f 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -123,11 +123,11 @@ if(suit.cell) cell_status = "[suit.cell.charge]/[suit.cell.maxcharge]" . += "Suit charge: [cell_status]" - if(mind) - if(mind.changeling) - . += "Chemical Storage: [mind.changeling.chem_charges]" - . += "Genetic Damage Time: [mind.changeling.geneticdamage]" - . += "Re-Adaptations: [mind.changeling.readapts]/[mind.changeling.max_readapts]" + var/datum/component/antag/changeling/comp = is_changeling(src) + if(comp) + . += "Chemical Storage: [comp.chem_charges]" + . += "Genetic Damage Time: [comp.geneticdamage]" + . += "Re-Adaptations: [comp.readapts]/[comp.max_readapts]" if(species) species.get_status_tab_items(src) diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 6f922a4b72..1227ee53de 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -56,8 +56,9 @@ //This is called when we want different types of 'cloaks' to stop working, e.g. when attacking. /mob/living/carbon/human/break_cloak() - if(mind && mind.changeling) //Changeling visible camo - mind.changeling.cloaked = 0 + var/datum/component/antag/changeling/comp = is_changeling(src) + if(comp) //Changeling visible camo + comp.cloaked = 0 if(istype(back, /obj/item/rig)) //Ninja cloak var/obj/item/rig/suit = back for(var/obj/item/rig_module/stealth_field/cloaker in suit.installed_modules) @@ -67,7 +68,8 @@ dr.uncloak() /mob/living/carbon/human/is_cloaked() - if(mind && mind.changeling && mind.changeling.cloaked) // Ling camo. + var/datum/component/antag/changeling/comp = is_changeling(src) + if(comp && comp.cloaked) // Ling camo. return TRUE else if(istype(back, /obj/item/rig)) //Ninja cloak var/obj/item/rig/suit = back diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index d6bc666b93..5a7cf566e5 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1807,14 +1807,19 @@ playsound_local(src,pick(GLOB.scarySounds),50, 1, -1) /mob/living/carbon/human/proc/handle_changeling() - if(mind && mind.changeling) - mind.changeling.regenerate() + var/datum/component/antag/changeling/comp = is_changeling(src) + if(!comp) + if(mind && hud_used) + ling_chem_display.invisibility = INVISIBILITY_ABSTRACT + return + else + comp.regenerate() if(hud_used) ling_chem_display.invisibility = INVISIBILITY_NONE // ling_chem_display.maptext = "
[round(mind.changeling.chem_charges)]
" - switch(mind.changeling.chem_storage) + switch(comp.chem_storage) if(1 to 50) - switch(mind.changeling.chem_charges) + switch(comp.chem_charges) if(0 to 9) ling_chem_display.icon_state = "ling_chems0" if(10 to 19) @@ -1828,7 +1833,7 @@ if(50) ling_chem_display.icon_state = "ling_chems50" if(51 to 80) //This is a crappy way of checking for engorged sacs... - switch(mind.changeling.chem_charges) + switch(comp.chem_charges) if(0 to 9) ling_chem_display.icon_state = "ling_chems0e" if(10 to 19) @@ -1847,9 +1852,6 @@ ling_chem_display.icon_state = "ling_chems70e" if(80) ling_chem_display.icon_state = "ling_chems80e" - else - if(mind && hud_used) - ling_chem_display.invisibility = INVISIBILITY_ABSTRACT /mob/living/carbon/human/handle_shock() ..() diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index ecf7878418..b7fb7b67cc 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -101,8 +101,9 @@ voice_sub = get_id_name() if(voice_sub) return voice_sub - if(mind && mind.changeling && mind.changeling.mimicing) - return mind.changeling.mimicing + var/datum/component/antag/changeling/comp = is_changeling(src) + if(comp && comp.mimicing) + return comp.mimicing if(GetSpecialVoice()) return GetSpecialVoice() return real_name diff --git a/code/modules/mob/living/carbon/human/species/station/traits/negative.dm b/code/modules/mob/living/carbon/human/species/station/traits/negative.dm index d61c2d9538..ad394e1cf4 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits/negative.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits/negative.dm @@ -519,14 +519,13 @@ if(H.hallucination >= hallucination_cap && H.loneliness_stage >= warning_cap) return - // Outpost 21 addition begin - extended loneliness mechanics - if(H.mind && H.mind.changeling) // We are never alone~ + var/datum/component/antag/changeling/comp = H.GetComponent(/datum/component/antag/changeling) + if(comp) // We are never alone~ H.loneliness_stage = 0 return if(H.has_brain_worms()) // Brain friends! sub_loneliness(H) return - // Outpost 21 addition end // Vored? Not gonna get frightened. if(isbelly(H.loc)) diff --git a/code/modules/organs/blood.dm b/code/modules/organs/blood.dm index 1afe1fc568..9e2dffb437 100644 --- a/code/modules/organs/blood.dm +++ b/code/modules/organs/blood.dm @@ -371,9 +371,10 @@ var/const/CE_STABLE_THRESHOLD = 0.5 if(!our) log_debug("Failed to re-initialize blood datums on [src]!") return - - - if(blood_incompatible(injected.data["blood_type"],our.data["blood_type"],injected.data["species"],our.data["species"]) ) + if(is_changeling(src)) //Changelings don't reject blood! + vessel.add_reagent(REAGENT_ID_BLOOD, amount, injected.data) + vessel.update_total() + else if(blood_incompatible(injected.data["blood_type"],our.data["blood_type"],injected.data["species"],our.data["species"]) ) reagents.add_reagent(REAGENT_ID_TOXIN,amount * 0.5) reagents.update_total() else diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 4996de5848..6f3c5942ab 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -146,6 +146,27 @@ loc.update_icon() return amount_used +/// Recharges the cell over time. 100 per second multiplied by the multiplier. +/obj/item/cell/proc/gradual_charge(iterations, multiplier, sparks, mob/living/user) + var/charged_object = src + if(!multiplier || iterations <= 0) + return + if(user) //If we have a user, time to check to make sure they're adjacent/holding us! + if(istype(loc, /obj/machinery/power/apc)) //We're in an APC! + charged_object = loc + if(loc != user && !(user in orange(1,charged_object))) //If we have a user fed to us, they need to hold us or be in range of us. + if(loc.loc && !istype(loc.loc, user)) //Are we inside of something the user is holding? + return + charge += 100 * multiplier + if(charge > maxcharge) + charge = maxcharge + if(sparks) + var/T = get_turf(src) + new /obj/effect/effect/sparks(T) + update_icon() + iterations-- + addtimer(CALLBACK(src, PROC_REF(gradual_charge), iterations, multiplier, sparks, user), 1 SECOND, TIMER_DELETE_ME) + /obj/item/cell/examine(mob/user) . = ..() diff --git a/code/modules/resleeving/autoresleever.dm b/code/modules/resleeving/autoresleever.dm index 49f904b3d5..5134213a9d 100644 --- a/code/modules/resleeving/autoresleever.dm +++ b/code/modules/resleeving/autoresleever.dm @@ -165,6 +165,7 @@ new_character.key = player_key //Were they any particular special role? If so, copy. + if(new_character.mind) new_character.mind.loaded_from_ckey = picked_ckey new_character.mind.loaded_from_slot = picked_slot @@ -172,6 +173,8 @@ if(antag_data) antag_data.add_antagonist(new_character.mind) antag_data.place_mob(new_character) + if(new_character.mind.antag_holder) + new_character.mind.antag_holder.apply_antags(new_character) for(var/lang in ghost_client.prefs.alternate_languages) var/datum/language/chosen_language = GLOB.all_languages[lang] diff --git a/code/modules/resleeving/computers.dm b/code/modules/resleeving/computers.dm index 70979922d9..05cefc9889 100644 --- a/code/modules/resleeving/computers.dm +++ b/code/modules/resleeving/computers.dm @@ -387,6 +387,12 @@ active_mr = null return + //Changeling lock. + if(sleever.get_occupant().changeling_locked && !is_changeling(active_mr.mind_ref)) + set_temp("Error: Mind incompatible with body", "danger") + active_mr = null + return TRUE + var/list/subtargets = list() for(var/mob/living/carbon/human/H in sleever.get_occupant()) if(H.resleeve_lock && active_mr.ckey != H.resleeve_lock) diff --git a/code/modules/resleeving/infocore_records.dm b/code/modules/resleeving/infocore_records.dm index 44778f995a..12faf38004 100644 --- a/code/modules/resleeving/infocore_records.dm +++ b/code/modules/resleeving/infocore_records.dm @@ -4,6 +4,7 @@ //////////////////////////////// /mob/living/carbon/human/var/resleeve_lock +/mob/living/carbon/human/var/changeling_locked /mob/living/carbon/human/var/original_player /////// Mind-backup record /////// @@ -79,6 +80,7 @@ //These may or may not be set, mostly irrelevant since it's just a body record. var/ckey var/locked + var/changeling_locked var/client/client_ref var/datum/mind/mind_ref var/synthetic @@ -123,6 +125,11 @@ //Person OOCly doesn't want people impersonating them locked = ckeylock + //The mob is a changeling, don't allow anyone to possess them. Not using locked as locked gives OOC notices. + if(is_changeling(M)) + changeling_locked = TRUE + + var/datum/species/S = GLOB.all_species["[M.dna.species]"] if(S) // Force ckey locking if species is whitelisted diff --git a/code/modules/resleeving/machines.dm b/code/modules/resleeving/machines.dm index 4291f02287..67562dbc9a 100644 --- a/code/modules/resleeving/machines.dm +++ b/code/modules/resleeving/machines.dm @@ -493,8 +493,15 @@ if(!occupant.mind) log_debug("[occupant] didn't have a mind to check for vore_death, which may be problematic.") - if(occupant.mind && occupant.original_player && ckey(occupant.mind.key) != occupant.original_player) - log_and_message_admins("is now a cross-sleeved character. Body originally belonged to [occupant.real_name]. Mind is now [occupant.mind.name].",occupant) + if(occupant.mind) + if(occupant.original_player && ckey(occupant.mind.key) != occupant.original_player) + log_and_message_admins("is now a cross-sleeved character. Body originally belonged to [occupant.real_name]. Mind is now [occupant.mind.name].",occupant) + var/datum/antagonist/antag_data = get_antag_data(occupant.mind.special_role) + if(antag_data) + antag_data.add_antagonist(occupant.mind) + antag_data.place_mob(occupant) + if(occupant.mind.antag_holder) + occupant.mind.antag_holder.apply_antags(occupant) if(original_occupant) occupant = original_occupant diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm index 3f528fc416..934e89e7dd 100644 --- a/code/modules/tgui/modules/appearance_changer.dm +++ b/code/modules/tgui/modules/appearance_changer.dm @@ -593,6 +593,7 @@ if(DC.allowed(ui.user) || BR.ckey == ui.user.ckey) BD.load_record_to_body(BR) owner.resleeve_lock = BR.locked + owner.changeling_locked = BR.changeling_locked DC.selected_record = TRUE return TRUE if("view_stock_brec") @@ -621,7 +622,9 @@ return FALSE if(!DC.disk) return FALSE - if(owner.resleeve_lock) + if(owner.changeling_locked) + to_chat(ui.user, span_warning("ERROR: Record too complex. Disk does not have enough space to store this record.")) + else if(owner.resleeve_lock) var/answer = tgui_alert(ui.user,"This body record will be written to a disk and allow any mind to inhabit it. This is against the current body owner's configured OOC preferences for body impersonation. Please confirm that you have permission to do this, and are sure! Admins will be notified.","Mind Compatability",list("No","Yes")) if(!answer) return @@ -630,7 +633,7 @@ else message_admins("[ui.user] wrote an unlocked version of [owner.real_name]'s bodyrecord to a disk. Their preferences do not allow body impersonation, but may be allowed with OOC consent.") owner.resleeve_lock = FALSE // unlock it, even though it's only temp, so you don't get the warning every time - if(!owner.resleeve_lock && can_change(owner, APPEARANCE_RACE)) + if(!owner.changeling_locked && (!owner.resleeve_lock && can_change(owner, APPEARANCE_RACE))) // Create it from the mob if(DC.disk.stored) qdel_null(DC.disk.stored) diff --git a/code/modules/vore/eating/belly_obj_vr.dm b/code/modules/vore/eating/belly_obj_vr.dm index d2413838d3..632e99206b 100644 --- a/code/modules/vore/eating/belly_obj_vr.dm +++ b/code/modules/vore/eating/belly_obj_vr.dm @@ -1003,6 +1003,7 @@ /obj/belly/proc/digestion_death(mob/living/M) digested_prey_count++ add_attack_logs(owner, M, "Digested in [lowertext(name)]") + owner.changeling_obtain_dna(M) // Reverts TF on death. This fixes a bug with posibrains or similar, and also makes reforming easier. if(M.tf_mob_holder && M.tf_mob_holder.loc == M) @@ -1149,6 +1150,7 @@ // TODO - Find a way to make the absorbed prey share the effects with the pred. // Currently this is infeasible because reagent containers are designed to have a single my_atom, and we get // problems when A absorbs B, and then C absorbs A, resulting in B holding onto an invalid reagent container. + Pred.changeling_obtain_dna(Prey) //This is probably already the case, but for sub-prey, it won't be. if(M.loc != src) diff --git a/code/modules/vore/eating/inbelly_spawn.dm b/code/modules/vore/eating/inbelly_spawn.dm index b4a674c1c3..2d92d44d70 100644 --- a/code/modules/vore/eating/inbelly_spawn.dm +++ b/code/modules/vore/eating/inbelly_spawn.dm @@ -154,10 +154,10 @@ Please do not abuse this ability. if(antag_data) antag_data.add_antagonist(new_character.mind) antag_data.place_mob(new_character) - - if(new_character.mind) new_character.mind.loaded_from_ckey = picked_ckey new_character.mind.loaded_from_slot = picked_slot + if(new_character.mind.antag_holder) + new_character.mind.antag_holder.apply_antags(new_character) for(var/lang in prey.prefs.alternate_languages) var/datum/language/chosen_language = GLOB.all_languages[lang] diff --git a/modular_chomp/code/modules/organs/internal/malignant/malignant.dm b/modular_chomp/code/modules/organs/internal/malignant/malignant.dm index f11c3915a0..3050588e1c 100644 --- a/modular_chomp/code/modules/organs/internal/malignant/malignant.dm +++ b/modular_chomp/code/modules/organs/internal/malignant/malignant.dm @@ -67,7 +67,7 @@ || species.name == SPECIES_PROTEAN \ || species.name == SPECIES_REPLICANT) return FALSE - if(mind && mind.changeling) + if(is_changeling(src)) return FALSE var/obj/item/organ/internal/malignant/neworgan = new type_path( src, TRUE) diff --git a/tgui/packages/tgui/interfaces/ChangelingPanel/ChangelingPowerList.tsx b/tgui/packages/tgui/interfaces/ChangelingPanel/ChangelingPowerList.tsx new file mode 100644 index 0000000000..bb3ccd0c13 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChangelingPanel/ChangelingPowerList.tsx @@ -0,0 +1,75 @@ +import { useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Button, Divider, Input, Section, Stack } from 'tgui-core/components'; +import { createSearch } from 'tgui-core/string'; + +import { getButtonColor } from './functions'; +import type { powerData } from './types'; + +export const ChangeLingSearchableList = (props: { + title: string; + powerData: powerData[]; + points: number; +}) => { + const { act } = useBackend(); + + const { title, powerData, points } = props; + + const [searchText, setSearchText] = useState(''); + + const searcher = createSearch(searchText, (power: powerData) => { + return power.power_name; + }); + + const shownPowers = powerData.filter(searcher); + + return ( + +
+ + + setSearchText(value)} + /> + + + +
+ + {shownPowers + .sort((a, b) => a.power_name.localeCompare(b.power_name)) + .map((entry) => ( + + + + ))} + +
+
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChangelingPanel/functions.ts b/tgui/packages/tgui/interfaces/ChangelingPanel/functions.ts new file mode 100644 index 0000000000..1101aee6b9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChangelingPanel/functions.ts @@ -0,0 +1,12 @@ +import type { powerData } from './types'; + +export function getButtonColor(entry: powerData, availablePoints: number) { + if (entry.power_purchased) { + return undefined; + } + if (availablePoints < entry.power_cost) { + return 'red'; + } else { + return 'green'; + } +} diff --git a/tgui/packages/tgui/interfaces/ChangelingPanel/index.tsx b/tgui/packages/tgui/interfaces/ChangelingPanel/index.tsx new file mode 100644 index 0000000000..a488e32a1c --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChangelingPanel/index.tsx @@ -0,0 +1,43 @@ +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { Stack } from 'tgui-core/components'; + +import { ChangeLingSearchableList } from './ChangelingPowerList'; +import type { Data } from './types'; + +export const ChangelingPanel = (props) => { + const { data } = useBackend(); + + const { available_points, power_list } = data; + + const purchasedPowers = power_list.filter((entry) => entry.power_purchased); + const unpurchasedPowers = power_list.filter( + (entry) => !entry.power_purchased, + ); + + return ( + + + + + Current evolution points left to evolve with: {available_points} + + + + + + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/ChangelingPanel/types.ts b/tgui/packages/tgui/interfaces/ChangelingPanel/types.ts new file mode 100644 index 0000000000..9bc6007ef0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChangelingPanel/types.ts @@ -0,0 +1,13 @@ +import type { BooleanLike } from 'tgui-core/react'; + +export type Data = { + available_points: number; + power_list: powerData[]; +}; + +export type powerData = { + power_name: string; + power_cost: number; + power_purchased: BooleanLike; + power_desc: string; +}; diff --git a/vorestation.dme b/vorestation.dme index 7f1863b4e5..77c081ce1d 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -51,6 +51,7 @@ #include "code\__defines\blueprints.dm" #include "code\__defines\borg_overlays.dm" #include "code\__defines\callbacks.dm" +#include "code\__defines\changeling.dm" #include "code\__defines\chat.dm" #include "code\__defines\chemistry.dm" #include "code\__defines\chemistry_vr.dm" @@ -544,7 +545,50 @@ #include "code\datums\components\recursive_move.dm" #include "code\datums\components\resize_guard.dm" #include "code\datums\components\swarm.dm" -#include "code\datums\components\waddle.dm" +#include "code\datums\components\antags\antag.dm" +#include "code\datums\components\antags\changeling\changeling.dm" +#include "code\datums\components\antags\changeling\helpers\absorbed_dna.dm" +#include "code\datums\components\antags\changeling\helpers\generic_equip_procs.dm" +#include "code\datums\components\antags\changeling\powers\absorb.dm" +#include "code\datums\components\antags\changeling\powers\armblade.dm" +#include "code\datums\components\antags\changeling\powers\armor.dm" +#include "code\datums\components\antags\changeling\powers\augmented_eyesight.dm" +#include "code\datums\components\antags\changeling\powers\bioelectrogenesis.dm" +#include "code\datums\components\antags\changeling\powers\blind_sting.dm" +#include "code\datums\components\antags\changeling\powers\boost_range.dm" +#include "code\datums\components\antags\changeling\powers\cryo_sting.dm" +#include "code\datums\components\antags\changeling\powers\darkvision.dm" +#include "code\datums\components\antags\changeling\powers\deaf_sting.dm" +#include "code\datums\components\antags\changeling\powers\delayed_toxin_sting.dm" +#include "code\datums\components\antags\changeling\powers\digital_camo.dm" +#include "code\datums\components\antags\changeling\powers\electric_lockpick.dm" +#include "code\datums\components\antags\changeling\powers\endoarmor.dm" +#include "code\datums\components\antags\changeling\powers\enfeebling_string.dm" +#include "code\datums\components\antags\changeling\powers\engorged_glands.dm" +#include "code\datums\components\antags\changeling\powers\enrage.dm" +#include "code\datums\components\antags\changeling\powers\epinephrine_overdose.dm" +#include "code\datums\components\antags\changeling\powers\escape_restraints.dm" +#include "code\datums\components\antags\changeling\powers\extract_dna_sting.dm" +#include "code\datums\components\antags\changeling\powers\fabricate_clothing.dm" +#include "code\datums\components\antags\changeling\powers\fake_death.dm" +#include "code\datums\components\antags\changeling\powers\fleshmend.dm" +#include "code\datums\components\antags\changeling\powers\hivemind.dm" +#include "code\datums\components\antags\changeling\powers\lesser_form.dm" +#include "code\datums\components\antags\changeling\powers\lsd_sting.dm" +#include "code\datums\components\antags\changeling\powers\mimic_voice.dm" +#include "code\datums\components\antags\changeling\powers\panacea.dm" +#include "code\datums\components\antags\changeling\powers\para_sting.dm" +#include "code\datums\components\antags\changeling\powers\rapid_regen.dm" +#include "code\datums\components\antags\changeling\powers\recursive_enhancement.dm" +#include "code\datums\components\antags\changeling\powers\respec.dm" +#include "code\datums\components\antags\changeling\powers\revive.dm" +#include "code\datums\components\antags\changeling\powers\self_respiration.dm" +#include "code\datums\components\antags\changeling\powers\shriek.dm" +#include "code\datums\components\antags\changeling\powers\silence_sting.dm" +#include "code\datums\components\antags\changeling\powers\transform.dm" +#include "code\datums\components\antags\changeling\powers\transform_sting.dm" +#include "code\datums\components\antags\changeling\powers\unfat_sting.dm" +#include "code\datums\components\antags\changeling\powers\visible_camouflage.dm" #include "code\datums\components\crafting\crafting.dm" #include "code\datums\components\crafting\crafting_external.dm" #include "code\datums\components\crafting\recipes.dm" @@ -567,6 +611,7 @@ #include "code\datums\components\traits\burninlight.dm" #include "code\datums\components\traits\drippy.dm" #include "code\datums\components\traits\gargoyle.dm" +#include "code\datums\components\traits\waddle.dm" #include "code\datums\components\traits\nutrition_size_change.dm" #include "code\datums\components\traits\photosynth.dm" #include "code\datums\components\traits\weaver.dm" @@ -858,46 +903,7 @@ #include "code\game\gamemodes\objective.dm" #include "code\game\gamemodes\setupgame.dm" #include "code\game\gamemodes\calamity\calamity.dm" -#include "code\game\gamemodes\changeling\absorbed_dna.dm" #include "code\game\gamemodes\changeling\changeling.dm" -#include "code\game\gamemodes\changeling\changeling_powers.dm" -#include "code\game\gamemodes\changeling\generic_equip_procs.dm" -#include "code\game\gamemodes\changeling\modularchangling.dm" -#include "code\game\gamemodes\changeling\powers\absorb.dm" -#include "code\game\gamemodes\changeling\powers\armblade.dm" -#include "code\game\gamemodes\changeling\powers\armor.dm" -#include "code\game\gamemodes\changeling\powers\augmented_eyesight.dm" -#include "code\game\gamemodes\changeling\powers\bioelectrogenesis.dm" -#include "code\game\gamemodes\changeling\powers\blind_sting.dm" -#include "code\game\gamemodes\changeling\powers\boost_range.dm" -#include "code\game\gamemodes\changeling\powers\cryo_sting.dm" -#include "code\game\gamemodes\changeling\powers\darkvision.dm" -#include "code\game\gamemodes\changeling\powers\deaf_sting.dm" -#include "code\game\gamemodes\changeling\powers\delayed_toxin_sting.dm" -#include "code\game\gamemodes\changeling\powers\digital_camo.dm" -#include "code\game\gamemodes\changeling\powers\electric_lockpick.dm" -#include "code\game\gamemodes\changeling\powers\endoarmor.dm" -#include "code\game\gamemodes\changeling\powers\enfeebling_string.dm" -#include "code\game\gamemodes\changeling\powers\engorged_glands.dm" -#include "code\game\gamemodes\changeling\powers\enrage.dm" -#include "code\game\gamemodes\changeling\powers\epinephrine_overdose.dm" -#include "code\game\gamemodes\changeling\powers\escape_restraints.dm" -#include "code\game\gamemodes\changeling\powers\extract_dna_sting.dm" -#include "code\game\gamemodes\changeling\powers\fabricate_clothing.dm" -#include "code\game\gamemodes\changeling\powers\fake_death.dm" -#include "code\game\gamemodes\changeling\powers\fleshmend.dm" -#include "code\game\gamemodes\changeling\powers\hivemind.dm" -#include "code\game\gamemodes\changeling\powers\mimic_voice.dm" -#include "code\game\gamemodes\changeling\powers\panacea.dm" -#include "code\game\gamemodes\changeling\powers\rapid_regen.dm" -#include "code\game\gamemodes\changeling\powers\recursive_enhancement.dm" -#include "code\game\gamemodes\changeling\powers\respec.dm" -#include "code\game\gamemodes\changeling\powers\revive.dm" -#include "code\game\gamemodes\changeling\powers\self_respiration.dm" -#include "code\game\gamemodes\changeling\powers\shriek.dm" -#include "code\game\gamemodes\changeling\powers\silence_sting.dm" -#include "code\game\gamemodes\changeling\powers\transform.dm" -#include "code\game\gamemodes\changeling\powers\visible_camouflage.dm" #include "code\game\gamemodes\cult\construct_spells.dm" #include "code\game\gamemodes\cult\construct_spells_ch.dm" #include "code\game\gamemodes\cult\cult.dm"