mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-27 23:14:18 +01:00
This reverts commit 27c40bdaf6.
This commit is contained in:
@@ -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
|
||||
@@ -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))
|
||||
@@ -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)
|
||||
@@ -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>."))
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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."))
|
||||
@@ -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]%."))
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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 ..()
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user