Revert "Psionics Rework: Kyoushiki Murasaki (#16805)" (#16948)

This reverts commit 27c40bdaf6.
This commit is contained in:
Matt Atlas
2023-07-31 01:51:40 +00:00
committed by GitHub
parent 06ca379a91
commit 87f637564f
145 changed files with 1617 additions and 3162 deletions
@@ -1,29 +0,0 @@
/singleton/psionic_power
/// Ability name.
var/name
/// Description of what the ability does.
var/desc
/// Spell object to spawn. Must be a path.
var/spell_path
/// Spell icon state.
var/icon_state
/// Ability flags define who can pick an ability - ship characters, adminspawn characters, antags.
var/ability_flags
/// Minimum required rank to use an ability.
var/minimum_rank = PSI_RANK_SENSITIVE
/// Point shop cost.
var/point_cost = 1
/// Called when a power is given to a mob.
/singleton/psionic_power/proc/apply(var/mob/living/carbon/human/H)
if(H.ability_master)
var/obj/spellbutton/spell = new(H, spell_path, name, icon_state)
H.ability_master.add_psionic_ability(spell, icon_state, src)
H.psi.psionic_powers |= type
return TRUE
else
log_debug("Psionic power [src.name] given to mob [H] without ability master!")
return FALSE
/// A psionic power is made up of two elements: the psionic power singleton (which defines the ability metadata such as its name, description, and cost)
/// and the physical spell object, which defines its behaviour when used. Keep in mind that ALL psionic abilities MUST have the ASPECT_PSIONIC aspect!
@@ -1,58 +0,0 @@
/singleton/psionic_power/air_bullet
name = "Air Bullet"
desc = "Wrap air in a psionic bubble, compress it, then send it flying at your enemies. Activate the spell in hand to create up to six air bullets, then \
fire them by clicking something."
icon_state = "tech_frostaura"
point_cost = 3
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/projectile/air_bullet
/obj/item/spell/projectile/air_bullet
name = "air bullet"
desc = "Not really a big bang attack, but it's actually pretty close."
icon_state = "force_missile"
cast_methods = CAST_RANGED|CAST_USE
aspect = ASPECT_PSIONIC
spell_projectile = /obj/item/projectile/air_bullet
fire_sound = 'sound/weapons/wave.ogg'
cooldown = 5
psi_cost = 0
var/bullets = 0
var/max_bullets = 6
/obj/item/spell/projectile/air_bullet/on_use_cast(mob/user)
. = ..()
if(!.)
return
if(!isliving(user))
return
var/mob/living/L = user
if(bullets < max_bullets)
if(do_after(L, 0.5 SECONDS))
to_chat(L, SPAN_NOTICE("You compress some air bullets between your fingers."))
bullets = min(bullets + 2, 6)
maptext = SMALL_FONTS(7, bullets)
playsound(L, 'sound/weapons/unjam.ogg', 25)
L.psi.spend_power(3)
if(bullets >= 6)
to_chat(L, SPAN_NOTICE("You finish compressing all the bullets you can hold."))
return
on_use_cast(L)
/obj/item/spell/projectile/air_bullet/on_ranged_cast(atom/hit_atom, mob/living/user, atom/pb_target)
if(!bullets)
to_chat(user, SPAN_WARNING("You flick your fingers, but find that you don't have any air bullets left!"))
return
. = ..(hit_atom, user, pb_target)
if(.)
bullets--
maptext = SMALL_FONTS(7, bullets)
/obj/item/projectile/air_bullet
name = "air bullet"
icon_state = "plasma_bolt"
damage = 30
armor_penetration = 10
impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_BULLET_MEAT, BULLET_IMPACT_METAL = SOUNDS_BULLET_METAL)
damage_flags = DAMAGE_FLAG_BULLET
damage_type = DAMAGE_BRUTE
-80
View File
@@ -1,80 +0,0 @@
/singleton/psionic_power/assay
name = "Assay"
desc = "Assay a creature's psionic level. Using Assay will also allow you to see psionic auras."
icon_state = "wiz_blind"
point_cost = 0
ability_flags = PSI_FLAG_FOUNDATIONAL
spell_path = /obj/item/spell/assay
/obj/item/spell/assay
name = "assay"
desc = "Read someone's psionic potential."
icon_state = "generic"
cast_methods = CAST_MELEE|CAST_INNATE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 2
/obj/item/spell/assay/on_innate_cast(mob/user)
if(!isliving(user))
return
. = ..()
if(!.)
return
var/mob/living/L = user
to_chat(user, SPAN_NOTICE("You can now see psionic auras."))
L.psi.show_auras()
/obj/item/spell/assay/Destroy()
if(isliving(owner))
var/mob/living/L = owner
to_chat(L, SPAN_NOTICE("You can no longer see psionic auras."))
L.psi.hide_auras()
return ..()
/obj/item/spell/assay/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(!.)
return
if(!ishuman(hit_atom))
return
if(!isliving(user))
return
var/mob/living/carbon/human/target = hit_atom
if(target.stat == DEAD || HAS_FLAG(target.status_flags, FAKEDEATH))
to_chat(user, SPAN_WARNING("Psionic power does not flow through a dead person."))
return
var/psi_blocked = target.is_psi_blocked()
if(psi_blocked)
to_chat(user, psi_blocked)
return
user.visible_message(SPAN_NOTICE("[user] lays both [user.get_pronoun("his")] palms on [target]'s temples..."),
SPAN_NOTICE("You lay your palms on [target]'s temples and begin tracing their Nlom signature..."))
if(do_mob(user, target, 4 SECONDS))
if(!target.psi)
to_chat(user, SPAN_NOTICE("[target] is psionically perceptive, but nothing more: [target.get_pronoun("he")] cannot manipulate the fabric that weaves \
this universe."))
if(target.psi)
switch(target.psi.get_rank())
if(PSI_RANK_SENSITIVE)
to_chat(user, SPAN_NOTICE("[target] is psionically sensitive, just like your typical Skrell. They can manipulate psionics, but not to a very \
precise degree."))
if(PSI_RANK_HARMONIOUS)
to_chat(user, SPAN_WARNING("[target] is psionically harmonious. This isn't a feat just about anyone can manage: only those born with a certain \
psionic attitude among Skrell can reach this level, and with strenuous training. They are capable of using psionics \
with very fine precision."))
if(PSI_RANK_APEX)
to_chat(user, SPAN_DANGER("Your psionic power is dwarfed by [target]. Just like a supernova in the night sky, their signature is absolutely brilliant \
beyond comprehension. This is the apex of psionic power, you are sure."))
if(PSI_RANK_LIMITLESS)
to_chat(user, SPAN_DANGER("A psionic power like this shouldn't be possible... what in the Stars is going on?"))
@@ -1,30 +0,0 @@
/singleton/psionic_power/barrier
name = "Psionic Barrier"
desc = "Activate in hand to give yourself psionic armour. This armour is slightly worse than generic heavy armour, and has a small passive upkeep."
icon_state = "const_mend"
point_cost = 1
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/barrier
/obj/item/spell/barrier
name = "psionic barrier"
icon_state = "generic"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 20
psi_cost = 30
/obj/item/spell/barrier/on_use_cast(mob/user, bypass_psi_check)
if(!ishuman(user))
return
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = user
var/armor = H.psi.GetComponent(/datum/component/armor/psionic)
if(armor)
to_chat(H, SPAN_WARNING("You are already enveloped by psionic armour!"))
return
to_chat(H, SPAN_NOTICE("You are enveloped by a protective warmth."))
H.psi.AddComponent(/datum/component/armor/psionic, list(laser = ARMOR_LASER_MEDIUM, bullet = ARMOR_BALLISTIC_CARBINE, energy = ARMOR_ENERGY_RESISTANT, melee = ARMOR_MELEE_RESISTANT))
-33
View File
@@ -1,33 +0,0 @@
/singleton/psionic_power/bubble
name = "Bubble"
desc = "Create a protective bubble around you that removes your need to breathe or wear voidsuits in EVA."
icon_state = "tech_condensation"
point_cost = 1
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/bubble
/obj/item/spell/bubble
name = "bubble"
icon_state = "shield"
cast_methods = CAST_INNATE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 10
/obj/item/spell/bubble/Destroy()
to_chat(owner, SPAN_NOTICE("Your bubble fades away."))
REMOVE_TRAIT(owner, TRAIT_NO_BREATHE, TRAIT_SOURCE_PSIONICS)
REMOVE_TRAIT(owner, TRAIT_PRESSURE_IMMUNITY, TRAIT_SOURCE_PSIONICS)
return ..()
/obj/item/spell/bubble/on_innate_cast(mob/user)
if(!ishuman(user))
return
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = user
H.visible_message(SPAN_NOTICE("A protective bubble forms around [H]."), SPAN_NOTICE("You form a protective bubble around yourself."))
ADD_TRAIT(H, TRAIT_NO_BREATHE, TRAIT_SOURCE_PSIONICS)
ADD_TRAIT(H, TRAIT_PRESSURE_IMMUNITY, TRAIT_SOURCE_PSIONICS)
-32
View File
@@ -1,32 +0,0 @@
/singleton/psionic_power/charge
name = "Charge"
desc = "Use this spell on an item with a cell to charge it."
icon_state = "wiz_charge"
point_cost = 1
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/charge
/obj/item/spell/charge
name = "charge"
icon_state = "audible_deception"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 5
psi_cost = 5
/obj/item/spell/charge/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
var/obj/item/cell/C = hit_atom.get_cell()
if(!C)
return
if(C.percent() >= 100)
to_chat(user, SPAN_WARNING("That cell is already charged."))
return
psi_cost = C.maxcharge * 0.001
. = ..()
if(!.)
return
user.visible_message(SPAN_NOTICE("[user] lays [get_pronoun("his")] hand on \the [C]..."), SPAN_NOTICE("You lay your hand on \the [C]..."),)
if(do_after(user, 1 SECOND))
to_chat(user, SPAN_WARNING("You restore some power to \the [C]."))
C.give(C.maxcharge * 0.1)
psi_cost = initial(psi_cost)
@@ -1,78 +0,0 @@
/singleton/psionic_power/command
name = "Command"
desc = "Send a psionic command to a target. It must be one word. The suggestion ends when they finish the task, when a minute passes or \
when they take notable damage."
icon_state = "wiz_blink"
spell_path = /obj/item/spell/command
ability_flags = PSI_FLAG_EVENT
/obj/item/spell/command
name = "command"
desc = "Send a psionic command to a target."
icon_state = "apportation"
cast_methods = CAST_RANGED|CAST_MELEE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 50
/obj/item/spell/command/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(!.)
return
command(hit_atom, user)
/obj/item/spell/command/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(!.)
return
command(hit_atom, user)
/obj/item/spell/command/proc/command(atom/hit_atom, mob/user)
if(!isliving(hit_atom))
return
if(!isliving(user))
return
var/mob/living/L = user
var/multi_word = L.psi.get_rank() > PSI_RANK_HARMONIOUS
var/mob/living/target = hit_atom
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("Not even a psion of your level can speak to the dead."))
return
var/psi_blocked = target.is_psi_blocked()
if(psi_blocked)
to_chat(user, psi_blocked)
return
user.visible_message(SPAN_CULT("[user] opens a palm and aims it at [hit_atom]..."))
var/text = input("Enter your command. [multi_word ? "This command may be up to 5 words." : "This command may only be one word."]", "Command", null, null)
if(!text)
return
text = formalize_text(text)
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("Not even a psion of your level can speak to the dead."))
return
log_say("[key_name(user)] issued a [multi_word ? "multi-word" : "single-word"] command to [key_name(target)]: [text]",ckey=key_name(src))
to_chat(user, SPAN_CULT("You psionically command to [target]: [text]"))
for (var/mob/M in player_list)
if (istype(M, /mob/abstract/new_player))
continue
else if(M.stat == DEAD && HAS_FLAG(M.client.prefs.toggles, CHAT_GHOSTEARS))
to_chat(M, "<span class='notice'>[user] psionically commands to [target]:</span> [text]")
var/mob/living/carbon/human/H = target
if(H.has_psionics())
to_chat(H, SPAN_CULT("<b>You are psionically commanded to carry out the following task:</b> [text]."))
else if(target.has_psi_aug())
to_chat(H, SPAN_CULT("<b>You sense [user]'s psyche link with your psi-receiver, a command sliding into your mind:</b> [text]"))
else
to_chat(H, SPAN_CULT("<b>A thought from outside your consciousness slips into your mind:</b> [text]"))
to_chat(H, SPAN_DANGER("You are required to follow this command. It ends when a minute has passed, when you carry it out, or when you take a significant amount \
of damage. [multi_word ? "You are required to follow up to five words." : "You are required to only follow the first word of the command."]"))
@@ -1,71 +0,0 @@
/singleton/psionic_power/commune
name = "Commune"
desc = "Psionically commune with the target."
icon_state = "tech_audibledeception"
point_cost = 0
ability_flags = PSI_FLAG_FOUNDATIONAL
spell_path = /obj/item/spell/commune
/obj/item/spell/commune
name = "commune"
desc = "Déjà-vu."
icon_state = "overload"
cast_methods = CAST_RANGED|CAST_MELEE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 15
/obj/item/spell/commune/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(!.)
return
commune(hit_atom, user)
/obj/item/spell/commune/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(!.)
return
commune(hit_atom, user)
/obj/item/spell/commune/proc/commune(atom/hit_atom, mob/user)
if(!isliving(hit_atom))
return
var/mob/living/target = hit_atom
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("Not even a psion of your level can speak to the dead."))
return
var/psi_blocked = target.is_psi_blocked()
if(psi_blocked)
to_chat(user, psi_blocked)
return
user.visible_message(SPAN_NOTICE("<i>[user] blinks, their eyes briefly developing an unnatural shine.</i>"))
var/text = input("What would you like to say?", "Speak to creature", null, null)
text = sanitize(text)
if(!text)
return
text = formalize_text(text)
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("Not even a psion of your level can speak to the dead."))
return
log_say("[key_name(user)] communed to [key_name(target)]: [text]",ckey=key_name(src))
to_chat(user, SPAN_CULT("You psionically say to [target]: [text]"))
for (var/mob/M in player_list)
if (istype(M, /mob/abstract/new_player))
continue
else if(M.stat == DEAD && M.client.prefs.toggles & CHAT_GHOSTEARS)
to_chat(M, "<span class='notice'>[user] psionically says to [target]:</span> [text]")
var/mob/living/carbon/human/H = target
if(H.has_psionics())
to_chat(H, SPAN_CULT("<b>You instinctively sense [user] passing a thought into your mind:</b> [text]"))
else if(target.has_psi_aug())
to_chat(H, SPAN_CULT("<b>You sense [user]'s psyche link with your psi-receiver, a thought sliding into your mind:</b> [text]"))
else
to_chat(H, SPAN_ALIEN("<b>A thought from outside your consciousness slips into your mind:</b> [text]"))
@@ -1,30 +0,0 @@
/singleton/psionic_power/electrocute
name = "Electrocute"
desc = "Administer a painful amount of psionic shock to the nervous system of a foe in melee range, causing burn and agony damage. "
icon_state = "tech_shockaura"
point_cost = 2
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/electrocute
/obj/item/spell/electrocute
name = "electrocute"
icon_state = "chain_lightning"
cast_methods = CAST_MELEE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 5
hitsound = 'sound/effects/psi/power_evoke.ogg'
attack_verb = list("lays a palm on")
/obj/item/spell/electrocute/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
if(!ishuman(hit_atom))
to_chat(user, SPAN_WARNING("That won't work!"))
return
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = hit_atom
H.adjustHalLoss(20)
H.adjustFireLoss(20)
H.flash_pain(20)
to_chat(H, SPAN_DANGER("A sharp, stinging pain enters your mind!"))
@@ -1,70 +0,0 @@
/singleton/psionic_power/emotional_suggestion
name = "Emotional Suggestion"
desc = "Allows you to psionically commune with the target."
icon_state = "tech_gambit"
ability_flags = PSI_FLAG_EVENT|PSI_FLAG_CANON
spell_path = /obj/item/spell/emotional_suggestion
/obj/item/spell/emotional_suggestion
name = "emotional suggestion"
desc = "Suggest an emotion to someone."
icon_state = "generic"
cast_methods = CAST_RANGED|CAST_MELEE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 15
/obj/item/spell/emotional_suggestion/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(!.)
return
emotional_suggestion(hit_atom, user)
/obj/item/spell/emotional_suggestion/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(!.)
return
emotional_suggestion(hit_atom, user)
/obj/item/spell/emotional_suggestion/proc/emotional_suggestion(atom/hit_atom, mob/user)
if(!isliving(hit_atom))
return
var/mob/living/target = hit_atom
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("Not even a psion of your level can suggest to the dead."))
return
var/psi_blocked = target.is_psi_blocked()
if(psi_blocked)
to_chat(user, psi_blocked)
return
user.visible_message(SPAN_NOTICE("<i>[user] blinks, their eyes briefly developing an unnatural shine.</i>"))
var/text = input("Which emotion would you like to suggest?", "Emotional Suggestion") as null|anything in list("Calm", "Happiness", "Sadness", "Fear", "Anger", "Stress", "Confusion")
if(!text)
return
text = lowertext(text)
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("Not even a psion of your level can suggest to the dead."))
return
log_say("[key_name(user)] suggested an emotion to [key_name(target)]: [text]",ckey=key_name(src))
to_chat(user, SPAN_CULT("You psionically suggest an emotion to [target]: [text]"))
for (var/mob/M in player_list)
if (istype(M, /mob/abstract/new_player))
continue
else if(M.stat == DEAD && HAS_FLAG(M.client.prefs.toggles, CHAT_GHOSTEARS))
to_chat(M, "<span class='notice'>[user] psionically suggests an emotion to [target]:</span> [text]")
var/mob/living/carbon/human/H = target
if(H.has_psionics())
to_chat(H, SPAN_NOTICE("You feel an emotion of <b>[text]</b> washing through your mind."))
else if(target.has_psi_aug())
to_chat(H, SPAN_NOTICE("You sense [user]'s psyche link with your psi-receiver, and an emotion envelops your mind: <b>[text]</b>."))
else
to_chat(H, SPAN_NOTICE("An emotion from outside your consciousness slips into your mind: <b>[text]</b>."))
-28
View File
@@ -1,28 +0,0 @@
/singleton/psionic_power/focus
name = "Focus"
desc = "Activate this spell in your hand to generate five units of synaptizine in your bloodstream."
icon_state = "tech_phaseshift"
point_cost = 1
ability_flags = PSI_FLAG_EVENT
spell_path = /obj/item/spell/focus
/obj/item/spell/focus
name = "focus"
desc = "Psionic drugs? No way."
icon_state = "blink"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 50
psi_cost = 20
/obj/item/spell/focus/on_use_cast(mob/user)
if(!ishuman(user))
return
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = user
if(do_after(user, 1 SECOND))
to_chat(H, SPAN_WARNING("A calm rush envelops your mind.."))
H.reagents.add_reagent(/singleton/reagent/synaptizine, 5)
H.update_canmove()
-51
View File
@@ -1,51 +0,0 @@
/singleton/psionic_power/grip
name = "Grip"
desc = "Grip a victim with psionic energy. You can squeeze your grip to crush them."
icon_state = "ling_bioelectrogenesis"
point_cost = 3
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/grip
/obj/item/spell/grip
name = "stasis"
desc = "General Kenobi..."
icon_state = "blink"
cast_methods = CAST_RANGED|CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 15
psi_cost = 5
var/mob/living/victim
/obj/item/spell/grip/Destroy()
victim.captured = FALSE
victim.update_canmove()
victim = null
return ..()
/obj/item/spell/grip/on_use_cast(mob/user, bypass_psi_check)
if(!victim)
to_chat(user, SPAN_WARNING("You need to grip someone first!"))
return
. = ..()
if(!.)
return
user.visible_message(SPAN_WARNING("[user] squeezes [user.get_pronoun("his")] hand!"), SPAN_WARNING("You squeeze your hand to tighten the psionic force around [victim]."))
to_chat(victim, SPAN_DANGER(FONT_HUGE("You are crushed by an invisible force!")))
victim.apply_damage(30, DAMAGE_BRUTE, armor_pen = 30, damage_flags = DAMAGE_FLAG_DISPERSED)
/obj/item/spell/grip/on_ranged_cast(atom/hit_atom, mob/user, bypass_psi_check)
if(!isliving(hit_atom))
return
if(victim)
to_chat(user, SPAN_WARNING("You can't target more than one person with this!"))
return
. = ..()
if(!.)
return
var/mob/living/M = hit_atom
user.visible_message(SPAN_DANGER("[user] extends [user.get_pronoun("his")] arm and makes a grab motion towards [M]!"),
SPAN_DANGER("You extend your arm and grab [M] with your psionic energy!"))
to_chat(M, SPAN_DANGER(FONT_HUGE("You feel an invisible force tighten around you!")))
M.captured = TRUE
M.update_canmove()
victim = M
@@ -1,106 +0,0 @@
/singleton/psionic_power/hollow_purple
name = "Hollow Technique: Purple"
desc = "This technique brings the concept of motion and reversal into reality. Purple is born from merging both infinites: Blue and Red, \
to produce an imaginary mass that rushes forth and erases everything in its path. Rather than the attraction of Blue or the repulsion of Red, \
Purple is an extraordinarily destructive energy wave of annihilation that rips whatever it hits from existence."
icon_state = "hollow_purple"
point_cost = 0
ability_flags = PSI_FLAG_LIMITLESS
minimum_rank = PSI_FLAG_LIMITLESS
spell_path = /obj/item/spell/projectile/hollow_purple
/obj/item/spell/projectile/hollow_purple
name = "hollow purple"
desc = "Throughout Heaven and Earth, I alone am the honored one!"
icon_state = "destablize"
cast_methods = CAST_RANGED
aspect = ASPECT_PSIONIC
spell_projectile = /obj/item/projectile/hollow_purple
fire_sound = 'sound/magic/WandODeath.ogg'
cooldown = 10
psi_cost = 50
var/stage = 0
/obj/item/spell/projectile/hollow_purple/on_ranged_cast(atom/hit_atom, mob/living/user, atom/pb_target)
if(!isliving(user))
return
stage = 1
user.visible_message(SPAN_NOTICE("<b><font size=4>[user] puts [user.get_pronoun("his")] palms together...</font></b>"))
START_PROCESSING(SSprocessing, src)
user.say("Cursed Technique Amplification: Blue...")
user.set_light(7, 10, COLOR_BLUE)
color = COLOR_BLUE
if(do_after(user, 4 SECONDS))
stage = 2
color = COLOR_RED
user.say("Cursed Technique Reversal: Red...")
user.set_light(0)
user.set_light(7, 10, COLOR_RED)
if(do_after(user, 4 SECONDS))
user.say("Hollow Technique: Purple!")
user.set_light(0)
user.visible_message(SPAN_CULT("<b><font size=4>[user] fires a gigantic purple sphere from [user.get_pronoun("his")] hand!</font></b>"))
. = ..()
STOP_PROCESSING(SSprocessing, src)
color = initial(color)
user.set_light(0)
stage = 0
/obj/item/spell/projectile/hollow_purple/process()
if(stage >= 1)
for(var/mob/living/L in get_hearers_in_view(7, src))
shake_camera(L, 5, 5)
/obj/item/projectile/hollow_purple
name = "hollow purple"
icon_state = "hollow_purple"
color = COLOR_PURPLE
damage = 1000
armor_penetration = 1000
penetrating = 100
range = 250
accuracy = 100
anti_materiel_potential = 100
damage_type = DAMAGE_BRUTE
/obj/item/projectile/hollow_purple/Initialize()
. = ..()
set_light(10, 10, COLOR_PURPLE)
/obj/item/projectile/hollow_purple/Destroy()
return ..()
/obj/item/projectile/hollow_purple/on_impact(var/atom/A)
if(ismob(A))
if(A != firer)
var/mob/M = A
M.gib()
explosion(A, 5, 5, 5)
..()
/obj/item/projectile/hollow_purple/on_hit(atom/target, blocked, def_zone)
if(ismob(target))
if(target != firer)
var/mob/M = target
M.gib()
explosion(target, 5, 5, 5)
..()
/obj/item/projectile/hollow_purple/check_penetrate(atom/A)
on_hit(A)
return TRUE
/obj/item/projectile/hollow_purple/after_move()
for(var/a in range(3, src))
if(isliving(a) && a != firer)
var/mob/living/M = a
M.gib()
playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
else if(isturf(a) || isobj(a))
var/atom/A = a
if(!A.density)
continue
A.ex_act(3)
playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
-24
View File
@@ -1,24 +0,0 @@
/singleton/psionic_power/ion
name = "Ion Blast"
desc = "Activate in hand to release a 3x3 ion blast around you. Be careful, this ability is expensive to use."
icon_state = "const_mend"
point_cost = 3
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/ion
/obj/item/spell/ion
name = "ion blast"
icon_state = "generic"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 20
psi_cost = 30
/obj/item/spell/ion/on_use_cast(mob/user, bypass_psi_check)
. = ..()
if(!.)
return
user.visible_message(SPAN_DANGER("<font size=4>[user] opens their arms and releases an ion blast around them!</font>"),
SPAN_DANGER("<font size=4>You open your arms and release an ion blast around you!</font>"))
empulse(get_turf(user), 3, 3)
-22
View File
@@ -1,22 +0,0 @@
/singleton/psionic_power/jump
name = "Jump"
desc = "Teleport to a destination you click on."
icon_state = "tech_dispel"
point_cost = 3
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/jump
/obj/item/spell/jump
name = "jump"
icon_state = "warp_strike"
cast_methods = CAST_RANGED
aspect = ASPECT_PSIONIC
cooldown = 50
psi_cost = 15
/obj/item/spell/jump/on_ranged_cast(atom/hit_atom, mob/user, bypass_psi_check)
. = ..()
if(!.)
return
user.visible_message(SPAN_WARNING("[user] warps and disappears!"), SPAN_WARNING("You weave the Nlom to bring you to your destination!"))
do_teleport(user, hit_atom)
@@ -1,59 +0,0 @@
/singleton/psionic_power/lightning
name = "Lightning"
desc = "Fire a concentrated lightning bolt. Activate this spell in hand to switch to a second mode that hits living beings in a 4x3 area in front of you."
icon_state = "tech_instabilitytapold"
point_cost = 0
ability_flags = PSI_FLAG_APEX
minimum_rank = PSI_RANK_APEX
spell_path = /obj/item/spell/projectile/psi_lightning
/obj/item/spell/projectile/psi_lightning
name = "lightning"
icon_state = "chain_lightning"
cast_methods = CAST_RANGED|CAST_USE
aspect = ASPECT_PSIONIC
spell_projectile = /obj/item/projectile/beam/psi_lightning
fire_sound = 'sound/magic/LightningShock.ogg'
cooldown = 10
psi_cost = 15
var/mode = 0
/obj/item/spell/projectile/psi_lightning/on_use_cast(mob/user, bypass_psi_check)
. = ..(user, TRUE)
mode = !mode
if(mode)
to_chat(user, SPAN_NOTICE("You will now fire area-of-effect lightning in a 4x3 area in front of you."))
spell_projectile = /obj/item/projectile/beam/psi_lightning/wide
else
to_chat(user, SPAN_NOTICE("You will now fire a normal lightning bolt."))
spell_projectile = /obj/item/projectile/beam/psi_lightning
/obj/item/projectile/beam/psi_lightning
name = "psionic lightning"
damage = 30
armor_penetration = 30
damage_type = DAMAGE_BURN
pass_flags = PASSTABLE | PASSGRILLE | PASSRAILING
range = 40
accuracy = 100
muzzle_type = /obj/effect/projectile/muzzle/tesla
tracer_type = /obj/effect/projectile/tracer/tesla
impact_type = /obj/effect/projectile/impact/tesla
/obj/item/projectile/beam/psi_lightning/pellet
damage = 20
armor_penetration = 20
/obj/item/projectile/beam/psi_lightning/wide
damage = 20
armor_penetration = 20
/obj/item/projectile/beam/psi_lightning/wide/Initialize()
for(var/i = 1 to 4)
var/turf/new_turf = get_random_turf_in_range(get_turf(firer), i + rand(0, i), 0, TRUE, FALSE)
var/obj/item/projectile/beam/psi_lightning/pellet/pellet = new type(new_turf)
var/turf/front_turf = get_step(pellet, pellet.dir)
pellet.launch_projectile(front_turf)
-69
View File
@@ -1,69 +0,0 @@
/singleton/psionic_power/mend
name = "Mend"
desc = "Mend a creature's wounds. This handles internal wounds as well, such as ruptured organs and broken bones."
icon_state = "tech_biomedaura"
point_cost = 2
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/mend
/obj/item/spell/mend
name = "mend"
desc = "Clear!"
icon_state = "mend_wounds"
cast_methods = CAST_MELEE
aspect = ASPECT_PSIONIC
cooldown = 50
psi_cost = 30
/obj/item/spell/mend/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
if(!ishuman(hit_atom))
return
var/mob/living/carbon/human/H = hit_atom
if(!H.has_zona_bovinae())
to_chat(user, SPAN_WARNING("Psionic power cannot flow through this being."))
return
if(H.stat == DEAD || H.status_flags & FAKEDEATH)
to_chat(user, SPAN_WARNING("Psionic power does not flow through a dead person."))
return
. = ..()
if(!.)
return
user.visible_message(SPAN_NOTICE("[user] lays a palm on [H]..."), SPAN_NOTICE("You lay your palm on [H] and get to work."))
to_chat(user, SPAN_NOTICE("You start by flowing your regenerative psionic energy through their vital organs..."))
for(var/obj/item/organ/O in H.internal_organs)
if(do_mob(user, H, 15 SECONDS))
if(O.damage > 0) // Fix internal damage
to_chat(SPAN_NOTICE("You mend their [O]'s bruising."))
O.heal_damage(O.damage)
if(HAS_FLAG(O.status, ORGAN_BROKEN))
to_chat(SPAN_NOTICE("You restart their [O]'s functionality."))
O.status &= ~ORGAN_BROKEN
if(O.damage <= 5 && O.organ_tag == BP_EYES) // Fix eyes
H.sdisabilities &= ~BLIND
to_chat(user, SPAN_NOTICE("You continue by flowing your regenerative psionic energy through their limbs..."))
for(var/obj/item/organ/external/O in H.organs) // Fix limbs
if(!O.robotic < ORGAN_ROBOT) // No robot parts for this.
continue
if(do_mob(user, H, 15 SECONDS))
to_chat(SPAN_NOTICE("You mend their [O]'s bruising."))
O.heal_damage(0, O.damage, internal = TRUE, robo_repair = FALSE)
to_chat(user, SPAN_NOTICE("Finally, you flowing your regenerative psionic energy through their broken limbs..."))
for(var/obj/item/organ/E in H.bad_external_organs) // Fix bones
var/obj/item/organ/external/affected = E
if(do_mob(user, H, 10 SECONDS))
if((affected.damage < affected.min_broken_damage * config.organ_health_multiplier) && (affected.status & ORGAN_BROKEN))
to_chat(SPAN_NOTICE("You mend their [affected] together."))
affected.status &= ~ORGAN_BROKEN
if(HAS_FLAG(affected.status, ORGAN_ARTERY_CUT))
to_chat(SPAN_NOTICE("You mend a spliced artery in their [affected]."))
affected.status &= ~ORGAN_ARTERY_CUT
if(HAS_FLAG(affected.status, ORGAN_DEAD))
to_chat(SPAN_NOTICE("You mend some necrosis from their [affected]."))
affected.status &= ~ORGAN_DEAD
user.visible_message(SPAN_NOTICE("[user] raises their palm from [H]."), SPAN_NOTICE("You raise your palm, having finished your work."))
@@ -1,29 +0,0 @@
/singleton/psionic_power/mind_muddle
name = "Mind Muddle"
desc = "Use this at range to confuse a target and give them a little bit of pain."
icon_state = "wiz_tele"
point_cost = 1
ability_flags = PSI_FLAG_EVENT
spell_path = /obj/item/spell/mind_muddle
/obj/item/spell/mind_muddle
name = "mind muddle"
desc = "Confuse people. That's all it does. Oh, and the pain."
icon_state = "warp_strike"
cast_methods = CAST_RANGED
aspect = ASPECT_PSIONIC
cooldown = 20
psi_cost = 10
/obj/item/spell/mind_muddle/on_ranged_cast(atom/hit_atom, mob/user, bypass_psi_check)
if(!ishuman(hit_atom))
to_chat(user, SPAN_WARNING("That won't work on them!"))
return
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = hit_atom
H.confused += 20
to_chat(H, SPAN_DANGER("A stinging sensation fills your mind! You feel nauseated..."))
H.adjustHalLoss(15)
@@ -1,53 +0,0 @@
/singleton/psionic_power/mirror_shade
name = "Mirror Shade"
desc = "Activate this spell to generate two psionic copies of yourself that will attack nearby mobs. These clones are not dense, will deal pain damage, and disappear \
after thirty seconds."
icon_state = "wiz_jaunt"
point_cost = 2
ability_flags = PSI_FLAG_EVENT
spell_path = /obj/item/spell/mirror_shade
/obj/item/spell/mirror_shade
name = "mirror shade"
desc = "Photocopy yourself."
icon_state = "darkness"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 100
psi_cost = 20
/obj/item/spell/mirror_shade/on_use_cast(mob/user)
. = ..()
if(!.)
return
if(do_after(user, 1 SECOND))
to_chat(user, SPAN_WARNING("You weave your psionic force into two copies of yourself!"))
for(var/i = 1 to 2)
var/mob/living/simple_animal/hostile/mirror_shade/MS = new(pick(get_adjacent_open_turfs(user)))
MS.appearance = user.appearance
MS.name = user.name
MS.owner = user
/mob/living/simple_animal/hostile/mirror_shade
damage_type = DAMAGE_PAIN
density = FALSE
destroy_surroundings = FALSE
melee_damage_lower = 10
melee_damage_upper = 15
attack_emote = "tries shambling towards"
attacktext = "phases its arms through"
var/mob/living/carbon/human/owner
/mob/living/simple_animal/hostile/mirror_shade/Initialize()
. = ..()
friends += owner
QDEL_IN(src, 30 SECONDS)
/mob/living/simple_animal/hostile/mirror_shade/examine(mob/user)
/// Technically suspicious, but these have 30 seconds of lifetime so it's probably fine.
return owner.examine(user)
/mob/living/simple_animal/hostile/mirror_shade/Destroy()
owner = null
visible_message(SPAN_WARNING("[src] dissipates into nothingness, as if it were air all along!"))
return ..()
@@ -1,76 +0,0 @@
/singleton/psionic_power/nlom_eyes
name = "Nlom Eyes"
desc = "Roughly locate a mob on your z-level."
icon_state = "tech_control"
point_cost = 2
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/nlom_eyes
/obj/item/spell/nlom_eyes
name = "nlom eyes"
desc = "Psionic drugs? No way."
icon_state = "track"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 5
/// The mob we are tracking.
var/mob/living/tracked
/// If we are tracking a mob.
var/tracking = FALSE
/obj/item/spell/nlom_eyes/Destroy()
tracked = null
tracking = 0
STOP_PROCESSING(SSprocessing, src)
return ..()
/obj/item/spell/nlom_eyes/on_use_cast(mob/user)
if(tracking)
tracking = FALSE
to_chat(user, SPAN_NOTICE("You stop looking in the Nlom for \the [tracked]'s whereabouts."))
tracked = null
STOP_PROCESSING(SSprocessing, src)
icon_state = "track"
return
. = ..()
if(!.)
return
var/list/mob_choices = list()
for(var/mob/living/L in mob_list)
if(L == user)
continue
if(!L.is_psi_blocked())
continue
if(GET_Z(L) != GET_Z(user))
continue
mob_choices += L
var/choice = input(user, "Decide who to track.", "Nlom Eyes") as null|anything in mob_choices
if(choice)
tracked = choice
tracking = TRUE
START_PROCESSING(SSprocessing, src)
/obj/item/spell/track/process()
if(!tracking)
icon_state = "track"
return
if(!tracked)
icon_state = "track_unknown"
if(GET_Z(tracked) != GET_Z(owner))
icon_state = "track_unknown"
else
set_dir(get_dir(src,get_turf(tracked)))
switch(get_dist(src,get_turf(tracked)))
if(0)
icon_state = "track_direct"
if(1 to 8)
icon_state = "track_close"
if(9 to 16)
icon_state = "track_medium"
if(16 to INFINITY)
icon_state = "track_far"
@@ -1,43 +0,0 @@
/singleton/psionic_power/awaken
name = "Awaken"
desc = "Stimulate a living being's Zona Bovinae and bring them to Psionically Harmonious rank."
icon_state = "tech_corona"
point_cost = 0
ability_flags = PSI_FLAG_APEX
minimum_rank = PSI_RANK_APEX
spell_path = /obj/item/spell/awaken
/obj/item/spell/awaken
name = "awaken"
icon_state = "energy_siphon_drain"
cast_methods = CAST_MELEE
aspect = ASPECT_PSIONIC
cooldown = 20
psi_cost = 100
/obj/item/spell/awaken/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
if(!isliving(hit_atom))
return
var/mob/living/L = hit_atom
if(!L.has_zona_bovinae())
to_chat(user, SPAN_WARNING("This being doesn't have a Zona Bovinae."))
return
if(L.psi && L.psi.get_rank() >= PSI_RANK_HARMONIOUS)
to_chat(user, SPAN_WARNING("This wouldn't do anything on them."))
return
. = ..()
if(!.)
return
user.visible_message(SPAN_DANGER("[user] grabs [L]'s head..."), SPAN_DANGER("You grab [L]'s head..."))
if(do_mob(user, L, 5 SECONDS))
L.set_psi_rank(PSI_RANK_HARMONIOUS)
L.flash_pain(50)
L.adjustHalLoss(20)
playsound(get_turf(L), 'sound/effects/psi/power_feedback.ogg', 100)
user.visible_message(SPAN_DANGER("[user] unleashes a psionic shock through [L]'s head!"),
SPAN_DANGER("You unleash a psionic shock through [L]'s head, targeting their Zona Bovinae and stimulating it it."))
to_chat(L, SPAN_HIGHDANGER("A large, painful shock courses through your head. You can see the weave of the universe... and manipulate it."))
@@ -1,57 +0,0 @@
/singleton/psionic_power/psi_drain
name = "Psi-Drain"
desc = "Drain psi-stamina from a living being. This can be used both in melee and at range."
icon_state = "gen_project"
point_cost = 0
ability_flags = PSI_FLAG_APEX
minimum_rank = PSI_RANK_APEX
spell_path = /obj/item/spell/psi_drain
/obj/item/spell/psi_drain
name = "psi-drain"
icon_state = "chain_lightning"
cast_methods = CAST_MELEE|CAST_RANGED
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 5
/obj/item/spell/psi_drain/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
if(!isliving(hit_atom))
return
var/mob/living/L = hit_atom
if(!L.has_zona_bovinae())
to_chat(user, SPAN_WARNING("This being doesn't have a Zona Bovinae."))
return
. = ..()
if(!.)
return
psi_drain(L, user)
/obj/item/spell/psi_drain/on_ranged_cast(atom/hit_atom, mob/user, bypass_psi_check)
if(!isliving(hit_atom))
return
var/mob/living/L = hit_atom
if(!L.has_zona_bovinae())
to_chat(user, SPAN_WARNING("This being doesn't have a Zona Bovinae."))
return
. = ..()
if(!.)
return
psi_drain(L, user)
/obj/item/spell/psi_drain/proc/psi_drain(mob/living/L, mob/living/user)
user.visible_message(SPAN_DANGER("[user] squeezes [user.get_pronoun("his")] hand at [L]!"), SPAN_DANGER("You squeeze your hand at [L] and siphon [L.get_pronoun("his")] psionic energy!"))
var/psi_taken = min(user.psi.max_stamina, rand(10, 25))
if(L.psi)
L.psi.spend_power(psi_taken)
else
L.adjustHalLoss(psi_taken * 1.5)
L.flash_pain(psi_taken * 1.5)
playsound(get_turf(user), 'sound/effects/psi/power_evoke.ogg', 100)
to_chat(L, SPAN_DANGER("You feel a headache split apart your mind!"))
@@ -1,31 +0,0 @@
/singleton/psionic_power/punch
name = "Psi-Punch"
desc = "Imbue your fists with psionic power."
icon_state = "const_fist"
point_cost = 2
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/punch
/obj/item/spell/punch
name = "psi-punch"
icon_state = "generic"
cast_methods = CAST_MELEE
aspect = ASPECT_PSIONIC
force = 20
armor_penetration = 20
cooldown = 0
psi_cost = 3
flags = 0
hitsound = 'sound/weapons/resonator_blast.ogg'
/obj/item/spell/punch/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
if(!isliving(hit_atom))
return
. = ..()
if(!.)
return
var/mob/living/M = hit_atom
if(prob(15))
M.throw_at(get_edge_target_turf(loc, loc.dir), 2, 7)
M.visible_message(SPAN_DANGER("[M] is floored by psychic energy!"), SPAN_DANGER("You are floored by psychic energy!"))
M.apply_effect(2, WEAKEN)
@@ -1,37 +0,0 @@
/singleton/psionic_power/psi_recovery
name = "Psionic Recovery"
desc = "Activate in hand to regenerate psi-stamina."
icon_state = "swarm_zeropoint"
point_cost = 0
ability_flags = PSI_FLAG_FOUNDATIONAL
spell_path = /obj/item/spell/psi_recovery
/obj/item/spell/psi_recovery
name = "psionic recovery"
desc = "It's an aura recharge."
icon_state = "generic"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 5
psi_cost = 0
/obj/item/spell/psi_recovery/on_use_cast(mob/user)
. = ..()
if(!.)
return
if(!isliving(user))
return
var/mob/living/L = user
L.visible_message(SPAN_NOTICE("[user] puts [user.get_pronoun("his")] hands together and focuses..."),
SPAN_NOTICE("You put your hands together and begin focusing on recovering your psionic energy..."))
psi_recovery(user)
/obj/item/spell/psi_recovery/proc/psi_recovery(mob/user)
var/mob/living/L = user
if(do_after(user, 0.5 SECONDS))
L.psi.stamina = min(L.psi.max_stamina, L.psi.stamina + rand(1,3))
if(L.psi.stamina >= L.psi.max_stamina)
to_chat(user, SPAN_NOTICE("You've recovered all your psionic energy."))
return TRUE
psi_recovery(user)
@@ -1,68 +0,0 @@
/singleton/psionic_power/psi_search
name = "Psionic Search"
desc = "Scan your Z-level for Nlom signatures. The distance will be slightly inaccurate for weak signatures."
icon_state = "wiz_shield"
point_cost = 0
ability_flags = PSI_FLAG_FOUNDATIONAL
spell_path = /obj/item/spell/psi_search
/obj/item/spell/psi_search
name = "psionic search"
desc = "A psionic magnifying glass."
icon_state = "generic"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 5
psi_cost = 10
/obj/item/spell/psi_search/on_use_cast(mob/user)
. = ..()
if(!.)
return
if(!isliving(user))
return
var/mob/living/L = user
L.visible_message(SPAN_NOTICE("[L] puts two fingers to [L.get_pronoun("his")] forehead and focuses..."),
SPAN_NOTICE("You put two fingers to your temple and focus on locating Nlom signatures..."))
if(do_after(L, 3 SECONDS))
var/list/level_humans = list()
var/found_apex = FALSE
for(var/mob/living/carbon/human/H in human_mob_list)
if(H == L)
continue
if((GET_Z(H) == GET_Z(L)) && !H.is_psi_blocked())
if(HAS_TRAIT(H, TRAIT_PSIONIC_SUPPRESSION))
continue
level_humans |= H
if(H.psi)
if(H.psi.get_rank() >= PSI_RANK_APEX)
found_apex = TRUE
if(!length(level_humans))
to_chat(L, SPAN_WARNING("The Nlom is quiet and empty here."))
return TRUE
if(found_apex)
to_chat(L, SPAN_DANGER(FONT_HUGE("You reach out into the Nlom and your senses are overwhelmed by a massive signature!")))
L.flash_pain(20)
L.adjustHalLoss(20)
return
var/list/signatures = list()
var/harmonious_signatures = 0
var/sensitive_signatures = 0
var/perceptive_signatures = 0
for(var/mob/living/carbon/human/H in level_humans)
if(!H.psi)
perceptive_signatures++
continue
if(H.psi && H.psi.get_rank() == PSI_RANK_SENSITIVE)
sensitive_signatures++
continue
if(H.psi && H.psi.get_rank() == PSI_RANK_HARMONIOUS)
harmonious_signatures++
continue
if(perceptive_signatures)
signatures += "[perceptive_signatures] weak signature[perceptive_signatures > 1 ? "s" : ""]"
if(sensitive_signatures)
signatures += "[sensitive_signatures] robust signature[sensitive_signatures > 1 ? "s" : ""]"
if(harmonious_signatures)
signatures += "[harmonious_signatures] very powerful signature[harmonious_signatures > 1 ? "s" : ""]"
to_chat(user, SPAN_NOTICE("Reaching out into the Nlom, you sense [english_list(signatures)]."))
@@ -1,27 +0,0 @@
/singleton/psionic_power/psi_stamina
name = "Psi-Stamina Weave"
desc = "Activate this spell in your hand to regenerate your psi-stamina a little bit."
icon_state = "tech_mend_template"
point_cost = 1
ability_flags = PSI_FLAG_EVENT|PSI_FLAG_CANON
spell_path = /obj/item/spell/psi_stamina
/obj/item/spell/psi_stamina
name = "psi-stamina weave"
desc = "Kind of like charging up your aura."
icon_state = "generic"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 0
psi_cost = 0
/obj/item/spell/psi_stamina/on_use_cast(mob/user)
. = ..()
if(!.)
return
if(do_after(user, 1 SECOND))
to_chat(user, SPAN_NOTICE("You feel a bit more refreshed."))
owner.psi.stamina = min(owner.psi.max_stamina, owner.psi.stamina + rand(2,5))
if(owner.psi.stamina >= owner.psi.max_stamina)
to_chat(user, SPAN_NOTICE("You're ready to go again!"))
return TRUE
@@ -1,44 +0,0 @@
/singleton/psionic_power/sunder
name = "Sunder"
desc = "Destroy a living being's Zona Bovinae. This will make them psionically deaf."
icon_state = "ling_berserk"
point_cost = 0
ability_flags = PSI_FLAG_APEX
minimum_rank = PSI_RANK_APEX
spell_path = /obj/item/spell/sunder
/obj/item/spell/sunder
name = "sunder"
icon_state = "chain_lightning"
cast_methods = CAST_MELEE
aspect = ASPECT_PSIONIC
cooldown = 30
psi_cost = 20
/obj/item/spell/sunder/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
if(!isliving(hit_atom))
return
var/mob/living/L = hit_atom
if(!L.has_zona_bovinae())
to_chat(user, SPAN_WARNING("This being doesn't have a Zona Bovinae."))
return
. = ..()
if(!.)
return
user.visible_message(SPAN_DANGER("[user] grabs [L]'s head..."), SPAN_DANGER("You grab [L]'s head..."))
if(do_mob(user, L, 5 SECONDS))
if(L.psi)
QDEL_NULL(L.psi)
ADD_TRAIT(L, TRAIT_PSIONICALLY_DEAF, TRAIT_SOURCE_PSIONICS)
L.flash_pain(100)
L.adjustHalLoss(50)
L.stuttering += 20
playsound(get_turf(L), 'sound/effects/psi/power_feedback.ogg', 100)
user.visible_message(SPAN_DANGER("[user] unleashes a psionic shock through [L]'s head!"),
SPAN_DANGER("You unleash a psionic shock through [L]'s head, targeting their Zona Bovinae and destroying it."))
to_chat(L, SPAN_HIGHDANGER("A large, painful shock courses through your head. Part of your brain feels like it's gone."))
if(isskrell(L))
to_chat(L, SPAN_CULT("You feel absolutely empty..."))
@@ -1,27 +0,0 @@
/singleton/psionic_power/psi_suppression
name = "Psionic Suppression"
desc = "Hold in one of your hands to make yourself invisible to Psi-Search."
icon_state = "tech_shield"
point_cost = 1
ability_flags = PSI_FLAG_CANON
spell_path = /obj/item/spell/psi_suppression
/obj/item/spell/psi_suppression
name = "psionic suppression"
icon_state = "generic"
cast_methods = CAST_INNATE
aspect = ASPECT_PSIONIC
psi_cost = 5
/obj/item/spell/psi_suppression/Destroy()
to_chat(owner, SPAN_NOTICE("You are no longer hidden from Psi-Search."))
REMOVE_TRAIT(owner, TRAIT_PSIONIC_SUPPRESSION, TRAIT_SOURCE_PSIONICS)
return ..()
/obj/item/spell/psi_suppression/on_innate_cast(mob/user)
. = ..()
if(!.)
return
to_chat(user, SPAN_NOTICE("You are now hidden from Psi-Search."))
ADD_TRAIT(user, TRAIT_PSIONIC_SUPPRESSION, TRAIT_SOURCE_PSIONICS)
@@ -1,32 +0,0 @@
/singleton/psionic_power/psi_sword
name = "Psi-Sword"
desc = "Create a psionic sword."
icon_state = "psiblade"
point_cost = 3
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/psi_sword
/obj/item/spell/psi_sword
name = "psi-tool"
icon_state = "generic"
cast_methods = CAST_INNATE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 5
/obj/item/spell/psi_sword/on_innate_cast(mob/user)
if(!ishuman(user))
return
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = user
var/obj/item/psychic_power/psiblade/PT = new(user)
if(H.put_in_any_hand_if_possible(PT))
qdel(src)
return TRUE
else
to_chat(H, SPAN_WARNING("You need an empty hand for this!"))
return FALSE
@@ -1,32 +0,0 @@
/singleton/psionic_power/psi_tool
name = "Psi-Tool"
desc = "Create a psionic tool that can change into a screwdriver, crowbar, wrench or wirecutters."
icon_state = "psitool"
point_cost = 1
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/psi_tool
/obj/item/spell/psi_tool
name = "psi-tool"
icon_state = "generic"
cast_methods = CAST_INNATE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 5
/obj/item/spell/psi_tool/on_innate_cast(mob/user)
if(!ishuman(user))
return
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = user
var/obj/item/psychic_power/tinker/PT = new(user)
if(H.put_in_any_hand_if_possible(PT))
qdel(src)
return TRUE
else
to_chat(H, SPAN_WARNING("You need an empty hand for this!"))
return FALSE
-38
View File
@@ -1,38 +0,0 @@
/singleton/psionic_power/pull
name = "Pull"
desc = "Pulls the target straight towards the user, smashing windows along the way. If it can be held in your hands, \
it goes straight to your hand. Note that you can catch items you pull to yourself if you toggle throw mode before pulling an item."
icon_state = "tech_passwall"
point_cost = 2
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/pull
/obj/item/spell/pull
name = "pull"
desc = "Not as cool as the Mass Effect one."
icon_state = "control"
cast_methods = CAST_RANGED
aspect = ASPECT_PSIONIC
cooldown = 50
psi_cost = 10
/obj/item/spell/pull/on_ranged_cast(atom/hit_atom, mob/user, bypass_psi_check)
if(!ismovable(hit_atom))
return
if(!ishuman(user))
return
. = ..()
if(!.)
return
var/atom/movable/AM = hit_atom
var/mob/living/carbon/human/H = user
if(isobj(hit_atom) && w_class < ITEMSIZE_IMMENSE)
if(length(get_line(hit_atom, user)))
if(H.put_in_any_hand_if_possible(hit_atom))
return
user.visible_message(SPAN_WARNING("[user] extends [user.get_pronoun("his")] hand at [hit_atom]and pulls!"), SPAN_WARNING("You mimic pulling at [hit_atom]!"))
if(ismob(hit_atom))
to_chat(hit_atom, SPAN_WARNING("A psychic force pulls you!"))
AM.throw_at(user, 10, 7)
playsound(user, 'sound/effects/psi/power_evoke.ogg')
@@ -1,77 +0,0 @@
/singleton/psionic_power/read_mind
name = "Read Mind"
desc = "Rip thoughts from someone's mind. If your rank is Psionically Sensitive, you may only skim the surface thoughts from a person's mind. \
If your rank is Psionically Harmonious or above, your target is forced to respond to a five-word question with the truth."
icon_state = "tech_illusion"
spell_path = /obj/item/spell/read_mind
ability_flags = PSI_FLAG_EVENT|PSI_FLAG_CANON
/obj/item/spell/read_mind
name = "read mind"
desc = "Rip thoughts from someone's mind."
icon_state = "generic"
cast_methods = CAST_MELEE|CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 50
/obj/item/spell/read_mind/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(!.)
return
read_mind(hit_atom, user)
/obj/item/spell/read_mind/proc/read_mind(atom/hit_atom, mob/user)
if(!isliving(hit_atom))
return
if(!isliving(user))
return
var/mob/living/target = hit_atom
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("Not even a psion of your level can speak to the dead."))
return
var/psi_blocked = target.is_psi_blocked()
if(psi_blocked)
to_chat(user, psi_blocked)
return
var/safe_mode = FALSE
var/mob/living/L = user
if(L.psi.get_rank() < PSI_RANK_HARMONIOUS)
safe_mode = TRUE
user.visible_message(SPAN_WARNING("[user] lays a palm on [hit_atom]'s forehead..."))
var/question
if(!safe_mode)
question = sanitize(input(user, "Ask your question.", "Read Mind") as null|text)
if((!safe_mode && !question) || user.incapacitated())
return TRUE
var/started_mindread = world.time
if(target.has_psi_aug())
to_chat(user, SPAN_NOTICE("<b>Your psyche links with [target]'s psi-receiver, seeking [safe_mode ? "their surface thoughts." : "an answer from their mind's surface: <i>[question]</i>"]</b>"))
to_chat(target, SPAN_NOTICE("<b>[user]'s psyche links with your psi-receiver. [safe_mode ? "What are you thinking about, currently?" : "You cannot avoid the following question, and must answer truthfully: <i>[question]</i>"]</b>"))
else
to_chat(user, SPAN_NOTICE("<b>You dip your mentality into the surface layer of \the [target]'s mind, seeking an answer: <i>[question]</i></b>"))
to_chat(target, SPAN_NOTICE("<b>Your mind is compelled to answer. [safe_mode ? "What are you thinking about, currently?" : "You cannot avoid the following question, and must answer truthfully: <i>[question]</i>"]</b>"))
var/answer = sanitize(input(target, "[question]\n[safe_mode ? "You must answer with what you are currently thinking about." : "You must answer truthfully."]\nYou have 25 seconds to type a response.", "Read Mind") as null|text)
if(!answer || world.time > started_mindread + 25 SECONDS || user.stat != CONSCIOUS)
to_chat(user, SPAN_NOTICE("<b>You receive nothing useful from \the [target].</b>"))
to_chat(target, SPAN_NOTICE("Your mind blanks out momentarily."))
else
if(safe_mode)
to_chat(user, SPAN_NOTICE("<b>You skim the first thoughts in [target]'s mind: <i>[answer]</i></b>"))
else
to_chat(user, SPAN_NOTICE("<b>You pry the answer to your question from [target]'s mind: <i>[answer]</i></b>"))
msg_admin_attack("[key_name(user)] read mind of [key_name(target)] [safe_mode ? "skimming their surface thoughts" : "forcing them to answer truthfully with question \"[question]\""] and [answer?"got answer \"[answer]\".":"got no answer."]")
if(safe_mode)
target.confused += 15
target.adjustBrainLoss(10)
to_chat(target, SPAN_WARNING("You feel somewhat nauseated, and a headache's come up too..."))
else
target.adjustBrainLoss(20)
target.confused += 20
to_chat(target, SPAN_DANGER("Your head feels like it's going to explode, and you feel nauseated..."))
@@ -1,38 +0,0 @@
/singleton/psionic_power/rejuvenate
name = "Rejuvenate"
desc = "Restore a creature's blood."
icon_state = "tech_oxygenate"
point_cost = 1
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/rejuvenate
/obj/item/spell/rejuvenate
name = "rejuvenate"
desc = "Blood bag who?"
icon_state = "mend_life"
cast_methods = CAST_MELEE
aspect = ASPECT_PSIONIC
cooldown = 50
psi_cost = 30
/obj/item/spell/rejuvenate/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
if(!ishuman(hit_atom))
return
var/mob/living/carbon/human/target = hit_atom
if(!target.has_zona_bovinae())
to_chat(user, SPAN_WARNING("Psionic power cannot flow through this being."))
return
if(target.stat == DEAD || target.status_flags & FAKEDEATH)
to_chat(user, SPAN_WARNING("Psionic power does not flow through a dead person."))
return
. = ..()
if(!.)
return
user.visible_message(SPAN_NOTICE("[user] lays both palms on [target]..."), SPAN_NOTICE("You lay your palms on [target] and get to work."))
if(do_mob(user, target, 10 SECONDS))
target.restore_blood()
user.visible_message(SPAN_NOTICE("[user] removes their palms from [target]."), SPAN_NOTICE("You remove your palms from [target], having restored their blood."))
-64
View File
@@ -1,64 +0,0 @@
/singleton/psionic_power/rend
name = "Rend"
desc = "Rend an adjacent target's biomolecular state apart. Very powerful, but with an extremely long cooldown and a huge psi-stamina cost. \
Activate it in your hand to switch to a structure mode, in which you cannot target living beings (synthetics included) but you can tear apart \
walls and airlocks much faster."
icon_state = "gen_dissolve"
point_cost = 3
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/rend
/obj/item/spell/rend
name = "rend"
icon_state = "chain_lightning"
cast_methods = CAST_USE|CAST_MELEE
aspect = ASPECT_PSIONIC
force = 40
armor_penetration = 30
cooldown = 30
psi_cost = 35
attack_verb = list("rends apart", "disintegrates")
hitsound = 'sound/weapons/heavysmash.ogg'
flags = 0
var/structure_mode = FALSE
/obj/item/spell/rend/on_use_cast(mob/user, bypass_psi_check)
. = ..(user, TRUE)
structure_mode = !structure_mode
if(structure_mode)
to_chat(user, SPAN_WARNING("You reconfigure your rending to damage structures. It can no longer be used on organics or synthetics, but it is much cheaper \
to damage structures with."))
maptext = SMALL_FONTS(7, "S")
psi_cost = 15
else
to_chat(user, SPAN_WARNING("You reconfigure your rending to damage organics again."))
maptext = SMALL_FONTS(7, "O")
psi_cost = initial(psi_cost)
/obj/item/spell/rend/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
if(structure_mode && isliving(hit_atom))
to_chat(user, SPAN_WARNING("Your rending is not configured for organics!"))
return
. = ..()
if(!.)
return
if(structure_mode)
if(iswall(hit_atom))
var/turf/simulated/wall/W = hit_atom
var/base_time = 5 SECONDS
if(W.reinf_material)
base_time += (W.reinf_material.hardness / 10) SECONDS
user.visible_message(SPAN_WARNING("[user] lays [user.get_pronoun("his")] palms on \the [W] and begins discharging psionic energy on it..."),
SPAN_WARNING("You lay your palms on \the [W] and begin permeating psionic energy through its structure..."))
if(do_after(user, base_time))
user.visible_message(SPAN_WARNING("[user] disintegrates \the [W]!"), SPAN_WARNING("You disintegrate \the [W]!"))
W.dismantle_wall(TRUE, FALSE, TRUE)
else if(isairlock(hit_atom))
var/obj/machinery/door/airlock/A = hit_atom
var/base_time = (round(A.maxhealth / 60)) SECONDS
user.visible_message(SPAN_WARNING("[user] lays [user.get_pronoun("his")] palms on \the [A] and begins discharging psionic energy on it..."),
SPAN_WARNING("You lay your palms on \the [A] and begin permeating psionic energy through its structure..."))
if(do_after(user, base_time))
user.visible_message(SPAN_WARNING("[user] disintegrates \the [A]!"), SPAN_WARNING("You disintegrate \the [A]!"))
playsound(A, 'sound/effects/meteorimpact.ogg')
qdel(A)
@@ -1,42 +0,0 @@
/singleton/psionic_power/shockwave
name = "Shockwave"
desc = "Create a wave of telekinetic energy to pummel the ground in a straight line in the direction you're facing. \
Anyone caught in it falls over unless wearing magboots."
icon_state = "tech_haste"
point_cost = 2
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/shockwave
/obj/item/spell/shockwave
name = "shockwave"
icon_state = "flame_tongue"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 50
psi_cost = 20
/obj/item/spell/shockwave/on_use_cast(mob/user, bypass_psi_check)
. = ..()
if(!.)
return
var/user_dir = user.dir
var/turf/T = get_turf(user)
if(isspaceturf(T))
to_chat(user, SPAN_WARNING("You charge your shockwave, slam your foot down... and then remember that you're in space."))
return
user.visible_message(SPAN_DANGER(FONT_HUGE("[user] charges [user.get_pronoun("his")] foot with psionic energy and slams it down!")),
SPAN_DANGER(FONT_HUGE("You charge your foot with psionic energy and slam it down!")))
playsound(T, 'sound/effects/meteorimpact.ogg', 100)
for(var/mob/M in get_hearers_in_view(7, src))
shake_camera(M, 5, 5)
for(var/i = 1 to 5)
T = get_step(T, user_dir)
if(isspaceturf(T))
break
for(var/mob/living/M in T)
if(M.Check_Shoegrip())
continue
M.apply_effect(3, WEAKEN)
M.flash_pain(20)
to_chat(M, SPAN_DANGER("You get caught in the shockwave and fall down!"))
@@ -1,59 +0,0 @@
/singleton/psionic_power/singularity
name = "Singularity"
desc = "Creates a psionic illusion. Anyone within four tiles of it is forced to walk towards it. It will dissipate after ten seconds."
icon_state = "tech_energysiphon"
point_cost = 2
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/singularity
/obj/item/spell/singularity
name = "singularity"
icon_state = "destablize"
cast_methods = CAST_RANGED
aspect = ASPECT_PSIONIC
cooldown = 200
psi_cost = 20
var/obj/effect/singularity/singularity
/obj/item/spell/singularity/Destroy()
QDEL_NULL(singularity)
return ..()
/obj/item/spell/singularity/on_ranged_cast(atom/hit_atom, mob/user, bypass_psi_check)
if(!QDELETED(singularity))
to_chat(user, SPAN_WARNING("You can't make more than one singularity!"))
return
. = ..()
if(!.)
return
var/obj/effect/singularity/S = new(get_turf(hit_atom))
S.creator = user
singularity = S
playsound(get_turf(user), 'sound/effects/fingersnap.ogg')
user.visible_message(SPAN_DANGER(FONT_HUGE("[user] snaps [user.get_pronoun("his")] fingers and generates a hole of psionic energy!")),
SPAN_DANGER("You snap your fingers and generate a vortex of psionic energy."))
/obj/effect/singularity
name = "psionic singularity"
icon_state = "bluestream_fade"
var/mob/living/creator
/obj/effect/singularity/process()
for(var/mob/living/M in get_hearers_in_LOS(4, src))
if(M == creator)
continue
to_chat(M, SPAN_DANGER("Your eyes can't move away from \the [src]... you feel compelled to move towards it!"))
M.AdjustStunned(1)
step_towards(M, src)
/obj/effect/singularity/Initialize(mapload, ...)
. = ..()
QDEL_IN(src, 10 SECONDS)
START_PROCESSING(SSprocessing, src)
/obj/effect/singularity/Destroy()
visible_message(SPAN_WARNING("\The [src] dissipates harmlessly with a light 'fwoosh' sound."))
creator = null
STOP_PROCESSING(SSprocessing, src)
return ..()
@@ -1,67 +0,0 @@
/singleton/psionic_power/skinsight
name = "Skinsight"
desc = "Get information on a creature's vitals. Activate in your hand to switch between a normal mode or a slower, more detailed mode. Note that the body scan mode \
is not 100% accurate with the numbers given."
icon_state = "wiz_blind"
point_cost = 1
ability_flags = PSI_FLAG_EVENT|PSI_FLAG_CANON
spell_path = /obj/item/spell/skinsight
/obj/item/spell/skinsight
name = "skinsight"
desc = "A health analyzer, but cooler."
icon_state = "blink"
cast_methods = CAST_MELEE|CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 10
var/body_scan_mode = FALSE
/obj/item/spell/skinsight/on_use_cast(mob/user)
. = ..()
body_scan_mode = !body_scan_mode
if(body_scan_mode)
psi_cost = 30
to_chat(user, SPAN_NOTICE("You will now use more psionic energy for a more detailed readout."))
else
psi_cost = 10
to_chat(user, SPAN_NOTICE("You will now use less psionic energy for a more standard readout."))
/obj/item/spell/skinsight/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
if(!ishuman(hit_atom))
return
var/mob/living/carbon/human/target = hit_atom
if(!target.has_zona_bovinae())
to_chat(user, SPAN_WARNING("Psionic power cannot flow through this being."))
return
if(!isliving(user))
return
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("The total lack of psionic energy flowing through this person can only mean one thing: they are dead."))
return
. = ..()
if(!.)
return
if(!body_scan_mode)
user.visible_message(SPAN_NOTICE("[user] passes [user.get_pronoun("his")] hand over [target]."), SPAN_NOTICE("You pass your hand over [target]."))
health_scan_mob(target, user, TRUE, TRUE)
else
user.visible_message(SPAN_NOTICE("[user] slowly passes [user.get_pronoun("his")] hand over [target]..."),
SPAN_NOTICE("You slowly pass your hand over [target]..."))
if(do_mob(user, target, 10 SECONDS))
for(var/obj/item/organ/internal/I in target.internal_organs)
if(I.is_bruised())
to_chat(user, SPAN_WARNING("You notice some irregularity in the psionic flow through their [I]."))
if(I.is_broken())
to_chat(user, SPAN_WARNING("Your psionic flow through their [I] is <b>highly irregular</b>."))
if(I.damage)
to_chat(user, SPAN_WARNING("You are sure there is some damage in their [I]."))
var/pulse = target.get_pulse()
to_chat(user, SPAN_NOTICE("Their pulse is [pulse]."))
var/oxygenation = max(min(target.get_blood_oxygenation() + rand(-10, 10), 100), 0)
to_chat(user, SPAN_NOTICE("Their oxygenation is more or less [oxygenation]%."))
-30
View File
@@ -1,30 +0,0 @@
/singleton/psionic_power/spasm
name = "Spasm"
desc = "Force a target to drop the items in its hands. Note that this has a hefty power use and cooldown."
icon_state = "genetics_closed"
point_cost = 1
ability_flags = PSI_FLAG_EVENT
spell_path = /obj/item/spell/spasm
/obj/item/spell/spasm
name = "spasm"
desc = "Made you drop your gun."
icon_state = "control"
cast_methods = CAST_RANGED
aspect = ASPECT_PSIONIC
cooldown = 100
/// This looks like a lot, but this ability is really strong.
psi_cost = 20
/obj/item/spell/spasm/on_ranged_cast(atom/hit_atom, mob/user, bypass_psi_check)
if(!ishuman(hit_atom))
to_chat(user, SPAN_WARNING("That won't work!"))
return
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = hit_atom
H.drop_l_hand()
H.drop_r_hand()
to_chat(H, SPAN_DANGER("Your mind is filled by an acute pain making you drop what you're holding!"))
H.flash_pain(50)
-58
View File
@@ -1,58 +0,0 @@
/singleton/psionic_power/stasis
name = "Stasis"
desc = "Condenses the Nlom field around one person at a time. This immobilises them like an energy net and also applies stasis to them."
icon_state = "gen_ice"
point_cost = 2
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/stasis
/obj/item/spell/stasis
name = "stasis"
desc = "Almost as cool as the Mass Effect one."
icon_state = "overload"
cast_methods = CAST_RANGED
aspect = ASPECT_PSIONIC
cooldown = 100
psi_cost = 20
var/obj/effect/energy_net/our_net
/obj/item/spell/stasis/Destroy()
qdel(our_net)
our_net = null
return ..()
/obj/item/spell/stasis/on_ranged_cast(atom/hit_atom, mob/user, bypass_psi_check)
if(!isliving(hit_atom))
return
if(!QDELETED(our_net))
to_chat(user, SPAN_WARNING("You can't target more than one person with this!"))
return
. = ..()
if(!.)
return
var/mob/living/M = hit_atom
var/obj/effect/energy_net/EN = locate() in get_turf(M)
if(EN)
qdel(EN)
var/turf/T = get_turf(M)
if(T)
var/obj/effect/energy_net/stasis/net = new(T)
net.layer = M.layer + 1
M.captured = TRUE
M.update_canmove()
net.affecting = M
our_net = net
M.visible_message(SPAN_DANGER("A field of condensed Nlom appears around [M]!"), SPAN_DANGER("You are immobilised by a field of condensed Nlom! You feel your body and mind slow down..."))
/obj/effect/energy_net/stasis
name = "condensed nlom field"
desc = "A crystallized field of pure Nlom energy."
icon_state = "shield2"
health = 100
/obj/effect/energy_net/stasis/process()
. = ..()
if(iscarbon(affecting))
var/mob/living/carbon/C = affecting
C.SetStasis(10)
-93
View File
@@ -1,93 +0,0 @@
/singleton/psionic_power/throw_item
name = "Throw"
desc = "Click this spell with an item to prepare it to be thrown, then use the spell to throw the item with lethal force."
icon_state = "wiz_mm"
point_cost = 3
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/projectile/throw_item
/obj/item/spell/projectile/throw_item
name = "throw"
desc = "Almost as cool as the Mass Effect one, but still not as cool."
icon_state = "aspect_bolt"
cast_methods = CAST_RANGED
aspect = ASPECT_PSIONIC
spell_projectile = /obj/item/projectile/psionic_throw
fire_sound = 'sound/weapons/wave.ogg'
cooldown = 20
psi_cost = 20
var/obj/item_to_throw
/obj/item/spell/projectile/throw_item/attackby(obj/item/W, mob/user)
. = ..()
if(!item_to_throw)
owner.drop_item(W)
W.forceMove(src)
to_chat(user, SPAN_NOTICE("You imbue [W] with psionic energy!"))
add_overlay("controlled")
item_to_throw = W
/obj/item/spell/projectile/throw_item/Destroy()
if(item_to_throw)
item_to_throw.forceMove(loc)
item_to_throw = null
return ..()
/obj/item/spell/projectile/throw_item/on_use_cast(mob/user)
. = ..(user, TRUE)
if(item_to_throw)
owner.put_in_hands(item_to_throw)
item_to_throw = null
cut_overlays()
/obj/item/spell/projectile/throw_item/on_ranged_cast(atom/hit_atom, mob/living/user, atom/pb_target)
if(!item_to_throw)
to_chat(user, SPAN_WARNING("You need to imbue an item with psionic energy first!"))
return
. = ..()
item_to_throw = null
cut_overlays()
/obj/item/spell/projectile/throw_item/make_projectile(obj/item/projectile/projectile_type, mob/living/user)
var/obj/item/projectile/psionic_throw/P = ..()
if(P && item_to_throw)
var/base_damage = item_to_throw.force > 5 ? item_to_throw.force : 10
/// Why is it called a fucking divisor? It's a multiplier...
var/thrown_force_divisor = 1
if(istype(item_to_throw, /obj/item/material/twohanded))
var/obj/item/material/twohanded/MT = item_to_throw
base_damage = MT.force_wielded > MT.force ? MT.force_wielded : force
thrown_force_divisor = MT.thrown_force_divisor
/// There are different throwforce calculations, which means that we have to do this manually.
if(base_damage < (item_to_throw.throwforce * thrown_force_divisor))
base_damage = item_to_throw.throwforce * thrown_force_divisor
var/base_armor_penetration
if(item_to_throw.armor_penetration > 2)
base_armor_penetration = item_to_throw.armor_penetration
else
base_armor_penetration = 2
P.name = item_to_throw.name
P.appearance = item_to_throw.appearance
P.damage = base_damage * (item_to_throw.w_class / 2)
P.armor_penetration = base_armor_penetration * item_to_throw.w_class
/// We need to do this again because we're overriding the base make_projectile.
if(owner.psi.get_rank() >= PSI_RANK_APEX)
if(P.damage)
P.damage *= 1.1
P.armor_penetration *= 1.1
P.damage_flags |= item_to_throw.damage_flags()
P.damage_type = item_to_throw.damtype
P.source_item = item_to_throw
return P
/obj/item/projectile/psionic_throw
name = "thrown projectile"
damage_type = DAMAGE_BRUTE
impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_BULLET_MEAT, BULLET_IMPACT_METAL = SOUNDS_BULLET_METAL)
var/obj/source_item
/obj/item/projectile/psionic_throw/Destroy()
if(source_item)
source_item.forceMove(loc)
source_item = null
return ..()
@@ -1,47 +0,0 @@
/singleton/psionic_power/time_stop
name = "Time Stop"
desc = "Freeze living beings around you in a 5x5 area. This ability is very expensive, so be careful."
icon_state = "tech_control"
point_cost = 2
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/time_stop
/obj/item/spell/time_stop
name = "nlom eyes"
desc = "Psionic drugs? No way."
icon_state = "track"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 30
/obj/item/spell/time_stop/Destroy()
for(var/mob/living/L in get_hearers_in_view(5, owner))
if(L == owner)
continue
to_chat(L, SPAN_DANGER("Time around you returns to normal!"))
L.stunned = 0
L.silent = 0
return ..()
/obj/item/spell/time_stop/on_use_cast(mob/user)
. = ..()
if(!.)
return
if(do_after(user, 1 SECOND))
user.visible_message(SPAN_DANGER(FONT_HUGE("[user] extends [user.get_pronoun("his")] arms to [user.get_pronoun("his")] sides!")),
SPAN_DANGER("You extend your arms to your side and crystallize the Nlom around you!"))
time_stop(user)
/obj/item/spell/time_stop/proc/time_stop(mob/living/user)
for(var/mob/living/L in get_hearers_in_view(5, user))
if(L == user)
continue
to_chat(L, SPAN_DANGER("Time around you slows down to a crawl..."))
L.AdjustStunned(5)
L.silent += 30
if(do_after(user, 1 SECOND))
if(user.psi.spend_power(20))
time_stop(user)
-75
View File
@@ -1,75 +0,0 @@
/singleton/psionic_power/warp
name = "Warp"
desc = "Warp through objects."
icon_state = "tech_blink"
point_cost = 4
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/warp
/obj/item/spell/warp
name = "warp"
icon_state = "warp_strike"
cast_methods = CAST_MELEE
aspect = ASPECT_PSIONIC
cooldown = 50
psi_cost = 10
var/maximum_distance = 20 //Measured in tiles.
var/busy = FALSE
/obj/item/spell/warp/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(!.)
return
if(busy) //Prevent someone from trying to get two uses of the spell from one instance.
return FALSE
if(!allowed_to_teleport())
to_chat(user, SPAN_WARNING("You can't teleport here."))
return FALSE
var/turf/T = get_turf(hit_atom) //Turf we touched.
var/turf/our_turf = get_turf(user) //Where we are.
if(!T.density)
if(!T.check_density())
to_chat(user, SPAN_WARNING("This spell is for solid objects."))
return FALSE
var/direction = get_dir(our_turf, T)
var/turf/checked_turf = T //Turf we're currently checking for density in the loop below.
var/turf/found_turf = null //Our destination, if one is found.
var/i = maximum_distance
visible_message(SPAN_NOTICE("[user] rests a hand on \the [hit_atom]."))
busy = TRUE
spark(our_turf, 3, cardinal)
while(i)
checked_turf = get_step(checked_turf, direction) //Advance in the given direction
i--
if(!checked_turf.density) //If we found a destination (a non-dense turf), then we can stop.
var/dense_objs_on_turf = 0
for(var/atom/movable/stuff in checked_turf.contents) //Make sure nothing dense is where we want to go, like an airlock or window.
if(stuff.density)
dense_objs_on_turf = 1
if(!dense_objs_on_turf) //If we found a non-dense turf with nothing dense on it, then that's our destination.
found_turf = checked_turf
break
sleep(10)
if(found_turf)
if(user.loc != our_turf)
to_chat(user, SPAN_WARNING("You need to stand still in order to phase through \the [hit_atom]."))
return FALSE
if(!user.incapacitated())
visible_message(SPAN_WARNING("[user] appears to phase through \the [hit_atom]!"))
to_chat(user, SPAN_NOTICE("You find a destination on the other side of \the [hit_atom], and phase through it."))
spark(src, 5, 0)
user.forceMove(found_turf)
qdel(src)
return TRUE
else
to_chat(user, SPAN_WARNING("You don't have enough energy to phase through these walls!"))
busy = FALSE
else
to_chat(user, SPAN_NOTICE("You weren't able to find an open space to go to."))
busy = FALSE
@@ -1,41 +0,0 @@
/singleton/psionic_power/zona_absorption
name = "Zona Bovinae Absorption"
desc = "Absorb a psionic energy from a being's Zona Bovinae, granting you an extra point to be used in the Point Shop."
icon_state = "tech_shield_old"
point_cost = 0
ability_flags = PSI_FLAG_SPECIAL
spell_path = /obj/item/spell/zona_absorption
/obj/item/spell/zona_absorption
name = "zona bovinae absorption"
icon_state = "chain_lightning"
cast_methods = CAST_MELEE
aspect = ASPECT_PSIONIC
cooldown = 30
psi_cost = 10
/obj/item/spell/zona_absorption/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
if(!isliving(hit_atom))
return
var/mob/living/L = hit_atom
if(!L.has_zona_bovinae())
to_chat(user, SPAN_WARNING("This being doesn't have a Zona Bovinae."))
return
if(HAS_TRAIT(L, TRAIT_ZONA_BOVINAE_ABSORBED))
to_chat(user, SPAN_WARNING("You already absorbed points from this creature!"))
return
. = ..()
if(!.)
return
user.visible_message(SPAN_DANGER("[user] grabs [L]'s head..."), SPAN_DANGER("You grab [L]'s head..."))
if(do_mob(user, L, 5 SECONDS))
L.flash_pain(20)
L.adjustHalLoss(20)
playsound(get_turf(L), 'sound/effects/psi/power_feedback.ogg', 100)
user.visible_message(SPAN_DANGER("[user] absorbs tendrils of psionic energy from [L]!"),
SPAN_DANGER("You absorb psionic energy from [L], granting you an extra Psionic Point Shop point."))
var/mob/living/M = user
M.psi.psi_points++
to_chat(L, SPAN_DANGER("A splitting headache courses through your mind! Your head feels a bit lighter..."))
ADD_TRAIT(L, TRAIT_ZONA_BOVINAE_ABSORBED, TRAIT_SOURCE_PSIONICS)
+17 -15
View File
@@ -1,28 +1,35 @@
/datum/psi_complexus
var/announced = FALSE // Whether or not we have been announced to our holder yet.
var/suppressed = FALSE // Whether or not we are suppressing our psi powers.
var/suppressed = TRUE // Whether or not we are suppressing our psi powers.
var/use_psi_armor = TRUE // Whether or not we should automatically deflect/block incoming damage.
var/rebuild_power_cache = TRUE // Whether or not we need to rebuild our cache of psi powers.
var/rating = 0 // Overall psi rating.
var/cost_modifier = 1 // Multiplier for power use stamina costs.
var/stun = 0 // Number of process ticks we are stunned for.
var/next_power_use = 0 // world.time minimum before next power use.
var/stamina = 100 // Current psi pool.
var/max_stamina = 100 // Max psi pool.
var/psi_points = 0 // Points spendable in the psi pointshop.
var/spent_psi_points = 0 // Need to keep track of if someone has spent psi-points before for things like updating psi-rank.
var/stamina = 50 // Current psi pool.
var/max_stamina = 50 // Max psi pool.
var/psionic_rank
var/last_psionic_rank
var/list/latencies // List of all currently latent faculties.
var/list/ranks // Assoc list of psi faculties to current rank.
var/list/base_ranks // Assoc list of psi faculties to base rank, in case reset is needed
var/list/manifested_items // List of atoms manifested/maintained by psychic power.
var/list/psionic_powers = list() // List of singleton abilities.
var/next_latency_trigger = 0 // world.time minimum before a trigger can be attempted again.
var/last_armor_check // world.time of last armor check.
var/last_aura_size
var/last_aura_alpha
var/last_aura_color
var/aura_color = "#ff0022"
var/datum/component/armor/psionic/armor_component
// Cached powers.
var/list/melee_powers // Powers used in melee range.
var/list/grab_powers // Powers use by using a grab.
var/list/ranged_powers // Powers used at range.
var/list/manifestation_powers // Powers that create an item.
var/list/powers_by_faculty // All powers within a given faculty.
var/obj/screen/psi/hub/ui // Reference to the master psi UI object.
var/mob/living/owner // Reference to our owner.
var/image/_aura_image // Client image
@@ -63,17 +70,13 @@
/datum/psi_complexus/New(var/mob/_owner)
owner = _owner
SSpsi.all_psi_complexes |= src
START_PROCESSING(SSpsi, src)
/datum/psi_complexus/Destroy()
destroy_aura_image(_aura_image)
SSpsi.all_psi_complexes -= src
QDEL_NULL(armor_component)
STOP_PROCESSING(SSpsi, src)
if(owner)
if(owner.ability_master)
owner.ability_master.remove_all_psionic_abilities()
cancel()
if(owner.client)
owner.client.screen -= ui
for(var/thing in SSpsi.all_aura_images)
@@ -86,5 +89,4 @@
for(var/thing in manifested_items)
qdel(thing)
manifested_items.Cut()
. = ..()
@@ -1,28 +1,38 @@
/datum/psi_complexus/proc/cancel()
sound_to(owner, sound('sound/effects/psi/power_fail.ogg'))
if(LAZYLEN(manifested_items))
for(var/thing in manifested_items)
owner.drop_from_inventory(thing)
qdel(thing)
manifested_items = null
/datum/psi_complexus/proc/stunned(var/amount)
var/old_stun = stun
stun = max(stun, amount)
if(amount && !old_stun)
to_chat(owner, "<span class='danger'>Your concentration has been shattered! You cannot focus your psi power!</span>")
ui.update_icon()
cancel()
/datum/psi_complexus/proc/get_armor(var/armortype)
if(can_use_passive())
last_armor_check = world.time
return round(Clamp(Clamp(4 * get_rank(), 0, 20) * get_rank() / 2, 0, 100) * (stamina/max_stamina))
return round(Clamp(Clamp(4 * rating, 0, 20) * get_rank(SSpsi.armor_faculty_by_type[armortype]), 0, 100) * (stamina/max_stamina))
else
last_armor_check = 0
return 0
/datum/psi_complexus/proc/set_rank(var/rank, var/defer_update)
if(get_rank() != rank)
last_psionic_rank = psionic_rank
psionic_rank = rank
/datum/psi_complexus/proc/get_rank(var/faculty)
return LAZYACCESS(ranks, faculty)
/datum/psi_complexus/proc/set_rank(var/faculty, var/rank, var/defer_update, var/temporary)
if(get_rank(faculty) != rank)
LAZYSET(ranks, faculty, rank)
if(!temporary)
LAZYSET(base_ranks, faculty, rank)
if(!defer_update)
update()
/datum/psi_complexus/proc/get_rank()
return psionic_rank
/datum/psi_complexus/proc/set_cooldown(var/value)
next_power_use = world.time + value
ui.update_icon()
@@ -70,13 +80,11 @@
stunned(value * 2)
set_cooldown(value * 100)
if(prob(value*10))
owner.emote("scream")
if(prob(value*10)) owner.emote("scream")
// Your head asplode.
owner.adjustBrainLoss(value)
owner.adjustHalLoss(value * 25) //Ouch.
owner.psi.hide_auras()
if(ishuman(owner))
var/mob/living/carbon/human/pop = owner
if(pop.should_have_organ(BP_BRAIN))
@@ -89,6 +97,8 @@
/datum/psi_complexus/proc/reset()
aura_color = initial(aura_color)
ranks = base_ranks ? base_ranks.Copy() : null
max_stamina = initial(max_stamina)
stamina = min(stamina, max_stamina)
cancel()
update()
@@ -1,6 +1,17 @@
/datum/psi_complexus/proc/check_psionic_trigger(var/trigger_strength = 0, var/source, var/redactive = FALSE)
/datum/psi_complexus/proc/check_latency_trigger(var/trigger_strength = 0, var/source, var/redactive = FALSE)
if(!LAZYLEN(latencies) || world.time < next_latency_trigger)
return FALSE
if(!prob(trigger_strength))
next_latency_trigger = world.time + rand(100, 300)
return FALSE
var/faculty = pick(latencies)
var/new_rank = rand(2,5)
owner.set_psi_rank(new_rank)
to_chat(owner, SPAN_DANGER("You scream internally as your psionics are forced into operancy by [source]!"))
owner.set_psi_rank(faculty, new_rank)
var/datum/psionic_faculty/faculty_decl = SSpsi.get_faculty(faculty)
to_chat(owner, SPAN_DANGER("You scream internally as your [faculty_decl.name] faculty is forced into operancy by [source]!"))
next_latency_trigger = world.time + rand(600, 1800) * new_rank
if(!redactive) owner.adjustBrainLoss(rand(trigger_strength * 2, trigger_strength * 4))
return TRUE
@@ -0,0 +1,49 @@
/datum/psi_complexus/proc/rebuild_power_cache()
if(rebuild_power_cache)
melee_powers = list()
grab_powers = list()
ranged_powers = list()
manifestation_powers = list()
powers_by_faculty = list()
for(var/faculty in ranks)
var/relevant_rank = get_rank(faculty)
var/datum/psionic_faculty/faculty_decl = SSpsi.get_faculty(faculty)
for(var/thing in faculty_decl.powers)
var/datum/psionic_power/power = thing
if(relevant_rank >= power.min_rank)
if(!powers_by_faculty[power.faculty]) powers_by_faculty[power.faculty] = list()
powers_by_faculty[power.faculty] += power
if(power.use_ranged)
if(!ranged_powers[faculty]) ranged_powers[faculty] = list()
ranged_powers[faculty] += power
if(power.use_melee)
if(!melee_powers[faculty]) melee_powers[faculty] = list()
melee_powers[faculty] += power
if(power.use_manifest)
manifestation_powers += power
if(power.use_grab)
if(!grab_powers[faculty]) grab_powers[faculty] = list()
grab_powers[faculty] += power
rebuild_power_cache = FALSE
/datum/psi_complexus/proc/get_powers_by_faculty(var/faculty)
rebuild_power_cache()
return powers_by_faculty[faculty]
/datum/psi_complexus/proc/get_melee_powers(var/faculty)
rebuild_power_cache()
return melee_powers[faculty]
/datum/psi_complexus/proc/get_ranged_powers(var/faculty)
rebuild_power_cache()
return ranged_powers[faculty]
/datum/psi_complexus/proc/get_grab_powers(var/faculty)
rebuild_power_cache()
return grab_powers[faculty]
/datum/psi_complexus/proc/get_manifestations()
rebuild_power_cache()
return manifestation_powers
@@ -1,15 +1,38 @@
/datum/psi_complexus/proc/update(var/force)
set waitfor = FALSE
if(force || last_psionic_rank != psionic_rank)
if(psionic_rank == 0)
qdel(src)
var/last_rating = rating
var/highest_faculty
var/highest_rank = 0
var/combined_rank = 0
for(var/faculty in ranks)
var/check_rank = get_rank(faculty)
if(check_rank == 1)
LAZYADD(latencies, faculty)
else
if(check_rank <= 0)
ranks -= faculty
LAZYREMOVE(latencies, faculty)
combined_rank += check_rank
if(!highest_faculty || highest_rank < check_rank)
highest_faculty = faculty
highest_rank = check_rank
UNSETEMPTY(latencies)
var/rank_count = max(1, LAZYLEN(ranks))
if(force || last_rating != Ceiling(combined_rank/rank_count))
if(highest_rank <= 1)
if(highest_rank == 0)
qdel(src)
return
else
rebuild_power_cache = TRUE
sound_to(owner, 'sound/effects/psi/power_unlock.ogg')
rating = Ceiling(combined_rank/rank_count)
cost_modifier = 1
if(psionic_rank > 1)
cost_modifier -= min(1, max(0.1, psionic_rank / 10))
if(rating > 1)
cost_modifier -= min(1, max(0.1, (rating-1) / 10))
if(!ui)
ui = new(owner)
if(owner.client)
@@ -17,60 +40,35 @@
else
if(owner.client)
owner.client.screen |= ui
if(!suppressed && owner.client)
for(var/thing in SSpsi.all_aura_images)
owner.client.images |= thing
var/image/aura_image = get_aura_image()
if(psionic_rank >= PSI_RANK_APEX) // spooky boosters
if(rating >= PSI_RANK_PARAMOUNT) // spooky boosters
aura_color = "#aaffaa"
aura_image.blend_mode = BLEND_SUBTRACT
else
aura_image.blend_mode = BLEND_ADD
if(psionic_rank == PSI_RANK_SENSITIVE)
if(highest_faculty == PSI_COERCION)
aura_color = "#cc3333"
else if(highest_faculty == PSI_PSYCHOKINESIS)
aura_color = "#3333cc"
else if(highest_faculty == PSI_REDACTION)
aura_color = "#33cc33"
else if(highest_faculty == PSI_ENERGISTICS)
aura_color = "#cccc33"
else if(psionic_rank == PSI_RANK_HARMONIOUS)
aura_color = "#64c464"
if(psionic_rank > PSI_RANK_SENSITIVE && last_psionic_rank < PSI_RANK_HARMONIOUS)
switch(psionic_rank)
if(PSI_RANK_HARMONIOUS)
psi_points = PSI_POINTS_HARMONIOUS
if(PSI_RANK_APEX)
psi_points = PSI_POINTS_APEX
if(PSI_RANK_LIMITLESS)
psi_points = PSI_POINTS_LIMITLESS
/// We had special abilities unique to our level, so get rid of 'em.
if(last_psionic_rank > PSI_RANK_HARMONIOUS)
wipe_user_abilities()
if(last_psionic_rank > PSI_RANK_SENSITIVE && psionic_rank < PSI_RANK_HARMONIOUS)
psi_points = 0
wipe_user_abilities()
if(!announced && owner && owner.client && !QDELETED(src))
announced = TRUE
to_chat(owner, "<hr>")
to_chat(owner, SPAN_NOTICE("<font size = 3>You are <b>psionic</b>, touched by powers beyond understanding.</font>"))
to_chat(owner, SPAN_NOTICE("<b>Left-click</b> your psionic icon to open your Psionic Point Shop, which contains information about your powers. \
You can also <b>shift-click</b> your ability icons on the top right to know more about them. \
Beware: overusing your gifts can have <b>deadly consequences</b>."))
to_chat(owner, SPAN_NOTICE("<b>Shift-left-click your Psi icon</b> on the bottom right to <b>view a summary of how to use them</b>, or <b>left click</b> it to <b>suppress or unsuppress</b> your psionics. Beware: overusing your gifts can have <b>deadly consequences</b>."))
to_chat(owner, "<hr>")
if(get_rank() >= PSI_RANK_SENSITIVE)
for(var/singleton/psionic_power/P in GET_SINGLETON_SUBTYPE_LIST(/singleton/psionic_power))
if((P.ability_flags & PSI_FLAG_FOUNDATIONAL) || (P.ability_flags & PSI_FLAG_APEX && get_rank() >= PSI_RANK_APEX) || (P.ability_flags & PSI_FLAG_LIMITLESS && get_rank() >= PSI_RANK_LIMITLESS))
if(!(P.type in psionic_powers))
P.apply(owner)
/datum/psi_complexus/proc/wipe_user_abilities()
for(var/obj/screen/ability/obj_based/psionic/P in owner.ability_master.ability_objects)
if((P.connected_power.ability_flags & PSI_FLAG_APEX) && get_rank() < PSI_RANK_APEX)
owner.ability_master.remove_ability(P)
if((P.connected_power.ability_flags & PSI_FLAG_LIMITLESS) && get_rank() < PSI_RANK_LIMITLESS)
owner.ability_master.remove_ability(P)
/datum/psi_complexus/process()
var/update_hud
if(stun)
stun--
if(stun)
@@ -79,7 +77,6 @@
update_hud = TRUE
else
to_chat(owner, SPAN_NOTICE("You have recovered your mental composure."))
suppressed = FALSE
update_hud = TRUE
return
@@ -89,11 +86,11 @@
else if(owner?.stat == UNCONSCIOUS)
stamina = min(max_stamina, stamina + rand(3,5))
if(armor_component)
spend_power(1)
if(!owner.nervous_system_failure() && owner.stat == CONSCIOUS && stamina && !suppressed && get_rank(PSI_REDACTION) >= PSI_RANK_OPERANT)
attempt_regeneration()
var/next_aura_size = max(0.1, ((stamina / max_stamina)*min(3, psionic_rank)) / 3)
var/next_aura_alpha = round(((suppressed ? max(0, psionic_rank - 2) : psionic_rank) / 5)*255)
var/next_aura_size = max(0.1,((stamina/max_stamina)*min(3,rating))/5)
var/next_aura_alpha = round(((suppressed ? max(0,rating - 2) : rating)/5)*255)
if(next_aura_alpha != last_aura_alpha || next_aura_size != last_aura_size || aura_color != last_aura_color)
last_aura_size = next_aura_size
@@ -106,3 +103,126 @@
if(update_hud)
ui.update_icon()
/datum/psi_complexus/proc/attempt_regeneration()
var/heal_general = FALSE
var/heal_poison = FALSE
var/heal_internal = FALSE
var/heal_bleeding = FALSE
var/heal_rate = 0
var/mend_prob = 0
var/use_rank = get_rank(PSI_REDACTION)
if(use_rank >= PSI_RANK_PARAMOUNT)
heal_general = TRUE
heal_poison = TRUE
heal_internal = TRUE
heal_bleeding = TRUE
mend_prob = 50
heal_rate = 7
else if(use_rank == PSI_RANK_GRANDMASTER)
heal_poison = TRUE
heal_internal = TRUE
heal_bleeding = TRUE
mend_prob = 20
heal_rate = 5
else if(use_rank == PSI_RANK_MASTER)
heal_internal = TRUE
heal_bleeding = TRUE
mend_prob = 10
heal_rate = 3
else if(use_rank == PSI_RANK_OPERANT)
heal_bleeding = TRUE
mend_prob = 5
heal_rate = 1
else
return
if(!heal_rate || stamina < heal_rate)
return // Don't backblast from trying to heal ourselves thanks.
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
// Fix some pain.
if(heal_rate > 0)
H.shock_stage = max(0, H.shock_stage - max(1, round(heal_rate/2)))
// Mend internal damage.
if(prob(mend_prob))
// Fix our heart if we're paramount.
if(heal_general && H.is_asystole() && H.should_have_organ(BP_HEART) && spend_power(heal_rate))
H.resuscitate()
// Heal organ damage.
if(heal_internal)
for(var/obj/item/organ/I in H.internal_organs)
if(BP_IS_ROBOTIC(I))
continue
if(I.damage > 0 && spend_power(heal_rate))
I.damage = max(I.damage - heal_rate, 0)
if(prob(25))
to_chat(H, SPAN_NOTICE("Your innards itch as your autoredactive faculty mends your [I.name]."))
return
// Heal broken bones.
if(H.bad_external_organs.len)
for(var/obj/item/organ/external/E in H.bad_external_organs)
if(BP_IS_ROBOTIC(E))
continue
if(heal_internal && (E.status & ORGAN_BROKEN) && E.damage < E.min_broken_damage) // So we don't mend and autobreak.
if(spend_power(heal_rate))
if(E.mend_fracture())
to_chat(H, SPAN_NOTICE("Your autoredactive faculty coaxes together the shattered bones in your [E.name]."))
return
if(heal_bleeding)
if((E.status & ORGAN_ARTERY_CUT) && spend_power(heal_rate))
to_chat(H, SPAN_NOTICE("Your autoredactive faculty mends the torn artery in your [E.name], stemming the worst of the bleeding."))
E.status &= ~ORGAN_ARTERY_CUT
return
if((E.tendon_status() & TENDON_CUT) && E.tendon.can_recover())
to_chat(H, SPAN_NOTICE("Your autoredactive faculty repairs the severed tendon in your [E.name]."))
E.tendon.rejuvenate()
return TRUE
for(var/datum/wound/W in E.wounds)
if(W.bleeding() && spend_power(heal_rate))
to_chat(H, SPAN_NOTICE("Your autoredactive faculty knits together severed veins, stemming the bleeding from \a [W.desc] on your [E.name]."))
W.bleed_timer = 0
W.clamped = TRUE
E.status &= ~ORGAN_BLEEDING
return
// Heal radiation, cloneloss and poisoning.
if(heal_poison)
if(owner.total_radiation && spend_power(heal_rate))
if(prob(25))
to_chat(owner, SPAN_NOTICE("Your autoredactive faculty repairs some of the radiation damage to your body."))
owner.total_radiation = max(0, owner.total_radiation - heal_rate)
return
if(owner.getCloneLoss() && spend_power(heal_rate))
if(prob(25))
to_chat(owner, SPAN_NOTICE("Your autoredactive faculty stitches together some of your mangled DNA."))
owner.adjustCloneLoss(-heal_rate)
return
// Heal everything left.
if(heal_general && prob(mend_prob) && (owner.getBruteLoss() || owner.getFireLoss() || owner.getOxyLoss()) && spend_power(heal_rate))
owner.adjustBruteLoss(-(heal_rate))
owner.adjustFireLoss(-(heal_rate))
owner.adjustOxyLoss(-(heal_rate))
if(prob(25))
to_chat(owner, SPAN_NOTICE("Your skin crawls as your autoredactive faculty heals your body."))
@@ -14,7 +14,10 @@
)
var/operating = FALSE
var/boosted_rank = PSI_RANK_HARMONIOUS
var/list/boosted_faculties
var/boosted_rank = PSI_RANK_PARAMOUNT
var/unboosted_rank = PSI_RANK_MASTER
var/max_boosted_faculties = 3
var/boosted_psipower = 120
/obj/item/clothing/head/helmet/space/psi_amp/lesser
@@ -23,14 +26,51 @@
flags_inv = 0
body_parts_covered = 0
boosted_rank = PSI_RANK_HARMONIOUS
max_boosted_faculties = 1
boosted_rank = PSI_RANK_MASTER
unboosted_rank = PSI_RANK_OPERANT
boosted_psipower = 50
/obj/item/clothing/head/helmet/space/psi_amp/Initialize()
. = ..()
verbs += /obj/item/clothing/head/helmet/space/psi_amp/proc/integrate
/obj/item/clothing/head/helmet/space/psi_amp/attack_self(var/mob/user)
if(operating)
return
if(!canremove)
deintegrate()
return
var/mob/living/carbon/human/H = loc
if(istype(H) && H.head == src)
integrate()
return
var/choice = input("Select a brainboard to install or remove.","Psionic Amplifier") as null|anything in SSpsi.faculties_by_name
if(!choice)
return
var/removed
var/slots_left = max_boosted_faculties - LAZYLEN(boosted_faculties)
var/datum/psionic_faculty/faculty = SSpsi.get_faculty(choice)
if(faculty.id in boosted_faculties)
LAZYREMOVE(boosted_faculties, faculty.id)
removed = TRUE
else
if(slots_left <= 0)
to_chat(user, SPAN_WARNING("There are no slots left to install brainboards into."))
return
LAZYADD(boosted_faculties, faculty.id)
UNSETEMPTY(boosted_faculties)
slots_left = max_boosted_faculties - LAZYLEN(boosted_faculties)
to_chat(user, SPAN_NOTICE("You [removed ? "remove" : "install"] the [choice] brainboard [removed ? "from" : "in"] \the [src]. There [slots_left!=1 ? "are" : "is"] [slots_left] slot\s left."))
/obj/item/clothing/head/helmet/space/psi_amp/proc/deintegrate()
set name = "Remove Psi-Amp"
set desc = "Removes your psi-amp."
set category = "Abilities"
@@ -81,6 +121,7 @@
canremove = TRUE
/obj/item/clothing/head/helmet/space/psi_amp/proc/integrate()
set name = "Integrate Psionic Amplifier"
set desc = "Enhance your brainpower."
set category = "Abilities"
@@ -92,6 +133,10 @@
if(!canremove)
return
if(LAZYLEN(boosted_faculties) < max_boosted_faculties)
to_chat(usr, SPAN_NOTICE("You still have [max_boosted_faculties - LAZYLEN(boosted_faculties)] facult[LAZYLEN(boosted_faculties) == 1 ? "y" : "ies"] to select. Use \the [src] in-hand to select them."))
return
var/mob/living/carbon/human/H = loc
if(!istype(H) || H.head != src)
to_chat(usr, SPAN_WARNING("\The [src] must be worn on your head in order to be activated."))
@@ -104,7 +149,11 @@
sleep(80)
H.set_psi_rank(boosted_rank, temporary = TRUE)
for(var/faculty in list(PSI_COERCION, PSI_PSYCHOKINESIS, PSI_REDACTION, PSI_ENERGISTICS))
if(faculty in boosted_faculties)
H.set_psi_rank(faculty, boosted_rank, take_larger = TRUE, temporary = TRUE)
else
H.set_psi_rank(faculty, unboosted_rank, take_larger = TRUE, temporary = TRUE)
if(H.psi)
H.psi.max_stamina = boosted_psipower
H.psi.stamina = H.psi.max_stamina
@@ -116,32 +165,5 @@
operating = FALSE
action_button_name = "Remove Psionic Amplifier"
H.update_action_buttons()
H.client.init_verbs()
set_light(0.5, 0.1, 3, 2, l_color = "#880000")
/obj/item/psionic_jumpstarter
name = "psionic jumpstarter"
desc = "Use this to jumpstart your psionic rank to Psionically Harmonious, enabling you to use the Psionic Point Shop and buy offensive psionic abilities. \
This won't work on species with no Zona Bovinae, like synthetics, vaurcae or dionae! This item is definitely not canon."
icon = 'icons/obj/clothing/hats.dmi'
icon_state = "amp"
contained_sprite = FALSE
/obj/item/psionic_jumpstarter/attack_self(mob/user)
. = ..()
if(!ishuman(user))
return
var/mob/living/carbon/human/H = user
if(!H.has_zona_bovinae())
to_chat(H, SPAN_WARNING("You don't have a Zona Bovinae!"))
return
if(H.psi && H.psi.get_rank() >= PSI_RANK_HARMONIOUS)
to_chat(H, SPAN_WARNING("You've already awakened your psionic potential!"))
return
H.set_psi_rank(PSI_RANK_HARMONIOUS)
H.psi.psi_points = 8
to_chat(H, SPAN_NOTICE("You've awakened your psionic potential. Note that you have a reduced point pool than usual."))
qdel(src)
@@ -54,7 +54,3 @@
host.drop_from_inventory(src)
else
qdel(src)
/obj/item/psychic_power/damage_flags()
. = ..()
. |= DAMAGE_FLAG_PSIONIC
@@ -1,5 +1,5 @@
/obj/item/psychic_power/psiblade
name = "psionic blade"
name = "psychokinetic slash"
force = 10
sharp = 1
edge = TRUE
@@ -7,25 +7,21 @@
icon_state = "psiblade_short"
hitsound = 'sound/weapons/psisword.ogg'
/obj/item/psychic_power/psiblade/Initialize()
. = ..()
switch(owner.psi.get_rank())
if(PSI_RANK_SENSITIVE)
force = 20
armor_penetration = 10
if(PSI_RANK_HARMONIOUS)
force = 25
armor_penetration = 20
if(PSI_RANK_APEX)
force = 30
armor_penetration = 30
icon_state = "psiblade_long"
if(PSI_RANK_LIMITLESS)
force = 40
armor_penetration = 40
icon_state = "psiblade_long"
/obj/item/psychic_power/psiblade/dropped(var/mob/living/user)
. = ..()
playsound(loc, 'sound/effects/psi/power_fail.ogg', 30, 1)
QDEL_IN(src, 1)
/obj/item/psychic_power/psiblade/master
force = 20
maintain_cost = 2
/obj/item/psychic_power/psiblade/master/grand
force = 30
maintain_cost = 3
icon_state = "psiblade_long"
/obj/item/psychic_power/psiblade/master/grand/paramount // Silly typechecks because rewriting old interaction code is outside of scope.
force = 50
maintain_cost = 4
icon_state = "psiblade_long"
@@ -0,0 +1,94 @@
/obj/item/psychic_power/telekinesis
name = "telekinetic grip"
maintain_cost = 3
icon_state = "telekinesis"
var/atom/movable/focus
/obj/item/psychic_power/telekinesis/Destroy()
focus = null
. = ..()
/obj/item/psychic_power/telekinesis/process()
if(!focus || !istype(focus.loc, /turf) || get_dist(get_turf(focus), get_turf(owner)) > owner.psi.get_rank(PSI_PSYCHOKINESIS))
owner.drop_from_inventory(src)
return
. = ..()
/obj/item/psychic_power/telekinesis/proc/set_focus(var/atom/movable/_focus)
if(!_focus.simulated || !istype(_focus.loc, /turf))
return FALSE
var/check_paramount
if(ismob(_focus))
var/mob/victim = _focus
check_paramount = (victim.mob_size >= MOB_MEDIUM)
else if(isobj(_focus))
var/obj/thing = _focus
check_paramount = (thing.w_class >= 5)
else
return FALSE
if(_focus.anchored || (check_paramount && owner.psi.get_rank(PSI_PSYCHOKINESIS) < PSI_RANK_PARAMOUNT))
focus = _focus
. = attack_self(owner)
if(!.)
to_chat(owner, SPAN_WARNING("\The [_focus] is too hefty for you to get a mind-grip on."))
qdel(src)
return FALSE
focus = _focus
overlays.Cut()
var/image/I = image(icon = focus.icon, icon_state = focus.icon_state)
I.color = focus.color
I.overlays = focus.overlays
overlays += I
return TRUE
/obj/item/psychic_power/telekinesis/attack_self(var/mob/user)
user.visible_message(SPAN_NOTICE("\The [user] makes a strange gesture."))
sparkle()
return focus.do_simple_ranged_interaction(user)
/obj/item/psychic_power/telekinesis/afterattack(var/atom/target, var/mob/living/user, var/proximity)
if(!target || !user || (isobj(target) && !isturf(target.loc)) || !user.psi || !user.psi.can_use() || !user.psi.spend_power(5))
return
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.psi.set_cooldown(5)
var/distance = get_dist(get_turf(user), get_turf(focus ? focus : target))
if(distance > user.psi.get_rank(PSI_PSYCHOKINESIS))
to_chat(user, SPAN_WARNING("Your telekinetic power won't reach that far."))
return FALSE
if(target == focus)
attack_self(user)
else
user.visible_message(SPAN_DANGER("\The [user] gestures sharply!"))
sparkle()
if(!istype(target, /turf) && istype(focus,/obj/item) && target.Adjacent(focus))
var/obj/item/I = focus
var/resolved = target.attackby(I, user, user:get_organ_target())
if(!resolved && target && I)
I.afterattack(target,user,1) // for splashing with beakers
else
if(!focus.anchored)
var/user_rank = owner.psi.get_rank(PSI_PSYCHOKINESIS)
focus.throw_at(target, user_rank*2, user_rank*10, owner)
sleep(1)
sparkle()
/obj/item/psychic_power/telekinesis/proc/sparkle()
set waitfor = 0
if(focus)
var/obj/effect/overlay/O = new /obj/effect/overlay(get_turf(focus))
O.name = "sparkles"
O.anchored = 1
O.density = 0
O.layer = FLY_LAYER
O.set_dir(pick(cardinal))
O.icon = 'icons/effects/effects.dmi'
O.icon_state = "nothing"
flick("empdisable",O)
sleep(5)
qdel(O)
+8 -4
View File
@@ -48,12 +48,16 @@
else
to_chat(victim, SPAN_DANGER("An indescribable, brain-tearing sound hisses from [icon2html(source, victim)] \the [source], and you collapse in a seizure!"))
victim.seizure()
to_chat(victim, SPAN_DANGER(FONT_LARGE("[pick(psi_operancy_messages)]")))
victim.adjustBrainLoss(rand(10,20))
victim.set_psi_rank(1)
var/new_latencies = rand(2,4)
var/list/faculties = list(PSI_COERCION, PSI_REDACTION, PSI_ENERGISTICS, PSI_PSYCHOKINESIS)
for(var/i = 1 to new_latencies)
to_chat(victim, SPAN_DANGER("<font size = 3>[pick(psi_operancy_messages)]</font>"))
victim.adjustBrainLoss(rand(10,20))
victim.set_psi_rank(pick_n_take(faculties), 1)
sleep(30)
victim.psi.update()
sleep(45)
victim.psi.check_psionic_trigger(100, "a psionic scream", redactive = TRUE)
victim.psi.check_latency_trigger(100, "a psionic scream", redactive = TRUE)
/datum/event/minispasm/end()
command_announcement.Announce( \
@@ -0,0 +1,11 @@
/datum/psionic_faculty
var/id
var/name
var/associated_intent
var/list/armor_types = list()
var/list/powers = list()
/datum/psionic_faculty/New()
..()
for(var/atype in armor_types)
SSpsi.armor_faculty_by_type[atype] = id
+37
View File
@@ -0,0 +1,37 @@
/datum/psionic_power
var/name // Name. If null, psipower won't be generated on roundstart.
var/faculty // Associated psi faculty.
var/min_rank // Minimum psi rank to use this power.
var/cost // Base psi stamina cost for using this power.
var/cooldown // Deciseconds cooldown after using this power.
var/admin_log = TRUE // Whether or not using this power prints an admin attack log.
var/use_ranged // This power functions from a distance.
var/use_melee // This power functions at melee range.
var/use_grab // This power has a variant invoked via grab.
var/use_manifest // This power manifests an item in the user's hands.
var/use_description // A short description of how to use this power, shown via assay.
// A sound effect to play when the power is used.
var/use_sound = 'sound/effects/psi/power_used.ogg'
/datum/psionic_power/proc/invoke(var/mob/living/user, var/atom/target)
if(!user.psi)
return FALSE
if(faculty && min_rank)
var/user_rank = user.psi.get_rank(faculty)
if(user_rank < min_rank)
return FALSE
if(cost && !user.psi.spend_power(cost))
return FALSE
return TRUE
/datum/psionic_power/proc/handle_post_power(var/mob/living/user, var/atom/target)
if(cooldown)
user.psi.set_cooldown(cooldown)
if(admin_log && ismob(user) && ismob(target))
admin_attack_log(user, target, "Used psipower ([name])", "Was subjected to a psipower ([name])", "used a psipower ([name]) on")
if(use_sound)
playsound(user.loc, use_sound, 75)
+337
View File
@@ -0,0 +1,337 @@
/datum/psionic_faculty/coercion
id = PSI_COERCION
name = "Coercion"
associated_intent = I_DISARM
/datum/psionic_power/coercion
faculty = PSI_COERCION
/datum/psionic_power/coercion/invoke(var/mob/living/user, var/mob/living/target)
if (!istype(target))
to_chat(user, SPAN_WARNING("You cannot mentally attack \the [target]."))
return FALSE
. = ..()
/datum/psionic_power/coercion/blindstrike
name = "Blindstrike"
cost = 8
cooldown = 120
use_ranged = TRUE
use_melee = TRUE
min_rank = PSI_RANK_GRANDMASTER
use_description = "Target the eyes on disarm intent and click anywhere to use a radial attack that blinds, deafens and disorients everyone near you."
/datum/psionic_power/coercion/blindstrike/invoke(var/mob/living/user, var/mob/living/target)
if(user.zone_sel.selecting != BP_EYES)
return FALSE
. = ..()
if(.)
user.visible_message(SPAN_DANGER("\The [user] suddenly throws back their head, as though screaming silently!"), SPAN_NOTICE("You strike at all around you with a deafening psionic scream!"))
for(var/mob/living/M in orange(user.psi.get_rank(PSI_COERCION), user))
if(M == user)
continue
if(prob(60) && iscarbon(M))
var/mob/living/carbon/C = M
C.emote("scream")
to_chat(M, SPAN_DANGER("Your senses are blasted into oblivion by a psionic scream!"))
M.eye_blind = max(M.eye_blind,3)
M.ear_deaf = max(M.ear_deaf,6)
if(!M.isSynthetic())
M.confused = max(M.confused, rand(3,8))
return TRUE
/datum/psionic_power/coercion/mindread
name = "Read Mind"
cost = 30
cooldown = 150 //It should take a good bit to be able to use this again.
use_melee = TRUE
min_rank = PSI_RANK_OPERANT
use_description = "Target the head on disarm intent at melee range to attempt to read a victim's surface thoughts."
/datum/psionic_power/coercion/mindread/invoke(var/mob/living/user, var/mob/living/target)
if(!isliving(target) || !istype(target) || user.zone_sel.selecting != BP_HEAD)
return FALSE
. = ..()
if(!.)
return
if(target.stat == DEAD || (target.status_flags & FAKEDEATH) || !target.client)
to_chat(user, SPAN_WARNING("[target] is in no state for a mind-read."))
return TRUE
user.visible_message(SPAN_WARNING("\The [user] touches \the [target]'s temple..."))
var/question = sanitize(input(user, "Say something?", "Read Mind", "Penny for your thoughts?") as null|text)
if(!question || user.incapacitated() || !do_mob(user, target, 20))
return TRUE
var/psi_blocked = target.is_psi_blocked()
if(psi_blocked)
to_chat(user, psi_blocked)
return TRUE
var/started_mindread = world.time
if(target.has_psi_aug())
to_chat(user, SPAN_NOTICE("<b>Your psyche links with [target]'s psi-receiver, seeking an answer from their mind's surface: <i>[question]</i></b>"))
to_chat(target, SPAN_NOTICE("<b>[user]'s psyche links with your psi-receiver, your mind is compelled to answer: <i>[question]</i></b>"))
else
to_chat(user, SPAN_NOTICE("<b>You dip your mentality into the surface layer of \the [target]'s mind, seeking an answer: <i>[question]</i></b>"))
to_chat(target, SPAN_NOTICE("<b>Your mind is compelled to answer: <i>[question]</i></b>"))
var/answer = sanitize(input(target, "[question]\nYou have 25 seconds to type a response", "Read Mind") as null|text)
if(!answer || world.time > started_mindread + 25 SECONDS || user.stat != CONSCIOUS)
to_chat(user, SPAN_NOTICE("<b>You receive nothing useful from \the [target].</b>"))
to_chat(target, SPAN_NOTICE("Your mind blanks out momentarily."))
else
to_chat(user, SPAN_NOTICE("<b>You skim thoughts from the surface of \the [target]'s mind: <i>[answer]</i></b>"))
msg_admin_attack("[key_name(user)] read mind of [key_name(target)] with question \"[question]\" and [answer?"got answer \"[answer]\".":"got no answer."]")
return TRUE
/datum/psionic_power/coercion/agony
name = "Agony"
cost = 8
cooldown = 50
use_melee = TRUE
min_rank = PSI_RANK_MASTER
use_description = "Target the chest or groin on disarm intent to use a melee attack equivalent to a strike from a stun baton."
/datum/psionic_power/coercion/agony/invoke(var/mob/living/user, var/mob/living/target)
if(!istype(target))
return FALSE
if(user.zone_sel.selecting != BP_CHEST && user.zone_sel.selecting != BP_GROIN)
return FALSE
. = ..()
if(.)
user.visible_message(SPAN_DANGER("\The [target] has been struck by \the [user]!"))
playsound(user.loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
target.stun_effect_act(0, 60, user.zone_sel.selecting)
return TRUE
/datum/psionic_power/coercion/spasm
name = "Spasm"
cost = 15
cooldown = 100
use_melee = TRUE
use_ranged = TRUE
min_rank = PSI_RANK_MASTER
use_description = "Target the arms or hands on disarm intent to use a ranged attack that may rip the weapons away from the target."
/datum/psionic_power/coercion/spasm/invoke(var/mob/living/user, var/mob/living/carbon/human/target)
if(!istype(target))
return FALSE
if(!(user.zone_sel.selecting in list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND)))
return FALSE
. = ..()
if(.)
to_chat(user, SPAN_DANGER("You lash out, stabbing into \the [target] with a lance of psi-power."))
to_chat(target, SPAN_DANGER("The muscles in your arms cramp horrendously!"))
if(prob(75))
target.emote("scream")
if(prob(75) && target.l_hand && target.l_hand.simulated && target.unEquip(target.l_hand))
target.visible_message(SPAN_DANGER("\The [target] drops what they were holding as their left hand spasms!"))
if(prob(75) && target.r_hand && target.r_hand.simulated && target.unEquip(target.r_hand))
target.visible_message(SPAN_DANGER("\The [target] drops what they were holding as their right hand spasms!"))
return TRUE
/datum/psionic_power/coercion/mindslave
name = "Mindslave"
cost = 28
cooldown = 200
use_grab = TRUE
min_rank = PSI_RANK_PARAMOUNT
use_description = "Grab a victim, target the eyes, then use the grab on them while on disarm intent, in order to convert them into a loyal mind-slave. The process takes some time, and failure is punished harshly."
/datum/psionic_power/coercion/mindslave/invoke(var/mob/living/user, var/mob/living/target)
if(!istype(target) || user.zone_sel.selecting != BP_EYES)
return FALSE
. = ..()
if(.)
if(target.stat == DEAD || (target.status_flags & FAKEDEATH))
to_chat(user, SPAN_WARNING("\The [target] is dead!"))
return TRUE
if(!target.mind || !target.key)
to_chat(user, SPAN_WARNING("\The [target] is mindless!"))
return TRUE
for (var/obj/item/implant/mindshield/I in target)
if (I.implanted)
to_chat(user, SPAN_WARNING("\The [target]'s mind is protected from the mindslaving."))
return TRUE
user.visible_message(SPAN_DANGER("<i>\The [user] seizes the head of \the [target] in both hands...</i>"))
to_chat(user, SPAN_WARNING("You plunge your mentality into that of \the [target]..."))
to_chat(target, SPAN_DANGER("Your mind is invaded by the presence of \the [user]! They are trying to make you a slave!"))
if(!do_after(user, target.stat == CONSCIOUS ? 80 : 40, target, 0, 1))
user.psi.backblast(rand(10,25))
return TRUE
to_chat(user, SPAN_DANGER("You sear through \the [target]'s neurons, reshaping as you see fit and leaving them subservient to your will!"))
to_chat(target, SPAN_DANGER("Your defenses have eroded away and \the [user] has made you their mindslave."))
thralls.add_antagonist(target.mind, TRUE, TRUE, FALSE, TRUE, TRUE)
return TRUE
/datum/psionic_power/coercion/assay
name = "Assay"
cost = 15
cooldown = 100
use_grab = TRUE
min_rank = PSI_RANK_OPERANT
use_description = "Grab a patient, target the head, then use the grab on them while on disarm intent, in order to perform a deep coercive-redactive probe of their psionic potential."
/datum/psionic_power/coercion/assay/invoke(var/mob/living/user, var/mob/living/target)
if(user.zone_sel.selecting != BP_HEAD)
return FALSE
. = ..()
if(.)
user.visible_message(SPAN_WARNING("\The [user] holds the head of \the [target] in both hands..."))
to_chat(user, SPAN_NOTICE("You insinuate your mentality into that of \the [target]..."))
to_chat(target, SPAN_WARNING("Your persona is being probed by the psychic lens of \the [user]."))
if(!do_after(user, (target.stat == CONSCIOUS ? 50 : 25), target, 0, 1))
user.psi.backblast(rand(5,10))
return TRUE
to_chat(user, SPAN_NOTICE("You retreat from \the [target], holding your new knowledge close."))
to_chat(target, SPAN_DANGER("Your mental complexus is laid bare to judgement of \the [user]."))
target.show_psi_assay(user)
return TRUE
/datum/psionic_power/coercion/focus
name = "Focus"
cost = 10
cooldown = 80
use_grab = TRUE
min_rank = PSI_RANK_MASTER
use_description = "Grab a patient, target the mouth, then use the grab on them while on disarm intent, in order to cure ailments of the mind."
/datum/psionic_power/coercion/focus/invoke(var/mob/living/user, var/mob/living/target)
if(user.zone_sel.selecting != "mouth")
return FALSE
. = ..()
if(.)
user.visible_message(SPAN_WARNING("\The [user] holds the head of \the [target] in both hands..."))
to_chat(user, SPAN_NOTICE("You probe \the [target]'s mind for various ailments.."))
to_chat(target, SPAN_WARNING("Your mind is being cleansed of ailments by \the [user]."))
if(!do_after(user, (target.stat == CONSCIOUS ? 50 : 25), target, 0, 1))
user.psi.backblast(rand(5,10))
return TRUE
to_chat(user, SPAN_WARNING("You clear \the [target]'s mind of ailments."))
to_chat(target, SPAN_WARNING("Your mind is cleared of ailments."))
var/coercion_rank = user.psi.get_rank(PSI_COERCION)
if(coercion_rank >= PSI_RANK_GRANDMASTER)
target.AdjustParalysis(-1)
target.drowsiness = 0
if(istype(target, /mob/living/carbon))
var/mob/living/carbon/M = target
M.hallucination = max(M.hallucination, 10)
return TRUE
/datum/psionic_power/coercion/commune
name = "Commune"
cost = 15
cooldown = 10
use_melee = TRUE
use_ranged = TRUE
min_rank = PSI_RANK_OPERANT
use_sound = null
use_description = "Target the mouth and click on a creature on disarm intent to psionically send them a message."
admin_log = FALSE
/datum/psionic_power/coercion/commune/invoke(var/mob/living/user, var/mob/living/target)
if((target == user) || user.zone_sel.selecting != BP_MOUTH)
return FALSE
. = ..()
if(.)
user.visible_message(SPAN_NOTICE("<i>[user] blinks, their eyes briefly developing an unnatural shine.</i>"))
if(target.stat == DEAD)
to_chat(user, SPAN_CULT("Not even a psion of your level can speak to the dead."))
return
var/text = input("What would you like to say?", "Speak to creature", null, null)
text = sanitize(text)
if(!text)
return
text = formalize_text(text)
if(target.stat == DEAD)
to_chat(user, SPAN_CULT("Not even a psion of your level can speak to the dead."))
return
var/psi_blocked = target.is_psi_blocked()
if(psi_blocked)
to_chat(user, psi_blocked)
return
log_say("[key_name(user)] communed to [key_name(target)]: [text]",ckey=key_name(src))
to_chat(user, SPAN_CULT("You psionically say to [target]: [text]"))
for (var/mob/M in player_list)
if (istype(M, /mob/abstract/new_player))
continue
else if(M.stat == DEAD && M.client.prefs.toggles & CHAT_GHOSTEARS)
to_chat(M, "<span class='notice'>[user] psionically says to [target]:</span> [text]")
var/mob/living/carbon/human/H = target
if(H.can_commune() || H.psi)
to_chat(H, SPAN_CULT("<b>You instinctively sense [user] passing a thought into your mind:</b> [text]"))
else if(target.has_psi_aug())
to_chat(H, SPAN_CULT("<b>You sense [user]'s psyche link with your psi-receiver, a thought sliding into your mind:</b> [text]"))
else
to_chat(H, SPAN_ALIEN("<b>A thought from outside your consciousness slips into your mind:</b> [text]"))
/datum/psionic_power/coercion/psiping
name = "Psi Ping"
cost = 5
cooldown = 30
use_melee = TRUE
use_manifest = TRUE
min_rank = PSI_RANK_OPERANT
use_sound = null
use_description = "Activate an empty hand on help intent to detect nearby psionic signatures."
admin_log = FALSE
/datum/psionic_power/coercion/psiping/invoke(var/mob/living/user, var/mob/living/target)
if((target && user != target) || user.a_intent != I_HELP)
return FALSE
. = ..()
if(.)
to_chat(user, SPAN_NOTICE("You take a moment to tune into the local Nlom..."))
if(!do_after(user, 3 SECONDS))
return
var/list/dirs = list()
var/turf/our_turf = get_turf(user)
for(var/mob/living/L in range(20))
var/turf/T = get_turf(L)
if(!T || L == user || L.stat == DEAD || L.invisibility == INVISIBILITY_LEVEL_TWO)
continue
if(!L.is_psi_pingable())
continue
var/image/ping_image = image(icon = 'icons/effects/effects.dmi', icon_state = "sonar_ping", loc = our_turf, layer = OBFUSCATION_LAYER + 0.1)
pixel_shift_to_turf(ping_image, our_turf, T)
user << ping_image
QDEL_IN(ping_image, 8)
var/direction = num2text(get_dir(user, L))
var/dist
if(text2num(direction))
switch(get_dist(user, L) / user.client.view)
if(0 to 0.2)
dist = "very close"
if(0.2 to 0.4)
dist = "close"
if(0.4 to 0.6)
dist = "a little ways away"
if(0.6 to 0.8)
dist = "farther away"
else
dist = "far away"
else
dist = "on top of you"
LAZYINITLIST(dirs[direction])
dirs[direction][dist] += 1
for(var/d in dirs)
var/list/feedback = list()
for(var/dst in dirs[d])
feedback += "[dirs[d][dst]] psionic signature\s [dst],"
if(feedback.len > 1)
feedback[feedback.len - 1] += " and"
to_chat(user, SPAN_NOTICE("You sense " + jointext(feedback, " ") + " towards the [dir2text(text2num(d))]."))
if(!length(dirs))
to_chat(user, SPAN_NOTICE("You detect no psionic signatures but your own."))
@@ -0,0 +1,116 @@
/datum/psionic_faculty/energistics
id = PSI_ENERGISTICS
name = "Energistics"
associated_intent = I_HURT
armor_types = list("bomb", "laser", "energy")
/datum/psionic_power/energistics
faculty = PSI_ENERGISTICS
/datum/psionic_power/energistics/electropulse
name = "Electropulse"
cost = 40
cooldown = 100
use_melee = TRUE
min_rank = PSI_RANK_MASTER
use_description = "Target the right hand while on harm intent and click an object to use a melee attack that causes a localized EMP. This activates the EMP function on things, but it takes a while and applies a long cooldown, in addition to being expensive."
/datum/psionic_power/energistics/electropulse/invoke(var/mob/living/user, var/mob/living/target)
if(user.zone_sel.selecting != BP_R_HAND)
return FALSE
if(istype(target, /turf) || ismob(target))
return FALSE
. = ..()
if(.)
user.visible_message(SPAN_WARNING("\The [user] holds their hands over \the [target]..."))
if(do_after(user, 100, TRUE, target))
user.visible_message(SPAN_DANGER("\The [user] releases a gout of arcing lightning over \the [target]!"))
playsound(target, 'sound/magic/LightningShock.ogg', 75)
var/severity = 1 + user.psi.get_rank(PSI_ENERGISTICS)
target.emp_act(severity)
return TRUE
/datum/psionic_power/energistics/electrocute
name = "Electrocute"
cost = 15
cooldown = 25
use_melee = TRUE
min_rank = PSI_RANK_GRANDMASTER
use_description = "Target the chest or groin while on harm intent to use a melee attack that electrocutes a victim."
/datum/psionic_power/energistics/electrocute/invoke(var/mob/living/user, var/mob/living/target)
if(user.zone_sel.selecting != BP_CHEST && user.zone_sel.selecting != BP_GROIN)
return FALSE
if(istype(target, /turf))
return FALSE
. = ..()
if(.)
user.visible_message(SPAN_DANGER("\The [user] sends a jolt of electricity arcing into \the [target]!"))
if(istype(target))
target.electrocute_act(rand(15,45), user, 1, user.zone_sel.selecting)
return TRUE
else if(istype(target, /atom))
var/obj/item/cell/charging_cell = target.get_cell()
if(istype(charging_cell))
charging_cell.give(rand(15,45))
return TRUE
/datum/psionic_power/energistics/lightning
name = "Lightning"
cost = 20
cooldown = 20
use_ranged = TRUE
min_rank = PSI_RANK_MASTER
use_description = "Use this ranged lightning attack while on harm intent. Your mastery of Energistics will determine how powerful the lightning is. Be wary of overuse, and try not to fry your own brain."
/datum/psionic_power/energistics/lightning/invoke(var/mob/living/user, var/mob/living/target)
. = ..()
if(.)
user.visible_message(SPAN_DANGER("\The [user] suddenly holds their hand out!"))
var/user_rank = user.psi.get_rank(faculty)
var/obj/item/projectile/pew
var/pew_sound = 'sound/magic/LightningShock.ogg'
switch(user_rank)
if(PSI_RANK_PARAMOUNT)
pew = new /obj/item/projectile/beam/tesla/paramount(get_turf(user))
pew.name = "thunderstrike"
pew_sound = 'sound/effects/psi/thunderstrike.ogg'
if(PSI_RANK_GRANDMASTER)
pew = new /obj/item/projectile/beam/tesla/grandmaster(get_turf(user))
pew.name = "lightning shock"
if(PSI_RANK_MASTER)
pew = new /obj/item/projectile/beam/tesla/master(get_turf(user))
pew.name = "lightning beam"
if(istype(pew))
playsound(pew.loc, pew_sound, 25, 1)
pew.original = target
pew.starting = get_turf(user)
pew.shot_from = user
pew.launch_projectile(target)
return TRUE
/datum/psionic_power/energistics/spark
name = "Spark"
cost = 1
cooldown = 1
use_melee = TRUE
min_rank = PSI_RANK_OPERANT
use_description = "Target a non-living target in melee range on harm intent to cause some sparks to appear. This can light fires."
/datum/psionic_power/energistics/spark/invoke(var/mob/living/user, var/mob/living/target)
if(isnull(target) || istype(target))
return FALSE
. = ..()
if(.)
if(istype(target,/obj/item/clothing/mask/smokable/cigarette))
var/obj/item/clothing/mask/smokable/cigarette/S = target
S.light(SPAN_NOTICE("\The [user] snaps [user.get_pronoun("his")] fingers and \the [S] lights up."))
playsound(S.loc, /singleton/sound_category/spark_sound, 50, 1)
return TRUE
if(!isturf(target))
return FALSE
spark(target, 3)
return TRUE
@@ -0,0 +1,93 @@
/datum/psionic_faculty/psychokinesis
id = PSI_PSYCHOKINESIS
name = "Psychokinesis"
associated_intent = I_GRAB
armor_types = list("melee", "bullet")
/datum/psionic_power/psychokinesis
faculty = PSI_PSYCHOKINESIS
use_sound = null
/datum/psionic_power/psychokinesis/psiblade
name = "Psiblade"
cost = 10
cooldown = 30
min_rank = PSI_RANK_OPERANT
use_manifest = TRUE
use_description = "Click on or otherwise activate an empty hand while on harm intent to manifest a psychokinetic cutting blade. The power the blade will vary based on your mastery of the faculty."
admin_log = FALSE
/datum/psionic_power/psychokinesis/psiblade/invoke(var/mob/living/user, var/mob/living/target)
if((target && user != target) || user.a_intent != I_HURT)
return FALSE
. = ..()
if(.)
switch(user.psi.get_rank(faculty))
if(PSI_RANK_PARAMOUNT)
return new /obj/item/psychic_power/psiblade/master/grand/paramount(user, user)
if(PSI_RANK_GRANDMASTER)
return new /obj/item/psychic_power/psiblade/master/grand(user, user)
if(PSI_RANK_MASTER)
return new /obj/item/psychic_power/psiblade/master(user, user)
else
return new /obj/item/psychic_power/psiblade(user, user)
/datum/psionic_power/psychokinesis/tinker
name = "Tinker"
cost = 5
cooldown = 10
min_rank = PSI_RANK_MASTER
use_manifest = TRUE
use_description = "Click on or otherwise activate an empty hand while on grab intent to manifest a psychokinetic tool. Use it in-hand to switch between tool types."
admin_log = FALSE
/datum/psionic_power/psychokinesis/tinker/invoke(var/mob/living/user, var/mob/living/target)
if((target && user != target) || user.a_intent != I_GRAB)
return FALSE
. = ..()
if(.)
return new /obj/item/psychic_power/tinker(user)
/datum/psionic_power/psychokinesis/telekinesis
name = "Telekinesis"
cost = 5
cooldown = 10
use_ranged = TRUE
use_manifest = FALSE
min_rank = PSI_RANK_GRANDMASTER
use_description = "Click on a distant target while on grab intent to manifest a psychokinetic grip. Use it manipulate objects at a distance."
admin_log = FALSE
use_sound = 'sound/effects/psi/power_used.ogg'
var/global/list/valid_machine_types = list(
/obj/machinery/door
)
/datum/psionic_power/psychokinesis/telekinesis/invoke(var/mob/living/user, var/mob/living/target)
if(user.a_intent != I_GRAB)
return FALSE
. = ..()
if(.)
var/distance = get_dist(user, target)
if(distance > user.psi.get_rank(PSI_PSYCHOKINESIS))
to_chat(user, SPAN_WARNING("Your telekinetic power won't reach that far."))
return FALSE
if(istype(target, /mob) || istype(target, /obj))
var/obj/item/psychic_power/telekinesis/tk = new(user)
if(tk.set_focus(target))
tk.sparkle()
user.visible_message(SPAN_NOTICE("\The [user] reaches out."))
return tk
else if(istype(target, /obj/structure))
user.visible_message(SPAN_NOTICE("\The [user] makes a strange gesture."))
var/obj/O = target
O.attack_hand(user)
return TRUE
else if(istype(target, /obj/machinery))
for(var/mtype in valid_machine_types)
if(istype(target, mtype))
var/obj/machinery/machine = target
machine.attack_hand(user)
return TRUE
return FALSE
@@ -0,0 +1,186 @@
/datum/psionic_faculty/redaction
id = PSI_REDACTION
name = "Redaction"
associated_intent = I_HELP
armor_types = list("bio", "rad")
/datum/psionic_power/redaction
faculty = PSI_REDACTION
admin_log = FALSE
/datum/psionic_power/redaction/proc/check_dead(var/mob/living/target)
if(!istype(target))
return FALSE
if(target.stat == DEAD || (target.status_flags & FAKEDEATH))
return TRUE
return FALSE
/datum/psionic_power/redaction/invoke(var/mob/living/user, var/mob/living/target)
if(check_dead(target))
return FALSE
. = ..()
/datum/psionic_power/redaction/skinsight
name = "Skinsight"
cost = 3
cooldown = 30
use_grab = TRUE
min_rank = PSI_RANK_OPERANT
use_description = "Grab a patient, target the chest, then switch to help intent and use the grab on them to perform a check for wounds and damage."
/datum/psionic_power/redaction/skinsight/invoke(var/mob/living/user, var/mob/living/target)
if(user.zone_sel.selecting != BP_CHEST)
return FALSE
. = ..()
if(.)
user.visible_message(SPAN_NOTICE("\The [user] rests a hand on \the [target]."))
health_scan_mob(target, user, TRUE)
return TRUE
/datum/psionic_power/redaction/mend
name = "Mend"
cost = 7
cooldown = 50
use_melee = TRUE
min_rank = PSI_RANK_OPERANT
use_description = "Target a patient while on help intent at melee range to mend a variety of maladies, such as bleeding, or broken bones. Zone selection will affect which limb will be healed. Higher ranks in this faculty allow you to mend a wider range of problems."
/datum/psionic_power/redaction/mend/invoke(var/mob/living/user, var/mob/living/carbon/human/target)
if(!istype(user) || !istype(target))
return FALSE
. = ..()
if(.)
var/obj/item/organ/external/E = target.get_organ(user.zone_sel.selecting)
if(!E || E.is_stump())
to_chat(user, SPAN_WARNING("They are missing that limb."))
return TRUE
if(BP_IS_ROBOTIC(E))
to_chat(user, SPAN_WARNING("That limb is prosthetic."))
return TRUE
user.visible_message(SPAN_NOTICE("<i>\The [user] rests a hand on \the [target]'s [E.name]...</i>"))
to_chat(target, SPAN_NOTICE("A healing warmth suffuses you."))
var/redaction_rank = user.psi.get_rank(PSI_REDACTION)
var/pk_rank = user.psi.get_rank(PSI_PSYCHOKINESIS)
if(pk_rank >= PSI_RANK_LATENT && redaction_rank >= PSI_RANK_MASTER)
var/removal_size = Clamp(5-pk_rank, 0, 5)
var/valid_objects = list()
for(var/thing in E.implants)
var/obj/imp = thing
if(imp.w_class >= removal_size && !istype(imp, /obj/item/implant))
valid_objects += imp
if(LAZYLEN(valid_objects))
var/removing = pick(valid_objects)
target.remove_implant(removing, TRUE)
to_chat(user, SPAN_NOTICE("You extend a tendril of psychokinetic-redactive power and carefully tease \the [removing] free of \the [E]."))
return TRUE
if(redaction_rank >= PSI_RANK_MASTER)
if(E.status & ORGAN_ARTERY_CUT)
to_chat(user, SPAN_NOTICE("You painstakingly mend the torn veins in \the [E], stemming the internal bleeding."))
E.status &= ~ORGAN_ARTERY_CUT
return TRUE
if((E.tendon_status() & TENDON_CUT) && E.tendon.can_recover())
to_chat(user, SPAN_NOTICE("You interleave and repair the severed tendon in \the [E]."))
E.tendon.rejuvenate()
return TRUE
if(E.status & ORGAN_BROKEN)
to_chat(user, SPAN_NOTICE("You coax shattered bones to come together and fuse, mending the break."))
E.status &= ~ORGAN_BROKEN
E.stage = 0
return TRUE
for(var/datum/wound/W in E.wounds)
if(W.bleeding())
if(redaction_rank >= PSI_RANK_MASTER || W.wound_damage() < 30)
to_chat(user, SPAN_NOTICE("You knit together severed veins and broken flesh, stemming the bleeding."))
W.bleed_timer = 0
W.clamped = TRUE
E.status &= ~ORGAN_BLEEDING
return TRUE
else
to_chat(user, SPAN_WARNING("This [W.desc] is beyond your power to heal."))
if(redaction_rank >= PSI_RANK_GRANDMASTER)
for(var/obj/item/organ/internal/I in E.internal_organs)
if(!BP_IS_ROBOTIC(I) && I.damage > 0)
to_chat(user, SPAN_NOTICE("You encourage the damaged tissue of \the [I] to repair itself."))
var/heal_rate = redaction_rank
I.damage = max(0, I.damage - rand(heal_rate,heal_rate*2))
return TRUE
to_chat(user, SPAN_NOTICE("You can find nothing within \the [target]'s [E.name] to mend."))
return FALSE
/datum/psionic_power/redaction/cleanse
name = "Cleanse"
cost = 9
cooldown = 60
use_melee = TRUE
min_rank = PSI_RANK_GRANDMASTER
use_description = "Target a patient while on help intent at melee range to cleanse radiation and genetic damage from a patient."
/datum/psionic_power/redaction/cleanse/invoke(var/mob/living/user, var/mob/living/carbon/human/target)
if(!istype(user) || !istype(target))
return FALSE
. = ..()
if(.)
// No messages, as Mend procs them even if it fails to heal anything, and Cleanse is always checked after Mend.
var/removing = rand(20,25)
if(target.total_radiation)
to_chat(user, SPAN_NOTICE("You repair some of the radiation-damaged tissue within \the [target]..."))
if(target.total_radiation > removing)
target.total_radiation -= removing
else
target.total_radiation = 0
return TRUE
if(target.getCloneLoss())
to_chat(user, SPAN_NOTICE("You stitch together some of the mangled DNA within \the [target]..."))
if(target.getCloneLoss() >= removing)
target.adjustCloneLoss(-removing)
else
target.adjustCloneLoss(-(target.getCloneLoss()))
return TRUE
to_chat(user, SPAN_NOTICE("You can find no genetic damage or radiation to heal within \the [target]."))
return TRUE
/datum/psionic_power/revive
name = "Revive"
cost = 25
cooldown = 80
use_grab = TRUE
min_rank = PSI_RANK_PARAMOUNT
faculty = PSI_REDACTION
use_description = "Obtain a grab on a dead target, target the head, then select help intent and use the grab against them to attempt to bring them back to life. The process is lengthy and failure is punished harshly."
admin_log = FALSE
/datum/psionic_power/revive/invoke(var/mob/living/user, var/mob/living/target)
if(!isliving(target) || !istype(target) || user.zone_sel.selecting != BP_HEAD)
return FALSE
. = ..()
if(.)
if(target.stat != DEAD && !(target.status_flags & FAKEDEATH))
to_chat(user, SPAN_WARNING("This person is already alive!"))
return TRUE
if((world.time - target.timeofdeath) > 6000)
to_chat(user, SPAN_WARNING("\The [target] has been dead for too long to revive."))
return TRUE
user.visible_message(SPAN_NOTICE("<i>\The [user] splays out their hands over \the [target]'s body...</i>"))
if(!do_after(user, 100, target, 0, 1))
user.psi.backblast(rand(10,25))
return TRUE
for(var/mob/abstract/observer/G in dead_mob_list)
if(G.mind && G.mind.current == target && G.client)
to_chat(G, FONT_LARGE(SPAN_NOTICE("<b>Your body has been revived, <b>Re-Enter Corpse</b> to return to it.</b>")))
break
to_chat(target, FONT_LARGE(SPAN_NOTICE("<b>Life floods back into your body!</b>")))
target.visible_message(SPAN_NOTICE("\The [target] shudders violently!"))
target.adjustOxyLoss(-rand(15,20))
target.basic_revival()
return TRUE
+19 -61
View File
@@ -1,6 +1,6 @@
/obj/screen/psi/hub
name = "Psi"
icon_state = "psi_active"
icon_state = "psi_suppressed"
screen_loc = "EAST-1:28,CENTER-3:11"
hidden = FALSE
maptext_x = 6
@@ -13,6 +13,7 @@
START_PROCESSING(SSprocessing, src)
/obj/screen/psi/hub/update_icon()
if(!owner.psi)
return
@@ -29,68 +30,25 @@
return
if(!owner.psi)
return
maptext = SMALL_FONTS(7, "[round((owner.psi.stamina/owner.psi.max_stamina)*100)]%")
maptext = "[round((owner.psi.stamina/owner.psi.max_stamina)*100)]%"
update_icon()
/obj/screen/psi/hub/Click(var/location, var/control, var/params)
ui_interact(owner)
update_icon()
var/list/click_params = params2list(params)
if(click_params["shift"])
owner.show_psi_assay(owner)
return
/obj/screen/psi/hub/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
ui = new(user, src, "PsionicShop", "Psionic Point Shop", 400, 500)
ui.open()
if(owner.psi.suppressed && owner.psi.stun)
to_chat(owner, "<span class='warning'>You are dazed and reeling, and cannot muster enough focus to do that!</span>")
return
/obj/screen/psi/hub/ui_state(mob/user)
return conscious_state
/obj/screen/psi/hub/ui_status(mob/user, datum/ui_state/state)
return UI_INTERACTIVE
/obj/screen/psi/hub/ui_data(mob/user)
var/list/data = list()
var/owner_rank = owner.psi.get_rank()
data["available_psionics"] = list()
data["psi_rank"] = psychic_ranks_to_strings[owner_rank]
data["psi_points"] = owner.psi.psi_points
data["bought_powers"] = owner.psi.psionic_powers
for(var/singleton/psionic_power/P in GET_SINGLETON_SUBTYPE_LIST(/singleton/psionic_power))
if(HAS_FLAG(P.ability_flags, PSI_FLAG_SPECIAL) && !(P.type in owner.psi.psionic_powers))
continue
if(owner_rank < P.minimum_rank)
continue
/// Apex and Limitless abilities are automatically given, but we want them to have said abilities in the point shop so the users know what they do.
if(owner_rank < PSI_RANK_APEX && HAS_FLAG(P.ability_flags, PSI_FLAG_APEX))
continue
if(owner_rank < PSI_RANK_LIMITLESS && HAS_FLAG(P.ability_flags, PSI_FLAG_LIMITLESS))
continue
if(NOT_FLAG(P.ability_flags, PSI_FLAG_CANON))
if(owner_rank < PSI_RANK_HARMONIOUS && HAS_FLAG(P.ability_flags, PSI_FLAG_EVENT))
continue
if(owner_rank < PSI_RANK_HARMONIOUS && HAS_FLAG(P.ability_flags, PSI_FLAG_ANTAG))
continue
data["available_psionics"] += list(
list(
"name" = P.name,
"desc" = P.desc,
"point_cost" = P.point_cost,
"minimum_rank" = P.minimum_rank,
"path" = P.type
)
)
return data
/obj/screen/psi/hub/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return TRUE
switch(action)
if("buy")
var/psionic_path = text2path(params["buy"])
if(ispath(psionic_path))
var/singleton/psionic_power/P = GET_SINGLETON(psionic_path)
P.apply(owner)
to_chat(owner, SPAN_NOTICE("You are now capable of using [P.name]."))
return TRUE
owner.psi.suppressed = !owner.psi.suppressed
to_chat(owner, "<span class='notice'>You are <b>[owner.psi.suppressed ? "now suppressing" : "no longer suppressing"]</b> your psi-power.</span>")
if(owner.psi.suppressed)
owner.psi.cancel()
owner.psi.hide_auras()
else
sound_to(owner, sound('sound/effects/psi/power_unlock.ogg'))
owner.psi.show_auras()
update_icon()
-13
View File
@@ -1,13 +0,0 @@
/datum/component/armor/psionic
full_block_message = "You block the blow with your mind!"
partial_block_message = "You soften the blow with your mind!"
/datum/component/armor/psionic/Initialize(list/armor, armor_type)
. = ..()
var/datum/psi_complexus/PC = parent
PC.armor_component = src
/datum/component/armor/psionic/Destroy()
var/datum/psi_complexus/PC = parent
PC.armor_component = null
return ..()
+6 -7
View File
@@ -5,17 +5,16 @@
..()
if(psi)
psi.update(TRUE)
if(!psi.suppressed)
psi.show_auras()
/mob/living/Destroy()
QDEL_NULL(psi)
. = ..()
/mob/living/proc/set_psi_rank(var/rank, var/defer_update, var/temporary)
if(HAS_TRAIT(src, TRAIT_PSIONICALLY_DEAF))
to_chat(src, SPAN_WARNING("Something tingles in your head."))
return
/mob/living/proc/set_psi_rank(var/faculty, var/rank, var/take_larger, var/defer_update, var/temporary)
if(!psi)
psi = new(src)
var/current_rank = psi.get_rank()
if(current_rank != rank && current_rank < rank)
psi.set_rank(rank, defer_update, temporary)
var/current_rank = psi.get_rank(faculty)
if(current_rank != rank && (!take_larger || current_rank < rank))
psi.set_rank(faculty, rank, defer_update, temporary)
+79
View File
@@ -0,0 +1,79 @@
/mob/living/proc/show_psi_assay(var/mob/viewer)
if(!viewer) viewer = usr
var/use_He_is = "You are"
var/use_He_has = "You have"
if(istype(machine) || viewer != src)
use_He_is = "[get_pronoun("He")] [get_pronoun("is")]"
use_He_has = "[get_pronoun("He")] [get_pronoun("has")]"
var/list/dat = list()
dat += "<h2>Summary</h2>"
dat += "<hr>"
if(psi)
// Hi Warhammer 40k rating system, how are you?
// I hope you get along with the Galactic Milieu metapsychics.
var/use_rating
var/effective_rating = psi.rating
if(effective_rating > 1 && psi.suppressed)
effective_rating = max(0, psi.rating-2)
var/rating_descriptor
if(viewer != usr && thralls.is_antagonist(mind) && ishuman(viewer))
var/mob/living/H = viewer
if(H.psi && H.psi.get_rank(PSI_REDACTION) >= PSI_RANK_GRANDMASTER)
dat += "<font color='#FF0000'><b>Their mind has been cored like an apple, and enslaved by another operant psychic.</b></font>"
if(!use_rating)
switch(effective_rating)
if(1)
use_rating = "[effective_rating]-Epsilon"
rating_descriptor = "This indicates the presence of minor latent psi potential with little or no operant capabilities."
if(2)
use_rating = "[effective_rating]-Delta"
rating_descriptor = "This indicates the presence of minor psi capabilities of the Operant rank or higher."
if(3)
use_rating = "<font color = '#F4F441'>[effective_rating]-Gamma</font>"
rating_descriptor = "This indicates the presence of psi capabilities of the Master rank or higher."
if(4)
use_rating = "<font color = '#F4BC42'>[effective_rating]-Beta</font>"
rating_descriptor = "This indicates the presence of significant psi capabilities of the Grandmaster rank or higher."
if(5)
use_rating = "<font color = '#FF0000'>[effective_rating]-Alpha</font>"
rating_descriptor = "This indicates the presence of major psi capabilities of the Paramount Grandmaster rank or higher."
else
use_rating = "[effective_rating]-Lambda"
rating_descriptor = "This indicates the presence of trace latent psi capabilities."
dat += "[use_He_has] an overall psi rating of [use_rating].<br><i>[rating_descriptor]</i><hr>"
dat += "[use_He_is] currently <b>[psi.suppressed ? "suppressing" : "not suppressing"]</b> your psychic operancy.<br>"
dat += "[use_He_has] <b>[psi.stamina]/[psi.max_stamina]</b> psi stamina remaining.<br>"
dat += "<hr>"
for(var/faculty_id in psi.ranks)
var/datum/psionic_faculty/faculty = SSpsi.get_faculty(faculty_id)
if(psi.ranks[faculty.id] > 0)
dat += "[use_He_is] assayed at the rank of <b>[psychic_ranks_to_strings[psi.ranks[faculty.id]]]</b> for the <b>[faculty.name] faculty</b>.<br>"
else
dat += "[use_He_has] no notable power within the <b>[faculty.name] faculty</b>.<br>"
dat += "<hr>"
if(viewer == usr)
dat += "<table width = 100% border = 1><tr><td colspan = 2><h2>Psi-power Usage</h2></td></tr>"
for(var/faculty_id in psi.ranks)
var/list/check_powers = psi.get_powers_by_faculty(faculty_id)
if(LAZYLEN(check_powers))
var/datum/psionic_faculty/faculty = SSpsi.get_faculty(faculty_id)
dat += "<tr><td colspan = 2>[use_He_has] access to the following psi-powers within the <b>[faculty.name] faculty</b>:</td></tr>"
for(var/datum/psionic_power/power in check_powers)
dat += "<tr><td><b>[power.name]</b></td><td>[power.use_description]</td></tr>"
dat += "</table>"
var/datum/browser/popup = new(viewer, "psi_assay_\ref[src]", "Psi-Assay")
popup.set_content(jointext(dat,null))
popup.open()
+8 -11
View File
@@ -6,24 +6,21 @@
return psiaug && !psiaug.is_broken()
/mob/living/proc/is_psi_blocked()
return !has_psionics()
return !can_commune()
/mob/living/carbon/is_psi_blocked()
if(HAS_TRAIT(src, TRAIT_PSIONICALLY_DEAF))
return SPAN_WARNING("[src]'s mind is inaccessible, like hitting a brick wall.")
if(!psi && !has_psi_aug())
if(isSynthetic())
return SPAN_ALIEN("Reaching out, your mind grasps at nothing.")
if (isvaurca(src))
return SPAN_CULT("You reach out into the Nlom; your call sails right through and yields no response.")
if (is_diona())
return SPAN_ALIEN("[src]'s mind is incompatible, formless.")
for (var/obj/item/implant/mindshield/I in src)
if (I.implanted)
return SPAN_WARNING("[src]'s mind is inaccessible, like hitting a brick wall.")
return FALSE
/mob/living/proc/has_zona_bovinae()
return TRUE
/mob/living/carbon/has_zona_bovinae()
if(HAS_TRAIT(src, TRAIT_PSIONICALLY_DEAF))
return FALSE
return TRUE
/mob/living/proc/is_psi_pingable()
return !is_psi_blocked()
@@ -0,0 +1,37 @@
#define INVOKE_PSI_POWERS(holder, powers, target, return_on_invocation) \
if(holder && holder.psi && holder.psi.can_use()) { \
for(var/thing in powers) { \
var/datum/psionic_power/power = thing; \
var/obj/item/result = power.invoke(holder, target); \
if(result) { \
power.handle_post_power(holder, target); \
if(istype(result)) { \
sound_to(holder, sound('sound/effects/psi/power_evoke.ogg')); \
LAZYADD(holder.psi.manifested_items, result); \
holder.put_in_hands(result); \
} \
return return_on_invocation; \
} \
} \
}
/mob/living/UnarmedAttack(var/atom/A, var/proximity)
. = ..()
if(. && psi)
INVOKE_PSI_POWERS(src, psi.get_melee_powers(SSpsi.faculties_by_intent[a_intent]), A, FALSE)
/mob/living/RangedAttack(var/atom/A, var/params)
if(psi)
INVOKE_PSI_POWERS(src, psi.get_ranged_powers(SSpsi.faculties_by_intent[a_intent]), A, TRUE)
. = ..()
/mob/living/proc/check_psi_grab(var/obj/item/grab/grab)
if(psi && ismob(grab.affecting))
INVOKE_PSI_POWERS(src, psi.get_grab_powers(SSpsi.faculties_by_intent[a_intent]), grab.affecting, FALSE)
/mob/living/attack_empty_hand(var/bp_hand)
if(psi)
INVOKE_PSI_POWERS(src, psi.get_manifestations(), src, FALSE)
. = ..()
#undef INVOKE_PSI_POWERS