From d12b4ff304adffa3637fbd02363a8d5e28d2ec3c Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Sun, 24 Nov 2024 19:44:19 +0700
Subject: [PATCH 01/17] Basic
---
.../modules/psyonics/code/_psyonics.dm | 262 +++++++++++
tff_modular/modules/psyonics/code/_quirk.dm | 166 +++++++
tff_modular/modules/psyonics/code/coersion.dm | 427 ++++++++++++++++++
.../modules/psyonics/code/psychokinesis.dm | 286 ++++++++++++
.../modules/psyonics/code/redaction.dm | 306 +++++++++++++
tgstation.dme | 5 +
.../tff/psyonic_school.tsx | 15 +
7 files changed, 1467 insertions(+)
create mode 100644 tff_modular/modules/psyonics/code/_psyonics.dm
create mode 100644 tff_modular/modules/psyonics/code/_quirk.dm
create mode 100644 tff_modular/modules/psyonics/code/coersion.dm
create mode 100644 tff_modular/modules/psyonics/code/psychokinesis.dm
create mode 100644 tff_modular/modules/psyonics/code/redaction.dm
create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/tff/psyonic_school.tsx
diff --git a/tff_modular/modules/psyonics/code/_psyonics.dm b/tff_modular/modules/psyonics/code/_psyonics.dm
new file mode 100644
index 00000000000..9ce9e605530
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/_psyonics.dm
@@ -0,0 +1,262 @@
+/datum/action/cooldown/spell/conjure_item/psyonic
+ delete_old = FALSE
+ delete_on_failure = TRUE
+ requires_hands = TRUE
+ // Сколько маны стоит кастануть спелл
+ var/mana_cost = 10
+ // Некоторые спеллы могут отнимать стамину
+ var/stamina_cost = 0
+ // Что написать жертве
+ var/target_msg
+ // Сила способности
+ var/cast_power = 0
+ // Вторичная школа. Может дать особые эффекты при комбинациях
+ var/secondary_school = 0
+ // Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
+ 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/conjure_item/psyonic/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/conjure_item/psyonic/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 ..()
+
+// Для спеллов типа self
+/datum/action/cooldown/spell/psyonic
+ // Сколько маны стоит кастануть спелл
+ var/mana_cost = 10
+ // Некоторые спеллы могут отнимать стамину
+ var/stamina_cost = 0
+ // Сила способности
+ var/cast_power = 0
+ // Вторичная школа. Может дать особые эффекты при комбинациях
+ var/secondary_school = 0
+ // Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
+ 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/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/psyonic/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/psyonic/can_cast_spell(feedback)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!check_for_mana())
+ return FALSE
+ else
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/psyonic
+ // Сколько маны стоит кастануть спелл
+ var/mana_cost = 10
+ // Некоторые спеллы могут отнимать стамину
+ var/stamina_cost = 0
+ // Что написать жертве
+ var/target_msg
+ // Сила способности
+ var/cast_power = 0
+ // Вторичная школа. Может дать особые эффекты при комбинациях
+ var/secondary_school = 0
+ // Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
+ 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/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/pointed/psyonic/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/pointed/psyonic/can_cast_spell(feedback)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!check_for_mana())
+ return FALSE
+ else
+ return TRUE
+
+/datum/action/cooldown/spell/touch/psyonic
+ // Сколько маны стоит кастануть спелл
+ var/mana_cost = 10
+ // Некоторые спеллы могут отнимать стамину
+ var/stamina_cost = 0
+ // Что написать жертве
+ var/target_msg
+ // Сила способности
+ var/cast_power = 0
+ // Вторичная школа. Может дать особые эффекты при комбинациях
+ var/secondary_school = 0
+ // Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
+ 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/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/touch/psyonic/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/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..1a1c81a8f60
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/_quirk.dm
@@ -0,0 +1,166 @@
+#define TRAIT_PSYONIC_USER "psyonicuser"
+
+#define LATENT_PSYONIC 0
+#define OPERANT_PSYONIC 1
+#define MASTER_PSYONIC 2
+#define GRANDMASTER_PSYONIC 3
+#define PARAMOUNT_PSYONIC 4
+
+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 // Отдадите за псионику жопу
+ 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 = "bug"
+ mob_trait = TRAIT_PSYONIC_USER
+ //mail_goodies = list(/obj/item/toy/foamfinger) # ДОБАВИТЬ СЮДА ХИМИКАТЫ
+ 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
+
+/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/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
+ switch(psyonic_level)
+ if(LATENT_PSYONIC)
+ psyonic_level_string = "Pi"
+ if(OPERANT_PSYONIC)
+ psyonic_level_string = "Omicron"
+ if(MASTER_PSYONIC)
+ psyonic_level_string = "Lambda"
+ if(GRANDMASTER_PSYONIC)
+ psyonic_level_string = "Theta"
+ if(PARAMOUNT_PSYONIC)
+ psyonic_level_string = "Epsilon"
+ max_mana = (psyonic_level + 1) * 20
+ RegisterSignal(quirk_holder, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(get_status_tab_item))
+ var/mob/living/carbon/human/whom_to_give = quirk_holder
+ 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(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)
+ 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
+
+/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)
+ var/additional_mana = 1
+ if(quirk_holder.has_status_effect(/datum/status_effect/drugginess)) // Наркота даёт бафф к генерации маны
+ additional_mana = 1.5
+ 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..f868d08312a
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/coersion.dm
@@ -0,0 +1,427 @@
+#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/try_add_coercion_school(var/tier, 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)
+ 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)
+ 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)
+ var/datum/action/new_action3 = new /datum/action/cooldown/spell/touch/psyonic/psyonic_hypnosis(src.mind || src, tier, additional_school)
+ new_action3.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/mob/actions/actions_genetic.dmi'
+ button_icon_state = "mending_touch"
+ 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(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_state = "blind"
+ ranged_mousepointer = 'icons/effects/mouse_pointers/blind_target.dmi'
+
+ 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
+ fixs_brainz(cast_on)
+ drain_mana()
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_focus/proc/fixs_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_genetic.dmi'
+ button_icon_state = "mending_touch"
+ 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.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
+
+ 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?" + "
"
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_agony
+ name = "Psyonic Agony"
+ desc = "Deals pain."
+ button_icon = 'icons/mob/actions/actions_genetic.dmi'
+ button_icon_state = "mending_touch"
+ cooldown_time = 0.5 SECONDS
+ mana_cost = 15
+ 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.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
+ 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)
+
+/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_state = "blind"
+ ranged_mousepointer = 'icons/effects/mouse_pointers/blind_target.dmi'
+ cooldown_time = 1 SECONDS
+
+ mana_cost = 40
+ target_msg = "You 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.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)
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_hypnosis
+ name = "Psyonic Hypnosis"
+ desc = "Implant a looping pattern into victims head."
+ button_icon = 'icons/mob/actions/actions_genetic.dmi'
+ button_icon_state = "mending_touch"
+ 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(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)
+
+/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.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/psychokinesis.dm b/tff_modular/modules/psyonics/code/psychokinesis.dm
new file mode 100644
index 00000000000..eb16c3bd100
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/psychokinesis.dm
@@ -0,0 +1,286 @@
+/// Школа психокинетики
+/// Имеет 6 спеллов.
+/// Psi lighter - создаёт миниатюрный огонёк на кончиках пальцев. Работает как зажигалка.
+/// Psi blade - создаёт в руке пси-клинок. Урон увеличивается в зависимости от уровня.
+/// Psi tool - создаёт в руке универсальный инструмент.
+/// Tinker - чинит integrity чего бы то ни было.
+/// Psyforce - даёт "клешни жизни" для вскрытия дверей
+/// Telekinesis - даёт мутацию телекинеза.
+
+/mob/living/carbon/human/proc/try_add_psychokinesis_school(var/tier, 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))
+ 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()
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_tinker
+ name = "Psyonic Tinker"
+ desc = "Restore somethings condition to its normal state."
+ button_icon = 'icons/mob/actions/actions_genetic.dmi'
+ button_icon_state = "mending_touch"
+ 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..0d99f89a8a9
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/redaction.dm
@@ -0,0 +1,306 @@
+#define HALFWAYCRITDEATH ((HEALTH_THRESHOLD_CRIT + HEALTH_THRESHOLD_DEAD) * 0.5)
+
+/// Школа лечения
+/// Имеет 4 спелла в данный момент
+/// Roentgen - обычный мед скан, работающий на дистанции
+/// Меnding - лечит кровь, открытые раны и окси урон. Также удаляет импланты/ксеноморфов из тела при определённых условиях
+/// Cleansing - лечит токс урон
+/// Revive - пытается оживить труп
+
+/mob/living/carbon/human/proc/try_add_redaction_school(var/tier, 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_action = new /datum/action/cooldown/spell/touch/psyonic/psyonic_cleansing(src.mind || src, tier, additional_school)
+ new_action.Grant(src)
+ if(tier >= 3)
+ 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_state = "blind"
+ ranged_mousepointer = 'icons/effects/mouse_pointers/blind_target.dmi'
+
+ 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 = 'icons/mob/actions/actions_genetic.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(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/internal/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/touch/psyonic/psyonic_cleansing
+ name = "Psyonic Cleansing"
+ desc = "Filters patient blood out of toxin and removes accumulated radiation."
+ button_icon = 'icons/mob/actions/actions_genetic.dmi'
+ button_icon_state = "mending_touch"
+ 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(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)
+
+/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 = 'icons/mob/actions/actions_minor_antag.dmi'
+ button_icon_state = "set_drop"
+ 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."))
+ else
+ return FALSE
+ else
+ return FALSE
+
+/datum/action/cooldown/spell/touch/psyonic/psyonic_revival/proc/accident_harm(mob/living/carbon/human/patient)
+ patient.apply_damage(25, TOX, BODY_ZONE_CHEST)
+ patient.take_bodypart_damage(25, wound_bonus = 100)
+ patient.take_bodypart_damage(25, wound_bonus = 100, sharpness = SHARP_EDGED)
+ owner.visible_message(span_warning("Something inside of [owner]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/internal/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/internal/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/tgstation.dme b/tgstation.dme
index 61cab8f8cee..ea84153d7bf 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -8832,6 +8832,11 @@
#include "tff_modular\modules\police_nt\code\nt_police.dm"
#include "tff_modular\modules\police_nt\code\nt_police_items.dm"
#include "tff_modular\modules\police_nt\code\nt_police_loadaut.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\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,
+};
From d4064017fe4b811ccb7e6c9e11eadc33438f180b Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Mon, 25 Nov 2024 02:12:51 +0700
Subject: [PATCH 02/17] =?UTF-8?q?=D0=9F=D0=BE=D1=87=D1=82=D0=B8=20=D0=B4?=
=?UTF-8?q?=D0=BE=D0=B4=D0=B5=D0=BB=D0=B0=D0=BD=D0=BE(=3F)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
tff_modular/modules/psyonics/code/_quirk.dm | 30 ++-
tff_modular/modules/psyonics/code/coersion.dm | 39 ++--
tff_modular/modules/psyonics/code/cyberimp.dm | 38 ++++
.../modules/psyonics/code/energistics.dm | 183 ++++++++++++++++++
.../modules/psyonics/code/psychokinesis.dm | 6 +-
.../modules/psyonics/code/redaction.dm | 20 +-
.../modules/psyonics/icons/actions.dmi | Bin 0 -> 4932 bytes
tgstation.dme | 2 +
8 files changed, 279 insertions(+), 39 deletions(-)
create mode 100644 tff_modular/modules/psyonics/code/cyberimp.dm
create mode 100644 tff_modular/modules/psyonics/code/energistics.dm
create mode 100644 tff_modular/modules/psyonics/icons/actions.dmi
diff --git a/tff_modular/modules/psyonics/code/_quirk.dm b/tff_modular/modules/psyonics/code/_quirk.dm
index 1a1c81a8f60..681e1b999de 100644
--- a/tff_modular/modules/psyonics/code/_quirk.dm
+++ b/tff_modular/modules/psyonics/code/_quirk.dm
@@ -1,4 +1,6 @@
#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
@@ -25,7 +27,7 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
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 // Отдадите за псионику жопу
+ value = 12 // Отдадите за псионику жопу, чтобы потом вам Рэнди Рандом всегда слал наименьший уровень силы
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.")
@@ -34,11 +36,17 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
//mail_goodies = list(/obj/item/toy/foamfinger) # ДОБАВИТЬ СЮДА ХИМИКАТЫ
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
/datum/quirk/psyonic/add(client/client_source)
@@ -64,11 +72,11 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
psyonic_level_string = "Theta"
if(PARAMOUNT_PSYONIC)
psyonic_level_string = "Epsilon"
- max_mana = (psyonic_level + 1) * 20
+ max_mana = (psyonic_level + 1) * 20 // Минимальный - 20, максимальный - 100
RegisterSignal(quirk_holder, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(get_status_tab_item))
var/mob/living/carbon/human/whom_to_give = quirk_holder
if(school == secondary_school)
- psyonic_level += 1
+ psyonic_level += 1 // Если вторичка совпадает с первой - добавляем один уровень, но не меняем описание
switch(school)
if("Redaction")
whom_to_give.try_add_redaction_school(psyonic_level, secondary_school)
@@ -76,8 +84,10 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
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)
+ if(secondary_school != school) // Если школы разные, добавить способность нулевого уровня вторичной школы
switch(secondary_school)
if("Redaction")
whom_to_give.try_add_redaction_school(0, 0)
@@ -85,6 +95,9 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
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."]" + "
" + \
@@ -106,9 +119,16 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
items += "Current psyonic energy: [mana_level]/[max_mana]"
/datum/quirk/psyonic/process(seconds_per_tick)
+ if(HAS_TRAIT(quirk_holder, TRAIT_NO_PSYONICS))
+ return
+
var/additional_mana = 1
if(quirk_holder.has_status_effect(/datum/status_effect/drugginess)) // Наркота даёт бафф к генерации маны
- additional_mana = 1.5
+ 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)
diff --git a/tff_modular/modules/psyonics/code/coersion.dm b/tff_modular/modules/psyonics/code/coersion.dm
index f868d08312a..32c37aebd08 100644
--- a/tff_modular/modules/psyonics/code/coersion.dm
+++ b/tff_modular/modules/psyonics/code/coersion.dm
@@ -10,7 +10,8 @@
/// P.S. По гипнозу. В оригинале на финиках вообще было порабощение разума.
/// Psyonic blind - временно ослепляет.
-/mob/living/carbon/human/proc/try_add_coercion_school(var/tier, additional_school = 0)
+// Добавить школу внушения
+/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)
@@ -34,8 +35,8 @@
/datum/action/cooldown/spell/touch/psyonic/psyonic_assay
name = "Psyonic Assay"
desc = "Check if the target is a psyonic."
- button_icon = 'icons/mob/actions/actions_genetic.dmi'
- button_icon_state = "mending_touch"
+ button_icon = 'icons/obj/medical/organs/organs.dmi'
+ button_icon_state = "brain"
cooldown_time = 3 SECONDS
mana_cost = 5
stamina_cost = 0
@@ -75,8 +76,8 @@
/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_state = "blind"
- ranged_mousepointer = 'icons/effects/mouse_pointers/blind_target.dmi'
+ button_icon = 'icons/obj/medical/organs/organs.dmi'
+ button_icon_state = "brain-smooth"
cooldown_time = 1 SECONDS
@@ -111,11 +112,11 @@
if(!do_after(owner, 5 SECONDS, cast_on, IGNORE_SLOWDOWNS | IGNORE_TARGET_LOC_CHANGE, TRUE))
accident_harm(cast_on)
else
- fixs_brainz(cast_on)
+ fix_brainz(cast_on)
drain_mana()
return TRUE
-/datum/action/cooldown/spell/pointed/psyonic/psyonic_focus/proc/fixs_brainz(mob/living/carbon/human/cast_on)
+/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)
@@ -134,8 +135,8 @@
/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_genetic.dmi'
- button_icon_state = "mending_touch"
+ button_icon = 'icons/mob/actions/actions_spells.dmi'
+ button_icon_state = "mindread"
cooldown_time = 3 SECONDS
mana_cost = 40
stamina_cost = 40
@@ -197,7 +198,7 @@
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 // Там очень много ролей, а мага, еретика и культиста я думаю и без этой способности найти легко. Тем более мы читаем воспоминания, что более имбово
+ 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)))
@@ -251,8 +252,8 @@
/datum/action/cooldown/spell/touch/psyonic/psyonic_agony
name = "Psyonic Agony"
desc = "Deals pain."
- button_icon = 'icons/mob/actions/actions_genetic.dmi'
- button_icon_state = "mending_touch"
+ button_icon = 'icons/obj/weapons/baton.dmi'
+ button_icon_state = "stunbaton_active"
cooldown_time = 0.5 SECONDS
mana_cost = 15
stamina_cost = 0
@@ -296,13 +297,11 @@
/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_state = "blind"
- ranged_mousepointer = 'icons/effects/mouse_pointers/blind_target.dmi'
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
+ button_icon_state = "spasm"
cooldown_time = 1 SECONDS
-
mana_cost = 40
target_msg = "You muscles spasm!"
-
active_msg = "You prepare to stun a target..."
/datum/action/cooldown/spell/pointed/psyonic/psyonic_spasm/New(Target)
@@ -336,8 +335,8 @@
/datum/action/cooldown/spell/touch/psyonic/psyonic_hypnosis
name = "Psyonic Hypnosis"
desc = "Implant a looping pattern into victims head."
- button_icon = 'icons/mob/actions/actions_genetic.dmi'
- button_icon_state = "mending_touch"
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
+ button_icon_state = "hypno"
cooldown_time = 10 SECONDS
mana_cost = 25 // Стоит немного
@@ -390,10 +389,8 @@
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)
@@ -422,6 +419,6 @@
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)
+ 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..b4f16175d0c
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/cyberimp.dm
@@ -0,0 +1,38 @@
+#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)
+
+/obj/item/organ/internal/cyberimp/brain/pro_psyonic
+ name = "Psyonic Amplifier Model A"
+ desc = "This implant will boost psyonics energy regenerating."
+ 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/energistics.dm b/tff_modular/modules/psyonics/code/energistics.dm
new file mode 100644
index 00000000000..5d5bc14a864
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/energistics.dm
@@ -0,0 +1,183 @@
+/// Школа энергетики. 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/psyonic/psyonic_freeze(src.mind || src, tier, additional_school)
+ 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(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/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
+ var/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
+
+/datum/action/cooldown/spell/psyonic/psionic_electrocute
+ name = "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/psyonic/psyonic_freeze
+ name = "Psyonic Freeze"
+ desc = "Quickly freeze moist around 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
+ active_msg = "You prepare to create freeze prison..."
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_freeze/is_valid_target(atom/cast_on)
+ if(!isliving(cast_on))
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/psyonic/psyonic_freeze/cast(mob/living/cast_on)
+ . = ..()
+ if(HAS_TRAIT(cast_on, TRAIT_RESISTCOLD))
+ return FALSE
+ cast_on.apply_status_effect(/datum/status_effect/freon/watcher/extended)
+ drain_mana()
+ return TRUE
diff --git a/tff_modular/modules/psyonics/code/psychokinesis.dm b/tff_modular/modules/psyonics/code/psychokinesis.dm
index eb16c3bd100..29e970b1dc2 100644
--- a/tff_modular/modules/psyonics/code/psychokinesis.dm
+++ b/tff_modular/modules/psyonics/code/psychokinesis.dm
@@ -7,7 +7,7 @@
/// Psyforce - даёт "клешни жизни" для вскрытия дверей
/// Telekinesis - даёт мутацию телекинеза.
-/mob/living/carbon/human/proc/try_add_psychokinesis_school(var/tier, additional_school = 0)
+/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)
@@ -144,8 +144,8 @@
/datum/action/cooldown/spell/touch/psyonic/psyonic_tinker
name = "Psyonic Tinker"
desc = "Restore somethings condition to its normal state."
- button_icon = 'icons/mob/actions/actions_genetic.dmi'
- button_icon_state = "mending_touch"
+ button_icon = 'icons/obj/tools.dmi'
+ button_icon_state = "wrench"
cooldown_time = 3 SECONDS
mana_cost = 40
stamina_cost = 50
diff --git a/tff_modular/modules/psyonics/code/redaction.dm b/tff_modular/modules/psyonics/code/redaction.dm
index 0d99f89a8a9..04718f8d19d 100644
--- a/tff_modular/modules/psyonics/code/redaction.dm
+++ b/tff_modular/modules/psyonics/code/redaction.dm
@@ -7,7 +7,7 @@
/// Cleansing - лечит токс урон
/// Revive - пытается оживить труп
-/mob/living/carbon/human/proc/try_add_redaction_school(var/tier, additional_school = 0)
+/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)
@@ -24,8 +24,8 @@
/datum/action/cooldown/spell/pointed/psyonic/psyonic_roentgen
name = "Roentgen"
desc = "Try to read target's vital energy and determine their state."
- button_icon_state = "blind"
- ranged_mousepointer = 'icons/effects/mouse_pointers/blind_target.dmi'
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
+ button_icon_state = "roentgen"
cooldown_time = 1 SECONDS
@@ -74,7 +74,7 @@
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 = 'icons/mob/actions/actions_genetic.dmi'
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
button_icon_state = "mending_touch"
cooldown_time = 3 SECONDS
mana_cost = 25
@@ -117,7 +117,7 @@
if(patient.getOxyLoss() >= OXYLOSS_PASSOUT_THRESHOLD-10)
patient.adjustOxyLoss(-cast_power*5, forced = TRUE)
- if(patient.implants && secondary_school == "Psychokinesis" && cast_power >= 2)
+ 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)
@@ -128,7 +128,7 @@
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)
+ if(patient.get_organ_slot("parasite_egg") && cast_power >=4) // Удаляем ксеноморфов
var/obj/item/organ/internal/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(
@@ -144,8 +144,8 @@
/datum/action/cooldown/spell/touch/psyonic/psyonic_cleansing
name = "Psyonic Cleansing"
desc = "Filters patient blood out of toxin and removes accumulated radiation."
- button_icon = 'icons/mob/actions/actions_genetic.dmi'
- button_icon_state = "mending_touch"
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
+ button_icon_state = "cleansing"
cooldown_time = 3 SECONDS
mana_cost = 35
stamina_cost = 40
@@ -182,8 +182,8 @@
/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 = 'icons/mob/actions/actions_minor_antag.dmi'
- button_icon_state = "set_drop"
+ button_icon = 'tff_modular/modules/psyonics/icons/actions.dmi'
+ button_icon_state = "revive"
cooldown_time = 3 SECONDS
mana_cost = 80
stamina_cost = 160
diff --git a/tff_modular/modules/psyonics/icons/actions.dmi b/tff_modular/modules/psyonics/icons/actions.dmi
new file mode 100644
index 0000000000000000000000000000000000000000..beb7a648626421f29010b3a1cae3bffcf330de76
GIT binary patch
literal 4932
zcmaJ_XE+>67v2rlh9xU{4-!PTga~%^9;>&jcQ>Nw=q_h^!KX5
zPshMR&c@x!&B4XP!PyA_@W}`pktPZefD(**Zc>CesTS9n*lH)Q$&_mZr?CSwl@g6w
z0;pwjG!o{oAH^ZWmUiDwSzWaNn
z@6)^8w;-8jsHP=wjdoh;&F$_=R1(V)`Wg6jh>?b+>$$f+G~{gRmpvkWZF_DXS2RCT
zGqje!*6?K?R=KPt_RSB$G|{A3
z)9S0H>P%?i4A-Dj1$o%WSHr;`7Idch%Qj$}*=WEL>Sc@=JmV_Oy`u%k-)A>Ee7&4@
zF?PIU%M{(#2B*Nd<)kzNFwGxZNap?=#5-PjDH$2k^eifxf?`BJ!E$n;ZX)Keu44Po
z{n!jh>VWlND8_i;J9^LQIF3lY>mA5RhMZ1!5y}rw2Jdn8g4!~W;dHuP&1sos(
z(nU@Ew_>NMcM8{tzN*AAOG@Wx9v&=dKVG?ZUg{9*$yf5$a!Up+wgexUjk0KdpQ|2Dx?_jaM~q!oul=nu4B9oF>5s(kC>I|#7H#Ad;Ei>Uj_UC_E+}l_EgN}hspL5C
zzHKgcrkvmY_A4
zD~{h?D?Ll2g6H*c?6x1f21PXBm&I+|Hw(q*A}jReHye}704q4NGvk4B({1V)_D}O<
zxx`H=Bz4FIGxN56Y>Wg@+5G$19q;<292I~3r&9dld4XBbr7~geR9gSOEJCb-
zhm%7D^p|F40b;+>0RL_mMl{>fCh5erwBr#2Ys$nZDUN%L6!-DeM6+Kl(sCo|Se#`b
z+(1hnVFQ6OvGeSA(S27#I3>Lt7dx0!)c0jdzT&zBJ
z?*ckFXrA$+URwE(7QXZiJ(xC`4
z&dt7e^Y+A`vXAhV$SxyD858eiunE#wW(KKxAIya?58Labe@?Hx(S#DAczokW$RJ2w
zD78UYYJOu4dgZYlzrVAiT#~eRnI`bX47^b-mNgI{!Kw~GcIU3F@TI+cX`+FW;y&tn
ze)Ge?FpSDlhI^aQrJ_C{!IYu?rtk<)?=y-WkDZQd_EgBiW{Yi+!sI(R^$n^a!nv(G
zL#Zuc)oB)~=&!@=9l=JgB`=-AToHUaMaDeg=jRui!Y=%-RF9-J{|zx!GP6K}N>0eUt-X|Z#!
z?#4)inGn&f;~b$AO(8Gt8ePmMReUs+@)v?qZ_lpt@AZOc)bCCDm(=3?U0=RiUW$S?
z=+e_#1QSIrfhP_AHO_(d4$Rylh!vG_q@v~3xQfuNo
zBZJk;C@tSbQKFo{YW!X;!RxH
zZ>NW|{5~E{hf@KMuOX6uMM0qkf`$*A?)hMq>T`z%z~QBnCu
zOvskB6dshC+A
z*AJ;6?*&LH$|h;bOc=@}&~{EJv-Uoa#?RYs8G{G_>l7}R0_P3C;py<$g!M`_oF%
zo!TdhM_5sf;cVLMC=gwD6LtxC;w}fagxXvU
zG+-GU%`^69kTRY|bUx7lgr$V>g|HZ16uzQsc==I&j7!jnzZ2vyEuF9z23iEnJ8-@?
zyYxP*-b~#6k&mX7#90_RqlV^3OMlvGs6+94$ueYR?D2DFrTEYgfIw8H@(8gJ;~c%i
ztZ@z0mI(2N@yq90Pv}GAd|41$fjHK{f|7vtQ0dHdzQ%$W@Zyw&VNa4RYvEns5_=1E
zl;UyKVC#E95~usAgjxMye14f9T>a1$5JUUlmN%cla@0>=0TYNJb4D~SUXullWJ>uz
zbLOXdV(u6+6|&=H^$cR3kyZF=i$@>3Q&_?QCg}T2ZOE(@6fWpGr0nrK&4Ob9wG!&&
zo^q}%&Qzw4d!!?|3MaOcqM@`w+q&xW^;PqgQKX%rq3R*-qFJ%*}IRwCHSx3a!N
zAFHv@w5%|V^%ku1;sApMU$zhh1A3g+oTRz9C=AR8@mQ6a`p3RA3QDKRhPL3*;s~i(
zu}Ic!;V*hJJtnHfYJ4^x6{LTrbzslmkU_M^*^egl5{^6@jUB=hr{mcP9o}Q={qdm7
z9zuW%cR^^3c^k^}0{7GDu}d)v6a5gDU>X{w411qSauTGJPv~hc15&dm5HED>s1`c?
z(kLO+p?=m-QzuORhmBPAfxh-~`^kIP*wa|*=|4hg6rkgmV$^Bq{%6?wSu7Xd>qmAR}Jpiq@DYMMJQpe@aN{=35B((GmSS;wxbeZ8>7z!cLhZnE7nk
z4L%k1q0bj=$evxn_I&os}rh+{JQDniY!TM
zeCK!=YL@md>R!%Ta;SOE^_l8Taoe%Dgll?JtFvBEAvN*cK4HWs10*)y1o0vFB>~HF
zPs_a>?84YYhAoGu;-!y$%S?FMD4C!Y#~cK>vb&4}7V~
zSDsfrJP4I_qxxtXp^(bq($@=-g3FbQm?QvY#`&f^5qlGrO$vn}=&3~ATyi*^YlIvtM|)hB*SU9gk)
zp%|ZwC5bej(swuE6JYCtHUN4(f7hlN_M3){21TkA6C%C2!gC5;~_%4
z)KTClbsDJoa531xBL77;ZENi5rv#JcKh@16%@h+4qKPXR?Y~!@KH9g3^2K4TTJ7AN
z2?w97SWwcbYli(*-UWTt)%kE@Pl_bd-sc7XsV=T3a&d%!7c;aNMNFUB;7n>jENr1j
z?#0jkq@M$
zw4QYtQY&iuPkKEJASavGDvDlwYbyQGdmiS_SWSo8nv%Xa$$Q%=N@gg+o~lh5LsDVY
z7mmjvVxidO>fVSa?v*j;^0z7U6^n2@YRUR?Ge<3Goy?1f@sU?c_>&lEs5q41Yb@6c
zaTzB}q5T|8<=pnwBo0`)eKry%G*mZ$d#U+EE|ODvr!U?DLfdo!2$oKL0?a+n(|%bH
zq#xdX3fSIj-vn;uIo6k3)k3t`EZBW$agZq_;4tJX6&8c0l>e3)e~yduV8#lrXZB=-
ze@t(vt9jkFj<$aRkQ<*Mn+88pU(Gj}lm0^Z0N)iYh$QFx2Bszq?NvX!k=XYQZH;9Y
zAq$uVOe$ZG}rWa)~pc$?>D!94u{Q7UBhQoOK=bW+Ut)2`B##P^f2
z;y#(hE7;+FuyZw7EvPl6&j!$}qwSg^XDY-J)Du?93~zNUkX2zD;;>=hed6dZ2gMUt
zunq@l%GXwexC_mtH*`LQ8;8aKj-$6elg3WbMU7L1^^QxN6N1C}*0Cafzy?x3LVCup
zrBq(&^19(NoGQwrAmKbS*xDD1$TWUzfGA$%i*|}BC8|8DhFnwzY|x1ouodt`7;YW;
z2<-kkJeBAw6?`JJiWsrpew;L+T=wML+qQ93w_+yb4L2h!QjzW@YufUXesXLEAd~h^UoOu*CdZVx
z>?zl7t2vrE@a$lPhOS6f7W#^$blst48ros;rC_iz&2Xfb!s1#kA+kVGjlf(sciA7&
zz{J5S5rpsvi-*9%N0(1-T+;An-gRsgtbuw^hX_L5$fjtGMhlAno=CUMaI>|vJXP;^
R_upawWqEbEG8yyW{{hr-OP~M%
literal 0
HcmV?d00001
diff --git a/tgstation.dme b/tgstation.dme
index ea84153d7bf..e1a2f09d270 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -8835,6 +8835,8 @@
#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\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"
From aab3c50e56252106ae9714458880beb2e4a85eea Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Mon, 25 Nov 2024 02:51:49 +0700
Subject: [PATCH 03/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?=
=?UTF-8?q?=D1=82=D1=8C=20=D0=BC=D0=B5=D0=B4=20=D0=BE=D0=BF=D0=B8=D1=81?=
=?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
tff_modular/modules/psyonics/code/_quirk.dm | 1 +
tff_modular/modules/psyonics/code/energistics.dm | 2 --
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/tff_modular/modules/psyonics/code/_quirk.dm b/tff_modular/modules/psyonics/code/_quirk.dm
index 681e1b999de..9232a67b00b 100644
--- a/tff_modular/modules/psyonics/code/_quirk.dm
+++ b/tff_modular/modules/psyonics/code/_quirk.dm
@@ -28,6 +28,7 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
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 is a psyonic"
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.")
diff --git a/tff_modular/modules/psyonics/code/energistics.dm b/tff_modular/modules/psyonics/code/energistics.dm
index 5d5bc14a864..9d1b0bfb74a 100644
--- a/tff_modular/modules/psyonics/code/energistics.dm
+++ b/tff_modular/modules/psyonics/code/energistics.dm
@@ -6,8 +6,6 @@
/// Elecrocute - добавляет мутацию shock touch
/// Freeze - заковывает моба в лёд на небольшой промежуток.
-
-
// Добавить школу внушения
/mob/living/carbon/human/proc/try_add_energistics_school(tier = 0, additional_school = 0)
if(tier >= 0)
From 3de6d01e918fbf58037e21c0fb879b9abfad66cb Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Mon, 25 Nov 2024 02:55:20 +0700
Subject: [PATCH 04/17] =?UTF-8?q?=D0=98=D0=BA=D0=BE=D0=BD=D0=BA=D0=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
tff_modular/modules/psyonics/code/_quirk.dm | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/tff_modular/modules/psyonics/code/_quirk.dm b/tff_modular/modules/psyonics/code/_quirk.dm
index 9232a67b00b..22d25c28225 100644
--- a/tff_modular/modules/psyonics/code/_quirk.dm
+++ b/tff_modular/modules/psyonics/code/_quirk.dm
@@ -32,9 +32,8 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
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 = "bug"
+ icon = "star"
mob_trait = TRAIT_PSYONIC_USER
- //mail_goodies = list(/obj/item/toy/foamfinger) # ДОБАВИТЬ СЮДА ХИМИКАТЫ
veteran_only = TRUE
allow_for_donator = TRUE
// Текущий уровень маны
From e5f503595a4266aa85f4622c8b555daab7ba86d3 Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Mon, 25 Nov 2024 23:33:45 +0700
Subject: [PATCH 05/17] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=B8=D0=B5=20?=
=?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../modules/psyonics/code/_psyonics.dm | 67 +++++++++++++++++--
tff_modular/modules/psyonics/code/_quirk.dm | 28 ++++----
tff_modular/modules/psyonics/code/coersion.dm | 21 ++++--
tff_modular/modules/psyonics/code/cyberimp.dm | 2 +
.../modules/psyonics/code/energistics.dm | 31 +++++----
.../modules/psyonics/code/psychokinesis.dm | 11 ++-
.../modules/psyonics/code/redaction.dm | 25 +++++--
7 files changed, 141 insertions(+), 44 deletions(-)
diff --git a/tff_modular/modules/psyonics/code/_psyonics.dm b/tff_modular/modules/psyonics/code/_psyonics.dm
index 9ce9e605530..017d80e1570 100644
--- a/tff_modular/modules/psyonics/code/_psyonics.dm
+++ b/tff_modular/modules/psyonics/code/_psyonics.dm
@@ -1,3 +1,6 @@
+// Тут хранятся некрасивые базовые классы и прочее. Не смотрите сюда.
+
+// Спеллы для призвания предмета
/datum/action/cooldown/spell/conjure_item/psyonic
delete_old = FALSE
delete_on_failure = TRUE
@@ -59,7 +62,7 @@
drain_mana()
return ..()
-// Для спеллов типа self
+// Для спеллов которые применяются на себя тыком кнопки a.k.a. выдача генов
/datum/action/cooldown/spell/psyonic
// Сколько маны стоит кастануть спелл
var/mana_cost = 10
@@ -115,7 +118,8 @@
else
return TRUE
-/datum/action/cooldown/spell/pointed/psyonic
+// Спеллы для пострелушек
+/datum/action/cooldown/spell/pointed/projectile/psyonic
// Сколько маны стоит кастануть спелл
var/mana_cost = 10
// Некоторые спеллы могут отнимать стамину
@@ -134,6 +138,62 @@
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/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/pointed/projectile/psyonic/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/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
+ // Сколько маны стоит кастануть спелл
+ var/mana_cost = 10
+ // Некоторые спеллы могут отнимать стамину
+ var/stamina_cost = 0
+ // Что написать жертве
+ var/target_msg
+ // Сила способности
+ var/cast_power = 0
+ // Вторичная школа. Может дать особые эффекты при комбинациях
+ var/secondary_school = 0
+ // Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
+ 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)
. = ..()
@@ -173,6 +233,7 @@
else
return TRUE
+// Спеллы которыми надо каснуться чего либо
/datum/action/cooldown/spell/touch/psyonic
// Сколько маны стоит кастануть спелл
var/mana_cost = 10
@@ -186,12 +247,10 @@
var/secondary_school = 0
// Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
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
diff --git a/tff_modular/modules/psyonics/code/_quirk.dm b/tff_modular/modules/psyonics/code/_quirk.dm
index 22d25c28225..9a525c98585 100644
--- a/tff_modular/modules/psyonics/code/_quirk.dm
+++ b/tff_modular/modules/psyonics/code/_quirk.dm
@@ -18,21 +18,21 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
/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."
+ 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 is a psyonic"
+ 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 = "star"
+ icon = "fa-star"
mob_trait = TRAIT_PSYONIC_USER
veteran_only = TRUE
allow_for_donator = TRUE
@@ -104,7 +104,7 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
"[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
+ psyonic_level -= 1 // Обязаловка, иначе выдаст спеллы которые нельзя кастануть
/datum/quirk/psyonic/remove()
UnregisterSignal(quirk_holder, COMSIG_MOB_GET_STATUS_TAB_ITEMS)
@@ -119,14 +119,14 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
items += "Current psyonic energy: [mana_level]/[max_mana]"
/datum/quirk/psyonic/process(seconds_per_tick)
- if(HAS_TRAIT(quirk_holder, TRAIT_NO_PSYONICS))
+ if(HAS_TRAIT(quirk_holder, TRAIT_NO_PSYONICS)) // Имплант подавления регена
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))
+ if(HAS_TRAIT(quirk_holder, TRAIT_PRO_PSYONICS)) // Если есть имплант для увеличения регена маны
additional_mana *= 2
if(mana_level <= max_mana)
diff --git a/tff_modular/modules/psyonics/code/coersion.dm b/tff_modular/modules/psyonics/code/coersion.dm
index 32c37aebd08..b79d61cad10 100644
--- a/tff_modular/modules/psyonics/code/coersion.dm
+++ b/tff_modular/modules/psyonics/code/coersion.dm
@@ -32,6 +32,7 @@
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."
@@ -41,7 +42,6 @@
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.")
@@ -73,17 +73,15 @@
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)
@@ -132,6 +130,7 @@
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."
@@ -249,6 +248,7 @@
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."
@@ -257,7 +257,6 @@
cooldown_time = 0.5 SECONDS
mana_cost = 15
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.")
@@ -279,6 +278,7 @@
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)
@@ -294,6 +294,7 @@
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."
@@ -329,9 +330,18 @@
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."
@@ -383,6 +393,7 @@
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."
diff --git a/tff_modular/modules/psyonics/code/cyberimp.dm b/tff_modular/modules/psyonics/code/cyberimp.dm
index b4f16175d0c..fa2d8b5da27 100644
--- a/tff_modular/modules/psyonics/code/cyberimp.dm
+++ b/tff_modular/modules/psyonics/code/cyberimp.dm
@@ -1,5 +1,6 @@
#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."
@@ -14,6 +15,7 @@
. = ..()
REMOVE_TRAIT(organ_owner, TRAIT_NO_PSYONICS, IMPLANT_TRAIT)
+// Увеличивает реген маны в 1.5 раза
/obj/item/organ/internal/cyberimp/brain/pro_psyonic
name = "Psyonic Amplifier Model A"
desc = "This implant will boost psyonics energy regenerating."
diff --git a/tff_modular/modules/psyonics/code/energistics.dm b/tff_modular/modules/psyonics/code/energistics.dm
index 9d1b0bfb74a..e1d4d591a8a 100644
--- a/tff_modular/modules/psyonics/code/energistics.dm
+++ b/tff_modular/modules/psyonics/code/energistics.dm
@@ -23,9 +23,10 @@
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/psyonic/psyonic_freeze(src.mind || src, tier, additional_school)
+ 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."
@@ -34,7 +35,6 @@
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.")
@@ -72,6 +72,7 @@
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."
@@ -88,6 +89,8 @@
/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)
@@ -95,16 +98,15 @@
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
var/mana_cost = 10
-
projectile_type = /obj/projectile/energy/photon
/datum/action/cooldown/spell/basic_projectile/psyonic_laser/cast(atom/cast_on)
@@ -116,6 +118,7 @@
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."
@@ -137,6 +140,7 @@
else
return FALSE
+// Даёт мутацию Shock Touch
/datum/action/cooldown/spell/psyonic/psionic_electrocute
name = "Shock Touch"
desc = "Force yourself to recieve shock touch mutation."
@@ -158,24 +162,25 @@
drain_mana()
return TRUE
-/datum/action/cooldown/spell/pointed/psyonic/psyonic_freeze
+// Стреляет снарядом вотчера, замораживая жертву. Требует почти максимально возможный запас маны
+/datum/action/cooldown/spell/pointed/projectile/psyonic/psyonic_freeze
name = "Psyonic Freeze"
- desc = "Quickly freeze moist around target, encasing them in an ice prison."
+ 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
- active_msg = "You prepare to create freeze prison..."
+ cast_range = 9
+ active_msg = "You prepare to fire ice shard..."
+ deactive_msg = "You relax."
+ projectile_type = /obj/projectile/temp/watcher/ice_wing
-/datum/action/cooldown/spell/pointed/psyonic/psyonic_freeze/is_valid_target(atom/cast_on)
+/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/psyonic/psyonic_freeze/cast(mob/living/cast_on)
- . = ..()
- if(HAS_TRAIT(cast_on, TRAIT_RESISTCOLD))
- return FALSE
- cast_on.apply_status_effect(/datum/status_effect/freon/watcher/extended)
+/datum/action/cooldown/spell/pointed/projectile/psyonic/psyonic_freeze/cast(mob/living/cast_on)
drain_mana()
+ . = ..()
return TRUE
diff --git a/tff_modular/modules/psyonics/code/psychokinesis.dm b/tff_modular/modules/psyonics/code/psychokinesis.dm
index 29e970b1dc2..802aa52f9eb 100644
--- a/tff_modular/modules/psyonics/code/psychokinesis.dm
+++ b/tff_modular/modules/psyonics/code/psychokinesis.dm
@@ -7,6 +7,7 @@
/// 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)
@@ -26,6 +27,7 @@
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."
@@ -36,6 +38,7 @@
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."
@@ -46,6 +49,7 @@
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."
@@ -64,8 +68,11 @@
/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."
@@ -74,7 +81,6 @@
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.")
@@ -121,6 +127,7 @@
door_to_force.prying_so_hard = FALSE
return
+// Даёт мутацию телекинеза
/datum/action/cooldown/spell/psyonic/psionic_telekinesis
name = "Telekinesis"
desc = "Force yourself to recieve telekinesis mutation."
@@ -141,6 +148,7 @@
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."
@@ -149,7 +157,6 @@
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.")
diff --git a/tff_modular/modules/psyonics/code/redaction.dm b/tff_modular/modules/psyonics/code/redaction.dm
index 04718f8d19d..ec82e5747c9 100644
--- a/tff_modular/modules/psyonics/code/redaction.dm
+++ b/tff_modular/modules/psyonics/code/redaction.dm
@@ -7,6 +7,7 @@
/// 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)
@@ -21,6 +22,7 @@
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."
@@ -70,6 +72,8 @@
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, \
@@ -80,7 +84,6 @@
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.")
@@ -141,6 +144,7 @@
if(drop_loc)
parasite.forceMove(drop_loc)
+// Лечит токс урон.
/datum/action/cooldown/spell/touch/psyonic/psyonic_cleansing
name = "Psyonic Cleansing"
desc = "Filters patient blood out of toxin and removes accumulated radiation."
@@ -179,6 +183,14 @@
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."
@@ -217,11 +229,12 @@
else
return FALSE
-/datum/action/cooldown/spell/touch/psyonic/psyonic_revival/proc/accident_harm(mob/living/carbon/human/patient)
- patient.apply_damage(25, TOX, BODY_ZONE_CHEST)
- patient.take_bodypart_damage(25, wound_bonus = 100)
- patient.take_bodypart_damage(25, wound_bonus = 100, sharpness = SHARP_EDGED)
- owner.visible_message(span_warning("Something inside of [owner]s body cracks!"),
+// 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."))
From 138c440b0c2747891d66fe80630b299e84d3bcbe Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Mon, 25 Nov 2024 23:51:51 +0700
Subject: [PATCH 06/17] =?UTF-8?q?=D0=9F=D0=BE=D0=BC=D0=B5=D0=BD=D1=8F?=
=?UTF-8?q?=D1=82=D1=8C=20=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5?=
=?UTF-8?q?=20=D1=87=D1=82=D0=BE=D0=B1=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=20?=
=?UTF-8?q?=D0=BD=D0=B5=20=D1=80=D1=83=D0=B3=D0=B0=D0=BB=D1=81=D1=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
tff_modular/modules/psyonics/code/energistics.dm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tff_modular/modules/psyonics/code/energistics.dm b/tff_modular/modules/psyonics/code/energistics.dm
index e1d4d591a8a..62b2d4e0921 100644
--- a/tff_modular/modules/psyonics/code/energistics.dm
+++ b/tff_modular/modules/psyonics/code/energistics.dm
@@ -142,7 +142,7 @@
// Даёт мутацию Shock Touch
/datum/action/cooldown/spell/psyonic/psionic_electrocute
- name = "Shock Touch"
+ name = "Psyonic Shock Touch"
desc = "Force yourself to recieve shock touch mutation."
cooldown_time = 60 SECONDS
mana_cost = 60
From a209a46608586cbcd16413f74382e6a3df537446 Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Sun, 8 Dec 2024 00:30:10 +0700
Subject: [PATCH 07/17] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=BD=D1=8F=D1=82?=
=?UTF-8?q?=D1=8C=20=D0=BF=D0=BE=D0=B2=D1=82=D0=BE=D1=80=D1=8F=D1=8E=D1=89?=
=?UTF-8?q?=D0=B8=D0=B5=D1=81=D1=8F=20=D0=BF=D1=80=D0=BE=D0=BA=D0=B8=20?=
=?UTF-8?q?=D0=B2=D1=8B=D1=88=D0=B5=20=D0=BD=D0=B0=20=D1=83=D1=80=D0=BE?=
=?UTF-8?q?=D0=B2=D0=B5=D0=BD=D1=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../modules/psyonics/code/_psyonics.dm | 146 ++----------------
.../modules/psyonics/code/energistics.dm | 2 +-
2 files changed, 10 insertions(+), 138 deletions(-)
diff --git a/tff_modular/modules/psyonics/code/_psyonics.dm b/tff_modular/modules/psyonics/code/_psyonics.dm
index 017d80e1570..cf6475344d3 100644
--- a/tff_modular/modules/psyonics/code/_psyonics.dm
+++ b/tff_modular/modules/psyonics/code/_psyonics.dm
@@ -1,10 +1,6 @@
// Тут хранятся некрасивые базовые классы и прочее. Не смотрите сюда.
-// Спеллы для призвания предмета
-/datum/action/cooldown/spell/conjure_item/psyonic
- delete_old = FALSE
- delete_on_failure = TRUE
- requires_hands = TRUE
+/datum/action/cooldown/spell
// Сколько маны стоит кастануть спелл
var/mana_cost = 10
// Некоторые спеллы могут отнимать стамину
@@ -15,6 +11,12 @@
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
@@ -26,7 +28,7 @@
secondary_school = additional_school
// Проверяем достаточно ли маны
-/datum/action/cooldown/spell/conjure_item/psyonic/proc/check_for_mana()
+/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)
@@ -35,7 +37,7 @@
return FALSE
// Сосём ману у псионика
-/datum/action/cooldown/spell/conjure_item/psyonic/proc/drain_mana(forced = 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)
@@ -64,14 +66,6 @@
// Для спеллов которые применяются на себя тыком кнопки a.k.a. выдача генов
/datum/action/cooldown/spell/psyonic
- // Сколько маны стоит кастануть спелл
- var/mana_cost = 10
- // Некоторые спеллы могут отнимать стамину
- var/stamina_cost = 0
- // Сила способности
- var/cast_power = 0
- // Вторичная школа. Может дать особые эффекты при комбинациях
- var/secondary_school = 0
// Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
antimagic_flags = MAGIC_RESISTANCE_MIND
@@ -85,29 +79,6 @@
cast_power = power
secondary_school = additional_school
-// Проверяем достаточно ли маны
-/datum/action/cooldown/spell/psyonic/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/psyonic/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/psyonic/can_cast_spell(feedback)
. = ..()
if(!.)
@@ -120,16 +91,6 @@
// Спеллы для пострелушек
/datum/action/cooldown/spell/pointed/projectile/psyonic
- // Сколько маны стоит кастануть спелл
- var/mana_cost = 10
- // Некоторые спеллы могут отнимать стамину
- var/stamina_cost = 0
- // Что написать жертве
- var/target_msg
- // Сила способности
- var/cast_power = 0
- // Вторичная школа. Может дать особые эффекты при комбинациях
- var/secondary_school = 0
// Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
antimagic_flags = MAGIC_RESISTANCE_MIND
@@ -143,29 +104,6 @@
cast_power = power
secondary_school = additional_school
-// Проверяем достаточно ли маны
-/datum/action/cooldown/spell/pointed/projectile/psyonic/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/pointed/projectile/psyonic/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/pointed/projectile/psyonic/can_cast_spell(feedback)
. = ..()
if(!.)
@@ -178,16 +116,6 @@
// Направленные спеллы a.k.a. псионик выбирают цель на дистанции
/datum/action/cooldown/spell/pointed/psyonic
- // Сколько маны стоит кастануть спелл
- var/mana_cost = 10
- // Некоторые спеллы могут отнимать стамину
- var/stamina_cost = 0
- // Что написать жертве
- var/target_msg
- // Сила способности
- var/cast_power = 0
- // Вторичная школа. Может дать особые эффекты при комбинациях
- var/secondary_school = 0
// Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
antimagic_flags = MAGIC_RESISTANCE_MIND
school = SCHOOL_UNSET
@@ -200,29 +128,6 @@
cast_power = power
secondary_school = additional_school
-// Проверяем достаточно ли маны
-/datum/action/cooldown/spell/pointed/psyonic/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/pointed/psyonic/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/pointed/psyonic/can_cast_spell(feedback)
. = ..()
if(!.)
@@ -235,16 +140,6 @@
// Спеллы которыми надо каснуться чего либо
/datum/action/cooldown/spell/touch/psyonic
- // Сколько маны стоит кастануть спелл
- var/mana_cost = 10
- // Некоторые спеллы могут отнимать стамину
- var/stamina_cost = 0
- // Что написать жертве
- var/target_msg
- // Сила способности
- var/cast_power = 0
- // Вторичная школа. Может дать особые эффекты при комбинациях
- var/secondary_school = 0
// Псионические способности (в основном) не блокируются, но выводят особенные сообщения тем, кто это может
antimagic_flags = MAGIC_RESISTANCE_MIND
school = SCHOOL_UNSET
@@ -256,29 +151,6 @@
cast_power = power
secondary_school = additional_school
-// Проверяем достаточно ли маны
-/datum/action/cooldown/spell/touch/psyonic/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/touch/psyonic/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/touch/psyonic/can_cast_spell(feedback)
. = ..()
if(!.)
diff --git a/tff_modular/modules/psyonics/code/energistics.dm b/tff_modular/modules/psyonics/code/energistics.dm
index 62b2d4e0921..d53c8d03593 100644
--- a/tff_modular/modules/psyonics/code/energistics.dm
+++ b/tff_modular/modules/psyonics/code/energistics.dm
@@ -106,7 +106,7 @@
button_icon_state = "solarflare"
cooldown_time = 0 SECONDS
spell_requirements = NONE
- var/mana_cost = 10
+ mana_cost = 10
projectile_type = /obj/projectile/energy/photon
/datum/action/cooldown/spell/basic_projectile/psyonic_laser/cast(atom/cast_on)
From ec9c4b68c8bc78e2e49cdf592267ab5d7991a817 Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Sun, 8 Dec 2024 00:40:21 +0700
Subject: [PATCH 08/17] Update tgstation.dme
---
tgstation.dme | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tgstation.dme b/tgstation.dme
index 6236a52e4d8..d306c492701 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -8876,7 +8876,8 @@
#include "tff_modular\modules\police_nt\code\nt_police_loadaut.dm"
#include "tff_modular\modules\psyonics\code\_psyonics.dm"
#include "tff_modular\modules\psyonics\code\_quirk.dm"
-#includme "tff_modular\modules\psyonics\code\coersion.dm"
+#include "tff_modular\modules\psyonics\code\cyberimp.dm"
+#include "tff_modular\modules\psyonics\code\coersion.dm"
#include "tff_modular\odules\psyonics\code\cyberimp.dm"
#include "tff_modular\modules\psyonics\code\energistics.dm"
#include "tff_modular\modules\psyonics\code\psychokinesis.dm"
From 7c6ac64dafa19fc516db42ea3fe38d16c889f007 Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Sun, 8 Dec 2024 00:43:48 +0700
Subject: [PATCH 09/17] Update tgstation.dme
---
tgstation.dme | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tgstation.dme b/tgstation.dme
index d306c492701..04d3fb60871 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -8878,7 +8878,7 @@
#include "tff_modular\modules\psyonics\code\_quirk.dm"
#include "tff_modular\modules\psyonics\code\cyberimp.dm"
#include "tff_modular\modules\psyonics\code\coersion.dm"
-#include "tff_modular\odules\psyonics\code\cyberimp.dm"
+#include "tff_modular\modules\psyonics\code\cyberimp.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"
From d2c9ec5b16e0ddc2caea5052cd3ba6ef1b33f464 Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Sun, 8 Dec 2024 00:49:55 +0700
Subject: [PATCH 10/17] =?UTF-8?q?=D0=AF=D0=BF=D0=BE=D0=BD=D1=81=D0=BA?=
=?UTF-8?q?=D0=B8=D0=B9=20=D0=B8=D1=81=D1=82=D1=80=D0=B5=D0=B1=D0=B8=D1=82?=
=?UTF-8?q?=D0=B5=D0=BB=D1=8C...?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
tff_modular/modules/psyonics/code/coersion.dm | 2 +-
tgstation.dme | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/tff_modular/modules/psyonics/code/coersion.dm b/tff_modular/modules/psyonics/code/coersion.dm
index b79d61cad10..42188a071b9 100644
--- a/tff_modular/modules/psyonics/code/coersion.dm
+++ b/tff_modular/modules/psyonics/code/coersion.dm
@@ -302,7 +302,7 @@
button_icon_state = "spasm"
cooldown_time = 1 SECONDS
mana_cost = 40
- target_msg = "You muscles spasm!"
+ target_msg = "Your muscles spasm!"
active_msg = "You prepare to stun a target..."
/datum/action/cooldown/spell/pointed/psyonic/psyonic_spasm/New(Target)
diff --git a/tgstation.dme b/tgstation.dme
index 04d3fb60871..93f660155c8 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -8876,7 +8876,6 @@
#include "tff_modular\modules\police_nt\code\nt_police_loadaut.dm"
#include "tff_modular\modules\psyonics\code\_psyonics.dm"
#include "tff_modular\modules\psyonics\code\_quirk.dm"
-#include "tff_modular\modules\psyonics\code\cyberimp.dm"
#include "tff_modular\modules\psyonics\code\coersion.dm"
#include "tff_modular\modules\psyonics\code\cyberimp.dm"
#include "tff_modular\modules\psyonics\code\energistics.dm"
From d62d1423580d2d8c9a54a0a818bfe963c05bbe5a Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Sun, 8 Dec 2024 01:14:16 +0700
Subject: [PATCH 11/17] =?UTF-8?q?=D0=A8=D0=B0=D0=BF=D0=BE=D1=87=D0=BA?=
=?UTF-8?q?=D0=B0=20=D0=B8=D0=B7=20=D1=84=D0=BE=D0=BB=D1=8C=D0=B3=D0=B8=20?=
=?UTF-8?q?=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=B7=D0=B0=D1=89=D0=B8?=
=?UTF-8?q?=D1=89=D0=B0=D0=B5=D1=82=20=D0=BE=D1=82=20=D1=88=D0=BA=D0=BE?=
=?UTF-8?q?=D0=BB=D1=8B=20=D0=B2=D0=BD=D1=83=D1=88=D0=B5=D0=BD=D0=B8=D1=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
tff_modular/modules/psyonics/code/coersion.dm | 35 +++++++++++++++++++
tgstation.dme | 4 +--
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/tff_modular/modules/psyonics/code/coersion.dm b/tff_modular/modules/psyonics/code/coersion.dm
index 42188a071b9..e0ae3f96713 100644
--- a/tff_modular/modules/psyonics/code/coersion.dm
+++ b/tff_modular/modules/psyonics/code/coersion.dm
@@ -10,6 +10,12 @@
/// 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)
@@ -149,6 +155,11 @@
/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!"))
@@ -265,6 +276,12 @@
/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."))
@@ -321,6 +338,12 @@
/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
@@ -361,6 +384,12 @@
/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
@@ -420,6 +449,12 @@
/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
diff --git a/tgstation.dme b/tgstation.dme
index 93f660155c8..e4bfbace146 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -8874,6 +8874,8 @@
#include "tff_modular\modules\police_nt\code\nt_police.dm"
#include "tff_modular\modules\police_nt\code\nt_police_items.dm"
#include "tff_modular\modules\police_nt\code\nt_police_loadaut.dm"
+#include "tff_modular\modules\poster_contest\contraband.dm"
+#include "tff_modular\modules\poster_contest\official.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"
@@ -8881,8 +8883,6 @@
#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\poster_contest\contraband.dm"
-#include "tff_modular\modules\poster_contest\official.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"
From d318c0187fbec75efb5477dfb76a70ad31b90dcd Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Sun, 8 Dec 2024 02:31:09 +0700
Subject: [PATCH 12/17] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B9=20=D1=81?=
=?UTF-8?q?=D0=BF=D0=B5=D0=BB=D0=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../modules/psyonics/code/redaction.dm | 54 ++++++++++++++++++-
1 file changed, 52 insertions(+), 2 deletions(-)
diff --git a/tff_modular/modules/psyonics/code/redaction.dm b/tff_modular/modules/psyonics/code/redaction.dm
index ec82e5747c9..d11d2e1d897 100644
--- a/tff_modular/modules/psyonics/code/redaction.dm
+++ b/tff_modular/modules/psyonics/code/redaction.dm
@@ -1,9 +1,10 @@
#define HALFWAYCRITDEATH ((HEALTH_THRESHOLD_CRIT + HEALTH_THRESHOLD_DEAD) * 0.5)
/// Школа лечения
-/// Имеет 4 спелла в данный момент
+/// Имеет 5 спеллов в данный момент
/// Roentgen - обычный мед скан, работающий на дистанции
-/// Меnding - лечит кровь, открытые раны и окси урон. Также удаляет импланты/ксеноморфов из тела при определённых условиях
+/// Меnding - лечит кровь, открытые раны и окси урон. Также удаляет импланты/ксеноморфов из тела при определённых условиях.
+/// Ethanol Synthesis - если интент на харма, то "превращает" упитанность в алкоголь на дистанции. Любой другой - наоборот.
/// Cleansing - лечит токс урон
/// Revive - пытается оживить труп
@@ -15,6 +16,8 @@
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)
+ 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 >= 2)
var/datum/action/new_action = new /datum/action/cooldown/spell/touch/psyonic/psyonic_cleansing(src.mind || src, tier, additional_school)
new_action.Grant(src)
@@ -144,6 +147,53 @@
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) && !issynthetic(cast_on))
+ 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"
From 1d0f4d27b17085f3535591cc830d798639f3c7a6 Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Sat, 14 Dec 2024 00:39:15 +0700
Subject: [PATCH 13/17] =?UTF-8?q?Patch=20=E2=84=961?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Фикс комментов и грамматики.
Подняли стоимость стан дубинки с 15 до 40. То есть максимум 2 удара подряд, пока не кончится мана
Пофиксил спелл заморозки
Передвинул спелл оживления на один уровень повыше, аналогично для очищения
---
tff_modular/modules/psyonics/code/coersion.dm | 2 +-
tff_modular/modules/psyonics/code/cyberimp.dm | 4 ++--
tff_modular/modules/psyonics/code/energistics.dm | 16 +++++++++++++++-
tff_modular/modules/psyonics/code/redaction.dm | 5 +++--
4 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/tff_modular/modules/psyonics/code/coersion.dm b/tff_modular/modules/psyonics/code/coersion.dm
index e0ae3f96713..b5d7605fc63 100644
--- a/tff_modular/modules/psyonics/code/coersion.dm
+++ b/tff_modular/modules/psyonics/code/coersion.dm
@@ -266,7 +266,7 @@
button_icon = 'icons/obj/weapons/baton.dmi'
button_icon_state = "stunbaton_active"
cooldown_time = 0.5 SECONDS
- mana_cost = 15
+ 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.")
diff --git a/tff_modular/modules/psyonics/code/cyberimp.dm b/tff_modular/modules/psyonics/code/cyberimp.dm
index fa2d8b5da27..130ade04a83 100644
--- a/tff_modular/modules/psyonics/code/cyberimp.dm
+++ b/tff_modular/modules/psyonics/code/cyberimp.dm
@@ -15,10 +15,10 @@
. = ..()
REMOVE_TRAIT(organ_owner, TRAIT_NO_PSYONICS, IMPLANT_TRAIT)
-// Увеличивает реген маны в 1.5 раза
+// Увеличивает реген маны в 2 раза
/obj/item/organ/internal/cyberimp/brain/pro_psyonic
name = "Psyonic Amplifier Model A"
- desc = "This implant will boost psyonics energy regenerating."
+ desc = "This implant will boost psyonics energy regeneration."
icon_state = "brain_implant_rebooter"
slot = ORGAN_SLOT_BRAIN_PSYONIC
diff --git a/tff_modular/modules/psyonics/code/energistics.dm b/tff_modular/modules/psyonics/code/energistics.dm
index d53c8d03593..b855fc261e3 100644
--- a/tff_modular/modules/psyonics/code/energistics.dm
+++ b/tff_modular/modules/psyonics/code/energistics.dm
@@ -173,7 +173,7 @@
cast_range = 9
active_msg = "You prepare to fire ice shard..."
deactive_msg = "You relax."
- projectile_type = /obj/projectile/temp/watcher/ice_wing
+ 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))
@@ -184,3 +184,17 @@
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/redaction.dm b/tff_modular/modules/psyonics/code/redaction.dm
index d11d2e1d897..129845467cc 100644
--- a/tff_modular/modules/psyonics/code/redaction.dm
+++ b/tff_modular/modules/psyonics/code/redaction.dm
@@ -16,12 +16,13 @@
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 >= 2)
+ 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 >= 3)
+ 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)
From 047302a5754d5bade16dd28ebdf4719722c05442 Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Mon, 16 Dec 2024 21:37:35 +0700
Subject: [PATCH 14/17] =?UTF-8?q?Patch=20=E2=84=962?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Добавлено побольше проверок на синтов
Чтение разума доступно только психологу(и то если зарандомит >=2 уровень)
У психолога есть один дополнительные рандом
Люди с МЩ более не регенерируют ману.
---
tff_modular/modules/psyonics/code/_quirk.dm | 14 +++++++---
tff_modular/modules/psyonics/code/coersion.dm | 26 ++++++++++++++-----
.../modules/psyonics/code/energistics.dm | 3 +++
.../modules/psyonics/code/redaction.dm | 15 ++++++++---
4 files changed, 46 insertions(+), 12 deletions(-)
diff --git a/tff_modular/modules/psyonics/code/_quirk.dm b/tff_modular/modules/psyonics/code/_quirk.dm
index 9a525c98585..04d4d4b0440 100644
--- a/tff_modular/modules/psyonics/code/_quirk.dm
+++ b/tff_modular/modules/psyonics/code/_quirk.dm
@@ -7,6 +7,7 @@
#define MASTER_PSYONIC 2
#define GRANDMASTER_PSYONIC 3
#define PARAMOUNT_PSYONIC 4
+#define GREATEST_PSYONIC 5
GLOBAL_LIST_INIT(psyonic_schools, list(
"Redaction",
@@ -56,25 +57,29 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
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 = "Lambda"
+ psyonic_level_string = "Kappa"
if(GRANDMASTER_PSYONIC)
- psyonic_level_string = "Theta"
+ 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))
- var/mob/living/carbon/human/whom_to_give = quirk_holder
if(school == secondary_school)
psyonic_level += 1 // Если вторичка совпадает с первой - добавляем один уровень, но не меняем описание
switch(school)
@@ -122,6 +127,9 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
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
diff --git a/tff_modular/modules/psyonics/code/coersion.dm b/tff_modular/modules/psyonics/code/coersion.dm
index b5d7605fc63..445593b74c2 100644
--- a/tff_modular/modules/psyonics/code/coersion.dm
+++ b/tff_modular/modules/psyonics/code/coersion.dm
@@ -3,7 +3,7 @@
/// Школа внушения. 7 спеллов
/// Psyonic assay - скан, является ли человек псиоником
/// Psyonic focus - лечение мозга и псих болезней
-/// Psyonic mind read - продвинутое чтение разума(Как обычная ген. мутация, но + работа + воспоминания). Имба
+/// Psyonic mind read - продвинутое чтение разума(Как обычная ген. мутация, но + работа + воспоминания). Выдаётся только и ТОЛЬКО психологу, если он псионик
/// Psyonic agony - работает как стан дубинка, исчезает после одного удара
/// Psyonic spasm - станит на полсекунды, заставляет выронить всё из рук. Работает дистанционно
/// Psyonic hypnosis - гипнотизирует цель фразой, которую выбрал псионик. ERP IS BAD. DO NOT ERP.
@@ -25,15 +25,16 @@
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)
- 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)
+ 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)
- var/datum/action/new_action3 = new /datum/action/cooldown/spell/touch/psyonic/psyonic_hypnosis(src.mind || src, tier, additional_school)
- new_action3.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)
@@ -71,6 +72,10 @@
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]."),
@@ -183,6 +188,10 @@
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)
@@ -286,6 +295,11 @@
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)
@@ -390,7 +404,7 @@
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))
+ 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))
diff --git a/tff_modular/modules/psyonics/code/energistics.dm b/tff_modular/modules/psyonics/code/energistics.dm
index b855fc261e3..37b47cd7ed2 100644
--- a/tff_modular/modules/psyonics/code/energistics.dm
+++ b/tff_modular/modules/psyonics/code/energistics.dm
@@ -44,6 +44,9 @@
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]."),
diff --git a/tff_modular/modules/psyonics/code/redaction.dm b/tff_modular/modules/psyonics/code/redaction.dm
index 129845467cc..818305b46e2 100644
--- a/tff_modular/modules/psyonics/code/redaction.dm
+++ b/tff_modular/modules/psyonics/code/redaction.dm
@@ -96,6 +96,9 @@
/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
@@ -159,9 +162,11 @@
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) && !issynthetic(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)
@@ -198,7 +203,7 @@
// Лечит токс урон.
/datum/action/cooldown/spell/touch/psyonic/psyonic_cleansing
name = "Psyonic Cleansing"
- desc = "Filters patient blood out of toxin and removes accumulated radiation."
+ 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
@@ -214,6 +219,9 @@
/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
@@ -275,6 +283,7 @@
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
From 7e82900c3d34fca03d8a9fc97ba17306cccb9c08 Mon Sep 17 00:00:00 2001
From: Iajret
Date: Fri, 20 Dec 2024 00:35:14 +0300
Subject: [PATCH 15/17] Update redaction.dm
---
tff_modular/modules/psyonics/code/redaction.dm | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tff_modular/modules/psyonics/code/redaction.dm b/tff_modular/modules/psyonics/code/redaction.dm
index 818305b46e2..9c797baa0a8 100644
--- a/tff_modular/modules/psyonics/code/redaction.dm
+++ b/tff_modular/modules/psyonics/code/redaction.dm
@@ -139,7 +139,7 @@
)
if(patient.get_organ_slot("parasite_egg") && cast_power >=4) // Удаляем ксеноморфов
- var/obj/item/organ/internal/body_egg/parasite = patient.get_organ_slot("parasite_egg")
+ 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!"),
@@ -308,7 +308,7 @@
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/internal/heart/target_heart = patient.get_organ_slot(ORGAN_SLOT_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
@@ -325,7 +325,7 @@
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/internal/brain/target_brain = patient.get_organ_slot(ORGAN_SLOT_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)
From 55738eb1ef783885ed6f0b8fe0af08e438677026 Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Mon, 23 Dec 2024 02:21:36 +0700
Subject: [PATCH 16/17] =?UTF-8?q?=D0=9B=D0=B8=D1=86=D0=B5=D0=BD=D0=B7?=
=?UTF-8?q?=D0=B8=D0=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
tff_modular/modules/psyonics/code/_quirk.dm | 21 +++
.../modules/psyonics/code/documents.dm | 73 +++++++++++
tff_modular/modules/psyonics/icons/card.dmi | Bin 0 -> 332 bytes
tgstation.dme | 1 +
.../tgui/interfaces/PsyonicLicense.tsx | 120 ++++++++++++++++++
5 files changed, 215 insertions(+)
create mode 100644 tff_modular/modules/psyonics/code/documents.dm
create mode 100644 tff_modular/modules/psyonics/icons/card.dmi
create mode 100644 tgui/packages/tgui/interfaces/PsyonicLicense.tsx
diff --git a/tff_modular/modules/psyonics/code/_quirk.dm b/tff_modular/modules/psyonics/code/_quirk.dm
index 04d4d4b0440..d48444b5e76 100644
--- a/tff_modular/modules/psyonics/code/_quirk.dm
+++ b/tff_modular/modules/psyonics/code/_quirk.dm
@@ -49,6 +49,9 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
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)
@@ -111,6 +114,24 @@ GLOBAL_LIST_INIT(psyonic_schools, list(
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)
diff --git a/tff_modular/modules/psyonics/code/documents.dm b/tff_modular/modules/psyonics/code/documents.dm
new file mode 100644
index 00000000000..fa23af94a77
--- /dev/null
+++ b/tff_modular/modules/psyonics/code/documents.dm
@@ -0,0 +1,73 @@
+/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(!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/icons/card.dmi b/tff_modular/modules/psyonics/icons/card.dmi
new file mode 100644
index 0000000000000000000000000000000000000000..f7a2eee1ba03641ad64b95fd666a46c94dff3c24
GIT binary patch
literal 332
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvxd5LK*8>L*1m|=z{r&G0QFQzE
zZEc%?|Ns9#XJ9C=>tDBir&43cSD;G9k|4ie28U-i(twp3}OMiyu9At-axX6tK|@*i^7$o
zM@7||1Uxv43w;ZDk1jvNx`b^$*YqnQ;@3lBv?eh4ERf+@z$9Zl(Si9yxvWRSJI7vO
a1_mj6n?`lTXQ4p*7(8A5T-G@yGywp@j(P9^
literal 0
HcmV?d00001
diff --git a/tgstation.dme b/tgstation.dme
index f7990bd66bf..4e14f21e83a 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -8904,6 +8904,7 @@
#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"
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}
+
+
+
+
+
+
+
+
+ {
+ 'Данная лицензия удостоверяет то, что пользователь является псиоником.'
+ }
+ {
+ ' Лицензия пользователя псионики позволяет пользователю применять способности в целях самозащиты, оказания лечебной помощи и в прочих, установленных законом случаев.'
+ }
+ {
+ ' Пользователь псионики обязан иметь лицензию при себе всё время, иначе его действия могут быть классифицированы СБ НТ как нелегальные и повлечь за собой юридические последствия.'
+ }
+
+
+
+
+
+
+ {'ПОДДЕЛКА ЛИЦЕНЗИИ КАРАЕТСЯ ЗАКОНОМ'}
+
+
+
+
+
+ );
+};
From 6296ee467bd613bda5fb92197574a4c2d9ef6fb1 Mon Sep 17 00:00:00 2001
From: SuperDrish <59139863+Microvolnovka19@users.noreply.github.com>
Date: Mon, 23 Dec 2024 02:38:20 +0700
Subject: [PATCH 17/17] fix
---
tff_modular/modules/psyonics/code/documents.dm | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tff_modular/modules/psyonics/code/documents.dm b/tff_modular/modules/psyonics/code/documents.dm
index fa23af94a77..81a6386c6ac 100644
--- a/tff_modular/modules/psyonics/code/documents.dm
+++ b/tff_modular/modules/psyonics/code/documents.dm
@@ -56,6 +56,8 @@
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)