[MIRROR] Changing changeling (Refactor) (#11142)

Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-07-04 19:54:55 -07:00
committed by GitHub
parent 9feb91ec4c
commit a0c273ce1f
83 changed files with 1385 additions and 1382 deletions

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]!"))

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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."))

View File

@@ -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

View File

@@ -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!"))

View File

@@ -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."))

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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."))

View File

@@ -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

View File

@@ -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."))

View File

@@ -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

View File

@@ -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,

View File

@@ -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 <b>Revive</b> 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 <b>Revive</b> verb when you are ready.")))

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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 = ""

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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))

View File

@@ -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

View File

@@ -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

View File

@@ -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."))

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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("<html><body>")
var/geneticpoints_current = my_client.mob.mind.changeling.geneticpoints
var/geneticpoints_max = my_client.mob.mind.changeling.max_geneticpoints
dat += "<center>Genetic Points Available: [geneticpoints_current] / [geneticpoints_max] <br>"
dat += "Obtain more by feeding on your own kind. <br> <hr>"
dat += "<a style='background-color:#c72121;' href='byond://?src=\ref[src];tutorial=1'>What am I?</a><br><hr>"
dat += "<a style='background-color:#c72121;' href='byond://?src=\ref[src];inherent=1'>Inherent</a>"
dat += "<a style='background-color:#c72121;' href='byond://?src=\ref[src];armor=1'>Armor</a>"
dat += "<a style='background-color:#c72121;' href='byond://?src=\ref[src];weapons=1'>Weapons</a>"
dat += "<a style='background-color:#c72121;' href='byond://?src=\ref[src];stings=1'>Stings</a>"
dat += "<a style='background-color:#c72121;' href='byond://?src=\ref[src];shrieks=1'>Shrieks</a>"
dat += "<a style='background-color:#c72121;' href='byond://?src=\ref[src];health=1'>Health</a>"
dat += "<a style='background-color:#c72121;' href='byond://?src=\ref[src];enhancements=1'>Enhancements</a></center>"
if(textbody)
dat += "<table border='1' style='width:100%; background-color:#000000;'>"
dat += "[textbody]"
dat += "</table>"
dat += "</body></html>"
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 = "<tr><th><center>" + span_red("What am I?") + "</center><br></th></tr>"
textbody += "<tr><td>"
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.") + "<br><br>"
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.") + "<br><br>"
textbody += span_white("Of course, you need to know how it works to begin with.") + "<br><br>"
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.") + "<br><br>"
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.") + "<br><br>"
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 = "<tr><th><center>" + span_red("[cat] Skills") + "<br></th></tr>"
textbody += "<tr><td>" + span_white("[catinfo]") + "</center><br><hr></td></tr>"
for(var/A in ability_list)
var/datum/power/changeling/powerdata = A
textbody += "<tr><td><center>" + span_red(span_bold("[initial(powerdata.name)]")) + "<br></center>"
textbody += span_white("[initial(powerdata.desc)]") + "<br><br>"
textbody += span_white(span_italics("[powerdata.helptext]")) + "<br>"
if(powerdata.enhancedtext != "")
textbody += span_white(span_bold("WHEN ENHANCED: ") + span_italics("[powerdata.enhancedtext]")) + "<br>"
if(powerdata in my_client.mob.mind.changeling.purchased_powers)
textbody += "<center>" + span_white(span_italics(span_bold("This ability is already evolved!"))) + "</center>"
else if(cat != "Inherent")
textbody += "<center>Cost: [powerdata.genomecost]</center>"
textbody += "<center><a style='background-color:#c72121;' href='byond://?src=\ref[src];evolve=[A]'>Evolve</a></center>"
textbody += "</td></tr>"
display()

View File

@@ -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()

View File

@@ -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()

View File

@@ -13,12 +13,25 @@
antaghud_indicator = "hudchangeling"
/datum/antagonist/changeling/get_special_objective_text(var/datum/mind/player)
return "<br><b>Changeling ID:</b> [player.changeling.changelingID].<br><b>Genomes Absorbed:</b> [player.changeling.absorbedcount]"
if(player.current)
var/datum/component/antag/changeling/comp = player.current.GetComponent(/datum/component/antag/changeling)
if(comp)
return "<br><b>Changeling ID:</b> [comp.changelingID].<br><b>Genomes Absorbed:</b> [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 += "<br>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 += "<br>Bought [english_list(comp.purchased_powers_history)]."
return text

View File

@@ -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

View File

@@ -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 = "<html><head><title>Changling Evolution Menu</title></head>"
//javascript, the part that does most of the work~
dat += {"
<head>
<script type='text/javascript'>
var locked_tabs = new Array();
function updateSearch(){
var filter_text = document.getElementById('filter');
var filter = filter_text.value.toLowerCase();
if(complete_list != null && complete_list != ""){
var mtbl = document.getElementById("maintable_data_archive");
mtbl.innerHTML = complete_list;
}
if(filter.value == ""){
return;
}else{
var maintable_data = document.getElementById('maintable_data');
var ltr = maintable_data.getElementsByTagName("tr");
for ( var i = 0; i < ltr.length; ++i )
{
try{
var tr = ltr\[i\];
if(tr.getAttribute("id").indexOf("data") != 0){
continue;
}
var ltd = tr.getElementsByTagName("td");
var td = ltd\[0\];
var lsearch = td.getElementsByTagName("b");
var search = lsearch\[0\];
//var inner_span = li.getElementsByTagName("span")\[1\] //Should only ever contain one element.
//document.write("<p>"+search.innerText+"<br>"+filter+"<br>"+search.innerText.indexOf(filter))
if ( search.innerText.toLowerCase().indexOf(filter) == -1 )
{
//document.write("a");
//ltr.removeChild(tr);
td.innerHTML = "";
i--;
}
}catch(err) { }
}
}
var count = 0;
var index = -1;
var debug = document.getElementById("debug");
locked_tabs = new Array();
}
function expand(id,name,desc,helptext,enhancedtext,power,ownsthis){
clearAll();
var span = document.getElementById(id);
body = "<table><tr><td>";
body += "</td><td align='center'>";
body += "<font size='2'><b>"+desc+"</b></font> <BR>"
body += "<font size='2'><font color = 'red'><b>"+helptext+"</b></font></font><BR>"
if(enhancedtext)
{
body += "<font size='2'><font color = 'blue'>Recursive Enhancement Effect: <b>"+enhancedtext+"</b></font></font><BR>"
}
if(!ownsthis)
{
body += "<a href='byond://?src=\ref[src];P="+power+"'>Evolve</a>"
}
body += "</td><td align='center'>";
body += "</td></tr></table>";
span.innerHTML = body
}
function clearAll(){
var spans = document.getElementsByTagName('span');
for(var i = 0; i < spans.length; i++){
var span = spans\[i\];
var id = span.getAttribute("id");
if(!(id.indexOf("item")==0))
continue;
var pass = 1;
for(var j = 0; j < locked_tabs.length; j++){
if(locked_tabs\[j\]==id){
pass = 0;
break;
}
}
if(pass != 1)
continue;
span.innerHTML = "";
}
}
function addToLocked(id,link_id,notice_span_id){
var link = document.getElementById(link_id);
var decision = link.getAttribute("name");
if(decision == "1"){
link.setAttribute("name","2");
}else{
link.setAttribute("name","1");
removeFromLocked(id,link_id,notice_span_id);
return;
}
var pass = 1;
for(var j = 0; j < locked_tabs.length; j++){
if(locked_tabs\[j\]==id){
pass = 0;
break;
}
}
if(!pass)
return;
locked_tabs.push(id);
var notice_span = document.getElementById(notice_span_id);
notice_span.innerHTML = "<font color='red'>Locked</font> ";
//link.setAttribute("onClick","attempt('"+id+"','"+link_id+"','"+notice_span_id+"');");
//document.write("removeFromLocked('"+id+"','"+link_id+"','"+notice_span_id+"')");
//document.write("aa - "+link.getAttribute("onClick"));
}
function attempt(ab){
return ab;
}
function removeFromLocked(id,link_id,notice_span_id){
//document.write("a");
var index = 0;
var pass = 0;
for(var j = 0; j < locked_tabs.length; j++){
if(locked_tabs\[j\]==id){
pass = 1;
index = j;
break;
}
}
if(!pass)
return;
locked_tabs\[index\] = "";
var notice_span = document.getElementById(notice_span_id);
notice_span.innerHTML = "";
//var link = document.getElementById(link_id);
//link.setAttribute("onClick","addToLocked('"+id+"','"+link_id+"','"+notice_span_id+"')");
}
function selectTextField(){
var filter_text = document.getElementById('filter');
filter_text.focus();
filter_text.select();
}
</script>
</head>
"}
//body tag start + onload and onkeypress (onkeyup) javascript event calls
dat += "<body onload='selectTextField(); updateSearch();' onkeyup='updateSearch();'>"
//title + search bar
dat += {"
<table width='560' align='center' cellspacing='0' cellpadding='5' id='maintable'>
<tr id='title_tr'>
<td align='center'>
<font size='5'><b>Changling Evolution Menu</b></font><br>
Hover over a power to see more information<br>
Current evolution points left to evolve with: [geneticpoints]<br>
Absorb other changelings to acquire more evolution points
<p>
</td>
</tr>
<tr id='search_tr'>
<td align='center'>
<b>Search:</b> <input type='text' id='filter' value='' style='width:300px;'>
</td>
</tr>
</table>
"}
//player table header
dat += {"
<span id='maintable_data_archive'>
<table width='560' align='center' cellspacing='0' cellpadding='5' id='maintable_data'>"}
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 += {"
<tr id='data[i]' name='[i]' onClick="addToLocked('item[i]','data[i]','notice_span[i]')">
<td align='center' bgcolor='[color]'>
<span id='notice_span[i]'></span>
<a id='link[i]'
onmouseover='expand("item[i]","[P.name]","[P.desc]","[P.helptext]","[P.enhancedtext]","[P]",[ownsthis])'
>
<span id='search[i]'><b>Evolve [P] - Cost: [ownsthis ? "Purchased" : P.genomecost]</b></span>
</a>
<br><span id='item[i]'></span>
</td>
</tr>
"}
i++
//player table ending
dat += {"
</table>
</span>
<script type='text/javascript'>
var maintable = document.getElementById("maintable_data_archive");
var complete_list = maintable.innerHTML;
</script>
</body></html>
"}
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()

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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))

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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.

View File

@@ -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)

View File

@@ -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

View File

@@ -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 = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#dd66dd'>[round(mind.changeling.chem_charges)]</font></div>"
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()
..()

View File

@@ -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

View File

@@ -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))

View File

@@ -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

View File

@@ -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)
. = ..()

View File

@@ -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]

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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]

View File

@@ -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)

View File

@@ -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 (
<Stack.Item basis="49%" grow>
<Section fill title={title}>
<Stack vertical fill>
<Stack.Item>
<Input
mr="10px"
fluid
value={searchText}
placeholder={'Search for ' + title + '...'}
onChange={(value: string) => setSearchText(value)}
/>
</Stack.Item>
<Divider />
<Stack.Item grow>
<Section fill scrollable>
<Stack vertical fill>
{shownPowers
.sort((a, b) => a.power_name.localeCompare(b.power_name))
.map((entry) => (
<Stack.Item key={entry.power_name}>
<Button
fluid
tooltipPosition={
entry.power_purchased ? 'left' : 'right'
}
disabled={
points < entry.power_cost || entry.power_purchased
}
color={getButtonColor(entry, points)}
tooltip={entry.power_desc}
onClick={() =>
act('evolve_power', { val: entry.power_name })
}
>
<Stack>
<Stack.Item grow>{entry.power_name}</Stack.Item>
<Stack.Item>({entry.power_cost})</Stack.Item>
</Stack>
</Button>
</Stack.Item>
))}
</Stack>
</Section>
</Stack.Item>
</Stack>
</Section>
</Stack.Item>
);
};

View File

@@ -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';
}
}

View File

@@ -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<Data>();
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 (
<Window width={600} height={700}>
<Window.Content>
<Stack vertical fill>
<Stack.Item>
Current evolution points left to evolve with: {available_points}
</Stack.Item>
<Stack.Item grow>
<Stack fill>
<ChangeLingSearchableList
title="Purchasable Powers"
powerData={unpurchasedPowers}
points={available_points}
/>
<ChangeLingSearchableList
title="Purchased Powers"
powerData={purchasedPowers}
points={available_points}
/>
</Stack>
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};

View File

@@ -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;
};

View File

@@ -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"