diff --git a/baystation12.dme b/baystation12.dme
index cbc43ffe2b6b0..6eddf79a8fd9d 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -34,6 +34,7 @@
#include "code\__defines\atmos.dm"
#include "code\__defines\atmospherics.dm"
#include "code\__defines\byond_tracy.dm"
+#include "code\__defines\changeling.dm"
#include "code\__defines\chemistry.dm"
#include "code\__defines\client.dm"
#include "code\__defines\colors.dm"
@@ -348,6 +349,8 @@
#include "code\datums\item_modifiers\space_suits.dm"
#include "code\datums\item_modifiers\~defines.dm"
#include "code\datums\licences\license.dm"
+#include "code\datums\managed_browsers\_managed_browser.dm"
+#include "code\datums\managed_browsers\changelingevolution.dm"
#include "code\datums\mind\memory.dm"
#include "code\datums\mind\mind.dm"
#include "code\datums\move_intent\move_intent.dm"
@@ -610,8 +613,48 @@
#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_evolutiontree.dm"
#include "code\game\gamemodes\changeling\changeling_powers.dm"
-#include "code\game\gamemodes\changeling\modularchangling.dm"
+#include "code\game\gamemodes\changeling\generic_equip_procs.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\death_sting.dm"
+#include "code\game\gamemodes\changeling\powers\delayed_toxin_sting.dm"
+#include "code\game\gamemodes\changeling\powers\electric_lockpick.dm"
+#include "code\game\gamemodes\changeling\powers\endoarmor.dm"
+#include "code\game\gamemodes\changeling\powers\engorged_glands.dm"
+#include "code\game\gamemodes\changeling\powers\enrage.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\false_identity.dm"
+#include "code\game\gamemodes\changeling\powers\fleshmend.dm"
+#include "code\game\gamemodes\changeling\powers\hivemind.dm"
+#include "code\game\gamemodes\changeling\powers\lsd_sting.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\rapid_synthesis.dm"
+#include "code\game\gamemodes\changeling\powers\reattach_limb.dm"
+#include "code\game\gamemodes\changeling\powers\recursive_enhancement.dm"
+#include "code\game\gamemodes\changeling\powers\reinforced_tendons.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\synaptizine_overdose.dm"
+#include "code\game\gamemodes\changeling\powers\tentacle.dm"
+#include "code\game\gamemodes\changeling\powers\transform.dm"
+#include "code\game\gamemodes\changeling\powers\visible_camouflage.dm"
#include "code\game\gamemodes\cult\cult.dm"
#include "code\game\gamemodes\cult\cult_items.dm"
#include "code\game\gamemodes\cult\cult_structures.dm"
@@ -1532,7 +1575,10 @@
#include "code\modules\augment\passive\armor.dm"
#include "code\modules\augment\passive\boost.dm"
#include "code\modules\augment\passive\fluff.dm"
+#include "code\modules\augment\passive\ling_lenses.dm"
+#include "code\modules\augment\passive\lingcore.dm"
#include "code\modules\augment\passive\nanoaura.dm"
+#include "code\modules\augment\passive\ragecore.dm"
#include "code\modules\augment\passive\boost\muscle.dm"
#include "code\modules\augment\passive\boost\reflex.dm"
#include "code\modules\augment\passive\boost\shooting.dm"
diff --git a/code/__defines/changeling.dm b/code/__defines/changeling.dm
new file mode 100644
index 0000000000000..7323244d7b72e
--- /dev/null
+++ b/code/__defines/changeling.dm
@@ -0,0 +1,2 @@
+#define ABSORB_NONLETHAL 1
+#define ABSORB_LETHAL 2
diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm
index 34f171523147f..ef75f71f548d0 100644
--- a/code/__defines/gamemode.dm
+++ b/code/__defines/gamemode.dm
@@ -97,6 +97,9 @@
#define Sp_CHARGES "charges"
#define Sp_HOLDVAR "holdervar"
+//changeling cost
+#define CHANGELING_STASIS_COST 20
+
//Voting-related
#define VOTE_PROCESS_ABORT 1
#define VOTE_PROCESS_COMPLETE 2
@@ -105,3 +108,14 @@
#define VOTE_STATUS_PREVOTE 1
#define VOTE_STATUS_ACTIVE 2
#define VOTE_STATUS_COMPLETE 3
+
+/*
+Changeling Defines
+*/
+#define CHANGELING_POWER_INHERENT "Inherent"
+#define CHANGELING_POWER_ARMOR "Armor"
+#define CHANGELING_POWER_STINGS "Stings"
+#define CHANGELING_POWER_SHRIEKS "Shrieks"
+#define CHANGELING_POWER_HEALTH "Health"
+#define CHANGELING_POWER_ENHANCEMENTS "Enhancements"
+#define CHANGELING_POWER_WEAPONS "Weapons"
diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm
index 187145f271bac..120c1ccf5faa9 100644
--- a/code/_helpers/mobs.dm
+++ b/code/_helpers/mobs.dm
@@ -103,6 +103,52 @@
/proc/get_exposed_defense_zone(atom/movable/target)
return pick(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG, BP_CHEST, BP_GROIN)
+/proc/do_mob(mob/user , mob/target, time = 30, target_zone = 0, uninterruptible = FALSE, progress = TRUE, ignore_movement = FALSE, incapacitation_affected = TRUE)
+ if(!user || !target)
+ return 0
+ var/user_loc = user.loc
+ var/target_loc = target.loc
+
+ var/holding = user.get_active_hand()
+ var/datum/progressbar/private/progbar
+ if (progress)
+ progbar = new(user, time, target)
+
+ var/endtime = world.time+time
+ var/starttime = world.time
+ . = TRUE
+ while (world.time < endtime)
+ stoplag(1)
+ if (progress)
+ progbar.update(world.time - starttime)
+ if(!user || !target)
+ . = FALSE
+ break
+ if(uninterruptible)
+ continue
+
+ if(!user || (user.incapacitated() && incapacitation_affected))
+ . = FALSE
+ break
+
+ if(user.loc != user_loc && !ignore_movement)
+ . = FALSE
+ break
+
+ if(target.loc != target_loc && !ignore_movement)
+ . = FALSE
+ break
+
+ if(user.get_active_hand() != holding)
+ . = FALSE
+ break
+
+ if(target_zone && user.zone_sel.selecting != target_zone)
+ . = FALSE
+ break
+
+ if (progbar)
+ qdel(progbar)
/// Integer. Unique sequential ID from the `do_after` proc used to validate `DO_USER_UNIQUE_ACT` flag checks.
/mob/var/do_unique_user_handle = 0
diff --git a/code/_onclick/hud/ability_screen_objects.dm b/code/_onclick/hud/ability_screen_objects.dm
index 7b07703e89c74..43bf4f9dbbae6 100644
--- a/code/_onclick/hud/ability_screen_objects.dm
+++ b/code/_onclick/hud/ability_screen_objects.dm
@@ -252,9 +252,9 @@
//Changeling Abilities
/obj/screen/ability/verb_based/changeling
- icon_state = "ling_spell_base"
- background_base_state = "ling"
-
+ icon_state = "const_spell_base"
+ background_base_state = "const"
+//use this to force add powers
/obj/screen/movable/ability_master/proc/add_ling_ability(object_given, verb_given, name_given, ability_icon_given, arguments)
if(!object_given)
message_admins("ERROR: add_ling_ability() was not given an object in its arguments.")
diff --git a/code/datums/extensions/chameleon.dm b/code/datums/extensions/chameleon.dm
index ffacbddc2423e..36b5b2b2aa833 100644
--- a/code/datums/extensions/chameleon.dm
+++ b/code/datums/extensions/chameleon.dm
@@ -23,6 +23,7 @@
if (!chameleon_choices)
var/chameleon_type = chameleon_base_type || holder.parent_type
chameleon_choices = LAZYACCESS(chameleon_choices_by_type, chameleon_type)
+
if (!chameleon_choices)
chameleon_choices = GenerateChameleonChoices(chameleon_type)
LAZYSET(chameleon_choices_by_type, chameleon_type, chameleon_choices)
@@ -69,8 +70,10 @@
return null
/datum/extension/chameleon/proc/GenerateChameleonChoices(basetype)
+
var/choices = list()
var/types = islist(basetype) ? basetype : typesof(basetype)
+
for (var/path in types)
AddChameleonChoice(choices, path)
return sortAssoc(choices)
diff --git a/code/datums/managed_browsers/_managed_browser.dm b/code/datums/managed_browsers/_managed_browser.dm
new file mode 100644
index 0000000000000..02fd99615d424
--- /dev/null
+++ b/code/datums/managed_browsers/_managed_browser.dm
@@ -0,0 +1,52 @@
+GLOBAL_VAR(managed_browser_id_ticker)
+
+// This holds information on managing a /datum/browser object.
+// Managing can include things like persisting the state of specific information inside of this object, receiving Topic() calls, or deleting itself when the window is closed.
+// This is useful for browser windows to be able to stand 'on their own' instead of being tied to something in the game world, like an object or mob.
+/datum/managed_browser
+ var/client/my_client = null
+ var/browser_id = null
+ var/base_browser_id = null
+
+ var/title = null
+ var/size_x = 200
+ var/size_y = 400
+
+ var/display_when_created = TRUE
+
+/datum/managed_browser/New(client/new_client)
+ if(!new_client)
+ crash_with("Managed browser object was not given a client.")
+ return
+ if(!base_browser_id)
+ crash_with("Managed browser object does not have a base browser id defined in its type.")
+ return
+
+ my_client = new_client
+ browser_id = "[base_browser_id]-[GLOB.managed_browser_id_ticker++]"
+
+ if(display_when_created)
+ display()
+
+/datum/managed_browser/Destroy()
+ my_client = null
+ return ..()
+
+// Override if you want to have the browser title change conditionally.
+// Otherwise it's easier to just change the title variable directly.
+/datum/managed_browser/proc/get_title()
+ return title
+
+// Override to display the html information.
+// It is suggested to build it with a list, and use list.Join() at the end.
+// This helps prevent excessive concatination, which helps preserves BYOND's string tree from becoming a laggy mess.
+/datum/managed_browser/proc/get_html()
+ return
+
+/datum/managed_browser/proc/display()
+ interact(get_html(), get_title(), my_client)
+
+/datum/managed_browser/proc/interact(html, title, client/C)
+ var/datum/browser/popup = new(C.mob, browser_id, title, size_x, size_y, src)
+ popup.set_content(html)
+ popup.open()
diff --git a/code/datums/managed_browsers/changelingevolution.dm b/code/datums/managed_browsers/changelingevolution.dm
new file mode 100644
index 0000000000000..340702906c25c
--- /dev/null
+++ b/code/datums/managed_browsers/changelingevolution.dm
@@ -0,0 +1,165 @@
+/client
+ var/datum/managed_browser/changelingevolution/changelingevolution = null
+
+/datum/managed_browser/changelingevolution
+ base_browser_id = "evolution_tree"
+ title = "Evolution Tree"
+ size_x = 480
+ size_y = 600
+ var/textbody = null
+
+/datum/managed_browser/changelingevolution/New(client/new_client)
+ if(!new_client.mob || !new_client.mob.mind || !new_client.mob.mind.changeling)
+ message_admins("[new_client] tried to access changeling evolutions while not changeling.")
+ qdel(src)
+
+ ..()
+
+/datum/managed_browser/changelingevolution/Destroy()
+ if(my_client)
+ my_client.changelingevolution = null
+ return ..()
+
+/datum/managed_browser/changelingevolution/get_html()
+ var/list/dat = list("
")
+ var/geneticpoints_current = my_client.mob.mind.changeling.geneticpoints
+ var/geneticpoints_max = my_client.mob.mind.changeling.max_geneticpoints
+
+ dat += "Genetic Points Available: [geneticpoints_current] / [geneticpoints_max]
"
+ dat += "Obtain more by feeding on your own kind.
"
+ dat += "What am I?
"
+ dat += "Inherent"
+ dat += "Armor"
+ dat += "Weapons"
+ dat += "Stings"
+ dat += "Shrieks"
+ dat += "Health"
+ dat += "Enhancements"
+ if(textbody)
+ dat += ""
+ dat += "[textbody]"
+ dat += "
"
+ dat += ""
+
+ return dat.Join()
+
+/datum/managed_browser/changelingevolution/Topic(href, href_list[])
+ if(!my_client)
+ return FALSE
+
+ if(href_list["close"])
+ return
+
+ if(href_list["inherent"])
+ generate_abilitylist(CHANGELING_POWER_INHERENT)
+
+ if(href_list["armor"])
+ generate_abilitylist(CHANGELING_POWER_ARMOR)
+
+ if(href_list["weapons"])
+ generate_abilitylist(CHANGELING_POWER_WEAPONS)
+
+ if(href_list["stings"])
+ generate_abilitylist(CHANGELING_POWER_STINGS)
+
+ if(href_list["shrieks"])
+ generate_abilitylist(CHANGELING_POWER_SHRIEKS)
+
+ if(href_list["health"])
+ generate_abilitylist(CHANGELING_POWER_HEALTH)
+
+ if(href_list["enhancements"])
+ generate_abilitylist(CHANGELING_POWER_ENHANCEMENTS)
+
+ if(href_list["evolve"])
+ var/datum/mind/M = my_client.mob.mind
+ var/datum/changeling/C = my_client.mob.mind.changeling
+ var/datum/power/changeling/Thepower = href_list["evolve"]
+
+ for (var/datum/power/changeling/P in GLOB.powerinstances)
+ if(P.name == Thepower)
+ Thepower = P
+ break
+
+ if(!istype(M))
+ return
+
+ if(!Thepower)
+ CRASH("Purchase failed. Inform a dev of this error.")
+
+ 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 = "What am I?
|
"
+ textbody += ""
+ textbody += "You are a changeling, a creature empowered with genetic-based abilities that change your body in bizarre ways."
+ textbody += " It's probably best the crew doesn't know about your power -- at least not right away.
"
+ textbody += "What a changeling is, however, is up to you. Are you a strange alien impersonating crew? Are you a"
+ textbody += " normal crewmember infected with a parasite? An experiment gone wrong? It's up to you to make the story.
"
+ textbody += "Of course, you need to know how it works to begin with.
"
+ textbody += "Your abilities cost chemicals that your body will slowly regenerate with varying speeds based on enhancements obtained."
+ textbody += " There are a set of inherent abilities you will always have while the rest may be purchased through genomes.
"
+ textbody += "You may obtain more genomes if you find another changeling and absorb them, but this is not required. If you've found "
+ textbody += "your abilities aren't to your liking, you have up to two re-adapts available, and these may be refilled by absorbing anyone -- including monkeys.
"
+ textbody += "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 GLOB.powerinstances)
+ if(P.power_category == cat)
+ ability_list[LIST_PRE_INC(ability_list)] = 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 = " |
[cat] Skills
|
"
+ textbody += "[catinfo]
|
"
+ for(var/A in ability_list)
+ var/datum/power/changeling/powerdata = A
+ textbody += "[initial(powerdata.name)] "
+ textbody += "[initial(powerdata.desc)]
"
+ textbody += "[powerdata.helptext] "
+ if(powerdata.enhancedtext != "")
+ textbody += "WHEN EHANCED: [powerdata.enhancedtext] "
+ if(powerdata in my_client.mob.mind.changeling.purchased_powers)
+ textbody += "This ability is already evolved!"
+ else if(cat != "Inherent")
+ textbody += "Evolve"
+ textbody += " |
"
+ display()
diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm
index 07cce3b41eede..3d071b22387d5 100644
--- a/code/datums/mind/mind.dm
+++ b/code/datums/mind/mind.dm
@@ -82,7 +82,7 @@
if(current) //remove ourself from our old body's mind variable
if(changeling)
current.remove_changeling_powers()
- current.verbs -= /datum/changeling/proc/EvolutionMenu
+ current.verbs -= /datum/changeling/proc/EvolutionTree
current.mind = null
SSnano.user_transferred(current, new_character) // transfer active NanoUI instances to new user
@@ -97,9 +97,6 @@
if(learned_spells && length(learned_spells))
restore_spells(new_character)
- if(changeling)
- new_character.make_changeling()
-
if(active)
new_character.key = key //now transfer the key to link the client to our new body
diff --git a/code/datums/observation/see_in_dark_set.dm b/code/datums/observation/see_in_dark_set.dm
index ea1235f92b0fc..cc7a9671b79d5 100644
--- a/code/datums/observation/see_in_dark_set.dm
+++ b/code/datums/observation/see_in_dark_set.dm
@@ -18,7 +18,7 @@ GLOBAL_DATUM_INIT(see_in_dark_set_event, /singleton/observ/see_in_dark_set, new)
* See In Dark Set Handling *
***************************/
-/mob/proc/set_see_in_dark(new_see_in_dark)
+/mob/proc/set_see_in_dark(new_see_in_dark, seedarkness)
var/old_see_in_dark = sight
if(old_see_in_dark != new_see_in_dark)
see_in_dark = new_see_in_dark
diff --git a/code/game/antagonist/station/changeling.dm b/code/game/antagonist/station/changeling.dm
index 68a4e5ac44750..77f60752c282f 100644
--- a/code/game/antagonist/station/changeling.dm
+++ b/code/game/antagonist/station/changeling.dm
@@ -27,8 +27,7 @@ GLOBAL_DATUM_INIT(changelings, /datum/antagonist/changeling, new)
. = ..()
if(. && player && player.current)
player.current.remove_changeling_powers()
- player.current.verbs -= /datum/changeling/proc/EvolutionMenu
- player.current.remove_language(LANGUAGE_CHANGELING_GLOBAL)
+ player.current.verbs -= /datum/changeling/proc/EvolutionTree
QDEL_NULL(player.changeling)
/datum/antagonist/changeling/create_objectives(datum/mind/changeling)
diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm
index 8d4c783f0729c..ef1d39e9e8f27 100644
--- a/code/game/dna/dna2_helpers.dm
+++ b/code/game/dna/dna2_helpers.dm
@@ -158,7 +158,9 @@
H.update_eyes()
H.skin_tone = 35 - dna.GetUIValueRange(DNA_UI_SKIN_TONE, 220) // Value can be negative.
-
+ //Necessary for mirror to set plural properly
+ if(H.gender == PLURAL)
+ H.gender = PLURAL
if(H.gender != NEUTER)
if (dna.GetUIState(DNA_UI_GENDER))
H.gender = FEMALE
diff --git a/code/game/gamemodes/changeling/absorbed_dna.dm b/code/game/gamemodes/changeling/absorbed_dna.dm
index 850939253181a..907c64ace2bcb 100644
--- a/code/game/gamemodes/changeling/absorbed_dna.dm
+++ b/code/game/gamemodes/changeling/absorbed_dna.dm
@@ -3,12 +3,21 @@
var/datum/dna/dna
var/speciesName
var/list/languages
+ var/gender
var/pronouns
-
-/datum/absorbed_dna/New(newName, newDNA, newSpecies, newLanguages, newPronouns)
+ var/list/flavour_texts
+ var/list/genMods
+ var/list/descriptors
+ var/list/icon_render_details
+/datum/absorbed_dna/New(newName, newDNA, newSpecies, newLanguages, newGender , newPronouns, list/newFlavour, list/New_icon_render_keys, list/newBuild)
..()
name = newName
dna = newDNA
speciesName = newSpecies
languages = newLanguages
+ gender = newGender
pronouns = newPronouns
+ flavour_texts = newFlavour ? newFlavour.Copy() : null
+ icon_render_details = New_icon_render_keys ? New_icon_render_keys : list()
+ //defaults to average height and build if none set
+ descriptors = newBuild ? newBuild.Copy() : null
\ No newline at end of file
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index e6966e09426ac..831bcdbe6c8ef 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -11,7 +11,7 @@
No one knows where it came from. No one knows who it is or what it wants. One thing is for \
certain though... there is never just one of them. Good luck."
config_tag = "changeling"
- required_players = 5
+ required_players = 12
required_enemies = 1
end_on_antag_death = FALSE
antag_scaling_coeff = 15
diff --git a/code/game/gamemodes/changeling/changeling_evolutiontree.dm b/code/game/gamemodes/changeling/changeling_evolutiontree.dm
new file mode 100644
index 0000000000000..fadbdcde30c6a
--- /dev/null
+++ b/code/game/gamemodes/changeling/changeling_evolutiontree.dm
@@ -0,0 +1,368 @@
+// 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/global/list/powers = typesof(/datum/power/changeling) - /datum/power/changeling //needed for the badmin verb for now
+GLOBAL_LIST_EMPTY(powerinstances)
+/mob/proc/generic_sting()
+ return
+/datum/power //Could be used by other antags too
+ var/name = "Power"
+ var/desc = "Placeholder"
+ var/helptext = ""
+ var/enhancedtext = ""
+ /// Is it an active power, or passive?
+ var/isVerb = 1
+ /// Path to a verb that contains the effects.
+ var/verbpath
+ /// Is this ability significant enough to dedicate screen space for a HUD button?
+ var/make_hud_button = 1
+ /// icon_state for icons for the ability HUD. Must be in screen_spells.dmi.
+ var/ability_icon_state = null
+ var/is_sting = FALSE
+/datum/power/changeling
+ var/allowduringlesserform = 0
+ /// Cost for the changling to evolve this power.
+ var/genomecost = 500000
+ /// _defines/gamemode.dm
+ var/power_category = null
+ /// only override for sting powers, used for boost range
+ var/sting_effect = /mob/proc/generic_sting
+ var/sting_duration = null
+ var/reagents_transferred = null
+
+// Modularchangling, totally stolen from the new player panel. YAYY
+/datum/changeling/proc/EvolutionTree()//The new one
+ set name = "-Evolution Tree-"
+ set category = "Changeling"
+ set desc = "Adapt yourself carefully."
+
+ if(!usr || !usr.mind || !usr.mind.changeling) return
+ src = usr.mind.changeling
+
+ if(!length(GLOB.powerinstances))
+ for(var/P in powers)
+ GLOB.powerinstances += new P()
+
+ var/dat = "Changeling Evolution Tree"
+
+ //javascript, the part that does most of the work~
+ dat += {"
+
+
+
+
+
+
+ "}
+
+ //body tag start + onload and onkeypress (onkeyup) javascript event calls
+ dat += ""
+
+ //title + search bar
+ dat += {"
+
+
+
+ "}
+
+ //player table header
+ dat += {"
+
+ "}
+
+ var/i = 1
+ for(var/datum/power/changeling/P in GLOB.powerinstances)
+ var/ownsthis = 0
+
+ if(P in purchased_powers)
+ ownsthis = 1
+
+
+ var/color = "#e6e6e6"
+ if(i%2 == 0)
+ color = "#f2f2f2"
+
+
+ dat += {"
+
+
+
+
+
+ Evolve [P] - Cost: [ownsthis ? "Purchased" : P.genomecost]
+
+
+ |
+
+
+ "}
+
+ i++
+
+
+ //player table ending
+ dat += {"
+
+
+
+
+
+ "}
+
+ show_browser(usr, 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/EvolutionTree)()
+
+
+
+/datum/changeling/proc/purchasePower(datum/mind/M, Pname, remake_verbs = 1)
+ if(!M || !M.changeling)
+ return
+
+ var/datum/power/changeling/Thepower = Pname
+
+
+ for (var/datum/power/changeling/P in GLOB.powerinstances)
+ if(P.name == Pname)
+ Thepower = P
+ break
+
+
+ if(!Thepower)
+ to_chat(M.current, "This is awkward. Changeling power purchase failed, please report this bug to a coder!")
+ return
+
+ if(Thepower in purchased_powers)
+ 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(null, M.current)
+ M.current.ability_master.add_ling_ability(
+ object_given = M.current,
+ verb_given = Thepower.verbpath,
+ name_given = Thepower.name,
+ ability_icon_given = Thepower.ability_icon_state,
+ arguments = list()
+ )
+
+ if(!Thepower.isVerb && Thepower.verbpath)
+ call(M.current, Thepower.verbpath)()
+ else if(remake_verbs)
+ M.current.make_changeling()
diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm
index 312daa2f227d5..f728d006f7adb 100644
--- a/code/game/gamemodes/changeling/changeling_powers.dm
+++ b/code/game/gamemodes/changeling/changeling_powers.dm
@@ -1,9 +1,11 @@
+
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()
+ 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
@@ -11,17 +13,25 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
var/changelingID = "Changeling"
var/geneticdamage = 0
var/isabsorbing = 0
- var/geneticpoints = 25
- var/purchasedpowers = list()
+ var/geneticpoints = 15
+ var/max_geneticpoints = 15
+ var/list/purchased_powers = list()
+ var/list/purchased_organs = list()
+ var/already_regenerating = FALSE
var/mimicing = ""
-
-/datum/changeling/Destroy()
- purchasedpowers = null
- absorbed_languages.Cut()
- absorbed_dna.Cut()
- . = ..()
-
-/datum/changeling/New()
+ var/cloaked = 0
+ var/absorbing_lethally = ABSORB_NONLETHAL
+ var/selected_ranged_sting = null;
+ var/tendons_reinforced = FALSE;
+ var/list/toxin_victims = list()
+ 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?
+ var/last_human_form = null
+/datum/changeling/New(gender=FEMALE)
..()
if(length(possible_changeling_IDs))
changelingID = pick(possible_changeling_IDs)
@@ -53,50 +63,83 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
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
-
+//ling creation, add unique organ here
//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)
- verbs += /datum/changeling/proc/EvolutionMenu
- add_language(LANGUAGE_CHANGELING_GLOBAL)
+ verbs.Add(/datum/changeling/proc/EvolutionTree)
+ verbs.Add(/mob/proc/toggle_absorb_type)
+ add_language("Changeling")
var/lesser_form = !ishuman(src)
-
- if(!length(powerinstances))
+ var/has_core = FALSE
+ if(!length(GLOB.powerinstances))
for(var/P in powers)
- powerinstances += new P()
+ GLOB.powerinstances += new P()
// Code to auto-purchase free powers.
- for(var/datum/power/changeling/P in powerinstances)
+ for(var/datum/power/changeling/P in GLOB.powerinstances)
if(!P.genomecost) // Is it free?
- if(!(P in mind.changeling.purchasedpowers)) // 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.
+ if(!(P in mind.changeling.purchased_powers)) // Do we not have it already?
+ mind.changeling.purchased_powers += P /// Add it.
+ mind.changeling.purchasePower(mind, P, 0)// Purchase it. Don't remake our verbs, we're doing it after this.
- for(var/datum/power/changeling/P in mind.changeling.purchasedpowers)
+ 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))
- src.verbs += P.verbpath
+ verbs.Add(P.verbpath)
+ if(P.make_hud_button)
+ if(!src.ability_master)
+ src.ability_master = new /obj/screen/movable/ability_master(null, 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/datum/absorbed_dna/newDNA = new(H.real_name, H.dna, H.species.name, H.languages)
+ 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.gender, H.pronouns, H.flavor_texts, H.icon_render_keys, H.descriptors)
absorbDNA(newDNA)
-
+ for(var/obj/item/organ/internal/augment/lingcore/C in H.internal_organs)
+ has_core++
+ if(has_core == 0 && istype(src,/mob/living/carbon/human))
+ var/obj/item/organ/external/chest = H.get_organ(BP_CHEST)
+ var/obj/item/organ/internal/augment/core = new /obj/item/organ/internal/augment/lingcore
+ core.forceMove(src)
+ core.replaced(src, chest)
+ core = null
+ var/obj/item/organ/external/parent = H.get_organ(BP_CHEST)
+ var/obj/item/organ/internal/brain/mbrain = H.internal_organs_by_name[BP_BRAIN]
+ mbrain.parent_organ = BP_CHEST
+ mbrain.forceMove(parent)
+ var/obj/item/organ/external/head = H.get_organ(BP_HEAD)
+ for(var/obj/item/organ/internal/I in head.internal_organs)
+ if(istype(I,/obj/item/organ/internal/brain))
+ head.internal_organs.Remove(I)
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.purchasedpowers)
+ for(var/datum/power/changeling/P in mind.changeling.purchased_powers)
if(P.isVerb)
- verbs -= P.verbpath
+ verbs.Remove(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
@@ -111,694 +154,116 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
return
if(src.stat > max_stat)
- to_chat(src, SPAN_WARNING("We are incapacitated."))
+ to_chat(src, "We are incapacitated.")
return
if(length(changeling.absorbed_dna) < required_dna)
- to_chat(src, SPAN_WARNING("We require at least [required_dna] samples of compatible DNA."))
+ to_chat(src, "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!"))
+ to_chat(src, "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."))
+ to_chat(src, "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(updated_languages)
-
languages = list()
for(var/language in updated_languages)
languages += language
//This isn't strictly necessary but just to be safe...
- add_language(LANGUAGE_CHANGELING_GLOBAL)
-
- return
-
-//Absorbs the victim's DNA making them uncloneable. Requires a strong grip on the victim.
-//Doesn't cost anything as it's the most basic ability.
-/mob/proc/changeling_absorb_dna()
- set category = "Changeling"
- set name = "Absorb DNA"
-
- var/datum/changeling/changeling = changeling_power(0,0,100)
- if(!changeling) return
-
- var/obj/item/grab/G = src.get_active_hand()
- if(!istype(G))
- to_chat(src, SPAN_WARNING("We must be grabbing a creature in our active hand to absorb them."))
- return
-
- var/mob/living/carbon/human/T = G.affecting
- if(!istype(T))
- to_chat(src, SPAN_WARNING("[T] is not compatible with our biology."))
- return
-
- if((T.species.species_flags & SPECIES_FLAG_NO_SCAN) || T.isSynthetic())
- to_chat(src, SPAN_WARNING("We cannot extract DNA from this creature!"))
- return
-
- if(MUTATION_HUSK in T.mutations)
- to_chat(src, SPAN_WARNING("This creature's DNA is ruined beyond useability!"))
- return
-
- if(!G.can_absorb())
- to_chat(src, SPAN_WARNING("We must have a tighter grip to absorb this creature."))
- return
-
- if(changeling.isabsorbing)
- to_chat(src, SPAN_WARNING("We are already absorbing!"))
- return
-
- var/obj/item/organ/external/affecting = T.get_organ(src.zone_sel.selecting)
- if(!affecting)
- to_chat(src, SPAN_WARNING("They are missing that body part!"))
-
- changeling.isabsorbing = 1
- for(var/stage = 1, stage<=3, stage++)
- switch(stage)
- if(1)
- to_chat(src, SPAN_NOTICE("This creature is compatible. We must hold still..."))
- if(2)
- to_chat(src, SPAN_NOTICE("We extend a proboscis."))
- src.visible_message(SPAN_WARNING("[src] extends a proboscis!"))
- if(3)
- 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!"))
- affecting.take_external_damage(39, 0, DAMAGE_FLAG_SHARP, "large organic needle")
- if(!do_after(src, 15 SECONDS, T, DO_PUBLIC_UNIQUE))
- to_chat(src, SPAN_WARNING("Our absorption of [T] has been interrupted!"))
- changeling.isabsorbing = 0
- return
-
- to_chat(src, SPAN_NOTICE("We have absorbed [T]!"))
- src.visible_message(SPAN_DANGER("[src] sucks the fluids from [T]!"))
- to_chat(T, SPAN_DANGER("You have been absorbed by the changeling!"))
- changeling.chem_charges += 10
- changeling.geneticpoints += 2
-
- //Steal all of their languages!
- for(var/language in T.languages)
- if(!(language in changeling.absorbed_languages))
- changeling.absorbed_languages += language
-
- changeling_update_languages(changeling.absorbed_languages)
-
- var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages, T.pronouns)
- absorbDNA(newDNA)
-
- if(mind && T.mind)
- T.mind.CopyMemories(mind)
-
- 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(changeling.GetDNA(dna_data.name))
- continue
- absorbDNA(dna_data)
- changeling.absorbedcount++
- T.mind.changeling.absorbed_dna.Cut(2)
-
- if(T.mind.changeling.purchasedpowers)
- for(var/datum/power/changeling/Tp in T.mind.changeling.purchasedpowers)
- if(Tp in changeling.purchasedpowers)
- continue
- else
- changeling.purchasedpowers += Tp
-
- if(!Tp.isVerb)
- call(Tp.verbpath)()
- else
- src.make_changeling()
-
- changeling.chem_charges += T.mind.changeling.chem_charges
- changeling.geneticpoints += T.mind.changeling.geneticpoints
- T.mind.changeling.chem_charges = 0
- T.mind.changeling.geneticpoints = 0
- T.mind.changeling.absorbedcount = 0
-
- changeling.absorbedcount++
- changeling.isabsorbing = 0
-
- T.death(0)
- T.ChangeToHusk()
- return 1
-
-
-//Change our DNA to that of somebody we've absorbed.
-/mob/proc/changeling_transform()
- set category = "Changeling"
- set name = "Transform (5)"
-
- var/datum/changeling/changeling = changeling_power(5,1,0)
- if(!changeling) return
-
- var/list/names = list()
- for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna)
- names += "[DNA.name]"
-
- var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
- if(!S) return
-
- var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S)
- if(!chosen_dna)
- return
-
- changeling.chem_charges -= 5
- changeling.geneticdamage = 30
-
- var/S_name = chosen_dna.speciesName
- var/datum/species/S_dat = all_species[S_name]
- var/changeTime = 2 SECONDS
- if(mob_size != S_dat.mob_size)
- src.visible_message(SPAN_WARNING("[src]'s body begins to twist, their mass changing rapidly!"))
- changeTime = 8 SECONDS
- else
- src.visible_message(SPAN_WARNING("[src]'s body begins to twist, changing rapidly!"))
-
- if(!do_after(src, changeTime, do_flags = DO_DEFAULT | DO_USER_UNIQUE_ACT))
- to_chat(src, SPAN_NOTICE("You fail to change shape."))
- return
- handle_changeling_transform(chosen_dna)
-
- src.verbs -= /mob/proc/changeling_transform
- spawn(10)
- src.verbs += /mob/proc/changeling_transform
-
- changeling_update_languages(changeling.absorbed_languages)
- return 1
-
-/mob/proc/handle_changeling_transform(datum/absorbed_dna/chosen_dna)
- src.visible_message(SPAN_WARNING("[src] transforms!"))
-
- src.dna = chosen_dna.dna
- src.real_name = chosen_dna.name
- src.flavor_text = ""
- src.pronouns = chosen_dna.pronouns
-
- if(ishuman(src))
- var/mob/living/carbon/human/H = src
- var/newSpecies = chosen_dna.speciesName
- H.set_species(newSpecies,1)
- H.b_type = chosen_dna.dna.b_type
- H.sync_organ_dna()
-
- domutcheck(src, null)
- src.UpdateAppearance()
-
-
-//Transform into a monkey.
-/mob/proc/changeling_lesser_form()
- set category = "Changeling"
- set name = "Lesser Form (1)"
-
- var/datum/changeling/changeling = changeling_power(1,0,0)
- if(!changeling) return
-
- if(src.has_brain_worms())
- to_chat(src, SPAN_WARNING("We cannot perform this ability at the present time!"))
- return
-
- var/mob/living/carbon/human/H = src
-
- if(!istype(H) || !H.species.primitive_form)
- to_chat(src, SPAN_WARNING("We cannot perform this ability in this form!"))
- return
-
- changeling.chem_charges--
- H.visible_message(SPAN_WARNING("[H] transforms!"))
- changeling.geneticdamage = 30
- to_chat(H, SPAN_WARNING("Our genes cry out!"))
- H = H.monkeyize()
- return 1
-
-//Transform into a human
-/mob/proc/changeling_lesser_transform()
- set category = "Changeling"
- set name = "Transform (1)"
-
- var/datum/changeling/changeling = changeling_power(1,1,0)
- if(!changeling) return
-
- if(HAS_TRANSFORMATION_MOVEMENT_HANDLER(src))
- return
-
- var/list/names = list()
- for(var/datum/dna/DNA in changeling.absorbed_dna)
- names += "[DNA.real_name]"
-
- var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
- if(!S) return
-
- var/datum/dna/chosen_dna = changeling.GetDNA(S)
- if(!chosen_dna)
- return
-
- var/mob/living/carbon/human/C = src
-
- changeling.chem_charges--
- C.remove_changeling_powers()
- C.visible_message(SPAN_WARNING("[C] transforms!"))
- C.dna = chosen_dna.Clone()
-
- var/list/implants = list()
- for (var/obj/item/implant/I in C) //Still preserving implants
- implants += I
-
- ADD_TRANSFORMATION_MOVEMENT_HANDLER(C)
- C.icon = null
- C.ClearOverlays()
- C.set_invisibility(INVISIBILITY_ABSTRACT)
- var/atom/movable/fake_overlay/animation = new /atom/movable/fake_overlay(src)
- animation.icon_state = "blank"
- animation.icon = 'icons/mob/mob.dmi'
- flick("monkey2h", animation)
- sleep(48)
- qdel(animation)
-
- for(var/obj/item/W in src)
- C.drop_from_inventory(W)
-
- var/mob/living/carbon/human/O = new /mob/living/carbon/human( src )
- if (C.dna.GetUIState(DNA_UI_GENDER))
- O.gender = FEMALE
- else
- O.gender = MALE
- O.dna = C.dna.Clone()
- C.dna = null
- O.real_name = chosen_dna.real_name
-
- for(var/obj/T in C)
- qdel(T)
-
- O.dropInto(C.loc)
-
- O.UpdateAppearance()
- domutcheck(O, null)
- O.setToxLoss(C.getToxLoss())
- O.adjustBruteLoss(C.getBruteLoss())
- O.setOxyLoss(C.getOxyLoss())
- O.adjustFireLoss(C.getFireLoss())
- O.set_stat(C.stat)
- for (var/obj/item/implant/I in implants)
- I.forceMove(O)
- I.implanted = O
-
- C.mind.transfer_to(O)
- O.make_changeling()
- O.changeling_update_languages(changeling.absorbed_languages)
- qdel(C)
- return 1
-
-
-//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay.
-/mob/proc/changeling_fakedeath()
- set category = "Changeling"
- set name = "Regenerative Stasis (20)"
-
- var/datum/changeling/changeling = changeling_power(20,1,100,DEAD)
- if(!changeling) return
-
- var/mob/living/carbon/C = src
- if(!C.stat && alert("Are we sure we wish to fake our death?",,"Yes","No") == "No")//Confirmation for living changelings if they want to fake their death
- return
- to_chat(C, SPAN_NOTICE("We will attempt to regenerate our form."))
- C.status_flags |= FAKEDEATH //play dead
- C.UpdateLyingBuckledAndVerbStatus()
- C.remove_changeling_powers()
-
- C.emote("gasp")
-
- spawn(rand(800,2000))
- if(changeling_power(20,1,100,DEAD))
- // charge the changeling chemical cost for stasis
- changeling.chem_charges -= 20
-
- to_chat(C, SPAN_NOTICE(FONT_GIANT("We are ready to rise. Use the Revive verb when you are ready.")))
- C.verbs += /mob/proc/changeling_revive
- return 1
-
-/mob/proc/changeling_revive()
- set category = "Changeling"
- set name = "Revive"
-
- var/mob/living/carbon/C = src
- // restore us to health
- C.revive()
- // remove our fake death flag
- C.status_flags &= ~(FAKEDEATH)
- // let us move again
- C.UpdateLyingBuckledAndVerbStatus()
- // re-add out changeling powers
- C.make_changeling()
- // sending display messages
- to_chat(C, SPAN_NOTICE("We have regenerated."))
- C.verbs -= /mob/proc/changeling_revive
-
-
-//Boosts the range of your next sting attack by 1
-/mob/proc/changeling_boost_range()
- set category = "Changeling"
- 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)
- if(!changeling) return 0
- changeling.chem_charges -= 10
- to_chat(src, SPAN_NOTICE("Your throat adjusts to launch the sting."))
- changeling.sting_range = 2
- src.verbs -= /mob/proc/changeling_boost_range
- spawn(5) src.verbs += /mob/proc/changeling_boost_range
- return 1
-
-
-//Recover from stuns.
-/mob/proc/changeling_unstun()
- set category = "Changeling"
- set name = "Epinephrine Sacs (45)"
- set desc = "Removes all stuns"
-
- var/datum/changeling/changeling = changeling_power(45,0,100,UNCONSCIOUS)
- if(!changeling) return 0
- changeling.chem_charges -= 45
-
- var/mob/living/carbon/human/C = src
- C.set_stat(CONSCIOUS)
- C.SetParalysis(0)
- C.SetStunned(0)
- C.SetWeakened(0)
- C.lying = 0
- C.UpdateLyingBuckledAndVerbStatus()
-
- src.verbs -= /mob/proc/changeling_unstun
- spawn(5) src.verbs += /mob/proc/changeling_unstun
- return 1
+ add_language("Changeling")
-
-//Speeds up chemical regeneration
-/mob/proc/changeling_fastchemical()
- src.mind.changeling.chem_recharge_rate *= 2
- return 1
-
-//Increases macimum chemical storage
-/mob/proc/changeling_engorgedglands()
- src.mind.changeling.chem_storage += 25
- return 1
-
-
-//Prevents AIs tracking you but makes you easily detectable to the human-eye.
-/mob/proc/changeling_digitalcamo()
- set category = "Changeling"
- 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()
- if(!changeling) return 0
-
- var/mob/living/carbon/human/C = src
- if(C.digitalcamo) to_chat(C, SPAN_NOTICE("We return to normal."))
- else to_chat(C, SPAN_NOTICE("We distort our form to prevent AI-tracking."))
- 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)
- sleep(40)
-
- src.verbs -= /mob/proc/changeling_digitalcamo
- spawn(5) src.verbs += /mob/proc/changeling_digitalcamo
- return 1
-
-
-//Starts healing you every second for 10 seconds. Can be used whilst unconscious.
-/mob/proc/changeling_rapidregen()
- set category = "Changeling"
- set name = "Rapid Regeneration (30)"
- set desc = "Begins rapidly regenerating. Does not effect stuns or chemicals."
-
- var/datum/changeling/changeling = changeling_power(30,0,100,UNCONSCIOUS)
- if(!changeling) return 0
- src.mind.changeling.chem_charges -= 30
-
- var/mob/living/carbon/human/C = src
- spawn(0)
- for(var/i = 0, i<10,i++)
- if(C)
- C.adjustBruteLoss(-10)
- C.adjustToxLoss(-10)
- C.adjustOxyLoss(-10)
- C.adjustFireLoss(-10)
- sleep(10)
-
- src.verbs -= /mob/proc/changeling_rapidregen
- spawn(5) src.verbs += /mob/proc/changeling_rapidregen
- return 1
-
-// HIVE MIND UPLOAD/DOWNLOAD DNA
-
-var/global/list/datum/absorbed_dna/hivemind_bank = list()
-
-/mob/proc/changeling_hiveupload()
- set category = "Changeling"
- 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/list/names = list()
- for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna)
- var/valid = 1
- for(var/datum/absorbed_dna/DNB in hivemind_bank)
- if(DNA.name == DNB.name)
- valid = 0
- break
- if(valid)
- names += DNA.name
-
- if(length(names) <= 0)
- to_chat(src, SPAN_NOTICE("The airwaves already have all of our DNA."))
- return
-
- var/S = input("Select a DNA to channel: ", "Channel DNA", null) as null|anything in names
- if(!S) return
-
- var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S)
- if(!chosen_dna)
- return
-
- var/datum/species/spec = all_species[chosen_dna.speciesName]
-
- if(spec && spec.species_flags & SPECIES_FLAG_NEED_DIRECT_ABSORB)
- to_chat(src, SPAN_NOTICE("That species must be absorbed directly."))
- return
-
- changeling.chem_charges -= 10
- hivemind_bank += chosen_dna
- to_chat(src, SPAN_NOTICE("We channel the DNA of [S] to the air."))
- return 1
-
-/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/list/names = list()
- for(var/datum/absorbed_dna/DNA in hivemind_bank)
- if(!(changeling.GetDNA(DNA.name)))
- names[DNA.name] = DNA
-
- if(length(names) <= 0)
- to_chat(src, SPAN_NOTICE("There's no new DNA to absorb from the air."))
- return
-
- var/S = input("Select a DNA absorb from the air: ", "Absorb DNA", null) as null|anything in names
- if(!S) return
- var/datum/dna/chosen_dna = names[S]
- if(!chosen_dna)
- return
-
- changeling.chem_charges -= 20
- absorbDNA(chosen_dna)
- to_chat(src, SPAN_NOTICE("We absorb the DNA of [S] from the air."))
- return 1
-
-// Fake Voice
-
-/mob/proc/changeling_mimicvoice()
- set category = "Changeling"
- set name = "Mimic Voice"
- 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()
- if(!changeling) return
-
- if(changeling.mimicing)
- changeling.mimicing = ""
- to_chat(src, SPAN_NOTICE("We return our vocal glands to their original location."))
- return
-
- var/mimic_voice = sanitize(input(usr, "Enter a name to mimic.", "Mimic Voice", null), MAX_NAME_LEN)
- if(!mimic_voice)
- return
-
- changeling.mimicing = mimic_voice
-
- to_chat(src, SPAN_NOTICE("We shape our glands to take the voice of [mimic_voice], this will stop us from regenerating chemicals while active."))
- to_chat(src, SPAN_NOTICE("Use this power again to return to our original voice and reproduce chemicals again."))
-
- 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)
- sleep(40)
- if(src && src.mind && src.mind.changeling)
- src.mind.changeling.mimicing = ""
//////////
//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/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!"))
+ to_chat(src, "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, TYPE_PROC_REF(/turf, AdjacentTurfs), TYPE_PROC_REF(/turf, 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!"))
+ 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, "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(required_chems=0, verb_path, loud, sting_name = "unnamed sting")
+/mob/proc/changeling_sting(required_chems=0, verb_path)
var/datum/changeling/changeling = changeling_power(required_chems)
+ var/mob/living/carbon/human/ling = src
if(!changeling) return
var/list/victims = list()
- for(var/mob/living/carbon/human/C in oview(changeling.sting_range))
+ for(var/mob/living/carbon/C in oview(changeling.sting_range))
victims += C
- var/mob/living/carbon/human/T = input(src, "Who will we sting?") as null|anything in victims
+ var/mob/living/carbon/T = input(src, "Who will we sting?") as null|anything in victims
- if(!T) return
+ if(!T)
+ return
+ if(T.isSynthetic())
+ to_chat(src, "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
- var/obj/item/organ/external/target_limb = T.get_organ(src.zone_sel.selecting)
- if (!target_limb)
- to_chat(src, SPAN_WARNING("[T] is missing that limb."))
+
+ if(!(T.can_inject(ling,ling.zone_sel.selecting, FALSE) == CAN_INJECT))
+ to_chat(src,SPAN_WARNING("We are unable to pierce the thick material covering [T]."))
return
changeling.chem_charges -= required_chems
changeling.sting_range = 1
src.verbs -= verb_path
spawn(10) src.verbs += verb_path
- if(!loud)
- to_chat(src, SPAN_NOTICE("We stealthily sting [T]."))
- else
- visible_message(SPAN_DANGER("[src] fires an organic shard into [T]!"))
- admin_attack_log(src, T, "Stung their victim using [sting_name]", "Was stung using [sting_name]", "stung using [sting_name]")
-
- for(var/obj/item/clothing/clothes in list(T.head, T.wear_mask, T.wear_suit, T.w_uniform, T.gloves, T.shoes))
- if(istype(clothes) && (clothes.body_parts_covered & target_limb.body_part) && (clothes.item_flags & ITEM_FLAG_THICKMATERIAL))
- to_chat(src, SPAN_WARNING("[T]'s armor has protected them."))
- return //thick clothes will protect from the sting
- if(T.isSynthetic() || BP_IS_ROBOTIC(target_limb)) return
- 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."))
+ to_chat(src, "We stealthily sting [T].")
+ if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting
+ to_chat(T, "You feel a tiny prick.")
return
-
-
-/mob/proc/changeling_lsdsting()
- set category = "Changeling"
- set name = "Hallucination Sting (15)"
- set desc = "Causes terror in the target."
-
- var/mob/living/carbon/human/T = changeling_sting(15, TYPE_PROC_REF(/mob, changeling_lsdsting), sting_name = "Hallucination Sting")
- if(!T) return 0
- spawn(rand(300,600))
- if(T) T.hallucination(400, 80)
- return 1
-
-/mob/proc/changeling_silence_sting()
- set category = "Changeling"
- set name = "Silence sting (10)"
- set desc="Sting target"
-
- var/mob/living/carbon/human/T = changeling_sting(10, TYPE_PROC_REF(/mob, changeling_silence_sting), sting_name = "Silence Sting")
- if(!T) return 0
- T.silent += 30
- return 1
-
-/mob/proc/changeling_blind_sting()
- set category = "Changeling"
- set name = "Blind sting (20)"
- set desc="Sting target"
-
- var/mob/living/carbon/human/T = changeling_sting(20, TYPE_PROC_REF(/mob, changeling_blind_sting), sting_name = "Blind Sting")
- if(!T) return 0
- to_chat(T, SPAN_DANGER("Your eyes burn horrificly!"))
- T.disabilities |= NEARSIGHTED
- spawn(300) T.disabilities &= ~NEARSIGHTED
- T.eye_blind = 10
- T.eye_blurry = 20
- return 1
-
-/mob/proc/changeling_deaf_sting()
- set category = "Changeling"
- set name = "Deaf sting (5)"
- set desc="Sting target:"
-
- var/mob/living/carbon/human/T = changeling_sting(5, TYPE_PROC_REF(/mob, changeling_deaf_sting), sting_name = "Deaf Sting")
- if(!T) return 0
- to_chat(T, SPAN_DANGER("Your ears pop and begin ringing loudly!"))
- T.ear_deaf += 15
- return 1
-
-/mob/proc/changeling_DEATHsting()
- set category = "Changeling"
- set name = "Death Sting (40)"
- set desc = "Causes spasms onto death."
- var/loud = 1
-
- var/mob/living/carbon/human/T = changeling_sting(40, TYPE_PROC_REF(/mob, changeling_DEATHsting), loud, sting_name = "Death Sting")
- if(!T) return 0
- to_chat(T, SPAN_DANGER("You feel a small prick and your chest becomes tight."))
- T.make_jittery(400)
- if(T.reagents) T.reagents.add_reagent(/datum/reagent/lexorin, 10)
- return 1
-
-/mob/proc/changeling_extract_dna_sting()
- set category = "Changeling"
- 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
- if(!changeling)
- return 0
-
- var/mob/living/carbon/human/T = changeling_sting(40, TYPE_PROC_REF(/mob, changeling_extract_dna_sting), sting_name = "Extract DNA Sting")
- if(!T) return 0
- if((MUTATION_HUSK in T.mutations) || (T.species.species_flags & SPECIES_FLAG_NO_SCAN))
- to_chat(src, SPAN_WARNING("We cannot extract DNA from this creature!"))
- return 0
-
- if(T.species.species_flags & SPECIES_FLAG_NEED_DIRECT_ABSORB)
- to_chat(src, SPAN_NOTICE("That species must be absorbed directly."))
- return
-
- var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages)
- absorbDNA(newDNA)
- return 1
diff --git a/code/game/gamemodes/changeling/generic_equip_procs.dm b/code/game/gamemodes/changeling/generic_equip_procs.dm
new file mode 100644
index 0000000000000..803c4ea0d4a11
--- /dev/null
+++ b/code/game/gamemodes/changeling/generic_equip_procs.dm
@@ -0,0 +1,295 @@
+///This is a generic proc that should be called by other ling armor procs to equip them.
+/mob/proc/changeling_generic_armor(armor_type, helmet_type, boot_type, chem_cost)
+
+ if(!ishuman(src))
+ return FALSE
+ var/list/species_restricted = list("exclude", SPECIES_NABBER, SPECIES_ADHERENT,SPECIES_VOX)
+ var/mob/living/carbon/human/M = src
+
+ 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)
+
+ if(!changeling)
+ return
+ if(M.species in species_restricted)
+ to_chat(M,SPAN_WARNING("Our current form is not suited to such a transformation."))
+ return
+
+ //First, check if we're already wearing the armor, and if so, take it off.
+ if(istype(M.wear_suit, armor_type) || istype(M.head, helmet_type) || istype(M.shoes, boot_type))
+ M.visible_message("[M] casts off their [M.wear_suit.name]!",
+ "We cast off our [M.wear_suit.name]",
+ "You hear the organic matter ripping and tearing!")
+ if(istype(M.wear_suit, armor_type))
+ qdel(M.wear_suit)
+ if(istype(M.head, helmet_type))
+ qdel(M.head)
+ if(istype(M.shoes, boot_type))
+ qdel(M.shoes)
+ M.update_inv_wear_suit()
+ M.update_inv_head()
+ M.update_hair()
+ M.update_inv_shoes()
+ return TRUE
+
+ if(M.head || M.wear_suit) //Make sure our slots aren't full
+ to_chat(src, "We require nothing to be on our head, and we cannot wear any external suits, or shoes.")
+ return FALSE
+
+ var/obj/item/clothing/suit/A = new armor_type(src)
+ src.equip_to_slot_or_del(A, slot_wear_suit)
+
+ var/obj/item/clothing/suit/H = new helmet_type(src)
+ src.equip_to_slot_or_del(H, slot_head)
+
+ 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
+ if(istype(M.wear_suit,/obj/item/clothing/suit/space/changeling/armored))
+ playsound(src, 'sound/effects/ling_horror.ogg', 30, 1)
+ src.visible_message(SPAN_DANGER("\the [src] lets out out a monstrous roar of fury!"))
+ else
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ M.update_inv_wear_suit()
+ M.update_inv_head()
+ M.update_hair()
+ M.update_inv_shoes()
+ M.mind.changeling.armor_deployed = TRUE
+ return TRUE
+
+/mob/proc/changeling_generic_equip_all_slots(list/stuff_to_equip, cost)
+ var/datum/changeling/changeling = changeling_power(cost,1,100,CONSCIOUS)
+ if(!changeling)
+ return
+
+ if(!ishuman(src))
+ return FALSE
+
+ var/mob/living/carbon/human/M = src
+
+ var/success = FALSE
+
+ //First, check if we're already wearing the armor, and if so, take it off.
+
+ if(M.mind.changeling.armor_deployed)
+ if(M.head && stuff_to_equip["head"])
+ if(istype(M.head, stuff_to_equip["head"]))
+ qdel(M.head)
+ success = TRUE
+
+ if(M.wear_id && stuff_to_equip["wear_id"])
+ if(istype(M.wear_id, stuff_to_equip["wear_id"]))
+ qdel(M.wear_id)
+ success = TRUE
+
+ if(M.wear_suit && stuff_to_equip["wear_suit"])
+ if(istype(M.wear_suit, stuff_to_equip["wear_suit"]))
+ qdel(M.wear_suit)
+ success = TRUE
+
+ if(M.gloves && stuff_to_equip["gloves"])
+ if(istype(M.gloves, stuff_to_equip["gloves"]))
+ qdel(M.gloves)
+ success = TRUE
+ if(M.shoes && stuff_to_equip["shoes"])
+ if(istype(M.shoes, stuff_to_equip["shoes"]))
+ qdel(M.shoes)
+ success = TRUE
+
+ if(M.belt && stuff_to_equip["belt"])
+ if(istype(M.belt, stuff_to_equip["belt"]))
+ qdel(M.belt)
+ success = TRUE
+
+ if(M.glasses && stuff_to_equip["glasses"])
+ if(istype(M.glasses, stuff_to_equip["glasses"]))
+ qdel(M.glasses)
+ success = TRUE
+
+ if(M.wear_mask && stuff_to_equip["wear_mask"])
+ if(istype(M.wear_mask, stuff_to_equip["wear_mask"]))
+ qdel(M.wear_mask)
+ success = TRUE
+
+ if(M.back && stuff_to_equip["back"])
+ if(istype(M.back, stuff_to_equip["back"]))
+ for(var/atom/movable/AM in M.back.contents) //Dump whatever's in the bag before deleting.
+ AM.forceMove(src.loc)
+ qdel(M.back)
+ success = TRUE
+
+ if(M.w_uniform && stuff_to_equip["w_uniform"])
+ if(istype(M.w_uniform, stuff_to_equip["w_uniform"]))
+ qdel(M.w_uniform)
+ success = TRUE
+
+ if(success)
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ visible_message("[src] pulls on their clothes, peeling it off along with parts of their skin attached!",
+ "We remove and deform our equipment.")
+ M.mind.changeling.armor_deployed = 0
+ src.update_icon()
+ return success
+
+ else
+
+ to_chat(M, "We begin growing our new equipment...")
+
+ var/list/grown_items_list = list()
+
+ var/t = stuff_to_equip["head"]
+ if(!M.head && t)
+ var/I = new t
+ M.equip_to_slot_or_del(I, slot_head)
+ grown_items_list.Add("a helmet")
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ success = TRUE
+ sleep(1 SECOND)
+
+ t = stuff_to_equip["w_uniform"]
+ if(!M.w_uniform && t)
+ var/I = new t
+ M.equip_to_slot_or_del(I, slot_w_uniform)
+ grown_items_list.Add("a uniform")
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ success = TRUE
+ sleep(1 SECOND)
+
+ t = stuff_to_equip["gloves"]
+ if(!M.gloves && t)
+ var/I = new t
+ M.equip_to_slot_or_del(I, slot_gloves)
+ grown_items_list.Add("some gloves")
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ success = TRUE
+ sleep(1 SECOND)
+
+ t = stuff_to_equip["shoes"]
+ if(!M.shoes && t)
+ var/I = new t
+ M.equip_to_slot_or_del(I, slot_shoes)
+ grown_items_list.Add("shoes")
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ success = TRUE
+ sleep(1 SECOND)
+
+ t = stuff_to_equip["belt"]
+ if(!M.belt && t)
+ var/I = new t
+ M.equip_to_slot_or_del(I, slot_belt)
+ grown_items_list.Add("a belt")
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ success = TRUE
+ sleep(1 SECOND)
+
+ t = stuff_to_equip["glasses"]
+ if(!M.glasses && t)
+ var/I = new t
+ M.equip_to_slot_or_del(I, slot_glasses)
+ grown_items_list.Add("some glasses")
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ success = TRUE
+ sleep(1 SECOND)
+
+ t = stuff_to_equip["wear_mask"]
+ if(!M.wear_mask && t)
+ var/I = new t
+ M.equip_to_slot_or_del(I, slot_wear_mask)
+ grown_items_list.Add("a mask")
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ success = TRUE
+ sleep(1 SECOND)
+
+ t = stuff_to_equip["back"]
+ if(!M.back && t)
+ var/I = new t
+ M.equip_to_slot_or_del(I, slot_back)
+ grown_items_list.Add("a backpack")
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ success = TRUE
+ sleep(1 SECOND)
+
+ t = stuff_to_equip["wear_suit"]
+ if(!M.wear_suit && t)
+ var/I = new t
+ M.equip_to_slot_or_del(I, slot_wear_suit)
+ grown_items_list.Add("an exosuit")
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ success = TRUE
+ sleep(1 SECOND)
+
+ t = stuff_to_equip["wear_id"]
+ if(!M.wear_id && t)
+ var/I = new t
+ M.equip_to_slot_or_del(I, slot_wear_id)
+ grown_items_list.Add("an ID card")
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ success = TRUE
+ sleep(1 SECOND)
+
+ var/feedback = english_list(grown_items_list, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" )
+
+ to_chat(M, "We have grown [feedback].")
+
+ if(success)
+ M.mind.changeling.armor_deployed = 1
+ M.mind.changeling.chem_charges -= 10
+ src.update_icon()
+ return success
+
+///This is a generic proc that should be called by other ling weapon procs to equip them.
+/mob/proc/changeling_generic_weapon(weapon_type, make_sound = TRUE, cost)
+
+ var/datum/changeling/changeling = changeling_power(cost,1,100,CONSCIOUS)
+ if(!changeling)
+ return
+
+ if(!ishuman(src))
+ return FALSE
+ if (!src || src.incapacitated() || src.lying)
+ to_chat(src,SPAN_WARNING("We cannot do this while we are stunned."))
+ return FALSE
+ var/mob/living/carbon/human/M = src
+ if(M.l_hand && M.r_hand) //Make sure our hands aren't full.
+ to_chat(src, SPAN_WARNING("We do not have any free hands we can shape into a weapon!"))
+ return FALSE
+
+
+ var/obj/item/weapon/W = new weapon_type(src)
+ if(!M.put_in_hands(W))
+ to_chat(src, SPAN_WARNING("We cannot shape a missing limb into a weapon!"))
+ qdel(W)
+ return FALSE
+
+ src.mind.changeling.chem_charges -= cost
+ if(make_sound)
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ return TRUE
+
+///Special proc for the ranged sting to pass information about the type of sting
+/mob/proc/changeling_equip_ranged(weapon_type, make_sound = FALSE, cost = 10, sting_type)
+
+ var/datum/changeling/changeling = changeling_power(cost,1,100,CONSCIOUS)
+ if(!changeling)
+ return
+
+ if(!ishuman(src))
+ return FALSE
+
+ var/mob/living/carbon/human/M = src
+
+ if(M.l_hand && M.r_hand ) //Make sure our hands aren't full.
+ to_chat(src, SPAN_WARNING("We do not have a free hand to shape into a weapon. Drop something first."))
+ return FALSE
+
+ var/obj/item/gun/projectile/changeling/W = new weapon_type(src)
+ src.put_in_hands(W)
+ W.selected_sting = sting_type
+ src.mind.changeling.selected_ranged_sting = sting_type
+ src.mind.changeling.chem_charges -= cost
+ if(make_sound)
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ return TRUE
diff --git a/code/game/gamemodes/changeling/modularchangling.dm b/code/game/gamemodes/changeling/modularchangling.dm
index d4bd05f7e9350..6d1fb2485e378 100644
--- a/code/game/gamemodes/changeling/modularchangling.dm
+++ b/code/game/gamemodes/changeling/modularchangling.dm
@@ -461,7 +461,7 @@ var/global/list/datum/power/changeling/powerinstances = list()
if(isnull(Thepower))
- to_chat(M.current, "This is awkward. Changeling power purchase failed, please report this bug to a coder!")
+ CRASH("This is awkward. Changeling power purchase failed, please report this bug to a coder!")
return
if(Thepower in purchasedpowers)
diff --git a/code/game/gamemodes/changeling/powers/absorb.dm b/code/game/gamemodes/changeling/powers/absorb.dm
new file mode 100644
index 0000000000000..abebe16b7ffb4
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/absorb.dm
@@ -0,0 +1,135 @@
+/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."
+ ability_icon_state = "ling_absorb_dna"
+ genomecost = 0
+ power_category = CHANGELING_POWER_INHERENT
+ verbpath = /mob/living/proc/changeling_absorb_dna
+
+/**
+ * Absorbs the victim's DNA. Requires a strong grip on the victim.
+ * Doesn't cost anything as it's the most basic ability.
+ */
+/mob/proc/toggle_absorb_type()
+ set category = "Changeling"
+ set name = "Toggle Absorb Type"
+ set desc = "Allows us to determine whether we wish to kill targets we absorb"
+ if(src.mind.changeling)
+ if(src.mind.changeling.absorbing_lethally == ABSORB_NONLETHAL)
+ to_chat(src,SPAN_INFO("We will now kill creatures whose DNA we sample."))
+ src.mind.changeling.absorbing_lethally = ABSORB_LETHAL
+ return
+ if(src.mind.changeling.absorbing_lethally == ABSORB_LETHAL)
+ to_chat(src,SPAN_INFO("We will no longer kill creatures whose DNA we sample."))
+ src.mind.changeling.absorbing_lethally = ABSORB_NONLETHAL
+ return
+/mob/living/proc/changeling_absorb_dna(mob/living/carbon/M)
+ set category = "Changeling"
+ set name = "Absorb DNA"
+ var/list/names = list()
+
+ var/datum/changeling/changeling = changeling_power(0,0,100)
+ if(!changeling) return
+
+ var/obj/item/grab/G = src.get_active_hand()
+ if(!istype(G))
+ to_chat(src, "We must be grabbing a creature in our active hand to absorb them.")
+ return
+
+ var/mob/living/carbon/human/T = G.affecting
+ for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna)
+ names += "[DNA.name]"
+ if(T.real_name in names)
+ to_chat(src,SPAN_NOTICE("We have already sampled the DNA of this creature. There is nothing further to learn here."))
+ return
+ if(T == src)
+ to_chat(src,SPAN_WARNING("We would have to be very silly to try absorbing ourselves."))
+ return
+ if(!istype(T) || T.isSynthetic() || istype(T.species,/datum/species/vox))
+ to_chat(src, "\The [T] is not compatible with our biology.")
+ return
+
+ if(MUTATION_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)
+ to_chat(src, "This creature's DNA is ruined beyond useability!")
+ return
+
+ if(!G.force_danger())
+ to_chat(src, "We must have a tighter grip to absorb this creature.")
+ return
+
+ if(changeling.isabsorbing)
+ to_chat(src, "We are already absorbing!")
+ return
+
+ changeling.isabsorbing = 1
+ for(var/stage = 1, stage<=3, stage++)
+ switch(stage)
+ if(1)
+ to_chat(src, "This creature is compatible. We must hold still...")
+ if(2)
+ to_chat(src, "We extend a proboscis.")
+ src.visible_message("[src] extends a proboscis!")
+ if(3)
+ to_chat(src, "We stab [T] with the proboscis.")
+ src.visible_message("[src] stabs [T] with the proboscis!")
+ to_chat(T, "You feel a sharp stabbing pain!")
+ admin_attack_log(src,T,"Absorbed (changeling)")
+ var/obj/item/organ/external/affecting = T.get_organ(src.zone_sel.selecting)
+ T.apply_damage(50, DAMAGE_BRUTE, affecting , damage_flags = DAMAGE_FLAG_SHARP, used_weapon="organic needle", armor_pen=50)
+
+
+ if(!do_mob(src, T, 150, progress= TRUE) || !G.force_danger())
+ to_chat(src, "Our absorption of the DNA of \the [T] has been interrupted!")
+ changeling.isabsorbing = 0
+ return
+
+ to_chat(src, "We have absorbed the DNA of \the [T]!")
+
+
+ changeling.chem_charges += 10
+
+
+ //add pronouns to this line
+ var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages, T.gender, T.pronouns, T.flavor_texts, T.icon_render_keys, T.descriptors)
+ 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(dna_data in changeling.absorbed_dna)
+ continue
+ absorbDNA(dna_data)
+ changeling.absorbedcount++
+ LIST_RESIZE(T.mind.changeling.absorbed_dna, 1)
+
+ // This is where lings get boosts from eating eachother
+ if(T.mind.changeling.lingabsorbedcount)
+ for(var/a = 1 to T.mind.changeling.lingabsorbedcount)
+ changeling.lingabsorbedcount++
+ changeling.geneticpoints += 6
+ changeling.max_geneticpoints += 6
+
+ to_chat(src, SPAN_NOTICE("We have feasted upon a fellow changeling, and grown stronger."))
+
+ 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
+ T.death(0)
+ T.Drain()
+ return TRUE
+ changeling.absorbedcount++
+ changeling.isabsorbing = 0
+ to_chat(T, SPAN_DANGER("You feel everything go black, as \the [src] releases you."))
+ if(changeling.absorbing_lethally == ABSORB_LETHAL)
+ T.death(0)
+ T.Drain()
+ changeling.geneticpoints += 1
+ changeling.max_geneticpoints += 1
+ if(changeling.absorbing_lethally == ABSORB_NONLETHAL)
+ changeling.geneticpoints += 2
+ changeling.max_geneticpoints += 2
+ T.Sleeping(200)
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/armblade.dm b/code/game/gamemodes/changeling/powers/armblade.dm
new file mode 100644
index 0000000000000..4f4b1c8a1a382
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/armblade.dm
@@ -0,0 +1,329 @@
+
+/datum/power/changeling/arm_hammer
+ name = "Arm Hammer"
+ desc = "We reform one of our arms into a weighty hammer capable of smashing quickly through obstacles or victims alike. We can slam enemies aside with it on disarm intent, sending them flying."
+ helptext = "We may retract our armhammer by using the ability again with the hammer in our active hand."
+ ability_icon_state = "ling_hammer"
+ enhancedtext = "The hammer will have further armor peneratration."
+ genomecost = 2
+ power_category = CHANGELING_POWER_WEAPONS
+ verbpath = /mob/proc/changeling_arm_hammer
+/datum/power/changeling/arm_blade
+ name = "Arm Blade"
+ desc = "We reform one of our arms into a deadly blade capable of slicing through armor. It can pry open airlocks, breaking them in the process."
+ helptext = "We may retract our armblade by using the ability again with the blade in our active hand."
+ enhancedtext = "The blade will have greater armor peneratration and parry more effectively."
+ ability_icon_state = "ling_blade"
+ genomecost = 2
+ power_category = CHANGELING_POWER_WEAPONS
+ verbpath = /mob/proc/changeling_arm_blade
+
+//HAMMERTIME
+/mob/proc/changeling_arm_hammer()
+ set category = "Changeling"
+ set name = "Arm Hammer (15)"
+ var/holding = src.get_active_hand()
+ if (istype(holding, /obj/item/melee/changeling/arm_hammer))
+ to_chat(src,SPAN_WARNING("We shrink our arm back to its normal size."))
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ qdel(holding)
+ return 0
+ if(src.mind.changeling.recursive_enhancement)
+ src.mind.changeling.recursive_enhancement = FALSE
+ if(changeling_generic_weapon(/obj/item/melee/changeling/arm_hammer/greater, cost = 15))
+ //to_chat(src, "We prepare an extra sharp blade.")
+ return 1
+ else
+ if(changeling_generic_weapon(/obj/item/melee/changeling/arm_hammer, cost = 15))
+ return 1
+ return 0
+
+//Grows a scary, and powerful arm blade.
+/mob/proc/changeling_arm_blade()
+ set category = "Changeling"
+ set name = "Arm Blade (20)"
+ var/holding = src.get_active_hand()
+ if (istype(holding, /obj/item/melee/changeling/arm_blade))
+ to_chat(src,SPAN_WARNING("We retract our blade back into our body."))
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ qdel(holding)
+ return 0
+ if(src.mind.changeling.recursive_enhancement)
+ src.mind.changeling.recursive_enhancement = FALSE
+ if(changeling_generic_weapon(/obj/item/melee/changeling/arm_blade/greater, cost = 20))
+ //to_chat(src, "We prepare an extra sharp blade.")
+ return 1
+
+ else
+ if(changeling_generic_weapon(/obj/item/melee/changeling/arm_blade, cost = 20))
+ return 1
+ return 0
+
+//Claws
+/datum/power/changeling/claw
+ name = "Claw"
+ desc = "We reform one of our arms into a deadly claw. We can instantly aggressively grab enemies with it on grab intent."
+ helptext = "We may retract our claw by using the ability again with the claw in our active hand."
+ enhancedtext = "The claw will have armor peneratration."
+ ability_icon_state = "ling_claw"
+ genomecost = 1
+ power_category = CHANGELING_POWER_WEAPONS
+ verbpath = /mob/proc/changeling_claw
+
+//Grows a scary, and powerful claw.
+/mob/proc/changeling_claw()
+ set category = "Changeling"
+ set name = "Claw (15)"
+ var/holding = src.get_active_hand()
+ if (istype(holding, /obj/item/melee/changeling/claw))
+ to_chat(src,SPAN_WARNING("We reform our claw back into an ordinary appendage."))
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ qdel(holding)
+ return 0
+ if(src.mind.changeling.recursive_enhancement)
+ src.mind.changeling.recursive_enhancement = FALSE
+ if(changeling_generic_weapon(/obj/item/melee/changeling/claw/greater, 1, cost = 15))
+ to_chat(src, SPAN_NOTICE("We prepare an extra sharp claw."))
+ return 1
+
+ else
+ if(changeling_generic_weapon(/obj/item/melee/changeling/claw, 1, cost = 15))
+ return 1
+ return 0
+
+/obj/item/melee/changeling
+ name = "arm weapon"
+ desc = "A grotesque weapon made out of bone and flesh that cleaves through people as a hot knife through butter."
+ icon = 'icons/obj/weapons/melee_physical.dmi'
+ icon_state = "ling_blade"
+ item_icons = list(
+ icon_l_hand = 'icons/mob/onmob/items/lefthand.dmi',
+ icon_r_hand = 'icons/mob/onmob/items/righthand.dmi',
+ )
+ item_state = "arm_blade"
+ w_class = ITEM_SIZE_HUGE
+ force = 5
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ anchored = TRUE
+ throwforce = 0 //Just to be on the safe side
+ throw_range = 0
+ throw_speed = 0
+ 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"
+ canremove = FALSE
+ base_parry_chance = 40 // The base chance for the weapon to parry.
+
+/obj/item/melee/changeling/Initialize()
+ . = ..()
+ START_PROCESSING(SSobj, src)
+ if(ismob(loc))
+ src.creator = loc
+
+
+/obj/item/melee/changeling/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ creator = null
+ return ..()
+/obj/item/melee/changeling/Process()
+ var/mob/living/carbon/human/H = creator
+ if ( H.handcuffed || (H.stat != CONSCIOUS))
+ qdel(src)
+ if(!istype(loc,/mob))
+ src.visible_message(SPAN_DANGER("\The [src] rapidly decays and melts into a puddle of slime!"))
+ new /obj/decal/cleanable/ling_vomit(src.loc)
+ qdel(src)
+ ..()
+/obj/item/melee/changeling/arm_hammer
+ name = "arm hammer"
+ desc = "A hammer made out of flesh and bone, heavy enough to smash through armor and people alike."
+ icon_state = "ling_hammer"
+ item_state = "ling_hammer"
+ force = 25
+ hitsound = 'sound/weapons/genhit3.ogg'
+ armor_penetration = 30
+ sharp = FALSE
+ edge = FALSE
+ attack_verb = list("attacked", "struck", "smashed", "clubbed", "beaten", "hit", "battered", "smacked")
+ base_parry_chance = 50
+ var/last_slam = null
+ var/cooldown = 7 SECONDS
+/obj/item/melee/changeling/arm_hammer/greater
+ name = "arm greathammer"
+ desc = "A massive hammer made out of flesh and bone, heavy enough to smash through armor and people alike."
+ armor_penetration = 50
+
+/obj/item/melee/changeling/arm_hammer/use_before(atom/target,mob/user)
+ if (user.a_intent == I_DISARM)
+ if ((last_slam + cooldown > world.time))
+ to_chat(user, SPAN_WARNING("We are still recovering from our last slam attack."))
+ return
+ if(istype(target,/mob/living/carbon))
+ var/mob/living/carbon/M = target
+ user.do_attack_animation(M)
+ var/target_zone = check_zone(user.zone_sel.selecting)
+ M.apply_damage(force, DAMAGE_BRUTE, target_zone)
+ user.visible_message(SPAN_DANGER("\the [user] slams their arm hammer into \the [M] in a furious blow, sending them flying!"))
+ M.throw_at(get_edge_target_turf(user, user.dir), 5, 5)
+ playsound(src, 'sound/weapons/punch1.ogg', 30, 1)
+ last_slam = world.time
+
+ if(istype(target,/turf/simulated/wall) || istype(target,/obj/structure) && (user.a_intent != I_HELP))
+ target.damage_health(rand(25, 75), DAMAGE_BRUTE)
+
+/obj/item/melee/changeling/arm_blade
+ name = "arm blade"
+ desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter."
+ icon_state = "ling_blade"
+ item_state = "arm_blade"
+ force = 35
+ armor_penetration = 15
+ sharp = TRUE
+ edge = TRUE
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "ripped", "diced", "cut")
+ base_parry_chance = 60
+
+/obj/item/melee/changeling/arm_blade/use_before(atom/target,mob/user)
+ if (istype(target, /obj/machinery/door/airlock))
+ var/obj/machinery/door/airlock/A = target
+ if(A.locked)
+ to_chat(user,SPAN_WARNING("We cannot force open an airlock held in place by bolts."))
+ return
+ A.visible_message(SPAN_DANGER("\The [user] forces \the [src] between \the [A], causing the metal to creak!"))
+ playsound(A, 'sound/machines/airlock_creaking.ogg', 100, 1)
+ if (do_after(user, 5 SECONDS, A, DO_DEFAULT | DO_USER_UNIQUE_ACT | DO_PUBLIC_PROGRESS) && !A.locked)
+ A.welded = FALSE
+ A.update_icon()
+ playsound(A, 'sound/effects/meteorimpact.ogg', 100, 1)
+ A.visible_message(SPAN_DANGER("\The [user] tears \the [A] open with \a [src]!"))
+ addtimer(new Callback(A, /obj/machinery/door/airlock/.proc/open, TRUE), 0)
+ A.open()
+ A.set_broken(TRUE)
+
+ if (istype(target, /obj/machinery/door/firedoor))
+ var/obj/machinery/door/firedoor/A = target
+ playsound(A, 'sound/machines/airlock_creaking.ogg', 100, 1)
+ if (do_after(user, 2 SECONDS, A, DO_DEFAULT | DO_USER_UNIQUE_ACT | DO_PUBLIC_PROGRESS))
+ A.visible_message(SPAN_DANGER("\The [user] pries \the [A] wide open effortlessly!"))
+ A.open(TRUE)
+ if (istype(target, /obj/machinery/door/blast))
+ var/obj/machinery/door/blast/A = target
+ if(!istype(user.get_inactive_hand(),/obj/item/melee/changeling/arm_blade))
+ to_chat(user,SPAN_WARNING("We require an armblade in both arms to be able to exert enough force to pry a blast door open."))
+ return
+ A.visible_message(SPAN_DANGER("\The [user] forces both armblades between \the [A], prying with incredible strength!"))
+ playsound(A, 'sound/machines/airlock_creaking.ogg', 100, 1)
+ if (do_after(user, 20 SECONDS, A, DO_DEFAULT | DO_USER_UNIQUE_ACT | DO_PUBLIC_PROGRESS) )
+ A.update_icon()
+ playsound(A, 'sound/effects/meteorimpact.ogg', 100, 1)
+ A.visible_message(SPAN_DANGER("\The [user] tears \the [A] wide open!"))
+ A.force_open()
+
+/obj/item/melee/changeling/arm_blade/greater
+ name = "arm greatblade"
+ desc = "A grotesque blade made out of bone and flesh that cleaves through people and armor as a hot knife through butter."
+ armor_penetration = 30
+ base_parry_chance = 70
+
+/obj/item/melee/changeling/claw
+ name = "hand claw"
+ item_state = "ling_claw"
+ desc = "A grotesque claw made out of bone and flesh that cleaves through people as a hot knife through butter."
+ icon_state = "ling_claw"
+ force = 20
+ sharp = FALSE
+ edge = TRUE
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ base_parry_chance = 50
+ var/last_grab = null
+ var/cooldown = 5 SECONDS
+/obj/item/melee/changeling/claw/use_before(mob/living/M,mob/user)
+
+ if(user.a_intent == I_GRAB)
+ if ((last_grab + cooldown > world.time))
+ to_chat(user, SPAN_WARNING("We are still recovering from our last grab attack."))
+ return
+ if(istype(M,/mob/living/carbon) && (user.mind.changeling.chem_charges > 10))
+ var/mob/living/carbon/human/H = user
+ if(!user.get_inactive_hand())
+ user.swap_hand()
+ H.species.attempt_grab(H,M)
+ var/obj/item/grab/holding = H.get_active_hand()
+ if(istype(holding,/obj/item/grab))
+ holding.upgrade(bypass_cooldown = TRUE)
+ last_grab = world.time
+
+/obj/item/melee/changeling/claw/greater
+ name = "hand greatclaw"
+ force = 25
+ armor_penetration = 20
+ base_parry_chance = 60
+
+/datum/power/changeling/arm_shield
+ name = "Arm Shield"
+ desc = "We reform one of our arms into a resilient shield to protect ourselves from harm. The shield can deflect projectiles, but not reliably."
+ helptext = "We may retract our shield by using the ability again with the shield in hand."
+ enhancedtext = "The shield will be more resistant to damage."
+ ability_icon_state = "ling_shield"
+ genomecost = 2
+ power_category = CHANGELING_POWER_WEAPONS
+ verbpath = /mob/proc/changeling_arm_shield
+
+/mob/proc/changeling_arm_shield()
+ set category = "Changeling"
+ set name = "Arm Shield (20)"
+ var/holding = src.get_active_hand()
+ if (istype(holding, /obj/item/shield/riot/changeling))
+ to_chat(src,SPAN_WARNING("We retract our claw back into our body."))
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ qdel(holding)
+ return 0
+ if(src.mind.changeling.recursive_enhancement)
+ src.mind.changeling.recursive_enhancement = FALSE
+ if(changeling_generic_weapon(/obj/item/shield/riot/changeling/greater))
+ //to_chat(src, "We prepare an extra sharp blade.")
+ return 1
+
+ else
+ if(changeling_generic_weapon(/obj/item/shield/riot/changeling))
+ return 1
+ return 0
+
+/obj/item/shield/riot/changeling
+ name = "chitin shield"
+ item_state = "ling_shield"
+ icon_state = "ling_shield"
+ desc = "A monstrously thick and bulky mess of fleshy chitin, covered in shards of bone."
+ throwforce = 0
+ throw_speed = 0
+ throw_range = 0
+ anchored = 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"
+ canremove = FALSE
+/obj/item/shield/riot/changeling/greater
+ name = "chitin greatshield"
+ max_block = 50
+ can_block_lasers = TRUE
+
+/obj/item/shield/riot/changeling/Initialize()
+ . = ..()
+ START_PROCESSING(SSobj, src)
+ if(ismob(loc))
+ src.creator = loc
+
+/obj/item/shield/riot/changeling/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ creator = null
+ return ..()
+
+/obj/item/shield/riot/changeling/Process()
+ var/mob/living/carbon/human/H = creator
+ if ( H.handcuffed || (H.stat != CONSCIOUS))
+ qdel(src)
+ if(!istype(loc,/mob))
+ src.visible_message(SPAN_DANGER("\The [src] rapidly decays and melts into a puddle of slime!"))
+ new /obj/decal/cleanable/ling_vomit(src.loc)
+ qdel(src)
+ ..()
diff --git a/code/game/gamemodes/changeling/powers/armor.dm b/code/game/gamemodes/changeling/powers/armor.dm
new file mode 100644
index 0000000000000..eeb9429c550f1
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/armor.dm
@@ -0,0 +1,196 @@
+/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
+ verbpath = /mob/proc/changeling_spacesuit
+
+/mob/proc/changeling_spacesuit()
+ set category = "Changeling"
+ set name = "Organic Space Suit (20)"
+ if(changeling_generic_armor(/obj/item/clothing/suit/space/changeling,/obj/item/clothing/head/helmet/space/changeling,/obj/item/clothing/shoes/magboots/changeling, 20))
+ return TRUE
+ return FALSE
+
+/datum/power/changeling/armor
+ name = "Chitinous Spacearmor"
+ desc = "We turn our skin into tough chitin to protect us from damage and space exposure."
+ helptext = "To remove the armor, use the ability again."
+ ability_icon_state = "ling_armor"
+ genomecost = 3
+ verbpath = /mob/proc/changeling_spacearmor
+
+/mob/proc/changeling_spacearmor()
+ set category = "Changeling"
+ set name = "Chitinous Spacearmor (20)"
+
+ if(changeling_generic_armor(/obj/item/clothing/suit/space/changeling/armored,/obj/item/clothing/head/helmet/space/changeling/armored,/obj/item/clothing/shoes/magboots/changeling/armored, 20))
+
+ return TRUE
+ return FALSE
+
+//Space suit
+
+/obj/item/clothing/suit/space/changeling
+ name = "organic suit"
+ icon_state = "lingspacesuit"
+ desc = "A thin, skintight mass of pressure and temperature-resistant organic tissue, evolved to facilitate space travel."
+ allowed = list(/obj/item/device/flashlight, /obj/item/tank/oxygen_emergency, /obj/item/tank/oxygen_emergency_extended)
+ armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) //No armor at all.
+ canremove = FALSE
+ can_breach = FALSE
+ flags_inv = BLOCKHAIR | HIDETAIL
+ var/remove_on_respec = TRUE
+
+/obj/item/clothing/suit/space/changeling/Initialize()
+ . = ..()
+ slowdown_per_slot[slot_wear_suit] = 0
+ START_PROCESSING(SSobj, src)
+ if(ismob(loc))
+ to_chat(src,SPAN_WARNING("[loc.name]\'s flesh splits and twists, forming into a thin fleshy membrane around their body!"))
+
+/obj/item/clothing/suit/space/changeling/dropped(mob/user)
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+
+/obj/item/clothing/suit/space/changeling/Destroy()
+
+ STOP_PROCESSING(SSobj, src)
+
+ return ..()
+
+/obj/item/clothing/head/helmet/space/changeling
+ name = "flesh mass"
+ icon_state = "lingspacehelmet"
+ desc = "A covering of pressure and temperature-resistant organic tissue with a translucent, glass-like front made of organic tissue."
+ flags_inv = HIDEEARS|BLOCKHEADHAIR //Again, no THICKMATERIAL.
+ body_parts_covered = HEAD|FACE|EYES
+ var/remove_on_respec = TRUE
+ canremove = 0
+ flags_inv = BLOCKHAIR | HIDETAIL | HIDEFACE
+
+
+/obj/item/clothing/head/helmet/space/changeling/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/item/clothing/shoes/magboots/changeling
+ desc = "A suction cupped mass of flesh, shaped like a foot."
+ name = "fleshy grippers"
+ icon_state = "lingspacesuit"
+ action_button_name = "Toggle Grippers"
+ canremove = 0
+ online_slowdown = 3
+ var/remove_on_respec = TRUE
+
+/obj/item/clothing/shoes/magboots/changeling/set_slowdown()
+ slowdown_per_slot[slot_shoes] = shoes? max(0, shoes.slowdown_per_slot[slot_shoes]): 0 //So you can't put on magboots to make you walk faster.
+ if (magpulse)
+ slowdown_per_slot[slot_shoes] += online_slowdown
+
+/obj/item/clothing/shoes/magboots/changeling/attack_self(mob/user)
+ if(magpulse)
+ item_flags &= ~ITEM_FLAG_NOSLIP
+ magpulse = 0
+ set_slowdown()
+ force = 3
+ to_chat(user, "We release our grip on the floor.")
+ else
+ item_flags |= ITEM_FLAG_NOSLIP
+ magpulse = 1
+ set_slowdown()
+ force = 5
+ to_chat(user, "We cling to the terrain below us.")
+
+
+/obj/item/clothing/shoes/magboots/changeling/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+//Armor
+
+/obj/item/clothing/suit/space/changeling/armored
+ name = "chitinous mass"
+ desc = "A protective shell of muscle, bone and chitin, designed for violence and violence alone."
+ icon_state = "lingarmor"
+ body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
+ //It costs 3 points, so it should be very protective.
+ //armor = list(melee = 75, bullet = 65, laser = 60, energy = 60, bomb = 60, bio = 0, rad = 0)
+ armor = list(
+ melee = ARMOR_MELEE_VERY_HIGH,
+ bullet = ARMOR_BALLISTIC_RESISTANT,
+ laser = ARMOR_LASER_HANDGUNS,
+ energy = ARMOR_ENERGY_RESISTANT,
+ bomb = ARMOR_BOMB_PADDED,
+ )
+ siemens_coefficient = 0.3
+ can_breach = FALSE
+ flags_inv = BLOCKHAIR | HIDETAIL
+ //max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE
+ slowdown_general = 0
+
+/obj/item/clothing/suit/space/changeling/armored/Initialize()
+ . = ..()
+ START_PROCESSING(SSobj, src)
+ slowdown_per_slot[slot_wear_suit] = 0
+ if(ismob(loc))
+ to_chat(src,SPAN_WARNING("Our muscles twist and our bones crack with a crunching noise as we form claws, teeth and armor!"))
+
+ /*
+ loc.visible_message("[loc.name]\'s flesh turns black, quickly transforming into a hard, chitinous mass!",
+ "We harden our flesh, creating a suit of armor!",
+ "You hear organic matter ripping and tearing!")
+ */
+/obj/item/clothing/suit/space/changeling/armored/Destroy()
+
+
+ STOP_PROCESSING(SSobj, src)
+
+ return ..()
+
+
+/obj/item/clothing/head/helmet/space/changeling/armored
+ name = "chitinous mass"
+ desc = "An enormous eyeless maw of teeth that hungers."
+ icon_state = "lingarmorhelmet"
+ armor = list(
+ melee = ARMOR_MELEE_VERY_HIGH,
+ bullet = ARMOR_BALLISTIC_RESISTANT,
+ laser = ARMOR_LASER_HANDGUNS,
+ energy = ARMOR_ENERGY_RESISTANT,
+ bomb = ARMOR_BOMB_PADDED,
+ )
+ siemens_coefficient = 0.3
+ max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
+ flags_inv = BLOCKHAIR | HIDETAIL | HIDEFACE
+
+/obj/item/clothing/shoes/magboots/changeling/armored
+ desc = "A tough, hard mass of chitin, with long talons for digging into terrain."
+ name = "chitinous talons"
+ icon_state = "lingchameleon"
+ action_button_name = "Toggle Talons"
+
+/obj/item/clothing/gloves/combat/changeling //Combined insulated/fireproof gloves
+ name = "chitinous gauntlets"
+ desc = "Very resilient gauntlets made out of black chitin. It looks very durable, and can probably resist electrical shock in addition to the elements."
+ icon_state = "lingarmorgloves"
+ armor = list(melee = 75, bullet = 60, laser = 60,energy = 60, bomb = 60, bio = 0, rad = 0) //No idea if glove armor gets checked
+ siemens_coefficient = 0
+
+/obj/item/clothing/shoes/boots/combat/changeling //Noslips
+ name = "chitinous boots"
+ desc = "Footwear made out of a hard, black chitinous material. The bottoms of these appear to have spikes that can protrude or extract itself into and out \
+ of the floor at will, granting the wearer stability."
+ icon_state = "lingchameleon"
+ armor = list(
+ melee = ARMOR_MELEE_VERY_HIGH,
+ bullet = ARMOR_BALLISTIC_RESISTANT,
+ laser = ARMOR_LASER_HANDGUNS,
+ energy = ARMOR_ENERGY_RESISTANT,
+ bomb = ARMOR_BOMB_PADDED,
+ )
+ siemens_coefficient = 0.3
+ cold_protection = FEET
+ min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE
+ heat_protection = FEET
+ max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE
diff --git a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm
new file mode 100644
index 0000000000000..bc6c03d541dee
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm
@@ -0,0 +1,42 @@
+//Augmented Eyesight: Gives you thermal vision. Also, higher DNA cost because of how powerful it is.
+//needs to be fixed to act per tick, polaris port didn't work in that respect
+
+/datum/power/changeling/augmented_eyesight
+ name = "Augmented Eyesight"
+ desc = "Allows for extra sensory information in the eyes, increasing photon reception capabilities for a short span of time."
+ helptext = "Grants us temporary thermal vision, allowing us to track organics beyond walls."
+ ability_icon_state = "ling_augmented_eyesight"
+ genomecost = 2
+ verbpath = /mob/proc/changeling_augmented_eyesight
+
+/mob/proc/changeling_augmented_eyesight()
+ set category = "Changeling"
+ set name = "Augmented Eyesight (3 per tick)"
+ set desc = "We evolve our eyes to sense the infrared temporarily."
+
+ var/datum/changeling/changeling = changeling_power(3,0,100,CONSCIOUS)
+ var/mob/living/carbon/human/H = src
+ var/obj/item/organ/external/parent = H.get_organ(BP_HEAD)
+ var/has_organ = FALSE;
+ for(var/obj/item/organ/internal/augment/ling_lenses/eyes in H.internal_organs)
+ has_organ++
+ if(!changeling)
+ return FALSE
+ if(!has_organ)
+ var/obj/item/organ/internal/augment/eyes = new /obj/item/organ/internal/augment/ling_lenses
+ eyes.forceMove(src)
+ eyes.replaced(src, parent)
+ eyes = null
+ has_organ++
+ if(has_organ)
+ if(H.seedarkness)
+ H.seedarkness = FALSE
+ for(var/obj/item/organ/internal/augment/ling_lenses/eyes in H.internal_organs)
+ if(!eyes.is_active)
+ to_chat(src, SPAN_NOTICE("We feel a minute twitch in our eyes, and a hidden layer to the world is revealed."))
+ eyes.is_active = TRUE
+ else
+ to_chat(src,SPAN_NOTICE("Our lenses retract, causing us to lose our augmented vision."))
+ eyes.is_active = FALSE
+ changeling.chem_charges -= 5
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm b/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm
new file mode 100644
index 0000000000000..d145130362eec
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm
@@ -0,0 +1,219 @@
+/datum/power/changeling/bioelectrogenesis
+ name = "Bioelectrogenesis"
+ desc = "We reconfigure a large number of cells in our body to generate an electric charge. \
+ On demand, we can attempt to recharge anything in our active hand, or we can touch someone with an electrified hand, shocking them."
+ helptext = "We can shock someone by grabbing them and using this ability, or using the ability with an empty hand and touching them. \
+ Shocking someone costs ten chemicals per use."
+ enhancedtext = "Shocking biologicals without grabbing only requires five chemicals, and has more disabling power."
+ ability_icon_state = "ling_bioelectrogenesis"
+ genomecost = 2
+ verbpath = /mob/living/carbon/human/proc/changeling_bioelectrogenesis
+
+//Recharge whatever's in our hand, or shock people.
+/mob/living/carbon/human/proc/changeling_bioelectrogenesis()
+ set category = "Changeling"
+ set name = "Bioelectrogenesis (15 + 10/shock)"
+ set desc = "Recharges anything in your hand, or shocks people."
+ var/obj/held_item = get_active_hand()
+ if (istype(held_item, /obj/item/weapon/electric_hand))
+ qdel(held_item)
+ return 0
+ var/datum/changeling/changeling = changeling_power(20,0,100,CONSCIOUS)
+
+
+ var/last_shock = null
+ var/cooldown = 3 SECONDS
+ if(!changeling)
+ return FALSE
+
+ if(!held_item)
+ if(src.mind.changeling.recursive_enhancement)
+ if(changeling_generic_weapon(/obj/item/weapon/electric_hand/efficent,0))
+ to_chat(src, "We will shock others more efficently.")
+ return TRUE
+ else
+ if(changeling_generic_weapon(/obj/item/weapon/electric_hand,0)) //Chemical cost is handled in the equip proc.
+ 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
+ if((last_shock + cooldown > world.time))
+ to_chat(src,SPAN_DANGER("Our bioelectric hand is still recharging a voltage powerful enough to shock someone!"))
+ return FALSE
+ //If we're grabbing someone, electrocute them.
+ var/obj/item/grab/G = src.get_active_hand()
+ if(istype(G))
+ if(G.affecting)
+ G.affecting.electrocute_act(10 * siemens, src, 1.0, BP_CHEST, 0)
+ var/agony = 80 * siemens //Does more than if hit with an electric hand, since grabbing is slower.
+ G.affecting.stun_effect_act(0, agony, BP_CHEST, src)
+
+ admin_attack_log(src,G.affecting,"Changeling shocked")
+
+ if(siemens)
+ visible_message("Arcs of electricity strike [G.affecting]!",
+ "Our hand channels raw electricity into [G.affecting].",
+ "You hear sparks!")
+ last_shock = world.time
+ else
+ to_chat(src, "Our gloves block us from shocking \the [G.affecting].")
+ src.mind.changeling.chem_charges -= 10
+ return 1
+
+ //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/list/L = new() //We make a new list to avoid copypasta.
+
+ //Check our hand.
+ if(istype(held_item,/obj/item/cell))
+ L.Add(held_item)
+
+ //Now check our hand's item's contents, so we can recharge guns and other stuff.
+ for(var/obj/item/cell/cell in held_item.contents)
+ L.Add(cell)
+
+ //Now for the actual recharging.
+ for(var/obj/item/cell/cell in L)
+ visible_message("Some sparks fall out from \the [src.name]\'s [held_item]!",
+ "Our hand channels raw electricity into \the [held_item].",
+ "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/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.
+ to_chat(src, "We are unable to affect \the [held_item].")
+ else
+ src.mind.changeling.chem_charges -= 10
+ return success
+
+/obj/item/weapon/electric_hand
+ name = "electrified hand"
+ desc = "You could probably shock someone badly if you touched them, or recharge something."
+ icon = 'icons/obj/weapons/melee_energy.dmi'
+ icon_state = "electric_hand"
+ canremove = FALSE
+ var/shock_cost = 10
+ var/agony_amount = 60
+ var/electrocute_amount = 10
+ var/last_shock = null
+ var/cooldown = 5 SECONDS
+/obj/item/weapon/electric_hand/efficent
+ name = "supercharged hand"
+ desc = "Power! Unlimited Power!"
+ shock_cost = 5
+ agony_amount = 80
+ electrocute_amount = 20
+
+/obj/item/weapon/electric_hand/Initialize()
+ . = ..()
+ if(ismob(loc))
+ src.visible_message(SPAN_DANGER("[src]\'s hand crackles with electricity, sparks flying about!"))
+ var/T = get_turf(src)
+ new /obj/sparks(T)
+
+/obj/item/weapon/electric_hand/afterattack(atom/target, mob/living/carbon/human/user, proximity)
+ if(!target)
+ return
+ if(!proximity)
+ return
+
+ // Handle glove conductivity.
+ var/obj/item/clothing/gloves/gloves = user.gloves
+ var/siemens = 1
+ if(gloves)
+ siemens = gloves.siemens_coefficient
+
+ //Excuse the copypasta.
+ if(istype(target,/mob/living/carbon))
+ var/mob/living/carbon/C = target
+
+ if(user.mind.changeling.chem_charges < shock_cost)
+ to_chat(user, SPAN_WARNING("We require at least 10 chemicals to electrocute [C]!"))
+ return FALSE
+ if((last_shock + cooldown > world.time))
+ to_chat(user,SPAN_DANGER("Our bioelectric hand is still recharging a voltage powerful enough to shock someone!"))
+ return FALSE
+ C.electrocute_act(electrocute_amount * siemens,src,1.0,BP_CHEST)
+ C.stun_effect_act(0, agony_amount * siemens, BP_CHEST, src)
+
+ admin_attack_log(user,C,"Shocked with [src]")
+
+ if(siemens)
+ to_chat(user, SPAN_NOTICE("Our hand channels raw electricity into [C]"))
+ src.visible_message(SPAN_DANGER("Arcs of electricity strike [C]!"))
+ last_shock = world.time
+ //visible_message("Arcs of electricity strike [C]!",
+ //"Our hand channels raw electricity into [C]",
+ //"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 TRUE
+
+ else if(istype(target,/mob/living/silicon))
+ var/mob/living/silicon/S = target
+
+ if(user.mind.changeling.chem_charges < 10)
+ to_chat(src, "We require at least 10 chemicals to electrocute [S]!")
+ return FALSE
+
+ S.electrocute_act(60,src,0.75) //If only they had surge protectors.
+ if(siemens)
+ visible_message("Arcs of electricity strike [S]!",
+ "Our hand channels raw electricity into [S]",
+ "You hear sparks!")
+ to_chat(S, "Warning: Electrical surge detected!")
+ //qdel(src)
+ user.mind.changeling.chem_charges -= 10
+ return TRUE
+
+ else
+ if(istype(target,/obj))
+ var/success = 0
+ 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)
+ src.visible_message(SPAN_WARNING("[src]\'s hand crackles, sparks darting from it to \the [cell]"))
+ /*visible_message("Some sparks fall out from \the [target]!",
+ "Our hand channels raw electricity into \the [target].",
+ "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/sparks(Turf)
+ T.update_icon()
+ i--
+ sleep(1 SECOND)
+ success = 1
+ break
+ if(success == 0)
+ to_chat(src, "We are unable to affect \the [target].")
+ else
+ qdel(src)
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/blind_sting.dm b/code/game/gamemodes/changeling/powers/blind_sting.dm
new file mode 100644
index 0000000000000..8e5f178db6b59
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/blind_sting.dm
@@ -0,0 +1,33 @@
+/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
+ verbpath = /mob/proc/changeling_blind_sting
+ sting_effect = /mob/proc/ling_blind
+ is_sting = TRUE
+/mob/proc/apply_blind()
+ src.disabilities &= ~NEARSIGHTED
+/mob/proc/ling_blind(mob/living/carbon/M, duration)
+ addtimer(new Callback(M,/mob/.proc/apply_blind),duration)
+ M.eye_blind = 10
+ M.eye_blurry = 20
+/mob/proc/changeling_blind_sting()
+ set category = "Changeling"
+ set name = "Blind sting (20)"
+ set desc="Sting target"
+
+ var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_blind_sting)
+ if(!T)
+ return FALSE
+ admin_attack_log(src,T,"Blind sting (changeling)")
+ to_chat(T, "You are surrounded by darkness. You cannot see!")
+ T.disabilities |= NEARSIGHTED
+ var/duration = 300
+ if(src.mind.changeling.recursive_enhancement)
+ duration = duration + 150
+ to_chat(src, "They will be deprived of sight for longer.")
+ ling_blind(T,duration)
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/boost_range.dm b/code/game/gamemodes/changeling/powers/boost_range.dm
new file mode 100644
index 0000000000000..cae0ecaa359de
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/boost_range.dm
@@ -0,0 +1,123 @@
+/datum/power/changeling/boost_range
+ name = "Boost Range"
+ desc = "We evolve the ability to fire biological darts from our hand to deliver our sting."
+ helptext = "Allows us to use the effects of our stings at a distance"
+ enhancedtext = "The range is extended to five tiles."
+ ability_icon_state = "ling_sting_boost_range"
+ genomecost = 1
+ allowduringlesserform = 1
+ verbpath = /mob/proc/changeling_boost_range
+
+//Boosts the range of your next sting attack by 1
+
+/obj/item/ammo_casing/chemdart/changeling
+ name = "organic dart"
+ desc = "A needle thin dart formed of bone."
+ icon_state = "ling_dart"
+ icon = 'icons/obj/projectiles.dmi'
+ caliber = CALIBER_DART
+ projectile_type = /obj/item/projectile/bullet/chemdart/changeling
+ leaves_residue = FALSE
+/obj/item/projectile/bullet/chemdart/changeling
+ name = "organic dart"
+ fire_sound = 'sound/weapons/Genhit.ogg'
+ icon_state = "ling_dart"
+ life_span = 3
+ icon = 'icons/obj/projectiles.dmi'
+/obj/item/projectile/bullet/chemdart/changeling/on_hit(atom/target, blocked = 0, def_zone)
+ if(blocked < 100 && isliving(target))
+ var/mob/living/L = target
+ if (src.firer == target)
+ return
+ var/datum/power/changeling/sting = src.firer.mind.changeling.selected_ranged_sting
+ //use firer ling mind to set ling selected ranged sting
+ if(L.can_inject(null, def_zone) == CAN_INJECT)
+ call(sting.sting_effect)(target,sting.sting_duration)
+
+
+/obj/item/gun/projectile/changeling
+ name = "thin mass"
+ desc = "A thin, narrow and hollow mass of flesh protruding from the palm. It twitches and pulses, eager to strike."
+ can_reload = FALSE
+ screen_shake = FALSE
+ space_recoil = FALSE
+ has_safety = FALSE
+ silenced = TRUE
+ auto_eject = FALSE
+ handle_casings = CLEAR_CASINGS
+ max_shells = 1
+ starts_loaded = TRUE
+ has_safety = FALSE
+ silenced = TRUE
+ icon = 'icons/obj/guns/dartgun.dmi'
+ item_state = null
+ icon_state = "ling_dart"
+ fire_sound = 'sound/weapons/Genhit.ogg'
+ anchored = TRUE
+ throwforce = 0 //Just to be on the safe side
+ throw_range = 0
+ throw_speed = 0
+ screen_shake = FALSE
+ space_recoil = FALSE
+ 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"
+ canremove = FALSE
+ jam_chance = 0
+ ammo_type = /obj/item/ammo_casing/chemdart/changeling
+ var/list/available_stings = list()
+ var/selected_sting = null
+
+/obj/item/gun/projectile/changeling/Initialize()
+ . = ..()
+ START_PROCESSING(SSobj, src)
+ if(ismob(loc))
+ src.creator = loc
+
+/obj/item/gun/projectile/changeling/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ creator = null
+ return ..()
+
+/obj/item/gun/projectile/changeling/Process()
+ var/mob/living/carbon/human/H = creator
+
+ if ( H.handcuffed || (H.stat != CONSCIOUS) || (length(loaded) == 0))
+ //playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ to_chat(creator,SPAN_WARNING("We retract our ranged stinger back into our body."))
+ qdel(src)
+
+/mob/proc/changeling_boost_range()
+ set category = "Changeling"
+ set name = "Ranged Stinger (20)"
+ set desc="We transform one of our hands so that it can fire organic darts at targets."
+
+ var/list/available_stings = list()
+ var/datum/changeling/changeling = changeling_power(10,0,100)
+ if(!changeling)
+ return TRUE
+ //changeling.chem_charges -= 10
+ var/holding = src.get_active_hand()
+ if (istype(holding, /obj/item/gun/projectile/changeling))
+ to_chat(src,SPAN_WARNING("We retract our ranged stinger back into our body."))
+ //playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ qdel(holding)
+ return 0
+ for (var/datum/power/changeling/power in src.mind.changeling.purchased_powers)
+ if (power.is_sting)
+ available_stings.Add(power)
+ if(length(available_stings) == 0)
+ to_chat(src,SPAN_WARNING("We do not have any stings that we can fire at range!"))
+ return FALSE
+ var/datum/power/changeling/sting= input(src,"Select a sting.", "Select") as null|anything in available_stings
+ if(sting)
+ var/sting_name = sting.name
+ to_chat(src,SPAN_WARNING("We prepare to fire a [sting_name]"))
+ if(src.mind.changeling.recursive_enhancement)
+ if(changeling_equip_ranged(/obj/item/gun/projectile/changeling, sting_type = sting))
+ return 1
+
+ else
+ if(changeling_equip_ranged(/obj/item/gun/projectile/changeling, sting_type = sting))
+ return 1
+ return 0
diff --git a/code/game/gamemodes/changeling/powers/cryo_sting.dm b/code/game/gamemodes/changeling/powers/cryo_sting.dm
new file mode 100644
index 0000000000000..c51ffdd7fb5a3
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/cryo_sting.dm
@@ -0,0 +1,38 @@
+/datum/power/changeling/cryo_sting
+ name = "Cryogenic Sting"
+ desc = "We silently sting a biological with a cocktail of chemicals that freeze them."
+ helptext = "Does not provide a warning to the victim, though they will likely realize they are suddenly freezing. Has \
+ a three minute cooldown between uses."
+ enhancedtext = "Increases the amount of chemicals injected."
+ ability_icon_state = "ling_sting_cryo"
+ genomecost = 1
+ verbpath = /mob/proc/changeling_cryo_sting
+ sting_effect = /mob/proc/ling_cryo
+ is_sting = TRUE
+
+/mob/proc/cryo_cooldown()
+ to_chat(src, "Our cryogenic string is ready to be used once more.")
+ src.verbs |= /mob/proc/changeling_cryo_sting
+
+/mob/proc/ling_cryo(mob/living/carbon/M, duration)
+ M.reagents.add_reagent(/datum/reagent/toxin/cryotoxin, 10)
+
+/mob/proc/changeling_cryo_sting()
+ set category = "Changeling"
+ 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)
+ if(!T)
+ return FALSE
+ admin_attack_log(src,T,"Cryo sting (changeling)")
+ var/inject_amount = 10
+ if(src.mind.changeling.recursive_enhancement)
+ inject_amount = inject_amount * 1.5
+ to_chat(src, "We inject extra chemicals.")
+ if(T.reagents)
+ T.reagents.add_reagent(/datum/reagent/toxin/cryotoxin, inject_amount)
+ src.verbs -= /mob/proc/changeling_cryo_sting
+ addtimer(new Callback(src,/mob/.proc/cryo_cooldown), 3 MINUTES)
+
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/darkvision.dm b/code/game/gamemodes/changeling/powers/darkvision.dm
new file mode 100644
index 0000000000000..011403d6c8361
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/darkvision.dm
@@ -0,0 +1,30 @@
+/datum/power/changeling/darksight
+ name = "Dark Sight"
+ desc = "We change the composition of our eyes, banishing the shadows from our vision."
+ helptext = "We will be able to see in the dark."
+ ability_icon_state = "ling_augmented_eyesight"
+ genomecost = 0
+ power_category = CHANGELING_POWER_INHERENT
+ verbpath = /mob/proc/changeling_darksight
+
+/mob/proc/changeling_darksight()
+ set category = "Changeling"
+ set name = "Toggle Darkvision"
+ set desc = "We are able see in the dark."
+
+ var/datum/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS)
+ if(!changeling)
+ return 0
+
+ if(istype(src,/mob/living/carbon))
+ var/mob/living/carbon/C = src
+ //disables thermals if we have it to prevent permanent double stacking
+ src.sight &= ~(SEE_MOBS)
+ if(C.seedarkness)
+ to_chat(C, SPAN_NOTICE("We no longer need light to see."))
+ C.seedarkness = 0
+ else
+ to_chat(C, SPAN_NOTICE("We allow the shadows to return."))
+ C.seedarkness = 1
+
+ return 0
diff --git a/code/game/gamemodes/changeling/powers/deaf_sting.dm b/code/game/gamemodes/changeling/powers/deaf_sting.dm
new file mode 100644
index 0000000000000..7b93982c4c769
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/deaf_sting.dm
@@ -0,0 +1,29 @@
+/datum/power/changeling/deaf_sting
+ name = "Deaf Sting"
+ desc = "We silently sting a human, completely deafening them for a short time."
+ enhancedtext = "Deafness duration is extended."
+ ability_icon_state = "ling_sting_deafen"
+ genomecost = 1
+ allowduringlesserform = 1
+ verbpath = /mob/proc/changeling_deaf_sting
+ sting_effect = /mob/proc/ling_deaf
+ sting_duration = 300
+ is_sting = TRUE
+/mob/proc/ling_deaf(mob/living/carbon/M, duration)
+ M.ear_deaf += duration
+/mob/proc/changeling_deaf_sting()
+ set category = "Changeling"
+ set name = "Deaf sting (5)"
+ set desc="Sting target:"
+
+ var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_deaf_sting)
+ if(!T)
+ return FALSE
+ admin_attack_log(src,T,"Deaf sting (changeling)")
+ var/duration = 300
+ if(src.mind.changeling.recursive_enhancement)
+ duration = duration + 100
+ to_chat(src, "They will be unable to hear for a little longer.")
+ to_chat(T, "Your ears pop and begin ringing loudly!")
+ ling_deaf(T,duration)
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/death_sting.dm b/code/game/gamemodes/changeling/powers/death_sting.dm
new file mode 100644
index 0000000000000..aebbb4905ec8e
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/death_sting.dm
@@ -0,0 +1,23 @@
+/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 = 7
+ verbpath = /mob/proc/changeling_DEATHsting
+ is_sting = TRUE
+/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 FALSE
+ admin_attack_log(src,T,"Death sting (changeling)")
+ to_chat(T, "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(/datum/reagent/lexorin, 40)
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/delayed_toxin_sting.dm b/code/game/gamemodes/changeling/powers/delayed_toxin_sting.dm
new file mode 100644
index 0000000000000..536e64ed5299d
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/delayed_toxin_sting.dm
@@ -0,0 +1,50 @@
+/datum/power/changeling/delayed_toxic_sting
+ name = "Delayed Toxic Sting"
+ desc = "We silently sting a biological, causing a significant amount of toxins after a few minutes, allowing us to not \
+ implicate ourselves. While rarely lethal, highly effective for making organics leave their post to seek medical attention."
+ helptext = "The toxin takes effect in about two minutes. Multiple applications within the two minutes will not cause increased toxicity."
+ enhancedtext = "The toxic damage is doubled."
+ ability_icon_state = "ling_sting_del_toxin"
+ genomecost = 1
+ verbpath = /mob/proc/changeling_delayed_toxic_sting
+ sting_effect = /mob/proc/ling_delay_toxin
+ is_sting = TRUE
+//disgusting copypasta, but the alternative is a new prop to ling powers just for this one sting
+/mob/proc/ling_delay_toxin(mob/living/carbon/M, null)
+ sleep(2 MINUTES)
+ M.adjustToxLoss(rand(20,30))
+/mob/proc/changeling_delayed_toxic_sting()
+ set category = "Changeling"
+ set name = "Delayed Toxic Sting (20)"
+ set desc = "Injects the target with a toxin that will take effect after a few minutes."
+
+ var/effect_delay = 2 MINUTES
+ var/damage = rand(20,30)
+ var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_delayed_toxic_sting)
+ if(!T)
+ return FALSE
+ admin_attack_log(src,T,"Delayed toxic sting (changeling)")
+ if(src.mind.changeling.recursive_enhancement)
+ //type_to_give = /datum/modifier/delayed_toxin_sting/strong
+ to_chat(src, "Our toxin will be extra potent, when it strikes.")
+ damage = rand(40,60)
+ src.mind.changeling.recursive_enhancement = FALSE
+ if(!(T in src.mind.changeling.toxin_victims))
+ src.mind.changeling.toxin_victims.Add(T)
+ sleep(effect_delay)
+ if(!(T in src.mind.changeling.toxin_victims))
+ return FALSE
+ T.adjustToxLoss(damage)
+ src.mind.changeling.toxin_victims.Remove(T)
+
+ //var/test = new /datum/delayed_toxin_sting()
+ //var/sting_delay = /datum/modifier/delayed_toxin_sting
+
+
+
+
+ //T.add_modifier(type_to_give, 10 SECONDS)
+
+
+
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/electric_lockpick.dm b/code/game/gamemodes/changeling/powers/electric_lockpick.dm
new file mode 100644
index 0000000000000..1641c06c08966
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/electric_lockpick.dm
@@ -0,0 +1,99 @@
+/datum/power/changeling/electric_lockpick
+ name = "Electric Lockpick"
+ desc = "We discreetly evolve a finger to be able to send a small electric charge. \
+ We can open most electrical locks, but it will be obvious when we do so."
+ helptext = "Use the ability, then touch something that utilizes an electrical locking system, to open it. Each use costs 10 chemicals."
+ ability_icon_state = "ling_electric_lockpick"
+ genomecost = 3
+ verbpath = /mob/proc/changeling_electric_lockpick
+
+//Emag-lite
+/mob/proc/changeling_electric_lockpick()
+ set category = "Changeling"
+ set name = "Electric Lockpick (5 + 10/use)"
+ set desc = "Bruteforces open most electrical locking systems, at 10 chemicals per use."
+
+
+
+ var/obj/held_item = get_active_hand()
+ if (istype(held_item, /obj/item/weapon/finger_lockpick))
+ //to_chat(src,SPAN_WARNING("We cease charging our hand with electricity"))
+ //playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ qdel(held_item)
+ return 0
+ var/datum/changeling/changeling = changeling_power(5,0,100,CONSCIOUS)
+ if(!changeling)
+ return FALSE
+
+ if(!held_item)
+ if(changeling_generic_weapon(/obj/item/weapon/finger_lockpick,0,5)) //Chemical cost is handled in the equip proc.
+ return TRUE
+ return FALSE
+
+/obj/item/weapon/finger_lockpick
+ name = "finger lockpick"
+ desc = "This finger appears to be an organic datajack."
+ icon = 'icons/obj/weapons/melee_energy.dmi'
+ icon_state = "electric_lockpick"
+ canremove = FALSE
+/obj/item/weapon/finger_lockpick/Initialize()
+ . = ..()
+ if(ismob(loc))
+ to_chat(loc, "We shape our finger to fit inside electronics, and are ready to force them open.")
+/obj/item/weapon/finger_lockpick/use_before(atom/target, mob/living/user, click_parameters)
+
+ if(!target)
+ return
+ if(!target.Adjacent(user))
+ return
+ if(!user.mind.changeling)
+ return
+
+ var/datum/changeling/ling_datum = user.mind.changeling
+ var/breach_secure = TRUE
+ if(ling_datum.chem_charges < 10)
+ to_chat(user, "We require at least 10 chem charges to do that.")
+ return
+
+ //Airlocks require an ugly block of code, but we don't want to just call emag_act(), since we don't want to break airlocks forever.
+ if(istype(target,/obj/machinery/door))
+ if(istype(target,/obj/machinery/door/airlock/highsecurity) || istype(target,/obj/machinery/door/airlock/vault))
+ to_chat(user, SPAN_WARNING("This door's security is complex. It will take us longer to determine the charge needed to breach it."))
+ breach_secure = do_after(user, (15 SECONDS + rand(0, 5 SECONDS) + rand(0, 5 SECONDS)), target, do_flags = (DO_DEFAULT | DO_BOTH_UNIQUE_ACT) & ~DO_SHOW_PROGRESS)
+ var/obj/machinery/door/door = target
+ to_chat(user, "We send an electrical pulse up our finger, and into \the [target], attempting to open it.")
+
+ if(door.density && door.operable() && breach_secure)
+ door.do_animate("spark")
+ sleep(6)
+ //More typechecks, because windoors can't be locked. Fun.
+ if(istype(target,/obj/machinery/door/airlock))
+
+ var/obj/machinery/door/airlock/airlock = target
+
+ if(airlock.locked) //Check if we're bolted.
+ airlock.unlock()
+ to_chat(user, "We've unlocked \the [airlock]. Another pulse is requried to open it.")
+ else //We're not bolted, so open the door already.
+ airlock.open()
+ to_chat(user, "We've opened \the [airlock].")
+ else
+ door.open() //If we're a windoor, open the windoor.
+ to_chat(user, "We've opened \the [door].")
+ else //Probably broken or no power.
+ to_chat(user, "The door does not respond to the pulse.")
+ door.add_fingerprint(user)
+ log_and_message_admins("finger-lockpicked \an [door].")
+ ling_datum.chem_charges -= 10
+ return TRUE
+
+ else if(istype(target,/obj/structure/closet/secure_closet)) //This should catch everything else we might miss, without a million typechecks.
+ var/obj/O = target
+ to_chat(user, "We send an electrical pulse up our finger, and into \the [O].")
+ O.add_fingerprint(user)
+ O.emag_act(1,user,src)
+ log_and_message_admins("finger-lockpicked \an [O].")
+ ling_datum.chem_charges -= 10
+
+ return TRUE
+ return FALSE
diff --git a/code/game/gamemodes/changeling/powers/endoarmor.dm b/code/game/gamemodes/changeling/powers/endoarmor.dm
new file mode 100644
index 0000000000000..8a5fbc84ab768
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/endoarmor.dm
@@ -0,0 +1,23 @@
+/datum/power/changeling/endoarmor
+ name = "Endoarmor"
+ desc = "We grow hard plating underneath our skin, granting us subtle armor organics will not notice."
+ helptext = "The armor will not provide us any protection from lasers or fire."
+ genomecost = 1
+ isVerb = 0
+ verbpath = /mob/proc/changeling_endoarmor
+
+/mob/proc/changeling_endoarmor()
+ if(ishuman(src))
+
+ var/mob/living/carbon/human/H = src
+ var/obj/item/organ/internal/augment/armor = new /obj/item/organ/internal/augment/armor/changeling
+ //H.add_modifier(/datum/modifier/endoarmor)
+ var/obj/item/organ/external/parent = H.get_organ(BP_CHEST)
+ armor.forceMove(src)
+ armor.replaced(src, parent)
+ src.mind.changeling.purchased_organs.Add(armor)
+ armor = null
+ playsound(src, 'sound/effects/corpsecube.ogg',35,1)
+ to_chat(src, SPAN_NOTICE("Our flesh knits and cracks as the endoarmor forms beneath our skin."))
+
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/engorged_glands.dm b/code/game/gamemodes/changeling/powers/engorged_glands.dm
new file mode 100644
index 0000000000000..4300fa2850e8e
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/engorged_glands.dm
@@ -0,0 +1,13 @@
+/datum/power/changeling/EngorgedGlands
+ 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."
+ genomecost = 1
+ isVerb = 0
+ verbpath = /mob/proc/changeling_engorgedglands
+
+//Increases macimum chemical storage
+/mob/proc/changeling_engorgedglands()
+ src.mind.changeling.chem_storage += 30
+ //src.mind.changeling.chem_recharge_rate *= 2
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/enrage.dm b/code/game/gamemodes/changeling/powers/enrage.dm
new file mode 100644
index 0000000000000..fb45843189435
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/enrage.dm
@@ -0,0 +1,43 @@
+/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."
+ helptext = "Renders us nearly immune to pain and superhumanly fast for one minute, and losing the ability to \
+ be accurate at ranged while active. Afterwards, we will suffer extreme amounts of exhaustion, slowing us and potentially causing us to even pass out."
+ enhancedtext = "The length of our rage will be extended by an additional 30 seconds."
+ ability_icon_state = "ling_berserk"
+ genomecost = 2
+ power_category = CHANGELING_POWER_ENHANCEMENTS
+ allowduringlesserform = 1
+ verbpath = /mob/living/proc/changeling_berserk
+
+// Makes the ling very upset.
+/mob/living/proc/changeling_berserk()
+ set category = "Changeling"
+ set name = "Enrage (30)"
+ set desc = "Causes you to go Berserk."
+
+ var/datum/changeling/changeling = changeling_power(30,0,100)
+ var/mob/living/carbon/human/H = src
+ var/obj/item/organ/external/parent = H.get_organ(BP_GROIN)
+ var/has_organ = 0;
+ if(!changeling)
+ return 0
+
+ for(var/obj/item/organ/internal/augment/changeling/ragecore/rage in H.internal_organs)
+ has_organ++
+ if(has_organ == 0)
+ var/obj/item/organ/internal/augment/changeling/rage = new /obj/item/organ/internal/augment/changeling/ragecore
+ rage.forceMove(src)
+ rage.replaced(src, parent)
+ rage = null
+ has_organ++
+ if(has_organ == 1)
+ for(var/obj/item/organ/internal/augment/changeling/ragecore/rage in H.internal_organs)
+ if(src.mind.changeling.recursive_enhancement)
+ rage.ticks_remaining = 90;
+ src.mind.changeling.recursive_enhancement = FALSE
+ else
+ rage.ticks_remaining = 60;
+ to_chat(src,SPAN_WARNING("Our adrenal glands release a surge of energy, pushing our body to its limits!"))
+ changeling.chem_charges -= 30
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/escape_restraints.dm b/code/game/gamemodes/changeling/powers/escape_restraints.dm
new file mode 100644
index 0000000000000..8ba5de26fd773
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/escape_restraints.dm
@@ -0,0 +1,57 @@
+/datum/power/changeling/escape_restraints
+ name = "Escape Restraints"
+ desc = "We evolve more complex joints"
+ helptext = "We can instantly escape from most restraints and bindings, but we cannot do it often."
+ enhancedtext = "More frequent escapes."
+ ability_icon_state = "ling_escape_restraints"
+ genomecost = 2
+ verbpath = /mob/proc/changeling_escape_restraints
+
+//Escape Cuffs. By design this does not escape from straight jackets
+/mob/proc/changeling_escape_restraints()
+ set category = "Changeling"
+ set name = "Escape Restraints (40)"
+ set desc = "Removes handcuffs and legcuffs instantly."
+
+ 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)
+ if(!changeling)
+ return 0
+ if(world.time < changeling.next_escape)
+ to_chat(src, "We are still recovering from our last escape...")
+ return 0
+ if(!(C.handcuffed || 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, "We are are not restrained in a way we can escape...")
+ return 0
+
+ changeling.chem_charges -= 40
+
+ to_chat(C,"We contort our extremities and slip our cuffs.")
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ if(C.handcuffed)
+ var/obj/item/weapon/W = C.handcuffed
+ C.unEquip(C.handcuffed)
+ //C.handcuffed = null
+ if(C.buckled && C.buckled.buckle_require_restraints)
+ C.buckled.unbuckle_mob()
+ C.drop_from_inventory(C.handcuffed)
+ if (C.client)
+ C.client.screen -= W
+ W.forceMove(C.loc)
+ W.dropped(C)
+ if(W)
+ W.layer = initial(W.layer)
+ if(istype(C.wear_suit, /obj/item/clothing/suit/straight_jacket))
+ var/obj/item/clothing/suit/straight_jacket/SJ = C.wear_suit
+ SJ.forceMove(C.loc)
+ SJ.dropped(C)
+ C.wear_suit = null
+ escape_cooldown *= 1.5 // Straight jackets are tedious compared to cuffs.
+
+ if(src.mind.changeling.recursive_enhancement)
+ escape_cooldown *= 0.5
+
+ changeling.next_escape = world.time + escape_cooldown //And now we set the timer
+
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/extract_dna_sting.dm b/code/game/gamemodes/changeling/powers/extract_dna_sting.dm
new file mode 100644
index 0000000000000..ba1394461a0f7
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/extract_dna_sting.dm
@@ -0,0 +1,40 @@
+/datum/power/changeling/extractdna
+ 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
+ verbpath = /mob/proc/changeling_extract_dna_sting
+
+/mob/proc/changeling_extract_dna_sting()
+ set category = "Changeling"
+ 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
+ if(!changeling)
+ return FALSE
+
+ var/mob/living/carbon/human/T = changeling_sting(40, /mob/proc/changeling_extract_dna_sting)
+
+ if(!T)
+ return
+
+ if(!istype(T) || T.isSynthetic())
+ to_chat(src, "\The [T] is not compatible with our biology.")
+ return FALSE
+
+ if(MUTATION_HUSK in T.mutations)
+ to_chat(src, "This creature's DNA is ruined beyond useability!")
+ return FALSE
+
+ admin_attack_log(src,T,"DNA extraction sting (changeling)")
+
+ 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.gender, T.pronouns, T.flavor_texts, T.icon_render_keys, T.descriptors )
+ absorbDNA(newDNA)
+
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/fabricate_clothing.dm b/code/game/gamemodes/changeling/powers/fabricate_clothing.dm
new file mode 100644
index 0000000000000..82162217f0923
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/fabricate_clothing.dm
@@ -0,0 +1,303 @@
+var/global/list/changeling_fabricated_clothing = list(
+ "w_uniform" = /obj/item/clothing/under/chameleon/changeling,
+ "head" = /obj/item/clothing/head/chameleon/changeling,
+ "wear_suit" = /obj/item/clothing/suit/chameleon/changeling,
+ "shoes" = /obj/item/clothing/shoes/chameleon/changeling,
+ "gloves" = /obj/item/clothing/gloves/chameleon/changeling,
+ "wear_mask" = /obj/item/clothing/mask/chameleon/changeling,
+ "glasses" = /obj/item/clothing/glasses/chameleon/changeling,
+ "back" = /obj/item/storage/backpack/chameleon/changeling,
+ "wear_id" = /obj/item/card/id/syndicate/changeling
+ )
+
+/datum/power/changeling/fabricate_clothing
+ name = "Fabricate Clothing"
+ desc = "We reform our flesh to resemble various cloths, leathers, and other materials, allowing us to quickly create a disguise. \
+ We cannot be relieved of this clothing by others. We start"
+ helptext = "The disguise we create offers no defensive ability. Each equipment slot that is empty will be filled with fabricated equipment. \
+ To remove our new fabricated clothing, use this ability again."
+ ability_icon_state = "ling_fabricate_clothing"
+ genomecost = 1
+ verbpath = /mob/proc/changeling_fabricate_clothing
+
+//Grows biological versions of chameleon clothes.
+/mob/proc/changeling_fabricate_clothing()
+ set category = "Changeling"
+ set name = "Fabricate Clothing (10)"
+
+ if(changeling_generic_equip_all_slots(changeling_fabricated_clothing, cost = 10))
+ return TRUE
+ return FALSE
+
+/obj/item/clothing/under/chameleon/changeling
+ name = "malformed flesh"
+ icon_state = "lingchameleon"
+ item_state = "lingchameleon"
+ worn_state = "lingchameleon"
+ desc = "The flesh all around us has grown a new layer of cells that can shift appearance and create a biological fabric that cannot be distinguished from \
+ ordinary cloth, allowing us to make ourselves appear to wear almost anything."
+ origin_tech = list() //The base chameleon items have origin technology, which we will inherit if we don't null out this variable.
+ canremove = 0 //Since this is essentially flesh impersonating clothes, tearing someone's skin off as if it were clothing isn't possible.
+
+/obj/item/clothing/under/chameleon/changeling/emp_act(severity) //As these are purely organic, EMP does nothing to them.
+ ..()
+ return
+
+/obj/item/clothing/under/chameleon/changeling/verb/shred() //Remove individual pieces if needed.
+ set name = "Shred Jumpsuit"
+ set category = "Chameleon Items"
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ qdel(H.wear_id)
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ visible_message("[H] tears off [src]!",
+ "We remove [src].")
+ qdel(src)
+/obj/item/clothing/under/chameleon/changeling/verb/secrete_accessory()
+ set name = "Secrete Accessory"
+ set category = "Chameleon Items"
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ if(H.l_hand && H.r_hand) //Make sure our hands aren't full.
+ to_chat(H, SPAN_WARNING("Our hands are full. Drop something first."))
+ return FALSE
+ var/A = new /obj/item/clothing/accessory/chameleon/changeling
+ H.put_in_hands(A)
+ to_chat(H, SPAN_WARNING("We pluck off a piece of our flesh, and prepare to shape it in the form of some organic trinket."))
+/obj/item/clothing/head/chameleon/changeling
+ name = "malformed head"
+ icon_state = "lingchameleon"
+ item_state = "lingchameleon"
+ flags_inv = HIDEFACE | HIDEEYES
+ desc = "Our head is swelled with a large quantity of rapidly shifting skin cells. We can reform our head to resemble various hats and \
+ helmets that biologicals are so fond of wearing."
+ origin_tech = list()
+ canremove = 0
+
+/obj/item/clothing/head/chameleon/changeling/emp_act(severity)
+ ..()
+ return
+
+/obj/item/clothing/head/chameleon/changeling/verb/shred() //The copypasta is real.
+ set name = "Shred Helmet"
+ set category = "Chameleon Items"
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ visible_message("[H] tears off [src]!",
+ "We remove [src].")
+ qdel(src)
+
+/obj/item/clothing/suit/chameleon/changeling
+ name = "chitinous chest"
+ icon_state = "lingchameleon"
+ item_icons = list(
+ slot_l_hand_str = 'icons/mob/onmob/items/lefthand_spacesuits.dmi',
+ slot_r_hand_str = 'icons/mob/onmob/items/righthand_spacesuits.dmi',
+ )
+ item_state = "lingchameleon"
+ desc = "The cells in our chest are rapidly shifting, ready to reform into material that can resemble most pieces of clothing."
+ origin_tech = list()
+ canremove = FALSE
+
+/obj/item/clothing/suit/chameleon/changeling/emp_act(severity)
+ ..()
+ return
+
+/obj/item/clothing/suit/chameleon/changeling/verb/shred()
+ set name = "Shred Suit"
+ set category = "Chameleon Items"
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ visible_message("[H] tears off [src]!",
+ "We remove [src].")
+ qdel(src)
+
+/obj/item/clothing/shoes/chameleon/changeling
+ name = "malformed feet"
+ icon_state = "lingchameleon"
+ item_state = "lingchameleon"
+ desc = "Our feet are overlayed with another layer of flesh and bone on top. We can reform our feet to resemble various boots and shoes."
+ origin_tech = list()
+ canremove = FALSE
+
+/obj/item/clothing/shoes/chameleon/changeling/emp_act()
+ ..()
+ return
+
+/obj/item/clothing/shoes/chameleon/changeling/verb/shred()
+ set name = "Shred Shoes"
+ set category = "Chameleon Items"
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ visible_message("[H] tears off [src]!",
+ "We remove [src].")
+ qdel(src)
+
+/obj/item/storage/backpack/chameleon/changeling
+ name = "backpack"
+ icon_state = "lingchameleon"
+ item_icons = list(
+ slot_l_hand_str = 'icons/mob/onmob/items/lefthand_backpacks.dmi',
+ slot_r_hand_str = 'icons/mob/onmob/items/righthand_backpacks.dmi',
+ )
+ item_state = "lingchameleon"
+ desc = "A large pouch imbedded in our back, it can shift form to resemble many common backpacks that other biologicals are fond of using."
+ origin_tech = list()
+ canremove = FALSE
+ item_flags = ITEM_FLAG_IS_CHAMELEON_ITEM
+
+/obj/item/storage/backpack/chameleon/changeling/emp_act()
+ ..()
+ return
+
+/obj/item/storage/backpack/chameleon/changeling/verb/shred()
+ set name = "Shred Backpack"
+ set category = "Chameleon Items"
+ if(ishuman(loc))
+ if(length(contents))
+ to_chat(loc,SPAN_WARNING("We cannot shred our storage while there are items still inside."))
+ return
+ var/mob/living/carbon/human/H = loc
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ visible_message("[H] tears off [src]!",
+ "We remove [src].")
+ for(var/atom/movable/AM in src.contents) //Dump whatever's in the bag before deleting.
+ AM.forceMove(get_turf(loc))
+ qdel(src)
+
+/obj/item/clothing/gloves/chameleon/changeling
+ name = "malformed hands"
+ icon_state = "lingchameleon"
+ item_state = "lingchameleon"
+ desc = "Our hands have a second layer of flesh on top. We can reform our hands to resemble a large variety of fabrics and materials that biologicals \
+ tend to wear on their hands. Remember that these won't protect your hands from harm."
+ origin_tech = list()
+ canremove = FALSE
+
+/obj/item/clothing/gloves/chameleon/changeling/emp_act()
+ ..()
+ return
+
+/obj/item/clothing/gloves/chameleon/changeling/verb/shred()
+ set name = "Shred Gloves"
+ set category = "Chameleon Items"
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ visible_message("[H] tears off [src]!",
+ "We remove [src].")
+ qdel(src)
+
+/obj/item/clothing/mask/chameleon/changeling
+ name = "chitin visor"
+ flags_inv = HIDEFACE | HIDEEYES
+ icon_state = "lingchameleon"
+ item_state = "lingchameleon"
+ desc = "A transparent visor of brittle chitin covers our face. We can reform it to resemble various masks that biologicals use. It can also utilize internal \
+ tanks.."
+ origin_tech = list()
+ canremove = FALSE
+
+/obj/item/clothing/mask/chameleon/changeling/emp_act()
+ ..()
+ return
+
+/obj/item/clothing/mask/chameleon/changeling/verb/shred()
+ set name = "Shred Mask"
+ set category = "Chameleon Items"
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ visible_message("[H] tears off [src]!",
+ "We remove [src].")
+ qdel(src)
+/obj/item/clothing/accessory/chameleon/changeling
+ name = "malformed lump"
+ icon_state = "ling_accessory"
+ item_state = "ling_accessory"
+ desc = "A small, rapidly shifting and twitching clump of flesh, with veins pulsing throughout."
+ origin_tech = list()
+ throwforce = 0 //Just to be on the safe side
+ throw_range = 0
+ throw_speed = 0
+
+/obj/item/clothing/accessory/chameleon/changeling/attack_hand()
+ src.visible_message(SPAN_WARNING("\The [src] melts upon being touched!"))
+ new /obj/decal/cleanable/ling_vomit(loc)
+ qdel(src)
+
+/obj/item/clothing/accessory/chameleon/changeling/emp_act()
+ ..()
+ return
+
+/obj/item/clothing/accessory/chameleon/changeling/verb/shred()
+ set name = "Shred Accessory"
+ set category = "Chameleon Items"
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ visible_message("[H] tears off [src]!",
+ "We remove [src].")
+ qdel(src)
+
+/obj/item/clothing/glasses/chameleon/changeling
+ name = "chitin goggles"
+ icon_state = "lingchameleon"
+ item_state = "lingchameleon"
+ desc = "A transparent piece of eyewear made out of brittle chitin. We can reform it to resemble various glasses and goggles."
+ origin_tech = list()
+ canremove = FALSE
+
+/obj/item/clothing/glasses/chameleon/changeling/emp_act()
+ ..()
+ return
+
+/obj/item/clothing/glasses/chameleon/changeling/verb/shred()
+ set name = "Shred Glasses"
+ set category = "Chameleon Items"
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ visible_message("[H] tears off [src]!",
+ "We remove [src].")
+ qdel(src)
+
+/obj/item/card/id/syndicate/changeling
+ name = "identification card"
+ desc = "An identification card issued to SolGov crewmembers aboard the SEV Torch."
+ icon_state = "changeling"
+ assignment = "Harvester"
+ origin_tech = list()
+ electronic_warfare = 1 //The lack of RFID stuff makes it hard for AIs to track, I guess. *handwaves*
+ registered_user = null
+ access = null
+ canremove = FALSE
+
+/obj/item/card/id/syndicate/changeling/Initialize()
+ . = ..()
+ if(ismob(loc))
+ registered_user = loc
+ access = null
+
+/obj/item/card/id/syndicate/changeling/verb/shred()
+ set name = "Shred ID Card"
+ set category = "Chameleon Items"
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ playsound(src, 'sound/effects/splat.ogg', 30, 1)
+ visible_message("[H] tears off [src]!",
+ "We remove [src].")
+ qdel(src)
+
+/obj/item/card/id/syndicate/changeling/Click() //Since we can't hold it in our hands, and attack_hand() doesn't work if it in inventory...
+ if(!registered_user)
+ registered_user = usr
+ usr.set_id_info(src)
+ ui_interact(registered_user)
+ ..()
+/obj/item/card/id/syndicate/changeling/dropped(mob/user)
+ if(!ismob(loc))
+ return
diff --git a/code/game/gamemodes/changeling/powers/fake_death.dm b/code/game/gamemodes/changeling/powers/fake_death.dm
new file mode 100644
index 0000000000000..4e433200e213d
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/fake_death.dm
@@ -0,0 +1,42 @@
+/datum/power/changeling/fakedeath
+ 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
+ verbpath = /mob/proc/changeling_fakedeath
+/mob/proc/finish_revive()
+ //The ling will now be able to choose when to revive
+ verbs.Add(/mob/proc/changeling_revive)
+ new /obj/changeling_revive_holder(src)
+ to_chat(src, "We are ready to rise. Use the Revive verb when you are ready.")
+//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay.
+/mob/proc/changeling_fakedeath()
+ set category = "Changeling"
+ set name = "Regenerative Stasis (20)"
+
+ var/datum/changeling/changeling = changeling_power(CHANGELING_STASIS_COST,1,100,DEAD)
+ if(!changeling)
+ return
+
+ var/mob/living/carbon/C = src
+
+ if(changeling.max_geneticpoints < 0) //Absorbed by another ling
+ to_chat(src, "We have no genomes, not even our own, and cannot regenerate.")
+ return 0
+
+ if(!C.stat && alert("Are we sure we wish to regenerate? We will appear to be dead while doing so.","Revival","Yes","No") == "No")
+ return
+ to_chat(C, "We will attempt to regenerate our form.")
+
+ C.UpdateLyingBuckledAndVerbStatus()
+ C.remove_changeling_powers()
+ C.status_flags |= FAKEDEATH
+ changeling.chem_charges -= CHANGELING_STASIS_COST
+
+ if(C.stat != DEAD)
+ C.adjustOxyLoss(C.maxHealth * 2)
+ addtimer(new Callback(src,/mob/.proc/finish_revive),rand(2 MINUTES, 4 MINUTES))
+
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/false_identity.dm b/code/game/gamemodes/changeling/powers/false_identity.dm
new file mode 100644
index 0000000000000..7c9c919626b90
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/false_identity.dm
@@ -0,0 +1,34 @@
+/datum/power/changeling/false_identity
+ name = "False Identity"
+ desc = "We twist our body like clay into an identity of our own making."
+ ability_icon_state = "ling_transform"
+ genomecost = 1
+ power_category = CHANGELING_POWER_INHERENT
+ verbpath = /mob/proc/changeling_false_identity
+
+/mob/proc/changeling_false_identity()
+ set category = "Changeling"
+ set name = "False Identity (10)"
+ set desc="Twists our body into a new false identity"
+ var/datum/changeling/changeling = changeling_power(10,0,100,CONSCIOUS)
+ var/mob/living/carbon/human/C = src
+ C.mind.changeling.last_human_form = C.real_name
+ C.change_appearance(APPEARANCE_BASIC, state = GLOB.z_state)
+ C.visible_message(SPAN_DANGER("\The [src] lets out a vile crunching noise, as their body begins to reshape itself!"))
+ playsound(C.loc, 'sound/effects/corpsecube.ogg', 60)
+ var/response = input(C, "What would you like to call your new self?", "Name change") as null | text
+ response = sanitize(response, MAX_NAME_LEN)
+ if (!response)
+ return
+ C.real_name = response
+ C.SetName(response)
+ C.dna.real_name = response
+ C.sync_organ_dna()
+ var/obj/item/organ/external/head/H = C.organs_by_name[BP_HEAD]
+ if (istype(H) && H.forehead_graffiti)
+ H.forehead_graffiti = null
+ if (C.mind)
+ C.mind.name = C.name
+ if(changeling)
+ changeling.chem_charges -= 10
+ //to_world("[C.mind.changeling.last_human_form]")
diff --git a/code/game/gamemodes/changeling/powers/fleshmend.dm b/code/game/gamemodes/changeling/powers/fleshmend.dm
new file mode 100644
index 0000000000000..62df05b940edc
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/fleshmend.dm
@@ -0,0 +1,48 @@
+/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
+/mob/proc/end_fleshmend()
+ to_chat(src, "Our regeneration has slowed to normal levels.")
+ src.verbs += /mob/proc/changeling_fleshmend
+ var/datum/changeling/changeling = src.mind.changeling
+ changeling.already_regenerating = FALSE
+//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."
+ set waitfor = FALSE
+ var/datum/changeling/changeling = changeling_power(10,0,100,UNCONSCIOUS)
+ if(!changeling)
+ return
+ var/mob/living/carbon/human/C = src
+ if(C.on_fire)
+ to_chat(src,SPAN_DANGER("We cannot regenerate while engulfed in flames!"))
+ return
+ if(changeling.already_regenerating)
+ to_chat(src,SPAN_DANGER("We are already regenerating our flesh."))
+ return
+ src.mind.changeling.chem_charges -= 10
+ var/heal_amount = 2
+ if(src.mind.changeling.recursive_enhancement)
+ src.mind.changeling.recursive_enhancement = FALSE
+ heal_amount = heal_amount * 2
+ to_chat(src, "We will heal much faster.")
+
+ to_chat(src, "We begin to heal ourselves.")
+ changeling.already_regenerating = TRUE
+ for(var/i = 0, i<50,i++)
+ if(C && !C.on_fire)
+ C.adjustBruteLoss(-heal_amount)
+ C.adjustOxyLoss(-heal_amount)
+ C.adjustFireLoss(-heal_amount)
+ C.regenerate_icons()
+ sleep(1 SECOND)
+
+ src.verbs -= /mob/proc/changeling_fleshmend
+ addtimer(new Callback(src,/mob/.proc/end_fleshmend), 50 SECONDS)
diff --git a/code/game/gamemodes/changeling/powers/hivemind.dm b/code/game/gamemodes/changeling/powers/hivemind.dm
new file mode 100644
index 0000000000000..1eff9067366fe
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/hivemind.dm
@@ -0,0 +1,77 @@
+// Hivemind
+
+/datum/power/changeling/hive_upload
+ name = "Hive Channel"
+ 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
+ verbpath = /mob/proc/changeling_hiveupload
+
+/datum/power/changeling/hive_download
+ name = "Hive Absorb"
+ 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
+ verbpath = /mob/proc/changeling_hivedownload
+
+// HIVE MIND UPLOAD/DOWNLOAD DNA
+GLOBAL_LIST_EMPTY(hivemind_bank)
+
+/mob/proc/changeling_hiveupload()
+ set category = "Changeling"
+ 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/list/names = list()
+ for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna)
+ if(!(DNA in GLOB.hivemind_bank))
+ names += DNA.name
+
+ if(!length(names))
+ to_chat(src, "The airwaves already have all of our DNA.")
+ return
+
+ var/S = input("Select a DNA to channel: ", "Channel DNA", null) as null|anything in names
+ if(!S) return
+
+ var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S)
+ if(!chosen_dna)
+ return
+
+ changeling.chem_charges -= 10
+ GLOB.hivemind_bank += chosen_dna
+ to_chat(src, "We channel the DNA of [S] to the air.")
+ 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/list/names = list()
+ for(var/datum/absorbed_dna/DNA in GLOB.hivemind_bank)
+ if(!(DNA in changeling.absorbed_dna))
+ names[DNA.name] = DNA
+
+ if(!length(names))
+ to_chat(src, "There's no new DNA to absorb from the air.")
+ return
+
+ var/S = input("Select a DNA absorb from the air: ", "Absorb DNA", null) as null|anything in names
+ if(!S) return
+ var/datum/absorbed_dna/chosen_dna = names[S]
+ if(!chosen_dna)
+ return
+
+ changeling.chem_charges -= 20
+ absorbDNA(chosen_dna)
+ to_chat(src, "We absorb the DNA of [S] from the air.")
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/lsd_sting.dm b/code/game/gamemodes/changeling/powers/lsd_sting.dm
new file mode 100644
index 0000000000000..fdd0235358d44
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/lsd_sting.dm
@@ -0,0 +1,26 @@
+//This only exists to be abused, so it's highly recommended to ensure this file is unchecked.
+/datum/power/changeling/LSDSting
+ 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."
+ genomecost = 1
+ verbpath = /mob/proc/changeling_lsdsting
+ sting_effect = /mob/proc/ling_lsd
+ is_sting = TRUE
+ sting_duration = 400
+/mob/proc/time_lsd(T)
+ if(T)
+ ling_lsd(T,400)
+/mob/proc/ling_lsd(mob/living/carbon/M, duration)
+ M.hallucination(duration,80)
+/mob/proc/changeling_lsdsting()
+ set category = "Changeling"
+ set name = "Hallucination Sting (15)"
+ set desc = "Causes terror in the target."
+
+ var/mob/living/carbon/T = changeling_sting(15,/mob/proc/changeling_lsdsting)
+ if(!T) return FALSE
+ admin_attack_log(src,T,"Hallucination sting (changeling)")
+ addtimer(new Callback(T,/mob/.proc/time_lsd), 400)
+
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/mimic_voice.dm b/code/game/gamemodes/changeling/powers/mimic_voice.dm
new file mode 100644
index 0000000000000..48c70fe87b1ff
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/mimic_voice.dm
@@ -0,0 +1,39 @@
+/datum/power/changeling/mimicvoice
+ name = "Mimic Voice"
+ desc = "We shape our vocal glands to sound like a desired voice."
+ helptext = "Will turn your voice into the name that you enter. We must constantly expend chemicals to maintain our form like this"
+ ability_icon_state = "ling_mimic_voice"
+ genomecost = 1
+ verbpath = /mob/proc/changeling_mimicvoice
+
+// Fake Voice
+
+/mob/proc/changeling_mimicvoice()
+ set category = "Changeling"
+ set name = "Mimic Voice"
+ set desc = "Shape our vocal glands to form a voice of someone we choose. We cannot regenerate chemicals when mimicing."
+ set waitfor = FALSE
+
+ var/datum/changeling/changeling = changeling_power()
+ if(!changeling) return
+
+ if(changeling.mimicing)
+ changeling.mimicing = ""
+ to_chat(src, "We return our vocal glands to their original location.")
+ return
+
+ var/mimic_voice = sanitize(input(usr, "Enter a name to mimic.", "Mimic Voice", null), MAX_NAME_LEN)
+ if(!mimic_voice)
+ return
+
+ changeling.mimicing = mimic_voice
+
+ to_chat(src, "We shape our glands to take the voice of [mimic_voice], this will stop us from regenerating chemicals while active.")
+ to_chat(src, "Use this power again to return to our original voice and reproduce chemicals again.")
+
+
+ while(src && src.mind && src.mind.changeling && src.mind.changeling.mimicing)
+ src.mind.changeling.chem_charges = max(src.mind.changeling.chem_charges - 1, 0)
+ sleep(40)
+ if(src && src.mind && src.mind.changeling)
+ src.mind.changeling.mimicing = ""
diff --git a/code/game/gamemodes/changeling/powers/panacea.dm b/code/game/gamemodes/changeling/powers/panacea.dm
new file mode 100644
index 0000000000000..eaf3959e45df9
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/panacea.dm
@@ -0,0 +1,51 @@
+/datum/power/changeling/panacea
+ name = "Anatomic Panacea"
+ desc = "Expels impurifications from our form; curing diseases, removing toxins, chemicals, radiation, and resetting our genetic code completely."
+ helptext = "Can be used while unconscious. This will also purge any reagents inside ourselves, both harmful and beneficial."
+ enhancedtext = "We heal more toxins."
+ ability_icon_state = "ling_anatomic_panacea"
+ genomecost = 1
+ verbpath = /mob/proc/changeling_panacea
+
+//Heals the things that the other regenerative abilities don't.
+/mob/proc/changeling_panacea()
+ set category = "Changeling"
+ set name = "Anatomic Panacea (20)"
+ set desc = "Clense ourselves of impurities."
+
+ var/datum/changeling/changeling = changeling_power(20,0,100,UNCONSCIOUS)
+ if(!changeling)
+ return FALSE
+ src.mind.changeling.chem_charges -= 20
+
+ to_chat(src, "We cleanse impurities from our form.")
+
+ var/mob/living/carbon/human/C = src
+
+ C.radiation = 0
+ C.sdisabilities = 0
+ C.disabilities = 0
+ C.reagents.clear_reagents()
+ C.empty_stomach()
+
+
+ var/heal_amount = 5
+ if(src.mind.changeling.recursive_enhancement)
+ src.mind.changeling.recursive_enhancement = FALSE
+ heal_amount = heal_amount * 2
+ to_chat(src, "We will heal much faster.")
+
+
+ for(var/obj/item/organ/external/E in C.organs)
+ var/obj/item/organ/external/G = E
+ if(G.germ_level)
+ var/germ_heal = heal_amount * 100
+ G.germ_level = min(0, G.germ_level - germ_heal)
+
+ for(var/obj/item/organ/internal/I in C.internal_organs)
+ var/obj/item/organ/internal/G = I
+ if(G.germ_level)
+ var/germ_heal = heal_amount * 100
+ G.germ_level = min(0, G.germ_level - germ_heal)
+
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/rapid_regen.dm b/code/game/gamemodes/changeling/powers/rapid_regen.dm
new file mode 100644
index 0000000000000..e9d7fe3b78136
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/rapid_regen.dm
@@ -0,0 +1,110 @@
+//had to make this because restore_all_organs just does a full heal, which we don't want.
+/obj/item/organ/external/proc/ling_organ_rejuvenate(ignore_prosthetic_prefs)
+
+ // handle internal organs. Heavily debating making this just fix some of the damage since a full heal might be much? idk
+ for(var/obj/item/organ/current_organ in internal_organs)
+ current_organ.rejuvenate(ignore_prosthetic_prefs)
+
+ // remove embedded objects and drop them on the floor
+ for(var/obj/implanted_object in implants)
+ if(!istype(implanted_object,/obj/item/implant,) || istype(implanted_object,/obj/item/organ/internal/augment)) // We don't want to remove REAL implants. Just shrapnel etc.
+ implanted_object.forceMove(get_turf(src))
+ implants -= implanted_object
+
+ if(owner && !ignore_prosthetic_prefs)
+ if(owner.client && owner.client.prefs && owner.client.prefs.real_name == owner.real_name)
+ var/status = owner.client.prefs.organ_data[organ_tag]
+ if(status == "amputated")
+ remove_rejuv()
+ owner.updatehealth()
+ if(status & ORGAN_ARTERY_CUT)
+ status &= ~ORGAN_ARTERY_CUT
+ if(status & ORGAN_TENDON_CUT)
+ status &= ~ORGAN_TENDON_CUT
+ if(status & ORGAN_BROKEN)
+ status &= ~ORGAN_BROKEN
+ stage = 0
+ if(!QDELETED(src) && species)
+ species.post_organ_rejuvenate(src, owner)
+/mob/living/carbon/human/proc/ling_heal_single_organ(ignore_prosthetic_prefs)
+ for(var/bodypart in BP_BY_DEPTH)
+ var/obj/item/organ/external/current_organ = organs_by_name[bodypart]
+ //checks if the limb has been twisted/hurt/affected and if it has, fixes it
+ if(istype(current_organ) && (current_organ.status != initial(current_organ.status) && !(BP_IS_ROBOTIC(current_organ))))
+ current_organ.ling_organ_rejuvenate(ignore_prosthetic_prefs)
+ return
+ verbs -= /mob/living/carbon/human/proc/undislocate
+/datum/power/changeling/rapid_regen
+ name = "Rapid Regeneration"
+ desc = "We quickly heal ourselves, removing most advanced injuries, at a high chemical cost. We cannot regenerate lost limbs however."
+ helptext = "We will begin to regenerate our flesh, healing ourselves of brute, burn, oxygen and brain damage, as well as mending internal organs, broken bones, \
+ and organ damage. The process is fast, but we must remain still and anyone who sees us will know us for what we are."
+ enhancedtext = "Healing increased to heal up to maximum health."
+ ability_icon_state = "ling_rapid_regeneration"
+ genomecost = 2
+ verbpath = /mob/proc/changeling_rapid_regen
+
+//Gives a big heal, removing various injuries that might shut down normal people, like IB or fractures.
+/mob/proc/changeling_rapid_regen()
+ set category = "Changeling"
+ set name = "Rapid Regeneration (30 per tick)"
+ set desc = "Heal ourselves of most injuries rapidly."
+ set waitfor = FALSE
+ var/finish_regen = TRUE
+ var/datum/changeling/changeling = changeling_power(30,0,100,UNCONSCIOUS)
+ if(!changeling)
+ return
+
+
+ if(ishuman(src))
+ var/mob/living/carbon/human/C = src
+ if(C.on_fire)
+ to_chat(src,SPAN_DANGER("We cannot regenerate while engulfed in flames!"))
+ return
+ if(changeling.already_regenerating)
+ to_chat(src,SPAN_DANGER("We are already regenerating our flesh."))
+ return
+ var/healing_amount = 40
+ if(src.mind.changeling.recursive_enhancement)
+ src.mind.changeling.recursive_enhancement = FALSE
+ healing_amount = 80
+ to_chat(src, SPAN_NOTICE("Our regeneration will be stronger."))
+ if(src.mind.changeling.chem_charges < 30)
+ to_chat(src,SPAN_WARNING("We require at least 10 chemicals to regenerate!"))
+ return
+ to_chat(src,SPAN_NOTICE("We begin to regenerate our form. We must remain still to complete the process."))
+ changeling.already_regenerating = TRUE
+ while((src.mind.changeling.chem_charges >= 30))
+
+ finish_regen = do_mob(C,C,5 SECONDS,ignore_movement = FALSE, incapacitation_affected = FALSE)
+
+ if(finish_regen)
+ C.adjustBruteLoss(-healing_amount)
+ C.adjustFireLoss(-healing_amount)
+ C.adjustOxyLoss(-healing_amount)
+ C.adjustToxLoss(-healing_amount)
+ C.adjustCloneLoss(-healing_amount)
+ C.adjustBrainLoss(-healing_amount)
+ C.regenerate_blood(healing_amount*2)
+ C.ling_heal_single_organ()
+ C.blinded = 0
+ C.eye_blind = 0
+ C.eye_blurry = 0
+ C.ear_deaf = 0
+ C.ear_damage = 0
+
+ // make the icons look correct
+ C.regenerate_icons()
+
+ // now make it obvious that we're not human (or whatever xeno race they are impersonating)
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ var/T = get_turf(src)
+ new /obj/gibspawner/human(T)
+
+ else
+ to_chat(src,SPAN_WARNING("Our regeneration is brought to a halt by us moving!"))
+ changeling.already_regenerating = FALSE
+ return
+
+ src.mind.changeling.chem_charges -= 30
+ changeling.already_regenerating = FALSE
diff --git a/code/game/gamemodes/changeling/powers/rapid_synthesis.dm b/code/game/gamemodes/changeling/powers/rapid_synthesis.dm
new file mode 100644
index 0000000000000..a707d07148cb6
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/rapid_synthesis.dm
@@ -0,0 +1,12 @@
+/datum/power/changeling/RapidSynthesis
+ name = "Rapid Chemical Synthesis"
+ desc = "We alter our chemical glands to optimize our production of internal chemicals."
+ helptext = "Allows us to store an extra 30 units of chemicals, and doubles production rate."
+ genomecost = 1
+ isVerb = 0
+ verbpath = /mob/proc/changeling_rapid_synthesis
+
+//Increases macimum chemical storage
+/mob/proc/changeling_rapid_synthesis()
+ src.mind.changeling.chem_recharge_rate *= 2
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/reattach_limb.dm b/code/game/gamemodes/changeling/powers/reattach_limb.dm
new file mode 100644
index 0000000000000..bb9116d1591d8
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/reattach_limb.dm
@@ -0,0 +1,68 @@
+//Change this to just regrowing it honestly
+/datum/power/changeling/reattach_limb
+ name = "Regrow Limb"
+ desc = "We reform a lost or damaged appendage."
+ helptext = "To select the body part to regrow, target the area on the health doll and click the ability."
+ ability_icon_state = "ling_sting_extract"
+ genomecost = 1
+ power_category = CHANGELING_POWER_INHERENT
+ allowduringlesserform = 1
+ verbpath = /mob/proc/changeling_reattach_limb
+
+/mob/proc/changeling_reattach_limb()
+ set category = "Changeling"
+ set name = "Regrow Limb (25)"
+ set desc="Regrow a limb that we have lost."
+
+ var/datum/changeling/changeling = changeling_power(25,0,100,CONSCIOUS)
+ if(src.mind && src.mind.changeling)
+ changeling = src.mind.changeling
+ if(!changeling || (src.mind.changeling.chem_charges < 25))
+ //to_chat(src,SPAN_WARNING("We require at least 25 chemicals to regenerate a limb!"))
+ return FALSE
+
+ var/mob/living/carbon/human/C = src
+ //zone_sel.selecting gets string
+ var/obj/item/organ/external/target_limb = C.zone_sel.selecting
+ var/list/organ_data = C.species.has_limbs[target_limb]
+ var/obj/item/organ/external/limb_path = organ_data["path"]
+ var/obj/item/organ/external/current_limb = C.get_organ(C.zone_sel.selecting)
+ if((current_limb.organ_tag == BP_HEAD) || (current_limb.organ_tag == BP_CHEST))
+ to_chat(C,SPAN_WARNING("That part of our body is too complex for us to simply regrow. We must enter hibernating stasis to regrow it."))
+ return FALSE
+ if (!current_limb || current_limb.is_stump())
+ var/obj/item/organ/external/regrown_limb = new limb_path(C)
+ var/obj/item/organ/external/parent_limb = (C.get_organ(regrown_limb.parent_organ))
+ if(!parent_limb || parent_limb.is_stump())
+ to_chat(C,SPAN_WARNING("We cannot regrow this limb without regrowing the limb it attaches to first!"))
+ qdel(regrown_limb)
+ return
+ if(current_limb)
+ qdel(current_limb)
+ regrown_limb.replaced(C)
+ regrown_limb.forceMove(C)
+ regrown_limb = null
+ C.update_body()
+ C.updatehealth()
+ C.UpdateDamageIcon()
+ C.visible_message(SPAN_DANGER("\The [C] regenerates the tissue of their limb with a sickening squelch!"))
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ changeling.chem_charges -= 25
+ else
+ current_limb.rejuvenate(ignore_prosthetic_prefs = FALSE)
+ C.update_body()
+ C.updatehealth()
+ C.UpdateDamageIcon()
+ C.visible_message(SPAN_DANGER("\The [C] regenerates the tissue of their limb with a sickening squelch!"))
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ changeling.chem_charges -= 25
+
+
+
+ if(!C)
+ return
+
+
+
+
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/recursive_enhancement.dm b/code/game/gamemodes/changeling/powers/recursive_enhancement.dm
new file mode 100644
index 0000000000000..de7f241a8fd4a
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/recursive_enhancement.dm
@@ -0,0 +1,24 @@
+/datum/power/changeling/recursive_enhancement
+ name = "Recursive Enhancement"
+ desc = "We cause our abilities to have increased or additional effects."
+ helptext = "To check the effects for each ability, check the blue text underneath the ability in the evolution menu."
+ ability_icon_state = "ling_recursive_enhancement"
+ genomecost = 3
+ verbpath = /mob/proc/changeling_recursive_enhancement
+
+//Increases macimum chemical storage
+/mob/proc/changeling_recursive_enhancement()
+ set category = "Changeling"
+ set name = "Recursive Enhancement"
+ set desc = "Empowers our abilities."
+ var/datum/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS)
+ if(!changeling)
+ return FALSE
+ if(src.mind.changeling.recursive_enhancement)
+ to_chat(src, "We will no longer empower our abilities.")
+ src.mind.changeling.recursive_enhancement = 0
+ return FALSE
+ to_chat(src, "We empower ourselves. Our next ability will now be extra potent.")
+ src.mind.changeling.recursive_enhancement = 1
+ changeling.chem_charges -= 10
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/reinforced_tendons.dm b/code/game/gamemodes/changeling/powers/reinforced_tendons.dm
new file mode 100644
index 0000000000000..b9672c1e2de1c
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/reinforced_tendons.dm
@@ -0,0 +1,18 @@
+/datum/power/changeling/reinforced_tendons
+ name = "Reinforced Tendons"
+ desc = "We reinforce our tendons to lunch"
+ helptext = "We will be also be able to fall great distances without damage. If we leap at a target with both arms shaped into weapons, we will perform a special pounce attack."
+ genomecost = 2
+ isVerb = 0
+ verbpath = /mob/proc/reinforced_tendons
+/mob/proc/reinforced_tendons()
+
+ var/mob/living/carbon/human/C = src
+ //C.available_maneuvers.Remove(/singleton/maneuver/leap)
+ C.available_maneuvers.Add(/singleton/maneuver/leap/changeling)
+ src.mind.changeling.tendons_reinforced = TRUE
+ C.buff_skill(list(SKILL_HAULING = 1), 0, buff_type = /datum/skill_buff)
+ //C.prepared_maneuver = /singleton/maneuver/leap/grab
+
+ playsound(src, 'sound/effects/corpsecube.ogg',35,1)
+ to_chat(src, SPAN_NOTICE("Our leg muscles twist and reknit themselves, forming stronger muscles that can both leap further and fall from great heights!"))
diff --git a/code/game/gamemodes/changeling/powers/revive.dm b/code/game/gamemodes/changeling/powers/revive.dm
new file mode 100644
index 0000000000000..d2126021d14bb
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/revive.dm
@@ -0,0 +1,47 @@
+//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)
+ if(!changeling)
+ return 0
+
+ if(changeling.max_geneticpoints < 0) //Absorbed by another ling
+ to_chat(src, "You have no genomes, not even your own, and cannot revive.")
+ return 0
+ var/mob/living/carbon/C = src
+ // restore us to health
+ C.revive()
+ // remove our fake death flag
+ C.status_flags &= ~(FAKEDEATH)
+ // let us move again
+ C.UpdateLyingBuckledAndVerbStatus()
+ // re-add out changeling powers
+ C.make_changeling()
+ // sending display messages
+ to_chat(C, SPAN_NOTICE("We have regenerated."))
+ C.verbs -= /mob/proc/changeling_revive
+
+ return TRUE
+
+//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)
diff --git a/code/game/gamemodes/changeling/powers/self_respiration.dm b/code/game/gamemodes/changeling/powers/self_respiration.dm
new file mode 100644
index 0000000000000..3a0a2312b5080
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/self_respiration.dm
@@ -0,0 +1,34 @@
+/datum/power/changeling/self_respiration
+ name = "Self Respiration"
+ desc = "We evolve our body to no longer require drawing oxygen from the atmosphere."
+ helptext = "We will no longer require internals, and we cannot inhale any gas, including harmful ones."
+ ability_icon_state = "ling_toggle_breath"
+ genomecost = 0
+ power_category = CHANGELING_POWER_INHERENT
+ verbpath = /mob/proc/changeling_self_respiration
+
+//No breathing required
+/mob/proc/changeling_self_respiration()
+ set category = "Changeling"
+ set name = "Activate Internal Air Sacs"
+ set desc = "We choose whether or not to breathe."
+ var/mnoBreath = 100
+
+ var/datum/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS)
+ if(!changeling)
+ return FALSE
+
+ if(istype(src,/mob/living/carbon))
+ var/mob/living/carbon/C = src
+ if (mnoBreath in C.mutations)
+ //C.mutations |= mNobreath
+ C.mutations.Remove(mnoBreath)
+ to_chat(src,SPAN_NOTICE("We resume breathing, to avoid suspicion."))
+ return FALSE
+ else
+ C.mutations.Add(mnoBreath)
+ to_chat(src, "We stop breathing, as we no longer need to.")
+ return TRUE
+ //else
+ //C.does_not_breathe = 0
+ //to_chat(src, "We resume breathing, as we now need to again.")
diff --git a/code/game/gamemodes/changeling/powers/shriek.dm b/code/game/gamemodes/changeling/powers/shriek.dm
new file mode 100644
index 0000000000000..dfa48a2183e81
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/shriek.dm
@@ -0,0 +1,152 @@
+/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."
+ helptext = "Lights are blown, organics are disoriented, and synthetics act as if they were flashed."
+ enhancedtext = "Range is doubled."
+ ability_icon_state = "ling_resonant_shriek"
+ genomecost = 2
+ verbpath = /mob/proc/changeling_resonant_shriek
+
+/datum/power/changeling/dissonant_shriek
+ name = "Dissonant Shriek"
+ desc = "We shift our vocal cords to release a high-frequency sound that overloads nearby radios"
+ helptext = "Radios within a radius of us are shut off."
+ enhancedtext = "Range is doubled."
+ ability_icon_state = "ling_dissonant_shriek"
+ genomecost = 2
+ verbpath = /mob/proc/changeling_dissonant_shriek
+
+//A flashy ability, good for crowd control and sewing chaos.
+/mob/proc/changeling_resonant_shriek()
+ set category = "Changeling"
+ set name = "Resonant Shriek (15)"
+ set desc = "Emits a high-frequency sound that deafens organics, blows out nearby lights, and overloads synthetics' sensors."
+
+ var/mob/living/carbon/human/H = src
+
+ var/datum/changeling/changeling = changeling_power(15,0,100,CONSCIOUS)
+ if(!changeling) return 0
+
+ if(is_muzzled())
+ to_chat(src, "Mmmf mrrfff!")
+ return 0
+
+ if(ishuman(src))
+ if(H.silent)
+ to_chat(src, "You can't speak!")
+ return 0
+
+ if(world.time < (changeling.last_shriek + 10 SECONDS) )
+ to_chat(src, "We are still recovering from our last shriek...")
+ return 0
+
+ if(!isturf(loc))
+ to_chat(src, "Shrieking here would be a bad idea.")
+ return 0
+
+ H.remove_cloaking_source(src) //No more invisible shrieking
+
+ changeling.chem_charges -= 15
+ var/range = 4
+ if(src.mind.changeling.recursive_enhancement)
+ src.mind.changeling.recursive_enhancement = FALSE
+ range = range * 2
+ to_chat(src, "We are extra loud.")
+
+ visible_message("[src] appears to shout.")
+ playsound(src, 'sound/effects/ling_shriek.ogg',100,1)
+ var/list/affected = list()
+ for(var/mob/living/M in range(range, src))
+ if(iscarbon(M))
+ if(!M.mind || !M.mind.changeling)
+ if(M.get_sound_volume_multiplier() < 0.2)
+ continue
+ to_chat(M, "You hear an extremely loud screeching sound! It \
+ [pick("confuses","confounds","perturbs","befuddles","dazes","unsettles","disorients")] you.")
+ M.adjustEarDamage(0,30)
+ affected += M
+ else
+ if(M != src)
+ to_chat(M, "You hear a familiar screech from nearby. It has no effect on you.")
+
+ if(issilicon(M))
+ to_chat(M, "Auditory input overloaded. Reinitializing...")
+ M.Weaken(rand(5,10))
+ affected += M
+
+ for(var/obj/machinery/light/L in range(range, src))
+ L.on = 1
+ L.broken()
+ for(var/obj/item/device/flashlight/F in range(range, src))
+ //add code for flashlights that cannot be turned off here
+ if (F.on)
+ F.on = FALSE
+ F.set_light(0)
+ F.update_icon()
+ changeling.last_shriek = world.time
+
+ admin_attack_log(src,affected,"Used resonant shriek")
+ return TRUE
+
+//EMP version
+/mob/proc/changeling_dissonant_shriek()
+ set category = "Changeling"
+ set name = "Dissonant Shriek (15)"
+ set desc = "We shift our vocal cords to release a high-frequency sound that overloads nearby radios."
+
+ var/mob/living/carbon/human/H = src
+
+ var/datum/changeling/changeling = changeling_power(15,0,100,CONSCIOUS)
+ if(!changeling)
+ return FALSE
+
+ if(is_muzzled())
+ to_chat(src, "Mmmf mrrfff!")
+ return FALSE
+
+ if(ishuman(src))
+ if(H.silent)
+ to_chat(src, "You can't speak!")
+ return FALSE
+
+ if(world.time < (changeling.last_shriek + 10 SECONDS) )
+ to_chat(src, "We are still recovering from our last shriek...")
+ return FALSE
+
+ if(!isturf(loc))
+ to_chat(src, "Shrieking here would be a bad idea.")
+ return FALSE
+
+ H.remove_cloaking_source(src) //No more invisible shrieking
+
+ changeling.chem_charges -= 15
+
+ var/range_heavy = 1
+ var/range_med = 2
+ var/range_light = 4
+ var/range_long = 6
+ if(src.mind.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, "We are extra loud.")
+ src.mind.changeling.recursive_enhancement = FALSE
+
+ visible_message("[src] appears to shout.")
+ playsound(src, 'sound/effects/ling_shriek2.ogg',100,1)
+ admin_attack_log(src,null,"Use dissonant shriek")
+
+
+ for(var/obj/item/device/radio/R in range(range_long,src))
+ R.broadcasting = FALSE
+ R.listening = FALSE
+ for (var/ch_name in R.channels)
+ R.channels[ch_name] = FALSE
+ if(R.cell)
+ R.cell.emp_act(2)
+
+
+ changeling.last_shriek = world.time
+
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/silence_sting.dm b/code/game/gamemodes/changeling/powers/silence_sting.dm
new file mode 100644
index 0000000000000..c570902e5b322
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/silence_sting.dm
@@ -0,0 +1,29 @@
+/datum/power/changeling/silence_sting
+ name = "Silence Sting"
+ desc = "We silently sting a human, completely silencing them for a short time."
+ helptext = "Does not provide a warning to a victim that they have been stung, until they try to speak and cannot."
+ enhancedtext = "Silence duration is extended."
+ ability_icon_state = "ling_sting_mute"
+ genomecost = 2
+ allowduringlesserform = 1
+ verbpath = /mob/proc/changeling_silence_sting
+ sting_effect = /mob/proc/silence
+ is_sting = TRUE
+ sting_duration = 30
+/mob/proc/silence(mob/living/carbon/M, duration)
+ M.silent += duration
+/mob/proc/changeling_silence_sting()
+ set category = "Changeling"
+ set name = "Silence sting (10)"
+ set desc="Sting target"
+
+ var/mob/living/carbon/T = changeling_sting(10,/mob/proc/changeling_silence_sting)
+ if(!T) return 0
+ admin_attack_log(src,T,"Silence sting (changeling)")
+ var/duration = 30
+ if(src.mind.changeling.recursive_enhancement)
+ src.mind.changeling.recursive_enhancement = FALSE
+ duration = duration + 10
+ to_chat(src, "They will be unable to cry out in fear for a little longer.")
+ silence(T,duration)
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/synaptizine_overdose.dm b/code/game/gamemodes/changeling/powers/synaptizine_overdose.dm
new file mode 100644
index 0000000000000..8392e7f397db7
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/synaptizine_overdose.dm
@@ -0,0 +1,43 @@
+/datum/power/changeling/epinephrine_overdose
+ name = "Adrenaline Surge"
+ desc = "We evolve additional sacs of adrenaline throughout our body."
+ helptext = "We can instantly recover from stuns and reduce the effect of future stuns, but we will suffer toxicity in the long term. Can be used while unconscious."
+ enhancedtext = "Immunity from most disabling effects for 30 seconds."
+ ability_icon_state = "ling_epinephrine_overdose"
+ genomecost = 2
+ verbpath = /mob/proc/changeling_synaptizine_overdose
+
+//Recover from stuns.
+/mob/proc/changeling_synaptizine_overdose()
+ set category = "Changeling"
+ set name = "Adrenaline Surge (20)"
+ set desc = "Removes all stuns instantly, and reduces future stuns."
+
+ var/datum/changeling/changeling = changeling_power(20,0,100,UNCONSCIOUS)
+ if(!changeling)
+ return FALSE
+ changeling.chem_charges -= 20
+
+ var/mob/living/carbon/human/C = src
+ to_chat(C, "Energy rushes through us. [C.lying ? "We arise." : ""]")
+ C.set_stat(CONSCIOUS)
+ C.SetParalysis(0)
+ C.SetStunned(0)
+ C.SetWeakened(0)
+ C.setHalLoss(0)
+ C.lying = 0
+ C.blinded = 0
+ C.eye_blind = 0
+ C.eye_blurry = 0
+ C.ear_deaf = 0
+ C.ear_damage = 0
+ C.clear_confused()
+ C.sleeping = 0
+// C.reagents.add_reagent("toxin", 10)
+ C.reagents.add_reagent("synaptizine", 10)
+
+ if(src.mind.changeling.recursive_enhancement)
+ src.mind.changeling.recursive_enhancement = FALSE
+
+
+ return TRUE
diff --git a/code/game/gamemodes/changeling/powers/tentacle.dm b/code/game/gamemodes/changeling/powers/tentacle.dm
new file mode 100644
index 0000000000000..a713ef335ea80
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/tentacle.dm
@@ -0,0 +1,103 @@
+/proc/get_steps_with_obstacles(turf/Turf1,turf/Turf2)
+ var/list/steps = list()
+ while(Turf1 != Turf2)
+ var/turf/next_tile = get_step_towards(Turf1,Turf2)
+ steps.Add(next_tile)
+ Turf1 = next_tile
+ return steps
+/proc/find_tentacle_hit(list/tentacle_obstacles)
+ for(var/turf/check_turf in tentacle_obstacles)
+ if(check_turf.density)
+ return check_turf
+ for(var/obj/item in check_turf)
+ if(!item.throwpass && item.density)
+ return item
+//commented out until sprite gets added
+/*
+/datum/power/changeling/tentacle
+ name = "Tentacle"
+ desc = "We reform our arm into a tentacle capable of grabbing objects or victims at a distance. Victims grabbed by the tentacle will have their stamina drained, and struggle to run away."
+ helptext = "Items grabbed by the tentacle will be pulled directly into our hand."
+ enhancedtext = "The range is extended to five tiles."
+ ability_icon_state = "ling_sting_boost_range"
+ genomecost = 2
+ allowduringlesserform = 1
+ verbpath = /mob/proc/changeling_tentacle
+*/
+/obj/item/tentacle
+ name = "tentacle"
+ desc = "A quivering tentacle of flesh that shudders with anticipation."
+ icon = 'icons/obj/guns/confuseray.dmi'
+ icon_state = "confuseray"
+ canremove = FALSE
+ throw_speed = 0
+ throwforce = 0
+ throw_range = 0
+ var/default_range = 5
+ var/last_tentacle = null
+ var/cooldown = 4 SECONDS
+/obj/item/tentacle/enhanced
+ default_range = 7
+/obj/ebeam/ling
+ pass_flags = PASS_FLAG_TABLE
+
+/obj/item/tentacle/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ if ((last_tentacle + cooldown > world.time))
+ to_chat(user, SPAN_WARNING("We are still recovering from our last tentacle attack."))
+ return
+ if(get_dist(user, target) >= default_range)
+ to_chat(user,SPAN_WARNING("Our tentacle cannot extend to that distance!"))
+ return
+ var/list/tentacle_obstacles = get_steps_with_obstacles(get_turf(user),get_turf(target))
+ if(find_tentacle_hit(tentacle_obstacles))
+ target = find_tentacle_hit(tentacle_obstacles)
+ user.Beam(BeamTarget = target, icon_state = "r_beam", time = 3, maxdistance = INFINITY, beam_type = /obj/ebeam/ling)
+
+ user.visible_message(SPAN_DANGER("\the [user] lashes at \the [target] with their tentacle!"))
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ last_tentacle = world.time
+ if(isobj(target))
+ var/obj/grabbed_object = target
+ if(grabbed_object.anchored)
+ to_chat(user,SPAN_WARNING("\the [grabbed_object] is too secured in place for our tentacle to get a good grip!"))
+ return FALSE
+ user.throw_mode_on()
+ grabbed_object.throw_at(user, 5,3)
+ qdel(src)
+ if(ismob(target))
+ var/mob/victim = target
+ var/turf/tile = get_step_towards(user,victim)
+ victim.throw_at(tile, 5,1)
+ if(victim.get_active_hand())
+ victim.unequip_item()
+ if(istype(victim,/mob/living/carbon/human))
+ var/mob/living/carbon/human/V = victim
+ V.stamina = 0
+ return TRUE
+/mob/proc/changeling_tentacle()
+ set category = "Changeling"
+ set name = "Tentacle (20)"
+ set desc="We transform one of our arms into a tentacle that can pull distant targets towards us."
+
+ var/datum/changeling/changeling = changeling_power(10,0,100)
+ if(!changeling)
+ return TRUE
+
+ var/holding = src.get_active_hand()
+ if (istype(holding, /obj/item/tentacle))
+ to_chat(src,SPAN_WARNING("We reform our arm from a tentacle back into its normal form."))
+ playsound(src, 'sound/effects/blobattack.ogg', 30, 1)
+ qdel(holding)
+ return 0
+
+ changeling.chem_charges -= 10
+
+ if(src.mind.changeling.recursive_enhancement)
+ if(changeling_generic_weapon(/obj/item/tentacle/enhanced, 10))
+ src.mind.changeling.recursive_enhancement = FALSE
+ return 1
+
+ else
+ if(changeling_generic_weapon(/obj/item/tentacle, 10))
+ return 1
+ return 0
diff --git a/code/game/gamemodes/changeling/powers/transform.dm b/code/game/gamemodes/changeling/powers/transform.dm
new file mode 100644
index 0000000000000..08105e63f6f93
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/transform.dm
@@ -0,0 +1,69 @@
+/datum/power/changeling/transform
+ name = "Transform"
+ desc = "We take on the appearance and voice of one we have absorbed."
+ ability_icon_state = "ling_transform"
+ genomecost = 0
+ power_category = CHANGELING_POWER_INHERENT
+ verbpath = /mob/proc/changeling_transform
+/mob/proc/transform_cooldown()
+ src.verbs += /mob/proc/changeling_transform
+ src.regenerate_icons()
+ src.update_hud()
+//Change our DNA to that of somebody we've absorbed.
+/mob/proc/changeling_transform()
+ set category = "Changeling"
+ set name = "Transform (5)"
+
+ var/datum/changeling/changeling = changeling_power(5,1,0)
+ if(!changeling) return
+
+ if(!isturf(loc) || !(/mob/proc/changeling_transform in src.verbs))
+ to_chat(src, "Transforming here would be a bad idea.")
+ return 0
+
+ var/list/names = list()
+ for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna)
+ names += "[DNA.name]"
+
+ var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
+ if(!S) return
+
+ var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S)
+ if(!chosen_dna)
+ return
+
+ changeling.chem_charges -= 5
+ src.visible_message("[src] transforms!")
+ changeling.geneticdamage = 5
+
+ if(ishuman(src))
+ var/mob/living/carbon/human/H = src
+ var/newSpecies = chosen_dna.speciesName
+ H.set_species(newSpecies,1)
+ H.ability_master.toggle_open(forced_state = 2)
+ for(var/obj/item/organ/internal/augment/ling_organ in H.mind.changeling.purchased_organs)
+ var/obj/item/organ/external/parent = H.get_organ(BP_CHEST)
+ ling_organ.forceMove(src)
+ ling_organ.replaced(src, parent)
+ ling_organ = null
+ 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.dna = chosen_dna.dna.Clone()
+ H.b_type = "AB+" //For some reason we have two blood types on the mob.
+ H.gender = chosen_dna.gender
+ H.pronouns = chosen_dna.pronouns
+ H.flavor_texts = chosen_dna.flavour_texts ? chosen_dna.flavour_texts.Copy() : null
+ if(H.descriptors)
+ H.descriptors = chosen_dna.descriptors.Copy()
+ H.sync_organ_dna()
+ H.icon_render_keys = chosen_dna.icon_render_details
+ src.real_name = chosen_dna.name
+ domutcheck(src, null)
+
+ src.UpdateAppearance()
+
+ changeling_update_languages(changeling.absorbed_languages)
+
+ src.verbs -= /mob/proc/changeling_transform
+ addtimer(new Callback(src,/mob/.proc/transform_cooldown), 10 SECONDS)
diff --git a/code/game/gamemodes/changeling/powers/visible_camouflage.dm b/code/game/gamemodes/changeling/powers/visible_camouflage.dm
new file mode 100644
index 0000000000000..e6ed45b873599
--- /dev/null
+++ b/code/game/gamemodes/changeling/powers/visible_camouflage.dm
@@ -0,0 +1,75 @@
+/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(istype(src,/mob/living/carbon/human))
+ var/mob/living/carbon/human/H = src
+
+ if(H.mind.changeling.cloaked)
+ H.mind.changeling.cloaked = 0
+ return TRUE
+
+ //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 FALSE
+ changeling.chem_charges -= 10
+ var/old_regen_rate = H.mind.changeling.chem_recharge_rate
+
+ to_chat(H, "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)
+ src.mind.changeling.recursive_enhancement = FALSE
+ must_walk = FALSE
+ to_chat(src, "We may move at our normal speed while hidden.")
+
+ if(must_walk)
+ H.set_move_intent(default_walk_intent)
+
+ 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(MOVING_QUICKLY(H) && 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
+ //using or equipping an item will uncloak you
+ if(H.l_hand || H.r_hand)
+ 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("[src] suddenly fades in, seemingly from nowhere!",
+ "We revert our camouflage, revealing ourselves.")
+ H.mind.changeling.cloaked = 0
+ H.mind.changeling.chem_recharge_rate = old_regen_rate
+
+ animate(src,alpha = 10, alpha = 255, time = 10)
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index d0663f1930023..079a88e8d5039 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -294,7 +294,13 @@
icon_state = "greenshatter"
anchored = TRUE
layer = BLOOD_LAYER
-
+/obj/decal/cleanable/ling_vomit
+ name = "green goop"
+ desc = "Green squishy mess."
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "greenshatter"
+ anchored = TRUE
+ layer = BLOOD_LAYER
/obj/spider/cocoon
name = "cocoon"
desc = "Something wrapped in silky spider web."
diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm
index d9a2528cc320b..c1dff74f87270 100644
--- a/code/game/objects/structures/railing.dm
+++ b/code/game/objects/structures/railing.dm
@@ -360,9 +360,13 @@
/obj/structure/railing/hitby(atom/movable/AM, datum/thrownthing/TT)
var/mob/living/L = AM
+ var/chance
if (!istype(L))
return
- var/chance = TT.thrower.skill_check(SKILL_HAULING, SKILL_EXPERIENCED) ? 100 : 50
+ if (istype(TT.thrower, L))
+ chance = TT.thrower.skill_check(SKILL_HAULING, SKILL_EXPERIENCED) ? 100 : 50
+ else
+ chance = 50
if (prob(chance))
slam_into(L)
diff --git a/code/modules/augment/passive/armor.dm b/code/modules/augment/passive/armor.dm
index d8695936dd7cb..1206d8f873923 100644
--- a/code/modules/augment/passive/armor.dm
+++ b/code/modules/augment/passive/armor.dm
@@ -6,3 +6,13 @@
augment_flags = AUGMENT_MECHANICAL | AUGMENT_BIOLOGICAL | AUGMENT_SCANNABLE | AUGMENT_INSPECTABLE
var/brute_mult = 0.8
var/burn_mult = 1
+/obj/item/organ/internal/augment/armor/changeling
+ name = "organic armor"
+ icon_state = "endoarmor"
+ augment_flags = AUGMENT_BIOLOGICAL
+ status = ORGAN_CONFIGURE
+ desc = "A collection of bone, chitin and tissue carefully wrapped around the chest cavity."
+ brute_mult = 0.75
+/obj/item/organ/internal/augment/armor/changeling/emp_act()
+ SHOULD_CALL_PARENT(FALSE)
+ return
\ No newline at end of file
diff --git a/code/modules/augment/passive/ling_lenses.dm b/code/modules/augment/passive/ling_lenses.dm
new file mode 100644
index 0000000000000..1e3ec15e96852
--- /dev/null
+++ b/code/modules/augment/passive/ling_lenses.dm
@@ -0,0 +1,19 @@
+/obj/item/organ/internal/augment/ling_lenses
+ name = "organic eyelid lense"
+ desc = "a thin, almost transparent lense surrounded by nerve fibers"
+ icon_state = "thermal_eyes"
+ status = ORGAN_CONFIGURE
+ augment_slots = AUGMENT_HEAD
+ augment_flags = AUGMENT_BIOLOGICAL
+ var/is_active = FALSE
+/obj/item/organ/internal/augment/ling_lenses/emp_act()
+ ..()
+ return
+/obj/item/organ/internal/augment/ling_lenses/Process()
+ ..()
+ if(is_active && (owner.mind.changeling.chem_charges > 0))
+ owner.set_sight(owner.sight | SEE_MOBS)
+ owner.mind.changeling.chem_charges -= 3
+ if(owner.mind.changeling.chem_charges < 3)
+ to_chat(owner,SPAN_NOTICE("Our lenses retract, causing us to lose our augmented vision."))
+ is_active = FALSE
diff --git a/code/modules/augment/passive/lingcore.dm b/code/modules/augment/passive/lingcore.dm
new file mode 100644
index 0000000000000..eec814725f3a5
--- /dev/null
+++ b/code/modules/augment/passive/lingcore.dm
@@ -0,0 +1,57 @@
+/obj/item/organ/internal/augment/lingcore
+ name = "bizarre mass"
+ desc = "a twitching, pulsating mass that almost resembles a deformed embryo"
+ icon_state = "lingcore"
+ w_class = ITEM_SIZE_TINY
+ augment_slots = AUGMENT_CHEST
+ status = ORGAN_CONFIGURE
+ augment_flags = AUGMENT_BIOLOGICAL
+ var/ticks_remaining = 0;
+ var/datum/mind/backup
+ var/ownerckey
+ var/default_language
+ var/list/languages = list()
+
+/obj/item/organ/internal/augment/lingcore/emp_act()
+ ..()
+ return
+/obj/item/organ/internal/augment/lingcore/getToxLoss()
+ return 0
+/obj/item/organ/internal/augment/lingcore/Process()
+ ..()
+ if(istype(owner,/mob/living/carbon/human))
+ var/mob/living/carbon/human/C = owner
+ var/obj/item/organ/internal/brain/B = C.internal_organs_by_name[BP_BRAIN]
+ if(B.germ_level != 0)
+ B.germ_level = 0
+/obj/item/organ/internal/augment/lingcore/onInstall()
+ ..()
+ if(istype(owner,/mob/living/carbon) && (owner.mind) && (!owner.mind.changeling))
+ overwrite()
+ owner.make_changeling()
+ if(owner.mind && owner.mind.changeling)
+ do_backup()
+/obj/item/organ/internal/augment/lingcore/onRemove()
+ ..()
+ if(istype(owner,/mob/living/carbon) )
+ owner.death(0)
+ owner.Drain()
+/obj/item/organ/internal/augment/lingcore/proc/do_backup()
+ if(owner && owner.stat != DEAD && !is_broken() && owner.mind)
+ languages = owner.languages.Copy()
+ backup = owner.mind
+ default_language = owner.default_language
+ if(owner.ckey)
+ ownerckey = owner.ckey
+/obj/item/organ/internal/augment/lingcore/proc/overwrite()
+ if(owner.mind && owner.ckey) //Someone is already in this body!
+ if(owner.mind == backup) // Oh, it's the same mind in the backup. Someone must've spammed the 'Start Procedure' button in a panic.
+ return
+ owner.visible_message(SPAN_DANGER("\The [owner] spasms and seizes!"))
+ owner.ghostize()
+ backup.active = 1
+ backup.transfer_to(owner)
+ if (default_language)
+ owner.default_language = default_language
+ owner.languages = languages.Copy()
+ to_chat(owner, SPAN_NOTICE("Our tendrils extend throughout our new form, wrapping around nerve filaments. We awaken."))
diff --git a/code/modules/augment/passive/ragecore.dm b/code/modules/augment/passive/ragecore.dm
new file mode 100644
index 0000000000000..333bf1d4d33f4
--- /dev/null
+++ b/code/modules/augment/passive/ragecore.dm
@@ -0,0 +1,29 @@
+/obj/item/organ/internal/augment/changeling/ragecore
+ name = "strange mass"
+ desc = "a throbbing, writhing sack of meat that almost seems to beat like a living heart"
+ icon_state = "berserk_sac"
+ augment_slots = AUGMENT_GROIN
+ status = ORGAN_CONFIGURE
+ augment_flags = AUGMENT_BIOLOGICAL
+ var/ticks_remaining = 0;
+/obj/item/organ/internal/augment/changeling/ragecore/emp_act()
+ ..()
+ return
+/obj/item/organ/internal/augment/changeling/ragecore/Process()
+ ..()
+ if (ticks_remaining > 0)
+ owner.add_chemical_effect(CE_PAINKILLER, 160) // About twice as strong as tramadol at full strength
+ owner.add_chemical_effect(CE_SPEEDBOOST, 1)
+ owner.add_chemical_effect(CE_STIMULANT, 4)
+ ticks_remaining -= 1
+ if (ticks_remaining == 15)
+ to_chat(owner, SPAN_WARNING("We feel our adrenal sacs beginning to exhaust themselves. If we do not retreat soon, we will be left vulnerable."))
+ if (!ticks_remaining) // ...but comes at a price. Brief short term benefit for a long-term comedown
+ to_chat(owner, SPAN_WARNING("We feel our body grow weak and sluggish, our adrenal sacs depleted."))
+ owner.drowsyness = max(owner.drowsyness, 15)
+ owner.set_confused(15)
+ owner.slurring = max(owner.slurring, 30)
+ owner.chem_effects[CE_PAINKILLER] = 0
+ owner.stamina = 0
+ if(MOVING_QUICKLY(owner))
+ owner.set_moving_slowly()
diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm
index 474811ab4261b..600fa4c9db4bc 100644
--- a/code/modules/clothing/chameleon.dm
+++ b/code/modules/clothing/chameleon.dm
@@ -7,6 +7,10 @@
origin_tech = list(TECH_ESOTERIC = 3)
item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM
+/obj/item/clothing/under/chameleon/Initialize()
+ . = ..()
+ set_extension(src,/datum/extension/chameleon/clothing/under, /obj/item/clothing/under)
+
/obj/item/clothing/head/chameleon
name = "cap"
icon_state = "greysoft"
@@ -15,6 +19,11 @@
body_parts_covered = 0
item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM
+/obj/item/clothing/head/chameleon/Initialize()
+ . = ..()
+
+ set_extension(src, /datum/extension/chameleon/clothing/head,/obj/item/clothing/head)
+
/obj/item/clothing/suit/chameleon
name = "armor"
icon_state = "armor"
@@ -23,6 +32,10 @@
origin_tech = list(TECH_ESOTERIC = 3)
item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM
+/obj/item/clothing/suit/chameleon/Initialize()
+ . = ..()
+ set_extension(src, /datum/extension/chameleon/clothing/suit,/obj/item/clothing/suit)
+
/obj/item/clothing/shoes/chameleon
name = "shoes"
icon_state = "black"
@@ -31,6 +44,10 @@
origin_tech = list(TECH_ESOTERIC = 3)
item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM
+/obj/item/clothing/shoes/chameleon/Initialize()
+ . = ..()
+ set_extension(src, /datum/extension/chameleon/clothing/shoes, /obj/item/clothing/shoes)
+
/obj/item/storage/backpack/chameleon
name = "backpack"
icon_state = "backpack"
@@ -39,6 +56,7 @@
origin_tech = list(TECH_ESOTERIC = 3)
item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM
+
/obj/item/clothing/gloves/chameleon
name = "gloves"
icon_state = "black"
@@ -47,6 +65,10 @@
origin_tech = list(TECH_ESOTERIC = 3)
item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM
+/obj/item/clothing/gloves/chameleon/Initialize()
+ . = ..()
+ set_extension(src, /datum/extension/chameleon/clothing/gloves, /obj/item/clothing/gloves)
+
/obj/item/clothing/mask/chameleon
name = "mask"
icon_state = "fullgas"
@@ -55,6 +77,10 @@
origin_tech = list(TECH_ESOTERIC = 3)
item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM
+/obj/item/clothing/mask/chameleon/Initialize()
+ . = ..()
+ set_extension(src, /datum/extension/chameleon/clothing/mask, /obj/item/clothing/mask)
+
/obj/item/clothing/glasses/chameleon
name = "goggles"
icon_state = "meson"
@@ -63,6 +89,10 @@
origin_tech = list(TECH_ESOTERIC = 3)
item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM
+/obj/item/clothing/glasses/chameleon/Initialize()
+ . = ..()
+ set_extension(src, /datum/extension/chameleon/clothing/glasses, /obj/item/clothing/glasses)
+
/obj/item/device/radio/headset/chameleon
name = "radio headset"
icon_state = "headset"
@@ -79,6 +109,10 @@
origin_tech = list(TECH_ESOTERIC = 3)
item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM
+/obj/item/clothing/accessory/chameleon/Initialize()
+ . = ..()
+ set_extension(src,/datum/extension/chameleon/clothing/accessory, /obj/item/clothing/accessory)
+
/obj/item/gun/energy/chameleon
name = "chameleon gun"
desc = "A hologram projector in the shape of a gun. There is a dial on the side to change the gun's disguise."
diff --git a/code/modules/clothing/under/accessories/_accessory.dm b/code/modules/clothing/under/accessories/_accessory.dm
index 628320f5f7ef1..2c98c7c7d559d 100644
--- a/code/modules/clothing/under/accessories/_accessory.dm
+++ b/code/modules/clothing/under/accessories/_accessory.dm
@@ -84,6 +84,9 @@
//when user attached an accessory to S
/obj/item/clothing/accessory/proc/on_attached(obj/item/clothing/S, mob/user)
+ if(istype(src,/obj/item/clothing/accessory/chameleon/changeling))
+ if(is_type_in_list(S,changeling_fabricated_clothing))
+ return
if(!istype(S))
return
parent = S
diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm
index 29134484954ce..315e5896f1b7e 100644
--- a/code/modules/mob/living/carbon/human/appearance.dm
+++ b/code/modules/mob/living/carbon/human/appearance.dm
@@ -23,11 +23,15 @@
/mob/living/carbon/human/proc/change_gender(gender)
if(src.gender == gender)
return
-
src.gender = gender
+
reset_hair()
+ force_update_limbs()
update_body()
update_dna()
+ src.sync_organ_dna()
+ if(src.gender == MALE)
+ src.UpdateAppearance()
return 1
/mob/living/carbon/human/proc/randomize_gender()
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index 9349e5bcdff45..a6d6fe3b79745 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -87,6 +87,11 @@
update_body(1)
return
+/mob/living/carbon/human/proc/Drain()
+ ChangeToHusk()
+ mutations |= MUTATION_HUSK
+ return
+
/mob/living/carbon/human/proc/ChangeToSkeleton()
if(MUTATION_SKELETON in src.mutations) return
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 604313ccf91e6..c32aeef4f2a36 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -119,6 +119,11 @@ If you have any questions/constructive-comments/bugs-to-report/or have a massivl
Please contact me on #coderbus IRC. ~Carn x
*/
+//Add an entry to overlays, assuming it exists
+/mob/living/carbon/human/proc/apply_layer(cache_index)
+ if((. = overlays_standing[cache_index]))
+ overlays.Add(.)
+
//Human Overlays Indexes/////////
#define HO_BODY_LAYER 1
#define HO_MUTATIONS_LAYER 2
@@ -147,7 +152,8 @@ Please contact me on #coderbus IRC. ~Carn x
#define HO_L_HAND_LAYER 25
#define HO_R_HAND_LAYER 26
#define HO_FIRE_LAYER 27 //If you're on fire
-#define TOTAL_LAYERS 28
+#define HO_EFFECTS_LAYER 28
+#define TOTAL_LAYERS 29
//////////////////////////////////
/mob/living/carbon/human
@@ -328,6 +334,7 @@ var/global/list/damage_icon_parts = list()
var/list/icon_render_keys = list()
/mob/living/carbon/human/proc/update_body(update_icons=1)
+
//Update all limbs and visible organs one by one
var/list/needs_update = list()
var/limb_count_update = FALSE
@@ -766,7 +773,6 @@ var/global/list/damage_icon_parts = list()
if(update_icons)
queue_icon_update()
-
/mob/living/carbon/human/update_fire(update_icons=1)
overlays_standing[HO_FIRE_LAYER] = null
if(on_fire)
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 95787852e45e7..844bcc992e4ac 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -11,7 +11,6 @@
if(machine && !CanMouseDrop(machine, src))
machine = null
-
//Handle temperature/pressure differences between body and environment
var/datum/gas_mixture/environment = loc.return_air()
if(environment)
@@ -194,12 +193,21 @@
/mob/living/proc/update_sight()
if(stat == DEAD || eyeobj)
update_dead_sight()
- else
- update_living_sight()
+ if (seedarkness)
+ set_sight(0)
+ set_see_in_dark(0)
+ if(stat == DEAD || eyeobj)
+ update_dead_sight()
+ else
+ update_living_sight()
- var/list/vision = get_accumulated_vision_handlers()
- set_sight(sight | vision[1])
- set_see_invisible(max(vision[2], see_invisible))
+ var/list/vision = get_accumulated_vision_handlers()
+ set_sight(sight | vision[1])
+ set_see_invisible(max(vision[2], see_invisible))
+
+ else
+ set_see_in_dark(8)
+ set_see_invisible(SEE_INVISIBLE_NOLIGHTING)
/mob/living/proc/update_living_sight()
var/set_sight_flags = sight & ~(SEE_TURFS|SEE_MOBS|SEE_OBJS)
diff --git a/code/modules/mob/living/maneuvers/maneuver_leap.dm b/code/modules/mob/living/maneuvers/maneuver_leap.dm
index 8f785dde54418..815e377cb4c39 100644
--- a/code/modules/mob/living/maneuvers/maneuver_leap.dm
+++ b/code/modules/mob/living/maneuvers/maneuver_leap.dm
@@ -81,3 +81,19 @@
animate(pixel_z = user.default_pixel_z, time = 3, easing = SINE_EASING | EASE_OUT)
user.throw_at(get_turf(target), strength, 1, user, FALSE, new Callback(src, TYPE_PROC_REF(/singleton/maneuver/leap, end_leap), user, target))
addtimer(new Callback(user, TYPE_PROC_REF(/mob/living, jump_layer_shift_end)), 4.5)
+
+/singleton/maneuver/leap/changeling
+ name = "vigorous leap"
+ stamina_cost = 20
+ reflexive_modifier = 0.5
+ cooldown = 5 SECONDS
+ delay = 0 SECONDS
+
+/singleton/maneuver/leap/changeling/end_leap(mob/living/user, atom/target)
+ . = ..()
+ if(ishuman(user) && !user.lying && ismob(target) && user.Adjacent(target))
+ if(istype(user.get_active_hand(),/obj/item/melee/changeling))
+ target.use_weapon(user.get_active_hand(),user)
+ if(istype(user.get_inactive_hand(), /obj/item/melee/changeling))
+ user.swap_hand()
+ target.use_weapon(user.get_active_hand(),user)
\ No newline at end of file
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 11549b6583b87..4b99ee3c9e197 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -153,6 +153,9 @@
var/voice_name = "unidentifiable voice"
+ // To check if mobs can see shadows or not. By default they can.
+ var/seedarkness = 1
+
var/faction = MOB_FACTION_NEUTRAL //Used for checking whether hostile simple animals will attack you, possibly more stuff later
var/last_faction = MOB_FACTION_NEUTRAL
var/blinded = null
diff --git a/code/modules/mob/observer/ghost/ghost.dm b/code/modules/mob/observer/ghost/ghost.dm
index 89e89882922a6..325c6d2ab01e2 100644
--- a/code/modules/mob/observer/ghost/ghost.dm
+++ b/code/modules/mob/observer/ghost/ghost.dm
@@ -30,7 +30,7 @@ var/global/list/image/ghost_sightless_images = list() //this is a list of images
var/admin_ghosted = 0
var/anonsay = 0
var/ghostvision = 1 //is the ghost able to see things humans can't?
- var/seedarkness = 1
+ seedarkness = 1
var/obj/item/device/multitool/ghost_multitool
var/list/hud_images // A list of hud images
diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm
index d5876e2fbf631..88e22de9f3668 100644
--- a/code/modules/multiz/movement.dm
+++ b/code/modules/multiz/movement.dm
@@ -242,6 +242,7 @@
SPAN_NOTICE("\The [src]'s suit whirrs loudly as \the [rig] absorbs the fall!"),
SPAN_NOTICE("You hear an electric *whirr* right after the slam!")
)
+
if (fall_damage())
for (var/mob/living/M in landing.contents)
if (M == src)
@@ -263,7 +264,8 @@
/mob/living/carbon/human/handle_fall_effect(turf/landing)
if(species && species.handle_fall_special(src, landing))
return
-
+ if(src.mind && src.mind.changeling && src.mind.changeling.tendons_reinforced)
+ return
var/obj/item/rig/rig = get_rig()
if (istype(rig))
for (var/obj/item/rig_module/actuators/A in rig.installed_modules)
diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm
index e5c33df72149b..fc69b33afa8d5 100644
--- a/code/modules/organs/external/_external.dm
+++ b/code/modules/organs/external/_external.dm
@@ -429,7 +429,7 @@ This function completely restores a damaged organ to perfect condition.
// remove embedded objects and drop them on the floor
for(var/obj/implanted_object in implants)
- if(!istype(implanted_object,/obj/item/implant)) // We don't want to remove REAL implants. Just shrapnel etc.
+ if(!istype(implanted_object,/obj/item/implant,) || istype(implanted_object,/obj/item/organ/internal/augment)) // We don't want to remove REAL implants. Just shrapnel etc.
implanted_object.forceMove(get_turf(src))
implants -= implanted_object
diff --git a/code/modules/organs/external/_external_damage.dm b/code/modules/organs/external/_external_damage.dm
index b8a0be3f1831b..86cf857db14b1 100644
--- a/code/modules/organs/external/_external_damage.dm
+++ b/code/modules/organs/external/_external_damage.dm
@@ -10,6 +10,7 @@
take_external_damage(amount)
/obj/item/organ/external/proc/take_external_damage(brute, burn, damage_flags, used_weapon = null)
+
brute = round(brute * get_brute_mod(damage_flags), 0.1)
burn = round(burn * get_burn_mod(damage_flags), 0.1)
diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm
index 54757fc8ecc89..07e27811e8c4f 100644
--- a/code/modules/organs/internal/lungs.dm
+++ b/code/modules/organs/internal/lungs.dm
@@ -29,7 +29,7 @@
var/breathing = 0
var/last_successful_breath
var/breath_fail_ratio // How badly they failed a breath. Higher is worse.
-
+ var/mnoBreath = 100
/obj/item/organ/internal/lungs/proc/can_drown()
return (is_broken() || !has_gills)
@@ -80,7 +80,7 @@
if(prob(5))
owner.emote("cough") //respitory tract infection
- if(is_bruised() && !owner.is_asystole())
+ if(is_bruised() && !owner.is_asystole() && !(mnoBreath in owner.mutations))
if(prob(2))
if(active_breathing)
owner.visible_message(
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index fe797006d15e2..7ce951d91ed88 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -164,7 +164,11 @@
return 0
if(!user.IsAdvancedToolUser())
return 0
-
+ if(istype(user,/mob/living/carbon/human))
+ var/mob/living/carbon/human/H = user
+ if(istype(H.wear_suit,/obj/item/clothing/suit/space/changeling/armored) && !istype(src,/obj/item/gun/projectile/changeling))
+ to_chat(user,SPAN_WARNING("This form is too bulky to make use of the trigger guard!"))
+ return FALSE
var/mob/living/M = user
if(!safety() && world.time > last_safety_check + 5 MINUTES && !user.skill_check(SKILL_WEAPONS, SKILL_BASIC))
if(prob(30))
diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm
index 2321283af05ce..89130ca6e809e 100644
--- a/code/modules/projectiles/guns/projectile.dm
+++ b/code/modules/projectiles/guns/projectile.dm
@@ -36,7 +36,7 @@
var/mag_insert_sound = 'sound/weapons/guns/interaction/pistol_magin.ogg'
var/mag_remove_sound = 'sound/weapons/guns/interaction/pistol_magout.ogg'
var/can_special_reload = TRUE //Whether or not we can tactical/speed reload
-
+ var/can_reload = TRUE
var/is_jammed = 0 //Whether this gun is jammed
var/jam_chance = 0 //Chance it jams on fire
//TODO generalize ammo icon states for guns
@@ -134,6 +134,8 @@
//Attempts to load A into src, depending on the type of thing being loaded and the load_method
//Maybe this should be broken up into separate procs for each load method?
/obj/item/gun/projectile/proc/load_ammo(obj/item/A, mob/user)
+ if(!can_reload)
+ return
if(istype(A, /obj/item/ammo_magazine))
. = TRUE
var/obj/item/ammo_magazine/AM = A
@@ -255,6 +257,8 @@
//attempts to unload src. If allow_dump is set to 0, the speedloader unloading method will be disabled
/obj/item/gun/projectile/proc/unload_ammo(mob/user, allow_dump=1)
+ if(!can_reload)
+ return
if(is_jammed)
user.visible_message("\The [user] begins to unjam [src].", "You clear the jam and unload [src]")
if(!do_after(user, 0.4 SECONDS, src, DO_DEFAULT | DO_BOTH_UNIQUE_ACT))
diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm
index dc9ab272f5e55..d9aa5435a9c36 100644
--- a/code/modules/species/species.dm
+++ b/code/modules/species/species.dm
@@ -419,7 +419,6 @@ The slots that you can use are found in items_clothing.dm and are the inventory
warning("[O.type] has a default organ tag \"[O.organ_tag]\" that differs from the species' organ tag \"[organ_tag]\". Updating organ_tag to match.")
O.organ_tag = organ_tag
H.internal_organs_by_name[organ_tag] = O
-
for(var/name in H.organs_by_name)
H.organs |= H.organs_by_name[name]
@@ -429,7 +428,6 @@ The slots that you can use are found in items_clothing.dm and are the inventory
for(var/obj/item/organ/O in (H.organs|H.internal_organs))
O.owner = H
post_organ_rejuvenate(O, H)
-
H.sync_organ_dna()
/* ------------------------> code\modules\emotes\definitions\_species.dm (У нас всё в моде в mods\emote_panel\code\species.dm)
/datum/species/proc/hug(mob/living/carbon/human/H, mob/living/target)
diff --git a/icons/mob/modifier_effects.dmi b/icons/mob/modifier_effects.dmi
new file mode 100644
index 0000000000000..99a4f6e4ed0d4
Binary files /dev/null and b/icons/mob/modifier_effects.dmi differ
diff --git a/icons/mob/onmob/items/lefthand.dmi b/icons/mob/onmob/items/lefthand.dmi
index 52eb1962b7f75..363f44c1f6015 100644
Binary files a/icons/mob/onmob/items/lefthand.dmi and b/icons/mob/onmob/items/lefthand.dmi differ
diff --git a/icons/mob/onmob/items/lefthand_spacesuits.dmi b/icons/mob/onmob/items/lefthand_spacesuits.dmi
index a2a812e1ed62b..2411f534473f9 100644
Binary files a/icons/mob/onmob/items/lefthand_spacesuits.dmi and b/icons/mob/onmob/items/lefthand_spacesuits.dmi differ
diff --git a/icons/mob/onmob/items/righthand.dmi b/icons/mob/onmob/items/righthand.dmi
index b2d9e57420577..ef5c0ffc0ab61 100644
Binary files a/icons/mob/onmob/items/righthand.dmi and b/icons/mob/onmob/items/righthand.dmi differ
diff --git a/icons/mob/onmob/items/righthand_spacesuits.dmi b/icons/mob/onmob/items/righthand_spacesuits.dmi
index e0a3f2cd06ae1..9640ec709b3df 100644
Binary files a/icons/mob/onmob/items/righthand_spacesuits.dmi and b/icons/mob/onmob/items/righthand_spacesuits.dmi differ
diff --git a/icons/mob/onmob/onmob_belt.dmi b/icons/mob/onmob/onmob_belt.dmi
index 9e7941b095e4d..4ab840928e9f3 100644
Binary files a/icons/mob/onmob/onmob_belt.dmi and b/icons/mob/onmob/onmob_belt.dmi differ
diff --git a/icons/mob/onmob/onmob_eyes.dmi b/icons/mob/onmob/onmob_eyes.dmi
index 47c97701f868a..d49a12a5e0fa2 100644
Binary files a/icons/mob/onmob/onmob_eyes.dmi and b/icons/mob/onmob/onmob_eyes.dmi differ
diff --git a/icons/mob/onmob/onmob_feet.dmi b/icons/mob/onmob/onmob_feet.dmi
index 954ce5656840e..2754ebb2c31fd 100644
Binary files a/icons/mob/onmob/onmob_feet.dmi and b/icons/mob/onmob/onmob_feet.dmi differ
diff --git a/icons/mob/onmob/onmob_hands.dmi b/icons/mob/onmob/onmob_hands.dmi
index 43964e3ce8f4a..8adb2586a3f02 100644
Binary files a/icons/mob/onmob/onmob_hands.dmi and b/icons/mob/onmob/onmob_hands.dmi differ
diff --git a/icons/mob/onmob/onmob_head.dmi b/icons/mob/onmob/onmob_head.dmi
index ab89699f89b6c..d9ccd6d0c834a 100644
Binary files a/icons/mob/onmob/onmob_head.dmi and b/icons/mob/onmob/onmob_head.dmi differ
diff --git a/icons/mob/onmob/onmob_mask.dmi b/icons/mob/onmob/onmob_mask.dmi
index 26b7ec6ddeda4..44a3b562407ff 100644
Binary files a/icons/mob/onmob/onmob_mask.dmi and b/icons/mob/onmob/onmob_mask.dmi differ
diff --git a/icons/mob/onmob/onmob_suit.dmi b/icons/mob/onmob/onmob_suit.dmi
index cdd965312a55c..320ee8cf7a664 100644
Binary files a/icons/mob/onmob/onmob_suit.dmi and b/icons/mob/onmob/onmob_suit.dmi differ
diff --git a/icons/mob/onmob/onmob_under.dmi b/icons/mob/onmob/onmob_under.dmi
index 2cb6b7ddc48de..f39d63695a3c8 100644
Binary files a/icons/mob/onmob/onmob_under.dmi and b/icons/mob/onmob/onmob_under.dmi differ
diff --git a/icons/mob/screen_spells.dmi b/icons/mob/screen_spells.dmi
index 50be07d787b8c..541f09908c282 100644
Binary files a/icons/mob/screen_spells.dmi and b/icons/mob/screen_spells.dmi differ
diff --git a/icons/obj/augment.dmi b/icons/obj/augment.dmi
index 48e29f6e018c5..7416d992f9b9d 100644
Binary files a/icons/obj/augment.dmi and b/icons/obj/augment.dmi differ
diff --git a/icons/obj/clothing/obj_accessories.dmi b/icons/obj/clothing/obj_accessories.dmi
index 9948e18d39640..a1f1b10ed5130 100644
Binary files a/icons/obj/clothing/obj_accessories.dmi and b/icons/obj/clothing/obj_accessories.dmi differ
diff --git a/icons/obj/clothing/obj_backpacks.dmi b/icons/obj/clothing/obj_backpacks.dmi
index 121b8260e5a3a..0b397695adae9 100644
Binary files a/icons/obj/clothing/obj_backpacks.dmi and b/icons/obj/clothing/obj_backpacks.dmi differ
diff --git a/icons/obj/clothing/obj_belt.dmi b/icons/obj/clothing/obj_belt.dmi
index cb6487083229e..e47e6491a2af2 100644
Binary files a/icons/obj/clothing/obj_belt.dmi and b/icons/obj/clothing/obj_belt.dmi differ
diff --git a/icons/obj/clothing/obj_eyes.dmi b/icons/obj/clothing/obj_eyes.dmi
index c746f3a1a661b..5db018dc41f7e 100644
Binary files a/icons/obj/clothing/obj_eyes.dmi and b/icons/obj/clothing/obj_eyes.dmi differ
diff --git a/icons/obj/clothing/obj_feet.dmi b/icons/obj/clothing/obj_feet.dmi
index 8d29e712d9cd1..e5086830b489d 100644
Binary files a/icons/obj/clothing/obj_feet.dmi and b/icons/obj/clothing/obj_feet.dmi differ
diff --git a/icons/obj/clothing/obj_hands.dmi b/icons/obj/clothing/obj_hands.dmi
index 09b807a538398..0bdb00ca1c2cd 100644
Binary files a/icons/obj/clothing/obj_hands.dmi and b/icons/obj/clothing/obj_hands.dmi differ
diff --git a/icons/obj/clothing/obj_head.dmi b/icons/obj/clothing/obj_head.dmi
index 22964cf7c9af4..b6e612c8feaf4 100644
Binary files a/icons/obj/clothing/obj_head.dmi and b/icons/obj/clothing/obj_head.dmi differ
diff --git a/icons/obj/clothing/obj_mask.dmi b/icons/obj/clothing/obj_mask.dmi
index 168436c1a2538..0bcb6f8cc641f 100644
Binary files a/icons/obj/clothing/obj_mask.dmi and b/icons/obj/clothing/obj_mask.dmi differ
diff --git a/icons/obj/clothing/obj_suit.dmi b/icons/obj/clothing/obj_suit.dmi
index 95fdc5db3973f..8475fc1a2213e 100644
Binary files a/icons/obj/clothing/obj_suit.dmi and b/icons/obj/clothing/obj_suit.dmi differ
diff --git a/icons/obj/clothing/obj_under.dmi b/icons/obj/clothing/obj_under.dmi
index f9968bcbaf687..76b6f671b826d 100644
Binary files a/icons/obj/clothing/obj_under.dmi and b/icons/obj/clothing/obj_under.dmi differ
diff --git a/icons/obj/guns/dartgun.dmi b/icons/obj/guns/dartgun.dmi
index 628a1464c798d..65413149c669b 100644
Binary files a/icons/obj/guns/dartgun.dmi and b/icons/obj/guns/dartgun.dmi differ
diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi
index 13ab8d2ac82bd..8fd3c7d33364b 100644
Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ
diff --git a/icons/obj/weapons/melee_energy.dmi b/icons/obj/weapons/melee_energy.dmi
index 4d7e0b4c5e91a..9cd3b3c83022e 100644
Binary files a/icons/obj/weapons/melee_energy.dmi and b/icons/obj/weapons/melee_energy.dmi differ
diff --git a/icons/obj/weapons/melee_physical.dmi b/icons/obj/weapons/melee_physical.dmi
index bd59c7d83eea6..ccda4b64e103d 100644
Binary files a/icons/obj/weapons/melee_physical.dmi and b/icons/obj/weapons/melee_physical.dmi differ
diff --git a/sound/effects/ling_horror.ogg b/sound/effects/ling_horror.ogg
new file mode 100644
index 0000000000000..3dc6450bab89d
Binary files /dev/null and b/sound/effects/ling_horror.ogg differ
diff --git a/sound/effects/ling_shriek.ogg b/sound/effects/ling_shriek.ogg
new file mode 100644
index 0000000000000..5dd92382720fa
Binary files /dev/null and b/sound/effects/ling_shriek.ogg differ
diff --git a/sound/effects/ling_shriek2.ogg b/sound/effects/ling_shriek2.ogg
new file mode 100644
index 0000000000000..3292fff8e7db7
Binary files /dev/null and b/sound/effects/ling_shriek2.ogg differ
diff --git a/test/check-paths.sh b/test/check-paths.sh
index 9f3300ab701d6..60dc47576b5e4 100755
--- a/test/check-paths.sh
+++ b/test/check-paths.sh
@@ -48,7 +48,7 @@ exactly 27 "text2path uses" 'text2path'
exactly 5 "update_icon() override" '/update_icon\((.*)\)' -P
exactly 4 "goto use" 'goto '
exactly 1 "NOOP match" 'NOOP'
-exactly 332 "spawn uses" '^\s*spawn\s*\(\s*(-\s*)?\d*\s*\)' -P
+exactly 323 "spawn uses" '^\s*spawn\s*\(\s*(-\s*)?\d*\s*\)' -P
exactly 0 "tag uses" '\stag = ' -P '**/*.dmm'
exactly 0 "anchored = 0/1" 'anchored\s*=\s*\d' -P
exactly 2 "density = 0/1" 'density\s*=\s*\d' -P