mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-01-04 14:33:10 +00:00
Ports a psionic system from Bay. (#7717)
* Ports a psionic system from Bay. * Rip out this shitcode. * shitcoden't * fixes * it should work fully now * Admin fixes * Remove todos * remove todos part 2 * Removes psi-armour. We don't need this for now. * Skrell are now operants. Tweaks coercion. * Adds thralls. * Temp removal of psiarmour. Fixes psigrabs. * Thrall assay. * fixes * More fixes * unused define cleanup * Log and powers * Skrell powers are done. * Update code/modules/psionics/events/mini_spasm.dm Co-Authored-By: Geeves <ggrobler447@gmail.com> * Update code/modules/psionics/events/mini_spasm.dm Co-Authored-By: Geeves <ggrobler447@gmail.com> * Update code/modules/psionics/equipment/cerebro_enhancers.dm * did this work??? * jargon * arrow's fixes Co-authored-by: Geeves <ggrobler447@gmail.com>
This commit is contained in:
@@ -93,6 +93,26 @@ proc/admin_notice(var/message, var/rights)
|
||||
<A href='?src=\ref[src];subtlemessage=\ref[M]'>Subtle message</A>
|
||||
"}
|
||||
|
||||
body += "<br><br>"
|
||||
body += "<b>Psionics:</b><br/>"
|
||||
if(isliving(M))
|
||||
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>"
|
||||
body += "</table>"
|
||||
|
||||
if (M.client)
|
||||
if(!istype(M, /mob/abstract/new_player))
|
||||
body += "<br><br>"
|
||||
|
||||
@@ -573,6 +573,18 @@
|
||||
Game() // updates the main game menu
|
||||
.(href, list("f_secret"=1))
|
||||
|
||||
else if(href_list["remove_psionics"])
|
||||
var/datum/psi_complexus/psi = locate(href_list["remove_psionics"])
|
||||
if(psi && psi.owner && !QDELETED(psi))
|
||||
to_chat(psi.owner, span("notice", "<b>Your psionic powers vanish abruptly, leaving you cold and empty.</b>"))
|
||||
log_and_message_admins("removed all psionics from [key_name(psi.owner)].")
|
||||
QDEL_NULL(psi)
|
||||
|
||||
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)
|
||||
|
||||
else if(href_list["monkeyone"])
|
||||
if(!check_rights(R_SPAWN)) return
|
||||
|
||||
@@ -1075,51 +1087,6 @@
|
||||
|
||||
return
|
||||
|
||||
else if(href_list["SolGovFaxReply"])
|
||||
//TODO
|
||||
/*
|
||||
var/mob/living/carbon/human/H = locate(href_list["SolGovFaxReply"])
|
||||
var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"])
|
||||
|
||||
var/input = input(src.owner, "Please enter a message to reply to [key_name(H)] via secure connection. NOTE: BBCode does not work, but HTML tags do! Use <br> for line breaks.", "Outgoing message from Centcomm", "") as message|null
|
||||
if(!input) return
|
||||
|
||||
var/customname = input(src.owner, "Pick a title for the report", "Title") as text|null
|
||||
|
||||
for(var/obj/machinery/photocopier/faxmachine/F in machines)
|
||||
if(F == fax)
|
||||
if(! (F.stat & (BROKEN|NOPOWER) ) )
|
||||
|
||||
// animate! it's alive!
|
||||
flick("faxreceive", F)
|
||||
|
||||
// give the sprite some time to flick
|
||||
spawn(20)
|
||||
var/obj/item/paper/P = new /obj/item/paper( F.loc )
|
||||
P.name = "Sol Government- [customname]"
|
||||
P.info = input
|
||||
P.update_icon()
|
||||
|
||||
playsound(F.loc, "sound/items/polaroid1.ogg", 50, 1)
|
||||
|
||||
// Stamps
|
||||
var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
|
||||
stampoverlay.icon_state = "paper_stamp-cap"
|
||||
if(!P.stamped)
|
||||
P.stamped = new
|
||||
P.stamped += /obj/item/stamp
|
||||
P.overlays += stampoverlay
|
||||
P.stamps += "<HR><i>This paper has been stamped and encrypted by the Sol Government Quantum Relay.</i>"
|
||||
|
||||
to_chat(src.owner, "Message reply to transmitted successfully.")
|
||||
log_admin("[key_name(src.owner)] replied to a fax message from [key_name(H)]: [input]")
|
||||
message_admins("[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(H)]", 1)
|
||||
return
|
||||
to_chat(src.owner, "/red Unable to locate fax!")
|
||||
*/
|
||||
|
||||
|
||||
|
||||
else if(href_list["jumpto"])
|
||||
if(!check_rights(R_ADMIN)) return
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
if(ispath(objholder,/mob) && !check_rights(R_DEBUG,0))
|
||||
objholder = /obj/structure/closet
|
||||
if(3)
|
||||
var/list/locked = list("vars", "key", "ckey", "client", "firemut", "ishulk", "telekinesis", "xray", "virus", "viruses", "cuffed", "ka", "last_eaten", "urine")
|
||||
var/list/locked = list("vars", "key", "ckey", "client", "firemut", "ishulk", "xray", "virus", "viruses", "cuffed", "ka", "last_eaten", "urine")
|
||||
|
||||
master.buildmode.varholder = input(usr,"Enter variable name:" ,"Name", "name")
|
||||
if(master.buildmode.varholder in locked && !check_rights(R_DEBUG,0))
|
||||
|
||||
@@ -588,7 +588,7 @@
|
||||
check_health()
|
||||
return
|
||||
|
||||
/obj/machinery/portable_atmospherics/hydroponics/attack_tk(mob/user as mob)
|
||||
/obj/machinery/portable_atmospherics/hydroponics/do_simple_ranged_interaction(var/mob/user)
|
||||
if(dead)
|
||||
remove_dead(user)
|
||||
else if(harvest)
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
var/list/language_progress
|
||||
var/obj/item/clothing/head/hat
|
||||
var/datum/reagents/vessel
|
||||
var/list/internal_organs_by_name = list() // so internal organs have less ickiness too
|
||||
var/energy_duration = 144 // The time in seconds that this diona can exist in total darkness before its energy runs out
|
||||
var/dark_consciousness = 144 // How long this diona can stay on its feet and keep moving in darkness after energy is gone.
|
||||
var/dark_survival = 216 // How long this diona can survive in darkness after energy is gone, before it dies
|
||||
|
||||
@@ -448,4 +448,7 @@
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/proc/get_ingested_reagents()
|
||||
return reagents
|
||||
return reagents
|
||||
|
||||
/mob/living/carbon/proc/should_have_organ(var/organ_check)
|
||||
return 0
|
||||
|
||||
@@ -32,4 +32,6 @@
|
||||
var/coughedtime = null // should only be useful for carbons as the only thing using it has a carbon arg.
|
||||
|
||||
var/willfully_sleeping = 0
|
||||
var/consume_nutrition_from_air = FALSE // used by Diona
|
||||
var/consume_nutrition_from_air = FALSE // used by Diona
|
||||
var/list/organs_by_name = list() // map organ names to organs
|
||||
var/list/internal_organs_by_name = list() // so internal organs have less ickiness too
|
||||
@@ -1821,7 +1821,7 @@
|
||||
return TRUE
|
||||
return species.handle_death_check(src)
|
||||
|
||||
/mob/living/carbon/human/proc/should_have_organ(var/organ_check)
|
||||
/mob/living/carbon/human/should_have_organ(var/organ_check)
|
||||
return (species?.has_organ[organ_check])
|
||||
|
||||
/mob/living/carbon/human/proc/resuscitate()
|
||||
@@ -1845,13 +1845,6 @@
|
||||
if(stat == CONSCIOUS)
|
||||
reagents.add_reagent("adrenaline", amount)
|
||||
|
||||
/mob/living/carbon/human/proc/seizure()
|
||||
if(!paralysis && stat == CONSCIOUS)
|
||||
visible_message("<span class='danger'>\The [src] starts having a seizure!</span>")
|
||||
Paralyse(rand(8,16))
|
||||
make_jittery(rand(150,200))
|
||||
adjustHalLoss(rand(50,60))
|
||||
|
||||
/mob/living/carbon/human/proc/gigashatter()
|
||||
for(var/obj/item/organ/external/E in organs)
|
||||
E.fracture()
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
/mob/living/carbon/var/list/internal_organs = list()
|
||||
/mob/living/carbon/var/shock_stage = 0
|
||||
/mob/living/carbon/human/var/list/organs = list()
|
||||
/mob/living/carbon/human/var/list/organs_by_name = list() // map organ names to organs
|
||||
/mob/living/carbon/human/var/list/internal_organs_by_name = list() // so internal organs have less ickiness too
|
||||
|
||||
/mob/living/carbon/human/proc/recheck_bad_external_organs()
|
||||
var/damage_this_tick = getToxLoss()
|
||||
|
||||
@@ -309,7 +309,7 @@ mob/living/carbon/human/proc/change_monitor()
|
||||
if(prob(10) && !(H.species.flags & NO_BLOOD))
|
||||
to_chat(H,"<span class='warning'>Your nose begins to bleed...</span>")
|
||||
H.drip(3)
|
||||
else if(prob(25) && (can_feel_pain()))
|
||||
else if(prob(25) && (H.can_feel_pain()))
|
||||
to_chat(H,"<span class='warning'>Your head hurts...</span>")
|
||||
else if(prob(50))
|
||||
to_chat(H,"<span class='warning'>Your mind buzzes...</span>")
|
||||
|
||||
@@ -885,8 +885,8 @@
|
||||
//Fire and Brute damage overlay (BSSR)
|
||||
var/hurtdamage = src.getBruteLoss() + src.getFireLoss() + damageoverlaytemp
|
||||
damageoverlaytemp = 0 // We do this so we can detect if someone hits us or not.
|
||||
var/ovr
|
||||
if(hurtdamage)
|
||||
var/ovr
|
||||
switch(hurtdamage)
|
||||
if(10 to 25)
|
||||
ovr = "brutedamageoverlay1"
|
||||
@@ -901,10 +901,10 @@
|
||||
if(85 to INFINITY)
|
||||
ovr = "brutedamageoverlay6"
|
||||
|
||||
if (last_brute_overlay != ovr)
|
||||
damageoverlay.cut_overlay(last_brute_overlay)
|
||||
damageoverlay.add_overlay(ovr)
|
||||
last_brute_overlay = ovr
|
||||
if(last_brute_overlay != ovr)
|
||||
damageoverlay.cut_overlay(last_brute_overlay)
|
||||
damageoverlay.add_overlay(ovr)
|
||||
last_brute_overlay = ovr
|
||||
|
||||
if(healths)
|
||||
healths.overlays.Cut()
|
||||
|
||||
@@ -52,11 +52,6 @@
|
||||
stamina = 90
|
||||
sprint_speed_factor = 1.25 //Evolved for rapid escapes from predators
|
||||
|
||||
inherent_verbs = list(
|
||||
/mob/living/carbon/human/proc/commune,
|
||||
/mob/living/carbon/human/proc/sonar_ping,
|
||||
)
|
||||
|
||||
default_h_style = "Skrell Short Tentacles"
|
||||
|
||||
allowed_citizenships = list(CITIZENSHIP_JARGON, CITIZENSHIP_BIESEL, CITIZENSHIP_SOL, CITIZENSHIP_FRONTIER, CITIZENSHIP_ELYRA, CITIZENSHIP_ERIDANI, CITIZENSHIP_DOMINIA)
|
||||
@@ -65,5 +60,8 @@
|
||||
|
||||
zombie_type = "Skrell Zombie"
|
||||
|
||||
/datum/species/skrell/handle_post_spawn(mob/living/carbon/human/H)
|
||||
H.set_psi_rank(PSI_COERCION, PSI_RANK_OPERANT)
|
||||
|
||||
/datum/species/skrell/can_breathe_water()
|
||||
return TRUE
|
||||
|
||||
@@ -417,20 +417,6 @@ There are several things that need to be remembered:
|
||||
add_image = 1
|
||||
for(var/mut in mutations)
|
||||
switch(mut)
|
||||
/*
|
||||
if(HULK)
|
||||
if(fat)
|
||||
standing.underlays += "hulk_[fat]_s"
|
||||
else
|
||||
standing.underlays += "hulk_[g]_s"
|
||||
add_image = 1
|
||||
if(COLD_RESISTANCE)
|
||||
standing.underlays += "fire[fat]_s"
|
||||
add_image = 1
|
||||
if(TK)
|
||||
standing.underlays += "telekinesishead[fat]_s"
|
||||
add_image = 1
|
||||
*/
|
||||
if(LASER_EYES)
|
||||
standing.overlays += "lasereyes_s"
|
||||
add_image = 1
|
||||
|
||||
@@ -489,6 +489,37 @@ default behaviour is:
|
||||
|
||||
return
|
||||
|
||||
/mob/living/proc/basic_revival(var/repair_brain = TRUE)
|
||||
|
||||
if(repair_brain && getBrainLoss() > 50)
|
||||
repair_brain = FALSE
|
||||
setBrainLoss(50)
|
||||
|
||||
if(stat == DEAD)
|
||||
switch_from_dead_to_living_mob_list()
|
||||
timeofdeath = 0
|
||||
|
||||
stat = CONSCIOUS
|
||||
regenerate_icons()
|
||||
|
||||
BITSET(hud_updateflag, HEALTH_HUD)
|
||||
BITSET(hud_updateflag, STATUS_HUD)
|
||||
BITSET(hud_updateflag, LIFE_HUD)
|
||||
|
||||
failed_last_breath = 0 //So mobs that died of oxyloss don't revive and have perpetual out of breath.
|
||||
|
||||
/mob/living/carbon/basic_revival(var/repair_brain = TRUE)
|
||||
if(repair_brain && should_have_organ(BP_BRAIN))
|
||||
repair_brain = FALSE
|
||||
var/obj/item/organ/internal/brain/brain = internal_organs_by_name[BP_BRAIN]
|
||||
if(brain.damage > (brain.max_damage/2))
|
||||
brain.damage = (brain.max_damage/2)
|
||||
if(brain.status & ORGAN_DEAD)
|
||||
brain.status &= ~ORGAN_DEAD
|
||||
START_PROCESSING(SSprocessing, brain)
|
||||
brain.update_icon()
|
||||
..(repair_brain)
|
||||
|
||||
/mob/living/proc/UpdateDamageIcon()
|
||||
return
|
||||
|
||||
@@ -844,3 +875,11 @@ default behaviour is:
|
||||
if (size_reagent > src.composition_reagent_quantity)//We take the larger of the two
|
||||
src.composition_reagent_quantity = size_reagent
|
||||
#undef PPM
|
||||
|
||||
|
||||
/mob/living/proc/seizure()
|
||||
if(!paralysis && stat == CONSCIOUS)
|
||||
visible_message("<span class='danger'>\The [src] starts having a seizure!</span>")
|
||||
Paralyse(rand(8,16))
|
||||
make_jittery(rand(150,200))
|
||||
adjustHalLoss(rand(50,60))
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
//var/jetpack = 0
|
||||
var/obj/item/tank/jetpack/carbondioxide/synthetic/jetpack = null
|
||||
var/datum/effect/effect/system/ion_trail_follow/ion_trail = null
|
||||
var/datum/effect_system/sparks/spark_system//So they can initialize sparks whenever/N
|
||||
var/datum/effect_system/sparks/spark_system //So they can initialize sparks whenever/N
|
||||
var/jeton = 0
|
||||
var/killswitch = 0
|
||||
var/killswitch_time = 60
|
||||
|
||||
@@ -409,7 +409,7 @@
|
||||
break_stuff_probability = 100//Constantly smashing everything nearby
|
||||
speak_chance = 15
|
||||
var/idletime
|
||||
var/focus_time//How long we've focused on this target
|
||||
var/focus_time //How long we've focused on this target
|
||||
var/teleport_delay = 60
|
||||
var/tactical_delay = 3//Procs between shortrange teleports
|
||||
var/datum/effect_system/sparks/spark_system
|
||||
|
||||
@@ -372,12 +372,15 @@
|
||||
if (W)
|
||||
W.attack_self(src)
|
||||
update_inv_l_hand()
|
||||
else
|
||||
attack_empty_hand(BP_L_HAND)
|
||||
else
|
||||
var/obj/item/W = r_hand
|
||||
if (W)
|
||||
W.attack_self(src)
|
||||
update_inv_r_hand()
|
||||
return
|
||||
else
|
||||
attack_empty_hand(BP_R_HAND)
|
||||
|
||||
/mob/verb/memory()
|
||||
set name = "Notes"
|
||||
@@ -996,6 +999,22 @@
|
||||
/mob/proc/embedded_needs_process()
|
||||
return (embedded.len > 0)
|
||||
|
||||
/mob/proc/remove_implant(var/obj/item/implant, var/surgical_removal = FALSE)
|
||||
if(!LAZYLEN(get_visible_implants(0))) //Yanking out last object - removing verb.
|
||||
verbs -= /mob/proc/yank_out_object
|
||||
for(var/obj/item/O in pinned)
|
||||
if(O == implant)
|
||||
pinned -= O
|
||||
if(!pinned.len)
|
||||
anchored = 0
|
||||
implant.dropInto(loc)
|
||||
implant.add_blood(src)
|
||||
implant.update_icon()
|
||||
if(istype(implant,/obj/item/implant))
|
||||
var/obj/item/implant/imp = implant
|
||||
imp.removed()
|
||||
. = TRUE
|
||||
|
||||
mob/proc/yank_out_object()
|
||||
set category = "Object"
|
||||
set name = "Yank out object"
|
||||
|
||||
@@ -314,6 +314,12 @@
|
||||
/obj/item/grab/attack(mob/M, mob/living/user, var/target_zone)
|
||||
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
|
||||
|
||||
|
||||
@@ -90,5 +90,3 @@
|
||||
. = min(., loc.contents_nano_distance(src_object, src))
|
||||
else
|
||||
. = min(., shared_living_nano_distance(src_object))
|
||||
if(. == STATUS_UPDATE && (TK in mutations)) // If we have telekinesis and remain close enough, allow interaction.
|
||||
return STATUS_INTERACTIVE
|
||||
|
||||
@@ -65,23 +65,6 @@
|
||||
|
||||
return
|
||||
|
||||
/obj/structure/filingcabinet/attack_tk(mob/user)
|
||||
if(anchored)
|
||||
attack_self_tk(user)
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/structure/filingcabinet/attack_self_tk(mob/user)
|
||||
if(contents.len)
|
||||
if(prob(40 + contents.len * 5))
|
||||
var/obj/item/I = pick(contents)
|
||||
I.forceMove(loc)
|
||||
if(prob(25))
|
||||
step_rand(I)
|
||||
to_chat(user, "<span class='notice'>You pull \a [I] out of [src] at random.</span>")
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You find nothing in [src].</span>")
|
||||
|
||||
/obj/structure/filingcabinet/Topic(href, href_list)
|
||||
if(href_list["retrieve"])
|
||||
usr << browse("", "window=filingcabinet") // Close the menu)
|
||||
@@ -138,7 +121,7 @@ Important Notes:<BR>
|
||||
populate()
|
||||
..()
|
||||
|
||||
/obj/structure/filingcabinet/security/attack_tk()
|
||||
/obj/structure/filingcabinet/security/do_simple_ranged_interaction(var/mob/user)
|
||||
populate()
|
||||
..()
|
||||
|
||||
@@ -185,6 +168,6 @@ Important Notes:<BR>
|
||||
populate()
|
||||
..()
|
||||
|
||||
/obj/structure/filingcabinet/medical/attack_tk()
|
||||
/obj/structure/filingcabinet/medical/do_simple_ranged_interaction(var/mob/user)
|
||||
populate()
|
||||
..()
|
||||
|
||||
@@ -118,7 +118,7 @@ var/list/possible_cable_coil_colours = list(
|
||||
alpha = invisibility ? 127 : 255
|
||||
|
||||
//Telekinesis has no effect on a cable
|
||||
/obj/structure/cable/attack_tk(mob/user)
|
||||
/obj/structure/cable/do_simple_ranged_interaction(var/mob/user)
|
||||
return
|
||||
|
||||
// Items usable on a cable :
|
||||
|
||||
@@ -449,8 +449,6 @@
|
||||
|
||||
if(prot || (COLD_RESISTANCE in user.mutations))
|
||||
to_chat(user, "You remove the light [fitting]")
|
||||
else if(TK in user.mutations)
|
||||
to_chat(user, "You telekinetically remove the light [fitting].")
|
||||
else
|
||||
to_chat(user, "You try to remove the light [fitting], but it's too hot and you don't want to burn your hand.")
|
||||
return // if burned, don't remove the light
|
||||
@@ -481,7 +479,7 @@
|
||||
stat |= MAINT
|
||||
update()
|
||||
|
||||
/obj/machinery/light/attack_tk(mob/user)
|
||||
/obj/machinery/light/do_simple_ranged_interaction(var/mob/user)
|
||||
if(status == LIGHT_EMPTY)
|
||||
to_chat(user, "There is no [fitting] in this light.")
|
||||
return
|
||||
|
||||
92
code/modules/psionics/complexus/complexus.dm
Normal file
92
code/modules/psionics/complexus/complexus.dm
Normal file
@@ -0,0 +1,92 @@
|
||||
/datum/psi_complexus
|
||||
|
||||
var/announced = FALSE // Whether or not we have been announced to our holder yet.
|
||||
var/suppressed = TRUE // Whether or not we are suppressing our psi powers.
|
||||
var/use_psi_armour = TRUE // Whether or not we should automatically deflect/block incoming damage.
|
||||
var/rebuild_power_cache = TRUE // Whether or not we need to rebuild our cache of psi powers.
|
||||
|
||||
var/rating = 0 // Overall psi rating.
|
||||
var/cost_modifier = 1 // Multiplier for power use stamina costs.
|
||||
var/stun = 0 // Number of process ticks we are stunned for.
|
||||
var/next_power_use = 0 // world.time minimum before next power use.
|
||||
var/stamina = 50 // Current psi pool.
|
||||
var/max_stamina = 50 // Max psi pool.
|
||||
|
||||
var/list/latencies // List of all currently latent faculties.
|
||||
var/list/ranks // Assoc list of psi faculties to current rank.
|
||||
var/list/base_ranks // Assoc list of psi faculties to base rank, in case reset is needed
|
||||
var/list/manifested_items // List of atoms manifested/maintained by psychic power.
|
||||
var/next_latency_trigger = 0 // world.time minimum before a trigger can be attempted again.
|
||||
var/last_armor_check // world.time of last armour check.
|
||||
var/last_aura_size
|
||||
var/last_aura_alpha
|
||||
var/last_aura_color
|
||||
var/aura_color = "#ff0022"
|
||||
|
||||
// Cached powers.
|
||||
var/list/melee_powers // Powers used in melee range.
|
||||
var/list/grab_powers // Powers use by using a grab.
|
||||
var/list/ranged_powers // Powers used at range.
|
||||
var/list/manifestation_powers // Powers that create an item.
|
||||
var/list/powers_by_faculty // All powers within a given faculty.
|
||||
|
||||
var/obj/screen/psi/hub/ui // Reference to the master psi UI object.
|
||||
var/mob/living/owner // Reference to our owner.
|
||||
var/image/_aura_image // Client image
|
||||
|
||||
/datum/psi_complexus/proc/get_aura_image()
|
||||
if(_aura_image && !istype(_aura_image))
|
||||
var/atom/A = _aura_image
|
||||
log_debug("Non-image found in psi complexus: \ref[A] - \the [A] - [istype(A) ? A.type : "non-atom"]")
|
||||
destroy_aura_image(_aura_image)
|
||||
_aura_image = null
|
||||
if(!_aura_image)
|
||||
_aura_image = create_aura_image(owner)
|
||||
return _aura_image
|
||||
|
||||
/proc/create_aura_image(var/newloc)
|
||||
var/image/aura_image = image(loc = newloc, icon = 'icons/effects/psi_aura_small.dmi', icon_state = "aura")
|
||||
aura_image.blend_mode = BLEND_MULTIPLY
|
||||
aura_image.appearance_flags = NO_CLIENT_COLOR | RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM
|
||||
aura_image.layer = TURF_LAYER + 0.5
|
||||
aura_image.alpha = 0
|
||||
aura_image.pixel_x = -64
|
||||
aura_image.pixel_y = -64
|
||||
aura_image.mouse_opacity = 0
|
||||
aura_image.appearance_flags = 0
|
||||
for(var/thing in SSpsi.processing)
|
||||
var/datum/psi_complexus/psychic = thing
|
||||
if(psychic.owner.client && !psychic.suppressed)
|
||||
psychic.owner.client.images += aura_image
|
||||
SSpsi.all_aura_images[aura_image] = TRUE
|
||||
return aura_image
|
||||
|
||||
/proc/destroy_aura_image(var/image/aura_image)
|
||||
for(var/thing in SSpsi.processing)
|
||||
var/datum/psi_complexus/psychic = thing
|
||||
if(psychic.owner.client)
|
||||
psychic.owner.client.images -= aura_image
|
||||
SSpsi.all_aura_images -= aura_image
|
||||
|
||||
/datum/psi_complexus/New(var/mob/_owner)
|
||||
owner = _owner
|
||||
START_PROCESSING(SSpsi, src)
|
||||
|
||||
/datum/psi_complexus/Destroy()
|
||||
destroy_aura_image(_aura_image)
|
||||
STOP_PROCESSING(SSpsi, src)
|
||||
if(owner)
|
||||
cancel()
|
||||
if(owner.client)
|
||||
owner.client.screen -= ui
|
||||
for(var/thing in SSpsi.all_aura_images)
|
||||
owner.client.images -= thing
|
||||
QDEL_NULL(ui)
|
||||
owner.psi = null
|
||||
owner = null
|
||||
|
||||
if(manifested_items)
|
||||
for(var/thing in manifested_items)
|
||||
qdel(thing)
|
||||
manifested_items.Cut()
|
||||
. = ..()
|
||||
104
code/modules/psionics/complexus/complexus_helpers.dm
Normal file
104
code/modules/psionics/complexus/complexus_helpers.dm
Normal file
@@ -0,0 +1,104 @@
|
||||
/datum/psi_complexus/proc/cancel()
|
||||
sound_to(owner, sound('sound/effects/psi/power_fail.ogg'))
|
||||
if(LAZYLEN(manifested_items))
|
||||
for(var/thing in manifested_items)
|
||||
owner.drop_from_inventory(thing)
|
||||
qdel(thing)
|
||||
manifested_items = null
|
||||
|
||||
/datum/psi_complexus/proc/stunned(var/amount)
|
||||
var/old_stun = stun
|
||||
stun = max(stun, amount)
|
||||
if(amount && !old_stun)
|
||||
to_chat(owner, "<span class='danger'>Your concentration has been shattered! You cannot focus your psi power!</span>")
|
||||
ui.update_icon()
|
||||
cancel()
|
||||
|
||||
/datum/psi_complexus/proc/get_armour(var/armourtype)
|
||||
if(can_use_passive())
|
||||
last_armor_check = world.time
|
||||
return round(Clamp(Clamp(4 * rating, 0, 20) * get_rank(SSpsi.armour_faculty_by_type[armourtype]), 0, 100) * (stamina/max_stamina))
|
||||
else
|
||||
last_armor_check = 0
|
||||
return 0
|
||||
|
||||
/datum/psi_complexus/proc/get_rank(var/faculty)
|
||||
return LAZYACCESS(ranks, faculty)
|
||||
|
||||
/datum/psi_complexus/proc/set_rank(var/faculty, var/rank, var/defer_update, var/temporary)
|
||||
if(get_rank(faculty) != rank)
|
||||
LAZYSET(ranks, faculty, rank)
|
||||
if(!temporary)
|
||||
LAZYSET(base_ranks, faculty, rank)
|
||||
if(!defer_update)
|
||||
update()
|
||||
|
||||
/datum/psi_complexus/proc/set_cooldown(var/value)
|
||||
next_power_use = world.time + value
|
||||
ui.update_icon()
|
||||
|
||||
/datum/psi_complexus/proc/can_use_passive()
|
||||
return (owner.stat == CONSCIOUS && !suppressed && !stun)
|
||||
|
||||
/datum/psi_complexus/proc/can_use(var/incapacitation_flags)
|
||||
return (owner.stat == CONSCIOUS && (!incapacitation_flags || !owner.incapacitated(incapacitation_flags)) && !suppressed && !stun && world.time >= next_power_use)
|
||||
|
||||
/datum/psi_complexus/proc/spend_power(var/value = 0, var/check_incapacitated)
|
||||
. = FALSE
|
||||
if(isnull(check_incapacitated))
|
||||
check_incapacitated = (INCAPACITATION_STUNNED|INCAPACITATION_KNOCKOUT)
|
||||
if(can_use(check_incapacitated))
|
||||
value = max(1, Ceiling(value * cost_modifier))
|
||||
if(value <= stamina)
|
||||
stamina -= value
|
||||
ui.update_icon()
|
||||
. = TRUE
|
||||
else
|
||||
backblast(abs(stamina - value))
|
||||
stamina = 0
|
||||
. = FALSE
|
||||
ui.update_icon()
|
||||
|
||||
/datum/psi_complexus/proc/hide_auras()
|
||||
if(owner.client)
|
||||
for(var/thing in SSpsi.all_aura_images)
|
||||
owner.client.images -= thing
|
||||
|
||||
/datum/psi_complexus/proc/show_auras()
|
||||
if(owner.client)
|
||||
for(var/image/I in SSpsi.all_aura_images)
|
||||
owner.client.images |= I
|
||||
|
||||
/datum/psi_complexus/proc/backblast(var/value)
|
||||
|
||||
// Can't backblast if you're controlling your power.
|
||||
if(!owner || suppressed)
|
||||
return FALSE
|
||||
|
||||
sound_to(owner, sound('sound/effects/psi/power_feedback.ogg'))
|
||||
to_chat(owner, "<span class='danger'><font size=3>Wild energistic feedback blasts across your psyche!</font></span>")
|
||||
stunned(value * 2)
|
||||
set_cooldown(value * 100)
|
||||
|
||||
if(prob(value*10)) owner.emote("scream")
|
||||
|
||||
// Your head asplode.
|
||||
owner.adjustBrainLoss(value)
|
||||
owner.adjustHalLoss(value * 25) //Ouch.
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/pop = owner
|
||||
if(pop.should_have_organ(BP_BRAIN))
|
||||
var/obj/item/organ/internal/brain/sponge = pop.internal_organs_by_name[BP_BRAIN]
|
||||
if(sponge && sponge.damage >= sponge.max_damage)
|
||||
var/obj/item/organ/external/affecting = pop.get_organ(sponge.parent_organ)
|
||||
if(affecting && !affecting.is_stump())
|
||||
affecting.droplimb(0, DROPLIMB_BLUNT)
|
||||
if(sponge) qdel(sponge)
|
||||
|
||||
/datum/psi_complexus/proc/reset()
|
||||
aura_color = initial(aura_color)
|
||||
ranks = base_ranks ? base_ranks.Copy() : null
|
||||
max_stamina = initial(max_stamina)
|
||||
stamina = min(stamina, max_stamina)
|
||||
cancel()
|
||||
update()
|
||||
17
code/modules/psionics/complexus/complexus_latency.dm
Normal file
17
code/modules/psionics/complexus/complexus_latency.dm
Normal file
@@ -0,0 +1,17 @@
|
||||
/datum/psi_complexus/proc/check_latency_trigger(var/trigger_strength = 0, var/source, var/redactive = FALSE)
|
||||
|
||||
if(!LAZYLEN(latencies) || world.time < next_latency_trigger)
|
||||
return FALSE
|
||||
|
||||
if(!prob(trigger_strength))
|
||||
next_latency_trigger = world.time + rand(100, 300)
|
||||
return FALSE
|
||||
|
||||
var/faculty = pick(latencies)
|
||||
var/new_rank = rand(2,5)
|
||||
owner.set_psi_rank(faculty, new_rank)
|
||||
var/datum/psionic_faculty/faculty_decl = SSpsi.get_faculty(faculty)
|
||||
to_chat(owner, span("danger", "You scream internally as your [faculty_decl.name] faculty is forced into operancy by [source]!"))
|
||||
next_latency_trigger = world.time + rand(600, 1800) * new_rank
|
||||
if(!redactive) owner.adjustBrainLoss(rand(trigger_strength * 2, trigger_strength * 4))
|
||||
return TRUE
|
||||
49
code/modules/psionics/complexus/complexus_power_cache.dm
Normal file
49
code/modules/psionics/complexus/complexus_power_cache.dm
Normal file
@@ -0,0 +1,49 @@
|
||||
/datum/psi_complexus/proc/rebuild_power_cache()
|
||||
if(rebuild_power_cache)
|
||||
|
||||
melee_powers = list()
|
||||
grab_powers = list()
|
||||
ranged_powers = list()
|
||||
manifestation_powers = list()
|
||||
powers_by_faculty = list()
|
||||
|
||||
for(var/faculty in ranks)
|
||||
var/relevant_rank = get_rank(faculty)
|
||||
var/datum/psionic_faculty/faculty_decl = SSpsi.get_faculty(faculty)
|
||||
for(var/thing in faculty_decl.powers)
|
||||
var/datum/psionic_power/power = thing
|
||||
if(relevant_rank >= power.min_rank)
|
||||
if(!powers_by_faculty[power.faculty]) powers_by_faculty[power.faculty] = list()
|
||||
powers_by_faculty[power.faculty] += power
|
||||
if(power.use_ranged)
|
||||
if(!ranged_powers[faculty]) ranged_powers[faculty] = list()
|
||||
ranged_powers[faculty] += power
|
||||
if(power.use_melee)
|
||||
if(!melee_powers[faculty]) melee_powers[faculty] = list()
|
||||
melee_powers[faculty] += power
|
||||
if(power.use_manifest)
|
||||
manifestation_powers += power
|
||||
if(power.use_grab)
|
||||
if(!grab_powers[faculty]) grab_powers[faculty] = list()
|
||||
grab_powers[faculty] += power
|
||||
rebuild_power_cache = FALSE
|
||||
|
||||
/datum/psi_complexus/proc/get_powers_by_faculty(var/faculty)
|
||||
rebuild_power_cache()
|
||||
return powers_by_faculty[faculty]
|
||||
|
||||
/datum/psi_complexus/proc/get_melee_powers(var/faculty)
|
||||
rebuild_power_cache()
|
||||
return melee_powers[faculty]
|
||||
|
||||
/datum/psi_complexus/proc/get_ranged_powers(var/faculty)
|
||||
rebuild_power_cache()
|
||||
return ranged_powers[faculty]
|
||||
|
||||
/datum/psi_complexus/proc/get_grab_powers(var/faculty)
|
||||
rebuild_power_cache()
|
||||
return grab_powers[faculty]
|
||||
|
||||
/datum/psi_complexus/proc/get_manifestations()
|
||||
rebuild_power_cache()
|
||||
return manifestation_powers
|
||||
228
code/modules/psionics/complexus/complexus_process.dm
Normal file
228
code/modules/psionics/complexus/complexus_process.dm
Normal file
@@ -0,0 +1,228 @@
|
||||
/datum/psi_complexus/proc/update(var/force)
|
||||
|
||||
set waitfor = FALSE
|
||||
|
||||
var/last_rating = rating
|
||||
var/highest_faculty
|
||||
var/highest_rank = 0
|
||||
var/combined_rank = 0
|
||||
for(var/faculty in ranks)
|
||||
var/check_rank = get_rank(faculty)
|
||||
if(check_rank == 1)
|
||||
LAZYADD(latencies, faculty)
|
||||
else
|
||||
if(check_rank <= 0)
|
||||
ranks -= faculty
|
||||
LAZYREMOVE(latencies, faculty)
|
||||
combined_rank += check_rank
|
||||
if(!highest_faculty || highest_rank < check_rank)
|
||||
highest_faculty = faculty
|
||||
highest_rank = check_rank
|
||||
|
||||
UNSETEMPTY(latencies)
|
||||
var/rank_count = max(1, LAZYLEN(ranks))
|
||||
if(force || last_rating != Ceiling(combined_rank/rank_count))
|
||||
if(highest_rank <= 1)
|
||||
if(highest_rank == 0)
|
||||
qdel(src)
|
||||
return
|
||||
else
|
||||
rebuild_power_cache = TRUE
|
||||
sound_to(owner, 'sound/effects/psi/power_unlock.ogg')
|
||||
rating = Ceiling(combined_rank/rank_count)
|
||||
cost_modifier = 1
|
||||
if(rating > 1)
|
||||
cost_modifier -= min(1, max(0.1, (rating-1) / 10))
|
||||
if(!ui)
|
||||
ui = new(owner)
|
||||
if(owner.client)
|
||||
owner.client.screen += ui
|
||||
else
|
||||
if(owner.client)
|
||||
owner.client.screen |= ui
|
||||
if(!suppressed && owner.client)
|
||||
for(var/thing in SSpsi.all_aura_images)
|
||||
owner.client.images |= thing
|
||||
|
||||
var/image/aura_image = get_aura_image()
|
||||
if(rating >= PSI_RANK_PARAMOUNT) // spooky boosters
|
||||
aura_color = "#aaffaa"
|
||||
aura_image.blend_mode = BLEND_SUBTRACT
|
||||
else
|
||||
aura_image.blend_mode = BLEND_ADD
|
||||
if(highest_faculty == PSI_COERCION)
|
||||
aura_color = "#cc3333"
|
||||
else if(highest_faculty == PSI_PSYCHOKINESIS)
|
||||
aura_color = "#3333cc"
|
||||
else if(highest_faculty == PSI_REDACTION)
|
||||
aura_color = "#33cc33"
|
||||
else if(highest_faculty == PSI_ENERGISTICS)
|
||||
aura_color = "#cccc33"
|
||||
|
||||
if(!announced && owner && owner.client && !QDELETED(src))
|
||||
announced = TRUE
|
||||
to_chat(owner, "<hr>")
|
||||
to_chat(owner, span("notice", "<font size = 3>You are <b>psionic</b>, touched by powers beyond understanding.</font>"))
|
||||
to_chat(owner, span("notice", "<b>Shift-left-click your Psi icon</b> on the bottom right to <b>view a summary of how to use them</b>, or <b>left click</b> it to <b>suppress or unsuppress</b> your psionics. Beware: overusing your gifts can have <b>deadly consequences</b>."))
|
||||
to_chat(owner, "<hr>")
|
||||
|
||||
/datum/psi_complexus/process()
|
||||
|
||||
var/update_hud
|
||||
if(stun)
|
||||
stun--
|
||||
if(stun)
|
||||
if(!suppressed)
|
||||
suppressed = TRUE
|
||||
update_hud = TRUE
|
||||
else
|
||||
to_chat(owner, span("notice", "You have recovered your mental composure."))
|
||||
update_hud = TRUE
|
||||
return
|
||||
|
||||
else if(stamina < max_stamina)
|
||||
if(owner?.stat == CONSCIOUS)
|
||||
stamina = min(max_stamina, stamina + rand(1,3))
|
||||
else if(owner?.stat == UNCONSCIOUS)
|
||||
stamina = min(max_stamina, stamina + rand(3,5))
|
||||
|
||||
if(!owner.nervous_system_failure() && owner.stat == CONSCIOUS && stamina && !suppressed && get_rank(PSI_REDACTION) >= PSI_RANK_OPERANT)
|
||||
attempt_regeneration()
|
||||
|
||||
var/next_aura_size = max(0.1,((stamina/max_stamina)*min(3,rating))/5)
|
||||
var/next_aura_alpha = round(((suppressed ? max(0,rating - 2) : rating)/5)*255)
|
||||
|
||||
if(next_aura_alpha != last_aura_alpha || next_aura_size != last_aura_size || aura_color != last_aura_color)
|
||||
last_aura_size = next_aura_size
|
||||
last_aura_alpha = next_aura_alpha
|
||||
last_aura_color = aura_color
|
||||
var/matrix/M = matrix()
|
||||
if(next_aura_size != 1)
|
||||
M.Scale(next_aura_size)
|
||||
animate(get_aura_image(), alpha = next_aura_alpha, transform = M, color = aura_color, time = 3)
|
||||
|
||||
if(update_hud)
|
||||
ui.update_icon()
|
||||
|
||||
/datum/psi_complexus/proc/attempt_regeneration()
|
||||
|
||||
var/heal_general = FALSE
|
||||
var/heal_poison = FALSE
|
||||
var/heal_internal = FALSE
|
||||
var/heal_bleeding = FALSE
|
||||
var/heal_rate = 0
|
||||
var/mend_prob = 0
|
||||
|
||||
var/use_rank = get_rank(PSI_REDACTION)
|
||||
if(use_rank >= PSI_RANK_PARAMOUNT)
|
||||
heal_general = TRUE
|
||||
heal_poison = TRUE
|
||||
heal_internal = TRUE
|
||||
heal_bleeding = TRUE
|
||||
mend_prob = 50
|
||||
heal_rate = 7
|
||||
else if(use_rank == PSI_RANK_GRANDMASTER)
|
||||
heal_poison = TRUE
|
||||
heal_internal = TRUE
|
||||
heal_bleeding = TRUE
|
||||
mend_prob = 20
|
||||
heal_rate = 5
|
||||
else if(use_rank == PSI_RANK_MASTER)
|
||||
heal_internal = TRUE
|
||||
heal_bleeding = TRUE
|
||||
mend_prob = 10
|
||||
heal_rate = 3
|
||||
else if(use_rank == PSI_RANK_OPERANT)
|
||||
heal_bleeding = TRUE
|
||||
mend_prob = 5
|
||||
heal_rate = 1
|
||||
else
|
||||
return
|
||||
|
||||
if(!heal_rate || stamina < heal_rate)
|
||||
return // Don't backblast from trying to heal ourselves thanks.
|
||||
|
||||
if(ishuman(owner))
|
||||
|
||||
var/mob/living/carbon/human/H = owner
|
||||
|
||||
// Fix some pain.
|
||||
if(heal_rate > 0)
|
||||
H.shock_stage = max(0, H.shock_stage - max(1, round(heal_rate/2)))
|
||||
|
||||
// Mend internal damage.
|
||||
if(prob(mend_prob))
|
||||
|
||||
// Fix our heart if we're paramount.
|
||||
if(heal_general && H.is_asystole() && H.should_have_organ(BP_HEART) && spend_power(heal_rate))
|
||||
H.resuscitate()
|
||||
|
||||
// Heal organ damage.
|
||||
if(heal_internal)
|
||||
for(var/obj/item/organ/I in H.internal_organs)
|
||||
|
||||
if(BP_IS_ROBOTIC(I))
|
||||
continue
|
||||
|
||||
if(I.damage > 0 && spend_power(heal_rate))
|
||||
I.damage = max(I.damage - heal_rate, 0)
|
||||
if(prob(25))
|
||||
to_chat(H, span("notice", "Your innards itch as your autoredactive faculty mends your [I.name]."))
|
||||
return
|
||||
|
||||
// Heal broken bones.
|
||||
if(H.bad_external_organs.len)
|
||||
for(var/obj/item/organ/external/E in H.bad_external_organs)
|
||||
|
||||
if(BP_IS_ROBOTIC(E))
|
||||
continue
|
||||
|
||||
if(heal_internal && (E.status & ORGAN_BROKEN) && E.damage < (E.min_broken_damage * config.organ_health_multiplier)) // So we don't mend and autobreak.
|
||||
if(spend_power(heal_rate))
|
||||
if(E.mend_fracture())
|
||||
to_chat(H, span("notice", "Your autoredactive faculty coaxes together the shattered bones in your [E.name]."))
|
||||
return
|
||||
|
||||
if(heal_bleeding)
|
||||
|
||||
if((E.status & ORGAN_ARTERY_CUT) && spend_power(heal_rate))
|
||||
to_chat(H, span("notice", "Your autoredactive faculty mends the torn artery in your [E.name], stemming the worst of the bleeding."))
|
||||
E.status &= ~ORGAN_ARTERY_CUT
|
||||
return
|
||||
|
||||
if(E.status & ORGAN_TENDON_CUT)
|
||||
to_chat(H, span("notice", "Your autoredactive faculty repairs the severed tendon in your [E.name]."))
|
||||
E.status &= ~ORGAN_TENDON_CUT
|
||||
return TRUE
|
||||
|
||||
for(var/datum/wound/W in E.wounds)
|
||||
|
||||
if(W.bleeding() && spend_power(heal_rate))
|
||||
to_chat(H, span("notice", "Your autoredactive faculty knits together severed veins, stemming the bleeding from \a [W.desc] on your [E.name]."))
|
||||
W.bleed_timer = 0
|
||||
W.clamped = TRUE
|
||||
E.status &= ~ORGAN_BLEEDING
|
||||
return
|
||||
|
||||
// Heal radiation, cloneloss and poisoning.
|
||||
if(heal_poison)
|
||||
|
||||
if(owner.total_radiation && spend_power(heal_rate))
|
||||
if(prob(25))
|
||||
to_chat(owner, span("notice", "Your autoredactive faculty repairs some of the radiation damage to your body."))
|
||||
owner.total_radiation = max(0, owner.total_radiation - heal_rate)
|
||||
return
|
||||
|
||||
if(owner.getCloneLoss() && spend_power(heal_rate))
|
||||
if(prob(25))
|
||||
to_chat(owner, span("notice", "Your autoredactive faculty stitches together some of your mangled DNA."))
|
||||
owner.adjustCloneLoss(-heal_rate)
|
||||
return
|
||||
|
||||
// Heal everything left.
|
||||
if(heal_general && prob(mend_prob) && (owner.getBruteLoss() || owner.getFireLoss() || owner.getOxyLoss()) && spend_power(heal_rate))
|
||||
owner.adjustBruteLoss(-(heal_rate))
|
||||
owner.adjustFireLoss(-(heal_rate))
|
||||
owner.adjustOxyLoss(-(heal_rate))
|
||||
if(prob(25))
|
||||
to_chat(owner, span("notice", "Your skin crawls as your autoredactive faculty heals your body."))
|
||||
167
code/modules/psionics/equipment/cerebro_enhancers.dm
Normal file
167
code/modules/psionics/equipment/cerebro_enhancers.dm
Normal file
@@ -0,0 +1,167 @@
|
||||
//Psi-boosting item (antag only)
|
||||
/obj/item/clothing/head/helmet/space/psi_amp
|
||||
name = "cerebro-energetic enhancer"
|
||||
desc = "A matte-black, eyeless cerebro-energetic enhancement helmet. It uses highly sophisticated, and illegal, techniques to drill into your brain and install psi-infected AIs into the fluid cavities between your lobes."
|
||||
action_button_name = "Install Boosters"
|
||||
icon_state = "cerebro"
|
||||
|
||||
item_state_slots = list(
|
||||
slot_l_hand_str = "helmet",
|
||||
slot_r_hand_str = "helmet"
|
||||
)
|
||||
|
||||
var/operating = FALSE
|
||||
var/list/boosted_faculties
|
||||
var/boosted_rank = PSI_RANK_PARAMOUNT
|
||||
var/unboosted_rank = PSI_RANK_MASTER
|
||||
var/max_boosted_faculties = 3
|
||||
var/boosted_psipower = 120
|
||||
|
||||
/obj/item/clothing/head/helmet/space/psi_amp/lesser
|
||||
name = "psionic amplifier"
|
||||
desc = "A crown-of-thorns cerebro-energetic enhancer that interfaces directly with the brain, isolating and strengthening psionic signals. It kind of looks like a tiara having sex with an industrial robot."
|
||||
icon_state = "amp"
|
||||
flags_inv = 0
|
||||
body_parts_covered = 0
|
||||
|
||||
max_boosted_faculties = 1
|
||||
boosted_rank = PSI_RANK_MASTER
|
||||
unboosted_rank = PSI_RANK_OPERANT
|
||||
boosted_psipower = 50
|
||||
|
||||
/obj/item/clothing/head/helmet/space/psi_amp/Initialize()
|
||||
. = ..()
|
||||
verbs += /obj/item/clothing/head/helmet/space/psi_amp/proc/integrate
|
||||
|
||||
/obj/item/clothing/head/helmet/space/psi_amp/attack_self(var/mob/user)
|
||||
|
||||
if(operating)
|
||||
return
|
||||
|
||||
if(!canremove)
|
||||
deintegrate()
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/H = loc
|
||||
if(istype(H) && H.head == src)
|
||||
integrate()
|
||||
return
|
||||
|
||||
var/choice = input("Select a brainboard to install or remove.","Psionic Amplifier") as null|anything in SSpsi.faculties_by_name
|
||||
if(!choice)
|
||||
return
|
||||
|
||||
var/removed
|
||||
var/slots_left = max_boosted_faculties - LAZYLEN(boosted_faculties)
|
||||
var/datum/psionic_faculty/faculty = SSpsi.get_faculty(choice)
|
||||
if(faculty.id in boosted_faculties)
|
||||
LAZYREMOVE(boosted_faculties, faculty.id)
|
||||
removed = TRUE
|
||||
else
|
||||
if(slots_left <= 0)
|
||||
to_chat(user, span("warning", "There are no slots left to install brainboards into."))
|
||||
return
|
||||
LAZYADD(boosted_faculties, faculty.id)
|
||||
UNSETEMPTY(boosted_faculties)
|
||||
|
||||
slots_left = max_boosted_faculties - LAZYLEN(boosted_faculties)
|
||||
to_chat(user, span("notice", "You [removed ? "remove" : "install"] the [choice] brainboard [removed ? "from" : "in"] \the [src]. There [slots_left!=1 ? "are" : "is"] [slots_left] slot\s left."))
|
||||
|
||||
/obj/item/clothing/head/helmet/space/psi_amp/proc/deintegrate()
|
||||
|
||||
set name = "Remove Psi-Amp"
|
||||
set desc = "Removes your psi-amp."
|
||||
set category = "Abilities"
|
||||
set src in usr
|
||||
|
||||
if(operating)
|
||||
return
|
||||
|
||||
if(canremove)
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/H = loc
|
||||
if(!istype(H) || H.head != src)
|
||||
canremove = TRUE
|
||||
return
|
||||
|
||||
to_chat(H, span("warning", "You feel a strange tugging sensation as \the [src] begins removing the slave-minds from your brain..."))
|
||||
playsound(H, 'sound/weapons/circsawhit.ogg', 50, 1, -1)
|
||||
operating = TRUE
|
||||
|
||||
sleep(80)
|
||||
|
||||
if(H.psi)
|
||||
H.psi.reset()
|
||||
|
||||
to_chat(H, span("notice", "\The [src] chimes quietly as it finishes removing the slave-minds from your brain."))
|
||||
|
||||
canremove = TRUE
|
||||
operating = FALSE
|
||||
|
||||
verbs -= /obj/item/clothing/head/helmet/space/psi_amp/proc/deintegrate
|
||||
verbs |= /obj/item/clothing/head/helmet/space/psi_amp/proc/integrate
|
||||
|
||||
action_button_name = "Integrate Psionic Amplifier"
|
||||
H.update_action_buttons()
|
||||
|
||||
set_light(0)
|
||||
|
||||
/obj/item/clothing/head/helmet/space/psi_amp/Move()
|
||||
var/lastloc = loc
|
||||
. = ..()
|
||||
if(.)
|
||||
var/mob/living/carbon/human/H = lastloc
|
||||
if(istype(H) && H.psi)
|
||||
H.psi.reset()
|
||||
H = loc
|
||||
if(!istype(H) || H.head != src)
|
||||
canremove = TRUE
|
||||
|
||||
/obj/item/clothing/head/helmet/space/psi_amp/proc/integrate()
|
||||
|
||||
set name = "Integrate Psionic Amplifier"
|
||||
set desc = "Enhance your brainpower."
|
||||
set category = "Abilities"
|
||||
set src in usr
|
||||
|
||||
if(operating)
|
||||
return
|
||||
|
||||
if(!canremove)
|
||||
return
|
||||
|
||||
if(LAZYLEN(boosted_faculties) < max_boosted_faculties)
|
||||
to_chat(usr, span("notice", "You still have [max_boosted_faculties - LAZYLEN(boosted_faculties)] facult[LAZYLEN(boosted_faculties) == 1 ? "y" : "ies"] to select. Use \the [src] in-hand to select them."))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/H = loc
|
||||
if(!istype(H) || H.head != src)
|
||||
to_chat(usr, span("warning", "\The [src] must be worn on your head in order to be activated."))
|
||||
return
|
||||
|
||||
canremove = FALSE
|
||||
operating = TRUE
|
||||
to_chat(H, span("warning", "You feel a series of sharp pinpricks as \the [src] anaesthetises your scalp before drilling down into your brain."))
|
||||
playsound(H, 'sound/weapons/circsawhit.ogg', 50, 1, -1)
|
||||
|
||||
sleep(80)
|
||||
|
||||
for(var/faculty in list(PSI_COERCION, PSI_PSYCHOKINESIS, PSI_REDACTION, PSI_ENERGISTICS))
|
||||
if(faculty in boosted_faculties)
|
||||
H.set_psi_rank(faculty, boosted_rank, take_larger = TRUE, temporary = TRUE)
|
||||
else
|
||||
H.set_psi_rank(faculty, unboosted_rank, take_larger = TRUE, temporary = TRUE)
|
||||
if(H.psi)
|
||||
H.psi.max_stamina = boosted_psipower
|
||||
H.psi.stamina = H.psi.max_stamina
|
||||
H.psi.update(force = TRUE)
|
||||
|
||||
to_chat(H, span("notice", "You experience a brief but powerful wave of deja vu as \the [src] finishes modifying your brain."))
|
||||
verbs |= /obj/item/clothing/head/helmet/space/psi_amp/proc/deintegrate
|
||||
verbs -= /obj/item/clothing/head/helmet/space/psi_amp/proc/integrate
|
||||
operating = FALSE
|
||||
action_button_name = "Remove Psionic Amplifier"
|
||||
H.update_action_buttons()
|
||||
|
||||
set_light(0.5, 0.1, 3, 2, l_color = "#880000")
|
||||
0
code/modules/psionics/equipment/psimeter.dm
Normal file
0
code/modules/psionics/equipment/psimeter.dm
Normal file
51
code/modules/psionics/equipment/psipower.dm
Normal file
51
code/modules/psionics/equipment/psipower.dm
Normal file
@@ -0,0 +1,51 @@
|
||||
/obj/item/psychic_power
|
||||
name = "psychic power"
|
||||
icon = 'icons/obj/psychic_powers.dmi'
|
||||
flags = 0
|
||||
simulated = 1
|
||||
anchored = 1
|
||||
var/maintain_cost = 3
|
||||
var/mob/living/owner
|
||||
|
||||
/obj/item/psychic_power/New(var/mob/living/_owner)
|
||||
owner = _owner
|
||||
if(!istype(owner))
|
||||
qdel(src)
|
||||
return
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
..()
|
||||
|
||||
/obj/item/psychic_power/Destroy()
|
||||
if(istype(owner) && owner.psi)
|
||||
LAZYREMOVE(owner.psi.manifested_items, src)
|
||||
UNSETEMPTY(owner.psi.manifested_items)
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
. = ..()
|
||||
|
||||
/obj/item/psychic_power/get_storage_cost()
|
||||
return 5
|
||||
|
||||
/obj/item/psychic_power/attack_self(var/mob/user)
|
||||
sound_to(owner, 'sound/effects/psi/power_fail.ogg')
|
||||
user.drop_from_inventory(src)
|
||||
|
||||
/obj/item/psychic_power/dropped()
|
||||
..()
|
||||
qdel(src)
|
||||
|
||||
/obj/item/psychic_power/process()
|
||||
if(istype(owner))
|
||||
owner.psi.spend_power(maintain_cost)
|
||||
if(!owner || loc != owner || (owner.l_hand != src && owner.r_hand != src))
|
||||
if(istype(loc,/mob/living))
|
||||
var/mob/living/carbon/human/host = loc
|
||||
if(istype(host))
|
||||
for(var/obj/item/organ/external/organ in host.organs)
|
||||
for(var/obj/item/O in organ.implants)
|
||||
if(O == src)
|
||||
organ.implants -= src
|
||||
host.pinned -= src
|
||||
host.embedded -= src
|
||||
host.drop_from_inventory(src)
|
||||
else
|
||||
qdel(src)
|
||||
26
code/modules/psionics/equipment/psipower_blade.dm
Normal file
26
code/modules/psionics/equipment/psipower_blade.dm
Normal file
@@ -0,0 +1,26 @@
|
||||
/obj/item/psychic_power/psiblade
|
||||
name = "psychokinetic slash"
|
||||
force = 10
|
||||
sharp = 1
|
||||
edge = 1
|
||||
maintain_cost = 1
|
||||
icon_state = "psiblade_short"
|
||||
hitsound = 'sound/weapons/psisword.ogg'
|
||||
|
||||
/obj/item/psychic_power/psiblade/dropped(var/mob/living/user)
|
||||
playsound(loc, 'sound/effects/psi/power_fail.ogg', 30, 1)
|
||||
QDEL_IN(src, 1)
|
||||
|
||||
/obj/item/psychic_power/psiblade/master
|
||||
force = 20
|
||||
maintain_cost = 2
|
||||
|
||||
/obj/item/psychic_power/psiblade/master/grand
|
||||
force = 30
|
||||
maintain_cost = 3
|
||||
icon_state = "psiblade_long"
|
||||
|
||||
/obj/item/psychic_power/psiblade/master/grand/paramount // Silly typechecks because rewriting old interaction code is outside of scope.
|
||||
force = 50
|
||||
maintain_cost = 4
|
||||
icon_state = "psiblade_long"
|
||||
39
code/modules/psionics/equipment/psipower_tinker.dm
Normal file
39
code/modules/psionics/equipment/psipower_tinker.dm
Normal file
@@ -0,0 +1,39 @@
|
||||
/obj/item/psychic_power/tinker
|
||||
name = "psychokinetic crowbar"
|
||||
icon_state = "tinker"
|
||||
force = 1
|
||||
var/emulating = "Crowbar"
|
||||
|
||||
/obj/item/psychic_power/tinker/iscrowbar()
|
||||
return emulating == "Crowbar"
|
||||
|
||||
/obj/item/psychic_power/tinker/iswrench()
|
||||
return emulating == "Wrench"
|
||||
|
||||
/obj/item/psychic_power/tinker/isscrewdriver()
|
||||
return emulating == "Screwdriver"
|
||||
|
||||
/obj/item/psychic_power/tinker/iswirecutter()
|
||||
return emulating == "Wirecutters"
|
||||
|
||||
/obj/item/psychic_power/tinker/attack_self()
|
||||
|
||||
if(!owner || loc != owner)
|
||||
return
|
||||
|
||||
var/choice = input("Select a tool to emulate.","Power") as null|anything in list("Crowbar","Wrench","Screwdriver","Wirecutters","Dismiss")
|
||||
if(!choice)
|
||||
return
|
||||
|
||||
if(!owner || loc != owner)
|
||||
return
|
||||
|
||||
if(choice == "Dismiss")
|
||||
sound_to(owner, 'sound/effects/psi/power_fail.ogg')
|
||||
owner.drop_from_inventory(src)
|
||||
return
|
||||
|
||||
emulating = choice
|
||||
name = "psychokinetic [lowertext(emulating)]"
|
||||
to_chat(owner, "<span class='notice'>You begin emulating \a [lowertext(emulating)].</span>")
|
||||
sound_to(owner, 'sound/effects/psi/power_fabrication.ogg')
|
||||
96
code/modules/psionics/equipment/psipower_tk.dm
Normal file
96
code/modules/psionics/equipment/psipower_tk.dm
Normal file
@@ -0,0 +1,96 @@
|
||||
/obj/item/psychic_power/telekinesis
|
||||
name = "telekinetic grip"
|
||||
maintain_cost = 3
|
||||
icon_state = "telekinesis"
|
||||
var/atom/movable/focus
|
||||
|
||||
/obj/item/psychic_power/telekinesis/Destroy()
|
||||
focus = null
|
||||
. = ..()
|
||||
|
||||
/obj/item/psychic_power/telekinesis/process()
|
||||
if(!focus || !istype(focus.loc, /turf) || get_dist(get_turf(focus), get_turf(owner)) > owner.psi.get_rank(PSI_PSYCHOKINESIS))
|
||||
owner.drop_from_inventory(src)
|
||||
return
|
||||
. = ..()
|
||||
|
||||
/obj/item/psychic_power/telekinesis/proc/set_focus(var/atom/movable/_focus)
|
||||
|
||||
if(!_focus.simulated || !istype(_focus.loc, /turf))
|
||||
return FALSE
|
||||
|
||||
var/check_paramount
|
||||
if(ismob(_focus))
|
||||
var/mob/victim = _focus
|
||||
check_paramount = (victim.mob_size >= MOB_MEDIUM)
|
||||
else if(isobj(_focus))
|
||||
var/obj/thing = _focus
|
||||
check_paramount = (thing.w_class >= 5)
|
||||
else
|
||||
return FALSE
|
||||
|
||||
if(_focus.anchored || (check_paramount && owner.psi.get_rank(PSI_PSYCHOKINESIS) < PSI_RANK_PARAMOUNT))
|
||||
focus = _focus
|
||||
. = attack_self(owner)
|
||||
if(!.)
|
||||
to_chat(owner, span("warning", "\The [_focus] is too hefty for you to get a mind-grip on."))
|
||||
qdel(src)
|
||||
return FALSE
|
||||
|
||||
focus = _focus
|
||||
overlays.Cut()
|
||||
var/image/I = image(icon = focus.icon, icon_state = focus.icon_state)
|
||||
I.color = focus.color
|
||||
I.overlays = focus.overlays
|
||||
overlays += I
|
||||
return TRUE
|
||||
|
||||
/obj/item/psychic_power/telekinesis/attack_self(var/mob/user)
|
||||
user.visible_message(span("notice", "\The [user] makes a strange gesture."))
|
||||
sparkle()
|
||||
return focus.do_simple_ranged_interaction(user)
|
||||
|
||||
/obj/item/psychic_power/telekinesis/afterattack(var/atom/target, var/mob/living/user, var/proximity)
|
||||
|
||||
if(!target || !user || (isobj(target) && !isturf(target.loc)) || !user.psi || !user.psi.can_use() || !user.psi.spend_power(5))
|
||||
return
|
||||
|
||||
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
|
||||
user.psi.set_cooldown(5)
|
||||
|
||||
var/distance = get_dist(get_turf(user), get_turf(focus ? focus : target))
|
||||
if(distance > user.psi.get_rank(PSI_PSYCHOKINESIS))
|
||||
to_chat(user, span("warning", "Your telekinetic power won't reach that far."))
|
||||
return FALSE
|
||||
|
||||
if(target == focus)
|
||||
attack_self(user)
|
||||
else
|
||||
user.visible_message(span("danger", "\The [user] gestures sharply!"))
|
||||
sparkle()
|
||||
if(!istype(target, /turf) && istype(focus,/obj/item) && target.Adjacent(focus))
|
||||
var/obj/item/I = focus
|
||||
var/resolved = target.attackby(I, user, user:get_organ_target())
|
||||
if(!resolved && target && I)
|
||||
I.afterattack(target,user,1) // for splashing with beakers
|
||||
else
|
||||
if(!focus.anchored)
|
||||
var/user_rank = owner.psi.get_rank(PSI_PSYCHOKINESIS)
|
||||
focus.throw_at(target, user_rank*2, user_rank*10, owner)
|
||||
sleep(1)
|
||||
sparkle()
|
||||
|
||||
/obj/item/psychic_power/telekinesis/proc/sparkle()
|
||||
set waitfor = 0
|
||||
if(focus)
|
||||
var/obj/effect/overlay/O = new /obj/effect/overlay(get_turf(focus))
|
||||
O.name = "sparkles"
|
||||
O.anchored = 1
|
||||
O.density = 0
|
||||
O.layer = FLY_LAYER
|
||||
O.set_dir(pick(cardinal))
|
||||
O.icon = 'icons/effects/effects.dmi'
|
||||
O.icon_state = "nothing"
|
||||
flick("empdisable",O)
|
||||
sleep(5)
|
||||
qdel(O)
|
||||
22
code/modules/psionics/events/_psi.dm
Normal file
22
code/modules/psionics/events/_psi.dm
Normal file
@@ -0,0 +1,22 @@
|
||||
/datum/event/psi
|
||||
startWhen = 30
|
||||
endWhen = 120
|
||||
|
||||
/datum/event/psi/announce()
|
||||
priority_announcement.Announce( \
|
||||
"A localized disruption within the neighboring psionic continua has been detected. All psi-operant crewmembers \
|
||||
are advised to cease any sensitive activities and report to medical personnel in case of damage.", \
|
||||
"Jargon Federation Observation Probe TC-203 Sensor Array", new_sound = 'sound/misc/announcements/nightlight_old.ogg')
|
||||
|
||||
/datum/event/psi/end()
|
||||
priority_announcement.Announce( \
|
||||
"The psi-disturbance has ended and baseline normality has been re-asserted. \
|
||||
Anything you still can't cope with is therefore your own problem.", \
|
||||
"Jargon Federation Observation Probe TC-203 Sensor Array", new_sound = 'sound/misc/announcements/nightlight_old.ogg')
|
||||
|
||||
/datum/event/psi/tick()
|
||||
for(var/thing in SSpsi.processing)
|
||||
apply_psi_effect(thing)
|
||||
|
||||
/datum/event/psi/proc/apply_psi_effect(var/datum/psi_complexus/psi)
|
||||
return
|
||||
61
code/modules/psionics/events/mini_spasm.dm
Normal file
61
code/modules/psionics/events/mini_spasm.dm
Normal file
@@ -0,0 +1,61 @@
|
||||
/datum/event/minispasm
|
||||
startWhen = 60
|
||||
endWhen = 90
|
||||
var/static/list/psi_operancy_messages = list(
|
||||
"There's something in your skull!",
|
||||
"Something is eating your thoughts!",
|
||||
"You can feel your brain being rewritten!",
|
||||
"Something is crawling over your frontal lobe!",
|
||||
"<b>THE SIGNAL THE SIGNAL THE SIGNAL THE SIGNAL THE</b>",
|
||||
"Something is drilling through your skull!",
|
||||
"Your head feels like it's going to implode!",
|
||||
"Thousands of ants are tunneling in your head!"
|
||||
)
|
||||
|
||||
/datum/event/minispasm/announce()
|
||||
command_announcement.Announce( \
|
||||
"PRIORITY ALERT: SIGMA-[rand(50,80)] NON-STANDARD PSIONIC SIGNAL-WAVE TRANSMISSION DETECTED - 97% MATCH, NON-VARIANT \
|
||||
SIGNAL SOURCE TRIANGULATED TO DISTANT SITE: All personnel are advised to avoid \
|
||||
exposure to active audio transmission equipment including radio headsets and intercoms \
|
||||
for the duration of the signal broadcast.", \
|
||||
"Jargon Federation Observation Probe TC-203 Sensor Array", new_sound = 'sound/misc/announcements/security_level_old.ogg')
|
||||
|
||||
/datum/event/minispasm/start()
|
||||
var/list/victims = list()
|
||||
for(var/obj/item/device/radio/radio in listening_objects)
|
||||
if(radio.on)
|
||||
for(var/mob/living/victim in range(radio.canhear_range, radio.loc))
|
||||
if(isnull(victims[victim]) && victim.stat == CONSCIOUS && !victim.ear_deaf)
|
||||
victims[victim] = radio
|
||||
for(var/thing in victims)
|
||||
var/mob/living/victim = thing
|
||||
var/obj/item/device/radio/source = victims[victim]
|
||||
do_spasm(victim, source)
|
||||
|
||||
/datum/event/minispasm/proc/do_spasm(var/mob/living/victim, var/obj/item/device/radio/source)
|
||||
set waitfor = 0
|
||||
|
||||
if(victim.psi)
|
||||
playsound(source, 'sound/effects/narsie.ogg', 75) //LOUD AS FUCK BOY
|
||||
to_chat(victim, span("danger", "A hauntingly familiar sound hisses from \icon[source] \the [source], and your vision flickers!"))
|
||||
victim.psi.backblast(rand(5,15))
|
||||
victim.Paralyse(5)
|
||||
victim.make_jittery(100)
|
||||
else
|
||||
to_chat(victim, span("danger", "An indescribable, brain-tearing sound hisses from \icon[source] \the [source], and you collapse in a seizure!"))
|
||||
victim.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)
|
||||
to_chat(victim, span("danger", "<font size = 3>[pick(psi_operancy_messages)]</font>"))
|
||||
victim.adjustBrainLoss(rand(10,20))
|
||||
victim.set_psi_rank(pick_n_take(faculties), 1)
|
||||
sleep(30)
|
||||
victim.psi.update()
|
||||
sleep(45)
|
||||
victim.psi.check_latency_trigger(100, "a psionic scream", redactive = TRUE)
|
||||
|
||||
/datum/event/minispasm/end()
|
||||
command_announcement.Announce( \
|
||||
"PRIORITY ALERT: SIGNAL BROADCAST HAS CEASED. Personnel are cleared to resume use of non-hardened radio transmission equipment. Have a nice day.", \
|
||||
"Jargon Federation Observation Probe TC-203 Sensor Array", new_sound = 'sound/misc/announcements/nightlight_old.ogg')
|
||||
20
code/modules/psionics/events/psi_balm.dm
Normal file
20
code/modules/psionics/events/psi_balm.dm
Normal file
@@ -0,0 +1,20 @@
|
||||
/datum/event/psi/balm
|
||||
var/static/list/balm_messages = list(
|
||||
"A soothing balm washes over your psyche.",
|
||||
"For a moment, you can hear a distant, familiar voice singing a quiet lullaby.",
|
||||
"A sense of peace and comfort falls over you like a warm blanket."
|
||||
)
|
||||
|
||||
/datum/event/psi/balm/apply_psi_effect(var/datum/psi_complexus/psi)
|
||||
var/soothed
|
||||
if(psi.stun > 1)
|
||||
psi.stun--
|
||||
soothed = TRUE
|
||||
else if(psi.stamina < psi.max_stamina)
|
||||
psi.stamina = min(psi.max_stamina, psi.stamina + rand(1,3))
|
||||
soothed = TRUE
|
||||
else if(psi.owner.getBrainLoss() > 0)
|
||||
psi.owner.adjustBrainLoss(-1)
|
||||
soothed = TRUE
|
||||
if(soothed && prob(10))
|
||||
to_chat(psi.owner, span("notice", "<i>[pick(balm_messages)]</i>"))
|
||||
17
code/modules/psionics/events/psi_wail.dm
Normal file
17
code/modules/psionics/events/psi_wail.dm
Normal file
@@ -0,0 +1,17 @@
|
||||
/datum/event/psi/wail
|
||||
var/static/list/whine_messages = list(
|
||||
"A nerve-tearing psychic whine intrudes on your thoughts.",
|
||||
"A horrible, distracting humming sound breaks your train of thought.",
|
||||
"Your head aches as a psychic wail intrudes on your psyche."
|
||||
)
|
||||
|
||||
/datum/event/psi/wail/apply_psi_effect(var/datum/psi_complexus/psi)
|
||||
var/annoyed
|
||||
if(prob(1))
|
||||
psi.stunned(1)
|
||||
annoyed = TRUE
|
||||
else if(psi.stamina > 0)
|
||||
psi.stamina = max(0, psi.stamina - rand(1,3))
|
||||
annoyed = TRUE
|
||||
if(annoyed && prob(1))
|
||||
to_chat(psi.owner, "<span class='notice'><i>[pick(whine_messages)]</i></span>")
|
||||
11
code/modules/psionics/faculties/_faculty.dm
Normal file
11
code/modules/psionics/faculties/_faculty.dm
Normal file
@@ -0,0 +1,11 @@
|
||||
/datum/psionic_faculty
|
||||
var/id
|
||||
var/name
|
||||
var/associated_intent
|
||||
var/list/armour_types = list()
|
||||
var/list/powers = list()
|
||||
|
||||
/datum/psionic_faculty/New()
|
||||
..()
|
||||
for(var/atype in armour_types)
|
||||
SSpsi.armour_faculty_by_type[atype] = id
|
||||
37
code/modules/psionics/faculties/_power.dm
Normal file
37
code/modules/psionics/faculties/_power.dm
Normal file
@@ -0,0 +1,37 @@
|
||||
/datum/psionic_power
|
||||
var/name // Name. If null, psipower won't be generated on roundstart.
|
||||
var/faculty // Associated psi faculty.
|
||||
var/min_rank // Minimum psi rank to use this power.
|
||||
var/cost // Base psi stamina cost for using this power.
|
||||
var/cooldown // Deciseconds cooldown after using this power.
|
||||
var/admin_log = TRUE // Whether or not using this power prints an admin attack log.
|
||||
var/use_ranged // This power functions from a distance.
|
||||
var/use_melee // This power functions at melee range.
|
||||
var/use_grab // This power has a variant invoked via grab.
|
||||
var/use_manifest // This power manifests an item in the user's hands.
|
||||
var/use_description // A short description of how to use this power, shown via assay.
|
||||
// A sound effect to play when the power is used.
|
||||
var/use_sound = 'sound/effects/psi/power_used.ogg'
|
||||
|
||||
/datum/psionic_power/proc/invoke(var/mob/living/user, var/atom/target)
|
||||
|
||||
if(!user.psi)
|
||||
return FALSE
|
||||
|
||||
if(faculty && min_rank)
|
||||
var/user_rank = user.psi.get_rank(faculty)
|
||||
if(user_rank < min_rank)
|
||||
return FALSE
|
||||
|
||||
if(cost && !user.psi.spend_power(cost))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/proc/handle_post_power(var/mob/living/user, var/atom/target)
|
||||
if(cooldown)
|
||||
user.psi.set_cooldown(cooldown)
|
||||
if(admin_log && ismob(user) && ismob(target))
|
||||
admin_attack_log(user, target, "Used psipower ([name])", "Was subjected to a psipower ([name])", "used a psipower ([name]) on")
|
||||
if(use_sound)
|
||||
playsound(user.loc, use_sound, 75)
|
||||
332
code/modules/psionics/faculties/coercion.dm
Normal file
332
code/modules/psionics/faculties/coercion.dm
Normal file
@@ -0,0 +1,332 @@
|
||||
/datum/psionic_faculty/coercion
|
||||
id = PSI_COERCION
|
||||
name = "Coercion"
|
||||
associated_intent = I_DISARM
|
||||
|
||||
/datum/psionic_power/coercion
|
||||
faculty = PSI_COERCION
|
||||
|
||||
/datum/psionic_power/coercion/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if (!istype(target))
|
||||
to_chat(user, span("warning", "You cannot mentally attack \the [target]."))
|
||||
return FALSE
|
||||
|
||||
. = ..()
|
||||
|
||||
/datum/psionic_power/coercion/blindstrike
|
||||
name = "Blindstrike"
|
||||
cost = 8
|
||||
cooldown = 120
|
||||
use_ranged = TRUE
|
||||
use_melee = TRUE
|
||||
min_rank = PSI_RANK_GRANDMASTER
|
||||
use_description = "Target the eyes or mouth on disarm intent and click anywhere to use a radial attack that blinds, deafens and disorients everyone near you."
|
||||
|
||||
/datum/psionic_power/coercion/blindstrike/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(user.zone_sel.selecting != "mouth" && user.zone_sel.selecting != BP_EYES)
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
user.visible_message(span("danger", "\The [user] suddenly throws back their head, as though screaming silently!"))
|
||||
to_chat(user, span("danger", "You strike at all around you with a deafening psionic scream!"))
|
||||
for(var/mob/living/M in orange(user, user.psi.get_rank(PSI_COERCION)))
|
||||
if(M == user)
|
||||
continue
|
||||
if(prob(60) && iscarbon(M))
|
||||
var/mob/living/carbon/C = M
|
||||
if(C.can_feel_pain())
|
||||
M.emote("scream")
|
||||
to_chat(M, span("danger", "Your senses are blasted into oblivion by a psionic scream!"))
|
||||
M.eye_blind = max(M.eye_blind,3)
|
||||
M.ear_deaf = max(M.ear_deaf,6)
|
||||
M.confused = rand(3,8)
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/coercion/mindread
|
||||
name = "Read Mind"
|
||||
cost = 25
|
||||
cooldown = 250 //It should take a WHILE to be able to use this again.
|
||||
use_melee = TRUE
|
||||
min_rank = PSI_RANK_OPERANT
|
||||
use_description = "Target the head on disarm intent at melee range to attempt to read a victim's surface thoughts."
|
||||
|
||||
/datum/psionic_power/coercion/mindread/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(!isliving(target) || !istype(target) || user.zone_sel.selecting != BP_HEAD)
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
|
||||
if(target.stat == DEAD || (target.status_flags & FAKEDEATH) || !target.client)
|
||||
to_chat(user, span("warning", "\The [target] is in no state for a mind-ream."))
|
||||
return TRUE
|
||||
|
||||
user.visible_message(span("warning", "\The [user] touches \the [target]'s temple..."))
|
||||
var/question = input(user, "Say something?", "Read Mind", "Penny for your thoughts?") as null|text
|
||||
if(!question || user.incapacitated() || !do_after(user, 20))
|
||||
return TRUE
|
||||
|
||||
var/started_mindread = world.time
|
||||
to_chat(user, span("notice", "<b>You dip your mentality into the surface layer of \the [target]'s mind, seeking an answer: <i>[question]</i></b>"))
|
||||
to_chat(target, span("notice", "<b>Your mind is compelled to answer: <i>[question]</i></b>"))
|
||||
|
||||
var/answer = input(target, question, "Read Mind") as null|text
|
||||
if(!answer || world.time > started_mindread + 25 SECONDS || user.stat != CONSCIOUS || target.stat == DEAD)
|
||||
to_chat(user, span("notice", "<b>You receive nothing useful from \the [target].</b>"))
|
||||
else
|
||||
to_chat(user, span("notice", "<b>You skim thoughts from the surface of \the [target]'s mind: <i>[answer]</i></b>"))
|
||||
msg_admin_attack("[key_name(user)] read mind of [key_name(target)] with question \"[question]\" and [answer?"got answer \"[answer]\".":"got no answer."]")
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/coercion/agony
|
||||
name = "Agony"
|
||||
cost = 8
|
||||
cooldown = 50
|
||||
use_melee = TRUE
|
||||
min_rank = PSI_RANK_MASTER
|
||||
use_description = "Target the chest or groin on disarm intent to use a melee attack equivalent to a strike from a stun baton."
|
||||
|
||||
/datum/psionic_power/coercion/agony/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(!istype(target))
|
||||
return FALSE
|
||||
if(user.zone_sel.selecting != BP_CHEST && user.zone_sel.selecting != BP_GROIN)
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
user.visible_message("<span class='danger'>\The [target] has been struck by \the [user]!</span>")
|
||||
playsound(user.loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
|
||||
target.stun_effect_act(0, 60, user.zone_sel.selecting)
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/coercion/spasm
|
||||
name = "Spasm"
|
||||
cost = 15
|
||||
cooldown = 100
|
||||
use_melee = TRUE
|
||||
use_ranged = TRUE
|
||||
min_rank = PSI_RANK_MASTER
|
||||
use_description = "Target the arms or hands on disarm intent to use a ranged attack that may rip the weapons away from the target."
|
||||
|
||||
/datum/psionic_power/coercion/spasm/invoke(var/mob/living/user, var/mob/living/carbon/human/target)
|
||||
if(!istype(target))
|
||||
return FALSE
|
||||
|
||||
if(!(user.zone_sel.selecting in list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND)))
|
||||
return FALSE
|
||||
|
||||
. = ..()
|
||||
|
||||
if(.)
|
||||
to_chat(user, "<span class='danger'>You lash out, stabbing into \the [target] with a lance of psi-power.</span>")
|
||||
to_chat(target, "<span class='danger'>The muscles in your arms cramp horrendously!</span>")
|
||||
if(prob(75))
|
||||
target.emote("scream")
|
||||
if(prob(75) && target.l_hand && target.l_hand.simulated && target.unEquip(target.l_hand))
|
||||
target.visible_message("<span class='danger'>\The [target] drops what they were holding as their left hand spasms!</span>")
|
||||
if(prob(75) && target.r_hand && target.r_hand.simulated && target.unEquip(target.r_hand))
|
||||
target.visible_message("<span class='danger'>\The [target] drops what they were holding as their right hand spasms!</span>")
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/coercion/mindslave
|
||||
name = "Mindslave"
|
||||
cost = 28
|
||||
cooldown = 200
|
||||
use_grab = TRUE
|
||||
min_rank = PSI_RANK_PARAMOUNT
|
||||
use_description = "Grab a victim, target the eyes, then use the grab on them while on disarm intent, in order to convert them into a loyal mind-slave. The process takes some time, and failure is punished harshly."
|
||||
|
||||
/datum/psionic_power/coercion/mindslave/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(!istype(target) || user.zone_sel.selecting != BP_EYES)
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
if(target.stat == DEAD || (target.status_flags & FAKEDEATH))
|
||||
to_chat(user, "<span class='warning'>\The [target] is dead!</span>")
|
||||
return TRUE
|
||||
if(!target.mind || !target.key)
|
||||
to_chat(user, "<span class='warning'>\The [target] is mindless!</span>")
|
||||
return TRUE
|
||||
user.visible_message("<span class='danger'><i>\The [user] seizes the head of \the [target] in both hands...</i></span>")
|
||||
to_chat(user, "<span class='warning'>You plunge your mentality into that of \the [target]...</span>")
|
||||
to_chat(target, "<span class='danger'>Your mind is invaded by the presence of \the [user]! They are trying to make you a slave!</span>")
|
||||
if(!do_after(user, target.stat == CONSCIOUS ? 80 : 40, target, 0, 1))
|
||||
user.psi.backblast(rand(10,25))
|
||||
return TRUE
|
||||
to_chat(user, "<span class='danger'>You sear through \the [target]'s neurons, reshaping as you see fit and leaving them subservient to your will!</span>")
|
||||
to_chat(target, "<span class='danger'>Your defenses have eroded away and \the [user] has made you their mindslave.</span>")
|
||||
thralls.add_antagonist(target.mind, TRUE, TRUE, FALSE, TRUE, TRUE)
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/coercion/assay
|
||||
name = "Assay"
|
||||
cost = 15
|
||||
cooldown = 100
|
||||
use_grab = TRUE
|
||||
min_rank = PSI_RANK_OPERANT
|
||||
use_description = "Grab a patient, target the head, then use the grab on them while on disarm intent, in order to perform a deep coercive-redactive probe of their psionic potential."
|
||||
|
||||
/datum/psionic_power/coercion/assay/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(user.zone_sel.selecting != BP_HEAD)
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
user.visible_message(span("warning", "\The [user] holds the head of \the [target] in both hands..."))
|
||||
to_chat(user, span("notice", "You insinuate your mentality into that of \the [target]..."))
|
||||
to_chat(target, span("warning", "Your persona is being probed by the psychic lens of \the [user]."))
|
||||
if(!do_after(user, (target.stat == CONSCIOUS ? 50 : 25), target, 0, 1))
|
||||
user.psi.backblast(rand(5,10))
|
||||
return TRUE
|
||||
to_chat(user, span("notice", "You retreat from \the [target], holding your new knowledge close."))
|
||||
to_chat(target, span("danger", "Your mental complexus is laid bare to judgement of \the [user]."))
|
||||
target.show_psi_assay(user)
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/coercion/focus
|
||||
name = "Focus"
|
||||
cost = 10
|
||||
cooldown = 80
|
||||
use_grab = TRUE
|
||||
min_rank = PSI_RANK_MASTER
|
||||
use_description = "Grab a patient, target the mouth, then use the grab on them while on disarm intent, in order to cure ailments of the mind."
|
||||
|
||||
/datum/psionic_power/coercion/focus/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(user.zone_sel.selecting != "mouth")
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
user.visible_message(span("warning", "\The [user] holds the head of \the [target] in both hands..."))
|
||||
to_chat(user, span("notice", "You probe \the [target]'s mind for various ailments.."))
|
||||
to_chat(target, span("warning", "Your mind is being cleansed of ailments by \the [user]."))
|
||||
if(!do_after(user, (target.stat == CONSCIOUS ? 50 : 25), target, 0, 1))
|
||||
user.psi.backblast(rand(5,10))
|
||||
return TRUE
|
||||
to_chat(user, span("warning", "You clear \the [target]'s mind of ailments."))
|
||||
to_chat(target, span("warning", "Your mind is cleared of ailments."))
|
||||
|
||||
var/coercion_rank = user.psi.get_rank(PSI_COERCION)
|
||||
if(coercion_rank >= PSI_RANK_GRANDMASTER)
|
||||
target.AdjustParalysis(-1)
|
||||
target.drowsyness = 0
|
||||
if(istype(target, /mob/living/carbon))
|
||||
var/mob/living/carbon/M = target
|
||||
M.hallucination = max(M.hallucination, 10)
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/coercion/commune
|
||||
name = "Commune"
|
||||
cost = 10
|
||||
cooldown = 80
|
||||
use_melee = TRUE
|
||||
use_ranged = TRUE
|
||||
min_rank = PSI_RANK_OPERANT
|
||||
use_description = "Target the mouth and click on a creature on disarm intent to psionically send them a message."
|
||||
|
||||
/datum/psionic_power/coercion/commune/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(user.zone_sel.selecting != "mouth")
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
user.visible_message("<i><span class='notice'>[user] touches their fingers to their temple.</span></i>")
|
||||
var/text = input("What would you like to say?", "Speak to creature", null, null)
|
||||
text = sanitize(text)
|
||||
|
||||
if(!text)
|
||||
return
|
||||
|
||||
if(target.stat == DEAD)
|
||||
to_chat(user,"<span class='cult'>Not even a psion of your level can speak to the dead.</span>")
|
||||
return
|
||||
|
||||
if (target.isSynthetic())
|
||||
to_chat(user,"<span class='warning'>This can only be used on living organisms.</span>")
|
||||
return
|
||||
|
||||
if (target.is_diona())
|
||||
to_chat(user,"<span class='alium'>The creature's mind is incompatible, formless.</span>")
|
||||
return
|
||||
|
||||
if (isvaurca(target))
|
||||
to_chat (user, "<span class='cult'>You feel your thoughts pass right through a mind empty of psychic energy.</span>")
|
||||
return
|
||||
|
||||
log_say("[key_name(user)] communed to [key_name(target)]: [text]",ckey=key_name(src))
|
||||
|
||||
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 (target.can_commune())
|
||||
to_chat(H,"<b>You instinctively sense [user] sending their thoughts into your mind, hearing:</b> [text]")
|
||||
else if(prob(25) && (target.mind && target.mind.assigned_role=="Chaplain"))
|
||||
to_chat(H,"<b>You sense [user]'s psyche enter your mind, whispering quietly:</b> [text]")
|
||||
else
|
||||
to_chat(H,"<b>You feel something crawl behind your eyes, hearing:</b> [text]")
|
||||
if(istype(H))
|
||||
if (H.can_commune())
|
||||
return
|
||||
if(prob(10) && !(H.species.flags & NO_BLOOD))
|
||||
to_chat(H,"<span class='warning'>Your nose begins to bleed...</span>")
|
||||
H.drip(3)
|
||||
else if(prob(25) && (H.can_feel_pain()))
|
||||
to_chat(H,"<span class='warning'>Your head hurts...</span>")
|
||||
else if(prob(50))
|
||||
to_chat(H,"<span class='warning'>Your mind buzzes...</span>")
|
||||
|
||||
/datum/psionic_power/coercion/psiping
|
||||
name = "Psi-ping"
|
||||
cost = 30
|
||||
cooldown = 250
|
||||
use_melee = TRUE
|
||||
min_rank = PSI_RANK_OPERANT
|
||||
use_description = "Click on yourself with an empty hand on disarm intent to detect nearby psionic signatures."
|
||||
|
||||
/datum/psionic_power/coercion/psiping/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if((target && user != target))
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
to_chat(user, "<span class='notice'>You take a moment to tune into the local Nlom...</span>")
|
||||
if(!do_after(user, 3 SECONDS))
|
||||
return
|
||||
var/list/dirs = list()
|
||||
for(var/mob/living/L in range(20))
|
||||
var/turf/T = get_turf(L)
|
||||
if(!T || L == user || L.stat == DEAD || L.isSynthetic() || L.is_diona() || isvaurca(L) || L.invisibility == INVISIBILITY_LEVEL_TWO)
|
||||
continue
|
||||
var/image/ping_image = image(icon = 'icons/effects/effects.dmi', icon_state = "sonar_ping", loc = user)
|
||||
ping_image.plane = LIGHTING_LAYER+1
|
||||
ping_image.layer = LIGHTING_LAYER+1
|
||||
ping_image.pixel_x = (T.x - user.x) * WORLD_ICON_SIZE
|
||||
ping_image.pixel_y = (T.y - user.y) * WORLD_ICON_SIZE
|
||||
user << ping_image
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/qdel, ping_image), 8)
|
||||
var/direction = num2text(get_dir(user, L))
|
||||
var/dist
|
||||
if(text2num(direction))
|
||||
switch(get_dist(user, L) / user.client.view)
|
||||
if(0 to 0.2)
|
||||
dist = "very close"
|
||||
if(0.2 to 0.4)
|
||||
dist = "close"
|
||||
if(0.4 to 0.6)
|
||||
dist = "a little ways away"
|
||||
if(0.6 to 0.8)
|
||||
dist = "farther away"
|
||||
else
|
||||
dist = "far away"
|
||||
else
|
||||
dist = "on top of you"
|
||||
LAZYINITLIST(dirs[direction])
|
||||
dirs[direction][dist] += 1
|
||||
for(var/d in dirs)
|
||||
var/list/feedback = list()
|
||||
for(var/dst in dirs[d])
|
||||
feedback += "[dirs[d][dst]] psionic signature\s [dst],"
|
||||
if(feedback.len > 1)
|
||||
feedback[feedback.len - 1] += " and"
|
||||
to_chat(user, span("notice", "You sense " + jointext(feedback, " ") + " towards the [dir2text(text2num(d))]."))
|
||||
if(!length(dirs))
|
||||
to_chat(user, span("notice", "You detect no psionic signatures but your own."))
|
||||
113
code/modules/psionics/faculties/energistics.dm
Normal file
113
code/modules/psionics/faculties/energistics.dm
Normal file
@@ -0,0 +1,113 @@
|
||||
/datum/psionic_faculty/energistics
|
||||
id = PSI_ENERGISTICS
|
||||
name = "Energistics"
|
||||
associated_intent = I_HURT
|
||||
armour_types = list("bomb", "laser", "energy")
|
||||
|
||||
/datum/psionic_power/energistics
|
||||
faculty = PSI_ENERGISTICS
|
||||
|
||||
/datum/psionic_power/energistics/disrupt
|
||||
name = "Disrupt"
|
||||
cost = 10
|
||||
cooldown = 100
|
||||
use_melee = TRUE
|
||||
min_rank = PSI_RANK_MASTER
|
||||
use_description = "Target the head, eyes or mouth while on harm intent to use a melee attack that causes a localized electromagnetic pulse."
|
||||
|
||||
/datum/psionic_power/energistics/disrupt/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(user.zone_sel.selecting != BP_HEAD && user.zone_sel.selecting != BP_EYES && user.zone_sel.selecting != "mouth")
|
||||
return FALSE
|
||||
if(istype(target, /turf))
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
user.visible_message("<span class='danger'>\The [user] releases a gout of crackling static and arcing lightning over \the [target]!</span>")
|
||||
empulse(target, 0, 1)
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/energistics/electrocute
|
||||
name = "Electrocute"
|
||||
cost = 15
|
||||
cooldown = 25
|
||||
use_melee = TRUE
|
||||
min_rank = PSI_RANK_GRANDMASTER
|
||||
use_description = "Target the chest or groin while on harm intent to use a melee attack that electrocutes a victim."
|
||||
|
||||
/datum/psionic_power/energistics/electrocute/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(user.zone_sel.selecting != BP_CHEST && user.zone_sel.selecting != BP_GROIN)
|
||||
return FALSE
|
||||
if(istype(target, /turf))
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
user.visible_message("<span class='danger'>\The [user] sends a jolt of electricity arcing into \the [target]!</span>")
|
||||
if(istype(target))
|
||||
target.electrocute_act(rand(15,45), user, 1, user.zone_sel.selecting)
|
||||
return TRUE
|
||||
else if(istype(target, /atom))
|
||||
var/obj/item/cell/charging_cell = target.get_cell()
|
||||
if(istype(charging_cell))
|
||||
charging_cell.give(rand(15,45))
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/energistics/zorch
|
||||
name = "Zorch"
|
||||
cost = 20
|
||||
cooldown = 20
|
||||
use_ranged = TRUE
|
||||
min_rank = PSI_RANK_MASTER
|
||||
use_description = "Use this ranged laser attack while on harm intent. Your mastery of Energistics will determine how powerful the laser is. Be wary of overuse, and try not to fry your own brain."
|
||||
|
||||
/datum/psionic_power/energistics/zorch/invoke(var/mob/living/user, var/mob/living/target)
|
||||
. = ..()
|
||||
if(.)
|
||||
user.visible_message("<span class='danger'>\The [user]'s eyes flare with light!</span>")
|
||||
|
||||
var/user_rank = user.psi.get_rank(faculty)
|
||||
var/obj/item/projectile/pew
|
||||
var/pew_sound
|
||||
|
||||
switch(user_rank)
|
||||
if(PSI_RANK_PARAMOUNT)
|
||||
pew = new /obj/item/projectile/beam/heavylaser(get_turf(user))
|
||||
pew.name = "gigawatt mental laser"
|
||||
pew_sound = 'sound/weapons/lasercannonfire.ogg'
|
||||
if(PSI_RANK_GRANDMASTER)
|
||||
pew = new /obj/item/projectile/beam/midlaser(get_turf(user))
|
||||
pew.name = "megawatt mental laser"
|
||||
pew_sound = 'sound/weapons/Laser.ogg'
|
||||
if(PSI_RANK_MASTER)
|
||||
pew = new /obj/item/projectile/beam/stun(get_turf(user))
|
||||
pew.name = "mental laser"
|
||||
pew_sound = 'sound/weapons/Taser.ogg'
|
||||
|
||||
if(istype(pew))
|
||||
playsound(pew.loc, pew_sound, 25, 1)
|
||||
pew.original = target
|
||||
pew.starting = get_turf(user)
|
||||
pew.shot_from = user
|
||||
pew.launch_projectile(target)
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/energistics/spark
|
||||
name = "Spark"
|
||||
cost = 1
|
||||
cooldown = 1
|
||||
use_melee = TRUE
|
||||
min_rank = PSI_RANK_OPERANT
|
||||
use_description = "Target a non-living target in melee range on harm intent to cause some sparks to appear. This can light fires."
|
||||
|
||||
/datum/psionic_power/energistics/spark/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(isnull(target) || istype(target)) return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
if(istype(target,/obj/item/clothing/mask/smokable/cigarette))
|
||||
var/obj/item/clothing/mask/smokable/cigarette/S = target
|
||||
S.light("[user] snaps \his fingers and \the [S.name] lights up.")
|
||||
playsound(S.loc, "sparks", 50, 1)
|
||||
else
|
||||
var/datum/effect_system/sparks/spark_system
|
||||
spark_system = bind_spark(src, 3)
|
||||
spark_system.queue()
|
||||
return TRUE
|
||||
92
code/modules/psionics/faculties/psychokinesis.dm
Normal file
92
code/modules/psionics/faculties/psychokinesis.dm
Normal file
@@ -0,0 +1,92 @@
|
||||
/datum/psionic_faculty/psychokinesis
|
||||
id = PSI_PSYCHOKINESIS
|
||||
name = "Psychokinesis"
|
||||
associated_intent = I_GRAB
|
||||
armour_types = list("melee", "bullet")
|
||||
|
||||
/datum/psionic_power/psychokinesis
|
||||
faculty = PSI_PSYCHOKINESIS
|
||||
use_manifest = TRUE
|
||||
use_sound = null
|
||||
|
||||
/datum/psionic_power/psychokinesis/psiblade
|
||||
name = "Psiblade"
|
||||
cost = 10
|
||||
cooldown = 30
|
||||
min_rank = PSI_RANK_OPERANT
|
||||
use_description = "Click on or otherwise activate an empty hand while on harm intent to manifest a psychokinetic cutting blade. The power the blade will vary based on your mastery of the faculty."
|
||||
admin_log = FALSE
|
||||
|
||||
/datum/psionic_power/psychokinesis/psiblade/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if((target && user != target) || user.a_intent != I_HURT)
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
switch(user.psi.get_rank(faculty))
|
||||
if(PSI_RANK_PARAMOUNT)
|
||||
return new /obj/item/psychic_power/psiblade/master/grand/paramount(user, user)
|
||||
if(PSI_RANK_GRANDMASTER)
|
||||
return new /obj/item/psychic_power/psiblade/master/grand(user, user)
|
||||
if(PSI_RANK_MASTER)
|
||||
return new /obj/item/psychic_power/psiblade/master(user, user)
|
||||
else
|
||||
return new /obj/item/psychic_power/psiblade(user, user)
|
||||
|
||||
/datum/psionic_power/psychokinesis/tinker
|
||||
name = "Tinker"
|
||||
cost = 5
|
||||
cooldown = 10
|
||||
min_rank = PSI_RANK_MASTER
|
||||
use_description = "Click on or otherwise activate an empty hand while on help intent to manifest a psychokinetic tool. Use it in-hand to switch between tool types."
|
||||
admin_log = FALSE
|
||||
|
||||
/datum/psionic_power/psychokinesis/tinker/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if((target && user != target) || user.a_intent != I_HELP)
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
return new /obj/item/psychic_power/tinker(user)
|
||||
|
||||
/datum/psionic_power/psychokinesis/telekinesis
|
||||
name = "Telekinesis"
|
||||
cost = 5
|
||||
cooldown = 10
|
||||
use_ranged = TRUE
|
||||
use_manifest = FALSE
|
||||
min_rank = PSI_RANK_GRANDMASTER
|
||||
use_description = "Click on a distant target while on grab intent to manifest a psychokinetic grip. Use it manipulate objects at a distance."
|
||||
admin_log = FALSE
|
||||
use_sound = 'sound/effects/psi/power_used.ogg'
|
||||
var/global/list/valid_machine_types = list(
|
||||
/obj/machinery/door
|
||||
)
|
||||
|
||||
/datum/psionic_power/psychokinesis/telekinesis/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(user.a_intent != I_GRAB)
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
|
||||
var/distance = get_dist(user, target)
|
||||
if(distance > user.psi.get_rank(PSI_PSYCHOKINESIS))
|
||||
to_chat(user, "<span class='warning'>Your telekinetic power won't reach that far.</span>")
|
||||
return FALSE
|
||||
|
||||
if(istype(target, /mob) || istype(target, /obj))
|
||||
var/obj/item/psychic_power/telekinesis/tk = new(user)
|
||||
if(tk.set_focus(target))
|
||||
tk.sparkle()
|
||||
user.visible_message("<span class='notice'>\The [user] reaches out.</span>")
|
||||
return tk
|
||||
else if(istype(target, /obj/structure))
|
||||
user.visible_message("<span class='notice'>\The [user] makes a strange gesture.</span>")
|
||||
var/obj/O = target
|
||||
O.attack_hand(user)
|
||||
return TRUE
|
||||
else if(istype(target, /obj/machinery))
|
||||
for(var/mtype in valid_machine_types)
|
||||
if(istype(target, mtype))
|
||||
var/obj/machinery/machine = target
|
||||
machine.attack_hand(user)
|
||||
return TRUE
|
||||
return FALSE
|
||||
186
code/modules/psionics/faculties/redaction.dm
Normal file
186
code/modules/psionics/faculties/redaction.dm
Normal file
@@ -0,0 +1,186 @@
|
||||
/datum/psionic_faculty/redaction
|
||||
id = PSI_REDACTION
|
||||
name = "Redaction"
|
||||
associated_intent = I_HELP
|
||||
armour_types = list("bio", "rad")
|
||||
|
||||
/datum/psionic_power/redaction
|
||||
faculty = PSI_REDACTION
|
||||
admin_log = FALSE
|
||||
|
||||
/datum/psionic_power/redaction/proc/check_dead(var/mob/living/target)
|
||||
if(!istype(target))
|
||||
return FALSE
|
||||
if(target.stat == DEAD || (target.status_flags & FAKEDEATH))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/psionic_power/redaction/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(check_dead(target))
|
||||
return FALSE
|
||||
. = ..()
|
||||
|
||||
/datum/psionic_power/redaction/skinsight
|
||||
name = "Skinsight"
|
||||
cost = 3
|
||||
cooldown = 30
|
||||
use_grab = TRUE
|
||||
min_rank = PSI_RANK_OPERANT
|
||||
use_description = "Grab a patient, target the chest, then switch to help intent and use the grab on them to perform a check for wounds and damage."
|
||||
|
||||
/datum/psionic_power/redaction/skinsight/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(user.zone_sel.selecting != BP_CHEST)
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
user.visible_message(span("notice", "\The [user] rests a hand on \the [target]."))
|
||||
health_scan_mob(target, user, TRUE)
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/redaction/mend
|
||||
name = "Mend"
|
||||
cost = 7
|
||||
cooldown = 50
|
||||
use_melee = TRUE
|
||||
min_rank = PSI_RANK_OPERANT
|
||||
use_description = "Target a patient while on help intent at melee range to mend a variety of maladies, such as bleeding or broken bones. Higher ranks in this faculty allow you to mend a wider range of problems."
|
||||
|
||||
/datum/psionic_power/redaction/mend/invoke(var/mob/living/user, var/mob/living/carbon/human/target)
|
||||
if(!istype(user) || !istype(target))
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
var/obj/item/organ/external/E = target.get_organ(user.zone_sel.selecting)
|
||||
|
||||
if(!E || E.is_stump())
|
||||
to_chat(user, span("warning", "They are missing that limb."))
|
||||
return TRUE
|
||||
|
||||
if(BP_IS_ROBOTIC(E))
|
||||
to_chat(user, span("warning", "That limb is prosthetic."))
|
||||
return TRUE
|
||||
|
||||
user.visible_message(span("notice", "<i>\The [user] rests a hand on \the [target]'s [E.name]...</i>"))
|
||||
to_chat(target, span("notice", "A healing warmth suffuses you."))
|
||||
|
||||
var/redaction_rank = user.psi.get_rank(PSI_REDACTION)
|
||||
var/pk_rank = user.psi.get_rank(PSI_PSYCHOKINESIS)
|
||||
if(pk_rank >= PSI_RANK_LATENT && redaction_rank >= PSI_RANK_MASTER)
|
||||
var/removal_size = Clamp(5-pk_rank, 0, 5)
|
||||
var/valid_objects = list()
|
||||
for(var/thing in E.implants)
|
||||
var/obj/imp = thing
|
||||
if(imp.w_class >= removal_size && !istype(imp, /obj/item/implant))
|
||||
valid_objects += imp
|
||||
if(LAZYLEN(valid_objects))
|
||||
var/removing = pick(valid_objects)
|
||||
target.remove_implant(removing, TRUE)
|
||||
to_chat(user, span("notice", "You extend a tendril of psychokinetic-redactive power and carefully tease \the [removing] free of \the [E]."))
|
||||
return TRUE
|
||||
|
||||
if(redaction_rank >= PSI_RANK_MASTER)
|
||||
if(E.status & ORGAN_ARTERY_CUT)
|
||||
to_chat(user, span("notice", "You painstakingly mend the torn veins in \the [E], stemming the internal bleeding."))
|
||||
E.status &= ~ORGAN_ARTERY_CUT
|
||||
return TRUE
|
||||
if(E.status & ORGAN_TENDON_CUT)
|
||||
to_chat(user, span("notice", "You interleave and repair the severed tendon in \the [E]."))
|
||||
E.status &= ~ORGAN_TENDON_CUT
|
||||
return TRUE
|
||||
if(E.status & ORGAN_BROKEN)
|
||||
to_chat(user, span("notice", "You coax shattered bones to come together and fuse, mending the break."))
|
||||
E.status &= ~ORGAN_BROKEN
|
||||
E.stage = 0
|
||||
return TRUE
|
||||
|
||||
for(var/datum/wound/W in E.wounds)
|
||||
if(W.bleeding())
|
||||
if(redaction_rank >= PSI_RANK_MASTER || W.wound_damage() < 30)
|
||||
to_chat(user, span("notice", "You knit together severed veins and broken flesh, stemming the bleeding."))
|
||||
W.bleed_timer = 0
|
||||
W.clamped = TRUE
|
||||
E.status &= ~ORGAN_BLEEDING
|
||||
return TRUE
|
||||
else
|
||||
to_chat(user, span("notice", "This [W.desc] is beyond your power to heal."))
|
||||
|
||||
if(redaction_rank >= PSI_RANK_GRANDMASTER)
|
||||
for(var/obj/item/organ/internal/I in E.internal_organs)
|
||||
if(!BP_IS_ROBOTIC(I) && I.damage > 0)
|
||||
to_chat(user, span("notice", "You encourage the damaged tissue of \the [I] to repair itself."))
|
||||
var/heal_rate = redaction_rank
|
||||
I.damage = max(0, I.damage - rand(heal_rate,heal_rate*2))
|
||||
return TRUE
|
||||
|
||||
to_chat(user, span("notice", "You can find nothing within \the [target]'s [E.name] to mend."))
|
||||
return FALSE
|
||||
|
||||
/datum/psionic_power/redaction/cleanse
|
||||
name = "Cleanse"
|
||||
cost = 9
|
||||
cooldown = 60
|
||||
use_melee = TRUE
|
||||
min_rank = PSI_RANK_GRANDMASTER
|
||||
use_description = "Target a patient while on help intent at melee range to cleanse radiation and genetic damage from a patient."
|
||||
|
||||
/datum/psionic_power/redaction/cleanse/invoke(var/mob/living/user, var/mob/living/carbon/human/target)
|
||||
if(!istype(user) || !istype(target))
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
// No messages, as Mend procs them even if it fails to heal anything, and Cleanse is always checked after Mend.
|
||||
var/removing = rand(20,25)
|
||||
if(target.total_radiation)
|
||||
to_chat(user, span("notice", "You repair some of the radiation-damaged tissue within \the [target]..."))
|
||||
if(target.total_radiation > removing)
|
||||
target.total_radiation -= removing
|
||||
else
|
||||
target.total_radiation = 0
|
||||
return TRUE
|
||||
if(target.getCloneLoss())
|
||||
to_chat(user, span("notice", "You stitch together some of the mangled DNA within \the [target]..."))
|
||||
if(target.getCloneLoss() >= removing)
|
||||
target.adjustCloneLoss(-removing)
|
||||
else
|
||||
target.adjustCloneLoss(-(target.getCloneLoss()))
|
||||
return TRUE
|
||||
to_chat(user, span("notice", "You can find no genetic damage or radiation to heal within \the [target]."))
|
||||
return TRUE
|
||||
|
||||
/datum/psionic_power/revive
|
||||
name = "Revive"
|
||||
cost = 25
|
||||
cooldown = 80
|
||||
use_grab = TRUE
|
||||
min_rank = PSI_RANK_PARAMOUNT
|
||||
faculty = PSI_REDACTION
|
||||
use_description = "Obtain a grab on a dead target, target the head, then select help intent and use the grab against them to attempt to bring them back to life. The process is lengthy and failure is punished harshly."
|
||||
admin_log = FALSE
|
||||
|
||||
/datum/psionic_power/revive/invoke(var/mob/living/user, var/mob/living/target)
|
||||
if(!isliving(target) || !istype(target) || user.zone_sel.selecting != BP_HEAD)
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
if(target.stat != DEAD && !(target.status_flags & FAKEDEATH))
|
||||
to_chat(user, span("warning", "This person is already alive!"))
|
||||
return TRUE
|
||||
|
||||
if((world.time - target.timeofdeath) > 6000)
|
||||
to_chat(user, span("warning", "\The [target] has been dead for too long to revive."))
|
||||
return TRUE
|
||||
|
||||
user.visible_message(span("notice", "<i>\The [user] splays out their hands over \the [target]'s body...</i>"))
|
||||
if(!do_after(user, 100, target, 0, 1))
|
||||
user.psi.backblast(rand(10,25))
|
||||
return TRUE
|
||||
|
||||
for(var/mob/abstract/observer/G in dead_mob_list)
|
||||
if(G.mind && G.mind.current == target && G.client)
|
||||
to_chat(G, span("notice", "<font size = 3><b>Your body has been revived, <b>Re-Enter Corpse</b> to return to it.</b></font>"))
|
||||
break
|
||||
to_chat(target, span("notice", "<font size = 3><b>Life floods back into your body!</b></font>"))
|
||||
target.visible_message(span("notice", "\The [target] shudders violently!"))
|
||||
target.adjustOxyLoss(-rand(15,20))
|
||||
target.basic_revival()
|
||||
return TRUE
|
||||
14
code/modules/psionics/interface/ui.dm
Normal file
14
code/modules/psionics/interface/ui.dm
Normal file
@@ -0,0 +1,14 @@
|
||||
/obj/screen/psi
|
||||
icon = 'icons/mob/screen/psi.dmi'
|
||||
var/mob/living/owner
|
||||
var/hidden = TRUE
|
||||
|
||||
/obj/screen/psi/New(var/mob/living/_owner)
|
||||
loc = null
|
||||
owner = _owner
|
||||
update_icon()
|
||||
|
||||
/obj/screen/psi/Destroy()
|
||||
if(owner && owner.client)
|
||||
owner.client.screen -= src
|
||||
. = ..()
|
||||
54
code/modules/psionics/interface/ui_hub.dm
Normal file
54
code/modules/psionics/interface/ui_hub.dm
Normal file
@@ -0,0 +1,54 @@
|
||||
/obj/screen/psi/hub
|
||||
name = "Psi"
|
||||
icon_state = "psi_suppressed"
|
||||
screen_loc = "EAST-1:28,CENTER-3:11"
|
||||
hidden = FALSE
|
||||
maptext_x = 6
|
||||
maptext_y = -8
|
||||
var/image/on_cooldown
|
||||
|
||||
/obj/screen/psi/hub/New(var/mob/living/_owner)
|
||||
on_cooldown = image(icon, "cooldown")
|
||||
..()
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
|
||||
/obj/screen/psi/hub/update_icon()
|
||||
|
||||
if(!owner.psi)
|
||||
return
|
||||
|
||||
icon_state = owner.psi.suppressed ? "psi_suppressed" : "psi_active"
|
||||
|
||||
/obj/screen/psi/hub/Destroy()
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
owner = null
|
||||
. = ..()
|
||||
|
||||
/obj/screen/psi/hub/process()
|
||||
if(!istype(owner))
|
||||
qdel(src)
|
||||
return
|
||||
if(!owner.psi)
|
||||
return
|
||||
maptext = "[round((owner.psi.stamina/owner.psi.max_stamina)*100)]%"
|
||||
update_icon()
|
||||
|
||||
/obj/screen/psi/hub/Click(var/location, var/control, var/params)
|
||||
var/list/click_params = params2list(params)
|
||||
if(click_params["shift"])
|
||||
owner.show_psi_assay(owner)
|
||||
return
|
||||
|
||||
if(owner.psi.suppressed && owner.psi.stun)
|
||||
to_chat(owner, "<span class='warning'>You are dazed and reeling, and cannot muster enough focus to do that!</span>")
|
||||
return
|
||||
|
||||
owner.psi.suppressed = !owner.psi.suppressed
|
||||
to_chat(owner, "<span class='notice'>You are <b>[owner.psi.suppressed ? "now suppressing" : "no longer suppressing"]</b> your psi-power.</span>")
|
||||
if(owner.psi.suppressed)
|
||||
owner.psi.cancel()
|
||||
owner.psi.hide_auras()
|
||||
else
|
||||
sound_to(owner, sound('sound/effects/psi/power_unlock.ogg'))
|
||||
owner.psi.show_auras()
|
||||
update_icon()
|
||||
20
code/modules/psionics/mob/mob.dm
Normal file
20
code/modules/psionics/mob/mob.dm
Normal file
@@ -0,0 +1,20 @@
|
||||
/mob/living
|
||||
var/datum/psi_complexus/psi
|
||||
|
||||
/mob/living/Login()
|
||||
. = ..()
|
||||
if(psi)
|
||||
psi.update(TRUE)
|
||||
if(!psi.suppressed)
|
||||
psi.show_auras()
|
||||
|
||||
/mob/living/Destroy()
|
||||
QDEL_NULL(psi)
|
||||
. = ..()
|
||||
|
||||
/mob/living/proc/set_psi_rank(var/faculty, var/rank, var/take_larger, var/defer_update, var/temporary)
|
||||
if(!psi)
|
||||
psi = new(src)
|
||||
var/current_rank = psi.get_rank(faculty)
|
||||
if(current_rank != rank && (!take_larger || current_rank < rank))
|
||||
psi.set_rank(faculty, rank, defer_update, temporary)
|
||||
80
code/modules/psionics/mob/mob_assay.dm
Normal file
80
code/modules/psionics/mob/mob_assay.dm
Normal file
@@ -0,0 +1,80 @@
|
||||
/mob/living/proc/show_psi_assay(var/mob/viewer)
|
||||
|
||||
if(!viewer) viewer = usr
|
||||
|
||||
var/use_He_is = "You are"
|
||||
var/use_He_has = "You have"
|
||||
if(istype(machine) || viewer != src)
|
||||
var/datum/gender/G = gender_datums[gender]
|
||||
use_He_is = "[G.He] [G.is]"
|
||||
use_He_has = "[G.He] [G.has]"
|
||||
|
||||
var/list/dat = list()
|
||||
|
||||
dat += "<h2>Summary</h2>"
|
||||
dat += "<hr>"
|
||||
|
||||
if(psi)
|
||||
|
||||
// Hi Warhammer 40k rating system, how are you?
|
||||
// I hope you get along with the Galactic Milieu metapsychics.
|
||||
var/use_rating
|
||||
var/effective_rating = psi.rating
|
||||
if(effective_rating > 1 && psi.suppressed)
|
||||
effective_rating = max(0, psi.rating-2)
|
||||
var/rating_descriptor
|
||||
|
||||
if(viewer != usr && thralls.is_antagonist(mind) && ishuman(viewer))
|
||||
var/mob/living/H = viewer
|
||||
if(H.psi && H.psi.get_rank(PSI_REDACTION) >= PSI_RANK_GRANDMASTER)
|
||||
dat += "<font color='#FF0000'><b>Their mind has been cored like an apple, and enslaved by another operant psychic.</b></font>"
|
||||
|
||||
if(!use_rating)
|
||||
switch(effective_rating)
|
||||
if(1)
|
||||
use_rating = "[effective_rating]-Epsilon"
|
||||
rating_descriptor = "This indicates the presence of minor latent psi potential with little or no operant capabilities."
|
||||
if(2)
|
||||
use_rating = "[effective_rating]-Delta"
|
||||
rating_descriptor = "This indicates the presence of minor psi capabilities of the Operant rank or higher."
|
||||
if(3)
|
||||
use_rating = "<font color = '#F4F441'>[effective_rating]-Gamma</font>"
|
||||
rating_descriptor = "This indicates the presence of psi capabilities of the Master rank or higher."
|
||||
if(4)
|
||||
use_rating = "<font color = '#F4BC42'>[effective_rating]-Beta</font>"
|
||||
rating_descriptor = "This indicates the presence of significant psi capabilities of the Grandmaster rank or higher."
|
||||
if(5)
|
||||
use_rating = "<font color = '#FF0000'>[effective_rating]-Alpha</font>"
|
||||
rating_descriptor = "This indicates the presence of major psi capabilities of the Paramount Grandmaster rank or higher."
|
||||
else
|
||||
use_rating = "[effective_rating]-Lambda"
|
||||
rating_descriptor = "This indicates the presence of trace latent psi capabilities."
|
||||
|
||||
dat += "[use_He_has] an overall psi rating of [use_rating].<br><i>[rating_descriptor]</i><hr>"
|
||||
|
||||
dat += "[use_He_is] currently <b>[psi.suppressed ? "suppressing" : "not suppressing"]</b> your psychic operancy.<br>"
|
||||
dat += "[use_He_has] <b>[psi.stamina]/[psi.max_stamina]</b> psi stamina remaining.<br>"
|
||||
dat += "<hr>"
|
||||
|
||||
for(var/faculty_id in psi.ranks)
|
||||
var/datum/psionic_faculty/faculty = SSpsi.get_faculty(faculty_id)
|
||||
if(psi.ranks[faculty.id] > 0)
|
||||
dat += "[use_He_is] assayed at the rank of <b>[psychic_ranks_to_strings[psi.ranks[faculty.id]]]</b> for the <b>[faculty.name] faculty</b>.<br>"
|
||||
else
|
||||
dat += "[use_He_has] no notable power within the <b>[faculty.name] faculty</b>.<br>"
|
||||
dat += "<hr>"
|
||||
|
||||
if(viewer == usr)
|
||||
dat += "<table width = 100% border = 1><tr><td colspan = 2><h2>Psi-power Usage</h2></td></tr>"
|
||||
for(var/faculty_id in psi.ranks)
|
||||
var/list/check_powers = psi.get_powers_by_faculty(faculty_id)
|
||||
if(LAZYLEN(check_powers))
|
||||
var/datum/psionic_faculty/faculty = SSpsi.get_faculty(faculty_id)
|
||||
dat += "<tr><td colspan = 2>[use_He_has] access to the following psi-powers within the <b>[faculty.name] faculty</b>:</td></tr>"
|
||||
for(var/datum/psionic_power/power in check_powers)
|
||||
dat += "<tr><td><b>[power.name]</b></td><td>[power.use_description]</td></tr>"
|
||||
dat += "</table>"
|
||||
|
||||
var/datum/browser/popup = new(viewer, "psi_assay_\ref[src]", "Psi-Assay")
|
||||
popup.set_content(jointext(dat,null))
|
||||
popup.open()
|
||||
38
code/modules/psionics/mob/mob_interactions.dm
Normal file
38
code/modules/psionics/mob/mob_interactions.dm
Normal file
@@ -0,0 +1,38 @@
|
||||
#define INVOKE_PSI_POWERS(holder, powers, target, return_on_invocation) \
|
||||
if(holder && holder.psi && holder.psi.can_use()) { \
|
||||
for(var/thing in powers) { \
|
||||
var/datum/psionic_power/power = thing; \
|
||||
var/obj/item/result = power.invoke(holder, target); \
|
||||
if(result) { \
|
||||
power.handle_post_power(holder, target); \
|
||||
if(istype(result)) { \
|
||||
sound_to(holder, sound('sound/effects/psi/power_evoke.ogg')); \
|
||||
LAZYADD(holder.psi.manifested_items, result); \
|
||||
holder.put_in_hands(result); \
|
||||
} \
|
||||
return return_on_invocation; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
/mob/living/UnarmedAttack(var/atom/A, var/proximity)
|
||||
. = ..()
|
||||
if(. && psi)
|
||||
INVOKE_PSI_POWERS(src, psi.get_melee_powers(SSpsi.faculties_by_intent[a_intent]), A, FALSE)
|
||||
|
||||
/mob/living/RangedAttack(var/atom/A, var/params)
|
||||
if(psi)
|
||||
INVOKE_PSI_POWERS(src, psi.get_ranged_powers(SSpsi.faculties_by_intent[a_intent]), A, TRUE)
|
||||
. = ..()
|
||||
|
||||
/mob/living/proc/check_psi_grab(var/obj/item/grab/grab)
|
||||
if(psi && ismob(grab.affecting))
|
||||
INVOKE_PSI_POWERS(src, psi.get_grab_powers(SSpsi.faculties_by_intent[a_intent]), grab.affecting, FALSE)
|
||||
. = ..()
|
||||
|
||||
/mob/living/attack_empty_hand(var/bp_hand)
|
||||
if(psi)
|
||||
INVOKE_PSI_POWERS(src, psi.get_manifestations(), src, FALSE)
|
||||
. = ..()
|
||||
|
||||
#undef INVOKE_PSI_POWERS
|
||||
@@ -81,19 +81,6 @@
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/item/contract/wizard/tk
|
||||
name = "telekinesis contract"
|
||||
desc = "This contract makes your mind buzz. It promises to give you the ability to move things with your mind. At a price."
|
||||
color = "#990033"
|
||||
|
||||
/obj/item/contract/wizard/tk/contract_effect(mob/user as mob)
|
||||
..()
|
||||
if(!(TK in user.mutations))
|
||||
user.mutations.Add(TK)
|
||||
to_chat(user, "<span class='notice'>You feel your mind expanding!</span>")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/item/contract/boon
|
||||
name = "boon contract"
|
||||
desc = "this contract grants you a boon for signing it."
|
||||
|
||||
@@ -6,7 +6,6 @@ var/list/artefact_feedback = list(/obj/structure/closet/wizard/armor = "HS",
|
||||
/obj/item/monster_manual = "MA",
|
||||
/obj/item/contract/apprentice = "CP",
|
||||
/obj/structure/closet/wizard/souls = "SS",
|
||||
/obj/item/contract/wizard/tk = "TK",
|
||||
/obj/structure/closet/wizard/scrying = "SO",
|
||||
/obj/item/teleportation_scroll = "TS",
|
||||
/obj/item/gun/energy/staff = "ST",
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
/spell/targeted/subjugation = 1,
|
||||
/spell/aoe_turf/smoke = 1,
|
||||
/spell/aoe_turf/conjure/summon/bats = 3,
|
||||
/obj/item/contract/wizard/tk = 5,
|
||||
/obj/structure/closet/wizard/scrying = 2,
|
||||
/obj/item/storage/belt/wands/full = 4,
|
||||
/obj/item/teleportation_scroll = 1,
|
||||
|
||||
@@ -236,6 +236,6 @@ Note: This proc can be overwritten to allow for different types of auto-alignmen
|
||||
#undef CELLS
|
||||
#undef CELLSIZE
|
||||
|
||||
/obj/structure/table/attack_tk() // no telehulk sorry
|
||||
/obj/structure/table/do_simple_ranged_interaction(var/mob/user)
|
||||
return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user