diff --git a/tff_modular/modules/psyonics/code/_psyonics.dm b/tff_modular/modules/psyonics/code/_psyonics.dm
new file mode 100644
index 00000000000..cf6475344d3
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/_psyonics.dm
@@ -0,0 +1,193 @@
+// Тут хранятся некрасивые базовые классы и прочее. Не смотрите сюда.
+
+/datum/action/cooldown/spell
+ // Сколько маны стоит кастануть спелл
+ var/mana_cost = 10
+ // Некоторые спеллы могут отнимать стамину
+ var/stamina_cost = 0
+ // Что написать жертве
+ var/target_msg
+ // Сила способности
+ var/cast_power = 0
+ // Вторичная школа. Может дать особые эффекты при комбинациях
+ var/secondary_school = 0
+
+// Спеллы для призвания предмета
+/datum/action/cooldown/spell/conjure_item/psyonic
+ delete_old = FALSE
+ delete_on_failure = TRUE
+ requires_hands = TRUE
+ // Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
+ antimagic_flags = MAGIC_RESISTANCE_MIND
+ spell_requirements = NONE
+ cooldown_reduction_per_rank = 0 SECONDS
+
+/datum/action/cooldown/spell/conjure_item/psyonic/New(Target, power, additional_school)
+ . = ..()
+ cast_power = power
+ secondary_school = additional_school
+
+// Проверяем достаточно ли маны
+/datum/action/cooldown/spell/proc/check_for_mana()
+ var/mob/living/carbon/human/caster = owner
+ var/datum/quirk/psyonic/quirk_holder = caster.get_quirk(/datum/quirk/psyonic)
+ if(quirk_holder && (quirk_holder.mana_level - mana_cost) >= 0)
+ return TRUE
+ else
+ return FALSE
+
+// Сосём ману у псионика
+/datum/action/cooldown/spell/proc/drain_mana(forced = FALSE)
+ var/mob/living/carbon/human/caster = owner
+ var/datum/quirk/psyonic/quirk_holder = caster.get_quirk(/datum/quirk/psyonic)
+ caster.adjustStaminaLoss(stamina_cost, forced = TRUE)
+ if(quirk_holder && (quirk_holder.mana_level - mana_cost) >= 0)
+ quirk_holder.mana_level -= mana_cost
+ return TRUE
+ else if (forced)
+ quirk_holder.mana_level = 0
+ return TRUE
+ else
+ return FALSE
+
+/datum/action/cooldown/spell/conjure_item/psyonic/can_cast_spell(feedback)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!check_for_mana())
+ return FALSE
+ else
+ return TRUE
+
+/datum/action/cooldown/spell/conjure_item/psyonic/cast(atom/cast_on)
+ drain_mana()
+ return ..()
+
+// Для спеллов которые применяются на себя тыком кнопки a.k.a. выдача генов
+/datum/action/cooldown/spell/psyonic
+ // Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
+ antimagic_flags = MAGIC_RESISTANCE_MIND
+
+ school = SCHOOL_UNSET
+ invocation_type = INVOCATION_NONE
+ spell_requirements = NONE
+ cooldown_reduction_per_rank = 0 SECONDS
+
+/datum/action/cooldown/spell/psyonic/New(Target, power, additional_school)
+ . = ..()
+ cast_power = power
+ secondary_school = additional_school
+
+/datum/action/cooldown/spell/psyonic/can_cast_spell(feedback)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!check_for_mana())
+ return FALSE
+ else
+ return TRUE
+
+// Спеллы для пострелушек
+/datum/action/cooldown/spell/pointed/projectile/psyonic
+ // Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
+ antimagic_flags = MAGIC_RESISTANCE_MIND
+
+ school = SCHOOL_UNSET
+ invocation_type = INVOCATION_NONE
+ spell_requirements = NONE
+ cooldown_reduction_per_rank = 0 SECONDS
+
+/datum/action/cooldown/spell/pointed/projectile/psyonic/New(Target, power, additional_school)
+ . = ..()
+ cast_power = power
+ secondary_school = additional_school
+
+/datum/action/cooldown/spell/pointed/projectile/psyonic/can_cast_spell(feedback)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!check_for_mana())
+ return FALSE
+ else
+ return TRUE
+
+// Направленные спеллы a.k.a. псионик выбирают цель на дистанции
+/datum/action/cooldown/spell/pointed/psyonic
+ // Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
+ antimagic_flags = MAGIC_RESISTANCE_MIND
+ school = SCHOOL_UNSET
+ invocation_type = INVOCATION_NONE
+ spell_requirements = NONE
+ cooldown_reduction_per_rank = 0 SECONDS
+
+/datum/action/cooldown/spell/pointed/psyonic/New(Target, power, additional_school)
+ . = ..()
+ cast_power = power
+ secondary_school = additional_school
+
+/datum/action/cooldown/spell/pointed/psyonic/can_cast_spell(feedback)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!check_for_mana())
+ return FALSE
+ else
+ return TRUE
+
+// Спеллы которыми надо каснуться чего либо
+/datum/action/cooldown/spell/touch/psyonic
+ // Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
+ antimagic_flags = MAGIC_RESISTANCE_MIND
+ school = SCHOOL_UNSET
+ invocation_type = INVOCATION_NONE
+ spell_requirements = NONE
+
+/datum/action/cooldown/spell/touch/psyonic/New(Target, power, additional_school)
+ . = ..()
+ cast_power = power
+ secondary_school = additional_school
+
+/datum/action/cooldown/spell/touch/psyonic/can_cast_spell(feedback)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!check_for_mana())
+ return FALSE
+ else
+ return TRUE
+
+/datum/action/cooldown/spell/touch/psyonic/create_hand(mob/living/carbon/cast_on)
+ . = ..()
+ if(!.)
+ return .
+ var/obj/item/bodypart/transfer_limb = cast_on.get_active_hand()
+ if(IS_ROBOTIC_LIMB(transfer_limb))
+ to_chat(cast_on, span_notice("You fail to channel your psyonic powers through your inorganic hand."))
+ return FALSE
+
+ return TRUE
+
+/particles/droplets/psyonic
+ icon = 'icons/effects/particles/generic.dmi'
+ icon_state = list("dot"=2,"drop"=1)
+ width = 32
+ height = 36
+ count = 20
+ spawning = 0.2
+ lifespan = 1.5 SECONDS
+ fade = 0.5 SECONDS
+ color = "#00a2ff"
+ position = generator(GEN_BOX, list(-9,-9,0), list(9,18,0), NORMAL_RAND)
+ scale = generator(GEN_VECTOR, list(0.9,0.9), list(1.1,1.1), NORMAL_RAND)
+ gravity = list(0, 0.95)
+
+// Проверка на то, есть ли квирк псионики у хумана
+/mob/living/carbon/human/proc/ispsyonic()
+ if(has_quirk(/datum/quirk/psyonic))
+ return TRUE
+ return FALSE
diff --git a/tff_modular/modules/psyonics/code/_quirk.dm b/tff_modular/modules/psyonics/code/_quirk.dm
new file mode 100644
index 00000000000..d48444b5e76
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/_quirk.dm
@@ -0,0 +1,215 @@
+#define TRAIT_PSYONIC_USER "psyonicuser"
+#define TRAIT_NO_PSYONICS "no_psyonics"
+#define TRAIT_PRO_PSYONICS "pro_psyonics"
+
+#define LATENT_PSYONIC 0
+#define OPERANT_PSYONIC 1
+#define MASTER_PSYONIC 2
+#define GRANDMASTER_PSYONIC 3
+#define PARAMOUNT_PSYONIC 4
+#define GREATEST_PSYONIC 5
+
+GLOBAL_LIST_INIT(psyonic_schools, list(
+ "Redaction",
+ "Coercion",
+ "Psychokinesis",
+ "Energistics",
+))
+
+/datum/quirk/psyonic
+ name = "Psyonic Abilities"
+ desc = "Either you were born like this or gained powers from implants/training or other events - you are a psyonic. \
+ Your mind can access the world that lies beyond our mortal plane. One day voices from within had pierced your skull \
+ like a tide wave turns a sailboat over in open sea, but you withstanded it and received abilities your father haven't \
+ even dreamed of. From now on a special type of energy is stored in your mind, body and soul and you have control over it. \
+ Every psyonic is a follower of a certain school: \
+ Redaction - school of mending and curing bodies and souls; \
+ Coercion - school of trickery and controlling others; \
+ Psychokinesis - school of object manipulation; \
+ Energistics - school of elecricity, fire and light; \
+ You can select the school, but it's power will be randomised every round."
+ value = 12 // Отдадите за псионику жопу, чтобы потом вам Рэнди Рандом всегда слал наименьший уровень силы
+ medical_record_text = "Patient possesses connection to an another plain of reality."
+ quirk_flags = QUIRK_HIDE_FROM_SCAN|QUIRK_HUMAN_ONLY|QUIRK_PROCESSES // Сканеры не видят псиоников. Только псионик школы принуждения может точно определить, является ли живое существо псиоником
+ gain_text = span_cyan("You mind feels uneasy, but... so powerful.")
+ lose_text = span_warning("You lost something, that kept your connection with other realms.")
+ icon = "fa-star"
+ mob_trait = TRAIT_PSYONIC_USER
+ veteran_only = TRUE
+ allow_for_donator = TRUE
+ // Текущий уровень маны
+ var/mana_level = 0
+ // Максимально возможный уровень маны
+ var/max_mana = 10
+ // Уровень псионических способностей
+ var/psyonic_level = 0
+ // Строка для описания уровня
+ var/psyonic_level_string = "Latent"
+ // Первичная школа псионики
+ var/school
+ // Вторичная школа псионики
+ var/secondary_school
+ /// Два вара скопированные из item_quirk для правильной выдачи лицензии
+ var/list/where_items_spawned
+ var/open_backpack = FALSE
+
+/datum/quirk/psyonic/add(client/client_source)
+ school = client_source?.prefs?.read_preference(/datum/preference/choiced/psyonic_school)
+ if(!school)
+ school = pick(GLOB.psyonic_schools)
+ secondary_school = client_source?.prefs?.read_preference(/datum/preference/choiced/psyonic_school_secondary)
+ if(!secondary_school)
+ secondary_school = pick(GLOB.psyonic_schools)
+ var/mob/living/carbon/human/whom_to_give = quirk_holder
+ var/fluff_1 = rand(0,1)
+ var/fluff_2 = rand(0,1)
+ var/fluff_3 = rand(0,1)
+ var/fluff_4 = rand(0,1)
+ psyonic_level = fluff_1 + fluff_2 + fluff_3 + fluff_4
+ if(HAS_MIND_TRAIT(whom_to_give, TRAIT_MADNESS_IMMUNE)) // A.K.A. Психолог
+ psyonic_level += rand(0,1) // _возможное_ доп очко
+ switch(psyonic_level)
+ if(LATENT_PSYONIC)
+ psyonic_level_string = "Pi"
+ if(OPERANT_PSYONIC)
+ psyonic_level_string = "Omicron"
+ if(MASTER_PSYONIC)
+ psyonic_level_string = "Kappa"
+ if(GRANDMASTER_PSYONIC)
+ psyonic_level_string = "Lambda"
+ if(PARAMOUNT_PSYONIC)
+ psyonic_level_string = "Theta"
+ if(GREATEST_PSYONIC) // Дозволен только особо везучим психологам, у которых все предыдущие пять рандомов вышли на 1
+ psyonic_level_string = "Epsilon"
+ max_mana = (psyonic_level + 1) * 20 // Минимальный - 20, максимальный - 100
+ RegisterSignal(quirk_holder, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(get_status_tab_item))
+ if(school == secondary_school)
+ psyonic_level += 1 // Если вторичка совпадает с первой - добавляем один уровень, но не меняем описание
+ switch(school)
+ if("Redaction")
+ whom_to_give.try_add_redaction_school(psyonic_level, secondary_school)
+ if("Coercion")
+ whom_to_give.try_add_coercion_school(psyonic_level, secondary_school)
+ if("Psychokinesis")
+ whom_to_give.try_add_psychokinesis_school(psyonic_level, secondary_school)
+ if("Energistics")
+ whom_to_give.try_add_energistics_school(psyonic_level, secondary_school)
+
+ if(secondary_school != school) // Если школы разные, добавить способность нулевого уровня вторичной школы
+ switch(secondary_school)
+ if("Redaction")
+ whom_to_give.try_add_redaction_school(0, 0)
+ if("Coercion")
+ whom_to_give.try_add_coercion_school(0, 0)
+ if("Psychokinesis")
+ whom_to_give.try_add_psychokinesis_school(0, 0)
+ if("Energistics")
+ whom_to_give.try_add_energistics_school(0, 0)
+
+ var/fluff_text = span_cyan("Current psionic factors:") + "
" + \
+ "[fluff_1 ? "Current star position is aligned to your soul." : "The stars do not precede luck to you."]" + "
" + \
+ "[fluff_2 ? "Other realms are unusually active this shift." : "Other realms are quiet today."]" + "
" + \
+ "[fluff_3 ? "Time-bluespace continuum seems to be stable today." : "Time-bluespace continuum is not giving you energy today."]" + "
" + \
+ "[fluff_4 ? "Your mind is clearly open to otherwordly energy." : "Something clouds your connection to otherworld energy."]"
+ to_chat(quirk_holder, examine_block(span_infoplain(jointext(fluff_text, "\n• "))))
+ psyonic_level -= 1 // Обязаловка, иначе выдаст спеллы которые нельзя кастануть
+
+ var/obj/item/card/psyonic_license/new_license = new(whom_to_give)
+
+ give_item_to_holder(new_license, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS), flavour_text = "Make sure not to lose it. You can not remake this on the station.")
+
+/datum/quirk/psyonic/proc/give_item_to_holder(obj/item/quirk_item, list/valid_slots, flavour_text = null, default_location = "at your feet", notify_player = TRUE)
+ if(ispath(quirk_item))
+ quirk_item = new quirk_item(get_turf(quirk_holder))
+
+ var/mob/living/carbon/human/human_holder = quirk_holder
+
+ var/where = human_holder.equip_in_one_of_slots(quirk_item, valid_slots, qdel_on_fail = FALSE, indirect_action = TRUE) || default_location
+
+ if(where == LOCATION_BACKPACK)
+ open_backpack = TRUE
+
+ if(notify_player)
+ LAZYADD(where_items_spawned, span_boldnotice("You have \a [quirk_item] [where]. [flavour_text]"))
+
+/datum/quirk/psyonic/remove()
+ UnregisterSignal(quirk_holder, COMSIG_MOB_GET_STATUS_TAB_ITEMS)
+
+// Показывает текущее кол-во псионической энергии
+/datum/quirk/psyonic/proc/get_status_tab_item(mob/living/source, list/items)
+ SIGNAL_HANDLER
+
+ items += "Psyonic School: [school]"
+ items += "Secondary School: [secondary_school]"
+ items += "Psyonic Tier: [psyonic_level_string]"
+ items += "Current psyonic energy: [mana_level]/[max_mana]"
+
+/datum/quirk/psyonic/process(seconds_per_tick)
+ if(HAS_TRAIT(quirk_holder, TRAIT_NO_PSYONICS)) // Имплант подавления регена
+ return
+
+ if(HAS_TRAIT(quirk_holder, TRAIT_MINDSHIELD)) // Womp womp
+ return
+
+ var/additional_mana = 1
+ if(quirk_holder.has_status_effect(/datum/status_effect/drugginess)) // Наркота даёт бафф к генерации маны
+ additional_mana *= 1.5
+
+ if(HAS_TRAIT(quirk_holder, TRAIT_PRO_PSYONICS)) // Если есть имплант для увеличения регена маны
+ additional_mana *= 2
+
+ if(mana_level <= max_mana)
+ mana_level += seconds_per_tick * 0.5 * additional_mana
+ mana_level = clamp(mana_level, 0, max_mana)
+
+/datum/quirk_constant_data/psyonic_school
+ associated_typepath = /datum/quirk/psyonic
+ customization_options = list(/datum/preference/choiced/psyonic_school, /datum/preference/choiced/psyonic_school_secondary)
+
+/datum/preference/choiced/psyonic_school
+ category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
+ savefile_key = "psyonic_school"
+ savefile_identifier = PREFERENCE_CHARACTER
+
+/datum/preference/choiced/psyonic_school/create_default_value()
+ return "Redaction"
+
+/datum/preference/choiced/psyonic_school/init_possible_values()
+ return GLOB.psyonic_schools
+
+/datum/preference/choiced/psyonic_school/is_accessible(datum/preferences/preferences)
+ . = ..()
+ if (!.)
+ return FALSE
+
+ return "Psyonic Abilities" in preferences.all_quirks
+
+/datum/preference/choiced/psyonic_school/apply_to_human(mob/living/carbon/human/target, value)
+ return
+
+/datum/preference/choiced/psyonic_school_secondary
+ category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
+ savefile_key = "psyonic_school_secondary"
+ savefile_identifier = PREFERENCE_CHARACTER
+
+/datum/preference/choiced/psyonic_school_secondary/create_default_value()
+ return "Redaction"
+
+/datum/preference/choiced/psyonic_school_secondary/init_possible_values()
+ return GLOB.psyonic_schools
+
+/datum/preference/choiced/psyonic_school_secondary/is_accessible(datum/preferences/preferences)
+ . = ..()
+ if (!.)
+ return FALSE
+
+ return "Psyonic Abilities" in preferences.all_quirks
+
+/datum/preference/choiced/psyonic_school_secondary/apply_to_human(mob/living/carbon/human/target, value)
+ return
+
+#undef LATENT_PSYONIC
+#undef OPERANT_PSYONIC
+#undef MASTER_PSYONIC
+#undef GRANDMASTER_PSYONIC
+#undef PARAMOUNT_PSYONIC
diff --git a/tff_modular/modules/psyonics/code/coersion.dm b/tff_modular/modules/psyonics/code/coersion.dm
new file mode 100644
index 00000000000..445593b74c2
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/coersion.dm
@@ -0,0 +1,484 @@
+#define IS_HYPNOTIZED(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/hypnotized))
+
+/// Школа внушения. 7 спеллов
+/// Psyonic assay - скан, является ли человек псиоником
+/// Psyonic focus - лечение мозга и псих болезней
+/// Psyonic mind read - продвинутое чтение разума(Как обычная ген. мутация, но + работа + воспоминания). Выдаётся только и ТОЛЬКО психологу, если он псионик
+/// Psyonic agony - работает как стан дубинка, исчезает после одного удара
+/// Psyonic spasm - станит на полсекунды, заставляет выронить всё из рук. Работает дистанционно
+/// Psyonic hypnosis - гипнотизирует цель фразой, которую выбрал псионик. ERP IS BAD. DO NOT ERP.
+/// P.S. По гипнозу. В оригинале на финиках вообще было порабощение разума.
+/// Psyonic blind - временно ослепляет.
+
+// Прок для проверки носит ли моб шляпку из фольги. Удивительно, но защищает от некоторых спеллов школы внушения)
+/mob/living/carbon/human/proc/is_wearing_tinfoil_hat()
+ if(istype(head, /obj/item/clothing/head/costume/foilhat))
+ return TRUE
+ return FALSE
+
+// Добавить школу внушения
+/mob/living/carbon/human/proc/try_add_coercion_school(tier = 0, additional_school = 0)
+ if(tier >= 0)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/touch/psyonic/psyonic_assay(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 1)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/pointed/psyonic/psyonic_focus(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 2)
+ if(HAS_MIND_TRAIT(src, TRAIT_MADNESS_IMMUNE)) // A.K.A. станционный психолог
+ var/datum/action/new_action = new /datum/action/cooldown/spell/touch/psyonic/psyonic_mind_read(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ var/datum/action/new_action2 = new /datum/action/cooldown/spell/touch/psyonic/psyonic_hypnosis(src.mind || src, tier, additional_school)
+ new_action2.Grant(src)
+ if(tier >= 3)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/touch/psyonic/psyonic_agony(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ var/datum/action/new_action2 = new /datum/action/cooldown/spell/pointed/psyonic/psyonic_spasm(src.mind || src, tier, additional_school)
+ new_action2.Grant(src)
+ if(tier >= 4) // Способность вызывать слепоту на ~15 секунд втихую на расстоянии это боль.
+ var/datum/action/new_action = new /datum/action/cooldown/spell/pointed/psyonic/psyonic_blind(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+
+// Спелл для чтения разума другого игрока на наличие псионических способностей
+/datum/action/cooldown/spell/touch/psyonic/psyonic_assay
+ name = "Psyonic Assay"
+ desc = "Check if the target is a psyonic."
+ button_icon = 'icons/obj/medical/organs/organs.dmi'
+ button_icon_state = "brain"
+ cooldown_time = 3 SECONDS
+ mana_cost = 5
+ stamina_cost = 0
+ target_msg = "Your get a headache, but it quickly fades."
+ hand_path = /obj/item/melee/touch_attack/psyonic_mending
+ draw_message = span_notice("You ready your hand to cleanse a patient.")
+ drop_message = span_notice("You lower your hand.")
+ can_cast_on_self = TRUE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_assay/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/mendicant)
+ if(ishuman(victim))
+ var/mob/living/carbon/human/human_victim = victim
+ if(human_victim.can_block_magic(antimagic_flags))
+ to_chat(human_victim, span_notice("Psionic nearby tries to check you for psyonic levels."))
+ else
+ to_chat(human_victim, span_warning(target_msg))
+ owner.visible_message(span_warning("[owner] presses his thumb onto [victim]s forehead."),
+ span_notice("You press your thumb onto [victim]s forehead and begin reading them."))
+ to_chat(victim, span_danger("[owner] presses a thumb onto your forehead and holds it there. It burns sligthly!"))
+ if(do_after(mendicant, 6 SECONDS, human_victim, IGNORE_SLOWDOWNS, TRUE))
+ read_psyonic_level(human_victim)
+ drain_mana()
+ return TRUE
+ else
+ return FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_assay/proc/read_psyonic_level(mob/living/carbon/human/patient)
+ if(issynthetic(patient) && secondary_school != "Psychokinesis")
+ to_chat(owner, span_notice("I can see... just numbers. No idea how to work with synths."))
+ return FALSE
+
+ if(patient.ispsyonic())
+ var/datum/quirk/psyonic/target_quirk = patient.get_quirk(/datum/quirk/psyonic)
+ owner.visible_message(span_notice("[owner] backs off from [patient]."),
+ span_cyan("Target is a psyonic from the school of [target_quirk.school]. [patient.p_Their()] class is [target_quirk.psyonic_level_string]"))
+ else
+ owner.visible_message(span_notice("[owner] backs off from [patient]."),
+ span_cyan("Target is not a psyonic."))
+
+// Лечим мозги и брейнтравмы.
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_focus
+ name = "Psyonic Focus"
+ desc = "Try to restore patients brain to its natural initial condition, fixing brain damage. Has a chance to heal traumas. Can be cast over distance."
+ button_icon = 'icons/obj/medical/organs/organs.dmi'
+ button_icon_state = "brain-smooth"
+ cooldown_time = 1 SECONDS
+ mana_cost = 40
+ target_msg = "You feel like someone is messing with your brains."
+ active_msg = "You prepare to heal someones mind..."
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_focus/New(Target)
+ . = ..()
+ if(secondary_school == "Redaction")
+ cast_power += 1
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_focus/is_valid_target(atom/cast_on)
+ if(!ishuman(cast_on))
+ return FALSE
+ if(issynthetic(cast_on) && secondary_school != "Psychokinesis")
+ to_chat(owner, span_notice("I dont know how to work with synths."))
+ return FALSE
+
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_focus/cast(mob/living/carbon/human/cast_on)
+ . = ..()
+ if(cast_on.can_block_magic(antimagic_flags))
+ to_chat(cast_on, span_notice("Your mind is being healed by a psyonic nearby."))
+ else
+ to_chat(cast_on, span_warning(target_msg))
+ owner.Beam(cast_on, icon_state = "blood_light", time = 5 SECONDS)
+ owner.visible_message(span_warning("[owner] seems to concentrate on something."),
+ span_notice("You start concentrating your energy to heal [cast_on]s brains."))
+ if(!do_after(owner, 5 SECONDS, cast_on, IGNORE_SLOWDOWNS | IGNORE_TARGET_LOC_CHANGE, TRUE))
+ accident_harm(cast_on)
+ else
+ fix_brainz(cast_on)
+ drain_mana()
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_focus/proc/fix_brainz(mob/living/carbon/human/cast_on)
+ var/b_damage = cast_on.get_organ_loss(ORGAN_SLOT_BRAIN)
+ if(b_damage > 0)
+ cast_on.adjustOrganLoss(ORGAN_SLOT_BRAIN, -10 * cast_power)
+
+ var/traumas = cast_on.get_traumas()
+ if(traumas)
+ var/datum/brain_trauma/trauma = pick(traumas)
+ if(trauma.resilience != TRAUMA_RESILIENCE_ABSOLUTE)
+ cast_on.cure_trauma_type(resilience = trauma.resilience)
+ cast_on.apply_status_effect(/datum/status_effect/drugginess, 20 SECONDS)
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_focus/proc/accident_harm(mob/living/carbon/human/cast_on)
+ cast_on.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15 * cast_power, 101)
+ to_chat(cast_on, span_bolddanger("You head hurts!"))
+
+// Читаем разум. Выдаёт: последние сейлоги, интент, настоящее имя, воспоминания, намёк на работу, намёк на то, что в антаг_датум что то есть.
+/datum/action/cooldown/spell/touch/psyonic/psyonic_mind_read
+ name = "Psyonic Mind Read"
+ desc = "Rudely intrude into targets thoughts."
+ button_icon = 'icons/mob/actions/actions_spells.dmi'
+ button_icon_state = "mindread"
+ cooldown_time = 3 SECONDS
+ mana_cost = 40
+ stamina_cost = 40
+ target_msg = "You feel someone else in your head."
+
+ hand_path = /obj/item/melee/touch_attack/psyonic_mending
+ draw_message = span_notice("You ready your hand to read someones mind.")
+ drop_message = span_notice("You lower your hand.")
+ can_cast_on_self = FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_mind_read/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/mendicant)
+ if(ishuman(victim))
+ var/mob/living/carbon/human/human_victim = victim
+ if(human_victim.is_wearing_tinfoil_hat())
+ to_chat(human_victim, span_clockred("Your tinfoil hat vibrates, protecting your brain from some kind of invisible rays!"))
+ to_chat(owner, span_clockred("As soon as you touch [human_victim]s head, suddnely pictures of your own mind appear! Looks like the tinfoil hat on their head is interfering."))
+ drain_mana()
+ return TRUE
+ if(human_victim.mind && human_victim.stat != DEAD)
+ if(human_victim.can_block_magic(antimagic_flags))
+ to_chat(human_victim, span_bolddanger("Psionic nearby tries to read your mind!"))
+ else
+ to_chat(human_victim, span_warning(target_msg))
+ owner.visible_message(span_warning("[owner] presses his thumb onto [victim]s forehead."),
+ span_notice("You press your thumb onto [victim]s forehead and begin reading them."))
+ to_chat(victim, span_danger("[owner] presses a thumb onto your forehead and holds it there. It burns sligthly!"))
+ if(do_after(mendicant, 10 SECONDS, human_victim, IGNORE_SLOWDOWNS, TRUE))
+ read_mind(human_victim)
+ drain_mana()
+ return TRUE
+ else
+ return FALSE
+ else
+ return FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_mind_read/proc/read_mind(mob/living/carbon/human/patient)
+ if(patient.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0))
+ to_chat(owner, span_warning("As you reach into [patient]'s mind, \
+ you are stopped by a mental blockage. It seems you've been foiled."))
+ return
+
+ if(issynthetic(patient) && secondary_school != "Psychokinesis")
+ to_chat(owner, span_notice("I dont know how to work with synths. It's just zeros and ones. How am I supposed to get info out of this metal bucket?"))
+ return
+
+ var/text_to_show = ""
+
+ var/list/recent_speech = patient.copy_recent_speech(copy_amount = 10)
+ if(length(recent_speech))
+ text_to_show += span_boldnotice("You catch some drifting memories of their past conversations...") + "
"
+ for(var/spoken_memory in recent_speech)
+ text_to_show += span_notice("[spoken_memory]") + "
"
+
+ text_to_show += span_notice("You find that their intent is to [patient.combat_mode ? "harm" : "help"]...") + "
"
+ text_to_show += span_notice("You uncover that [patient.p_their()] true identity is [patient.mind.name].") + "
"
+ if(cast_power >= 3)
+ text_to_show += span_notice("You can vaguely read their memories: ") + examine_block(span_italics(get_memories(patient)))
+ text_to_show += span_notice("You try to read their job: ") + examine_block(span_italics(get_job_fluff(patient)))
+ if(patient.mind.enslaved_to || IS_HYPNOTIZED(patient))
+ text_to_show += span_boldnotice("[patient.p_Their()] will is not free.") + "
"
+ var/datum/mind/mind_to_read = patient.mind
+ if(prob(20 * cast_power) && mind_to_read.antag_datums)
+ if(IS_WIZARD(patient))
+ text_to_show += span_notice("You can feel strong potential pulsing in this individual.") + "
"
+ else if(IS_HERETIC(patient))
+ text_to_show += span_notice("Reality bends around you and goes back to normal, as you try to read [patient.p_their()] mind.") + "
"
+ var/mob/living/carbon/human/human_owner = owner
+ human_owner.add_mood_event("gates_of_mansus", /datum/mood_event/gates_of_mansus)
+ else if(IS_CULTIST(patient))
+ text_to_show += span_red("Your mind is assaulted with torrents of blood and gore, as you try to dig deeper.") + "
"
+ else // Там очень много ролей, в том числе не антажных, а мага, еретика и культиста я думаю и без этой способности найти легко. Тем более мы читаем воспоминания, что более имбово
+ text_to_show += span_notice("You also can feel something hidden within [patient.p_their()] mind, but it's not readable.") + "
"
+
+ to_chat(owner, examine_block(span_infoplain(text_to_show)))
+
+// Возвращает размытый текст о профессии
+/datum/action/cooldown/spell/touch/psyonic/psyonic_mind_read/proc/get_job_fluff(mob/living/carbon/human/patient)
+ var/datum/mind/mind_to_read = patient.mind
+ var/datum/job/patient_job = mind_to_read.assigned_role
+ var/text_to_return = ""
+ if(patient_job.departments_bitflags & DEPARTMENT_BITFLAG_SECURITY)
+ text_to_return += "This persons job involves beating up mimes and clowns." + "
"
+ else if(patient_job.departments_bitflags & DEPARTMENT_BITFLAG_CENTRAL_COMMAND)
+ text_to_return += "This persons is a greatest authority on this station." + "
"
+ else if(patient_job.departments_bitflags & DEPARTMENT_BITFLAG_CAPTAIN)
+ text_to_return += "This persons is likely to have megalomania." + "
"
+ else if(patient_job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
+ text_to_return += "This persons calling is commanding others." + "
"
+ else if(patient_job.departments_bitflags & DEPARTMENT_BITFLAG_SERVICE)
+ text_to_return += "This persons labor is about servicing others." + "
"
+ else if(patient_job.departments_bitflags & DEPARTMENT_BITFLAG_CARGO)
+ text_to_return += "This person works physically a lot." + "
"
+ else if(patient_job.departments_bitflags & DEPARTMENT_BITFLAG_ENGINEERING)
+ text_to_return += "This person keeps station alive." + "
"
+ else if(patient_job.departments_bitflags & DEPARTMENT_BITFLAG_SCIENCE)
+ text_to_return += "This person is an egghead." + "
"
+ else if(patient_job.departments_bitflags & DEPARTMENT_BITFLAG_MEDICAL)
+ text_to_return += "This person is accustomed with wounds, blood and etc." + "
"
+ else if(patient_job.departments_bitflags & DEPARTMENT_BITFLAG_SILICON)
+ text_to_return += "This is en etenral mankinds servant." + "
"
+ else if(patient_job.departments_bitflags & DEPARTMENT_BITFLAG_ASSISTANT)
+ text_to_return += "This persons mind reeks of freedom." + "
"
+ else
+ text_to_return += "This person is truly free. They are not obligated with any duties." + "
"
+
+ return span_notice(text_to_return)
+
+// Возвращает воспоминания разума. Имба против таторов, так как там хранится код от аплинка. А ну и банковский айди.
+/datum/action/cooldown/spell/touch/psyonic/psyonic_mind_read/proc/get_memories(mob/living/carbon/human/patient)
+ var/datum/mind/mind_to_read = patient.mind
+ if(mind_to_read)
+ var/itogo_text = ""
+ for(var/key in mind_to_read.memories)
+ var/datum/memory/mem = mind_to_read.memories[key]
+ itogo_text += mem.name + "
"
+ if(itogo_text == "")
+ itogo_text = "[patient.p_Their()] head is empty."
+ return itogo_text
+ else
+ return "I cant read [patient.p_their()] memories. Maybe there are none?" + "
"
+
+// Stun batong на минималках. Исчезает после одного удара
+/datum/action/cooldown/spell/touch/psyonic/psyonic_agony
+ name = "Psyonic Agony"
+ desc = "Deals pain."
+ button_icon = 'icons/obj/weapons/baton.dmi'
+ button_icon_state = "stunbaton_active"
+ cooldown_time = 0.5 SECONDS
+ mana_cost = 40
+ stamina_cost = 0
+ hand_path = /obj/item/melee/touch_attack/psyonic_mending
+ draw_message = span_notice("You ready your hand to deal pain.")
+ drop_message = span_notice("You lower your hand.")
+ can_cast_on_self = TRUE // Упс :)
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_agony/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/mendicant)
+ if(ishuman(victim))
+ var/mob/living/carbon/human/human_victim = victim
+ if(human_victim.is_wearing_tinfoil_hat())
+ to_chat(human_victim, span_clockred("Your tinfoil hat vibrates, protecting your brain from some kind of invisible rays!"))
+ to_chat(owner, span_clockred("As soon as you touch [human_victim], your own body hurts as hell! Looks like the tinfoil hat on their head is interfering."))
+ psyonic_attack(owner)
+ drain_mana()
+ return TRUE
+ if(human_victim.can_block_magic(antimagic_flags))
+ to_chat(human_victim, span_notice("Psionic nearby tries to attack you, but fails."))
+ to_chat(owner, span_notice("You can't attack them. They have some kind of protection."))
+ return FALSE
+ if(issynthetic(human_victim) && secondary_school != "Psychokinesis")
+ human_victim.visible_message(span_danger("[owner] slaps [human_victim] with his hand. Nothing happens. Wow!"),
+ span_warning("You slap [human_victim], but nothing happens. You cannot transfer your energy through metal."),
+ blind_message = span_hear("You hear a slap."))
+ return FALSE
+ else
+ to_chat(human_victim, span_warning("Pain floods your body as soon as [owner] touches you!."))
+ psyonic_attack(human_victim)
+ log_combat(owner, human_victim, "psyonically stunned")
+ drain_mana()
+ return TRUE
+ else
+ return FALSE
+
+// Прок удара
+/datum/action/cooldown/spell/touch/psyonic/psyonic_agony/proc/psyonic_attack(mob/living/carbon/human/patient)
+ patient.apply_damage(35, STAMINA) // Стандартный стан батонг
+ addtimer(CALLBACK(src, PROC_REF(apply_stun_effect), patient), 2 SECONDS)
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_agony/proc/apply_stun_effect(mob/living/carbon/human/patient)
+ patient.visible_message(span_danger("[owner] slaps [patient] with his hand, sparks flying out of it!"),
+ span_warning("You slap [patient], stunning him."),
+ blind_message = span_hear("You hear a slap and an electrical crackling afterwards."))
+ var/trait_check = HAS_TRAIT(patient, TRAIT_BATON_RESISTANCE) //var since we check it in out to_chat as well as determine stun duration
+ if(!patient.IsKnockdown())
+ to_chat(patient, span_warning("Your muscles seize, making you collapse[trait_check ? ", but your body quickly recovers..." : "!"]"))
+
+ if(!trait_check)
+ patient.Knockdown((cast_power/2) SECONDS)
+
+// Станит на непродолжительный срок(~0.5 сек) и заставляет выкинуть вещи из рук
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_spasm
+ name = "Psyonic Spasm"
+ desc = "Activate neurons in victims mucles, briefly stunning them and forcing to drop everything in their hands. Can be cast over distance. Silent."
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
+ button_icon_state = "spasm"
+ cooldown_time = 1 SECONDS
+ mana_cost = 40
+ target_msg = "Your muscles spasm!"
+ active_msg = "You prepare to stun a target..."
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_spasm/New(Target)
+ . = ..()
+ if(secondary_school == "Energistics")
+ cast_power += 1
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_spasm/is_valid_target(atom/cast_on)
+ if(!ishuman(cast_on))
+ return FALSE
+ if(issynthetic(cast_on) && secondary_school != "Psychokinesis")
+ to_chat(owner, span_notice("I dont know how to work with synths."))
+ return FALSE
+
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_spasm/cast(mob/living/carbon/human/cast_on)
+ . = ..()
+ if(cast_on.is_wearing_tinfoil_hat())
+ to_chat(cast_on, span_clockred("Your tinfoil hat vibrates, protecting your brain from some kind of invisible rays!"))
+ to_chat(owner, span_clockred("As soon as you try to spasm [cast_on], your own body twitches! Looks like the tinfoil hat on their head is interfering."))
+ drain_mana()
+ stun(owner)
+ return TRUE
+ if(cast_on.can_block_magic(antimagic_flags))
+ to_chat(cast_on, span_warning("Your body is assaulted with psyonic energy!"))
+ else
+ to_chat(cast_on, span_warning(target_msg))
+ log_combat(owner, cast_on, "psyonically spasmed")
+ stun(cast_on)
+ drain_mana()
+ return TRUE
+
+// Сам стан
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_spasm/proc/stun(mob/living/carbon/human/cast_on)
+ cast_on.Stun(0.2 SECONDS * cast_power)
+
+/**
+ * Гипнотизирует игрока заданной фразой и даёт брейнтравму с ней
+ *
+ * Условия:
+ * * 30 секунд ожидания
+ * * в агрограбе
+ * * без движения жертвы или псионика
+ */
+/datum/action/cooldown/spell/touch/psyonic/psyonic_hypnosis
+ name = "Psyonic Hypnosis"
+ desc = "Implant a looping pattern into victims head."
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
+ button_icon_state = "hypno"
+ cooldown_time = 10 SECONDS
+
+ mana_cost = 25 // Стоит немного
+ stamina_cost = 50 // Но выматывет
+ target_msg = "Your get a headache."
+
+ hand_path = /obj/item/melee/touch_attack/psyonic_mending
+ draw_message = span_notice("You ready your hand to hypnotize a victim.")
+ drop_message = span_notice("You lower your hand.")
+ can_cast_on_self = FALSE // No
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_hypnosis/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/mendicant)
+ if(ishuman(victim) && mendicant.grab_state == GRAB_AGGRESSIVE && mendicant.pulling == victim)
+ var/mob/living/carbon/human/human_victim = victim
+ if(human_victim.is_wearing_tinfoil_hat())
+ to_chat(human_victim, span_clockred("Your tinfoil hat vibrates, protecting your brain from some kind of invisible rays!"))
+ to_chat(owner, span_clockred("As soon as you touch [human_victim]s head, you feel incredibly sleepy! Looks like the tinfoil hat on their head is interfering."))
+ drain_mana()
+ addtimer(CALLBACK(owner, TYPE_PROC_REF(/mob/living, Stun), 60, TRUE, TRUE), 15)
+ return TRUE
+ if(HAS_MIND_TRAIT(human_victim, TRAIT_UNCONVERTABLE)) // Не работает на людей с МЩ
+ to_chat(owner, span_warning("Victims mind is too strong for you to penetrate."))
+ return FALSE
+ if(human_victim.can_block_magic(antimagic_flags))
+ to_chat(human_victim, span_boldwarning("Psionic nearby tries to hypnotize you!"))
+ else
+ to_chat(human_victim, span_warning(target_msg))
+ owner.visible_message(span_warning("[owner] firmly grabs [victim]s and begins creepely staring onto them."),
+ span_notice("You grab [victim]s head and begin implanting a thought into them."))
+ var/player_input = tgui_input_text(mendicant, "Hypnophrase", "Input the hypnophrase", max_length = MAX_MESSAGE_LEN)
+ if(!player_input)
+ return FALSE
+ if(do_after(mendicant, (10 - cast_power) SECONDS, human_victim, IGNORE_SLOWDOWNS, TRUE))
+ hypnotize(human_victim, player_input)
+ else
+ to_chat(owner, span_warning("You failed to hypnotize the victim."))
+ drain_mana()
+ return TRUE
+ else
+ to_chat(owner, span_notice("You need to grab a human in aggressive grab to hypnotize them."))
+ return FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_hypnosis/proc/hypnotize(mob/living/carbon/human/patient, hypnophrase)
+ patient.cure_trauma_type(/datum/brain_trauma/hypnosis, TRAUMA_RESILIENCE_SURGERY)
+
+ owner.log_message("hypnotised [key_name(patient)] with the phrase '[hypnophrase]'", LOG_ATTACK, color="red")
+
+ patient.log_message("has been hypnotised by the phrase '[hypnophrase]' spoken by [key_name(owner)]", LOG_VICTIM, color="orange", log_globally = FALSE)
+
+ addtimer(CALLBACK(patient, TYPE_PROC_REF(/mob/living/carbon, gain_trauma), /datum/brain_trauma/hypnosis, TRAUMA_RESILIENCE_SURGERY, hypnophrase), 1 SECONDS)
+ addtimer(CALLBACK(patient, TYPE_PROC_REF(/mob/living, Stun), 60, TRUE, TRUE), 15)
+
+// Ослепляет цель на дистанции на ~15 секунд. Способность максимального уровня
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_blind
+ name = "Psyonic Blind"
+ desc = "Interfere with the way neuron signals are transmitted in the victims eyes."
+ button_icon_state = "blind"
+ ranged_mousepointer = 'icons/effects/mouse_pointers/blind_target.dmi'
+ cooldown_time = 1 SECONDS
+ mana_cost = 60
+ target_msg = "You eyes hurt!"
+ active_msg = "You prepare to blind a target..."
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_blind/is_valid_target(atom/cast_on)
+ if(!ishuman(cast_on))
+ return FALSE
+ else
+ var/mob/living/carbon/human/victim = cast_on
+ if(victim.is_blind())
+ to_chat(owner, span_notice("[victim] is already blind."))
+ return FALSE
+ if(issynthetic(cast_on) && secondary_school != "Psychokinesis")
+ to_chat(owner, span_notice("I dont know how to work with synths."))
+ return FALSE
+
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_blind/cast(mob/living/carbon/human/cast_on)
+ . = ..()
+ if(cast_on.is_wearing_tinfoil_hat())
+ to_chat(cast_on, span_clockred("Your tinfoil hat vibrates, protecting your brain from some kind of invisible rays!"))
+ to_chat(owner, span_clockred("As soon as you try to blind [cast_on], your own eyes close on its own! Looks like the tinfoil hat on their head is interfering."))
+ drain_mana()
+ blind(owner)
+ return TRUE
+ if(cast_on.can_block_magic(antimagic_flags))
+ to_chat(cast_on, span_warning("Your eyes are burned with psyonic energy!"))
+ else
+ to_chat(cast_on, span_warning(target_msg))
+ log_combat(owner, cast_on, "psyonically blinded")
+ blind(cast_on)
+ drain_mana()
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_blind/proc/blind(mob/living/carbon/human/cast_on)
+ cast_on.adjust_temp_blindness( (10 + cast_power * 2) SECONDS)
+
+#undef IS_HYPNOTIZED
diff --git a/tff_modular/modules/psyonics/code/cyberimp.dm b/tff_modular/modules/psyonics/code/cyberimp.dm
new file mode 100644
index 00000000000..130ade04a83
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/cyberimp.dm
@@ -0,0 +1,40 @@
+#define ORGAN_SLOT_BRAIN_PSYONIC "brain_psyonic"
+
+// Не позволяет мане регенерироваться
+/obj/item/organ/internal/cyberimp/brain/anti_psyonic
+ name = "Psyonic Amplifier Model N"
+ desc = "This implant will prohibit psyonics from regenereting their energy."
+ icon_state = "brain_implant_rebooter"
+ slot = ORGAN_SLOT_BRAIN_PSYONIC
+
+/obj/item/organ/internal/cyberimp/brain/anti_psyonic/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
+ . = ..()
+ ADD_TRAIT(organ_owner, TRAIT_NO_PSYONICS, IMPLANT_TRAIT)
+
+/obj/item/organ/internal/cyberimp/brain/anti_psyonic/on_mob_remove(mob/living/carbon/organ_owner, special)
+ . = ..()
+ REMOVE_TRAIT(organ_owner, TRAIT_NO_PSYONICS, IMPLANT_TRAIT)
+
+// Увеличивает реген маны в 2 раза
+/obj/item/organ/internal/cyberimp/brain/pro_psyonic
+ name = "Psyonic Amplifier Model A"
+ desc = "This implant will boost psyonics energy regeneration."
+ icon_state = "brain_implant_rebooter"
+ slot = ORGAN_SLOT_BRAIN_PSYONIC
+
+/obj/item/organ/internal/cyberimp/brain/pro_psyonic/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
+ . = ..()
+ ADD_TRAIT(organ_owner, TRAIT_PRO_PSYONICS, IMPLANT_TRAIT)
+
+/obj/item/organ/internal/cyberimp/brain/pro_psyonic/on_mob_remove(mob/living/carbon/organ_owner, special)
+ . = ..()
+ REMOVE_TRAIT(organ_owner, TRAIT_PRO_PSYONICS, IMPLANT_TRAIT)
+
+/datum/supply_pack/medical/psyonic_implants
+ name = "Psyonic Implants"
+ desc = "A crate containing two experimental psyonic implants, which work ONLY on psyonic users. No warranty."
+ cost = CARGO_CRATE_VALUE * 5
+ contains = list(/obj/item/organ/internal/cyberimp/brain/anti_psyonic = 1,
+ /obj/item/organ/internal/cyberimp/brain/pro_psyonic = 1)
+ crate_name = "Psyonic implant crate"
+ discountable = SUPPLY_PACK_RARE_DISCOUNTABLE
diff --git a/tff_modular/modules/psyonics/code/documents.dm b/tff_modular/modules/psyonics/code/documents.dm
new file mode 100644
index 00000000000..81a6386c6ac
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/documents.dm
@@ -0,0 +1,75 @@
+/obj/item/card/psyonic_license
+ name = "psyonic license"
+ desc = "An official license given to psyonic users by the NanoTrasen Psyonics and Eugenics Division itself."
+ icon = 'tff_modular/modules/psyonics/icons/card.dmi'
+ icon_state = "card_psy"
+ inhand_icon_state = "card-id"
+ lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi'
+ w_class = WEIGHT_CLASS_TINY
+ pickup_sound = 'sound/items/handling/id_card/id_card_pickup1.ogg'
+ drop_sound = 'sound/items/handling/id_card/id_card_drop1.ogg'
+ sound_vary = TRUE
+ resistance_flags = FIRE_PROOF
+ var/datum/psyonic_licence_datum/owner_info
+
+/obj/item/card/psyonic_license/New(mob/living/carbon/human/owner)
+ . = ..()
+ owner_info = new(owner)
+
+/obj/item/card/psyonic_license/ui_interact(mob/user, datum/tgui/ui)
+ if(!owner_info)
+ balloon_alert(user, "The card isn't bound to anyone!")
+ return
+
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "PsyonicLicense")
+ ui.set_autoupdate(FALSE)
+ ui.open()
+
+/obj/item/card/psyonic_license/ui_static_data(mob/user)
+ var/list/data = list()
+
+ data["primary_school"] = owner_info.primary_school
+ data["secondary_school"] = owner_info.secondary_school
+ data["psyonic_level"] = owner_info.psyonic_level
+ data["owner_name"] = owner_info.owner_name
+ data["owner_age"] = owner_info.owner_age
+ data["owner_preview"] = owner_info.owner_preview
+ data["owner_species"] = owner_info.owner_species
+
+ return data
+
+/datum/psyonic_licence_datum
+ var/datum/weakref/original_owner
+ var/owner_name
+ var/owner_age
+ var/psyonic_level
+ var/primary_school
+ var/secondary_school
+ var/owner_species
+ var/icon/owner_preview
+
+/datum/psyonic_licence_datum/New(mob/living/carbon/human/human_owner)
+ . = ..()
+ original_owner = WEAKREF(human_owner)
+ if(original_owner && original_owner.resolve())
+ var/mob/living/carbon/human/owner = original_owner.resolve()
+ if(!istype(owner, /mob/living/carbon/human))
+ return
+ if(!owner.ispsyonic())
+ return
+ var/datum/quirk/psyonic/quirk_holder = owner.get_quirk(/datum/quirk/psyonic)
+ psyonic_level = quirk_holder.psyonic_level_string
+ primary_school = quirk_holder.school
+ secondary_school = quirk_holder.secondary_school
+ owner_name = owner.real_name
+ owner_age = owner.age
+
+ if (!owner.dna.species.lore_protected && owner.dna.features["custom_species"])
+ owner_species = "[owner.dna.features["custom_species"]]"
+ else
+ owner_species = "[owner.dna.species.name]"
+
+ owner_preview = icon2base64(getFlatIcon(owner, SOUTH, no_anim = TRUE))
diff --git a/tff_modular/modules/psyonics/code/energistics.dm b/tff_modular/modules/psyonics/code/energistics.dm
new file mode 100644
index 00000000000..37b47cd7ed2
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/energistics.dm
@@ -0,0 +1,203 @@
+/// Школа энергетики. 6 спеллов
+/// Spark - создаёт искры в указанном месте
+/// Discharge - разряжает АПЦ/Батарейку. Даёт ману в зависимости от кол-ва энергии
+/// Laser - стрелеят концентрированным пучком фотонов, пусть и не самым сильным.
+/// Distrupt - создаёт ЭМИ с небольшим радиусом.
+/// Elecrocute - добавляет мутацию shock touch
+/// Freeze - заковывает моба в лёд на небольшой промежуток.
+
+// Добавить школу внушения
+/mob/living/carbon/human/proc/try_add_energistics_school(tier = 0, additional_school = 0)
+ if(tier >= 0)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/touch/psyonic/psyonic_discharge(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ var/datum/action/new_action2 = new /datum/action/cooldown/spell/pointed/psyonic/psyonic_spark(src.mind || src, tier, additional_school)
+ new_action2.Grant(src)
+ if(tier >= 1)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/basic_projectile/psyonic_laser(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 2)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/touch/psyonic/psyonic_emp(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 3)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/psyonic/psionic_electrocute(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 4)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/pointed/projectile/psyonic/psyonic_freeze(src.mind || src)
+ new_action.Grant(src)
+
+// Разрядка АПЦ или батареек в обмен на ману
+/datum/action/cooldown/spell/touch/psyonic/psyonic_discharge
+ name = "Psyonic Discharge"
+ desc = "Try to discharge battery and convert electricity into raw psyonic energy."
+ button_icon = 'modular_nova/modules/aesthetics/cells/cell.dmi'
+ button_icon_state = "icell"
+ cooldown_time = 30 SECONDS
+ mana_cost = 0
+ stamina_cost = 15
+ hand_path = /obj/item/melee/touch_attack/psyonic_mending
+ draw_message = span_notice("You ready your hand to discharge an energy source.")
+ drop_message = span_notice("You lower your hand.")
+ can_cast_on_self = FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_discharge/is_valid_target(atom/cast_on)
+ return isatom(cast_on)
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_discharge/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/mendicant)
+ if(HAS_TRAIT(mendicant, TRAIT_MINDSHIELD)) // Womp womp
+ to_chat(mendicant, span_warning("As soon as you touch [victim], your energy dissipates without a trace. Mindshield implant messes up your concentration."))
+ return FALSE
+ if(istype(victim, /obj/item/stock_parts/power_store) || istype(victim, /obj/machinery/power/apc))
+ owner.visible_message(span_warning("[owner] presses his hands against [victim]."),
+ span_notice("You press your hands against [victim]."),
+ blind_message = span_hear("You hear electrical crackling."))
+ if(do_after(mendicant, 2.5 SECONDS, victim, IGNORE_SLOWDOWNS, TRUE))
+ var/datum/quirk/psyonic/quirk_holder = mendicant.get_quirk(/datum/quirk/psyonic)
+ if(!quirk_holder)
+ return FALSE
+ if(istype(victim, /obj/item/stock_parts/power_store))
+ var/obj/item/stock_parts/power_store/batt = victim
+ var/to_charge = (batt.charge / STANDARD_CELL_VALUE)
+ batt.use(batt.charge(), TRUE)
+ quirk_holder.mana_level = clamp(quirk_holder.mana_level + to_charge, 0, quirk_holder.max_mana)
+ else if(istype(victim, /obj/machinery/power/apc))
+ var/obj/machinery/power/apc/target_apc = victim
+ var/obj/item/stock_parts/power_store/batt = target_apc.cell
+ if(!batt)
+ to_chat(owner, span_notice("There is no battery in this APC."))
+ return FALSE
+ var/to_charge = (batt.charge() / (STANDARD_BATTERY_CHARGE/10))
+ batt.use(batt.charge(), TRUE)
+ quirk_holder.mana_level = clamp(quirk_holder.mana_level + to_charge, 0, quirk_holder.max_mana)
+ else
+ to_chat(owner, span_notice("You've failed to discharge energy."))
+ return TRUE
+ else
+ return FALSE
+
+// Создаёт искры в указанном месте
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_spark
+ name = "Psyonic Spark"
+ desc = "Cause some sparks to appear at a place of your choice."
+ button_icon = 'icons/effects/effects.dmi'
+ button_icon_state = "blessed"
+ cooldown_time = 1 SECONDS
+ mana_cost = 10
+ active_msg = "You prepare to create sparks..."
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_spark/is_valid_target(atom/cast_on)
+ if(!isturf(cast_on))
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_spark/cast(turf/cast_on)
+ . = ..()
+ var/mob/living/carbon/human/caster = owner
+ caster.emote_snap()
+ var/datum/effect_system/spark_spread/sparks = new
+ sparks.set_up(5, 1, cast_on)
+ sparks.attach(cast_on)
+ sparks.start()
+ drain_mana()
+ return TRUE
+
+// Стреляет по направлению куклы псионика фотонной пушкой. Считайте аналог флешки
+/datum/action/cooldown/spell/basic_projectile/psyonic_laser
+ name = "Photon Laser"
+ desc = "Channels psyonic energy into a weak concentrated photon laser."
+ button_icon = 'icons/obj/weapons/guns/projectiles.dmi'
+ button_icon_state = "solarflare"
+ cooldown_time = 0 SECONDS
+ spell_requirements = NONE
+ mana_cost = 10
+ projectile_type = /obj/projectile/energy/photon
+
+/datum/action/cooldown/spell/basic_projectile/psyonic_laser/cast(atom/cast_on)
+ var/mob/living/carbon/human/caster = owner
+ var/datum/quirk/psyonic/quirk_holder = caster.get_quirk(/datum/quirk/psyonic)
+ if(!(quirk_holder && (quirk_holder.mana_level - mana_cost) >= 0))
+ return FALSE
+ else
+ quirk_holder.mana_level -= mana_cost
+ ..()
+
+// Создаёт ЕМП в месте удара руки
+/datum/action/cooldown/spell/touch/psyonic/psyonic_emp
+ name = "Psyonic EMP"
+ desc = "Try to cause a small local EMP."
+ button_icon = 'icons/obj/weapons/grenade.dmi'
+ button_icon_state = "emp"
+ cooldown_time = 15 SECONDS
+ mana_cost = 40
+ stamina_cost = 40
+ hand_path = /obj/item/melee/touch_attack/psyonic_mending
+ draw_message = span_notice("You ready your hand to cause an EMP.")
+ drop_message = span_notice("You lower your hand.")
+ can_cast_on_self = TRUE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_emp/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/mendicant)
+ if(isatom(victim))
+ empulse(victim, 1, cast_power/2)
+ drain_mana()
+ return TRUE
+ else
+ return FALSE
+
+// Даёт мутацию Shock Touch
+/datum/action/cooldown/spell/psyonic/psionic_electrocute
+ name = "Psyonic Shock Touch"
+ desc = "Force yourself to recieve shock touch mutation."
+ cooldown_time = 60 SECONDS
+ mana_cost = 60
+ stamina_cost = 60
+
+/datum/action/cooldown/spell/psyonic/psionic_electrocute/is_valid_target(atom/cast_on)
+ return !issynthetic(cast_on)
+
+/datum/action/cooldown/spell/psyonic/psionic_electrocute/cast(mob/living/cast_on)
+ . = ..()
+ if(!ishuman(cast_on))
+ return FALSE
+ var/mob/living/carbon/human/to_mutate = cast_on
+ if(!to_mutate.can_mutate())
+ return FALSE
+ to_mutate.dna.add_mutation(/datum/mutation/human/shock, MUT_OTHER)
+ drain_mana()
+ return TRUE
+
+// Стреляет снарядом вотчера, замораживая жертву. Требует почти максимально возможный запас маны
+/datum/action/cooldown/spell/pointed/projectile/psyonic/psyonic_freeze
+ name = "Psyonic Freeze"
+ desc = "Fire freezing shark at a target, encasing them in an ice prison."
+ button_icon = 'icons/effects/freeze.dmi'
+ button_icon_state = "ice_cube"
+ cooldown_time = 1 SECONDS
+ mana_cost = 80
+ cast_range = 9
+ active_msg = "You prepare to fire ice shard..."
+ deactive_msg = "You relax."
+ projectile_type = /obj/projectile/temp/watcher/psyonic_freeze
+
+/datum/action/cooldown/spell/pointed/projectile/psyonic/psyonic_freeze/is_valid_target(atom/cast_on)
+ if(!isliving(cast_on))
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/projectile/psyonic/psyonic_freeze/cast(mob/living/cast_on)
+ drain_mana()
+ . = ..()
+ return TRUE
+
+// Вывел в отдельный тип, потому что в оригинальном ice_wing снаряде видимо баг(?) и он не замораживает, хотя должен.
+/obj/projectile/temp/watcher/psyonic_freeze
+ name = "freezing blast"
+ damage = 0 // Нет дамага, вместо этого замораживает
+
+/obj/projectile/temp/watcher/psyonic_freeze/apply_status(mob/living/target)
+ if(HAS_TRAIT(target, TRAIT_RESISTCOLD)) // Вот тут у ice_wing лишний !
+ return
+ target.apply_status_effect(/datum/status_effect/freon/watcher/psyonic_freeze)
+
+/datum/status_effect/freon/watcher/psyonic_freeze
+ duration = 4 // 4 секунды вместо 8
+ can_melt = TRUE
diff --git a/tff_modular/modules/psyonics/code/psychokinesis.dm b/tff_modular/modules/psyonics/code/psychokinesis.dm
new file mode 100644
index 00000000000..802aa52f9eb
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/psychokinesis.dm
@@ -0,0 +1,293 @@
+/// Школа психокинетики
+/// Имеет 6 спеллов.
+/// Psi lighter - создаёт миниатюрный огонёк на кончиках пальцев. Работает как зажигалка.
+/// Psi blade - создаёт в руке пси-клинок. Урон увеличивается в зависимости от уровня.
+/// Psi tool - создаёт в руке универсальный инструмент.
+/// Tinker - чинит integrity чего бы то ни было.
+/// Psyforce - даёт "клешни жизни" для вскрытия дверей
+/// Telekinesis - даёт мутацию телекинеза.
+
+// Добавляет школу психокинетики
+/mob/living/carbon/human/proc/try_add_psychokinesis_school(tier = 0, additional_school = 0)
+ if(tier >= 0)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/conjure_item/psyonic/psilighter(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 1)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/conjure_item/psyonic/psiblade(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 2)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/conjure_item/psyonic/psitool(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ var/datum/action/new_action2 = new /datum/action/cooldown/spell/touch/psyonic/psyonic_tinker(src.mind || src, tier, additional_school)
+ new_action2.Grant(src)
+ if(tier >= 3)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/touch/psyonic/psyonic_force(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 4)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/psyonic/psionic_telekinesis(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+
+// Спавнит зажигалку в руке. Очень полезно
+/datum/action/cooldown/spell/conjure_item/psyonic/psilighter
+ name = "Psi lighter"
+ desc = "Concentrates psyonic energy to create a small flame in your hand."
+ button_icon = 'icons/obj/cigarettes.dmi'
+ button_icon_state = "match_lit"
+ cooldown_time = 1.5 SECONDS
+ item_type = /obj/item/psyonic_fire
+ mana_cost = 5
+ stamina_cost = 0
+
+// Спавнит пси-клинок в руке. Сила зависит от уровня псионика
+/datum/action/cooldown/spell/conjure_item/psyonic/psiblade
+ name = "Psi blade"
+ desc = "Concentrates psyonic energy to create a sharp blade in your hand."
+ button_icon = 'icons/obj/weapons/transforming_energy.dmi'
+ button_icon_state = "blade"
+ cooldown_time = 1.5 SECONDS
+ item_type = /obj/item/melee/psyonic_blade
+ mana_cost = 40
+ stamina_cost = 0
+
+// Спавнит омни инструмент в руке псионика. Аналог абдукторского
+/datum/action/cooldown/spell/conjure_item/psyonic/psitool
+ name = "Psi tool"
+ desc = "Concentrates psyonic energy to create a universal tool."
+ button_icon = 'icons/obj/antags/abductor.dmi'
+ button_icon_state = "omnitool"
+ cooldown_time = 1.5 SECONDS
+ item_type = /obj/item/psyonic_omnitool
+ mana_cost = 30
+ stamina_cost = 0
+
+/datum/action/cooldown/spell/conjure_item/psyonic/psiblade/New(Target)
+ . = ..()
+ if(secondary_school == "Psychokinesis")
+ cast_power += 1
+
+/datum/action/cooldown/spell/conjure_item/psyonic/psiblade/make_item(atom/caster)
+ var/obj/item/made_item = new item_type(caster.loc, cast_power)
+ LAZYADD(item_refs, WEAKREF(made_item))
+ var/mob/living/carbon/human/caster_pawn = owner
+ caster_pawn.emote_snap()
+ return made_item
+
+// Аналог клешней жизни
+/datum/action/cooldown/spell/touch/psyonic/psyonic_force
+ name = "Psyonic Force"
+ desc = "Concentrates psyonic energy to force a door open."
+ button_icon = 'icons/mob/actions/actions_spells.dmi'
+ button_icon_state = "knock"
+ cooldown_time = 3 SECONDS
+ mana_cost = 50
+ stamina_cost = 50
+ hand_path = /obj/item/melee/touch_attack/psyonic_mending
+ draw_message = span_notice("You ready your hand to force a door open.")
+ drop_message = span_notice("You lower your hand.")
+ can_cast_on_self = FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_force/is_valid_target(atom/cast_on)
+ return istype(cast_on, /obj/machinery/door/airlock)
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_force/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/mendicant)
+ if(isatom(victim))
+ if(istype(victim, /obj/machinery/door/airlock))
+ var/obj/machinery/door/airlock/door_to_force = victim
+ owner.visible_message(span_warning("[owner] targets their hands at [victim], like they are some kind of jedi."),
+ span_notice("You psyonically grab [victim], trying to force it open."))
+ if(do_after(mendicant, 5 SECONDS, victim, IGNORE_SLOWDOWNS, TRUE))
+ force_door_open(door_to_force, mendicant)
+ drain_mana()
+ return TRUE
+ else
+ return FALSE
+ else
+ return FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_force/proc/force_door_open(obj/machinery/door/airlock/door_to_force, mob/living/carbon/user)
+ if(door_to_force.seal)
+ to_chat(user, span_warning("Remove the seal first!"))
+ return
+ if(door_to_force.locked)
+ to_chat(user, span_warning("The airlock's bolts prevent it from being forced!"))
+ return
+ if(door_to_force.welded)
+ to_chat(user, span_warning("It's welded, it won't budge!"))
+ return
+ if(door_to_force.hasPower())
+ if(!door_to_force.density)
+ return
+ if(!door_to_force.prying_so_hard)
+ playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 100, TRUE)
+ door_to_force.prying_so_hard = TRUE
+ door_to_force.open(BYPASS_DOOR_CHECKS)
+ door_to_force.take_damage(25, BRUTE, 0, 0)
+ if(door_to_force.density && !door_to_force.open(BYPASS_DOOR_CHECKS))
+ to_chat(user, span_warning("Despite your attempts, [src] refuses to open."))
+ door_to_force.prying_so_hard = FALSE
+ return
+
+// Даёт мутацию телекинеза
+/datum/action/cooldown/spell/psyonic/psionic_telekinesis
+ name = "Telekinesis"
+ desc = "Force yourself to recieve telekinesis mutation."
+ cooldown_time = 60 SECONDS
+ mana_cost = 80
+ stamina_cost = 80
+
+/datum/action/cooldown/spell/psyonic/psionic_telekinesis/is_valid_target(atom/cast_on)
+ return !issynthetic(cast_on)
+
+/datum/action/cooldown/spell/psyonic/psionic_telekinesis/cast(mob/living/cast_on)
+ . = ..()
+ if(!ishuman(cast_on))
+ return FALSE
+ var/mob/living/carbon/human/to_mutate = cast_on
+ if(!to_mutate.can_mutate())
+ return FALSE
+ to_mutate.dna.add_mutation(/datum/mutation/human/telekinesis, MUT_OTHER)
+ drain_mana()
+
+// Восстанавливает Integrity атома. Позволяет чинить многие нечинимые иными способами вещи
+/datum/action/cooldown/spell/touch/psyonic/psyonic_tinker
+ name = "Psyonic Tinker"
+ desc = "Restore somethings condition to its normal state."
+ button_icon = 'icons/obj/tools.dmi'
+ button_icon_state = "wrench"
+ cooldown_time = 3 SECONDS
+ mana_cost = 40
+ stamina_cost = 50
+ hand_path = /obj/item/melee/touch_attack/psyonic_mending
+ draw_message = span_notice("You ready your hand to tinker.")
+ drop_message = span_notice("You lower your hand.")
+ can_cast_on_self = FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_tinker/is_valid_target(atom/cast_on)
+ return cast_on.uses_integrity
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_tinker/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/mendicant)
+ if(isatom(victim))
+ var/atom/to_fix = victim
+ if((to_fix.get_integrity() >= to_fix.max_integrity) || !to_fix.uses_integrity)
+ return FALSE
+ owner.visible_message(span_warning("[owner] presses his hands onto [victim]."),
+ span_notice("You grab [victim], trying to repair it."))
+ if(do_after(mendicant, 6 SECONDS, victim, IGNORE_SLOWDOWNS, TRUE))
+ to_fix.update_integrity(clamp(to_fix.get_integrity()+(50*cast_power), 1, to_fix.max_integrity))
+ drain_mana()
+ return TRUE
+ else
+ return FALSE
+
+/obj/item/melee/psyonic_blade
+ name = "psyonic blade"
+ desc = "A concentrated collection of particles and energy that looks like a swords blade.."
+ icon = 'icons/obj/weapons/transforming_energy.dmi'
+ icon_state = "blade"
+ inhand_icon_state = "blade"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ w_class = WEIGHT_CLASS_HUGE
+ force = 10
+ throwforce = 10
+ hitsound = 'sound/items/weapons/blade1.ogg'
+ attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ sharpness = SHARP_EDGED
+ block_chance = 0
+ item_flags = DROPDEL | ABSTRACT | HAND_ITEM | EXAMINE_SKIP
+ color = COLOR_BRIGHT_BLUE
+
+/obj/item/melee/psyonic_blade/New(loc, power)
+ . = ..()
+ force = 10 + power*1.5
+ block_chance = power*5
+
+/obj/item/psyonic_fire
+ name = "small psyonic fire"
+ desc = "Small bluish fire, that jumps on your fingers and surprisigly doesn't burn them."
+ icon = 'icons/obj/weapons/hand.dmi'
+ icon_state = "greyscale"
+ color = COLOR_BRIGHT_BLUE
+ inhand_icon_state = "greyscale"
+ light_range = 2
+ light_power = 2
+ light_color = LIGHT_COLOR_LIGHT_CYAN
+ light_on = TRUE
+ damtype = BURN
+ force = 5
+ attack_verb_continuous = list("burns", "singes")
+ attack_verb_simple = list("burn", "singe")
+ resistance_flags = FIRE_PROOF
+ w_class = WEIGHT_CLASS_HUGE
+ light_system = OVERLAY_LIGHT
+ toolspeed = 2
+ tool_behaviour = TOOL_WELDER
+ item_flags = DROPDEL | ABSTRACT | HAND_ITEM | EXAMINE_SKIP
+ heat = HIGH_TEMPERATURE_REQUIRED - 100
+
+// Копирка с абдукторского
+/obj/item/psyonic_omnitool
+ name = "psyonic omnitool"
+ desc = "Space Swiss Army Knife, able to shapeshift itself to fulfill psyonics needs."
+ icon = 'icons/obj/antags/abductor.dmi'
+ lefthand_file = 'icons/mob/inhands/antag/abductor_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/antag/abductor_righthand.dmi'
+ icon_state = "omnitool"
+ inhand_icon_state = "silencer"
+ toolspeed = 1
+ tool_behaviour = TOOL_SCREWDRIVER
+ color = COLOR_BRIGHT_BLUE
+ usesound = 'sound/items/pshoom/pshoom.ogg'
+ var/list/tool_list = list()
+ item_flags = DROPDEL | ABSTRACT | HAND_ITEM | EXAMINE_SKIP
+
+/obj/item/psyonic_omnitool/New(loc)
+ . = ..()
+ tool_list = list(
+ "Crowbar" = image(icon = 'icons/obj/tools.dmi', icon_state = "crowbar"),
+ "Multitool" = image(icon = 'icons/obj/devices/tool.dmi', icon_state = "multitool"),
+ "Screwdriver" = image(icon = 'icons/obj/tools.dmi', icon_state = "screwdriver_map"),
+ "Wirecutters" = image(icon = 'icons/obj/tools.dmi', icon_state = "cutters_map"),
+ "Wrench" = image(icon = 'icons/obj/tools.dmi', icon_state = "wrench"),
+ )
+
+/obj/item/psyonic_omnitool/get_all_tool_behaviours()
+ return list(
+ TOOL_CROWBAR,
+ TOOL_MULTITOOL,
+ TOOL_SCREWDRIVER,
+ TOOL_WIRECUTTER,
+ TOOL_WRENCH,
+ )
+
+/obj/item/psyonic_omnitool/examine()
+ . = ..()
+ . += " The mode is: [tool_behaviour]"
+
+/obj/item/psyonic_omnitool/attack_self(mob/user)
+ if(!user)
+ return
+
+ var/tool_result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE, tooltips = TRUE)
+ if(!check_menu(user))
+ return
+ switch(tool_result)
+ if("Crowbar")
+ tool_behaviour = TOOL_CROWBAR
+ if("Multitool")
+ tool_behaviour = TOOL_MULTITOOL
+ if("Screwdriver")
+ tool_behaviour = TOOL_SCREWDRIVER
+ if("Wirecutters")
+ tool_behaviour = TOOL_WIRECUTTER
+ if("Wrench")
+ tool_behaviour = TOOL_WRENCH
+
+/obj/item/psyonic_omnitool/proc/check_menu(mob/user)
+ if(!istype(user))
+ return FALSE
+ if(user.incapacitated || !user.Adjacent(src))
+ return FALSE
+ return TRUE
+
diff --git a/tff_modular/modules/psyonics/code/redaction.dm b/tff_modular/modules/psyonics/code/redaction.dm
new file mode 100644
index 00000000000..9c797baa0a8
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/redaction.dm
@@ -0,0 +1,379 @@
+#define HALFWAYCRITDEATH ((HEALTH_THRESHOLD_CRIT + HEALTH_THRESHOLD_DEAD) * 0.5)
+
+/// Школа лечения
+/// Имеет 5 спеллов в данный момент
+/// Roentgen - обычный мед скан, работающий на дистанции
+/// Меnding - лечит кровь, открытые раны и окси урон. Также удаляет импланты/ксеноморфов из тела при определённых условиях.
+/// Ethanol Synthesis - если интент на харма, то "превращает" упитанность в алкоголь на дистанции. Любой другой - наоборот.
+/// Cleansing - лечит токс урон
+/// Revive - пытается оживить труп
+
+// Выдать школу лечения
+/mob/living/carbon/human/proc/try_add_redaction_school(tier = 0, additional_school = 0)
+ if(tier >= 0)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/pointed/psyonic/psyonic_roentgen(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 1)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/touch/psyonic/psyonic_mending(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 2)
+ var/datum/action/new_action2 = new /datum/action/cooldown/spell/pointed/psyonic/psyonic_drunkness(src.mind || src, tier, additional_school)
+ new_action2.Grant(src)
+ if(tier >= 3)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/touch/psyonic/psyonic_cleansing(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 4)
+ var/datum/action/new_action = new /datum/action/cooldown/spell/touch/psyonic/psyonic_revival(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+
+// Мед сканер на расстоянии
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_roentgen
+ name = "Roentgen"
+ desc = "Try to read target's vital energy and determine their state."
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
+ button_icon_state = "roentgen"
+
+ cooldown_time = 1 SECONDS
+
+ mana_cost = 10
+ target_msg = "You feel like someone is looking deep into you."
+
+ active_msg = "You prepare to scan a target..."
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_roentgen/New(Target)
+ . = ..()
+ if(secondary_school == "Redaction")
+ cast_power += 1
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_roentgen/is_valid_target(atom/cast_on)
+ if(!ishuman(cast_on))
+ return FALSE
+
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_roentgen/cast(mob/living/carbon/human/cast_on)
+ . = ..()
+ if(cast_on.can_block_magic(antimagic_flags))
+ to_chat(cast_on, span_notice("Your body is being read by a psyonic nearby."))
+ else
+ to_chat(cast_on, span_warning(target_msg))
+ if(cast_power > 2)
+ healthscan(owner, cast_on, SCANNER_VERBOSE, TRUE, tochat = TRUE)
+ else
+ healthscan(owner, cast_on, SCANNER_VERBOSE, FALSE, tochat = TRUE)
+ drain_mana()
+ return TRUE
+
+/obj/item/melee/touch_attack/psyonic_mending
+ name = "psyonic sparks"
+ desc = "Concentrated psyonic energy in a hand."
+ icon = 'icons/obj/weapons/hand.dmi'
+ icon_state = "greyscale"
+ color = COLOR_VERY_PALE_LIME_GREEN
+ inhand_icon_state = "greyscale"
+ light_range = 2
+ light_power = 1
+ light_color = LIGHT_COLOR_LIGHT_CYAN
+ light_on = TRUE
+
+// Восстанавливает кровь, окси урон, открытые травмы. Не лечит другие типы урона. Если вторичка - психокинетика, то вынимает импланты.
+// Если уровень Эпсилон - удаляет лярвы ксеноморфов.
+/datum/action/cooldown/spell/touch/psyonic/psyonic_mending
+ name = "Psyonic Mending"
+ desc = "You can try to restore patients bloodloss, bones, open wounds and partially oxygen level in blood. Does not heal brute, burn, \
+ and toxic damage. With Psychokinesis as secondary school also can remove small implants. At Epsilon level can remove xenomorph larvae."
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
+ button_icon_state = "mending_touch"
+ cooldown_time = 3 SECONDS
+ mana_cost = 25
+ stamina_cost = 25
+ target_msg = "You body numbs a little."
+ hand_path = /obj/item/melee/touch_attack/psyonic_mending
+ draw_message = span_notice("You ready your hand to mend a patient.")
+ drop_message = span_notice("You lower your hand.")
+ can_cast_on_self = TRUE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_mending/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/mendicant)
+ if(ishuman(victim))
+ var/mob/living/carbon/human/human_victim = victim
+ if(issynthetic(human_victim) && secondary_school != "Psychokinesis")
+ to_chat(owner, span_notice("I dont know how to work with synths."))
+ return FALSE
+ if(human_victim.can_block_magic(antimagic_flags))
+ to_chat(human_victim, span_notice("Psionic nearby tries to mend you."))
+ else
+ to_chat(human_victim, span_warning(target_msg))
+ if(!do_after(mendicant, 5 SECONDS, human_victim, IGNORE_SLOWDOWNS, TRUE))
+ accident_harm(human_victim)
+ else
+ try_heal_all(human_victim)
+ drain_mana()
+ return TRUE
+ else
+ return FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_mending/proc/accident_harm(mob/living/carbon/human/patient)
+ patient.take_bodypart_damage(5, wound_bonus = 100)
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_mending/proc/try_heal_all(mob/living/carbon/human/patient)
+ if(patient.blood_volume < BLOOD_VOLUME_NORMAL)
+ patient.blood_volume += ((BLOOD_VOLUME_NORMAL - patient.blood_volume) / 5) * cast_power // Эффективнее когда крови мало
+
+ if(patient.all_wounds)
+ var/datum/wound/wound2fix = patient.all_wounds[1]
+ wound2fix.remove_wound()
+ playsound(patient, 'sound/effects/wounds/crack2.ogg', 40, TRUE)
+
+ if(patient.getOxyLoss() >= OXYLOSS_PASSOUT_THRESHOLD-10)
+ patient.adjustOxyLoss(-cast_power*5, forced = TRUE)
+
+ if(patient.implants && secondary_school == "Psychokinesis" && cast_power >= 2) // Невольно удаляет импланты, если есть
+ var/obj/item/implant/imp_2_del = pick(patient.implants)
+ var/atom/drop_loc = imp_2_del.drop_location()
+ imp_2_del.removed(patient)
+ if(drop_loc)
+ imp_2_del.forceMove(drop_loc)
+ patient.visible_message(
+ span_warning("[patient]s skin rips open, [imp_2_del] flies out of it and then the wound suddenly heals."),
+ span_danger("You feel implant inside you starts to move and rips itself out! The resulting wound quickly closes itself though."),
+ )
+
+ if(patient.get_organ_slot("parasite_egg") && cast_power >=4) // Удаляем ксеноморфов
+ var/obj/item/organ/body_egg/parasite = patient.get_organ_slot("parasite_egg")
+ parasite.owner.vomit(VOMIT_CATEGORY_BLOOD | MOB_VOMIT_KNOCKDOWN | MOB_VOMIT_HARM)
+ parasite.owner.visible_message(
+ span_warning("[patient] twitches, gags and vomits a living creqture with blood! Gross!"),
+ span_bolddanger("Suddenly you feel sharp pain in your chest, then something starts moving up your throat. \
+ Before you can react somethign slips past your lips with a mix of vomit and blood!"),
+ )
+ var/atom/drop_loc = parasite.drop_location()
+ parasite.Remove(parasite.owner)
+ if(drop_loc)
+ parasite.forceMove(drop_loc)
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_drunkness
+ name = "Ethanol Body Synthesis"
+ desc = "Convert fat masses to ethanol in combat mode, vice versa otherwise. Works with time on distance, but not on synthetics."
+ button_icon = 'icons/obj/drinks/bottles.dmi'
+ button_icon_state = "beer"
+ cooldown_time = 1 SECONDS
+ mana_cost = 30
+ stamina_cost = 30
+ active_msg = "You prepare to convert fat tissues..."
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_drunkness/is_valid_target(atom/cast_on)
+ if(!ishuman(cast_on))
+ return FALSE
+ if(issynthetic(cast_on) )
+ to_chat(owner, span_notice("It's a synth. What am I supposed to convert? Oil?"))
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_drunkness/cast(mob/living/carbon/human/cast_on)
+ . = ..()
+ cast_on.apply_status_effect(/datum/status_effect/psyonic_fat_conversion, 5 * cast_power SECONDS, !cast_on.combat_mode)
+ drain_mana()
+ return TRUE
+
+/// С каждым тиком конвертируем или жир в алкоголь, или алкоголь в жир
+/datum/status_effect/psyonic_fat_conversion
+ id = "psyonic_fat_conversion"
+ alert_type = null
+ remove_on_fullheal = TRUE
+ var/eth2fat = TRUE
+
+/datum/status_effect/psyonic_fat_conversion/on_creation(mob/living/new_owner, duration = 10 SECONDS, eth2fat = TRUE)
+ src.duration = duration
+ src.eth2fat = eth2fat
+ return ..()
+
+/datum/status_effect/psyonic_fat_conversion/tick(seconds_between_ticks)
+ var/mob/living/carbon/human/human_owner = owner
+ var/fat = human_owner.nutrition
+ var/drunk = human_owner.get_drunk_amount()
+ if(eth2fat && !drunk) // если нет алкашки, то и конвертировать нечего
+ return
+ if(eth2fat) // алкашку в жир
+ human_owner.adjust_drunk_effect(-(drunk/6))
+ human_owner.adjust_nutrition(drunk)
+ if(!eth2fat && fat) // жир в алкашку. За 25 тиков полностью обезжирим человека!
+ human_owner.adjust_drunk_effect(fat/125)
+ human_owner.adjust_nutrition(-(fat/25))
+
+// Лечит токс урон.
+/datum/action/cooldown/spell/touch/psyonic/psyonic_cleansing
+ name = "Psyonic Cleansing"
+ desc = "Filters patient blood out of toxins and removes accumulated radiation."
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
+ button_icon_state = "cleansing"
+ cooldown_time = 3 SECONDS
+ mana_cost = 35
+ stamina_cost = 40
+ target_msg = "Your insides itch."
+
+ hand_path = /obj/item/melee/touch_attack/psyonic_mending
+ draw_message = span_notice("You ready your hand to cleanse a patient.")
+ drop_message = span_notice("You lower your hand.")
+ can_cast_on_self = TRUE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_cleansing/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/mendicant)
+ if(ishuman(victim))
+ var/mob/living/carbon/human/human_victim = victim
+ if(issynthetic(human_victim) && secondary_school != "Psychokinesis")
+ to_chat(owner, span_notice("I dont know how to work with synths. Why would I even try to? They dont have toxins."))
+ return FALSE
+ if(human_victim.can_block_magic(antimagic_flags))
+ to_chat(human_victim, span_notice("Psionic nearby tries to cleanse you."))
+ else
+ to_chat(human_victim, span_warning(target_msg))
+ if(!do_after(mendicant, 5 SECONDS, human_victim, IGNORE_SLOWDOWNS, TRUE))
+ accident_harm(human_victim)
+ else
+ try_heal_all(human_victim)
+ drain_mana()
+ return TRUE
+ else
+ return FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_cleansing/proc/accident_harm(mob/living/carbon/human/patient)
+ patient.apply_damage(25, TOX, BODY_ZONE_CHEST)
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_cleansing/proc/try_heal_all(mob/living/carbon/human/patient)
+ if(patient.getToxLoss() > 0)
+ patient.adjustToxLoss(clamp(-(patient.getToxLoss()/3)*cast_power, -35, 0), forced = TRUE)
+
+/**
+ * Пытается оживить труп
+ *
+ * Логика прока:
+ * 1. Смотрит есть ли причина по которой нельзя дефибнуть, пытается её устранить
+ * 2. Если не удалось устранить - не оживляет
+ * 3. Если удалось устранить причину - проверяет можно ли дефибнуть снова. Если появилась другая - не оживляет. Всё ок - оживляет.
+ */
+/datum/action/cooldown/spell/touch/psyonic/psyonic_revival
+ name = "Psyonic Revival"
+ desc = "Ability to trick death itself. Call for the bodys soul in the other realm in attempt to restore its vessel condition to an... acceptable levels."
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
+ button_icon_state = "revive"
+ cooldown_time = 3 SECONDS
+ mana_cost = 80
+ stamina_cost = 160
+
+ hand_path = /obj/item/melee/touch_attack/psyonic_mending
+ draw_message = span_notice("You ready your hand to revive a patient.")
+ drop_message = span_notice("You lower your hand.")
+ can_cast_on_self = FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_revival/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/mendicant)
+ if(ishuman(victim))
+ var/mob/living/carbon/human/human_victim = victim
+ var/synth_check = (secondary_school == "Psychokinesis" || !issynthetic(human_victim))
+ if(human_victim.stat == DEAD && synth_check)
+ owner.visible_message(span_notice("[owner] kneels before the body of [victim], lowers their hands onto cadavers chest and begins... meditating?"),
+ span_notice("You kneel before the cadaver, lower your hands onto their chest and start to concentrate energy. You better not \
+ get disturbed, or else..."))
+ var/obj/effect/abstract/particle_holder/particle_effect = new(human_victim, /particles/droplets/psyonic)
+ if(!do_after(mendicant, 25 SECONDS, human_victim, IGNORE_SLOWDOWNS, TRUE))
+ accident_harm(owner) // Ауч. Больно бьёт по псионику
+ else
+ try_heal_all(human_victim)
+ if(particle_effect)
+ QDEL_NULL(particle_effect)
+ drain_mana()
+ return TRUE
+ else if(issynthetic(human_victim) && human_victim.stat == DEAD)
+ to_chat(owner, span_warning("Your psyonic energy does not work very well with synths."))
+ return FALSE
+ else
+ return FALSE
+ else
+ return FALSE
+
+// 25 токса + 50 брута + 1 травма + позор роду псионическому
+/datum/action/cooldown/spell/touch/psyonic/psyonic_revival/proc/accident_harm(mob/living/carbon/human/unlucky_guy)
+ unlucky_guy.apply_damage(25, TOX, BODY_ZONE_CHEST)
+ unlucky_guy.take_bodypart_damage(25, wound_bonus = 100)
+ unlucky_guy.take_bodypart_damage(25, wound_bonus = 100, sharpness = SHARP_EDGED)
+ unlucky_guy.visible_message(span_warning("Something inside of [unlucky_guy]s body cracks!"),
+ span_bolddanger("Your revival energy backfired at you, causing severe injuries!"),
+ blind_message = span_hear("You hear bones breaking."))
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_revival/proc/can_defib_human(mob/living/carbon/human/patient)
+ var/defib_result = patient.can_defib()
+ var/fail_reason
+ var/synth_check = (secondary_school == "Psychokinesis")
+ switch (defib_result)
+ if (DEFIB_FAIL_SUICIDE)
+ fail_reason = "Patient has left this world on his terms. You can not restore him."
+ if (DEFIB_FAIL_NO_HEART)
+ fail_reason = "Patient's heart is missing and you are not Alpha tier to create it out of air."
+ if (DEFIB_FAIL_FAILING_HEART)
+ var/obj/item/organ/heart/target_heart = patient.get_organ_slot(ORGAN_SLOT_HEART)
+ if(target_heart)
+ target_heart.operated = TRUE
+ if((target_heart.organ_flags & ORGAN_ORGANIC) || synth_check) // Only fix organic heart
+ patient.setOrganLoss(ORGAN_SLOT_HEART, 60)
+ else
+ fail_reason = "Patient's heart is made out of metals and plastics. You can not work with that."
+ if (DEFIB_FAIL_TISSUE_DAMAGE)
+ patient.adjustBruteLoss(patient.getBruteLoss()/2)
+ patient.adjustFireLoss(patient.getFireLoss()/2)
+ if ((patient.getBruteLoss() >= MAX_REVIVE_BRUTE_DAMAGE) || (patient.getFireLoss() >= MAX_REVIVE_FIRE_DAMAGE))
+ fail_reason = "Patient's body is too flimsy to support life, but your energy partially healed that. Maybe try again?"
+ if (DEFIB_FAIL_HUSK)
+ patient.cure_husk()
+ if(HAS_TRAIT(patient, TRAIT_HUSK))
+ fail_reason = "Patient's body is a mere husk, and you can not cure them."
+ if (DEFIB_FAIL_FAILING_BRAIN)
+ var/obj/item/organ/brain/target_brain = patient.get_organ_slot(ORGAN_SLOT_BRAIN)
+ if(target_brain)
+ if((target_brain.organ_flags & ORGAN_ORGANIC) || synth_check) // Only fix organic heart
+ patient.setOrganLoss(ORGAN_SLOT_BRAIN, 60)
+ else
+ fail_reason = "Patient's brain is made out of metals and plastics. You can not work with that."
+ if (DEFIB_FAIL_NO_INTELLIGENCE)
+ fail_reason = "Patient is braindead. Your energy doesnt course through such body."
+ if (DEFIB_FAIL_NO_BRAIN)
+ fail_reason = "Patient's brain is missing and even if you were Alpha tier, you could not restore him.."
+ if (DEFIB_FAIL_BLACKLISTED)
+ fail_reason = "Patient soul is linked to the dead realm with death grip. You can not restore him."
+ if (DEFIB_FAIL_DNR)
+ fail_reason = "Patient cannot be restored due to star misalignment."
+ return fail_reason
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_revival/proc/try_heal_all(mob/living/carbon/human/patient)
+ var/fail_reason = can_defib_human(patient) // first to possibly cure something
+ fail_reason = can_defib_human(patient) // second to actually try revival
+ if(fail_reason)
+ owner.visible_message(span_warning(fail_reason))
+ else
+ var/defib_result = patient.can_defib()
+ if (defib_result == DEFIB_POSSIBLE)
+ var/total_brute = patient.getBruteLoss()
+ var/total_burn = patient.getFireLoss()
+
+ var/need_mob_update = FALSE
+ if (patient.health > HALFWAYCRITDEATH)
+ need_mob_update += patient.adjustOxyLoss(patient.health - HALFWAYCRITDEATH, updating_health = FALSE)
+ else
+ var/overall_damage = total_brute + total_burn + patient.getToxLoss() + patient.getOxyLoss()
+ var/mobhealth = patient.health
+ need_mob_update += patient.adjustOxyLoss((mobhealth - HALFWAYCRITDEATH) * (patient.getOxyLoss() / overall_damage), updating_health = FALSE)
+ need_mob_update += patient.adjustToxLoss((mobhealth - HALFWAYCRITDEATH) * (patient.getToxLoss() / overall_damage), updating_health = FALSE, forced = TRUE) // force tox heal for toxin lovers too
+ need_mob_update += patient.adjustFireLoss((mobhealth - HALFWAYCRITDEATH) * (total_burn / overall_damage), updating_health = FALSE)
+ need_mob_update += patient.adjustBruteLoss((mobhealth - HALFWAYCRITDEATH) * (total_brute / overall_damage), updating_health = FALSE)
+ if(need_mob_update)
+ patient.updatehealth()
+ owner.visible_message(span_green("Revival successful."))
+ playsound(src, 'sound/effects/ghost.ogg', 40, FALSE)
+ patient.set_heartattack(FALSE)
+ if(defib_result == DEFIB_POSSIBLE)
+ patient.grab_ghost()
+ patient.revive()
+ patient.emote("gasp")
+ patient.set_jitter_if_lower(200 SECONDS)
+ to_chat(patient, "[CONFIG_GET(string/blackoutpolicy)]")
+ SEND_SIGNAL(patient, COMSIG_LIVING_MINOR_SHOCK)
+ log_combat(owner, patient, "psyonically revived")
+
+#undef HALFWAYCRITDEATH
diff --git a/tff_modular/modules/psyonics/icons/actions.dmi b/tff_modular/modules/psyonics/icons/actions.dmi
new file mode 100644
index 00000000000..beb7a648626
Binary files /dev/null and b/tff_modular/modules/psyonics/icons/actions.dmi differ
diff --git a/tff_modular/modules/psyonics/icons/card.dmi b/tff_modular/modules/psyonics/icons/card.dmi
new file mode 100644
index 00000000000..f7a2eee1ba0
Binary files /dev/null and b/tff_modular/modules/psyonics/icons/card.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 14f3ecd3edc..4e14f21e83a 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -8900,6 +8900,14 @@
#include "tff_modular\modules\poster_contest\contraband.dm"
#include "tff_modular\modules\poster_contest\official.dm"
#include "tff_modular\modules\poster_contest\winners_items\code.dm"
+#include "tff_modular\modules\psyonics\code\_psyonics.dm"
+#include "tff_modular\modules\psyonics\code\_quirk.dm"
+#include "tff_modular\modules\psyonics\code\coersion.dm"
+#include "tff_modular\modules\psyonics\code\cyberimp.dm"
+#include "tff_modular\modules\psyonics\code\documents.dm"
+#include "tff_modular\modules\psyonics\code\energistics.dm"
+#include "tff_modular\modules\psyonics\code\psychokinesis.dm"
+#include "tff_modular\modules\psyonics\code\redaction.dm"
#include "tff_modular\modules\quirks\code\_quirk.dm"
#include "tff_modular\modules\redsec\code\vending.dm"
#include "tff_modular\modules\redsec_reskins\code\beret_reskin.dm"
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/tff/psyonic_school.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/tff/psyonic_school.tsx
new file mode 100644
index 00000000000..ec60f99b2ee
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/tff/psyonic_school.tsx
@@ -0,0 +1,15 @@
+import { FeatureChoiced } from '../../base';
+import { FeatureDropdownInput } from '../../dropdowns';
+
+export const psyonic_school: FeatureChoiced = {
+ name: 'School',
+ description: 'Choose a school, which abilities you shall receive.',
+ component: FeatureDropdownInput,
+};
+
+export const psyonic_school_secondary: FeatureChoiced = {
+ name: 'Secondary School',
+ description:
+ 'Choose a secondary school. Abilities in it will be less powerful.',
+ component: FeatureDropdownInput,
+};
diff --git a/tgui/packages/tgui/interfaces/PsyonicLicense.tsx b/tgui/packages/tgui/interfaces/PsyonicLicense.tsx
new file mode 100644
index 00000000000..bd664883817
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PsyonicLicense.tsx
@@ -0,0 +1,120 @@
+import { useBackend } from '../backend';
+import { Icon, Image, LabeledList, Section, Stack } from '../components';
+import { Window } from '../layouts';
+
+type Data = {
+ character_preview: string;
+ primary_school: string;
+ secondary_school: string;
+ psyonic_level: string;
+ owner_name: string;
+ owner_age: number;
+ owner_species: string;
+ owner_preview: string;
+};
+
+export const PsyonicLicense = (props) => {
+ const { act, data } = useBackend();
+ const {
+ primary_school,
+ secondary_school,
+ psyonic_level,
+ owner_name,
+ owner_age,
+ owner_species,
+ owner_preview,
+ } = data;
+ return (
+
+
+
+
+
+
+
+
+ {'Мы всегда наблюдаем'}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {data.owner_name}
+
+
+ {data.owner_age}
+
+
+ {data.owner_species}
+
+
+ {data.primary_school}
+
+
+ {data.secondary_school}
+
+
+ {data.psyonic_level}
+
+
+
+
+
+
+
+
+ {
+ 'Данная лицензия удостоверяет то, что пользователь является псиоником.'
+ }
+ {
+ ' Лицензия пользователя псионики позволяет пользователю применять способности в целях самозащиты, оказания лечебной помощи и в прочих, установленных законом случаев.'
+ }
+ {
+ ' Пользователь псионики обязан иметь лицензию при себе всё время, иначе его действия могут быть классифицированы СБ НТ как нелегальные и повлечь за собой юридические последствия.'
+ }
+
+
+
+
+
+
+ {'ПОДДЕЛКА ЛИЦЕНЗИИ КАРАЕТСЯ ЗАКОНОМ'}
+
+
+
+
+
+ );
+};