Psionics Rework: Kyoushiki Murasaki (#16951)

* Your unbeatable smile wrecks the media

 We wanna know your secret, so mysterious

* Even the things she lacks end up in her area

Such a perfect liar, you are

* Miss ingenious, godly idol!

(You're my savior, you're my saving grace)

* So, what'd you eat today?

What's your favorite book?

* And if you wanna go to play, where are you gonna go?

I haven't eaten at all

* That's a secret

No matter what you ask

* She's elusive, evasive

* So nonchalant

Yet still shining bright;

* Her secret: out of sight, as if visible, has the taste of honey

There is none, none, none of this

* And also none, none, none of that

What type of guy do you like?

* Who is your partner?

Come on, answer me

* "I don't understand what falling in love with someone else is like" (Hey! Hey!)

I cannot tell what is true from what is a lie (Hey! Hey! Hey! Hey!)

* And yet again, someone falls for such words (Hey! Hey! Hey! Hey!)

Gently lulled into love

* Drawing everyone's eyes

You are the perfect, ultimate idol

* Never to appear again

A reincarnation of Venus

* With that smile, with that "I love you"

You captivate everyone

* Even if those eyes, those words, are false

They make up a perfect love (Ai)

* Yeah, yeah, that girl stands out, exceptional

We were extras from the beginning

* BS that acted as adornments for the star's leading role

Everything we've accomplished is thanks to her — no!

* How impudent

How could envy and jealousy not exist?

* examine

* This isn't some made-up drama for appearances

So I cannot forgive

* You're not perfect, I cannot forgive you

I cannot forgive myself

* I cannot accept any version of you other than the one stronger than everyone else

* Everybody trusts and worships

The very strongest, most invincible idol

* Weaknesses and whatnot are not to be found

Carrying the morning star within

* Showing any weakness is no good, no good (Hey! Hey!)

Don't let them see anything that they don't want to know (Hey! Hey! Hey! Hey!)

* Anything but being one-of-a-kind is a definite no, no (Hey! Hey! Hey! Hey!)

That is the real love (Hey!)

* can commune fixes

* can commune rename

* fix is psi blocked

* fixes agian

* My signature smile that fires up the media

The only secret that I shall keep is this:

* My career was built upon fake "I love you"s

This is love, in my own way

* Even the flowing sweats are beautiful Aqua

These eyelids that hid the Ruby within

* Sing, dance, flutter, I am Maria —

Right, lying is the most exquisite form of love

* I have never been loved by anyone

Nor have I loved anyone else

* That such lies of mine will become true someday

I trust in it

* Someday, surely, I will have it all in hand

I am such a greedy idol

* Because I wish to love everybody properly in a life-sized way

I shall lie today as well (Hey! Hey! Hey! Hey!)

* a

* b

* c

* d

* And yet, still, (Hey! Hey!)

I have been unable to tell you, and you, alone (Hey! Hey! Hey! Hey!)

* jesus christ

* Update code/controllers/subsystems/processing/psi.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/controllers/subsystems/processing/psi.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/controllers/subsystems/processing/psi.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/game/gamemodes/technomancer/spell_objs.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/mob/living/carbon/human/human.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/mob/living/carbon/human/human_helpers.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/organs/organ_external.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/assay.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/command.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/emotional_suggestion.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/grip.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/grip.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/mend.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/mend.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/mend.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/psi_search.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/shockwave.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/shockwave.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/singularity.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/abilities/time_stop.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/events/mini_spasm.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/interface/ui_hub.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/interface/ui_hub.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/interface/ui_hub.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/interface/ui_hub.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/interface/ui_hub.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* Update code/modules/psionics/interface/ui_hub.dm

Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>

* cl update

---------

Co-authored-by: Matt Atlas <liermattia@gmail.com>
Co-authored-by: Fluffy <65877598+FluffyGhoster@users.noreply.github.com>
This commit is contained in:
Matt Atlas
2023-07-31 20:56:52 +02:00
committed by GitHub
co-authored by Fluffy Matt Atlas
parent a894c77990
commit d9b79db97b
146 changed files with 3213 additions and 1617 deletions
+5
View File
@@ -0,0 +1,5 @@
--
-- Adds psionic prefs
--
ALTER TABLE `ss13_characters` ADD COLUMN `psionics` LONGTEXT DEFAULT null AFTER `origin`;
+45 -10
View File
@@ -1586,6 +1586,7 @@
#include "code\modules\client\preference_setup\general\04_equipment.dm"
#include "code\modules\client\preference_setup\general\05_background.dm"
#include "code\modules\client\preference_setup\general\06_flavor.dm"
#include "code\modules\client\preference_setup\general\07_psionics.dm"
#include "code\modules\client\preference_setup\global\01_ui.dm"
#include "code\modules\client\preference_setup\global\02_settings.dm"
#include "code\modules\client\preference_setup\global\03_language.dm"
@@ -2891,32 +2892,66 @@
#include "code\modules\projectiles\targeting\targeting_mob.dm"
#include "code\modules\projectiles\targeting\targeting_overlay.dm"
#include "code\modules\projectiles\targeting\targeting_triggers.dm"
#include "code\modules\psionics\abilities\_ability.dm"
#include "code\modules\psionics\abilities\air_bullet.dm"
#include "code\modules\psionics\abilities\assay.dm"
#include "code\modules\psionics\abilities\barrier.dm"
#include "code\modules\psionics\abilities\bubble.dm"
#include "code\modules\psionics\abilities\charge.dm"
#include "code\modules\psionics\abilities\command.dm"
#include "code\modules\psionics\abilities\commune.dm"
#include "code\modules\psionics\abilities\electrocute.dm"
#include "code\modules\psionics\abilities\emotional_suggestion.dm"
#include "code\modules\psionics\abilities\focus.dm"
#include "code\modules\psionics\abilities\grip.dm"
#include "code\modules\psionics\abilities\hollow_purple.dm"
#include "code\modules\psionics\abilities\ion.dm"
#include "code\modules\psionics\abilities\jump.dm"
#include "code\modules\psionics\abilities\lightning.dm"
#include "code\modules\psionics\abilities\mend.dm"
#include "code\modules\psionics\abilities\mind_muddle.dm"
#include "code\modules\psionics\abilities\mirror_shade.dm"
#include "code\modules\psionics\abilities\nlom_eyes.dm"
#include "code\modules\psionics\abilities\psi_awakening.dm"
#include "code\modules\psionics\abilities\psi_drain.dm"
#include "code\modules\psionics\abilities\psi_punch.dm"
#include "code\modules\psionics\abilities\psi_recovery.dm"
#include "code\modules\psionics\abilities\psi_search.dm"
#include "code\modules\psionics\abilities\psi_stamina.dm"
#include "code\modules\psionics\abilities\psi_sunder.dm"
#include "code\modules\psionics\abilities\psi_suppression.dm"
#include "code\modules\psionics\abilities\psi_sword.dm"
#include "code\modules\psionics\abilities\psi_tool.dm"
#include "code\modules\psionics\abilities\pull.dm"
#include "code\modules\psionics\abilities\read_mind.dm"
#include "code\modules\psionics\abilities\rejuvenate.dm"
#include "code\modules\psionics\abilities\rend.dm"
#include "code\modules\psionics\abilities\shockwave.dm"
#include "code\modules\psionics\abilities\singularity.dm"
#include "code\modules\psionics\abilities\skinsight.dm"
#include "code\modules\psionics\abilities\spasm.dm"
#include "code\modules\psionics\abilities\stasis.dm"
#include "code\modules\psionics\abilities\throw.dm"
#include "code\modules\psionics\abilities\time_stop.dm"
#include "code\modules\psionics\abilities\warp.dm"
#include "code\modules\psionics\abilities\zona_absorption.dm"
#include "code\modules\psionics\complexus\complexus.dm"
#include "code\modules\psionics\complexus\complexus_helpers.dm"
#include "code\modules\psionics\complexus\complexus_latency.dm"
#include "code\modules\psionics\complexus\complexus_power_cache.dm"
#include "code\modules\psionics\complexus\complexus_process.dm"
#include "code\modules\psionics\equipment\cerebro_enhancers.dm"
#include "code\modules\psionics\equipment\psipower.dm"
#include "code\modules\psionics\equipment\psipower_blade.dm"
#include "code\modules\psionics\equipment\psipower_tinker.dm"
#include "code\modules\psionics\equipment\psipower_tk.dm"
#include "code\modules\psionics\events\_psi.dm"
#include "code\modules\psionics\events\mini_spasm.dm"
#include "code\modules\psionics\events\psi_balm.dm"
#include "code\modules\psionics\events\psi_wail.dm"
#include "code\modules\psionics\faculties\_faculty.dm"
#include "code\modules\psionics\faculties\_power.dm"
#include "code\modules\psionics\faculties\coercion.dm"
#include "code\modules\psionics\faculties\energistics.dm"
#include "code\modules\psionics\faculties\psychokinesis.dm"
#include "code\modules\psionics\faculties\redaction.dm"
#include "code\modules\psionics\interface\ui.dm"
#include "code\modules\psionics\interface\ui_hub.dm"
#include "code\modules\psionics\mob\armour.dm"
#include "code\modules\psionics\mob\mob.dm"
#include "code\modules\psionics\mob\mob_assay.dm"
#include "code\modules\psionics\mob\mob_helpers.dm"
#include "code\modules\psionics\mob\mob_interactions.dm"
#include "code\modules\radiation\radiation.dm"
#include "code\modules\random_map\_random_map_setup.dm"
#include "code\modules\random_map\random_map.dm"
+5 -5
View File
@@ -30,11 +30,11 @@
#define FONT_SIZE_HUGE 4
#define FONT_SIZE_GIANT 5
#define FONT_SMALL(X) ("<font size='1'>" + X + "</font>")
#define FONT_NORMAL(X) ("<font size='2'>" + X + "</font>")
#define FONT_LARGE(X) ("<font size='3'>" + X + "</font>")
#define FONT_HUGE(X) ("<font size='4'>" + X + "</font>")
#define FONT_GIANT(X) ("<font size='5'>" + X + "</font>")
#define FONT_SMALL(X) ("<font size=1>" + X + "</font>")
#define FONT_NORMAL(X) ("<font size=2>" + X + "</font>")
#define FONT_LARGE(X) ("<font size=3>" + X + "</font>")
#define FONT_HUGE(X) ("<font size=4>" + X + "</font>")
#define FONT_GIANT(X) ("<font size=5>" + X + "</font>")
#define MATRIX_DANGER(X) (FONT_LARGE(SPAN_DANGER(X)))
#define MATRIX_NOTICE(X) (FONT_LARGE(SPAN_NOTICE(X)))
+1
View File
@@ -20,6 +20,7 @@
#define DAMAGE_FLAG_EXPLODE 16
#define DAMAGE_FLAG_DISPERSED 32 // Makes apply_damage calls without specified zone distribute damage rather than randomly choose organ (for humans)
#define DAMAGE_FLAG_BIO 64
#define DAMAGE_FLAG_PSIONIC 128
#define STUN "stun"
#define WEAKEN "weaken"
+16 -10
View File
@@ -1,11 +1,17 @@
#define PSI_COERCION "coercion"
#define PSI_PSYCHOKINESIS "psychokinesis"
#define PSI_REDACTION "redaction"
#define PSI_ENERGISTICS "energistics"
#define PSI_RANK_SENSITIVE 1 /// Psionically sensitive. Skrell.
#define PSI_RANK_HARMONIOUS 2 /// Psionically harmonious. Event Skrell, antagonists.
#define PSI_RANK_APEX 3 /// Psionic apex. Event people.
#define PSI_RANK_LIMITLESS 4 /// Admin-only. Yes, it's a Gojou Satoru reference.
#define PSI_RANK_BLUNT 0
#define PSI_RANK_LATENT 1
#define PSI_RANK_OPERANT 2
#define PSI_RANK_MASTER 3
#define PSI_RANK_GRANDMASTER 4
#define PSI_RANK_PARAMOUNT 5
#define PSI_FLAG_CANON 1 /// An ability that can be selected by a ship character.
#define PSI_FLAG_EVENT 2 /// An ability for event characters, not selectable by ship characters, so technically canon.
#define PSI_FLAG_ANTAG 4 /// An ability for antag characters, not selectable by ship characters, not really canon.
#define PSI_FLAG_FOUNDATIONAL 8 /// An ability automatically given to Psionically Sensitive users.
#define PSI_FLAG_APEX 16 /// An ability automatically given to Psionic Apex users.
#define PSI_FLAG_LIMITLESS 32 /// An ability automatically given to Limitless users.
#define PSI_FLAG_SPECIAL 64 /// Unselectable in the point shop - must be handed out manually.
/// Psi shop points given by a certain rank.
#define PSI_POINTS_HARMONIOUS 10
#define PSI_POINTS_APEX 14
#define PSI_POINTS_LIMITLESS 40
+21
View File
@@ -139,6 +139,24 @@
/// forces the mob to speak gibberish, like highly damaged borgs
#define TRAIT_SPEAKING_GIBBERISH "speaking_gibberish"
/// Mob does not need to breathe.
#define TRAIT_NO_BREATHE "no_breathe"
/// Mob has pressure immunity.
#define TRAIT_PRESSURE_IMMUNITY "pressure_immunity"
/// Trait sourced from changeling abilities.
#define TRAIT_SOURCE_CHANGELING "changeling"
/// Mob is psionically deaf.
#define TRAIT_PSIONICALLY_DEAF "psionically_deaf"
/// Zona bovinae absorbed, used by Loner to track a mob that has had its ZB consumed. Doesn't make them psi-deaf.
#define TRAIT_ZONA_BOVINAE_ABSORBED "bovinae_absorbed"
/// Hidden from Psi-Search.
#define TRAIT_PSIONIC_SUPPRESSION "psionic_suppression"
/// lets mobs that traditionally don't hallucinate, hallucinate
#define TRAIT_BYPASS_HALLUCINATION_RESTRICTION "bypassing_hallucination_restriction"
@@ -153,3 +171,6 @@
#define TRAIT_OVERLOADER_OD_INITIAL "overloader_od_initial"
#define TRAIT_OVERLOADER_OD_MEDIUM "overloader_od_medium"
#define TRAIT_OVERLOADER_OD_EFFECT "overloader_od_effect"
/// Traits given by psionics.
#define TRAIT_SOURCE_PSIONICS "psionics"
+44 -4
View File
@@ -126,6 +126,10 @@
for(var/obj/screen/ability/A in ability_objects)
remove_ability(A)
/obj/screen/movable/ability_master/proc/remove_all_psionic_abilities()
for(var/obj/screen/ability/obj_based/psionic/A in ability_objects)
remove_ability(A)
/obj/screen/movable/ability_master/proc/get_ability_by_name(name_to_search)
for(var/obj/screen/ability/A in ability_objects)
if(A.name == name_to_search)
@@ -147,7 +151,7 @@
/mob/living/LateLogin()
. = ..()
if(ability_master)
ability_master.toggle_open(1)
ability_master.toggle_open(2)
client.screen -= ability_master
/mob/living/Initialize()
@@ -183,10 +187,15 @@
overlays += ability_icon_state
/obj/screen/ability/Click()
/obj/screen/ability/Click(var/location, var/control, var/params)
if(!usr)
return
var/list/click_params = params2list(params)
if(click_params["shift"])
examine(usr)
return
activate()
/obj/screen/ability/MouseDrop(var/atom/A)
@@ -283,13 +292,44 @@
//////////////////////////////
/obj/screen/ability/obj_based
var/obj/object = null
var/obj/object
/obj/screen/ability/obj_based/activate()
if(object)
object.Click()
// Technomancer
/// Psionics.
/obj/screen/ability/obj_based/psionic
icon_state = "nano_spell_base"
background_base_state = "nano"
var/singleton/psionic_power/connected_power
/obj/screen/ability/obj_based/psionic/Destroy()
connected_power = null
return ..()
/obj/screen/movable/ability_master/proc/add_psionic_ability(var/obj/object_given, var/ability_icon_given, var/singleton/psionic_power/P)
if(!object_given)
message_admins("ERROR: add_psionic_ability() was not given an object in its arguments.")
if(!P)
message_admins("Psionic ability added without connected psionic power singleton!")
if(get_ability_by_instance(object_given))
return // Duplicate
var/obj/screen/ability/obj_based/psionic/A = new /obj/screen/ability/obj_based/psionic()
A.ability_master = src
A.object = object_given
A.ability_icon_state = ability_icon_given
A.name = object_given.name
A.connected_power = P
ability_objects.Add(A)
if(my_mob.client)
toggle_open(2) //forces the icons to refresh on screen
/obj/screen/ability/obj_based/psionic/examine(mob/user)
to_chat(user, SPAN_NOTICE("<font size=4>This ability is <b>[connected_power.name]</b>.</font>"))
to_chat(user, SPAN_NOTICE("[connected_power.desc]"))
/// Technomancer.
/obj/screen/ability/obj_based/technomancer
icon_state = "wiz_spell_base"
background_base_state = "wiz"
+88 -28
View File
@@ -1,40 +1,100 @@
var/global/list/psychic_ranks_to_strings = list("Latent", "Operant", "Masterclass", "Grandmasterclass", "Paramount")
var/global/list/psychic_ranks_to_strings = list("Psionically Sensitive", "Psionically Harmonious", "Psionic Apex", "Limitless")
/var/datum/controller/subsystem/processing/psi/SSpsi
/datum/controller/subsystem/processing/psi
name = "Psychics"
name = "Psionics"
priority = SS_PRIORITY_PSYCHICS
flags = SS_POST_FIRE_TIMING | SS_BACKGROUND
flags = SS_BACKGROUND
var/list/faculties_by_id = list()
var/list/faculties_by_name = list()
var/list/all_aura_images = list()
var/list/all_psi_complexes = list()
var/list/psi_dampeners = list()
var/list/psi_monitors = list()
var/list/armor_faculty_by_type = list()
var/list/faculties_by_intent = list()
var/checking_nlom = FALSE
var/last_nlom_awareness_check = 0
var/completing_nlom = FALSE
var/complete_nlom_time = 0
var/list/nlom_votes = list()
var/list/nlom_voters = list()
var/list/all_aura_images = list()
var/list/all_psi_complexes = list()
/datum/controller/subsystem/processing/psi/New()
NEW_SS_GLOBAL(SSpsi)
/datum/controller/subsystem/processing/psi/proc/get_faculty(var/faculty)
return faculties_by_name[faculty] || faculties_by_id[faculty]
/datum/controller/subsystem/processing/psi/fire(resumed)
..()
if((world.time >= (last_nlom_awareness_check + 30 MINUTES)) && !checking_nlom && !completing_nlom)
checking_nlom = TRUE
nlom_votes = list(
"Calm" = 0,
"Happy" = 0,
"Sad" = 0,
"Fearful" = 0,
"Angry" = 0,
"Stressed" = 0,
"Confused" = 0
)
for(var/datum/psi_complexus/PC in all_psi_complexes)
if(PC.psionic_rank >= PSI_RANK_SENSITIVE)
to_chat(PC.owner, SPAN_NOTICE(FONT_HUGE("The Nlom field prompts you for your emotional state.")))
to_chat(PC.owner,
"<a href='?src=\ref[src];emotion=Calm;voter=[ref(PC)]'>Calm</a> | <a href='?src=\ref[src];emotion=Happy;voter=[ref(PC)]'>Happy</a> | \
<a href='?src=\ref[src];emotion=Sad;voter=[ref(PC)]'>Sad</a> | <a href='?src=\ref[src];emotion=Fearful;voter=[ref(PC)]'>Fearful</a> | \
<a href='?src=\ref[src];emotion=Angry;voter=[ref(PC)]'>Angry</a> | <a href='?src=\ref[src];emotion=Stressed;voter=[ref(PC)]'>Stressed</a> | \
<a href='?src=\ref[src];emotion=Confused;voter=[ref(PC)]'>Confused</a")
complete_nlom_time = world.time + 2 MINUTES
completing_nlom = TRUE
if(completing_nlom && world.time >= complete_nlom_time)
var/highest_emotion = "Happy"
for(var/emotion in nlom_votes)
if(nlom_votes[highest_emotion] < nlom_votes[emotion])
highest_emotion = emotion
for(var/datum/psi_complexus/PC in all_psi_complexes)
if(PC.psionic_rank >= PSI_RANK_SENSITIVE)
var/static/nlom_to_span = list(
"Calm" = "good",
"Happy" = "good",
"Sad" = "notice",
"Fearful" = "alien",
"Angry" = "danger",
"Stressed" = "warning",
"Confused" = "cult"
)
var/static/nlom_to_chat_text = list(
"Calm" = "calmness",
"Happy" = "happiness",
"Sad" = "sadness",
"Fearful" = "fear",
"Angry" = "anger",
"Stressed" = "stress",
"Confused" = "confusion"
)
if(nlom_votes[highest_emotion] > 0)
to_chat(PC.owner, SPAN_NOTICE(FONT_HUGE("A sense of <b><span class='[nlom_to_span[highest_emotion]]'>[nlom_to_chat_text[highest_emotion]]</span></b> washes over you.")))
else
to_chat(PC.owner, SPAN_WARNING(FONT_HUGE("The Nlom is silent and still. You feel uneasy.")))
checking_nlom = FALSE
completing_nlom = FALSE
last_nlom_awareness_check = world.time
nlom_votes.Cut()
nlom_voters.Cut()
/datum/controller/subsystem/processing/psi/Initialize()
/datum/controller/subsystem/processing/psi/Topic(href, href_list)
. = ..()
if(.)
return
var/list/faculties = subtypesof(/datum/psionic_faculty)
for(var/ftype in faculties)
var/datum/psionic_faculty/faculty = new ftype
faculties_by_id[faculty.id] = faculty
faculties_by_name[faculty.name] = faculty
faculties_by_intent[faculty.associated_intent] = faculty.id
var/list/powers = subtypesof(/datum/psionic_power)
for(var/ptype in powers)
var/datum/psionic_power/power = new ptype
if(power.faculty)
var/datum/psionic_faculty/faculty = get_faculty(power.faculty)
if(faculty)
faculty.powers |= power
if(href_list["emotion"])
var/emotion = href_list["emotion"]
var/datum/psi_complexus/PC = locate(href_list["voter"])
if(PC in nlom_voters)
to_chat(PC.owner, SPAN_WARNING("You have already sent your emotional state to the Nlom."))
return
if(emotion in nlom_votes)
var/base_vote = 1
var/psi_rank = PC.get_rank()
if(psi_rank == PSI_RANK_HARMONIOUS)
base_vote = 2
else if(psi_rank == PSI_RANK_APEX)
base_vote = 4
nlom_votes[emotion] += base_vote
to_chat(PC.owner, SPAN_NOTICE("You have transmitted your emotional state to the Nlom."))
nlom_voters |= PC
+11 -6
View File
@@ -112,7 +112,7 @@
new_character.make_vampire()
if(active)
new_character.key = key //now transfer the key to link the client to our new body
/datum/mind/proc/store_memory(new_text)
. = length(memory + new_text)
@@ -179,11 +179,6 @@
/datum/mind/Topic(href, href_list)
if(!check_rights(R_ADMIN)) return
if(current && isliving(current))
if(href_list["set_psi_faculty"] && href_list["set_psi_faculty_rank"])
current.set_psi_rank(href_list["set_psi_faculty"], text2num(href_list["set_psi_faculty_rank"]))
return TRUE
if(href_list["add_antagonist"])
var/datum/antagonist/antag = all_antag_types[href_list["add_antagonist"]]
if(antag)
@@ -441,6 +436,16 @@
for(var/datum/objective/objective in objectives)
to_chat(current, "<B>Objective #[obj_count]</B>: [objective.explanation_text]")
obj_count++
else if(href_list["set_psi_rank"])
current.set_psi_rank(text2num(href_list["set_psi_rank"]))
return
else if(href_list["set_psi_rank_limitless"])
var/sure = input(usr, "Limitless is INTENTIONALLY STUPIDLY OVERPOWERED! YOU SHOULD NOT BE USING THIS WITHOUT KNOWING EXACTLY WHAT YOU'RE DOING!", "Don't Get A Staff Complaint") as anything in list("I know what I'm doing!", "I fear no man. But that thing... it scares me!")
if(sure == "I know what I'm doing!")
current.set_psi_rank(PSI_RANK_LIMITLESS)
return
edit_memory()
/datum/mind/proc/find_syndicate_uplink()
-1
View File
@@ -172,7 +172,6 @@
/datum/outfit/admin/syndicate/mercenary/loner
name = "Loner"
head = /obj/item/clothing/head/helmet/space/psi_amp/lesser
l_ear = /obj/item/device/radio/headset/syndicate
r_pocket = /obj/item/device/special_uplink/burglar
@@ -251,3 +251,8 @@
name = ".50 Pistol"
telecrystal_cost = 5
path = /obj/item/gun/projectile/deagle/adhomai
/datum/uplink_item/item/visible_weapons/psionic_jumpstarter
name = "Psionic Jumpstarter"
telecrystal_cost = 19
path = /obj/item/psionic_jumpstarter
+6 -3
View File
@@ -7,8 +7,8 @@ var/datum/antagonist/loner/loners
bantype = "loner"
antag_indicator = "loner"
landmark_id = "lonerspawn"
welcome_text = "You are a Loner, someone underequipped to deal with the station. You will probably not survive for the whole round, so don't sweat it if you die!<br>\
You are equipped with a lesser cerebro-enhancer, which allows you to unlock your psionic potential. Use it in-hand to choose your boosted faculty, then install it on your head."
welcome_text = "You are a Loner, someone underequipped to deal with the station. You will probably not survive for the whole round, so don't sweat it if you die!<br> \
You have a special psionic power that allows you to absorb a psionic energy from a being's Zona Bovinae, granting you an extra point to be used in the Point Shop."
flags = ANTAG_OVERRIDE_JOB | ANTAG_CLEAR_EQUIPMENT | ANTAG_CHOOSE_NAME | ANTAG_VOTABLE | ANTAG_SET_APPEARANCE
antaghud_indicator = "hudloner"
required_age = 7
@@ -39,8 +39,11 @@ var/datum/antagonist/loner/loners
player.preEquipOutfit(/datum/outfit/admin/syndicate/mercenary/loner, FALSE)
player.equipOutfit(/datum/outfit/admin/syndicate/mercenary/loner, FALSE)
player.set_psi_rank(PSI_RANK_HARMONIOUS)
var/singleton/psionic_power/P = GET_SINGLETON(/singleton/psionic_power/zona_absorption)
P.apply(player)
player.force_update_limbs()
player.update_eyes()
player.regenerate_icons()
return TRUE
return TRUE
@@ -10,9 +10,7 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
var/chem_recharge_rate = 0.5
var/chem_storage = 50
var/sting_range = 1
var/space_adapted = FALSE
var/using_thermals = FALSE
var/no_breathing = FALSE
var/changelingID = "Changeling"
var/geneticdamage = 0
var/isabsorbing = 0
@@ -12,8 +12,7 @@
//removes the need to breathe, removes effects of very low pressure
/mob/proc/changeling_spaceadaption()
var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
changeling.space_adapted = TRUE
ADD_TRAIT(src, TRAIT_PRESSURE_IMMUNITY, TRAIT_SOURCE_CHANGELING)
return TRUE
/mob/proc/changeling_armor()
@@ -22,8 +21,7 @@
//removes the need to breathe
/mob/proc/changeling_nobreathing()
var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
changeling.no_breathing = TRUE
ADD_TRAIT(src, TRAIT_NO_BREATHE, TRAIT_SOURCE_CHANGELING)
return TRUE
// HIVE MIND UPLOAD/DOWNLOAD DNA
@@ -97,4 +95,4 @@ var/list/datum/absorbed_dna/hivemind_bank = list()
absorbDNA(chosen_dna)
to_chat(src, "<span class='notice'>We absorb the DNA of [S] from the air.</span>")
feedback_add_details("changeling_powers", "HD")
return TRUE
return TRUE
+2 -2
View File
@@ -356,7 +356,7 @@ var/list/all_technomancer_assistance = typesof(/datum/technomancer/assistance) -
return
var/obj/item/technomancer_core/core = H.get_technomancer_core()
if(core)
for(var/obj/spellbutton/spell in core.spells)
for(var/obj/spellbutton/technomancer/spell in core.spells)
for(var/datum/technomancer/spell/spell_datum in spell_instances)
if(spell_datum.obj_path == spell.spellpath)
budget += spell_datum.cost
@@ -381,7 +381,7 @@ var/list/all_technomancer_assistance = typesof(/datum/technomancer/assistance) -
// We sadly need to do special stuff here or else people who refund cores with spells will lose points permanently.
if(istype(AM, /obj/item/technomancer_core))
var/obj/item/technomancer_core/core = AM
for(var/obj/spellbutton/spell in core.spells)
for(var/obj/spellbutton/technomancer/spell in core.spells)
for(var/datum/technomancer/spell/spell_datum in spell_instances)
if(spell_datum.obj_path == spell.spellpath)
budget += spell_datum.cost
+13 -7
View File
@@ -178,17 +178,15 @@
/obj/spellbutton
name = "generic spellbutton"
var/spellpath
var/obj/item/technomancer_core/core
var/ability_icon_state
/obj/spellbutton/New(loc, var/path, var/new_name, var/new_icon_state)
/obj/spellbutton/Initialize(mapload, var/path, var/new_name, var/new_icon_state)
. = ..()
if(!path || !ispath(path))
message_admins("ERROR: /obj/spellbutton/New() was not given a proper path!")
qdel(src)
message_admins("ERROR: /obj/spellbutton/Initialize() was not given a proper path!")
return INITIALIZE_HINT_QDEL
src.name = new_name
src.spellpath = path
src.loc = loc
src.core = loc
src.ability_icon_state = new_icon_state
/obj/spellbutton/Click()
@@ -199,6 +197,14 @@
/obj/spellbutton/DblClick()
return Click()
/obj/spellbutton/technomancer
var/obj/item/technomancer_core/core
/obj/spellbutton/technomancer/Initialize(mapload, var/path, var/new_name, var/new_icon_state)
. = ..()
src.loc = loc
src.core = loc
/mob/living/carbon/human/get_status_tab_items()
. = ..()
@@ -216,7 +222,7 @@
message_admins("ERROR: /obj/item/technomancer_core/add_spell() was not given a proper path! \
The path supplied was [path].")
return
var/obj/spellbutton/spell = new(src, path, new_name, ability_icon_state)
var/obj/spellbutton/technomancer/spell = new(src, path, new_name, ability_icon_state)
spells.Add(spell)
if(wearer)
wearer.ability_master.add_technomancer_ability(spell, ability_icon_state)
+83 -34
View File
@@ -20,6 +20,7 @@
#define ASPECT_UNSTABLE "unstable" //Heavily RNG-based, causes instability to the victim.
#define ASPECT_CHROMATIC "chromatic" //Used to combine with other spells.
#define ASPECT_UNHOLY "unholy" //Involves the dead, blood, and most things against divine beings.
#define ASPECT_PSIONIC "psionics" //Psionic power. Bypasses core checks.
/obj/item/spell
name = "glowing particles"
@@ -32,13 +33,15 @@
)
throwforce = 0
force = 0
var/mob/living/carbon/human/owner = null
var/obj/item/technomancer_core/core = null
flags = NOBLUDGEON
var/mob/living/carbon/human/owner
var/obj/item/technomancer_core/core
var/cast_methods = null // Controls how the spell is casted.
var/aspect = null // Used for combining spells.
var/toggled = 0 // Mainly used for overlays.
var/cooldown = 0 // If set, will add a cooldown overlay and adjust click delay. Must be a multiple of 5 for overlays.
var/cast_sound = null // Sound file played when this is used.
var/psi_cost = 0 // Psi complexus cost to use this spell.
/obj/item/spell/examine(mob/user, distance) // Nothing on examine.
return
@@ -46,38 +49,62 @@
// Proc: on_use_cast()
// Parameters: 1 (user - the technomancer casting the spell)
// Description: Override this for clicking the spell in your hands.
/obj/item/spell/proc/on_use_cast(mob/user)
return
/obj/item/spell/proc/on_use_cast(mob/user, var/bypass_psi_check)
SHOULD_CALL_PARENT(TRUE)
if(aspect == ASPECT_PSIONIC && !bypass_psi_check)
if(!owner.psi.spend_power(psi_cost))
return FALSE
return TRUE
// Proc: on_throw_cast()
// Parameters: 1 (hit_atom - the atom hit by the spell object)
// Description: Override this for throwing effects.
/obj/item/spell/proc/on_throw_cast(atom/hit_atom)
return
/obj/item/spell/proc/on_throw_cast(atom/hit_atom, var/bypass_psi_check)
SHOULD_CALL_PARENT(TRUE)
if(aspect == ASPECT_PSIONIC && !bypass_psi_check)
if(!owner.psi.spend_power(psi_cost))
return FALSE
return TRUE
// Proc: on_ranged_cast()
// Parameters: 2 (hit_atom - the atom clicked on by the user, user - the technomancer that clicked hit_atom)
// Description: Override this for ranged effects.
/obj/item/spell/proc/on_ranged_cast(atom/hit_atom, mob/user)
return
/obj/item/spell/proc/on_ranged_cast(atom/hit_atom, mob/user, var/bypass_psi_check)
SHOULD_CALL_PARENT(TRUE)
if(aspect == ASPECT_PSIONIC && !bypass_psi_check)
if(!owner.psi.spend_power(psi_cost))
return FALSE
return TRUE
// Proc: on_melee_cast()
// Parameters: 3 (hit_atom - the atom clicked on by the user, user - the technomancer that clicked hit_atom, def_zone - unknown)
// Description: Override this for effects that occur at melee range.
/obj/item/spell/proc/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
return
SHOULD_CALL_PARENT(TRUE)
if(aspect == ASPECT_PSIONIC)
if(!owner.psi.spend_power(psi_cost))
return FALSE
return TRUE
// Proc: on_combine_cast()
// Parameters: 2 (I - the item trying to merge with the spell, user - the technomancer who initiated the merge)
// Description: Override this for combining spells, like Aspect spells.
/obj/item/spell/proc/on_combine_cast(obj/item/I, mob/user)
return
SHOULD_CALL_PARENT(TRUE)
if(aspect == ASPECT_PSIONIC)
if(!owner.psi.spend_power(psi_cost))
return FALSE
return TRUE
// Proc: on_innate_cast()
// Parameters: 1 (user - the entity who is casting innately (without using hands).)
// Description: Override this for casting without using hands (and as a result not using spell objects).
/obj/item/spell/proc/on_innate_cast(mob/user)
return
SHOULD_CALL_PARENT(TRUE)
if(aspect == ASPECT_PSIONIC)
if(!owner.psi.spend_power(psi_cost))
return FALSE
return TRUE
// Proc: on_scepter_use_cast()
// Parameters: 1 (user - the holder of the Scepter that clicked.)
@@ -99,7 +126,9 @@
// if they are able to pay, it is deducted automatically.
/obj/item/spell/proc/pay_energy(var/amount)
if(!core)
return 0
if(aspect == ASPECT_PSIONIC)
return owner.psi.spend_power(amount)
return amount
return core.pay_energy(amount)
// Proc: give_energy()
@@ -107,7 +136,7 @@
// Description: Redirects the call to the core's give_energy().
/obj/item/spell/proc/give_energy(var/amount)
if(!core)
return 0
return amount
return core.give_energy(amount)
// Proc: adjust_instability()
@@ -141,11 +170,17 @@
if(isliving(loc))
owner = loc
if(owner)
core = owner.get_technomancer_core()
if(!core)
to_chat(owner, "<span class='warning'>You need a Core to do that.</span>")
qdel(src)
return
if(aspect != ASPECT_PSIONIC)
core = owner.get_technomancer_core()
if(!core)
to_chat(owner, "<span class='warning'>You need a Core to do that.</span>")
return INITIALIZE_HINT_QDEL
else
if(owner.psi.get_rank() >= PSI_RANK_APEX)
if(force)
force *= 1.1
armor_penetration *= 1.1
update_icon()
// Proc: Destroy()
@@ -180,32 +215,37 @@
// if it still can't find one. It will also check if the core is being worn properly, and finally checks if the owner is a technomancer.
/obj/item/spell/proc/run_checks()
if(!owner)
return 0
if(!core)
core = locate(/obj/item/technomancer_core) in owner
return FALSE
if(aspect != ASPECT_PSIONIC)
if(!core)
core = locate(/obj/item/technomancer_core) in owner
if(!core)
to_chat(owner, "<span class='danger'>You need to be wearing a core on your back or your wrists!</span>")
return FALSE
if(core.loc != owner || (owner.back != core && owner.wrists != core)) //Make sure the core's being worn.
to_chat(owner, "<span class='danger'>You need to be wearing a core on your back or your wrists!</span>")
return 0
if(core.loc != owner || (owner.back != core && owner.wrists != core)) //Make sure the core's being worn.
to_chat(owner, "<span class='danger'>You need to be wearing a core on your back or your wrists!</span>")
return 0
if(!core.simple_operation && !technomancers.is_technomancer(owner.mind)) //Now make sure the person using this is the actual antag.
to_chat(owner, "<span class='danger'>You can't seem to figure out how to make the machine work properly.</span>")
return 0
return 1
return FALSE
if(!core.simple_operation && !technomancers.is_technomancer(owner.mind)) //Now make sure the person using this is the actual antag.
to_chat(owner, SPAN_DANGER("You can't seem to figure out how to make the machine work properly."))
return FALSE
else
if(!owner.psi)
return FALSE
return TRUE
// Proc: check_for_scepter()
// Parameters: 0
// Description: Terrible code to check if a scepter is in the offhand, returns 1 if yes.
/obj/item/spell/proc/check_for_scepter()
if(!src || !owner) return 0
if(!src || !owner)
return FALSE
if(owner.r_hand == src)
if(istype(owner.l_hand, /obj/item/scepter))
return 1
return TRUE
else
if(istype(owner.r_hand, /obj/item/scepter))
return 1
return 0
return TRUE
return FALSE
// Proc: get_other_hand()
// Parameters: 1 (I - item being compared to determine what the offhand is)
@@ -259,7 +299,11 @@
if(cast_methods & CAST_RANGED) //Try to use a ranged method if a melee one doesn't exist.
on_ranged_cast(target, user)
if(cooldown)
var/effective_cooldown = round(cooldown * core.cooldown_modifier, 5)
var/effective_cooldown
if(aspect == ASPECT_PSIONIC)
effective_cooldown = cooldown
else
effective_cooldown = round(cooldown * core.cooldown_modifier, 5)
user.setClickCooldown(effective_cooldown)
flick("cooldown_[effective_cooldown]",src)
@@ -330,3 +374,8 @@
spawn(20)
if(src)
qdel(src)
/obj/item/spell/damage_flags()
. = ..()
if(aspect == ASPECT_PSIONIC)
. |= DAMAGE_FLAG_PSIONIC
@@ -15,6 +15,7 @@
aspect = ASPECT_TELE
/obj/item/spell/abjuration/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(istype(hit_atom, /mob/living) && pay_energy(500) && within_range(hit_atom))
var/mob/living/L = hit_atom
var/mob/living/simple_animal/SM = null
@@ -38,4 +39,4 @@
// In case NarNar comes back someday.
if(istype(hit_atom, /obj/singularity/narsie))
to_chat(user, "<span class='danger'>One does not simply abjurate Nar'sie away.</span>")
adjust_instability(200)
adjust_instability(200)
@@ -16,6 +16,7 @@
aspect = ASPECT_TELE
/obj/item/spell/apportation/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(istype(hit_atom, /atom/movable))
var/atom/movable/AM = hit_atom
@@ -65,6 +65,7 @@
var/selected_sound = null
/obj/item/spell/audible_deception/on_use_cast(mob/user)
. = ..()
var/list/sound_options = available_sounds
if(check_for_scepter())
sound_options["!!AIR HORN!!"] = 'sound/items/AirHorn.ogg'
@@ -73,6 +74,7 @@
selected_sound = sound_options[new_sound]
/obj/item/spell/audible_deception/on_ranged_cast(atom/hit_atom, mob/living/user)
. = ..()
var/turf/T = get_turf(hit_atom)
if(selected_sound && pay_energy(200))
playsound(src, selected_sound, 80, 1, -1)
@@ -36,5 +36,6 @@
adjust_instability(2)
/obj/item/spell/aura/biomed/on_use_cast(mob/living/user)
. = ..()
heal_allies_only = !heal_allies_only
to_chat(user, "Your aura will now heal [heal_allies_only ? "your allies" : "everyone"] near you.")
@@ -30,6 +30,7 @@
set_light(6, 5, l_color = new_color)
/obj/item/spell/chroma/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
var/turf/T = get_turf(hit_atom)
if(T)
new /obj/effect/temporary_effect/chroma(T, color_to_use)
@@ -37,6 +38,7 @@
qdel(src)
/obj/item/spell/chroma/on_use_cast(mob/user)
. = ..()
var/new_color = input(user, "Choose the color you want your light to be.", "Color selection") as null|color
if(new_color)
color_to_use = new_color
@@ -17,6 +17,7 @@
cooldown = 2 SECONDS
/obj/item/spell/condensation/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(pay_energy(200))
if(istype(hit_atom, /turf/simulated) && within_range(hit_atom))
var/turf/simulated/T = hit_atom
@@ -41,4 +42,4 @@
log_and_message_admins("has wetted the floor with [src] at [T.x],[T.y],[T.z].")
else if(hit_atom.reagents && !ismob(hit_atom)) //TODO: Something for the scepter
hit_atom.reagents.add_reagent(/singleton/reagent/water, 60, safety = FALSE)
adjust_instability(5)
adjust_instability(5)
@@ -16,5 +16,6 @@
aspect = ASPECT_BIOMED
/obj/item/spell/dispel/on_ranged_cast(atom/hit_atom, mob/living/user)
. = ..()
user.adjust_instability(10)
qdel(src)
qdel(src)
@@ -49,6 +49,7 @@
/obj/item/spell/energy_siphon/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(istype(hit_atom, /atom/movable) && within_range(hit_atom, 4))
var/atom/movable/AM = hit_atom
populate_siphon_list(AM)
@@ -34,9 +34,10 @@
//Needed to make the spell welder have infinite fuel. Don't worry, it uses energy instead.
/obj/item/weldingtool/spell/use(var/amount = 1, var/mob/M = null, var/colourChange = TRUE)
return 1
return TRUE
/obj/item/spell/flame_tongue/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(isliving(hit_atom) && user.a_intent != I_HELP)
var/mob/living/L = hit_atom
if(pay_energy(1000))
@@ -26,6 +26,7 @@
aspect = ASPECT_UNSTABLE
/obj/item/spell/gambit/on_use_cast(mob/living/carbon/human/user)
. = ..()
if(pay_energy(200))
adjust_instability(3)
if(check_for_scepter())
@@ -113,4 +114,4 @@
if(!potential_spells.len)
potential_spells = all_technomancer_gambit_spells.Copy()
return pick(potential_spells)
return pick(potential_spells)
@@ -17,6 +17,7 @@
var/mob/living/simple_animal/illusion/illusion = null
/obj/item/spell/illusion/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(istype(hit_atom, /atom/movable))
var/atom/movable/AM = hit_atom
if(pay_energy(100))
@@ -38,6 +39,7 @@
return 1
/obj/item/spell/illusion/on_use_cast(mob/user)
. = ..()
if(illusion)
var/choice = alert(user, "Would you like to have \the [illusion] speak, or do an emote?", "Illusion", "Speak","Emote","Cancel")
switch(choice)
@@ -49,11 +49,13 @@
qdel(src)
/obj/item/spell/insert/on_melee_cast(atom/hit_atom, mob/user)
. = ..()
if(istype(hit_atom, /mob/living))
var/mob/living/L = hit_atom
insert(L,user)
/obj/item/spell/insert/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(istype(hit_atom, /mob/living))
var/mob/living/L = hit_atom
insert(L,user)
insert(L,user)
@@ -19,6 +19,7 @@
set_light(3, 2, l_color = "#FA58F4")
/obj/item/spell/instability_tap/on_use_cast(mob/user)
. = ..()
var/amount = calculate_spell_power(5000)
if(check_for_scepter())
core.give_energy(amount * 1.5)
@@ -27,4 +28,4 @@
core.give_energy(amount)
adjust_instability(50)
playsound(src, 'sound/effects/supermatter.ogg', 75, 1)
qdel(src)
qdel(src)
@@ -27,6 +27,7 @@
aspect = ASPECT_TELE
/obj/item/spell/mark/on_use_cast(mob/living/user)
. = ..()
if(!allowed_to_teleport()) // Otherwise you could teleport back to the admin Z-level.
to_chat(user, "<span class='warning'>You can't teleport here!</span>")
return 0
@@ -63,6 +64,7 @@
aspect = ASPECT_TELE
/obj/item/spell/recall/on_use_cast(mob/living/user)
. = ..()
if(pay_energy(3000))
if(!mark_spell_ref)
to_chat(user, "<span class='danger'>There's no Mark!</span>")
@@ -17,6 +17,7 @@
light_color = "#FF5C5C"
/obj/item/spell/mend_organs/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(isliving(hit_atom))
var/mob/living/L = hit_atom
var/heal_power = calculate_spell_power(40)
@@ -14,11 +14,13 @@
set_light(spell_light_range, spell_light_intensity, l_color = light_color)
/obj/item/spell/modifier/on_melee_cast(atom/hit_atom, mob/user)
. = ..()
if(isliving(hit_atom))
return on_add_modifier(hit_atom)
return FALSE
/obj/item/spell/modifier/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(isliving(hit_atom))
return on_add_modifier(hit_atom)
return FALSE
@@ -45,4 +47,4 @@
/datum/modifier/technomancer/stop()
..()
to_chat(target, SPAN_WARNING(on_expired_text))
to_chat(target, SPAN_WARNING(on_expired_text))
@@ -16,6 +16,7 @@
cooldown = 30
/obj/item/spell/oxygenate/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(!within_range(hit_atom))
return
if(ishuman(hit_atom))
@@ -30,4 +31,4 @@
T.assume_gas("oxygen", 200)
T.assume_gas("nitrogen", 800)
playsound(src, 'sound/effects/spray.ogg', 50, 1, -3)
adjust_instability(10)
adjust_instability(10)
@@ -17,6 +17,7 @@
var/busy = 0
/obj/item/spell/passwall/on_melee_cast(atom/hit_atom, mob/user)
. = ..()
if(busy) //Prevent someone from trying to get two uses of the spell from one instance.
return 0
if(!allowed_to_teleport())
@@ -46,7 +46,7 @@
L.forceMove(get_turf(src))
L.visible_message(SPAN_WARNING("\The [src] ejects [L]!"))
/obj/effect/phase_shift/relaymove(mob/user as mob)
/obj/effect/phase_shift/relaymove(mob/user)
if(user.stat)
return
@@ -55,6 +55,7 @@
qdel(src)
/obj/item/spell/phase_shift/on_use_cast(mob/user)
. = ..()
if(isturf(user.loc)) //Check if we're not already in a rift.
if(pay_energy(2000))
var/obj/effect/phase_shift/PS = new(get_turf(user))
@@ -10,9 +10,11 @@
var/fire_sound = null
/obj/item/spell/projectile/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
on_ranged_cast(hit_atom, user, hit_atom)
/obj/item/spell/projectile/on_ranged_cast(atom/hit_atom, mob/living/user, atom/pb_target)
. = ..(hit_atom, user, TRUE)
if(set_up(hit_atom, user))
var/obj/item/projectile/new_projectile = make_projectile(spell_projectile, user)
new_projectile.old_style_target(hit_atom)
@@ -20,18 +22,28 @@
log_and_message_admins("has casted [src] at \the [hit_atom].")
if(fire_sound)
playsound(src, fire_sound, 75, 1)
adjust_instability(instability_per_shot)
return 1
return 0
if(aspect != ASPECT_PSIONIC)
adjust_instability(instability_per_shot)
return TRUE
return FALSE
/obj/item/spell/projectile/proc/make_projectile(obj/item/projectile/projectile_type, mob/living/user)
var/obj/item/projectile/P = new projectile_type(get_turf(user))
P.damage = calculate_spell_power(P.damage)
P.firer = user
if(aspect != ASPECT_PSIONIC)
P.damage = calculate_spell_power(P.damage)
else
if(owner.psi.get_rank() >= PSI_RANK_APEX)
if(P.damage)
P.damage *= 1.1
P.armor_penetration *= 1.1
P.damage_flags |= DAMAGE_FLAG_PSIONIC
return P
/obj/item/spell/projectile/proc/set_up(atom/hit_atom, mob/living/user)
if(spell_projectile)
if(pay_energy(energy_cost_per_shot))
/// Psionic energy payment is handled at the beginning of on_ranged_cast.
if(pay_energy(aspect == ASPECT_PSIONIC ? psi_cost : energy_cost_per_shot))
if(pre_shot_delay)
var/image/target_image = image(icon = 'icons/obj/spells.dmi', loc = get_turf(hit_atom), icon_state = "target")
user << target_image
@@ -42,4 +54,4 @@
return TRUE
return FALSE // We got dropped before the firing occurred.
return TRUE // No delay, no need to check.
return FALSE
return FALSE
@@ -16,6 +16,7 @@
aspect = ASPECT_FORCE
/obj/item/spell/raise_wall/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
var/turf/target_turf = isturf(hit_atom) ? hit_atom : get_turf(hit_atom)
if(target_turf.density)
return
@@ -16,6 +16,7 @@
aspect = ASPECT_BIOMED
/obj/item/spell/resurrect/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(isliving(hit_atom))
var/mob/living/L = hit_atom
if(L == user)
@@ -15,6 +15,7 @@
aspect = ASPECT_UNSTABLE
/obj/item/spell/shared_burden/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(ishuman(hit_atom) && within_range(hit_atom))
var/mob/living/carbon/human/H = hit_atom
if(H == user)
@@ -27,4 +28,4 @@
var/instability_to_drain = min(H.instability, 25)
to_chat(user, "<span class='notice'>You draw instability away from \the [H] and towards you.</span>")
adjust_instability(instability_to_drain)
H.adjust_instability(-calculate_spell_power(instability_to_drain))
H.adjust_instability(-calculate_spell_power(instability_to_drain))
@@ -24,6 +24,7 @@
..()
/obj/item/spell/spawner/pulsar/on_throw_cast(atom/hit_atom, mob/user)
. = ..()
empulse(hit_atom, 1, 1, 1, 1, log=1)
// Does something every so often. Deletes itself when pulses_remaining hits zero.
@@ -40,7 +41,7 @@
/obj/effect/temporary_effect/pulse/proc/pulse_loop()
set waitfor = FALSE
while(pulses_remaining)
sleep(pulse_delay)
on_pulse()
@@ -7,9 +7,10 @@
var/obj/effect/spawner_type = null
/obj/item/spell/spawner/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
var/turf/T = get_turf(hit_atom)
if(T)
new spawner_type(T)
to_chat(user, "<span class='notice'>You shift \the [src] onto \the [T].</span>")
log_and_message_admins("has casted [src] at [T.x],[T.y],[T.z].")
qdel(src)
qdel(src)
@@ -12,6 +12,7 @@
var/instability_cost = 0
/obj/item/spell/summon/on_ranged_cast(atom/hit_atom, mob/living/user)
. = ..()
var/turf/T = get_turf(hit_atom)
if(summoned_mob_type && core.summoned_mobs.len < core.max_summons && within_range(hit_atom) && pay_energy(energy_cost))
var/obj/effect/E = new(T)
@@ -30,6 +31,7 @@
user.adjust_instability(instability_cost)
/obj/item/spell/summon/on_use_cast(mob/living/user)
. = ..()
if(summon_options.len)
var/choice = input(user, "Choose a creature to kidnap from somewhere!", "Summon") as null|anything in summon_options
if(choice)
@@ -37,4 +39,4 @@
// Called when a new mob is summoned, override for special behaviour.
/obj/item/spell/summon/proc/on_summon(var/mob/living/summoned)
return
return
@@ -15,6 +15,7 @@
aspect = ASPECT_FORCE //idk?
/obj/item/spell/targeting_matrix/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
var/turf/T = get_turf(hit_atom)
if(T)
var/mob/living/chosen_target = targeting_assist(T,5) //The person who's about to get attacked.
@@ -27,6 +27,7 @@ var/list/technomancer_belongings = list()
return ..()
/obj/item/spell/track/on_use_cast(mob/user)
. = ..()
if(tracking)
tracking = 0
to_chat(user, "<span class='notice'>You stop tracking for \the [tracked]'s whereabouts.</span>")
@@ -75,4 +76,4 @@ var/list/technomancer_belongings = list()
icon_state = "track_far"
spawn(5)
.()
.()
@@ -14,6 +14,7 @@
aspect = ASPECT_TELE
/obj/item/spell/warp_strike/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
var/turf/T = get_turf(hit_atom)
if(T)
if(!within_range(T))
+1 -1
View File
@@ -6,7 +6,7 @@ would spawn and follow the beaker, even if it is carried or thrown.
*/
/obj/effect/effect
/obj/effect
name = "effect"
icon = 'icons/effects/effects.dmi'
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
@@ -105,7 +105,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
/obj/item/flame/match/proc/light()
lit = TRUE
damtype = "burn"
damtype = "fire"
icon_state = "match_lit"
item_state = "match_lit"
if(ismob(loc))
@@ -163,6 +163,12 @@
else
to_chat(user, SPAN_WARNING("You need at least [glass_needed] sheets of [MATERIAL_GLASS_REINFORCED_PHORON] to finished the window."))
/obj/structure/window_frame/hitby(atom/movable/AM, speed)
. = ..()
var/obj/structure/window/W = locate() in get_turf(src)
if(istype(W))
W.hitby(AM)
/obj/structure/window_frame/unanchored // Used during in-game construction.
should_check_mapload = FALSE // No glass.
anchored = FALSE
+9 -11
View File
@@ -106,18 +106,16 @@ var/global/enabled_spooking = 0
var/mob/living/psyker = M
if(psyker.psi)
body += "<a href='?src=\ref[src];remove_psionics=\ref[psyker.psi]'>Remove psionics.</a><br/><br/>"
body += "<a href='?src=\ref[src];trigger_psi_latencies\ref[psyker.psi]'>Trigger latencies.</a><br/>"
body += "<table width = '100%'>"
for(var/faculty in list(PSI_COERCION, PSI_PSYCHOKINESIS, PSI_REDACTION, PSI_ENERGISTICS))
var/datum/psionic_faculty/faculty_decl = SSpsi.get_faculty(faculty)
var/faculty_rank = psyker.psi ? psyker.psi.get_rank(faculty) : 0
body += "<tr><td><b>[faculty_decl.name]</b></td>"
for(var/i = 1 to LAZYLEN(psychic_ranks_to_strings))
var/psi_title = psychic_ranks_to_strings[i]
if(i == faculty_rank)
psi_title = "<b>[psi_title]</b>"
body += "<td><a href='?src=\ref[psyker.mind];set_psi_faculty_rank=[i];set_psi_faculty=[faculty]'>[psi_title]</a></td>"
body += "</tr>"
for(var/psi_rank in list(PSI_RANK_SENSITIVE, PSI_RANK_HARMONIOUS, PSI_RANK_APEX, PSI_RANK_LIMITLESS))
var/owner_rank = psyker.psi ? psyker.psi.get_rank() : 0
var/psi_title = psychic_ranks_to_strings[psi_rank]
if(psi_rank == owner_rank)
psi_title = "<b>[psi_title]</b>"
if(psi_rank != PSI_RANK_LIMITLESS)
body += "<tr><a href='?src=\ref[psyker.mind];set_psi_rank=[psi_rank]'>[psi_title]</a></tr>"
else
body += "<tr><a href='?src=\ref[psyker.mind];set_psi_rank_limitless=1'><font color='red'>[psi_title]</font></a></tr>"
body += "</table>"
if (M.client)
+2 -2
View File
@@ -432,8 +432,8 @@
else if(href_list["trigger_psi_latencies"])
var/datum/psi_complexus/psi = locate(href_list["trigger_psi_latencies"])
log_and_message_admins("triggered psi latencies for [key_name(psi.owner)].")
psi.check_latency_trigger(100, "outside intervention", redactive = TRUE)
log_and_message_admins("triggered psionics for [key_name(psi.owner)].")
psi.check_psionic_trigger(100, "outside intervention", redactive = TRUE)
else if(href_list["monkeyone"])
if(!check_rights(R_SPAWN)) return
+3 -2
View File
@@ -42,10 +42,11 @@
/obj/effect/bmode//Cleaning up the tree a bit
density = 1
anchored = 1
layer = SCREEN_LAYER
layer = SCREEN_LAYER + 1
dir = NORTH
icon = 'icons/misc/buildmode.dmi'
var/obj/effect/bmode/buildholder/master = null
mouse_opacity = MOUSE_OPACITY_OPAQUE
var/obj/effect/bmode/buildholder/master
/obj/effect/bmode/Destroy()
if(master && master.cl)
@@ -0,0 +1,135 @@
/datum/category_item/player_setup_item/general/psionics
name = "Psionics"
sort_order = 7
/datum/category_item/player_setup_item/general/psionics/load_character(var/savefile/S)
S["psionics"] >> pref.psionics
/datum/category_item/player_setup_item/general/psionics/save_character(var/savefile/S)
var/list/psionics = pref.psionics
for(var/psi in psionics)
var/singleton/psionic_power/P = GET_SINGLETON(text2path(psi))
if(!istype(P))
continue
psionics |= P.type
S["psionics"] << json_encode(psionics)
/datum/category_item/player_setup_item/general/psionics/gather_load_query()
return list(
"ss13_characters" = list(
"vars" = list(
"psionics" = "psionics",
),
"args" = list("id")
)
)
/datum/category_item/player_setup_item/general/psionics/gather_load_parameters()
return list("id" = pref.current_character)
/datum/category_item/player_setup_item/general/psionics/gather_save_query()
return list(
"ss13_characters" = list(
"psionics",
"id" = 1
)
)
/datum/category_item/player_setup_item/general/psionics/gather_save_parameters()
var/list/sanitized_psionics = list()
for(var/S in pref.psionics)
var/singleton/psionic_power/P = GET_SINGLETON(text2path(S))
if(!istype(P))
continue
sanitized_psionics |= "[P.type]"
return list(
"psionics" = json_encode(sanitized_psionics),
"char_id" = pref.current_character,
"ckey" = PREF_CLIENT_CKEY
)
/datum/category_item/player_setup_item/general/psionics/load_special(savefile/S)
var/before = pref.psionics
try
pref.psionics = json_decode(pref.psionics)
catch (var/exception/e)
log_debug("PSIONICS: Caught [e]. Initial value: [before]")
pref.psionics = list()
/datum/category_item/player_setup_item/general/psionics/sanitize_character(var/sql_load = 0)
var/datum/species/mob_species = all_species[pref.species]
if(length(pref.psionics) && !mob_species.has_psionics)
to_chat(pref.client, SPAN_WARNING("This species does not have psionics! Resetting..."))
pref.psionics = list()
return
var/list/bought_psionic_powers = list()
for(var/S in pref.psionics)
var/singleton/psionic_power/P = GET_SINGLETON(text2path(S))
if(istype(P))
if(!(P.ability_flags & PSI_FLAG_CANON))
pref.psionics -= S
to_chat(pref.client, SPAN_WARNING("[P.name] is an invalid psionic!"))
continue
bought_psionic_powers |= P
else
to_chat(pref.client, SPAN_WARNING("[S] is an invalid psionic!"))
pref.psionics -= S
continue
if(length(bought_psionic_powers) > mob_species.character_creation_psi_points)
to_chat(pref.client, SPAN_WARNING("You have more psionics than possible! Resetting..."))
pref.psionics = list()
/datum/category_item/player_setup_item/general/psionics/content(var/mob/user)
var/datum/species/mob_species = all_species[pref.species]
if(!(mob_species.has_psionics))
return
var/list/bought_psionic_powers = list()
for(var/S in pref.psionics)
var/singleton/psionic_power/P = GET_SINGLETON(text2path(S))
if(istype(P))
bought_psionic_powers |= P
var/list/dat = list(
"<b>Psionics:</b><br>"
)
for(var/singleton/psionic_power/P in bought_psionic_powers)
dat += "- [P.name] <a href='?src=\ref[src];remove_psi_power=[P.type]'>-</a><br>"
dat += "<a href='?src=\ref[src];add_psi_power=1'>Add Psionic Power</a><br>"
. = dat.Join()
/datum/category_item/player_setup_item/general/psionics/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["remove_psi_power"])
var/power_to_remove = href_list["remove_psi_power"]
if(power_to_remove && (power_to_remove in pref.psionics))
pref.psionics -= power_to_remove
return TOPIC_REFRESH
else if(href_list["add_psi_power"])
var/datum/species/mob_species = all_species[pref.species]
var/total_psi_points = mob_species.character_creation_psi_points
var/list/available_psionics = list()
var/list/psionic_map = list()
var/list/bought_psionic_powers = list()
for(var/S in pref.psionics)
var/singleton/psionic_power/P = GET_SINGLETON(text2path(S))
if(istype(P))
bought_psionic_powers |= P
total_psi_points = max(0, total_psi_points - P.point_cost)
for(var/singleton/psionic_power/P in GET_SINGLETON_SUBTYPE_LIST(/singleton/psionic_power))
if((P.ability_flags & PSI_FLAG_CANON) && (P.point_cost <= total_psi_points) && !(P in bought_psionic_powers))
available_psionics |= "[P.name]"
psionic_map[P.name] = P.type
if(!length(available_psionics))
to_chat(user, SPAN_WARNING("You ran out of points!"))
return
var/new_power = input(user, "Choose a psionic power to add.", "Psionics") as null|anything in available_psionics
if(new_power)
var/singleton/psionic_power/P = GET_SINGLETON(psionic_map[new_power])
if(istype(P))
pref.psionics += "[P.type]"
return TOPIC_REFRESH
@@ -144,8 +144,7 @@
display_name = "psionic receiver"
description = "An augment installed into the head that functions as a surrogate for a missing zona bovinae, also functioning as a filter for the psionically-challenged."
path = /obj/item/organ/internal/augment/psi
whitelisted = list(SPECIES_HUMAN, SPECIES_HUMAN_OFFWORLD, SPECIES_TAJARA, SPECIES_TAJARA_ZHAN, SPECIES_TAJARA_MSAI, SPECIES_VAURCA_WORKER, SPECIES_VAURCA_WARRIOR, SPECIES_VAURCA_BULWARK, SPECIES_VAURCA_BREEDER, SPECIES_IPC, SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_ZENGHU, SPECIES_IPC_BISHOP, SPECIES_IPC_SHELL)
whitelisted = list(SPECIES_HUMAN, SPECIES_HUMAN_OFFWORLD, SPECIES_TAJARA, SPECIES_TAJARA_ZHAN, SPECIES_TAJARA_MSAI, SPECIES_UNATHI, SPECIES_VAURCA_WORKER, SPECIES_VAURCA_WARRIOR, SPECIES_VAURCA_BULWARK, SPECIES_VAURCA_BREEDER, SPECIES_IPC, SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_ZENGHU, SPECIES_IPC_BISHOP, SPECIES_IPC_SHELL)
/datum/gear/augment/memory_inhibitor
display_name = "memory inhibitor"
description = "A Zeng Hu implant that allows one to have control over their memories, allowing you to set a timer and remove any memories developed within it."
+9
View File
@@ -99,6 +99,8 @@ var/list/preferences_datums = list()
var/culture
var/origin
var/list/psionics = list()
var/list/char_render_holders //Should only be a key-value list of north/south/east/west = obj/screen.
var/static/list/preview_screen_locs = list(
"1" = "character_preview_map:1,5:-12",
@@ -471,6 +473,12 @@ var/list/preferences_datums = list()
character.headset_choice = headset_choice
if(length(psionics) && character.client)
for(var/power in psionics)
var/singleton/psionic_power/P = GET_SINGLETON(text2path(power))
if(istype(P) && (P.ability_flags & PSI_FLAG_CANON))
P.apply(character)
if(icon_updates)
character.force_update_limbs()
character.update_mutations(0)
@@ -638,6 +646,7 @@ var/list/preferences_datums = list()
ccia_actions = list()
disabilities = list()
psionics = list()
economic_status = ECONOMICALLY_AVERAGE
+2 -5
View File
@@ -23,12 +23,9 @@
/mob/living/carbon/proc/breathe(var/volume_needed = BREATH_VOLUME)
if(species && (species.flags & NO_BREATHE))
return
var/datum/changeling/changeling = get_antag_datum(MODE_CHANGELING)
if(changeling?.space_adapted)
if(HAS_TRAIT(src, TRAIT_PRESSURE_IMMUNITY))
return
if(changeling?.no_breathing)
return
volume_needed *= (species?.breath_vol_mul || 1)
var/datum/gas_mixture/breath = null
@@ -1485,6 +1485,9 @@
species.set_default_tail(src)
if(species.psi_deaf || HAS_FLAG(species.flags, IS_MECHANICAL) || HAS_FLAG(species.flags, NO_SCAN))
ADD_TRAIT(src, TRAIT_PSIONICALLY_DEAF, INNATE_TRAIT)
if(client)
client.init_verbs()
@@ -185,7 +185,7 @@
return O
/mob/living/carbon/human/proc/awaken_psi_basic(var/source, var/allow_latency = TRUE)
/mob/living/carbon/human/proc/awaken_psi_basic(var/source)
var/static/list/psi_operancy_messages = list(
"There's something in your skull!",
"Something is eating your thoughts!",
@@ -197,13 +197,10 @@
)
to_chat(src, SPAN_DANGER("An indescribable, brain-tearing sound hisses from [source], and you collapse in a seizure!"))
seizure()
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)
custom_pain(SPAN_DANGER("<font size = 3>[pick(psi_operancy_messages)]</font>"), 25)
set_psi_rank(pick_n_take(faculties), allow_latency ? PSI_RANK_LATENT : PSI_RANK_OPERANT) // if set to latent, it spikes anywhere from OPERANT to PARAMOUNT
sleep(30)
addtimer(CALLBACK(psi, TYPE_PROC_REF(/datum/psi_complexus, check_latency_trigger), 100, source, TRUE), 4.5 SECONDS)
custom_pain(SPAN_DANGER(FONT_LARGE("[pick(psi_operancy_messages)]")), 25)
set_psi_rank(PSI_RANK_HARMONIOUS)
sleep(30)
addtimer(CALLBACK(psi, TYPE_PROC_REF(/datum/psi_complexus, check_psionic_trigger), 100, source, TRUE), 4.5 SECONDS)
/mob/living/carbon/human/get_resist_power()
return species.resist_mod
@@ -349,8 +346,7 @@
/mob/living/carbon/human/proc/pressure_resistant()
if(HAS_FLAG(mutations, COLD_RESISTANCE))
return TRUE
var/datum/changeling/changeling = get_antag_datum(MODE_CHANGELING)
if(changeling?.space_adapted)
if(HAS_TRAIT(src, TRAIT_PRESSURE_IMMUNITY))
return TRUE
return FALSE
@@ -418,7 +414,7 @@
/mob/living/carbon/human/proc/get_tail_accessory()
var/obj/item/organ/external/groin/G = organs_by_name[BP_GROIN]
if(!G)
if(!istype(G))
return
if(!G.tail_storage)
return
@@ -239,14 +239,16 @@
// Simple mobs cannot use Skrellepathy
/mob/proc/can_commune()
/mob/proc/has_psionics()
return FALSE
/mob/living/carbon/human/can_commune()
/mob/living/carbon/human/has_psionics()
if(HAS_TRAIT(src, TRAIT_PSIONICALLY_DEAF))
return FALSE
if(psi)
return TRUE
else
return species ? species.can_commune() : FALSE
return species ? species.has_psionics() : FALSE
/mob/living/carbon/human/proc/commune()
set category = "Abilities"
@@ -322,14 +324,14 @@
to_chat(M,"<span class='notice'>[src] telepathically says to [target]:</span> [text]")
var/mob/living/carbon/human/H = target
if (target.can_commune())
if (target.has_psionics())
to_chat(H,"<span class='psychic'>You instinctively sense [src] sending their thoughts into your mind, hearing:</span> [text]")
else if(prob(25) && (target.mind && target.mind.assigned_role=="Chaplain"))
to_chat(H,"<span class='changeling'>You sense [src]'s thoughts enter your mind, whispering quietly:</span> [text]")
else
to_chat(H,"<span class='alium'>You feel pressure behind your eyes as alien thoughts enter your mind:</span> [text]")
if(istype(H))
if (target.can_commune())
if (target.has_psionics())
return
if(prob(10) && !(H.species.flags & NO_BLOOD))
to_chat(H,"<span class='warning'>Your nose begins to bleed...</span>")
@@ -295,6 +295,13 @@
var/use_alt_hair_layer = FALSE
/// Species psionics. FALSE for no psionics. Otherwise, set to the PSI_RANK define you want.
var/has_psionics = FALSE
/// Number of psi points in character creation.
var/character_creation_psi_points = 0
/// Is this species psionically deaf?
var/psi_deaf = FALSE
/datum/species/proc/get_eyes(var/mob/living/carbon/human/H)
return
@@ -470,6 +477,8 @@
if(!H.client || !H.client.prefs || !H.client.prefs.gender)
H.gender = pick(default_genders)
H.pronouns = H.gender
if(has_psionics)
H.set_psi_rank(has_psionics)
/datum/species/proc/handle_death(var/mob/living/carbon/human/H, var/gibbed = 0) //Handles any species-specific death events (such as dionaea nymph spawns).
return
@@ -739,11 +748,8 @@
/datum/species/proc/get_digestion_product()
return /singleton/reagent/nutriment
/datum/species/proc/can_commune()
return FALSE
/datum/species/proc/has_psi_potential()
return TRUE
/datum/species/proc/has_psionics()
return has_psionics
/datum/species/proc/handle_despawn()
return
@@ -129,6 +129,7 @@
)
alterable_internal_organs = list()
psi_deaf = TRUE
/datum/species/diona/can_understand(var/mob/other)
var/mob/living/carbon/alien/diona/D = other
@@ -184,9 +185,6 @@
if(SB)
SB.handle_item_insertion(new /obj/item/device/flashlight/survival(get_turf(H)), TRUE)
/datum/species/diona/has_psi_potential()
return FALSE
/datum/species/diona/is_naturally_insulated()
return TRUE
@@ -98,6 +98,8 @@ var/global/list/golem_types = list(SPECIES_GOLEM_COAL,
hud_type = /datum/hud_data/construct
psi_deaf = TRUE
var/turn_into_materials = TRUE //the golem will turn into materials upon its death
var/golem_designation = "Coal" //used in the creation of the name
@@ -124,9 +126,6 @@ var/global/list/golem_types = list(SPECIES_GOLEM_COAL,
return TRUE
return FALSE
/datum/species/golem/has_psi_potential()
return FALSE
/datum/species/golem/iron
name = SPECIES_GOLEM_IRON
name_plural = "iron golems"
@@ -143,6 +143,7 @@
var/move_charge_factor = 1
use_alt_hair_layer = TRUE
psi_deaf = TRUE
/datum/species/machine/handle_post_spawn(var/mob/living/carbon/human/H)
. = ..()
@@ -373,9 +374,6 @@
. = ..()
check_tag(H, H.client)
/datum/species/machine/has_psi_potential()
return FALSE
/datum/species/machine/handle_death_check(var/mob/living/carbon/human/H)
if(H.get_total_health() <= config.health_threshold_dead)
return TRUE
@@ -107,6 +107,9 @@
alterable_internal_organs = list(BP_HEART, BP_EYES, BP_LUNGS, BP_LIVER, BP_KIDNEYS, BP_STOMACH)
has_psionics = PSI_RANK_SENSITIVE
character_creation_psi_points = 4
/datum/species/skrell/handle_trail(var/mob/living/carbon/human/H, var/turf/T)
var/list/trail_info = ..()
if(!length(trail_info) && !H.shoes)
@@ -117,10 +120,6 @@
return trail_info
/datum/species/skrell/handle_post_spawn(mob/living/carbon/human/H)
..()
H.set_psi_rank(PSI_COERCION, PSI_RANK_OPERANT)
/datum/species/skrell/handle_strip(var/mob/user, var/mob/living/carbon/human/H, var/action)
switch(action)
if("headtail")
@@ -142,6 +141,3 @@
/datum/species/skrell/can_breathe_water()
return TRUE
/datum/species/skrell/can_commune()
return TRUE
@@ -141,6 +141,7 @@
alterable_internal_organs = list(BP_HEART, BP_EYES, BP_LUNGS, BP_STOMACH, BP_APPENDIX)
psi_deaf = TRUE
/datum/species/bug/before_equip(var/mob/living/carbon/human/H)
. = ..()
@@ -158,9 +159,6 @@
H.gender = NEUTER
return ..()
/datum/species/bug/has_psi_potential()
return FALSE
/datum/species/bug/is_naturally_insulated()
return TRUE
@@ -168,3 +166,4 @@
if(I.w_class <= ITEMSIZE_SMALL)
return TRUE
return FALSE
+6 -2
View File
@@ -18,6 +18,10 @@
var/natural_armor = GetComponent(/datum/component/armor)
if(natural_armor)
. += natural_armor
if(psi)
var/armor = psi.GetComponent(/datum/component/armor/psionic)
if(armor)
. += armor
/mob/living/bullet_act(var/obj/item/projectile/P, var/def_zone, var/used_weapon = null)
@@ -247,7 +251,7 @@
// End BS12 momentum-transfer code.
/mob/living/attack_generic(var/mob/user, var/damage, var/attack_message, var/armor_penetration, var/attack_flags)
/mob/living/attack_generic(var/mob/user, var/damage, var/attack_message, var/armor_penetration, var/attack_flags, var/damage_type = DAMAGE_BRUTE)
if(!damage)
return
@@ -257,7 +261,7 @@
src.visible_message("<span class='danger'>[user] has [attack_message] [src]!</span>")
user.do_attack_animation(src)
apply_damage(damage, DAMAGE_BRUTE, user.zone_sel?.selecting, armor_pen = armor_penetration, damage_flags = attack_flags)
apply_damage(damage, damage_type, user.zone_sel?.selecting, armor_pen = armor_penetration, damage_flags = attack_flags)
updatehealth()
return TRUE
@@ -453,8 +453,8 @@
if(chemicals < 150)
to_chat(src, SPAN_WARNING("You don't have enough chemicals!"))
return
if(!host.species.has_psi_potential())
to_chat(src, SPAN_WARNING("\The [host] lacks a zona bovinidae to psionically enlighten! How disturbing..."))
if(host.species.psi_deaf)
to_chat(src, SPAN_WARNING("\The [host] lacks a zona bovinae to psionically enlighten! How disturbing..."))
if(host.psi)
to_chat(src, SPAN_WARNING("Your host is already psionically active!"))
return
@@ -481,13 +481,14 @@
return
to_chat(src, SPAN_NOTICE("You succeed in interfacing with the host's zona bovinae, this will be a painful process for them."))
host.awaken_psi_basic("something in your head", FALSE)
host.awaken_psi_basic("something in your head")
host.psi.psi_points = 3 /// You don't get a lot at the start.
host.add_language(LANGUAGE_TCB) // if we don't have TCB, give them TCB | this allows monkey borers to RP
/mob/living/simple_animal/borer/verb/advance_faculty()
/mob/living/simple_animal/borer/verb/advance_psionics()
set category = "Abilities"
set name = "Advance Psionic Faculty (75)"
set desc = "Advance one of your host's psionic faculties by one step."
set name = "Advance Psionics (75)"
set desc = "Advance your host's psionic capability by one step."
if(!host)
to_chat(src, SPAN_NOTICE("You are not inside a host body."))
@@ -507,18 +508,8 @@
to_chat(src, SPAN_WARNING("\The [host]'s mind is shielded against your powers."))
return
var/list/faculties = list(capitalize(PSI_COERCION), capitalize(PSI_REDACTION), capitalize(PSI_ENERGISTICS), capitalize(PSI_PSYCHOKINESIS))
var/selected_faculty = input(src, "Choose a faculty to upgrade.") as null|anything in faculties
if(!selected_faculty)
return
selected_faculty = lowertext(selected_faculty)
var/max_rank = islesserform(host) ? PSI_RANK_OPERANT : PSI_RANK_MASTER
if(host.psi.get_rank(selected_faculty) >= max_rank)
to_chat(src, SPAN_NOTICE("This faculty has already been pushed to the max potential you can achieve!"))
return
var/faculty_time = 10 SECONDS
if(!start_ability(host, faculty_time))
var/upgrade_time = 10 SECONDS
if(!start_ability(host, upgrade_time))
to_chat(src, SPAN_WARNING("You're busy doing something else, complete that task first."))
return
@@ -526,19 +517,16 @@
to_chat(src, SPAN_NOTICE("You probe your tendrils deep within your host's zona bovinae, seeking to upgrade their abilities."))
to_chat(host, SPAN_WARNING("You feel a burning, tingling sensation at the back of your head..."))
addtimer(CALLBACK(src, PROC_REF(faculty_upgrade), selected_faculty), faculty_time)
addtimer(CALLBACK(src, PROC_REF(upgrade_rank)), upgrade_time)
/mob/living/simple_animal/borer/proc/faculty_upgrade(var/selected_faculty)
/mob/living/simple_animal/borer/proc/upgrade_rank()
if(!host)
return
var/host_psi_rank = host.psi.get_rank(selected_faculty)
var/next_rank = host_psi_rank > PSI_RANK_BLUNT ? host_psi_rank + 1 : PSI_RANK_OPERANT
host.psi.set_rank(selected_faculty, next_rank)
host.psi.update(TRUE)
to_chat(src, SPAN_NOTICE("You successfully manage to upgrade your host to [psychic_ranks_to_strings[host.psi.ranks[selected_faculty]]] [selected_faculty]."))
host.psi.psi_points++
to_chat(src, SPAN_NOTICE("You successfully manage to expand your living host's control over their Zona Bovinae. They know have an extra psionic point to spend."))
to_chat(host, SPAN_GOOD("A breeze of fresh air washes over your mind, you feel powerful!"))
to_chat(host, SPAN_NOTICE("You have been psionically enlightened. You are now a [psychic_ranks_to_strings[host.psi.ranks[selected_faculty]]] in the [selected_faculty] faculty."))
to_chat(host, SPAN_NOTICE("Your control over the Nlom has expanded. You now have an extra psionic point to spend in the Point Shop."))
/mob/living/simple_animal/borer/verb/host_health_scan()
set category = "Abilities"
@@ -214,7 +214,7 @@
var/atom/target
if(isliving(target_mob))
var/mob/living/L = target_mob
on_attack_mob(L, L.attack_generic(src, rand(melee_damage_lower, melee_damage_upper), attacktext, armor_penetration, attack_flags))
on_attack_mob(L, L.attack_generic(src, rand(melee_damage_lower, melee_damage_upper), attacktext, armor_penetration, attack_flags, damage_type))
target = L
else if(istype(target_mob, /obj/machinery/bot))
var/obj/machinery/bot/B = target_mob
@@ -14,6 +14,9 @@
mob_swap_flags = MONKEY|SLIME|SIMPLE_ANIMAL
mob_push_flags = MONKEY|SLIME|SIMPLE_ANIMAL
/// The type of damage this mob deals.
var/damage_type = DAMAGE_BRUTE
var/show_stat_health = 1 //does the percentage health show in the stat panel for the mob
var/icon_living = ""
@@ -538,7 +541,7 @@
simple_harm_attack(user)
/mob/living/simple_animal/proc/simple_harm_attack(var/mob/living/user)
apply_damage(harm_intent_damage, DAMAGE_BRUTE, used_weapon = "Attack by [user.name]")
apply_damage(harm_intent_damage, damage_type, used_weapon = "Attack by [user.name]")
user.visible_message(SPAN_WARNING("<b>\The [user]</b> [response_harm] \the [src]!"), SPAN_WARNING("You [response_harm] \the [src]!"))
user.do_attack_animation(src, FIST_ATTACK_ANIMATION)
poke(TRUE)
-5
View File
@@ -348,11 +348,6 @@
if(!affecting)
return
if(ishuman(user) && affecting == M)
var/mob/living/carbon/human/H = user
if(H.check_psi_grab(src))
return
if(world.time < (last_action + 20))
return
+18
View File
@@ -284,8 +284,26 @@
var/laser = (damage_flags & DAMAGE_FLAG_LASER)
var/sharp = (damage_flags & DAMAGE_FLAG_SHARP)
var/edge = (damage_flags & DAMAGE_FLAG_EDGE)
var/psionic = HAS_FLAG(damage_flags, DAMAGE_FLAG_PSIONIC)
var/blunt = !!(brute && !sharp && !edge)
/// Psionics and psionically deaf species take varying amounts of damage from psionic abilities.
if(psionic)
if(!owner.has_psionics())
brute *= 0.9
burn *= 0.9
else if(owner.psi)
var/psi_rank = owner.psi.get_rank()
if(psi_rank == PSI_RANK_SENSITIVE)
brute *= 1.05
burn *= 1.05
else if(psi_rank == PSI_RANK_HARMONIOUS)
brute *= 1.1
burn *= 1.1
else if(psi_rank == PSI_RANK_APEX)
brute *= 1.2
burn *= 1.2
if(status & ORGAN_BROKEN && prob(40) && brute)
if(owner && (owner.can_feel_pain()) && owner.stat == CONSCIOUS)
owner.emote(pick("scream", "groan")) //getting hit on broken hand hurts
-1
View File
@@ -529,7 +529,6 @@
organ_tag = BP_AUG_COCHLEAR
parent_organ = BP_HEAD
// Snakebitten!
/obj/item/organ/internal/augment/psi
name = "psionic receiver"
desc = "An augment installed into the head that functions as a surrogate for a missing zona bovinae, also functioning as a filter for the psionically-challenged."
+1 -1
View File
@@ -112,7 +112,7 @@ Pins Below.
fail_message = "<span class='warning'>PSIONICS CHECK FAILED.</span>"
/obj/item/device/firing_pin/psionic/pin_auth(mob/living/user)
if(user.can_commune())
if(user.has_psionics())
return 1
else
return 0
@@ -469,15 +469,6 @@
if(isliving(target))
tesla_zap(target, 5, 5000)
/obj/item/projectile/beam/tesla/master
damage = 15
/obj/item/projectile/beam/tesla/grandmaster
damage = 20
/obj/item/projectile/beam/tesla/paramount
damage = 25
/obj/item/projectile/beam/freezer
name = "freezing ray"
icon_state = "bluelaser"
@@ -0,0 +1,29 @@
/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!
@@ -0,0 +1,58 @@
/singleton/psionic_power/air_bullet
name = "Air Bullet"
desc = "Wrap air in a psionic bubble, compress it, then send it flying at your enemies. Activate the spell in hand to create up to six air bullets, then \
fire them by clicking something."
icon_state = "tech_frostaura"
point_cost = 3
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/projectile/air_bullet
/obj/item/spell/projectile/air_bullet
name = "air bullet"
desc = "Not really a big bang attack, but it's actually pretty close."
icon_state = "force_missile"
cast_methods = CAST_RANGED|CAST_USE
aspect = ASPECT_PSIONIC
spell_projectile = /obj/item/projectile/air_bullet
fire_sound = 'sound/weapons/wave.ogg'
cooldown = 5
psi_cost = 0
var/bullets = 0
var/max_bullets = 6
/obj/item/spell/projectile/air_bullet/on_use_cast(mob/user)
. = ..()
if(!.)
return
if(!isliving(user))
return
var/mob/living/L = user
if(bullets < max_bullets)
if(do_after(L, 0.5 SECONDS))
to_chat(L, SPAN_NOTICE("You compress some air bullets between your fingers."))
bullets = min(bullets + 2, 6)
maptext = SMALL_FONTS(7, bullets)
playsound(L, 'sound/weapons/unjam.ogg', 25)
L.psi.spend_power(3)
if(bullets >= 6)
to_chat(L, SPAN_NOTICE("You finish compressing all the bullets you can hold."))
return
on_use_cast(L)
/obj/item/spell/projectile/air_bullet/on_ranged_cast(atom/hit_atom, mob/living/user, atom/pb_target)
if(!bullets)
to_chat(user, SPAN_WARNING("You flick your fingers, but find that you don't have any air bullets left!"))
return
. = ..(hit_atom, user, pb_target)
if(.)
bullets--
maptext = SMALL_FONTS(7, bullets)
/obj/item/projectile/air_bullet
name = "air bullet"
icon_state = "plasma_bolt"
damage = 30
armor_penetration = 10
impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_BULLET_MEAT, BULLET_IMPACT_METAL = SOUNDS_BULLET_METAL)
damage_flags = DAMAGE_FLAG_BULLET
damage_type = DAMAGE_BRUTE
+80
View File
@@ -0,0 +1,80 @@
/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?"))
@@ -0,0 +1,30 @@
/singleton/psionic_power/barrier
name = "Psionic Barrier"
desc = "Activate in hand to give yourself psionic armour. This armour is slightly worse than generic heavy armour, and has a small passive upkeep."
icon_state = "const_mend"
point_cost = 1
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/barrier
/obj/item/spell/barrier
name = "psionic barrier"
icon_state = "generic"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 20
psi_cost = 30
/obj/item/spell/barrier/on_use_cast(mob/user, bypass_psi_check)
if(!ishuman(user))
return
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = user
var/armor = H.psi.GetComponent(/datum/component/armor/psionic)
if(armor)
to_chat(H, SPAN_WARNING("You are already enveloped by psionic armour!"))
return
to_chat(H, SPAN_NOTICE("You are enveloped by a protective warmth."))
H.psi.AddComponent(/datum/component/armor/psionic, list(laser = ARMOR_LASER_MEDIUM, bullet = ARMOR_BALLISTIC_CARBINE, energy = ARMOR_ENERGY_RESISTANT, melee = ARMOR_MELEE_RESISTANT))
+33
View File
@@ -0,0 +1,33 @@
/singleton/psionic_power/bubble
name = "Bubble"
desc = "Create a protective bubble around you that removes your need to breathe or wear voidsuits in EVA."
icon_state = "tech_condensation"
point_cost = 1
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/bubble
/obj/item/spell/bubble
name = "bubble"
icon_state = "shield"
cast_methods = CAST_INNATE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 10
/obj/item/spell/bubble/Destroy()
to_chat(owner, SPAN_NOTICE("Your bubble fades away."))
REMOVE_TRAIT(owner, TRAIT_NO_BREATHE, TRAIT_SOURCE_PSIONICS)
REMOVE_TRAIT(owner, TRAIT_PRESSURE_IMMUNITY, TRAIT_SOURCE_PSIONICS)
return ..()
/obj/item/spell/bubble/on_innate_cast(mob/user)
if(!ishuman(user))
return
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = user
H.visible_message(SPAN_NOTICE("A protective bubble forms around [H]."), SPAN_NOTICE("You form a protective bubble around yourself."))
ADD_TRAIT(H, TRAIT_NO_BREATHE, TRAIT_SOURCE_PSIONICS)
ADD_TRAIT(H, TRAIT_PRESSURE_IMMUNITY, TRAIT_SOURCE_PSIONICS)
+32
View File
@@ -0,0 +1,32 @@
/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)
@@ -0,0 +1,78 @@
/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."]"))
@@ -0,0 +1,71 @@
/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]"))
@@ -0,0 +1,30 @@
/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!"))
@@ -0,0 +1,70 @@
/singleton/psionic_power/emotional_suggestion
name = "Emotional Suggestion"
desc = "Allows you to psionically commune with the target."
icon_state = "tech_gambit"
ability_flags = PSI_FLAG_EVENT|PSI_FLAG_CANON
spell_path = /obj/item/spell/emotional_suggestion
/obj/item/spell/emotional_suggestion
name = "emotional suggestion"
desc = "Suggest an emotion to someone."
icon_state = "generic"
cast_methods = CAST_RANGED|CAST_MELEE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 15
/obj/item/spell/emotional_suggestion/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(!.)
return
emotional_suggestion(hit_atom, user)
/obj/item/spell/emotional_suggestion/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(!.)
return
emotional_suggestion(hit_atom, user)
/obj/item/spell/emotional_suggestion/proc/emotional_suggestion(atom/hit_atom, mob/user)
if(!isliving(hit_atom))
return
var/mob/living/target = hit_atom
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("Not even a psion of your level can suggest to the dead."))
return
var/psi_blocked = target.is_psi_blocked()
if(psi_blocked)
to_chat(user, psi_blocked)
return
user.visible_message(SPAN_NOTICE("<i>[user] blinks, their eyes briefly developing an unnatural shine.</i>"))
var/text = input("Which emotion would you like to suggest?", "Emotional Suggestion") as null|anything in list("Calm", "Happiness", "Sadness", "Fear", "Anger", "Stress", "Confusion")
if(!text)
return
text = lowertext(text)
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("Not even a psion of your level can suggest to the dead."))
return
log_say("[key_name(user)] suggested an emotion to [key_name(target)]: [text]",ckey=key_name(src))
to_chat(user, SPAN_CULT("You psionically suggest an emotion to [target]: [text]"))
for (var/mob/M in player_list)
if (istype(M, /mob/abstract/new_player))
continue
else if(M.stat == DEAD && HAS_FLAG(M.client.prefs.toggles, CHAT_GHOSTEARS))
to_chat(M, "<span class='notice'>[user] psionically suggests an emotion to [target]:</span> [text]")
var/mob/living/carbon/human/H = target
if(H.has_psionics())
to_chat(H, SPAN_NOTICE("You feel an emotion of <b>[text]</b> washing through your mind."))
else if(target.has_psi_aug())
to_chat(H, SPAN_NOTICE("You sense [user]'s psyche link with your psi-receiver, and an emotion envelops your mind: <b>[text]</b>."))
else
to_chat(H, SPAN_NOTICE("An emotion from outside your consciousness slips into your mind: <b>[text]</b>."))
+28
View File
@@ -0,0 +1,28 @@
/singleton/psionic_power/focus
name = "Focus"
desc = "Activate this spell in your hand to generate five units of synaptizine in your bloodstream."
icon_state = "tech_phaseshift"
point_cost = 1
ability_flags = PSI_FLAG_EVENT
spell_path = /obj/item/spell/focus
/obj/item/spell/focus
name = "focus"
desc = "Psionic drugs? No way."
icon_state = "blink"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 50
psi_cost = 20
/obj/item/spell/focus/on_use_cast(mob/user)
if(!ishuman(user))
return
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = user
if(do_after(user, 1 SECOND))
to_chat(H, SPAN_WARNING("A calm rush envelops your mind.."))
H.reagents.add_reagent(/singleton/reagent/synaptizine, 5)
H.update_canmove()
+51
View File
@@ -0,0 +1,51 @@
/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
@@ -0,0 +1,106 @@
/singleton/psionic_power/hollow_purple
name = "Hollow Technique: Purple"
desc = "This technique brings the concept of motion and reversal into reality. Purple is born from merging both infinites: Blue and Red, \
to produce an imaginary mass that rushes forth and erases everything in its path. Rather than the attraction of Blue or the repulsion of Red, \
Purple is an extraordinarily destructive energy wave of annihilation that rips whatever it hits from existence."
icon_state = "hollow_purple"
point_cost = 0
ability_flags = PSI_FLAG_LIMITLESS
minimum_rank = PSI_FLAG_LIMITLESS
spell_path = /obj/item/spell/projectile/hollow_purple
/obj/item/spell/projectile/hollow_purple
name = "hollow purple"
desc = "Throughout Heaven and Earth, I alone am the honored one!"
icon_state = "destablize"
cast_methods = CAST_RANGED
aspect = ASPECT_PSIONIC
spell_projectile = /obj/item/projectile/hollow_purple
fire_sound = 'sound/magic/WandODeath.ogg'
cooldown = 10
psi_cost = 50
var/stage = 0
/obj/item/spell/projectile/hollow_purple/on_ranged_cast(atom/hit_atom, mob/living/user, atom/pb_target)
if(!isliving(user))
return
stage = 1
user.visible_message(SPAN_NOTICE("<b><font size=4>[user] puts [user.get_pronoun("his")] palms together...</font></b>"))
START_PROCESSING(SSprocessing, src)
user.say("Cursed Technique Amplification: Blue...")
user.set_light(7, 10, COLOR_BLUE)
color = COLOR_BLUE
if(do_after(user, 4 SECONDS))
stage = 2
color = COLOR_RED
user.say("Cursed Technique Reversal: Red...")
user.set_light(0)
user.set_light(7, 10, COLOR_RED)
if(do_after(user, 4 SECONDS))
user.say("Hollow Technique: Purple!")
user.set_light(0)
user.visible_message(SPAN_CULT("<b><font size=4>[user] fires a gigantic purple sphere from [user.get_pronoun("his")] hand!</font></b>"))
. = ..()
STOP_PROCESSING(SSprocessing, src)
color = initial(color)
user.set_light(0)
stage = 0
/obj/item/spell/projectile/hollow_purple/process()
if(stage >= 1)
for(var/mob/living/L in get_hearers_in_view(7, src))
shake_camera(L, 5, 5)
/obj/item/projectile/hollow_purple
name = "hollow purple"
icon_state = "hollow_purple"
color = COLOR_PURPLE
damage = 1000
armor_penetration = 1000
penetrating = 100
range = 250
accuracy = 100
anti_materiel_potential = 100
damage_type = DAMAGE_BRUTE
/obj/item/projectile/hollow_purple/Initialize()
. = ..()
set_light(10, 10, COLOR_PURPLE)
/obj/item/projectile/hollow_purple/Destroy()
return ..()
/obj/item/projectile/hollow_purple/on_impact(var/atom/A)
if(ismob(A))
if(A != firer)
var/mob/M = A
M.gib()
explosion(A, 5, 5, 5)
..()
/obj/item/projectile/hollow_purple/on_hit(atom/target, blocked, def_zone)
if(ismob(target))
if(target != firer)
var/mob/M = target
M.gib()
explosion(target, 5, 5, 5)
..()
/obj/item/projectile/hollow_purple/check_penetrate(atom/A)
on_hit(A)
return TRUE
/obj/item/projectile/hollow_purple/after_move()
for(var/a in range(3, src))
if(isliving(a) && a != firer)
var/mob/living/M = a
M.gib()
playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
else if(isturf(a) || isobj(a))
var/atom/A = a
if(!A.density)
continue
A.ex_act(3)
playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
+24
View File
@@ -0,0 +1,24 @@
/singleton/psionic_power/ion
name = "Ion Blast"
desc = "Activate in hand to release a 3x3 ion blast around you. Be careful, this ability is expensive to use."
icon_state = "const_mend"
point_cost = 3
ability_flags = PSI_FLAG_ANTAG
spell_path = /obj/item/spell/ion
/obj/item/spell/ion
name = "ion blast"
icon_state = "generic"
cast_methods = CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 20
psi_cost = 30
/obj/item/spell/ion/on_use_cast(mob/user, bypass_psi_check)
. = ..()
if(!.)
return
user.visible_message(SPAN_DANGER("<font size=4>[user] opens their arms and releases an ion blast around them!</font>"),
SPAN_DANGER("<font size=4>You open your arms and release an ion blast around you!</font>"))
empulse(get_turf(user), 3, 3)
+22
View File
@@ -0,0 +1,22 @@
/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)
@@ -0,0 +1,59 @@
/singleton/psionic_power/lightning
name = "Lightning"
desc = "Fire a concentrated lightning bolt. Activate this spell in hand to switch to a second mode that hits living beings in a 4x3 area in front of you."
icon_state = "tech_instabilitytapold"
point_cost = 0
ability_flags = PSI_FLAG_APEX
minimum_rank = PSI_RANK_APEX
spell_path = /obj/item/spell/projectile/psi_lightning
/obj/item/spell/projectile/psi_lightning
name = "lightning"
icon_state = "chain_lightning"
cast_methods = CAST_RANGED|CAST_USE
aspect = ASPECT_PSIONIC
spell_projectile = /obj/item/projectile/beam/psi_lightning
fire_sound = 'sound/magic/LightningShock.ogg'
cooldown = 10
psi_cost = 15
var/mode = 0
/obj/item/spell/projectile/psi_lightning/on_use_cast(mob/user, bypass_psi_check)
. = ..(user, TRUE)
mode = !mode
if(mode)
to_chat(user, SPAN_NOTICE("You will now fire area-of-effect lightning in a 4x3 area in front of you."))
spell_projectile = /obj/item/projectile/beam/psi_lightning/wide
else
to_chat(user, SPAN_NOTICE("You will now fire a normal lightning bolt."))
spell_projectile = /obj/item/projectile/beam/psi_lightning
/obj/item/projectile/beam/psi_lightning
name = "psionic lightning"
damage = 30
armor_penetration = 30
damage_type = DAMAGE_BURN
pass_flags = PASSTABLE | PASSGRILLE | PASSRAILING
range = 40
accuracy = 100
muzzle_type = /obj/effect/projectile/muzzle/tesla
tracer_type = /obj/effect/projectile/tracer/tesla
impact_type = /obj/effect/projectile/impact/tesla
/obj/item/projectile/beam/psi_lightning/pellet
damage = 20
armor_penetration = 20
/obj/item/projectile/beam/psi_lightning/wide
damage = 20
armor_penetration = 20
/obj/item/projectile/beam/psi_lightning/wide/Initialize()
for(var/i = 1 to 4)
var/turf/new_turf = get_random_turf_in_range(get_turf(firer), i + rand(0, i), 0, TRUE, FALSE)
var/obj/item/projectile/beam/psi_lightning/pellet/pellet = new type(new_turf)
var/turf/front_turf = get_step(pellet, pellet.dir)
pellet.launch_projectile(front_turf)
+69
View File
@@ -0,0 +1,69 @@
/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."))
@@ -0,0 +1,29 @@
/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)
@@ -0,0 +1,53 @@
/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 ..()
@@ -0,0 +1,76 @@
/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"
@@ -0,0 +1,43 @@
/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."))
@@ -0,0 +1,57 @@
/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!"))
@@ -0,0 +1,31 @@
/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)
@@ -0,0 +1,37 @@
/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)
@@ -0,0 +1,68 @@
/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)]."))
@@ -0,0 +1,27 @@
/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

Some files were not shown because too many files have changed in this diff Show More