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;pyU 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)