mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-29 06:58:30 +01:00
Completes the /datum/component/shadekin work (#17895)
* Makes shadekin a component * Update shadekin.dm * Update human.dm * a * i love typos * Update life.dm * guh * Upports the shadekin stuff Also fixes an 8 year bug where the ability master would delete itself * boh * flashlight flickering * flickery * Update flashlight.dm * i hate lightcode * no clue * reenable * more changes * evil * Update living.dm * simplekin fix * yeah * maw fixes
This commit is contained in:
@@ -860,8 +860,9 @@
|
||||
#define COMSIG_GARGOYLE_CHECK_ENERGY "gargoyle_check_energy"
|
||||
|
||||
// Species Components
|
||||
///from /datum/species/xenochimera/handle_environment_special()
|
||||
///from /datum/species/proc/handle_species_components()
|
||||
#define COMSIG_XENOCHIMERA_COMPONENT "xenochimera_component"
|
||||
#define COMSIG_SHADEKIN_COMPONENT "shadekin_component"
|
||||
|
||||
// Hose Connector Component
|
||||
#define COMSIG_HOSE_FORCEPUMP "hose_force_pump"
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
#define ONLY_WHILE_SHIFTED 2
|
||||
#define SHIFTED_OR_NOT 3
|
||||
|
||||
#define AB_PHASE_SHIFTED 0x1
|
||||
#define AB_PHASE_SHIFTING 0x2
|
||||
#define AB_SHADE_REGEN 0x4
|
||||
#define AB_SHADE_REGEN 0x1
|
||||
|
||||
//Porting over the type system of the mobs
|
||||
#define BLUE_EYES 1
|
||||
|
||||
@@ -11,8 +11,13 @@ GLOBAL_LIST_EMPTY(everyone_traits_neutral) // Neutral traits available to all sp
|
||||
GLOBAL_LIST_EMPTY(everyone_traits_negative) // Neutral traits available to all species, indexed by path
|
||||
GLOBAL_LIST_EMPTY(traits_costs) // Just path = cost list, saves time in char setup
|
||||
GLOBAL_LIST_EMPTY(all_traits) // All of 'em at once (same instances)
|
||||
GLOBAL_LIST_EMPTY(active_ghost_pods)
|
||||
|
||||
GLOBAL_LIST_EMPTY(active_ghost_pods) //NYI - Used downstream
|
||||
GLOBAL_LIST_EMPTY(latejoin_gatewaystation) //NYI - Used downstream
|
||||
GLOBAL_LIST_EMPTY(latejoin_plainspath) //NYI - Used downstream
|
||||
GLOBAL_LIST_EMPTY(latejoin_fueldepot) //NYI - Used downstream
|
||||
GLOBAL_LIST_EMPTY(latejoin_tyrvillage) //NYI - Used downstream
|
||||
GLOBAL_LIST_EMPTY(latejoin_thedark) //NYI - Used downstream
|
||||
//Global vars for making the overmap_renamer subsystem.
|
||||
//Collects all instances by reference of visitable overmap objects of /obj/effect/overmap/visitable like the debris field.
|
||||
GLOBAL_LIST_EMPTY(visitable_overmap_object_instances)
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
return
|
||||
if(!Adjacent(usr) || !over.Adjacent(usr))
|
||||
return // should stop you from dragging through windows
|
||||
if(is_incorporeal(usr))
|
||||
return
|
||||
|
||||
INVOKE_ASYNC(over, TYPE_PROC_REF(/atom, MouseDrop_T), src, usr, src_location, over_location, src_control, over_control, params)
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
showing = 0
|
||||
overlays.len = 0
|
||||
overlays.Add(closed_state)
|
||||
else if(forced_state != 1) // We're opening it, show the icons.
|
||||
else if(forced_state != 1) // We're opening it, show the icons. OR, if forced_state == 2, we're forcing it to open it.
|
||||
open_ability_master()
|
||||
update_abilities(1)
|
||||
showing = 1
|
||||
@@ -84,6 +84,7 @@
|
||||
A.screen_loc = "[encode_screen_X(xpos)]:[x_pix],[encode_screen_Y(ypos)]:[y_pix]"
|
||||
if(my_mob && my_mob.client)
|
||||
my_mob.client.screen += A
|
||||
my_mob.client.screen |= src
|
||||
// A.handle_icon_updates = 1
|
||||
|
||||
/obj/screen/movable/ability_master/proc/update_abilities(forced = 0, mob/user)
|
||||
@@ -177,12 +178,12 @@
|
||||
/mob/Login()
|
||||
..()
|
||||
if(ability_master)
|
||||
ability_master.toggle_open(1)
|
||||
client.screen -= ability_master
|
||||
ability_master.toggle_open(2) //Force it to open on login.
|
||||
//client.screen -= ability_master
|
||||
|
||||
/mob/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!ability_master) //VOREStation Edit: S H A D E K I N
|
||||
if(!ability_master)
|
||||
ability_master = new /obj/screen/movable/ability_master(src)
|
||||
|
||||
///////////ACTUAL ABILITIES////////////
|
||||
|
||||
@@ -547,6 +547,31 @@ GLOBAL_LIST_INIT(global_huds, list(
|
||||
/mob/new_player/add_click_catcher()
|
||||
return
|
||||
|
||||
/mob/living/create_mob_hud(datum/hud/HUD, apply_to_client = TRUE)
|
||||
..()
|
||||
|
||||
var/list/hud_elements = list()
|
||||
shadekin_display = new /obj/screen/shadekin()
|
||||
shadekin_display.screen_loc = ui_shadekin_display
|
||||
shadekin_display.icon_state = "shadekin"
|
||||
hud_elements |= shadekin_display
|
||||
|
||||
xenochimera_danger_display = new /obj/screen/xenochimera/danger_level()
|
||||
xenochimera_danger_display.screen_loc = ui_xenochimera_danger_display
|
||||
xenochimera_danger_display.icon_state = "danger00"
|
||||
hud_elements |= xenochimera_danger_display
|
||||
|
||||
lleill_display = new /obj/screen/lleill()
|
||||
lleill_display.screen_loc = ui_lleill_display
|
||||
lleill_display.icon_state = "lleill"
|
||||
hud_elements |= lleill_display
|
||||
|
||||
if(client)
|
||||
client.screen = list()
|
||||
|
||||
client.screen += hud_elements
|
||||
client.screen += client.void
|
||||
|
||||
/* TGMC Ammo HUD Port
|
||||
* These procs call to screen_objects.dm's respective procs.
|
||||
* All these do is manage the amount of huds on screen and set the HUD.
|
||||
|
||||
@@ -321,22 +321,10 @@
|
||||
using.alpha = HUD.ui_alpha
|
||||
adding |= using
|
||||
|
||||
//VOREStation Addition begin
|
||||
shadekin_display = new /obj/screen/shadekin()
|
||||
shadekin_display.screen_loc = ui_shadekin_display
|
||||
shadekin_display.icon_state = "shadekin"
|
||||
//Component hud elements. Made in /mob/living/create_mob_hud
|
||||
hud_elements |= shadekin_display
|
||||
|
||||
xenochimera_danger_display = new /obj/screen/xenochimera/danger_level()
|
||||
xenochimera_danger_display.screen_loc = ui_xenochimera_danger_display
|
||||
xenochimera_danger_display.icon_state = "danger00"
|
||||
hud_elements |= xenochimera_danger_display
|
||||
|
||||
lleill_display = new /obj/screen/lleill()
|
||||
lleill_display.screen_loc = ui_lleill_display
|
||||
lleill_display.icon_state = "lleill"
|
||||
hud_elements |= lleill_display
|
||||
//VOREStation Addition end
|
||||
|
||||
ling_chem_display = new /obj/screen/ling/chems()
|
||||
ling_chem_display.screen_loc = ui_ling_chemical_display
|
||||
|
||||
@@ -623,7 +623,7 @@
|
||||
var/mob/living/silicon/ai/AI = usr
|
||||
AI.view_images()
|
||||
else
|
||||
return attempt_vr(src,"Click_vr",list(location,control,params)) //VOREStation Add - Additional things.
|
||||
return attempt_vr(src,"Click_vr",list(location,control,params))
|
||||
return 1
|
||||
|
||||
/obj/screen/inventory/Click()
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
/obj/screen/proc/Click_vr(location, control, params)
|
||||
if(!usr) return 1
|
||||
switch(name)
|
||||
|
||||
//Shadekin
|
||||
if("darkness")
|
||||
var/turf/T = get_turf(usr)
|
||||
var/darkness = round(1 - T.get_lumcount(),0.1)
|
||||
to_chat(usr,span_notice(span_bold("Darkness:") + " [darkness]"))
|
||||
if("energy")
|
||||
var/mob/living/simple_mob/shadekin/SK = usr
|
||||
if(istype(SK))
|
||||
to_chat(usr,span_notice(span_bold("Energy:") + " [SK.comp.dark_energy] ([SK.dark_gains])"))
|
||||
if("shadekin status")
|
||||
var/turf/T = get_turf(usr)
|
||||
if(T)
|
||||
var/darkness = round(1 - T.get_lumcount(),0.1)
|
||||
to_chat(usr,span_notice(span_bold("Darkness:") + " [darkness]"))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
if(istype(H) && istype(H.species, /datum/species/shadekin))
|
||||
to_chat(usr,span_notice(span_bold("Energy:") + " [H.shadekin_get_energy(H)]"))
|
||||
var/mob/living/H = usr
|
||||
if(ismob(H))
|
||||
var/datum/component/shadekin/SK = H.get_shadekin_component()
|
||||
if(SK)
|
||||
to_chat(usr,span_notice(span_bold("Energy:") + " [SK.shadekin_get_energy()]"))
|
||||
if("glamour")
|
||||
var/mob/living/carbon/human/H = usr
|
||||
if(istype(H))
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
/datum/component/shadekin
|
||||
var/dark_energy = 100
|
||||
var/max_dark_energy = 100
|
||||
var/dark_energy_infinite = FALSE
|
||||
|
||||
/datum/component/shadekin/Initialize()
|
||||
if(!ishuman(parent) && !issimplekin(parent)) //Both humankin and simplekin can have this component
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
@@ -0,0 +1,161 @@
|
||||
///Returns the shadekin component of the given mob
|
||||
/mob/living/proc/get_shadekin_component()
|
||||
var/datum/component/shadekin/SK = GetComponent(/datum/component/shadekin)
|
||||
if(SK)
|
||||
return SK
|
||||
|
||||
///Handles the shadekin's HUD updates.
|
||||
/datum/component/shadekin/proc/update_shadekin_hud()
|
||||
var/turf/T = get_turf(owner)
|
||||
if(owner.shadekin_display)
|
||||
var/l_icon = 0
|
||||
var/e_icon = 0
|
||||
|
||||
owner.shadekin_display.invisibility = INVISIBILITY_NONE
|
||||
if(T)
|
||||
var/brightness = T.get_lumcount() //Brightness in 0.0 to 1.0
|
||||
var/darkness = 1-brightness //Invert
|
||||
switch(darkness)
|
||||
if(0.80 to 1.00)
|
||||
l_icon = 0
|
||||
if(0.60 to 0.80)
|
||||
l_icon = 1
|
||||
if(0.40 to 0.60)
|
||||
l_icon = 2
|
||||
if(0.20 to 0.40)
|
||||
l_icon = 3
|
||||
if(0.00 to 0.20)
|
||||
l_icon = 4
|
||||
|
||||
switch(shadekin_get_energy())
|
||||
if(0 to 24)
|
||||
e_icon = 0
|
||||
if(25 to 49)
|
||||
e_icon = 1
|
||||
if(50 to 74)
|
||||
e_icon = 2
|
||||
if(75 to 99)
|
||||
e_icon = 3
|
||||
if(100 to INFINITY)
|
||||
e_icon = 4
|
||||
|
||||
owner.shadekin_display.icon_state = "shadekin-[l_icon]-[e_icon]"
|
||||
return
|
||||
|
||||
///For simplekin or those that have a pre-defined eye color
|
||||
/datum/component/shadekin/proc/set_eye_energy()
|
||||
if(!eye_color_influences_energy)
|
||||
return
|
||||
switch(eye_color)
|
||||
//Blue has constant, steady (slow) regen and ignores darkness.
|
||||
if(BLUE_EYES)
|
||||
set_light_and_darkness(0.75,0.75)
|
||||
if(RED_EYES)
|
||||
set_light_and_darkness(-0.5,0.5)
|
||||
if(PURPLE_EYES)
|
||||
set_light_and_darkness(-0.5,1)
|
||||
if(YELLOW_EYES)
|
||||
set_light_and_darkness(-2,3)
|
||||
if(GREEN_EYES)
|
||||
set_light_and_darkness(0.125,2)
|
||||
if(ORANGE_EYES)
|
||||
set_light_and_darkness(-0.25,0.75)
|
||||
|
||||
///Sets our eye color.
|
||||
/datum/component/shadekin/proc/set_shadekin_eyecolor(var/mob/living/carbon/human/H)
|
||||
if(!ishuman(owner))
|
||||
return eye_color //revert to default if we're not a human
|
||||
else
|
||||
H = owner
|
||||
var/eyecolor_rgb = rgb(H.r_eyes, H.g_eyes, H.b_eyes)
|
||||
|
||||
var/eyecolor_hue = rgb2num(eyecolor_rgb, COLORSPACE_HSV)[1]
|
||||
var/eyecolor_sat = rgb2num(eyecolor_rgb, COLORSPACE_HSV)[2]
|
||||
var/eyecolor_val = rgb2num(eyecolor_rgb, COLORSPACE_HSV)[3]
|
||||
|
||||
//First, clamp the saturation/value to prevent black/grey/white eyes
|
||||
if(eyecolor_sat < 10)
|
||||
eyecolor_sat = 10
|
||||
if(eyecolor_val < 40)
|
||||
eyecolor_val = 40
|
||||
|
||||
eyecolor_rgb = rgb(eyecolor_hue, eyecolor_sat, eyecolor_val, space=COLORSPACE_HSV)
|
||||
|
||||
H.r_eyes = rgb2num(eyecolor_rgb)[1]
|
||||
H.g_eyes = rgb2num(eyecolor_rgb)[2]
|
||||
H.b_eyes = rgb2num(eyecolor_rgb)[3]
|
||||
|
||||
//Now determine what color we fall into.
|
||||
switch(eyecolor_hue)
|
||||
if(0 to 20)
|
||||
eye_color = RED_EYES
|
||||
if(21 to 50)
|
||||
eye_color = ORANGE_EYES
|
||||
if(51 to 70)
|
||||
eye_color = YELLOW_EYES
|
||||
if(71 to 160)
|
||||
eye_color = GREEN_EYES
|
||||
if(161 to 260)
|
||||
eye_color = BLUE_EYES
|
||||
if(261 to 340)
|
||||
eye_color = PURPLE_EYES
|
||||
if(341 to 360)
|
||||
eye_color = RED_EYES
|
||||
return eye_color
|
||||
|
||||
///Adds the shadekin abilities to the owner.
|
||||
/datum/component/shadekin/proc/add_shadekin_abilities()
|
||||
if(!owner.ability_master || !istype(owner.ability_master, /obj/screen/movable/ability_master/shadekin))
|
||||
owner.ability_master = null
|
||||
owner.ability_master = new /obj/screen/movable/ability_master/shadekin(owner)
|
||||
for(var/datum/power/shadekin/P in shadekin_ability_datums)
|
||||
if(!(P.verbpath in owner.verbs))
|
||||
add_verb(owner, P.verbpath)
|
||||
owner.ability_master.add_shadekin_ability(
|
||||
object_given = owner,
|
||||
verb_given = P.verbpath,
|
||||
name_given = P.name,
|
||||
ability_icon_given = P.ability_icon_state,
|
||||
arguments = list()
|
||||
)
|
||||
|
||||
//wait, it's all light?
|
||||
///Allows setting the light and darkness gain.
|
||||
///@Args: light_gain, dark_gain
|
||||
/datum/component/shadekin/proc/set_light_and_darkness(light_gain, dark_gain)
|
||||
if(light_gain)
|
||||
energy_light = light_gain
|
||||
if(dark_gain)
|
||||
energy_dark = dark_gain
|
||||
|
||||
|
||||
//ENERGY HELPERS
|
||||
|
||||
/// Returns the shadekin's current energy.
|
||||
/// Returns max_energy if dark_energy_infinite is set to TRUE.
|
||||
/datum/component/shadekin/proc/shadekin_get_energy()
|
||||
if(dark_energy_infinite)
|
||||
return max_dark_energy
|
||||
return dark_energy
|
||||
|
||||
/// Returns the shadekin's maximum energy.
|
||||
/datum/component/shadekin/proc/shadekin_get_max_energy()
|
||||
return max_dark_energy
|
||||
|
||||
///Sets the shadekin's energy TO the given value.
|
||||
/datum/component/shadekin/proc/shadekin_set_energy(var/new_energy)
|
||||
if(!isnum(new_energy))
|
||||
return
|
||||
dark_energy = CLAMP(new_energy, 0, max_dark_energy)
|
||||
|
||||
///Sets the shadekin's maximum energy.
|
||||
/datum/component/shadekin/proc/shadekin_set_max_energy(var/new_max_energy)
|
||||
if(!isnum(new_max_energy))
|
||||
return //No.
|
||||
max_dark_energy = new_max_energy
|
||||
|
||||
///Adjusts the shadekin's energy by the given amount.
|
||||
/datum/component/shadekin/proc/shadekin_adjust_energy(var/amount)
|
||||
if(!isnum(amount))
|
||||
return //No
|
||||
shadekin_set_energy(dark_energy + amount)
|
||||
@@ -0,0 +1,66 @@
|
||||
//////////////////////
|
||||
/// CREATE SHADE ///
|
||||
//////////////////////
|
||||
/datum/power/shadekin/create_shade
|
||||
name = "Create Shade (25)"
|
||||
desc = "Create a field of darkness that follows you."
|
||||
verbpath = /mob/living/proc/create_shade
|
||||
ability_icon_state = "create_shade"
|
||||
|
||||
/mob/living/proc/create_shade()
|
||||
set name = "Create Shade (25)"
|
||||
set desc = "Create a field of darkness that follows you."
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
var/ability_cost = 25
|
||||
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(!istype(SK))
|
||||
to_chat(src, span_warning("Only a shadekin can use that!"))
|
||||
return FALSE
|
||||
else if(stat)
|
||||
to_chat(src, span_warning("Can't use that ability in your state!"))
|
||||
return FALSE
|
||||
else if(SK.shadekin_get_energy() < ability_cost)
|
||||
to_chat(src, span_warning("Not enough energy for that ability!"))
|
||||
return FALSE
|
||||
else if(SK.in_phase)
|
||||
to_chat(src, span_warning("You can't use that while phase shifted!"))
|
||||
return FALSE
|
||||
|
||||
playsound(src, 'sound/effects/bamf.ogg', 75, 1)
|
||||
|
||||
add_modifier(/datum/modifier/shadekin/create_shade,20 SECONDS)
|
||||
SK.shadekin_adjust_energy(-ability_cost)
|
||||
return TRUE
|
||||
|
||||
/datum/modifier/shadekin/create_shade
|
||||
name = "Shadekin Shadegen"
|
||||
desc = "Darkness envelops you."
|
||||
mob_overlay_state = ""
|
||||
|
||||
on_created_text = span_notice("You drag part of The Dark into realspace, enveloping yourself.")
|
||||
on_expired_text = span_warning("You lose your grasp on The Dark and realspace reasserts itself.")
|
||||
stacks = MODIFIER_STACK_EXTEND
|
||||
var/mob/living/simple_mob/shadekin/my_kin
|
||||
|
||||
/datum/modifier/shadekin/create_shade/tick()
|
||||
var/datum/component/shadekin/SK = my_kin.get_shadekin_component()
|
||||
if(SK && SK.in_phase)
|
||||
expire()
|
||||
|
||||
/datum/modifier/shadekin/create_shade/on_applied()
|
||||
my_kin = holder
|
||||
holder.glow_toggle = TRUE
|
||||
holder.glow_range = 8
|
||||
holder.glow_intensity = -10
|
||||
holder.glow_color = "#FFFFFF"
|
||||
holder.set_light(8, -10, "#FFFFFF")
|
||||
|
||||
/datum/modifier/shadekin/create_shade/on_expire()
|
||||
holder.glow_toggle = initial(holder.glow_toggle)
|
||||
holder.glow_range = initial(holder.glow_range)
|
||||
holder.glow_intensity = initial(holder.glow_intensity)
|
||||
holder.glow_color = initial(holder.glow_color)
|
||||
holder.set_light(0)
|
||||
my_kin = null
|
||||
@@ -0,0 +1,205 @@
|
||||
/datum/power/shadekin/dark_maw
|
||||
name = "Dark Maw (20)"
|
||||
desc = "Create a trap to capture others, or steal people from phase"
|
||||
verbpath = /mob/living/proc/dark_maw
|
||||
ability_icon_state = "dark_maw_ic"
|
||||
|
||||
/mob/living/proc/dark_maw()
|
||||
set name = "Dark Maw (20)"
|
||||
set desc = "Create a trap to capture others, or steal people from phase"
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
var/ability_cost = 20
|
||||
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(!SK)
|
||||
return FALSE
|
||||
if(stat)
|
||||
to_chat(src, span_warning("Can't use that ability in your state!"))
|
||||
return FALSE
|
||||
|
||||
if(SK.shadekin_get_energy() < ability_cost)
|
||||
to_chat(src, span_warning("Not enough energy for that ability!"))
|
||||
return FALSE
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(!istype(T))
|
||||
to_chat(src, span_warning("You don't seem to be able to set a trap here!"))
|
||||
return FALSE
|
||||
|
||||
if(T.get_lumcount() >= 0.5)
|
||||
to_chat(src, span_warning("There is too much light here for your trap to last!"))
|
||||
return FALSE
|
||||
|
||||
if(do_after(src, 10))
|
||||
if(SK.in_phase)
|
||||
new /obj/effect/abstract/dark_maw(loc, src, TRUE)
|
||||
else
|
||||
new /obj/effect/abstract/dark_maw(loc, src)
|
||||
SK.shadekin_adjust_energy(-ability_cost)
|
||||
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/proc/clear_dark_maws()
|
||||
set name = "Dispel dark maws"
|
||||
set desc = "Dispel any active dark maws in place"
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(!SK)
|
||||
to_chat(src, span_warning("Only a shadekin can use that!"))
|
||||
return FALSE
|
||||
|
||||
for(var/obj/effect/abstract/dark_maw/dm in SK.active_dark_maws)
|
||||
dm.dispel()
|
||||
|
||||
/obj/effect/abstract/dark_maw
|
||||
var/mob/living/owner = null
|
||||
var/obj/belly/target = null
|
||||
var/has_signal = FALSE
|
||||
icon = 'icons/obj/Shadekin_powers.dmi'
|
||||
icon_state = "dark_maw_waiting"
|
||||
|
||||
/obj/effect/abstract/dark_maw/Initialize(mapload, var/mob/user, var/trigger_now = FALSE)
|
||||
. = ..()
|
||||
if(!isturf(loc))
|
||||
return INITIALIZE_HINT_QDEL
|
||||
var/datum/component/shadekin/SK
|
||||
if(user && isliving(user))
|
||||
owner = user
|
||||
if(owner.vore_selected)
|
||||
target = owner.vore_selected
|
||||
RegisterSignal(owner, COMSIG_PARENT_QDELETING, PROC_REF(drop_everything_and_delete))
|
||||
has_signal = TRUE
|
||||
SK = owner.get_shadekin_component()
|
||||
|
||||
var/turf/T = loc
|
||||
if(T.get_lumcount() >= 0.5)
|
||||
visible_message(span_notice("A set of shadowy lines flickers away in the light."))
|
||||
icon_state = "dark_maw_used"
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
|
||||
var/mob/living/target_user = null
|
||||
for(var/mob/living/L in T)
|
||||
if(L != owner && !L.is_incorporeal())
|
||||
target_user = L
|
||||
break
|
||||
|
||||
if(istype(target_user))
|
||||
triggered_by(target_user, 1)
|
||||
// to trigger rebuild
|
||||
else if(trigger_now)
|
||||
icon_state = "dark_maw_used"
|
||||
flick("dark_maw_tr", src)
|
||||
visible_message(span_warning("A set of crystals suddenly springs from the ground and shadowy tendrils wrap around nothing before vanishing."))
|
||||
QDEL_IN(src, 3 SECONDS)
|
||||
else
|
||||
if(SK)
|
||||
SK.active_dark_maws += src
|
||||
flick("dark_maw", src)
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
///Called when we get a signal that our owner is being qdel'd
|
||||
/obj/effect/abstract/dark_maw/proc/drop_everything_and_delete()
|
||||
SIGNAL_HANDLER
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/abstract/dark_maw/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
if(owner)
|
||||
if(has_signal)
|
||||
UnregisterSignal(owner, COMSIG_PARENT_QDELETING)
|
||||
var/datum/component/shadekin/SK = owner.get_shadekin_component()
|
||||
if(SK)
|
||||
SK.active_dark_maws -= src
|
||||
owner = null
|
||||
target = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/abstract/dark_maw/Crossed(O)
|
||||
. = ..()
|
||||
if(!isliving(O))
|
||||
return
|
||||
if(icon_state != "dark_maw_waiting")
|
||||
return
|
||||
var/mob/living/L = O
|
||||
if(!L.is_incorporeal() && (!owner || L != owner))
|
||||
triggered_by(L)
|
||||
|
||||
/obj/effect/abstract/dark_maw/process()
|
||||
var/turf/T = get_turf(src)
|
||||
if(!istype(T) || T.get_lumcount() >= 0.5)
|
||||
dispel()
|
||||
|
||||
/obj/effect/abstract/dark_maw/proc/dispel()
|
||||
if(icon_state == "dark_maw_waiting")
|
||||
visible_message(span_notice("A set of shadowy lines flickers away in the light."))
|
||||
else
|
||||
visible_message(span_notice("The crystals and shadowy tendrils dissipate with the light shone on it."))
|
||||
icon_state = "dark_maw_used"
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/abstract/dark_maw/proc/triggered_by(var/mob/living/L, var/triggered_instantly = 0)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
icon_state = "dark_maw_used"
|
||||
flick("dark_maw_tr", src)
|
||||
L.AdjustStunned(4)
|
||||
visible_message(span_warning("A set of crystals spring out of the ground and shadowy tendrils start wrapping around [L]."))
|
||||
if(owner && !triggered_instantly)
|
||||
to_chat(owner, span_warning("A dark maw you deployed has triggered!"))
|
||||
addtimer(CALLBACK(src, PROC_REF(do_trigger), L), 1 SECOND, TIMER_DELETE_ME)
|
||||
|
||||
/obj/effect/abstract/dark_maw/proc/do_trigger(var/mob/living/L)
|
||||
var/will_vore = 1
|
||||
|
||||
if(!owner || !(target in owner) || !L.devourable || !L.can_be_drop_prey || !owner.can_be_drop_pred || !L.phase_vore)
|
||||
will_vore = 0
|
||||
|
||||
if(!src || src.gc_destroyed)
|
||||
//We got deleted probably, do nothing more
|
||||
return
|
||||
|
||||
if(L.loc != get_turf(src))
|
||||
visible_message(span_notice("The shadowy tendrils fail to catch anything and dissipate."))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
if(will_vore)
|
||||
visible_message(span_warning("The shadowy tendrils grab around [L] and drag them into the floor, leaving nothing behind."))
|
||||
L.forceMove(target)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
var/obj/effect/energy_net/dark/net = new /obj/effect/energy_net/dark(get_turf(src))
|
||||
if(net.buckle_mob(L))
|
||||
visible_message(span_warning("The shadowy tendrils wrap around [L] and traps them in a net of dark energy."))
|
||||
else
|
||||
visible_message(span_notice("The shadowy tendrils wrap around [L] and then dissipate, leaving them in place."))
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/energy_net/dark
|
||||
name = "dark net"
|
||||
desc = "It's a net made of dark energy."
|
||||
icon = 'icons/obj/Shadekin_powers.dmi'
|
||||
icon_state = "dark_net"
|
||||
|
||||
escape_time = 30 SECONDS
|
||||
|
||||
/obj/effect/energy_net/dark/user_unbuckle_mob(mob/living/buckled_mob, mob/user)
|
||||
if(isliving(user))
|
||||
var/mob/living/unbuckler = user
|
||||
var/datum/component/shadekin/SK = unbuckler.get_shadekin_component()
|
||||
if(SK)
|
||||
visible_message(span_danger("[user] dissipates \the [src] with a touch!"))
|
||||
unbuckle_mob(buckled_mob)
|
||||
return
|
||||
. = ..()
|
||||
|
||||
/obj/effect/energy_net/dark/process()
|
||||
. = ..()
|
||||
var/turf/T = get_turf(src)
|
||||
if(!istype(T) || T.get_lumcount() >= 0.6)
|
||||
visible_message(span_notice("The tangle of dark tendrils fades away in the light."))
|
||||
qdel(src)
|
||||
@@ -0,0 +1,105 @@
|
||||
//Non-Canon on Virgo. Used downstream.
|
||||
|
||||
/datum/power/shadekin/dark_respite
|
||||
name = "Dark Respite (Only in Dark)"
|
||||
desc = "Focus yourself on healing any injuries sustained."
|
||||
verbpath = /mob/living/proc/dark_respite
|
||||
ability_icon_state = "dark_respite"
|
||||
|
||||
/mob/living/proc/dark_respite()
|
||||
set name = "Dark Respite (Only in Dark)"
|
||||
set desc = "Focus yourself on healing any injuries sustained."
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(!SK)
|
||||
return FALSE
|
||||
if(stat)
|
||||
to_chat(src, span_warning("Can't use that ability in your state!"))
|
||||
return FALSE
|
||||
|
||||
if(!istype(get_area(src), /area/shadekin))
|
||||
to_chat(src, span_warning("Can only trigger Dark Respite in the Dark!"))
|
||||
return FALSE
|
||||
|
||||
if(SK.in_dark_respite)
|
||||
to_chat(src, span_warning("You can't use that so soon after an emergency warp!"))
|
||||
return FALSE
|
||||
|
||||
if(has_modifier_of_type(/datum/modifier/dark_respite) && !SK.manual_respite)
|
||||
to_chat(src, span_warning("You cannot manually end a Dark Respite triggered by an emergency warp!"))
|
||||
|
||||
if(SK.in_phase)
|
||||
to_chat(src, span_warning("You can't use that while phase shifted!"))
|
||||
return FALSE
|
||||
|
||||
if(has_modifier_of_type(/datum/modifier/dark_respite))
|
||||
to_chat(src, span_notice("You stop focusing the Dark on healing yourself."))
|
||||
SK.manual_respite = FALSE
|
||||
remove_a_modifier_of_type(/datum/modifier/dark_respite)
|
||||
return TRUE
|
||||
to_chat(src, span_notice("You start focusing the Dark on healing yourself. (Leave the dark or trigger the ability again to end this.)"))
|
||||
SK.manual_respite = TRUE
|
||||
add_modifier(/datum/modifier/dark_respite)
|
||||
return TRUE
|
||||
|
||||
/datum/modifier/dark_respite
|
||||
name = "Dark Respite"
|
||||
pain_immunity = 1
|
||||
var/datum/component/shadekin/SK
|
||||
|
||||
// Override this for special effects when it gets added to the mob.
|
||||
/datum/modifier/dark_respite/on_applied()
|
||||
SK = holder.get_shadekin_component()
|
||||
if(!SK)
|
||||
expire()
|
||||
return
|
||||
|
||||
/datum/modifier/dark_respite/tick()
|
||||
if(!SK)
|
||||
expire()
|
||||
return
|
||||
var/mob/living/carbon/human/H
|
||||
if(istype(holder, /mob/living/carbon/human))
|
||||
H = holder
|
||||
if(H.nutrition)
|
||||
H.add_chemical_effect(CE_BLOODRESTORE, 5)
|
||||
|
||||
if(istype(get_area(H), /area/shadekin))
|
||||
pain_immunity = TRUE
|
||||
//Very good healing, but only in the Dark.
|
||||
holder.adjustFireLoss((-0.25))
|
||||
holder.adjustBruteLoss((-0.25))
|
||||
holder.adjustToxLoss((-0.25))
|
||||
holder.heal_organ_damage(3, 0)
|
||||
if(H)
|
||||
H.add_chemical_effect(CE_ANTIBIOTIC, ANTIBIO_SUPER)
|
||||
for(var/obj/item/organ/I in H.internal_organs)
|
||||
if(I.robotic >= ORGAN_ROBOT)
|
||||
continue
|
||||
if(I.damage > 0)
|
||||
I.damage = max(I.damage - 0.25, 0)
|
||||
if(I.damage <= 5 && I.organ_tag == O_EYES)
|
||||
H.sdisabilities &= ~BLIND
|
||||
for(var/obj/item/organ/external/O in H.organs)
|
||||
if(O.status & ORGAN_BROKEN)
|
||||
O.mend_fracture() //Only works if the bone won't rebreak, as usual
|
||||
for(var/datum/wound/W in O.wounds)
|
||||
if(W.bleeding())
|
||||
W.damage = max(W.damage - 3, 0)
|
||||
if(W.damage <= 0)
|
||||
O.wounds -= W
|
||||
if(W.internal)
|
||||
W.damage = max(W.damage - 3, 0)
|
||||
if(W.damage <= 0)
|
||||
O.wounds -= W
|
||||
else
|
||||
if(SK.manual_respite)
|
||||
to_chat(holder, span_notice("As you leave the Dark, you stop focusing the Dark on healing yourself."))
|
||||
SK.manual_respite = FALSE
|
||||
expire()
|
||||
if(pain_immunity)
|
||||
pain_immunity = 0
|
||||
|
||||
/datum/modifier/dark_respite/on_expire()
|
||||
SK = null
|
||||
@@ -0,0 +1,205 @@
|
||||
GLOBAL_LIST_BOILERPLATE(all_darkportal_hubs, /obj/structure/dark_portal/hub)
|
||||
GLOBAL_LIST_BOILERPLATE(all_darkportal_minions, /obj/structure/dark_portal/minion)
|
||||
/obj/structure/dark_portal
|
||||
name = "Dark portal"
|
||||
icon = 'icons/obj/shadekin_portal.dmi'
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
var/locked = null
|
||||
var/locked_name = ""
|
||||
var/one_time_use = FALSE
|
||||
var/precision = 1
|
||||
|
||||
/obj/structure/dark_portal/proc/close_portal()
|
||||
return
|
||||
|
||||
/obj/structure/dark_portal/proc/teleport(atom/movable/M as mob|obj)
|
||||
if(!locked)
|
||||
return
|
||||
if(isliving(M))
|
||||
var/mob/living/to_check = M
|
||||
var/datum/component/shadekin/SK = to_check.GetComponent(/datum/component/shadekin)
|
||||
if(SK && SK.in_dark_respite)
|
||||
to_chat(M, span_warning("You can't go through this portal so soon after an emergency warp!"))
|
||||
to_check.Stun(10)
|
||||
return
|
||||
|
||||
do_teleport(M, locked, precision, local = FALSE, bohsafe = TRUE)
|
||||
|
||||
if(one_time_use)
|
||||
one_time_use = 0
|
||||
close_portal()
|
||||
return
|
||||
|
||||
// These things are always on. By default they always teleport you to themselves.
|
||||
/obj/structure/dark_portal/hub
|
||||
icon_state = "hub_portal"
|
||||
desc = "A large portal. Touching it without going through may alter the destination."
|
||||
var/list/destination_station_areas // Override this in map files!
|
||||
var/list/destination_wilderness_areas // Override this in map files!
|
||||
|
||||
/obj/structure/dark_portal/hub/Initialize(mapload)
|
||||
. = ..()
|
||||
locked = src
|
||||
locked_name = src.name
|
||||
|
||||
/obj/structure/dark_portal/hub/Initialize(mapload)
|
||||
. = ..()
|
||||
set_light(2, 3, "#ffffff")
|
||||
|
||||
/obj/structure/dark_portal/hub/close_portal()
|
||||
locked = src
|
||||
locked_name = src.name
|
||||
precision = 1
|
||||
|
||||
/obj/structure/dark_portal/hub/attack_hand(mob/living/user)
|
||||
if(!isliving(user))
|
||||
return
|
||||
var/datum/component/shadekin/SK = user.GetComponent(/datum/component/shadekin)
|
||||
if(SK)
|
||||
if(SK.in_dark_respite)
|
||||
to_chat(user, span_warning("You can't use this so soon after an emergency warp!"))
|
||||
return
|
||||
if(SK.in_phase)
|
||||
to_chat(user, span_warning("You can't use this while phase shifted!"))
|
||||
return
|
||||
if(locked != src)
|
||||
var/confirm = tgui_alert(user, "This portal is currently open to [locked_name]. Change the portal destination?", "Change Portal Destination", list("Yes", "Cancel"))
|
||||
if(!confirm || confirm == "Cancel")
|
||||
return
|
||||
var/list/L = list()
|
||||
for(var/obj/structure/dark_portal/hub/H in GLOB.all_darkportal_hubs)
|
||||
if(H == src)
|
||||
L["This Portal"] = H
|
||||
else
|
||||
L[H.name] = H
|
||||
for(var/obj/structure/dark_portal/minion/M in GLOB.all_darkportal_minions)
|
||||
var/tmpname = "Dark Portal ([get_area(M)])"
|
||||
L[tmpname] = M
|
||||
var/desc = tgui_input_list(user, "Please select a hub portal to connect to.", "Portal Menu", L)
|
||||
if(!desc)
|
||||
return
|
||||
locked = L[desc]
|
||||
locked_name = desc
|
||||
return
|
||||
else if(locked_name == "somewhere on the station" || locked_name == "somewhere in the wilderness")
|
||||
to_chat(user, span_warning("The portal distorts for a moment, before returning to how it was, seemingly already determined where to send you."))
|
||||
return
|
||||
else if(istype(user, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(H.job && H.job != JOB_OUTSIDER && LAZYLEN(destination_station_areas))
|
||||
var/list/floors = list()
|
||||
var/area/picked_area = pick(destination_station_areas)
|
||||
for(var/turf/simulated/floor/floor in get_area_turfs(picked_area))
|
||||
floors.Add(floor)
|
||||
if(!LAZYLEN(floors))
|
||||
log_and_message_admins("[src]: There were no floors to teleport to in [picked_area]!")
|
||||
to_chat(user, span_warning("The portal distorts for a moment, seemingly unable to determine where to send you."))
|
||||
close_portal()
|
||||
destination_station_areas.Remove(picked_area)
|
||||
return
|
||||
locked = pick(floors)
|
||||
locked_name = "somewhere on the station"
|
||||
one_time_use = TRUE
|
||||
precision = 0
|
||||
to_chat(user, span_notice("The portal distorts for a moment, resolving itself soon after. You feel like it will lead you to the station now."))
|
||||
return
|
||||
if(!LAZYLEN(destination_wilderness_areas))
|
||||
to_chat(user, span_warning("The portal distorts for a moment, seemingly unable to determine where to send you."))
|
||||
close_portal()
|
||||
return
|
||||
var/list/floors = list()
|
||||
var/area/picked_area = pick(destination_wilderness_areas)
|
||||
for(var/turf/simulated/floor/floor in get_area_turfs(picked_area))
|
||||
floors.Add(floor)
|
||||
if(!LAZYLEN(floors))
|
||||
log_and_message_admins("[src]: There were no floors to teleport to in [picked_area]!")
|
||||
to_chat(user, span_warning("The portal distorts for a moment, seemingly unable to determine where to send you."))
|
||||
close_portal()
|
||||
destination_wilderness_areas.Remove(picked_area)
|
||||
return
|
||||
locked = pick(floors)
|
||||
locked_name = "somewhere in the wilderness"
|
||||
one_time_use = TRUE
|
||||
precision = 0
|
||||
to_chat(user, span_notice("The portal distorts for a moment, resolving itself soon after. You feel like it will lead you to somewhere in the wilderness now."))
|
||||
return
|
||||
|
||||
/obj/structure/dark_portal/hub/Bumped(M as mob|obj)
|
||||
spawn()
|
||||
teleport(M)
|
||||
return
|
||||
|
||||
// These things have an off state. A shadekin has to boop them to turn it on
|
||||
/obj/structure/dark_portal/minion
|
||||
icon_state = "minion0"
|
||||
|
||||
/obj/structure/dark_portal/minion/close_portal()
|
||||
locked = null
|
||||
locked_name = ""
|
||||
precision = 1
|
||||
icon_state = "minion0"
|
||||
|
||||
/obj/structure/dark_portal/minion/attack_hand(mob/living/user)
|
||||
if(!isliving(user))
|
||||
return
|
||||
var/datum/component/shadekin/SK = user.GetComponent(/datum/component/shadekin)
|
||||
if(SK)
|
||||
if(SK.in_dark_respite)
|
||||
to_chat(user, span_warning("You can't use this so soon after an emergency warp!"))
|
||||
return FALSE
|
||||
if(SK.in_phase)
|
||||
to_chat(user, span_warning("You can't use this while phase shifted!"))
|
||||
return FALSE
|
||||
if(icon_state == "minion1")
|
||||
var/confirm = tgui_alert(user, "This portal is currently open to [locked_name]. Close this portal to the dark?", "Close Portal", list("Yes", "Cancel"))
|
||||
if(!confirm || confirm == "Cancel")
|
||||
return
|
||||
if(confirm == "Yes")
|
||||
close_portal()
|
||||
return
|
||||
if(SK.shadekin_get_energy() < 10)
|
||||
to_chat(user, span_warning("Not enough energy to open up the portal! (10 required)"))
|
||||
return
|
||||
if(!LAZYLEN(GLOB.all_darkportal_hubs))
|
||||
to_chat(user, span_warning("No hub portals exist!"))
|
||||
return
|
||||
if(LAZYLEN(GLOB.all_darkportal_hubs) == 1)
|
||||
SK.shadekin_adjust_energy(user, -10)
|
||||
var/obj/structure/dark_portal/target = GLOB.all_darkportal_hubs[1]
|
||||
locked = target
|
||||
locked_name = target.name
|
||||
icon_state = "minion1"
|
||||
addtimer(CALLBACK(src, PROC_REF(check_to_close),target), 5 MINUTES, TIMER_DELETE_ME)
|
||||
return
|
||||
var/list/L = list()
|
||||
for(var/obj/structure/dark_portal/hub/H in GLOB.all_darkportal_hubs)
|
||||
L[H.name] = H
|
||||
var/desc = tgui_input_list(user, "Please select a hub portal to connect to.", "Portal Menu", L)
|
||||
if(!desc)
|
||||
return
|
||||
locked = L[desc]
|
||||
locked_name = desc
|
||||
icon_state = "minion1"
|
||||
addtimer(CALLBACK(src, PROC_REF(check_to_close_desc),locked), 5 MINUTES, TIMER_DELETE_ME)
|
||||
return
|
||||
else if(!istype(user, /mob/living))
|
||||
return
|
||||
else if(icon_state == "minion0")
|
||||
to_chat(user, span_notice("You touch the portal... nothing happens."))
|
||||
else
|
||||
to_chat(user, span_notice("You touch the portal, your hand able to pass through without harm."))
|
||||
|
||||
/obj/structure/dark_portal/proc/check_to_close(var/obj/structure/dark_portal/target)
|
||||
if(locked == target)
|
||||
close_portal()
|
||||
|
||||
/obj/structure/dark_portal/proc/check_to_close_desc(var/old_locked)
|
||||
if(locked == old_locked)
|
||||
close_portal()
|
||||
|
||||
/obj/structure/dark_portal/minion/Bumped(M as mob|obj)
|
||||
spawn()
|
||||
if(icon_state == "minion1")
|
||||
teleport(M)
|
||||
return
|
||||
@@ -0,0 +1,88 @@
|
||||
//Non-Canon on Virgo. Used downstream.
|
||||
|
||||
/datum/power/shadekin/dark_tunneling
|
||||
name = "Dark Tunneling (100) (Once)"
|
||||
desc = "Make a passage to the dark."
|
||||
verbpath = /mob/living/proc/dark_tunneling
|
||||
ability_icon_state = "minion0"
|
||||
|
||||
/mob/living/proc/dark_tunneling()
|
||||
set name = "Dark Tunneling (100) (Once)"
|
||||
set desc = "Make a passage to the dark."
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
var/template_id = "dark_portal"
|
||||
var/datum/map_template/shelter/template
|
||||
|
||||
var/ability_cost = 100
|
||||
var/tunnel_time = 60 SECONDS
|
||||
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(!SK)
|
||||
return FALSE
|
||||
if(stat)
|
||||
to_chat(src, span_warning("Can't use that ability in your state!"))
|
||||
return FALSE
|
||||
|
||||
if(SK.in_phase)
|
||||
to_chat(src, span_warning("You can't use that while phase shifted!"))
|
||||
return FALSE
|
||||
|
||||
if(SK.created_dark_tunnel)
|
||||
to_chat(src, span_warning("You have already made a tunnel to the Dark!"))
|
||||
return FALSE
|
||||
|
||||
if(!template)
|
||||
template = SSmapping.shelter_templates[template_id]
|
||||
if(!template)
|
||||
throw EXCEPTION("Shelter template ([template_id]) not found!")
|
||||
return FALSE
|
||||
|
||||
var/turf/deploy_location = get_turf(src)
|
||||
var/status = template.check_deploy(deploy_location)
|
||||
|
||||
switch(status)
|
||||
//Not allowed due to /area technical reasons
|
||||
if(SHELTER_DEPLOY_BAD_AREA)
|
||||
to_chat(src, span_warning("A tunnel to the Dark will not function in this area."))
|
||||
|
||||
//Anchored objects or no space
|
||||
if(SHELTER_DEPLOY_BAD_TURFS, SHELTER_DEPLOY_ANCHORED_OBJECTS)
|
||||
var/width = template.width
|
||||
var/height = template.height
|
||||
to_chat(src, span_warning("There is not enough open area for a tunnel to the Dark to form! You need to clear a [width]x[height] area!"))
|
||||
|
||||
if(status != SHELTER_DEPLOY_ALLOWED)
|
||||
return FALSE
|
||||
|
||||
var/turf/T = deploy_location
|
||||
var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread()
|
||||
smoke.attach(T)
|
||||
smoke.set_up(10, 0, T)
|
||||
smoke.start()
|
||||
|
||||
src.visible_message(span_notice("[src] begins pulling dark energies around themselves."))
|
||||
if(do_after(src, tunnel_time))
|
||||
playsound(src, 'sound/effects/phasein.ogg', 100, 1)
|
||||
src.visible_message(span_notice("[src] finishes pulling dark energies around themselves, creating a portal."))
|
||||
|
||||
log_and_message_admins("[key_name_admin(src)] created a tunnel to the dark at [get_area(T)]!")
|
||||
template.annihilate_plants(deploy_location)
|
||||
template.load(deploy_location, centered = TRUE)
|
||||
template.update_lighting(deploy_location)
|
||||
SK.created_dark_tunnel = TRUE
|
||||
SK.shadekin_adjust_energy(-(ability_cost - 10)) //Leaving enough energy to actually activate the portal
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
/datum/map_template/shelter/dark_portal
|
||||
name = "Dark Portal"
|
||||
shelter_id = "dark_portal"
|
||||
description = "A portal to a section of the Dark"
|
||||
mappath = "maps/submaps/shelters/dark_portal.dmm"
|
||||
|
||||
/datum/map_template/shelter/dark_portal/New()
|
||||
. = ..()
|
||||
blacklisted_turfs = typecacheof(list(/turf/unsimulated))
|
||||
GLOB.blacklisted_areas = typecacheof(list(/area/centcom, /area/shadekin))
|
||||
@@ -0,0 +1,258 @@
|
||||
/////////////////////
|
||||
/// PHASE SHIFT ///
|
||||
/////////////////////
|
||||
//Visual effect for phase in/out
|
||||
/obj/effect/temp_visual/shadekin
|
||||
randomdir = FALSE
|
||||
duration = 5
|
||||
icon = 'icons/mob/vore_shadekin.dmi'
|
||||
|
||||
/obj/effect/temp_visual/shadekin/phase_in
|
||||
icon_state = "tp_in"
|
||||
|
||||
/obj/effect/temp_visual/shadekin/phase_out
|
||||
icon_state = "tp_out"
|
||||
|
||||
/datum/power/shadekin/phase_shift
|
||||
name = "Phase Shift (100)"
|
||||
desc = "Shift yourself out of alignment with realspace to travel quickly to different areas."
|
||||
verbpath = /mob/living/proc/phase_shift
|
||||
ability_icon_state = "phase_shift"
|
||||
|
||||
/mob/living/proc/phase_shift()
|
||||
set name = "Phase Shift (100)"
|
||||
set desc = "Shift yourself out of alignment with realspace to travel quickly to different areas."
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(!SK)
|
||||
return FALSE
|
||||
if(stat)
|
||||
to_chat(src, span_warning("Can't use that ability in your state!"))
|
||||
return FALSE
|
||||
var/area/A = get_area(src)
|
||||
if(!client?.holder && A.flag_check(AREA_BLOCK_PHASE_SHIFT))
|
||||
to_chat(src, span_warning("You can't do that here!"))
|
||||
return
|
||||
|
||||
var/ability_cost = 100
|
||||
|
||||
var/darkness = 1
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
to_chat(src,span_warning("You can't use that here!"))
|
||||
return FALSE
|
||||
|
||||
if(SK.doing_phase)
|
||||
return FALSE
|
||||
|
||||
var/brightness = T.get_lumcount() //Brightness in 0.0 to 1.0
|
||||
darkness = 1-brightness //Invert
|
||||
|
||||
var/watcher = 0
|
||||
for(var/mob/living/carbon/human/watchers in oview(7,src )) // If we can see them...
|
||||
if(watchers in oviewers(7,src)) // And they can see us...
|
||||
if(!(watchers.stat) && !isbelly(watchers.loc) && !istype(watchers.loc, /obj/item/holder)) // And they are alive and not being held by someone...
|
||||
watcher++ // They are watching us!
|
||||
|
||||
ability_cost = CLAMP(ability_cost/(0.01+darkness*2),50, 80)//This allows for 1 watcher in full light
|
||||
if(watcher>0)
|
||||
ability_cost = ability_cost + ( 15 * watcher )
|
||||
/*
|
||||
if(!(SK.in_phase))
|
||||
log_debug("[src] attempted to shift with [watcher] visible Carbons with a cost of [ability_cost] in a darkness level of [darkness]")
|
||||
*/
|
||||
|
||||
if(SK.doing_phase)
|
||||
to_chat(src, span_warning("You are already trying to phase!"))
|
||||
return FALSE
|
||||
else if(SK.shadekin_get_energy() < ability_cost && !(SK.in_phase))
|
||||
to_chat(src, span_warning("Not enough energy for that ability!"))
|
||||
return FALSE
|
||||
|
||||
if(!(SK.in_phase))
|
||||
SK.shadekin_adjust_energy(-ability_cost)
|
||||
playsound(src, 'sound/effects/stealthoff.ogg', 75, 1)
|
||||
|
||||
if(!T.CanPass(src,T) || loc != T)
|
||||
to_chat(src,span_warning("You can't use that here!"))
|
||||
return FALSE
|
||||
|
||||
//Shifting in
|
||||
if(SK.in_phase)
|
||||
phase_in(T, SK)
|
||||
//Shifting out
|
||||
else
|
||||
phase_out(T, SK)
|
||||
|
||||
|
||||
/mob/living/proc/phase_in(var/turf/T, var/datum/component/shadekin/SK)
|
||||
if(SK.in_phase)
|
||||
|
||||
// pre-change
|
||||
if(!isturf(T)) //Sanity
|
||||
return
|
||||
forceMove(T)
|
||||
var/original_canmove = canmove
|
||||
SetStunned(0)
|
||||
SetWeakened(0)
|
||||
if(buckled)
|
||||
buckled.unbuckle_mob()
|
||||
if(pulledby)
|
||||
pulledby.stop_pulling()
|
||||
stop_pulling()
|
||||
|
||||
// change
|
||||
canmove = FALSE
|
||||
SK.in_phase = FALSE
|
||||
SK.doing_phase = TRUE
|
||||
throwpass = FALSE
|
||||
name = get_visible_name()
|
||||
for(var/obj/belly/B as anything in vore_organs)
|
||||
B.escapable = initial(B.escapable)
|
||||
|
||||
//cut_overlays()
|
||||
invisibility = initial(invisibility)
|
||||
see_invisible = initial(see_invisible)
|
||||
incorporeal_move = initial(incorporeal_move)
|
||||
density = initial(density)
|
||||
can_pull_size = initial(can_pull_size)
|
||||
can_pull_mobs = initial(can_pull_mobs)
|
||||
hovering = initial(hovering)
|
||||
update_icon()
|
||||
|
||||
//Cosmetics mostly
|
||||
var/obj/effect/temp_visual/shadekin/phase_in/phaseanim = new /obj/effect/temp_visual/shadekin/phase_in(src.loc)
|
||||
phaseanim.pixel_y = (src.size_multiplier - 1) * 16 // Pixel shift for the animation placement
|
||||
phaseanim.adjust_scale(src.size_multiplier, src.size_multiplier)
|
||||
phaseanim.dir = dir
|
||||
alpha = 0
|
||||
automatic_custom_emote(VISIBLE_MESSAGE,"phases in!")
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(shadekin_complete_phase_in), original_canmove, SK), 5, TIMER_DELETE_ME)
|
||||
|
||||
|
||||
/mob/living/proc/shadekin_complete_phase_in(var/original_canmove, var/datum/component/shadekin/SK)
|
||||
canmove = original_canmove
|
||||
alpha = initial(alpha)
|
||||
remove_modifiers_of_type(/datum/modifier/shadekin_phase_vision)
|
||||
remove_modifiers_of_type(/datum/modifier/phased_out)
|
||||
|
||||
//Potential phase-in vore
|
||||
|
||||
if(can_be_drop_pred || can_be_drop_prey) //Toggleable in vore panel
|
||||
var/list/potentials = living_mobs(0)
|
||||
var/mob/living/our_prey
|
||||
if(potentials.len)
|
||||
var/mob/living/target = pick(potentials)
|
||||
if(can_be_drop_pred && istype(target) && target.devourable && target.can_be_drop_prey && target.phase_vore && vore_selected && phase_vore)
|
||||
target.forceMove(vore_selected)
|
||||
to_chat(target, span_vwarning("\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!"))
|
||||
to_chat(src, span_vwarning("You phase around [target], [vore_selected.vore_verb]ing them into your [vore_selected.name]!"))
|
||||
our_prey = target
|
||||
else if(can_be_drop_prey && istype(target) && devourable && target.can_be_drop_pred && target.phase_vore && target.vore_selected && phase_vore)
|
||||
our_prey = src
|
||||
forceMove(target.vore_selected)
|
||||
to_chat(target, span_vwarning("\The [src] phases into you, [target.vore_selected.vore_verb]ing them into your [target.vore_selected.name]!"))
|
||||
to_chat(src, span_vwarning("You phase into [target], having them [target.vore_selected.vore_verb] you into their [target.vore_selected.name]!"))
|
||||
if(our_prey)
|
||||
for(var/obj/item/flashlight/held_lights in our_prey.contents)
|
||||
if(istype(held_lights,/obj/item/flashlight/glowstick) ||istype(held_lights,/obj/item/flashlight/flare) ) //No affecting glowsticks or flares...As funny as that is
|
||||
continue
|
||||
held_lights.on = 0
|
||||
held_lights.update_brightness()
|
||||
|
||||
SK.doing_phase = FALSE
|
||||
if(!SK.flicker_time)
|
||||
return //Early return. No time, no flickering.
|
||||
//Affect nearby lights
|
||||
for(var/obj/machinery/light/L in range(SK.flicker_distance, src))
|
||||
if(prob(SK.flicker_break_chance))
|
||||
addtimer(CALLBACK(L, TYPE_PROC_REF(/obj/machinery/light, broken)), rand(5,25), TIMER_DELETE_ME)
|
||||
else
|
||||
if(SK.flicker_color)
|
||||
L.flicker(SK.flicker_time, SK.flicker_color)
|
||||
else
|
||||
L.flicker(SK.flicker_time)
|
||||
for(var/obj/item/flashlight/flashlights in range(SK.flicker_distance, src)) //Find any flashlights near us and make them flicker too!
|
||||
if(istype(flashlights,/obj/item/flashlight/glowstick) ||istype(flashlights,/obj/item/flashlight/flare)) //No affecting glowsticks or flares...As funny as that is
|
||||
continue
|
||||
flashlights.flicker(SK.flicker_time, SK.flicker_color, TRUE)
|
||||
for(var/mob/living/creatures in range(SK.flicker_distance, src))
|
||||
if(isbelly(creatures.loc)) //don't flicker anyone that gets nomphed.
|
||||
continue
|
||||
for(var/obj/item/flashlight/held_lights in creatures.contents)
|
||||
if(istype(held_lights,/obj/item/flashlight/glowstick) ||istype(held_lights,/obj/item/flashlight/flare) ) //No affecting glowsticks or flares...As funny as that is
|
||||
continue
|
||||
held_lights.flicker(SK.flicker_time, SK.flicker_color, TRUE)
|
||||
|
||||
/mob/living/proc/phase_out(var/turf/T)
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(!(SK.in_phase))
|
||||
// pre-change
|
||||
forceMove(T)
|
||||
var/original_canmove = canmove
|
||||
SetStunned(0)
|
||||
SetWeakened(0)
|
||||
if(buckled)
|
||||
buckled.unbuckle_mob()
|
||||
if(pulledby)
|
||||
pulledby.stop_pulling()
|
||||
stop_pulling()
|
||||
if(SK.normal_phase && SK.drop_items_on_phase)
|
||||
drop_both_hands()
|
||||
if(back)
|
||||
unEquip(back)
|
||||
|
||||
can_pull_size = 0
|
||||
can_pull_mobs = MOB_PULL_NONE
|
||||
hovering = TRUE
|
||||
canmove = FALSE
|
||||
|
||||
// change
|
||||
SK.in_phase = TRUE
|
||||
SK.doing_phase = TRUE
|
||||
throwpass = TRUE
|
||||
automatic_custom_emote(VISIBLE_MESSAGE,"phases out!")
|
||||
|
||||
if(real_name) //If we a real name, perfect, let's just set our name to our newfound visible name.
|
||||
name = get_visible_name()
|
||||
else //If we don't, let's put our real_name as our initial name.
|
||||
real_name = initial(name)
|
||||
name = get_visible_name()
|
||||
|
||||
for(var/obj/belly/B as anything in vore_organs)
|
||||
B.escapable = FALSE
|
||||
|
||||
var/obj/effect/temp_visual/shadekin/phase_out/phaseanim = new /obj/effect/temp_visual/shadekin/phase_out(src.loc)
|
||||
phaseanim.pixel_y = (src.size_multiplier - 1) * 16 // Pixel shift for the animation placement
|
||||
phaseanim.adjust_scale(src.size_multiplier, src.size_multiplier)
|
||||
phaseanim.dir = dir
|
||||
alpha = 0
|
||||
add_modifier(/datum/modifier/shadekin_phase_vision)
|
||||
if(SK.normal_phase)
|
||||
add_modifier(/datum/modifier/phased_out)
|
||||
addtimer(CALLBACK(src, PROC_REF(complete_phase_out), original_canmove, SK), 5, TIMER_DELETE_ME)
|
||||
|
||||
|
||||
/mob/living/proc/complete_phase_out(original_canmove, var/datum/component/shadekin/SK)
|
||||
invisibility = INVISIBILITY_SHADEKIN
|
||||
see_invisible = INVISIBILITY_SHADEKIN
|
||||
see_invisible_default = INVISIBILITY_SHADEKIN // Allow seeing phased entities while phased.
|
||||
update_icon()
|
||||
alpha = 127
|
||||
|
||||
canmove = original_canmove
|
||||
incorporeal_move = TRUE
|
||||
density = FALSE
|
||||
SK.doing_phase = FALSE
|
||||
|
||||
/datum/modifier/shadekin_phase_vision
|
||||
name = "Shadekin Phase Vision"
|
||||
vision_flags = SEE_THRU
|
||||
|
||||
/datum/modifier/phased_out
|
||||
name = "Phased Out"
|
||||
desc = "You are currently phased out of realspace, and cannot interact with it."
|
||||
hidden = TRUE
|
||||
//Stops you from using guns. See /obj/item/gun/proc/special_check(var/mob/user)
|
||||
@@ -0,0 +1,66 @@
|
||||
//////////////////////////
|
||||
/// REGENERATE OTHER ///
|
||||
//////////////////////////
|
||||
/datum/power/shadekin/regenerate_other
|
||||
name = "Regenerate Other (50)"
|
||||
desc = "Spend energy to heal physical wounds in another creature."
|
||||
verbpath = /mob/living/proc/regenerate_other
|
||||
ability_icon_state = "shadekin_regen"
|
||||
|
||||
/mob/living/proc/regenerate_other()
|
||||
set name = "Regenerate Other (50)"
|
||||
set desc = "Spend energy to heal physical wounds in another creature."
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
var/ability_cost = 50
|
||||
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(!SK)
|
||||
return FALSE
|
||||
if(stat)
|
||||
to_chat(src, span_warning("Can't use that ability in your state!"))
|
||||
return FALSE
|
||||
else if(SK.shadekin_get_energy() < ability_cost)
|
||||
to_chat(src, span_warning("Not enough energy for that ability!"))
|
||||
return FALSE
|
||||
else if(SK.in_phase)
|
||||
to_chat(src, span_warning("You can't use that while phase shifted!"))
|
||||
return FALSE
|
||||
|
||||
var/list/viewed = oview(1)
|
||||
var/list/targets = list()
|
||||
for(var/mob/living/L in viewed)
|
||||
targets += L
|
||||
if(!targets.len)
|
||||
to_chat(src,span_warning("Nobody nearby to mend!"))
|
||||
return FALSE
|
||||
|
||||
var/mob/living/target = tgui_input_list(src,"Pick someone to mend:","Mend Other", targets)
|
||||
if(!target)
|
||||
return FALSE
|
||||
|
||||
target.add_modifier(/datum/modifier/shadekin/heal_boop,1 MINUTE)
|
||||
playsound(src, 'sound/effects/EMPulse.ogg', 75, 1)
|
||||
SK.shadekin_adjust_energy(-ability_cost)
|
||||
visible_message(span_notice("\The [src] gently places a hand on \the [target]..."))
|
||||
face_atom(target)
|
||||
return TRUE
|
||||
|
||||
/datum/modifier/shadekin/heal_boop
|
||||
name = "Shadekin Regen"
|
||||
desc = "You feel serene and well rested."
|
||||
mob_overlay_state = "green_sparkles"
|
||||
|
||||
on_created_text = span_notice("Sparkles begin to appear around you, and all your ills seem to fade away.")
|
||||
on_expired_text = span_notice("The sparkles have faded, although you feel much healthier than before.")
|
||||
stacks = MODIFIER_STACK_EXTEND
|
||||
|
||||
/datum/modifier/shadekin/heal_boop/tick()
|
||||
if(!holder.getBruteLoss() && !holder.getFireLoss() && !holder.getToxLoss() && !holder.getOxyLoss() && !holder.getCloneLoss()) // No point existing if the spell can't heal.
|
||||
expire()
|
||||
return
|
||||
holder.adjustBruteLoss(-2)
|
||||
holder.adjustFireLoss(-2)
|
||||
holder.adjustToxLoss(-2)
|
||||
holder.adjustOxyLoss(-2)
|
||||
holder.adjustCloneLoss(-2)
|
||||
@@ -0,0 +1,224 @@
|
||||
//See comp_helpers.dm for helper procs.
|
||||
/datum/component/shadekin
|
||||
VAR_PRIVATE/mob/living/owner
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE
|
||||
|
||||
//Energy Vars
|
||||
///How much energy we have RIGHT NOW
|
||||
var/dark_energy = 100
|
||||
///How much energy we can have
|
||||
var/max_dark_energy = 100
|
||||
///Always be at our max_dark_energy
|
||||
var/dark_energy_infinite = FALSE
|
||||
///How much energy we generate in the dark
|
||||
var/energy_dark = 0.75
|
||||
///How much energy we generate in the light
|
||||
var/energy_light = 0.25
|
||||
///If we care about eye color when it comes to factoring in energy
|
||||
var/eye_color_influences_energy = TRUE
|
||||
///Energy gain from nutrition
|
||||
var/nutrition_conversion_scaling = 0.5
|
||||
///If we convert nutrition to energy
|
||||
var/nutrition_energy_conversion = FALSE
|
||||
|
||||
//Phase Vars
|
||||
///Are we currently in a phase transition?
|
||||
var/doing_phase = FALSE
|
||||
///Are we currently phased?
|
||||
var/in_phase = FALSE
|
||||
///Chance to break lights on phase-in
|
||||
var/flicker_break_chance = 0
|
||||
///Color that lights will flicker to on phase-in. Off by default.
|
||||
var/flicker_color
|
||||
///Time that lights will flicker on phase-in. Default is 10 times.
|
||||
var/flicker_time = 10
|
||||
///Range that we flicker lights. Default is 10.
|
||||
var/flicker_distance = 10
|
||||
///If we can get the 'phase debuff' applied to us. (No using guns, dropping things in hands, etc).
|
||||
var/normal_phase = TRUE
|
||||
///If we drop items on phase.
|
||||
var/drop_items_on_phase = FALSE
|
||||
|
||||
//Dark Respite Vars (Unused on Virgo)
|
||||
///If we are in dark respite or not
|
||||
var/in_dark_respite = FALSE
|
||||
var/manual_respite = FALSE
|
||||
var/respite_activating = FALSE
|
||||
|
||||
//Dark Tunneling Vars (Unused on Virgo)
|
||||
///If we have already made a dark tunnel
|
||||
var/created_dark_tunnel = FALSE
|
||||
|
||||
//Dark Maw Vars (Unused on Virgo)
|
||||
///Our current active dark maws
|
||||
var/list/active_dark_maws = list()
|
||||
|
||||
//Ability Vars
|
||||
///The innate abilities we start with
|
||||
var/list/shadekin_abilities = list(/datum/power/shadekin/phase_shift,
|
||||
/datum/power/shadekin/regenerate_other,
|
||||
/datum/power/shadekin/create_shade)
|
||||
///Datum holder. Largely ignore this.
|
||||
var/list/shadekin_ability_datums = list()
|
||||
|
||||
//Misc Vars
|
||||
///Eyecolor
|
||||
var/eye_color = BLUE_EYES
|
||||
///For downstream. Enables some extra verbs. Causes things to drop in hand when you phase.
|
||||
var/extended_kin = FALSE
|
||||
|
||||
/datum/component/shadekin/phase_only
|
||||
shadekin_abilities = list(/datum/power/shadekin/phase_shift)
|
||||
|
||||
/datum/component/shadekin/full
|
||||
shadekin_abilities = list(/datum/power/shadekin/phase_shift,
|
||||
/datum/power/shadekin/regenerate_other,
|
||||
/datum/power/shadekin/create_shade,
|
||||
/datum/power/shadekin/dark_maw,
|
||||
/datum/power/shadekin/dark_respite,
|
||||
/datum/power/shadekin/dark_tunneling)
|
||||
extended_kin = TRUE
|
||||
drop_items_on_phase = TRUE
|
||||
|
||||
/datum/component/shadekin/full/rakshasa
|
||||
flicker_time = 0 //Rakshasa don't flicker lights when they phase in.
|
||||
dark_energy_infinite = TRUE
|
||||
normal_phase = FALSE
|
||||
|
||||
/datum/component/shadekin/Initialize()
|
||||
//normal component bs
|
||||
if(!isliving(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
owner = parent
|
||||
add_shadekin_abilities(owner)
|
||||
if(ishuman(owner))
|
||||
RegisterSignal(owner, COMSIG_SHADEKIN_COMPONENT, PROC_REF(handle_comp)) //Happens every species tick.
|
||||
else
|
||||
RegisterSignal(owner, COMSIG_LIVING_LIFE, PROC_REF(handle_comp)) //Happens every life tick (mobs)
|
||||
|
||||
//generates powers and then adds them
|
||||
for(var/power in shadekin_abilities)
|
||||
var/datum/power/shadekin/SKP = new power(src)
|
||||
shadekin_ability_datums.Add(SKP)
|
||||
add_shadekin_abilities()
|
||||
|
||||
handle_comp() //First hit is free!
|
||||
|
||||
//decides what 'eye color' we are and how much energy we should get
|
||||
set_shadekin_eyecolor() //Gets what eye color we are.
|
||||
set_eye_energy() //Sets the energy values based on our eye color.
|
||||
|
||||
//Misc stuff we need to do
|
||||
if(extended_kin)
|
||||
add_verb(owner, /mob/living/proc/nutrition_conversion_toggle)
|
||||
add_verb(owner, /mob/living/proc/flicker_adjustment)
|
||||
|
||||
/datum/component/shadekin/Destroy(force)
|
||||
if(ishuman(owner))
|
||||
UnregisterSignal(owner, COMSIG_SHADEKIN_COMPONENT)
|
||||
else
|
||||
UnregisterSignal(owner, COMSIG_LIVING_LIFE)
|
||||
if(extended_kin)
|
||||
remove_verb(owner, /mob/living/proc/nutrition_conversion_toggle)
|
||||
remove_verb(owner, /mob/living/proc/flicker_adjustment)
|
||||
for(var/datum/power in shadekin_ability_datums)
|
||||
qdel(power)
|
||||
for(var/obj/effect/abstract/dark_maw/dm as anything in active_dark_maws) //if the component gets destroyed so does your precious maws
|
||||
if(!QDELETED(dm))
|
||||
qdel(dm)
|
||||
active_dark_maws.Cut()
|
||||
shadekin_abilities.Cut()
|
||||
shadekin_ability_datums.Cut()
|
||||
owner = null
|
||||
. = ..()
|
||||
|
||||
///Handles the component running.
|
||||
/datum/component/shadekin/proc/handle_comp()
|
||||
SIGNAL_HANDLER
|
||||
if(QDELETED(parent))
|
||||
return
|
||||
if(owner.stat == DEAD) //dead, don't process.
|
||||
return
|
||||
handle_shade()
|
||||
|
||||
///Handles the shadekin's energy gain and loss.
|
||||
/datum/component/shadekin/proc/handle_shade()
|
||||
//Shifted kin don't gain/lose energy (and save time if we're at the cap)
|
||||
var/darkness = 1
|
||||
var/dark_gains = 0
|
||||
|
||||
var/turf/T = get_turf(owner)
|
||||
if(!T)
|
||||
dark_gains = 0
|
||||
return
|
||||
|
||||
var/brightness = T.get_lumcount() //Brightness in 0.0 to 1.0
|
||||
darkness = 1-brightness //Invert
|
||||
var/is_dark = (darkness >= 0.5)
|
||||
|
||||
if(in_phase)
|
||||
dark_gains = 0
|
||||
else
|
||||
//Heal (very) slowly in good darkness
|
||||
if(is_dark)
|
||||
//The below sends a DB query...This needs to be fixed before this can be enabled as we're now dealing with signal handlers.
|
||||
//Reenable once that mess is taken care of.
|
||||
owner.adjustFireLoss((-0.10)*darkness)
|
||||
owner.adjustBruteLoss((-0.10)*darkness)
|
||||
owner.adjustToxLoss((-0.10)*darkness)
|
||||
//energy_dark and energy_light are set by the shadekin eye traits.
|
||||
//These are balanced around their playstyles and 2 planned new aggressive abilities
|
||||
dark_gains = energy_dark
|
||||
else
|
||||
dark_gains = energy_light
|
||||
|
||||
shadekin_adjust_energy(dark_gains)
|
||||
|
||||
//Update huds
|
||||
update_shadekin_hud()
|
||||
|
||||
|
||||
/mob/living/proc/nutrition_conversion_toggle()
|
||||
set name = "Toggle Energy <-> Nutrition conversions"
|
||||
set desc = "Toggle dark energy and nutrition being converted into each other when full"
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(!SK)
|
||||
to_chat(src, span_warning("Only a shadekin can use that!"))
|
||||
return FALSE
|
||||
|
||||
if(SK.nutrition_energy_conversion)
|
||||
to_chat(src, span_notice("Nutrition and dark energy conversions disabled."))
|
||||
SK.nutrition_energy_conversion = 0
|
||||
else
|
||||
to_chat(src, span_notice("Nutrition and dark energy conversions enabled."))
|
||||
SK.nutrition_energy_conversion = 1
|
||||
|
||||
/mob/living/proc/flicker_adjustment()
|
||||
set name = "Adjust Light Flicker"
|
||||
set desc = "Allows you to adjust the settings of the light flicker when you phase in!"
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(!SK)
|
||||
to_chat(src, span_warning("Only a shadekin can use that!"))
|
||||
return FALSE
|
||||
var/flicker_timer = tgui_input_number(src, "Adjust how long lights flicker when you phase in! (Min 10 Max 20 times!)", "Set Flicker", SK.flicker_time, 20, 10)
|
||||
if(flicker_timer > 20 || flicker_timer < 10)
|
||||
to_chat(src,"<span class='warning'>You must choose a number between 10 and 20</span>")
|
||||
return
|
||||
SK.flicker_time = flicker_timer
|
||||
to_chat(src,"<span class='warning'>Flicker timer set to [SK.flicker_time] seconds!</span>")
|
||||
|
||||
var/set_new_color = tgui_color_picker(src,"Select a color you wish the lights to flicker as (Default is #E0EFF0)","Color Selector", SK.flicker_color)
|
||||
if(set_new_color)
|
||||
SK.flicker_color = set_new_color
|
||||
to_chat(src,"<span class='warning'>Flicker color set to [SK.flicker_color]!</span>")
|
||||
|
||||
var/break_chance = tgui_input_number(src, "Adjust the % chance for lights to break when you phase in! (Default 0. Min 0. Max 25)", "Set Break Chance", 0, 25, 0)
|
||||
if(break_chance > 25 || break_chance < 0)
|
||||
to_chat(src,"<span class='warning'>You must choose a number between 0 and 25</span>")
|
||||
return
|
||||
SK.flicker_break_chance = break_chance
|
||||
to_chat(src,"<span class='warning'>Break chance set to [SK.flicker_break_chance]%</span>")
|
||||
@@ -6,7 +6,7 @@ var/bluespace_item_types = newlist(/obj/item/storage/backpack/holding,
|
||||
)
|
||||
|
||||
//wrapper
|
||||
/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit
|
||||
/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE, bohsafe=FALSE)
|
||||
new /datum/teleport/instant/science(arglist(args))
|
||||
return
|
||||
|
||||
@@ -19,26 +19,28 @@ var/bluespace_item_types = newlist(/obj/item/storage/backpack/holding,
|
||||
var/soundin //soundfile to play before teleportation
|
||||
var/soundout //soundfile to play after teleportation
|
||||
var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation)
|
||||
var/local = TRUE //VOREStation Add - If false, can teleport from/to any z-level
|
||||
var/local = TRUE //If false, can teleport from/to any z-level
|
||||
var/bohsafe = FALSE //If true, can teleport safely with a BoH
|
||||
|
||||
|
||||
/datum/teleport/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit
|
||||
/datum/teleport/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE, bohsafe=FALSE)
|
||||
..()
|
||||
if(!initTeleport(arglist(args)))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout,local) //VOREStation Edit
|
||||
/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout,local,bohsafe)
|
||||
if(!setTeleatom(ateleatom))
|
||||
return 0
|
||||
if(!setDestination(adestination))
|
||||
return 0
|
||||
src.bohsafe = bohsafe
|
||||
if(!setPrecision(aprecision))
|
||||
return 0
|
||||
setEffects(aeffectin,aeffectout)
|
||||
setForceTeleport(afteleport)
|
||||
setSounds(asoundin,asoundout)
|
||||
src.local = local // VOREStation Add
|
||||
src.local = local
|
||||
return 1
|
||||
|
||||
//must succeed
|
||||
@@ -121,7 +123,7 @@ var/bluespace_item_types = newlist(/obj/item/storage/backpack/holding,
|
||||
var/mob/living/L = teleatom
|
||||
if(L.buckled)
|
||||
C = L.buckled
|
||||
if(attempt_vr(src,"try_televore",args)) return //VOREStation Edit - Telenoms.
|
||||
if(attempt_vr(src,"try_televore",args)) return
|
||||
if(force_teleport)
|
||||
teleatom.forceMove(destturf)
|
||||
playSpecials(destturf,effectout,soundout)
|
||||
@@ -140,7 +142,7 @@ var/bluespace_item_types = newlist(/obj/item/storage/backpack/holding,
|
||||
|
||||
/datum/teleport/instant //teleports when datum is created
|
||||
|
||||
/datum/teleport/instant/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null)
|
||||
/datum/teleport/instant/New(ateleatom, adestination, aprecision=0, afteleport=1, bohsafe=0, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null)
|
||||
if(..())
|
||||
teleport()
|
||||
return
|
||||
@@ -150,14 +152,18 @@ var/bluespace_item_types = newlist(/obj/item/storage/backpack/holding,
|
||||
if(!aeffectin || !aeffectout)
|
||||
var/datum/effect/effect/system/spark_spread/aeffect = new
|
||||
aeffect.set_up(5, 1, teleatom)
|
||||
var/datum/effect/effect/system/spark_spread/aeffect2 = new
|
||||
aeffect2.set_up(5, 1, teleatom)
|
||||
effectin = effectin || aeffect
|
||||
effectout = effectout || aeffect
|
||||
effectout = effectout || aeffect2
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
/datum/teleport/instant/science/setPrecision(aprecision)
|
||||
..()
|
||||
if(bohsafe)
|
||||
return 1
|
||||
|
||||
var/list/bluespace_things = newlist()
|
||||
|
||||
@@ -166,14 +172,12 @@ var/bluespace_item_types = newlist(/obj/item/storage/backpack/holding,
|
||||
precision = rand(1, 100)
|
||||
bluespace_things |= teleatom.search_contents_for(item)
|
||||
|
||||
//VOREStation Addition Start: Prevent taurriding abuse
|
||||
if(isliving(teleatom))
|
||||
var/mob/living/L = teleatom
|
||||
if(LAZYLEN(L.buckled_mobs))
|
||||
for(var/mob/rider in L.buckled_mobs)
|
||||
for (var/item in bluespace_item_types)
|
||||
bluespace_things |= rider.search_contents_for(item)
|
||||
//VOREStation Addition End: Prevent taurriding abuse
|
||||
|
||||
if(bluespace_things.len)
|
||||
precision = max(rand(1,100)*bluespace_things.len,100)
|
||||
@@ -194,17 +198,6 @@ var/bluespace_item_types = newlist(/obj/item/storage/backpack/holding,
|
||||
else
|
||||
teleatom.visible_message(span_danger("\The [teleatom] bounces off of the portal!"))
|
||||
return 0
|
||||
/* VOREStation Removal
|
||||
if(destination.z in using_map.admin_levels) //CentCom z-level
|
||||
if(istype(teleatom, /obj/mecha))
|
||||
var/obj/mecha/MM = teleatom
|
||||
to_chat(MM.occupant, span_danger("\The [MM] would not survive the jump to a location so far away!"))
|
||||
return 0
|
||||
if(!isemptylist(teleatom.search_contents_for(/obj/item/storage/backpack/holding)))
|
||||
teleatom.visible_message(span_danger("\The [teleatom] bounces off of the portal!"))
|
||||
return 0
|
||||
*/ //VOREStation Removal End
|
||||
//VOREStation Edit Start
|
||||
var/obstructed = 0
|
||||
var/turf/dest_turf = get_turf(destination)
|
||||
if(local && !(dest_turf.z in using_map.player_levels))
|
||||
@@ -231,4 +224,3 @@ var/bluespace_item_types = newlist(/obj/item/storage/backpack/holding,
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
//VOREStation Edit End
|
||||
|
||||
@@ -2666,3 +2666,10 @@ var/list/the_station_areas = list (
|
||||
luminosity = 1
|
||||
dynamic_lighting = 0
|
||||
requires_power = 0
|
||||
|
||||
/area/shadekin
|
||||
name = "\improper Shadekin Retreat"
|
||||
icon_state = "blue"
|
||||
requires_power = 0
|
||||
ambience = AMBIENCE_OTHERWORLDLY
|
||||
flags = RAD_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT | BLUE_SHIELDED | AREA_ALLOW_LARGE_SIZE | AREA_LIMIT_DARK_RESPITE | AREA_ALLOW_CLOCKOUT
|
||||
|
||||
+4
-11
@@ -563,23 +563,16 @@ GLOBAL_DATUM(spoiler_obfuscation_image, /image)
|
||||
return (flags & flag) == flag
|
||||
return flags & flag
|
||||
|
||||
// RS Port #658 Start
|
||||
/area/proc/check_phase_shift(var/mob/ourmob)
|
||||
/area/proc/check_phase_shift(var/mob/living/ourmob)
|
||||
if(!flag_check(AREA_BLOCK_PHASE_SHIFT) || !ourmob.is_incorporeal())
|
||||
return
|
||||
if(!isliving(ourmob))
|
||||
return
|
||||
if(ourmob.client?.holder)
|
||||
return
|
||||
if(issimplekin(ourmob))
|
||||
var/mob/living/simple_mob/shadekin/SK = ourmob
|
||||
if(SK.ability_flags & AB_PHASE_SHIFTED)
|
||||
SK.phase_in(SK.loc)
|
||||
if(ishuman(ourmob))
|
||||
var/mob/living/carbon/human/SK = ourmob
|
||||
if(SK.ability_flags & AB_PHASE_SHIFTED)
|
||||
SK.phase_in(SK.loc)
|
||||
// RS Port #658 End
|
||||
var/datum/component/shadekin/SK = ourmob.get_shadekin_component()
|
||||
if(SK && SK.in_phase)
|
||||
ourmob.phase_in(ourmob.loc, SK)
|
||||
|
||||
/area/proc/isAlwaysIndoors()
|
||||
return FALSE
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
var/cell_type = /obj/item/cell/device
|
||||
var/power_usage = 1
|
||||
var/power_use = 1
|
||||
var/flickering = FALSE
|
||||
pickup_sound = 'sound/items/pickup/device.ogg'
|
||||
drop_sound = 'sound/items/drop/device.ogg'
|
||||
|
||||
@@ -88,6 +89,9 @@
|
||||
. += "It appears to have a high amount of power remaining."
|
||||
|
||||
/obj/item/flashlight/attack_self(mob/user)
|
||||
if(flickering)
|
||||
to_chat(user, "The light is currently malfunctioning and you're unable to adjust it!") //To prevent some lighting anomalities.
|
||||
return
|
||||
if(power_use)
|
||||
if(!isturf(user.loc))
|
||||
to_chat(user, "You cannot turn the light on while in this [user.loc].") //To prevent some lighting anomalities.
|
||||
@@ -240,6 +244,48 @@
|
||||
var/turf/T = get_turf(target)
|
||||
OL.place_directional_light(T)
|
||||
|
||||
/obj/item/flashlight/proc/flicker(var/amount = rand(10, 20), var/flicker_color, var/forced)
|
||||
if(flickering)
|
||||
return
|
||||
if(!flicker_color)
|
||||
flicker_color = light_color //If we don't have a flicker color, use our current light color.
|
||||
if((!power_use && !forced) || (!power_usage && !forced))
|
||||
return //We don't use power / have no power, so we're probably a flare or dead.
|
||||
flickering = TRUE
|
||||
var/original_color = light_color
|
||||
var/original_on = on
|
||||
var/datum/component/overlay_lighting/OL = GetComponent(/datum/component/overlay_lighting) //BEWARE, ESOTERIC BULLSHIT HERE.
|
||||
if(flicker_color && light_color != flicker_color)
|
||||
set_light_color(flicker_color)
|
||||
OL.directional_atom.color = flicker_color
|
||||
do_flicker(amount, flicker_color, original_color, original_on, OL, 1)
|
||||
|
||||
|
||||
/// Args:
|
||||
/// amount is how many timer to flicker.
|
||||
/// flicker_color is what to set the flashlight to when we flicker.
|
||||
/// original_color is what our original color was prior to flickering
|
||||
/// original_on is if we were originally on or not.
|
||||
/// OL is our overlay for lighting.
|
||||
/// ticker is how many times we have flickered so far.
|
||||
/obj/item/flashlight/proc/do_flicker(var/amount = rand(10, 20), var/flicker_color, var/original_color, var/original_on, var/datum/component/overlay_lighting/OL, var/ticker)
|
||||
if(ticker >= amount) //We have flickered enough times. Terminate the cycle.
|
||||
finish_flicker(original_color, original_on, OL)
|
||||
return
|
||||
on = !on
|
||||
update_brightness()
|
||||
if(!on) // Only play when the light turns off.
|
||||
playsound(src, 'sound/effects/light_flicker.ogg', 50, 1)
|
||||
addtimer(CALLBACK(src, PROC_REF(do_flicker), amount, flicker_color, original_color, original_on, OL, ++ticker), rand(5,15), TIMER_DELETE_ME)
|
||||
|
||||
/obj/item/flashlight/proc/finish_flicker(var/original_color, var/original_on, var/datum/component/overlay_lighting/OL)
|
||||
set_light_color(original_color)
|
||||
OL.directional_atom.color = original_color
|
||||
on = original_on
|
||||
flickering = FALSE
|
||||
update_brightness()
|
||||
|
||||
|
||||
/obj/item/flashlight/pen
|
||||
name = "penlight"
|
||||
desc = "A pen-sized light, used by medical staff."
|
||||
|
||||
@@ -283,3 +283,41 @@
|
||||
emote_message_1p_target = "You prbt at TARGET."
|
||||
emote_message_3p_target = "prbts at TARGET."
|
||||
emote_sound = 'sound/voice/prbt.ogg'
|
||||
|
||||
//Some Spooky sounds.
|
||||
/decl/emote/audible/evil_laugh
|
||||
key = "evillaugh"
|
||||
emote_message_3p = "laughs!"
|
||||
emote_sound = 'sound/mob/spooky/laugh.ogg'
|
||||
|
||||
/decl/emote/audible/evil_no
|
||||
key = "evilno"
|
||||
emote_message_3p = "says no!"
|
||||
emote_sound = 'sound/mob/spooky/no.ogg'
|
||||
|
||||
/decl/emote/audible/evil_breathing
|
||||
key = "evilbreath"
|
||||
emote_message_3p = "breaths heavily!"
|
||||
emote_sound = 'sound/mob/spooky/breath1.ogg'
|
||||
|
||||
/decl/emote/audible/evil_breathing_2
|
||||
key = "evilbreath2"
|
||||
emote_message_3p = "breaths heavily!"
|
||||
emote_sound = 'sound/mob/spooky/breath2.ogg'
|
||||
|
||||
/decl/emote/audible/goodripsound
|
||||
key = "goodripsound"
|
||||
emote_message_3p = "drips goo."
|
||||
|
||||
/decl/emote/audible/goodripsound/do_extra(var/mob/user)
|
||||
..()
|
||||
var/goo_sounds = list (
|
||||
'sound/mob/spooky/decay1.ogg',
|
||||
'sound/mob/spooky/decay2.ogg',
|
||||
'sound/mob/spooky/decay3.ogg',
|
||||
'sound/mob/spooky/corrosion1.ogg',
|
||||
'sound/mob/spooky/corrosion2.ogg',
|
||||
'sound/mob/spooky/corrosion3.ogg'
|
||||
)
|
||||
var/sound = pick(goo_sounds)
|
||||
playsound(user.loc, sound, 100, 1)
|
||||
|
||||
@@ -331,3 +331,18 @@
|
||||
/decl/emote/visible/tilt
|
||||
key = "tilt"
|
||||
emote_message_3p = "tilts USER_THEIR head."
|
||||
|
||||
/decl/emote/visible/goodrip
|
||||
key = "goodrip"
|
||||
emote_message_3p = "drips goo."
|
||||
|
||||
/decl/emote/visible/goodrip/do_extra(var/mob/user)
|
||||
..()
|
||||
new /obj/effect/decal/cleanable/blood/oil(user)
|
||||
var/goo_sounds = list (
|
||||
'sound/mob/spooky/decay1.ogg',
|
||||
'sound/mob/spooky/decay2.ogg',
|
||||
'sound/mob/spooky/decay3.ogg'
|
||||
)
|
||||
var/sound = pick(goo_sounds)
|
||||
playsound(user.loc, sound, 100, 1)
|
||||
|
||||
@@ -27,15 +27,12 @@
|
||||
if(world.time < next_emote)
|
||||
to_chat(src, span_warning("You cannot use another emote yet."))
|
||||
return
|
||||
//VOREStation Addition Start
|
||||
if(forced_psay)
|
||||
pme(message)
|
||||
return
|
||||
if(autowhisper)
|
||||
return me_verb_subtle(message)
|
||||
|
||||
//VOREStation Addition End
|
||||
|
||||
if(act == "help")
|
||||
if(world.time >= next_emote_refresh)
|
||||
var/list/usable_emotes = list()
|
||||
|
||||
@@ -332,12 +332,13 @@
|
||||
return if_no_id
|
||||
|
||||
//repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a seperate proc as it'll be useful elsewhere
|
||||
/mob/living/carbon/human/proc/get_visible_name()
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
/mob/living/carbon/human/get_visible_name()
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(SK && SK.in_phase)
|
||||
return "Something" // Something
|
||||
if( wear_mask && (wear_mask.flags_inv&HIDEFACE) ) //Wearing a mask which hides our face, use id-name if possible
|
||||
if(wear_mask && (wear_mask.flags_inv&HIDEFACE)) //Wearing a mask which hides our face, use id-name if possible
|
||||
return get_id_name("Unknown")
|
||||
if( head && (head.flags_inv&HIDEFACE) )
|
||||
if(head && (head.flags_inv&HIDEFACE))
|
||||
return get_id_name("Unknown") //Likewise for hats
|
||||
var/face_name = get_face_name()
|
||||
var/id_name = get_id_name("")
|
||||
|
||||
@@ -12,43 +12,3 @@
|
||||
var/low_sorting_priority = FALSE
|
||||
|
||||
tail_alt = TAIL_UPPER_LAYER // not a toggle for humans but a pointer for what layer the tail should be when facing North/East/West
|
||||
|
||||
/mob/living/carbon/human/proc/shadekin_get_energy()
|
||||
var/datum/species/shadekin/SK = species
|
||||
|
||||
if(!istype(SK))
|
||||
return 0
|
||||
|
||||
return SK.get_energy(src)
|
||||
|
||||
/mob/living/carbon/human/proc/shadekin_get_max_energy()
|
||||
var/datum/species/shadekin/SK = species
|
||||
|
||||
if(!istype(SK))
|
||||
return 0
|
||||
|
||||
return SK.get_max_energy(src)
|
||||
|
||||
/mob/living/carbon/human/proc/shadekin_set_energy(var/new_energy)
|
||||
var/datum/species/shadekin/SK = species
|
||||
|
||||
if(!istype(SK))
|
||||
return 0
|
||||
|
||||
SK.set_energy(src, new_energy)
|
||||
|
||||
/mob/living/carbon/human/proc/shadekin_set_max_energy(var/new_max_energy)
|
||||
var/datum/species/shadekin/SK = species
|
||||
|
||||
if(!istype(SK))
|
||||
return 0
|
||||
|
||||
SK.set_max_energy(src, new_max_energy)
|
||||
|
||||
/mob/living/carbon/human/proc/shadekin_adjust_energy(var/amount)
|
||||
var/datum/species/shadekin/SK = species
|
||||
|
||||
if(!istype(SK))
|
||||
return 0
|
||||
|
||||
SK.set_energy(src, SK.get_energy(src) + amount)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/mob/living/carbon/human/GetAltName()
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(SK && SK.in_phase)
|
||||
return ""
|
||||
if(name != GetVoice())
|
||||
return " (as [get_id_name("Unknown")])"
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
breath_type = null
|
||||
poison_type = null
|
||||
water_breather = TRUE //They don't quite breathe
|
||||
var/doing_phase = FALSE // Prevent bugs when spamming phase button
|
||||
|
||||
vision_flags = SEE_SELF|SEE_MOBS
|
||||
appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_SKIN_COLOR | HAS_EYE_COLOR | HAS_UNDERWEAR
|
||||
@@ -101,22 +100,8 @@
|
||||
BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right)
|
||||
)
|
||||
|
||||
//SHADEKIN-UNIQUE STUFF GOES HERE
|
||||
var/list/shadekin_abilities = list(/datum/power/shadekin/phase_shift,
|
||||
/datum/power/shadekin/regenerate_other,
|
||||
/datum/power/shadekin/create_shade)
|
||||
var/list/shadekin_ability_datums = list()
|
||||
var/kin_type
|
||||
var/energy_light = 0.25
|
||||
var/energy_dark = 0.75
|
||||
species_component = /datum/component/shadekin
|
||||
|
||||
/datum/species/shadekin/New()
|
||||
..()
|
||||
for(var/power in shadekin_abilities)
|
||||
var/datum/power/shadekin/SKP = new power(src)
|
||||
shadekin_ability_datums.Add(SKP)
|
||||
|
||||
/datum/species/shadekin/handle_death(var/mob/living/carbon/human/H)
|
||||
spawn(1)
|
||||
for(var/obj/item/W in H)
|
||||
@@ -129,212 +114,33 @@
|
||||
/datum/species/shadekin/get_random_name()
|
||||
return "shadekin"
|
||||
|
||||
/datum/species/shadekin/handle_environment_special(var/mob/living/carbon/human/H)
|
||||
handle_shade(H)
|
||||
..()
|
||||
|
||||
/datum/species/shadekin/add_inherent_verbs(var/mob/living/carbon/human/H)
|
||||
..()
|
||||
add_shadekin_abilities(H)
|
||||
|
||||
/datum/species/shadekin/proc/add_shadekin_abilities(var/mob/living/carbon/human/H)
|
||||
if(!H.ability_master || !istype(H.ability_master, /obj/screen/movable/ability_master/shadekin))
|
||||
H.ability_master = null
|
||||
H.ability_master = new /obj/screen/movable/ability_master/shadekin(H)
|
||||
for(var/datum/power/shadekin/P in shadekin_ability_datums)
|
||||
if(!(P.verbpath in H.verbs))
|
||||
add_verb(H, P.verbpath)
|
||||
H.ability_master.add_shadekin_ability(
|
||||
object_given = H,
|
||||
verb_given = P.verbpath,
|
||||
name_given = P.name,
|
||||
ability_icon_given = P.ability_icon_state,
|
||||
arguments = list()
|
||||
)
|
||||
|
||||
/datum/species/shadekin/proc/handle_shade(var/mob/living/carbon/human/H)
|
||||
//Shifted kin don't gain/lose energy (and save time if we're at the cap)
|
||||
var/darkness = 1
|
||||
var/dark_gains = 0
|
||||
|
||||
var/turf/T = get_turf(H)
|
||||
if(!T)
|
||||
dark_gains = 0
|
||||
return
|
||||
|
||||
var/brightness = T.get_lumcount() //Brightness in 0.0 to 1.0
|
||||
darkness = 1-brightness //Invert
|
||||
var/is_dark = (darkness >= 0.5)
|
||||
|
||||
if(H.ability_flags & AB_PHASE_SHIFTED)
|
||||
dark_gains = 0
|
||||
else
|
||||
//Heal (very) slowly in good darkness
|
||||
if(is_dark)
|
||||
H.adjustFireLoss((-0.10)*darkness)
|
||||
H.adjustBruteLoss((-0.10)*darkness)
|
||||
H.adjustToxLoss((-0.10)*darkness)
|
||||
//energy_dark and energy_light are set by the shadekin eye traits.
|
||||
//These are balanced around their playstyles and 2 planned new aggressive abilities
|
||||
dark_gains = energy_dark
|
||||
else
|
||||
dark_gains = energy_light
|
||||
|
||||
set_energy(H, get_energy(H) + dark_gains)
|
||||
|
||||
//Update huds
|
||||
update_shadekin_hud(H)
|
||||
|
||||
/datum/species/shadekin/proc/get_energy(var/mob/living/carbon/human/H)
|
||||
var/datum/component/shadekin/comp = H.GetComponent(/datum/component/shadekin)
|
||||
if(!comp)
|
||||
return FALSE //No component, no energy to be had.
|
||||
|
||||
if(comp.dark_energy_infinite)
|
||||
return comp.max_dark_energy
|
||||
|
||||
return comp.dark_energy
|
||||
|
||||
/datum/species/shadekin/proc/get_max_energy(var/mob/living/carbon/human/H)
|
||||
var/datum/component/shadekin/comp = H.GetComponent(/datum/component/shadekin)
|
||||
if(!comp)
|
||||
return FALSE //No component, no energy to be had.
|
||||
|
||||
return comp.max_dark_energy
|
||||
|
||||
/datum/species/shadekin/proc/set_energy(var/mob/living/carbon/human/H, var/new_energy)
|
||||
var/datum/component/shadekin/comp = H.GetComponent(/datum/component/shadekin)
|
||||
if(!comp)
|
||||
return FALSE //No component, no energy to be had.
|
||||
|
||||
comp.dark_energy = CLAMP(new_energy, 0, get_max_energy(H))
|
||||
|
||||
/datum/species/shadekin/proc/set_max_energy(var/mob/living/carbon/human/H, var/new_max_energy)
|
||||
var/datum/component/shadekin/comp = H.GetComponent(/datum/component/shadekin)
|
||||
if(!comp)
|
||||
return FALSE //No component, no energy to be had.
|
||||
|
||||
comp.max_dark_energy = new_max_energy
|
||||
|
||||
/datum/species/shadekin/proc/update_shadekin_hud(var/mob/living/carbon/human/H)
|
||||
var/turf/T = get_turf(H)
|
||||
if(H.shadekin_display)
|
||||
var/l_icon = 0
|
||||
var/e_icon = 0
|
||||
|
||||
H.shadekin_display.invisibility = INVISIBILITY_NONE
|
||||
if(T)
|
||||
var/brightness = T.get_lumcount() //Brightness in 0.0 to 1.0
|
||||
var/darkness = 1-brightness //Invert
|
||||
switch(darkness)
|
||||
if(0.80 to 1.00)
|
||||
l_icon = 0
|
||||
if(0.60 to 0.80)
|
||||
l_icon = 1
|
||||
if(0.40 to 0.60)
|
||||
l_icon = 2
|
||||
if(0.20 to 0.40)
|
||||
l_icon = 3
|
||||
if(0.00 to 0.20)
|
||||
l_icon = 4
|
||||
|
||||
switch(get_energy(H))
|
||||
if(0 to 24)
|
||||
e_icon = 0
|
||||
if(25 to 49)
|
||||
e_icon = 1
|
||||
if(50 to 74)
|
||||
e_icon = 2
|
||||
if(75 to 99)
|
||||
e_icon = 3
|
||||
if(100 to INFINITY)
|
||||
e_icon = 4
|
||||
|
||||
H.shadekin_display.icon_state = "shadekin-[l_icon]-[e_icon]"
|
||||
return
|
||||
|
||||
/datum/species/shadekin/proc/get_shadekin_eyecolor(var/mob/living/carbon/human/H)
|
||||
var/eyecolor_rgb = rgb(H.r_eyes, H.g_eyes, H.b_eyes)
|
||||
|
||||
var/eyecolor_hue = rgb2num(eyecolor_rgb, COLORSPACE_HSV)[1]
|
||||
var/eyecolor_sat = rgb2num(eyecolor_rgb, COLORSPACE_HSV)[2]
|
||||
var/eyecolor_val = rgb2num(eyecolor_rgb, COLORSPACE_HSV)[3]
|
||||
|
||||
//First, clamp the saturation/value to prevent black/grey/white eyes
|
||||
if(eyecolor_sat < 10)
|
||||
eyecolor_sat = 10
|
||||
if(eyecolor_val < 40)
|
||||
eyecolor_val = 40
|
||||
|
||||
eyecolor_rgb = rgb(eyecolor_hue, eyecolor_sat, eyecolor_val, space=COLORSPACE_HSV)
|
||||
|
||||
H.r_eyes = rgb2num(eyecolor_rgb)[1]
|
||||
H.g_eyes = rgb2num(eyecolor_rgb)[2]
|
||||
H.b_eyes = rgb2num(eyecolor_rgb)[3]
|
||||
|
||||
//Now determine what color we fall into.
|
||||
var/eyecolor_type = BLUE_EYES
|
||||
switch(eyecolor_hue)
|
||||
if(0 to 20)
|
||||
eyecolor_type = RED_EYES
|
||||
if(21 to 50)
|
||||
eyecolor_type = ORANGE_EYES
|
||||
if(51 to 70)
|
||||
eyecolor_type = YELLOW_EYES
|
||||
if(71 to 160)
|
||||
eyecolor_type = GREEN_EYES
|
||||
if(161 to 260)
|
||||
eyecolor_type = BLUE_EYES
|
||||
if(261 to 340)
|
||||
eyecolor_type = PURPLE_EYES
|
||||
if(341 to 360)
|
||||
eyecolor_type = RED_EYES
|
||||
|
||||
return eyecolor_type
|
||||
|
||||
/datum/species/shadekin/post_spawn_special(var/mob/living/carbon/human/H)
|
||||
.=..()
|
||||
|
||||
var/eyecolor_type = get_shadekin_eyecolor(H)
|
||||
var/datum/component/shadekin/SK = H.get_shadekin_component()
|
||||
if(!SK)
|
||||
CRASH("A shadekin [H] somehow is missing their shadekin component post-spawn!")
|
||||
|
||||
switch(eyecolor_type)
|
||||
switch(SK.eye_color)
|
||||
if(BLUE_EYES)
|
||||
total_health = 100
|
||||
energy_light = 0.5
|
||||
energy_dark = 0.5
|
||||
if(RED_EYES)
|
||||
total_health = 200
|
||||
energy_light = -1
|
||||
energy_dark = 0.1
|
||||
if(PURPLE_EYES)
|
||||
total_health = 150
|
||||
energy_light = -0.5
|
||||
energy_dark = 1
|
||||
if(YELLOW_EYES)
|
||||
total_health = 100
|
||||
energy_light = -2
|
||||
energy_dark = 3
|
||||
if(GREEN_EYES)
|
||||
total_health = 100
|
||||
energy_light = 0.125
|
||||
energy_dark = 2
|
||||
if(ORANGE_EYES)
|
||||
total_health = 175
|
||||
energy_light = -0.5
|
||||
energy_dark = 0.25
|
||||
|
||||
H.maxHealth = total_health
|
||||
|
||||
H.health = H.getMaxHealth()
|
||||
|
||||
/datum/species/shadekin/produceCopy(var/list/traits, var/mob/living/carbon/human/H, var/custom_base, var/reset_dna = TRUE) // Traitgenes reset_dna flag required, or genes get reset on resleeve
|
||||
|
||||
var/datum/species/shadekin/new_copy = ..()
|
||||
|
||||
new_copy.total_health = total_health
|
||||
|
||||
new_copy.energy_light = energy_light
|
||||
|
||||
new_copy.energy_dark = energy_dark
|
||||
|
||||
return new_copy
|
||||
|
||||
@@ -1,364 +1,22 @@
|
||||
/datum/power/shadekin
|
||||
|
||||
/mob/living/carbon/human/is_incorporeal()
|
||||
if(ability_flags & AB_PHASE_SHIFTED) //Shadekin
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(SK && SK.in_phase) //Shadekin
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/////////////////////
|
||||
/// PHASE SHIFT ///
|
||||
/////////////////////
|
||||
//Visual effect for phase in/out
|
||||
/obj/effect/temp_visual/shadekin
|
||||
randomdir = FALSE
|
||||
duration = 5
|
||||
icon = 'icons/mob/vore_shadekin.dmi'
|
||||
|
||||
/obj/effect/temp_visual/shadekin/phase_in
|
||||
icon_state = "tp_in"
|
||||
|
||||
/obj/effect/temp_visual/shadekin/phase_out
|
||||
icon_state = "tp_out"
|
||||
|
||||
/datum/power/shadekin/phase_shift
|
||||
name = "Phase Shift (100)"
|
||||
desc = "Shift yourself out of alignment with realspace to travel quickly to different areas."
|
||||
verbpath = /mob/living/carbon/human/proc/phase_shift
|
||||
ability_icon_state = "tech_passwall"
|
||||
|
||||
/mob/living/carbon/human/proc/phase_shift()
|
||||
set name = "Phase Shift (100)"
|
||||
set desc = "Shift yourself out of alignment with realspace to travel quickly to different areas."
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
//RS Port #658 Start
|
||||
var/area/A = get_area(src)
|
||||
if(!client?.holder && A.flag_check(AREA_BLOCK_PHASE_SHIFT))
|
||||
to_chat(src, span_warning("You can't do that here!"))
|
||||
return
|
||||
//RS Port #658 End
|
||||
|
||||
var/ability_cost = 100
|
||||
|
||||
var/darkness = 1
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
to_chat(src,span_warning("You can't use that here!"))
|
||||
return FALSE
|
||||
|
||||
if(ability_flags & AB_PHASE_SHIFTING)
|
||||
return FALSE
|
||||
|
||||
var/brightness = T.get_lumcount() //Brightness in 0.0 to 1.0
|
||||
darkness = 1-brightness //Invert
|
||||
|
||||
var/watcher = 0
|
||||
for(var/mob/living/carbon/human/watchers in oview(7,src )) // If we can see them...
|
||||
if(watchers in oviewers(7,src)) // And they can see us...
|
||||
if(!(watchers.stat) && !isbelly(watchers.loc) && !istype(watchers.loc, /obj/item/holder)) // And they are alive and not being held by someone...
|
||||
watcher++ // They are watching us!
|
||||
|
||||
ability_cost = CLAMP(ability_cost/(0.01+darkness*2),50, 80)//This allows for 1 watcher in full light
|
||||
if(watcher>0)
|
||||
ability_cost = ability_cost + ( 15 * watcher )
|
||||
if(!(ability_flags & AB_PHASE_SHIFTED))
|
||||
log_debug("[src] attempted to shift with [watcher] visible Carbons with a cost of [ability_cost] in a darkness level of [darkness]")
|
||||
|
||||
var/datum/species/shadekin/SK = species
|
||||
if(!istype(SK))
|
||||
to_chat(src, span_warning("Only a shadekin can use that!"))
|
||||
return FALSE
|
||||
else if(stat)
|
||||
to_chat(src, span_warning("Can't use that ability in your state!"))
|
||||
return FALSE
|
||||
else if(SK.doing_phase)
|
||||
to_chat(src, span_warning("You are already trying to phase!"))
|
||||
return FALSE
|
||||
else if(shadekin_get_energy() < ability_cost && !(ability_flags & AB_PHASE_SHIFTED))
|
||||
to_chat(src, span_warning("Not enough energy for that ability!"))
|
||||
return FALSE
|
||||
|
||||
if(!(ability_flags & AB_PHASE_SHIFTED))
|
||||
shadekin_adjust_energy(-ability_cost)
|
||||
playsound(src, 'sound/effects/stealthoff.ogg', 75, 1)
|
||||
|
||||
if(!T.CanPass(src,T) || loc != T)
|
||||
to_chat(src,span_warning("You can't use that here!"))
|
||||
return FALSE
|
||||
|
||||
SK.doing_phase = TRUE // Prevent bugs when spamming phase button
|
||||
//Shifting in
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
phase_in(T)
|
||||
//Shifting out
|
||||
else
|
||||
phase_out(T)
|
||||
SK.doing_phase = FALSE // Prevent bugs when spamming phase button
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/phase_in(var/turf/T)
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
|
||||
// pre-change
|
||||
forceMove(T)
|
||||
var/original_canmove = canmove
|
||||
SetStunned(0)
|
||||
SetWeakened(0)
|
||||
if(buckled)
|
||||
buckled.unbuckle_mob()
|
||||
if(pulledby)
|
||||
pulledby.stop_pulling()
|
||||
stop_pulling()
|
||||
|
||||
// change
|
||||
canmove = FALSE
|
||||
ability_flags &= ~AB_PHASE_SHIFTED
|
||||
ability_flags |= AB_PHASE_SHIFTING
|
||||
throwpass = FALSE
|
||||
name = get_visible_name()
|
||||
for(var/obj/belly/B as anything in vore_organs)
|
||||
B.escapable = initial(B.escapable)
|
||||
|
||||
//cut_overlays()
|
||||
invisibility = initial(invisibility)
|
||||
see_invisible = initial(see_invisible)
|
||||
incorporeal_move = initial(incorporeal_move)
|
||||
density = initial(density)
|
||||
force_max_speed = initial(force_max_speed)
|
||||
update_icon()
|
||||
|
||||
//Cosmetics mostly
|
||||
var/obj/effect/temp_visual/shadekin/phase_in/phaseanim = new /obj/effect/temp_visual/shadekin/phase_in(src.loc)
|
||||
phaseanim.pixel_y = (src.size_multiplier - 1) * 16 // Pixel shift for the animation placement
|
||||
phaseanim.adjust_scale(src.size_multiplier, src.size_multiplier)
|
||||
phaseanim.dir = dir
|
||||
alpha = 0
|
||||
automatic_custom_emote(VISIBLE_MESSAGE,"phases in!")
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(shadekin_complete_phase_in), original_canmove), 5, TIMER_DELETE_ME)
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/shadekin_complete_phase_in(var/original_canmove)
|
||||
canmove = original_canmove
|
||||
alpha = initial(alpha)
|
||||
remove_modifiers_of_type(/datum/modifier/shadekin_phase_vision)
|
||||
|
||||
//Potential phase-in vore
|
||||
if(can_be_drop_pred) //Toggleable in vore panel
|
||||
var/list/potentials = living_mobs(0)
|
||||
if(potentials.len)
|
||||
var/mob/living/target = pick(potentials)
|
||||
if(istype(target) && target.devourable && target.can_be_drop_prey && target.phase_vore && vore_selected && phase_vore)
|
||||
target.forceMove(vore_selected)
|
||||
to_chat(target,span_vwarning("\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!"))
|
||||
|
||||
ability_flags &= ~AB_PHASE_SHIFTING
|
||||
|
||||
//Affect nearby lights
|
||||
var/destroy_lights = 0
|
||||
|
||||
for(var/obj/machinery/light/L in GLOB.machines)
|
||||
if(L.z != z || get_dist(src,L) > 10)
|
||||
continue
|
||||
|
||||
if(prob(destroy_lights))
|
||||
addtimer(CALLBACK(L, TYPE_PROC_REF(/obj/machinery/light, broken)), rand(5,25), TIMER_DELETE_ME)
|
||||
else
|
||||
L.flicker(10)
|
||||
|
||||
/mob/living/carbon/human/proc/phase_out(var/turf/T)
|
||||
if(!(ability_flags & AB_PHASE_SHIFTED))
|
||||
// pre-change
|
||||
forceMove(T)
|
||||
var/original_canmove = canmove
|
||||
SetStunned(0)
|
||||
SetWeakened(0)
|
||||
if(buckled)
|
||||
buckled.unbuckle_mob()
|
||||
if(pulledby)
|
||||
pulledby.stop_pulling()
|
||||
stop_pulling()
|
||||
canmove = FALSE
|
||||
|
||||
// change
|
||||
ability_flags |= AB_PHASE_SHIFTED
|
||||
ability_flags |= AB_PHASE_SHIFTING
|
||||
throwpass = TRUE
|
||||
automatic_custom_emote(VISIBLE_MESSAGE,"phases out!")
|
||||
name = get_visible_name()
|
||||
|
||||
for(var/obj/belly/B as anything in vore_organs)
|
||||
B.escapable = FALSE
|
||||
|
||||
var/obj/effect/temp_visual/shadekin/phase_out/phaseanim = new /obj/effect/temp_visual/shadekin/phase_out(src.loc)
|
||||
phaseanim.pixel_y = (src.size_multiplier - 1) * 16 // Pixel shift for the animation placement
|
||||
phaseanim.adjust_scale(src.size_multiplier, src.size_multiplier)
|
||||
phaseanim.dir = dir
|
||||
alpha = 0
|
||||
add_modifier(/datum/modifier/shadekin_phase_vision)
|
||||
sleep(5)
|
||||
invisibility = INVISIBILITY_SHADEKIN
|
||||
see_invisible = INVISIBILITY_SHADEKIN
|
||||
see_invisible_default = INVISIBILITY_SHADEKIN // Allow seeing phased entities while phased.
|
||||
//cut_overlays()
|
||||
update_icon()
|
||||
alpha = 127
|
||||
|
||||
canmove = original_canmove
|
||||
incorporeal_move = TRUE
|
||||
density = FALSE
|
||||
force_max_speed = TRUE
|
||||
ability_flags &= ~AB_PHASE_SHIFTING
|
||||
|
||||
/datum/modifier/shadekin_phase_vision
|
||||
name = "Shadekin Phase Vision"
|
||||
vision_flags = SEE_THRU
|
||||
|
||||
//////////////////////////
|
||||
/// REGENERATE OTHER ///
|
||||
//////////////////////////
|
||||
/datum/power/shadekin/regenerate_other
|
||||
name = "Regenerate Other (50)"
|
||||
desc = "Spend energy to heal physical wounds in another creature."
|
||||
verbpath = /mob/living/carbon/human/proc/regenerate_other
|
||||
ability_icon_state = "tech_biomedaura"
|
||||
|
||||
/mob/living/carbon/human/proc/regenerate_other()
|
||||
set name = "Regenerate Other (50)"
|
||||
set desc = "Spend energy to heal physical wounds in another creature."
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
var/ability_cost = 50
|
||||
|
||||
var/datum/species/shadekin/SK = species
|
||||
if(!istype(SK))
|
||||
to_chat(src, span_warning("Only a shadekin can use that!"))
|
||||
return FALSE
|
||||
else if(stat)
|
||||
to_chat(src, span_warning("Can't use that ability in your state!"))
|
||||
return FALSE
|
||||
else if(shadekin_get_energy() < ability_cost)
|
||||
to_chat(src, span_warning("Not enough energy for that ability!"))
|
||||
return FALSE
|
||||
else if(ability_flags & AB_PHASE_SHIFTED)
|
||||
to_chat(src, span_warning("You can't use that while phase shifted!"))
|
||||
return FALSE
|
||||
|
||||
var/list/viewed = oview(1)
|
||||
var/list/targets = list()
|
||||
for(var/mob/living/L in viewed)
|
||||
targets += L
|
||||
if(!targets.len)
|
||||
to_chat(src,span_warning("Nobody nearby to mend!"))
|
||||
return FALSE
|
||||
|
||||
var/mob/living/target = tgui_input_list(src,"Pick someone to mend:","Mend Other", targets)
|
||||
if(!target)
|
||||
return FALSE
|
||||
|
||||
target.add_modifier(/datum/modifier/shadekin/heal_boop,1 MINUTE)
|
||||
playsound(src, 'sound/effects/EMPulse.ogg', 75, 1)
|
||||
shadekin_adjust_energy(-ability_cost)
|
||||
visible_message(span_notice("\The [src] gently places a hand on \the [target]..."))
|
||||
face_atom(target)
|
||||
return TRUE
|
||||
|
||||
/datum/modifier/shadekin/heal_boop
|
||||
name = "Shadekin Regen"
|
||||
desc = "You feel serene and well rested."
|
||||
mob_overlay_state = "green_sparkles"
|
||||
|
||||
on_created_text = span_notice("Sparkles begin to appear around you, and all your ills seem to fade away.")
|
||||
on_expired_text = span_notice("The sparkles have faded, although you feel much healthier than before.")
|
||||
stacks = MODIFIER_STACK_EXTEND
|
||||
|
||||
/datum/modifier/shadekin/heal_boop/tick()
|
||||
if(!holder.getBruteLoss() && !holder.getFireLoss() && !holder.getToxLoss() && !holder.getOxyLoss() && !holder.getCloneLoss()) // No point existing if the spell can't heal.
|
||||
expire()
|
||||
return
|
||||
holder.adjustBruteLoss(-2)
|
||||
holder.adjustFireLoss(-2)
|
||||
holder.adjustToxLoss(-2)
|
||||
holder.adjustOxyLoss(-2)
|
||||
holder.adjustCloneLoss(-2)
|
||||
|
||||
|
||||
//////////////////////
|
||||
/// CREATE SHADE ///
|
||||
//////////////////////
|
||||
/datum/power/shadekin/create_shade
|
||||
name = "Create Shade (25)"
|
||||
desc = "Create a field of darkness that follows you."
|
||||
verbpath = /mob/living/carbon/human/proc/create_shade
|
||||
ability_icon_state = "tech_dispelold"
|
||||
|
||||
/mob/living/carbon/human/proc/create_shade()
|
||||
set name = "Create Shade (25)"
|
||||
set desc = "Create a field of darkness that follows you."
|
||||
set category = "Abilities.Shadekin"
|
||||
|
||||
var/ability_cost = 25
|
||||
|
||||
var/datum/species/shadekin/SK = species
|
||||
if(!istype(SK))
|
||||
to_chat(src, span_warning("Only a shadekin can use that!"))
|
||||
return FALSE
|
||||
else if(stat)
|
||||
to_chat(src, span_warning("Can't use that ability in your state!"))
|
||||
return FALSE
|
||||
else if(shadekin_get_energy() < ability_cost)
|
||||
to_chat(src, span_warning("Not enough energy for that ability!"))
|
||||
return FALSE
|
||||
else if(ability_flags & AB_PHASE_SHIFTED)
|
||||
to_chat(src, span_warning("You can't use that while phase shifted!"))
|
||||
return FALSE
|
||||
|
||||
playsound(src, 'sound/effects/bamf.ogg', 75, 1)
|
||||
|
||||
add_modifier(/datum/modifier/shadekin/create_shade,20 SECONDS)
|
||||
shadekin_adjust_energy(-ability_cost)
|
||||
return TRUE
|
||||
|
||||
/datum/modifier/shadekin/create_shade
|
||||
name = "Shadekin Shadegen"
|
||||
desc = "Darkness envelops you."
|
||||
mob_overlay_state = ""
|
||||
|
||||
on_created_text = span_notice("You drag part of The Dark into realspace, enveloping yourself.")
|
||||
on_expired_text = span_warning("You lose your grasp on The Dark and realspace reasserts itself.")
|
||||
stacks = MODIFIER_STACK_EXTEND
|
||||
var/mob/living/simple_mob/shadekin/my_kin
|
||||
|
||||
/datum/modifier/shadekin/create_shade/tick()
|
||||
if(my_kin.ability_flags & AB_PHASE_SHIFTED)
|
||||
expire()
|
||||
|
||||
/datum/modifier/shadekin/create_shade/on_applied()
|
||||
my_kin = holder
|
||||
holder.glow_toggle = TRUE
|
||||
holder.glow_range = 8
|
||||
holder.glow_intensity = -10
|
||||
holder.glow_color = "#FFFFFF"
|
||||
holder.set_light(8, -10, "#FFFFFF")
|
||||
|
||||
/datum/modifier/shadekin/create_shade/on_expire()
|
||||
holder.glow_toggle = initial(holder.glow_toggle)
|
||||
holder.glow_range = initial(holder.glow_range)
|
||||
holder.glow_intensity = initial(holder.glow_intensity)
|
||||
holder.glow_color = initial(holder.glow_color)
|
||||
holder.set_light(0)
|
||||
my_kin = null
|
||||
|
||||
// force dephase proc, to be called by other procs to dephase the shadekin. T is the target to force dephase them to.
|
||||
/mob/living/carbon/human/proc/attack_dephase(var/turf/T = null, atom/dephaser)
|
||||
var/datum/species/shadekin/SK = species
|
||||
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(!SK)
|
||||
return FALSE
|
||||
// no assigned dephase-target, just use our own
|
||||
if(!T)
|
||||
T = get_turf(src)
|
||||
|
||||
// make sure it's possible to be dephased (and we're in phase)
|
||||
if(!istype(SK) || SK.doing_phase || !T.CanPass(src,T) || loc != T || !(ability_flags & AB_PHASE_SHIFTED) )
|
||||
if(SK.doing_phase || !T.CanPass(src,T) || loc != T || !(SK.in_phase) )
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -366,6 +24,6 @@
|
||||
message_admins("[key_name_admin(src)] was stunned out of phase at [T.x],[T.y],[T.z] by [dephaser.name], last touched by [dephaser.forensic_data?.get_lastprint()]. (<A href='byond://?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[T.x];Y=[T.y];Z=[T.z]'>JMP</a>)", 1)
|
||||
// start the dephase
|
||||
phase_in(T)
|
||||
shadekin_adjust_energy(-20) // loss of energy for the interception
|
||||
SK.shadekin_adjust_energy(-20) // loss of energy for the interception
|
||||
// apply a little extra stun for good measure
|
||||
src.Weaken(3)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
/obj/screen/ability/verb_based/shadekin
|
||||
icon_state = "grey_spell_base"
|
||||
background_base_state = "grey"
|
||||
icon = 'icons/mob/shadekin_abilities.dmi'
|
||||
|
||||
/obj/screen/movable/ability_master/proc/add_shadekin_ability(var/object_given, var/verb_given, var/name_given, var/ability_icon_given, var/arguments)
|
||||
if(!object_given)
|
||||
|
||||
@@ -594,12 +594,11 @@
|
||||
SEND_SIGNAL(H, COMSIG_XENOCHIMERA_COMPONENT)
|
||||
|
||||
//Shadekin Species Component.
|
||||
/* //For when shadekin actually have their component control everything.
|
||||
var/datum/component/shadekin/sk = H.get_xenochimera_component()
|
||||
//For when shadekin actually have their component control everything.
|
||||
var/datum/component/shadekin/sk = H.get_shadekin_component()
|
||||
if(sk)
|
||||
if(!H.stat || !(xc.revive_ready == REVIVING_NOW || xc.revive_ready == REVIVING_DONE))
|
||||
if(!H.stat)
|
||||
SEND_SIGNAL(H, COMSIG_SHADEKIN_COMPONENT)
|
||||
*/
|
||||
|
||||
// Used to update alien icons for aliens.
|
||||
/datum/species/proc/handle_login_special(var/mob/living/carbon/human/H)
|
||||
|
||||
@@ -153,14 +153,18 @@
|
||||
/datum/unarmed_attack/claws/shadekin/apply_effects(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage)
|
||||
..()
|
||||
if(!(target == user))
|
||||
user.shadekin_adjust_energy(attack_damage)
|
||||
var/datum/component/shadekin/SK = user.get_shadekin_component()
|
||||
if(SK)
|
||||
SK.shadekin_adjust_energy(attack_damage)
|
||||
|
||||
/datum/unarmed_attack/bite/sharp/shadekin
|
||||
|
||||
/datum/unarmed_attack/bite/sharp/shadekin/apply_effects(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage)
|
||||
..()
|
||||
if(!(target == user))
|
||||
user.shadekin_adjust_energy(attack_damage)
|
||||
var/datum/component/shadekin/SK = user.get_shadekin_component()
|
||||
if(SK)
|
||||
SK.shadekin_adjust_energy(attack_damage)
|
||||
|
||||
/datum/unarmed_attack/claws/chimera //special feral attack that gets stronger as they get angrier
|
||||
|
||||
|
||||
@@ -18,6 +18,15 @@
|
||||
|
||||
selected_image = image(icon = GLOB.buildmode_hud, loc = src, icon_state = "ai_sel")
|
||||
|
||||
/mob/living/proc/get_visible_name()
|
||||
var/datum/component/shadekin/SK = get_shadekin_component()
|
||||
if(SK && SK.in_phase)
|
||||
return "Something"
|
||||
if(real_name)
|
||||
return real_name
|
||||
else
|
||||
return name
|
||||
|
||||
/mob/living/Destroy()
|
||||
SSradiation.listeners -= src
|
||||
remove_all_modifiers(TRUE)
|
||||
@@ -1257,7 +1266,7 @@
|
||||
swap_hand()
|
||||
|
||||
/mob/living/throw_item(atom/target)
|
||||
if(incapacitated() || !target || istype(target, /obj/screen))
|
||||
if(incapacitated() || !target || istype(target, /obj/screen) || is_incorporeal())
|
||||
return FALSE
|
||||
|
||||
var/atom/movable/item = src.get_active_hand()
|
||||
|
||||
@@ -345,9 +345,10 @@
|
||||
|
||||
if(get_dist(get_turf(T), get_turf(src)) > leap_distance) return
|
||||
|
||||
if(ishuman(T))
|
||||
var/mob/living/carbon/human/H = T
|
||||
if(H.get_species() == SPECIES_SHADEKIN && (H.ability_flags & AB_PHASE_SHIFTED))
|
||||
if(isliving(T))
|
||||
var/mob/living/M = T
|
||||
var/datum/component/shadekin/SK = M.get_shadekin_component()
|
||||
if(SK && SK.in_phase)
|
||||
power_cost *= 2
|
||||
|
||||
if(!use_direct_power(power_cost, minimum_power - power_cost))
|
||||
|
||||
@@ -419,18 +419,18 @@
|
||||
for(var/datum/matter_synth/our_synth in module.synths)
|
||||
switch(our_synth.name)
|
||||
if(METAL_SYNTH)
|
||||
if(has_glass)
|
||||
possible_synths[/obj/item/stack/material/cyborg/glass/reinforced] = list(our_synth, has_glass)
|
||||
possible_synths += list(/obj/item/stack/material/cyborg/steel = list(our_synth))
|
||||
possible_synths += list(/obj/item/stack/tile/floor/cyborg = list(our_synth))
|
||||
possible_synths += list(/obj/item/stack/rods/cyborg = list(our_synth))
|
||||
possible_synths += list(/obj/item/stack/tile/roofing/cyborg = list(our_synth))
|
||||
if(has_glass)
|
||||
possible_synths |= list(/obj/item/stack/material/cyborg/glass/reinforced = list(our_synth, has_glass))
|
||||
if(PLASTEEL_SYNTH)
|
||||
possible_synths += list(/obj/item/stack/material/cyborg/plasteel = list(our_synth))
|
||||
if(GLASS_SYNTH)
|
||||
possible_synths += list(/obj/item/stack/material/cyborg/glass = list(our_synth))
|
||||
if(has_steel)
|
||||
possible_synths |= list(/obj/item/stack/material/cyborg/glass/reinforced = list(our_synth, has_steel))
|
||||
possible_synths[/obj/item/stack/material/cyborg/glass/reinforced] = list(our_synth, has_steel)
|
||||
possible_synths += list(/obj/item/stack/material/cyborg/glass = list(our_synth))
|
||||
if(WOOD_SYNTH)
|
||||
possible_synths += list(/obj/item/stack/tile/wood/cyborg = list(our_synth))
|
||||
possible_synths += list(/obj/item/stack/material/cyborg/wood = list(our_synth))
|
||||
|
||||
@@ -105,6 +105,8 @@
|
||||
|
||||
if(in_stasis)
|
||||
return 1 // return early to skip atmos checks
|
||||
if(is_incorporeal())
|
||||
return 1
|
||||
|
||||
var/atom/A = src.loc
|
||||
|
||||
|
||||
@@ -203,6 +203,11 @@
|
||||
zone_sel.update_icon()
|
||||
hud_elements |= zone_sel
|
||||
|
||||
//Component hud elements. Made in /mob/living/create_mob_hud
|
||||
hud_elements |= shadekin_display
|
||||
hud_elements |= xenochimera_danger_display
|
||||
hud_elements |= lleill_display
|
||||
|
||||
//Hand things
|
||||
if(has_hands)
|
||||
//Drop button
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
density = FALSE
|
||||
force_max_speed = TRUE
|
||||
|
||||
/mob/living/simple_mob/vore/demon/verb/phase_shift()
|
||||
/mob/living/simple_mob/vore/demon/verb/demonic_phase_shift()
|
||||
set name = "Phase Shift"
|
||||
set desc = "Shift out of reality temporarily"
|
||||
set category = "Abilities.Demon"
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
|
||||
/mob/living/simple_mob/clowns/big/c_shift/Initialize(mapload)
|
||||
. = ..()
|
||||
add_verb(src, /mob/living/simple_mob/clowns/big/c_shift/proc/phase_shift)
|
||||
comp = LoadComponent(comp)
|
||||
|
||||
@@ -1,92 +1,20 @@
|
||||
/mob/living/simple_mob/clowns/big/c_shift
|
||||
var/ability_flags = 0 //Flags for active abilities
|
||||
|
||||
// Phase shifting procs (and related procs)
|
||||
/mob/living/simple_mob/clowns/big/c_shift/proc/phase_shift()
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T.CanPass(src,T) || loc != T)
|
||||
to_chat(src,span_warning("You can't use that here!"))
|
||||
return FALSE
|
||||
|
||||
forceMove(T)
|
||||
var/original_canmove = canmove
|
||||
SetStunned(0)
|
||||
SetWeakened(0)
|
||||
if(buckled)
|
||||
buckled.unbuckle_mob()
|
||||
if(pulledby)
|
||||
pulledby.stop_pulling()
|
||||
stop_pulling()
|
||||
canmove = FALSE
|
||||
|
||||
//Shifting in
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
ability_flags &= ~AB_PHASE_SHIFTED
|
||||
mouse_opacity = 2
|
||||
name = real_name
|
||||
|
||||
|
||||
cut_overlays()
|
||||
alpha = initial(alpha)
|
||||
invisibility = initial(invisibility)
|
||||
see_invisible = initial(see_invisible)
|
||||
incorporeal_move = initial(incorporeal_move)
|
||||
density = initial(density)
|
||||
force_max_speed = initial(force_max_speed)
|
||||
|
||||
//Cosmetics mostly
|
||||
flick("tp_in",src)
|
||||
automatic_custom_emote(VISIBLE_MESSAGE,"phases in!")
|
||||
sleep(5) //The duration of the TP animation
|
||||
canmove = original_canmove
|
||||
|
||||
// Do this after the potential vore, so we get the belly
|
||||
update_icon()
|
||||
|
||||
//Affect nearby lights
|
||||
|
||||
|
||||
for(var/obj/machinery/light/L in GLOB.machines)
|
||||
if(L.z != z || get_dist(src,L) > 10)
|
||||
continue
|
||||
|
||||
L.flicker(10)
|
||||
|
||||
//Shifting out
|
||||
else
|
||||
ability_flags |= AB_PHASE_SHIFTED
|
||||
mouse_opacity = 0
|
||||
automatic_custom_emote(VISIBLE_MESSAGE,"phases out!")
|
||||
real_name = name
|
||||
name = "Something"
|
||||
|
||||
cut_overlays()
|
||||
flick("tp_out",src)
|
||||
sleep(5)
|
||||
invisibility = INVISIBILITY_LEVEL_TWO
|
||||
see_invisible = INVISIBILITY_LEVEL_TWO
|
||||
update_icon()
|
||||
alpha = 127
|
||||
|
||||
canmove = original_canmove
|
||||
incorporeal_move = TRUE
|
||||
density = FALSE
|
||||
force_max_speed = TRUE
|
||||
var/datum/component/shadekin/comp = /datum/component/shadekin/phase_only //Component that holds all the shadekin vars.
|
||||
|
||||
/mob/living/simple_mob/clowns/big/c_shift/UnarmedAttack()
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
if(comp.in_phase)
|
||||
return FALSE //Nope.
|
||||
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_mob/clowns/big/c_shift/can_fall()
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
if(comp.in_phase)
|
||||
return FALSE //Nope!
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_mob/clowns/big/c_shift/zMove(direction)
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
if(comp.in_phase)
|
||||
var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src)
|
||||
if(destination)
|
||||
forceMove(destination)
|
||||
|
||||
@@ -20,10 +20,9 @@
|
||||
|
||||
/obj/effect/shadekin_ability/proc/atom_button_text()
|
||||
var/shift_denial
|
||||
|
||||
if(shift_mode == NOT_WHILE_SHIFTED && (my_kin.ability_flags & AB_PHASE_SHIFTED))
|
||||
if(shift_mode == NOT_WHILE_SHIFTED && my_kin.comp.in_phase)
|
||||
shift_denial = "Physical Only"
|
||||
else if(shift_mode == ONLY_WHILE_SHIFTED && !(my_kin.ability_flags & AB_PHASE_SHIFTED))
|
||||
else if(shift_mode == ONLY_WHILE_SHIFTED && !(my_kin.comp.in_phase))
|
||||
shift_denial = "Shifted Only"
|
||||
|
||||
if(shift_denial)
|
||||
@@ -47,10 +46,10 @@
|
||||
if(my_kin.stat)
|
||||
to_chat(my_kin,span_warning("Can't use that ability in your state!"))
|
||||
return FALSE
|
||||
if(shift_mode == NOT_WHILE_SHIFTED && (my_kin.ability_flags & AB_PHASE_SHIFTED))
|
||||
if(shift_mode == NOT_WHILE_SHIFTED && (my_kin.comp.in_phase))
|
||||
to_chat(my_kin,span_warning("Can't use that ability while phase shifted!"))
|
||||
return FALSE
|
||||
else if(shift_mode == ONLY_WHILE_SHIFTED && !(my_kin.ability_flags & AB_PHASE_SHIFTED))
|
||||
else if(shift_mode == ONLY_WHILE_SHIFTED && !(my_kin.comp.in_phase))
|
||||
to_chat(my_kin,span_warning("Can only use that ability while phase shifted!"))
|
||||
return FALSE
|
||||
else if(my_kin.comp.dark_energy < cost)
|
||||
@@ -75,7 +74,7 @@
|
||||
if(!..())
|
||||
return
|
||||
my_kin.phase_shift()
|
||||
if(my_kin.ability_flags & AB_PHASE_SHIFTED)
|
||||
if(my_kin.comp.in_phase)
|
||||
cost = 0 //Shifting back is free (but harmful in light)
|
||||
else
|
||||
cost = initial(cost)
|
||||
@@ -136,7 +135,7 @@
|
||||
var/mob/living/simple_mob/shadekin/my_kin
|
||||
|
||||
/datum/modifier/shadekin/create_shade/tick()
|
||||
if(my_kin.ability_flags & AB_PHASE_SHIFTED)
|
||||
if(my_kin.comp.in_phase)
|
||||
expire()
|
||||
|
||||
/datum/modifier/shadekin/create_shade/on_applied()
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
// Phase shifting procs (and related procs)
|
||||
/mob/living/simple_mob/shadekin/proc/phase_shift()
|
||||
/mob/living/simple_mob/shadekin/phase_shift()
|
||||
var/turf/T = get_turf(src)
|
||||
var/area/A = T.loc //RS Port #658
|
||||
var/area/A = T.loc
|
||||
if(!T.CanPass(src,T) || loc != T)
|
||||
to_chat(src,span_warning("You can't use that here!"))
|
||||
return FALSE
|
||||
//RS Port #658 Start
|
||||
if(!client?.holder && A.flag_check(AREA_BLOCK_PHASE_SHIFT))
|
||||
to_chat(src,span_warning("You can't use that here!"))
|
||||
return FALSE
|
||||
//RS Port #658 End
|
||||
|
||||
else if(doing_phase)
|
||||
to_chat(src, span_warning("You are already trying to phase!"))
|
||||
@@ -17,133 +15,27 @@
|
||||
|
||||
doing_phase = TRUE
|
||||
//Shifting in
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
if(comp.in_phase)
|
||||
phase_in(T)
|
||||
//Shifting out
|
||||
else
|
||||
phase_out(T)
|
||||
doing_phase = FALSE
|
||||
|
||||
/mob/living/simple_mob/shadekin/proc/phase_in(var/turf/T)
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
|
||||
// pre-change
|
||||
forceMove(T)
|
||||
var/original_canmove = canmove
|
||||
SetStunned(0)
|
||||
SetWeakened(0)
|
||||
if(buckled)
|
||||
buckled.unbuckle_mob()
|
||||
if(pulledby)
|
||||
pulledby.stop_pulling()
|
||||
stop_pulling()
|
||||
canmove = FALSE
|
||||
|
||||
// change
|
||||
ability_flags &= ~AB_PHASE_SHIFTED
|
||||
throwpass = FALSE
|
||||
name = real_name
|
||||
for(var/obj/belly/B as anything in vore_organs)
|
||||
B.escapable = initial(B.escapable)
|
||||
|
||||
cut_overlays()
|
||||
alpha = initial(alpha)
|
||||
invisibility = initial(invisibility)
|
||||
see_invisible = initial(see_invisible)
|
||||
incorporeal_move = initial(incorporeal_move)
|
||||
density = initial(density)
|
||||
force_max_speed = initial(force_max_speed)
|
||||
|
||||
//Cosmetics mostly
|
||||
flick("tp_in",src)
|
||||
automatic_custom_emote(VISIBLE_MESSAGE,"phases in!")
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(shadekin_complete_phase_in), original_canmove), 5, TIMER_DELETE_ME)
|
||||
|
||||
|
||||
/mob/living/simple_mob/shadekin/proc/shadekin_complete_phase_in(var/original_canmove)
|
||||
canmove = original_canmove
|
||||
|
||||
//Potential phase-in vore
|
||||
if(can_be_drop_pred) //Toggleable in vore panel
|
||||
var/list/potentials = living_mobs(0)
|
||||
if(potentials.len)
|
||||
var/mob/living/target = pick(potentials)
|
||||
if(istype(target) && target.devourable && target.can_be_drop_prey && vore_selected)
|
||||
target.forceMove(vore_selected)
|
||||
to_chat(target,span_vwarning("\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!"))
|
||||
|
||||
// Do this after the potential vore, so we get the belly
|
||||
update_icon()
|
||||
|
||||
//Affect nearby lights
|
||||
var/destroy_lights = 0
|
||||
if(eye_state == RED_EYES)
|
||||
destroy_lights = 80
|
||||
if(eye_state == PURPLE_EYES)
|
||||
destroy_lights = 25
|
||||
|
||||
for(var/obj/machinery/light/L in GLOB.machines)
|
||||
if(L.z != z || get_dist(src,L) > 10)
|
||||
continue
|
||||
|
||||
if(prob(destroy_lights))
|
||||
addtimer(CALLBACK(L, TYPE_PROC_REF(/obj/machinery/light, broken)), rand(5,25), TIMER_DELETE_ME)
|
||||
else
|
||||
L.flicker(10)
|
||||
|
||||
/mob/living/simple_mob/shadekin/proc/phase_out(var/turf/T)
|
||||
if(!(ability_flags & AB_PHASE_SHIFTED))
|
||||
|
||||
// pre-change
|
||||
forceMove(T)
|
||||
var/original_canmove = canmove
|
||||
SetStunned(0)
|
||||
SetWeakened(0)
|
||||
if(buckled)
|
||||
buckled.unbuckle_mob()
|
||||
if(pulledby)
|
||||
pulledby.stop_pulling()
|
||||
stop_pulling()
|
||||
canmove = FALSE
|
||||
|
||||
// change
|
||||
ability_flags |= AB_PHASE_SHIFTED
|
||||
throwpass = TRUE
|
||||
automatic_custom_emote(VISIBLE_MESSAGE,"phases out!")
|
||||
real_name = name
|
||||
name = "Something"
|
||||
|
||||
for(var/obj/belly/B as anything in vore_organs)
|
||||
B.escapable = FALSE
|
||||
|
||||
cut_overlays()
|
||||
flick("tp_out",src)
|
||||
sleep(5)
|
||||
invisibility = INVISIBILITY_LEVEL_TWO
|
||||
see_invisible = INVISIBILITY_LEVEL_TWO
|
||||
update_icon()
|
||||
alpha = 127
|
||||
|
||||
canmove = original_canmove
|
||||
incorporeal_move = TRUE
|
||||
density = FALSE
|
||||
force_max_speed = TRUE
|
||||
|
||||
/mob/living/simple_mob/shadekin/UnarmedAttack()
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
if(comp.in_phase)
|
||||
return FALSE //Nope.
|
||||
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_mob/shadekin/can_fall()
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
if(comp.in_phase)
|
||||
return FALSE //Nope!
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_mob/shadekin/zMove(direction)
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
if(comp.in_phase)
|
||||
var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src)
|
||||
if(destination)
|
||||
forceMove(destination)
|
||||
|
||||
@@ -71,8 +71,6 @@
|
||||
var/datum/component/shadekin/comp = /datum/component/shadekin //Component that holds all the shadekin vars.
|
||||
var/dark_gains = 0 //Last tick's change in energy
|
||||
var/ability_flags = 0 //Flags for active abilities
|
||||
var/obj/screen/darkhud //Holder to update this icon
|
||||
var/obj/screen/energyhud //Holder to update this icon
|
||||
|
||||
var/list/shadekin_abilities
|
||||
var/check_for_observer = FALSE
|
||||
@@ -97,6 +95,7 @@
|
||||
flags |= ATOM_INITIALIZED
|
||||
return INITIALIZE_HINT_QDEL
|
||||
comp = LoadComponent(comp)
|
||||
set_eye_energy()
|
||||
|
||||
if(icon_state == "map_example")
|
||||
icon_state = pick("white","dark","brown")
|
||||
@@ -201,7 +200,7 @@
|
||||
|
||||
/mob/living/simple_mob/shadekin/Life()
|
||||
. = ..()
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
if(comp.in_phase)
|
||||
density = FALSE
|
||||
|
||||
//Convert spare nutrition into energy at a certain ratio
|
||||
@@ -215,9 +214,9 @@
|
||||
if(!issimplekin(M))
|
||||
non_kin_count ++
|
||||
// Technically can be combined with ||, they call the same function, but readability is poor
|
||||
if(!non_kin_count && (ability_flags & AB_PHASE_SHIFTED))
|
||||
if(!non_kin_count && (comp.in_phase))
|
||||
phase_shift() // shifting back in, nobody present
|
||||
else if (non_kin_count && !(ability_flags & AB_PHASE_SHIFTED))
|
||||
else if (non_kin_count && !(comp.in_phase))
|
||||
phase_shift() // shifting out, scaredy
|
||||
|
||||
/mob/living/simple_mob/shadekin/update_icon()
|
||||
@@ -248,13 +247,89 @@
|
||||
|
||||
//They phase back to the dark when killed
|
||||
/mob/living/simple_mob/shadekin/death(gibbed, deathmessage = "phases to somewhere far away!")
|
||||
cut_overlays()
|
||||
icon_state = ""
|
||||
flick("tp_out",src)
|
||||
spawn(1 SECOND)
|
||||
qdel(src) //Back from whence you came!
|
||||
var/special_handling = FALSE //varswitch for downstream
|
||||
if(!special_handling)
|
||||
cut_overlays()
|
||||
icon_state = ""
|
||||
flick("tp_out",src)
|
||||
QDEL_IN(src, 1 SECOND)
|
||||
. = ..(FALSE, deathmessage)
|
||||
else
|
||||
if(comp.respite_activating)
|
||||
return
|
||||
cut_overlays()
|
||||
flick("tp_out",src)
|
||||
|
||||
var/area/current_area = get_area(src)
|
||||
if((comp.in_dark_respite) || current_area.flag_check(AREA_LIMIT_DARK_RESPITE))
|
||||
icon_state = ""
|
||||
spawn(1 SECOND)
|
||||
qdel(src) //Back from whence you came!
|
||||
|
||||
return ..(FALSE, deathmessage)
|
||||
|
||||
|
||||
if(!LAZYLEN(GLOB.latejoin_thedark))
|
||||
log_and_message_admins("[src] died outside of the dark but there were no valid floors to warp to")
|
||||
icon_state = ""
|
||||
spawn(1 SECOND)
|
||||
qdel(src) //Back from whence you came!
|
||||
|
||||
return ..(FALSE, deathmessage)
|
||||
|
||||
visible_message("<b>\The [src.name]</b> [deathmessage]")
|
||||
comp.respite_activating = TRUE
|
||||
|
||||
drop_l_hand()
|
||||
drop_r_hand()
|
||||
|
||||
comp.dark_energy = 0
|
||||
comp.in_dark_respite = TRUE
|
||||
invisibility = INVISIBILITY_LEVEL_TWO
|
||||
|
||||
adjustFireLoss(-(getFireLoss() / 2))
|
||||
adjustBruteLoss(-(getBruteLoss() / 2))
|
||||
adjustToxLoss(-(getToxLoss() / 2))
|
||||
Stun(10)
|
||||
movement_cooldown = 5
|
||||
nutrition = 0
|
||||
|
||||
if(istype(src.loc, /obj/belly))
|
||||
//Yay digestion... presumably...
|
||||
var/obj/belly/belly = src.loc
|
||||
add_attack_logs(belly.owner, src, "Digested in [lowertext(belly.name)]")
|
||||
to_chat(belly.owner, span_notice("\The [src.name] suddenly vanishes within your [belly.name]"))
|
||||
forceMove(pick(GLOB.latejoin_thedark))
|
||||
flick("tp_in",src)
|
||||
comp.respite_activating = FALSE
|
||||
comp.in_dark_respite = TRUE
|
||||
belly.owner.handle_belly_update()
|
||||
clear_fullscreen("belly")
|
||||
if(hud_used)
|
||||
if(!hud_used.hud_shown)
|
||||
toggle_hud_vis()
|
||||
stop_sound_channel(CHANNEL_PREYLOOP)
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(can_leave_dark)), 10 MINUTES, TIMER_DELETE_ME)
|
||||
else
|
||||
addtimer(CALLBACK(src, PROC_REF(enter_the_dark)), 1 SECOND, TIMER_DELETE_ME)
|
||||
addtimer(CALLBACK(src, PROC_REF(can_leave_dark)), 15 MINUTES, TIMER_DELETE_ME)
|
||||
|
||||
/mob/living/simple_mob/shadekin/proc/enter_the_dark()
|
||||
comp.respite_activating = FALSE
|
||||
comp.in_dark_respite = TRUE
|
||||
|
||||
forceMove(pick(GLOB.latejoin_thedark))
|
||||
update_icon()
|
||||
flick("tp_in",src)
|
||||
invisibility = initial(invisibility)
|
||||
comp.respite_activating = FALSE
|
||||
|
||||
/mob/living/simple_mob/shadekin/proc/can_leave_dark()
|
||||
comp.in_dark_respite = FALSE
|
||||
movement_cooldown = initial(movement_cooldown)
|
||||
to_chat(src, span_notice("You feel like you can leave the Dark again"))
|
||||
|
||||
. = ..(FALSE, deathmessage)
|
||||
|
||||
/* //VOREStation AI Temporary Removal
|
||||
//Blue-eyes want to nom people to heal them
|
||||
@@ -270,97 +345,34 @@
|
||||
//They reach nutritional equilibrium (important for blue-eyes healbelly)
|
||||
/mob/living/simple_mob/shadekin/Life()
|
||||
if((. = ..()))
|
||||
handle_shade()
|
||||
comp.handle_comp()
|
||||
|
||||
/mob/living/simple_mob/shadekin/proc/set_eye_energy()
|
||||
switch(eye_state)
|
||||
//Blue has constant, steady (slow) regen and ignores darkness.
|
||||
if(BLUE_EYES)
|
||||
comp.set_light_and_darkness(0.75,0.75)
|
||||
if(RED_EYES)
|
||||
comp.set_light_and_darkness(-0.5,0.5)
|
||||
if(PURPLE_EYES)
|
||||
comp.set_light_and_darkness(-0.5,1)
|
||||
if(YELLOW_EYES)
|
||||
comp.set_light_and_darkness(-2,3)
|
||||
if(GREEN_EYES)
|
||||
comp.set_light_and_darkness(0.125,2)
|
||||
if(ORANGE_EYES)
|
||||
comp.set_light_and_darkness(-0.25,0.75)
|
||||
|
||||
/mob/living/simple_mob/shadekin/is_incorporeal()
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
if(comp.in_phase)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_mob/shadekin/handle_atmos()
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
if(comp.in_phase)
|
||||
return
|
||||
else
|
||||
return .=..()
|
||||
|
||||
/mob/living/simple_mob/shadekin/proc/handle_shade()
|
||||
//Shifted kin don't gain/lose energy (and save time if we're at the cap)
|
||||
var/darkness = 1
|
||||
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
dark_gains = 0
|
||||
return
|
||||
|
||||
var/brightness = T.get_lumcount() //Brightness in 0.0 to 1.0
|
||||
darkness = 1-brightness //Invert
|
||||
|
||||
if(ability_flags & AB_PHASE_SHIFTED)
|
||||
dark_gains = 0
|
||||
else
|
||||
//Heal (very) slowly in good darkness
|
||||
if(darkness >= 0.75)
|
||||
adjustFireLoss(-0.05)
|
||||
adjustBruteLoss(-0.05)
|
||||
adjustToxLoss(-0.05)
|
||||
|
||||
switch(eye_state)
|
||||
//Blue has constant, steady (slow) regen and ignores darkness.
|
||||
if(BLUE_EYES)
|
||||
dark_gains = 0.5
|
||||
//Red has extremely tiny energy buildup in dark, none in light, and hunts for energy.
|
||||
if(RED_EYES)
|
||||
if(darkness >= 0.75)
|
||||
dark_gains = 0.25
|
||||
//Purple eyes have moderate gains in darkness and loss in light.
|
||||
if(PURPLE_EYES)
|
||||
dark_gains = round((darkness - 0.5) * 2, 0.1)
|
||||
//Yellow has extreme gains in darkness and loss in light.
|
||||
if(YELLOW_EYES)
|
||||
dark_gains = round((darkness - 0.5) * 4, 0.1)
|
||||
//Similar to blues, but passive is less, and affected by dark
|
||||
if(GREEN_EYES)
|
||||
dark_gains = 0.25
|
||||
dark_gains += round((darkness - 0.5), 0.1)
|
||||
//More able to get energy out of the dark, worse attack gains tho
|
||||
if(ORANGE_EYES)
|
||||
if(darkness >= 0.65)
|
||||
dark_gains = 0.30
|
||||
|
||||
comp.dark_energy = max(0,min(initial(comp.dark_energy),comp.dark_energy + dark_gains))
|
||||
|
||||
if(comp.dark_energy_infinite)
|
||||
comp.dark_energy = 100
|
||||
|
||||
//Update turf darkness hud
|
||||
if(darkhud)
|
||||
switch(darkness)
|
||||
if(0.80 to 1.00)
|
||||
darkhud.icon_state = "dark2"
|
||||
if(0.60 to 0.80)
|
||||
darkhud.icon_state = "dark1"
|
||||
if(0.40 to 0.60)
|
||||
darkhud.icon_state = "dark"
|
||||
if(0.20 to 0.40)
|
||||
darkhud.icon_state = "dark-1"
|
||||
if(0.00 to 0.20)
|
||||
darkhud.icon_state = "dark-2"
|
||||
|
||||
//Update energy storage hud
|
||||
if(energyhud)
|
||||
switch(comp.dark_energy)
|
||||
if(80 to INFINITY)
|
||||
energyhud.icon_state = "energy0"
|
||||
if(60 to 80)
|
||||
energyhud.icon_state = "energy1"
|
||||
if(40 to 60)
|
||||
energyhud.icon_state = "energy2"
|
||||
if(20 to 40)
|
||||
energyhud.icon_state = "energy3"
|
||||
if(0 to 20)
|
||||
energyhud.icon_state = "energy4"
|
||||
|
||||
/* //VOREStation AI Removal
|
||||
//Friendly ones wander towards people, maybe shy-ly if they are set to shy
|
||||
/mob/living/simple_mob/shadekin/handle_wander_movement()
|
||||
@@ -443,26 +455,6 @@
|
||||
|
||||
comp.dark_energy += gains
|
||||
|
||||
//Special hud elements for darkness and energy gains
|
||||
/mob/living/simple_mob/shadekin/extra_huds(var/datum/hud/hud,var/icon/ui_style,var/list/hud_elements)
|
||||
//Darkness hud
|
||||
darkhud = new /obj/screen()
|
||||
darkhud.icon = ui_style
|
||||
darkhud.icon_state = "dark"
|
||||
darkhud.name = "darkness"
|
||||
darkhud.screen_loc = "CENTER-2:16,SOUTH:5" //Left of the left hand
|
||||
darkhud.alpha = 150
|
||||
hud_elements |= darkhud
|
||||
|
||||
//Energy hud
|
||||
energyhud = new /obj/screen()
|
||||
energyhud.icon = ui_style
|
||||
energyhud.icon_state = "energy0"
|
||||
energyhud.name = "energy"
|
||||
energyhud.screen_loc = "CENTER+1:16,SOUTH:5" //Right of the right hand
|
||||
energyhud.alpha = 150
|
||||
hud_elements |= energyhud
|
||||
|
||||
// When someone clicks us with an empty hand
|
||||
/mob/living/simple_mob/shadekin/attack_hand(mob/living/carbon/human/M as mob)
|
||||
. = ..()
|
||||
|
||||
@@ -402,6 +402,7 @@ var/global/list/light_type_cache = list()
|
||||
else
|
||||
base_state = "flamp"
|
||||
..()
|
||||
|
||||
/obj/machinery/light/proc/set_alert_atmos()
|
||||
if(!shows_alerts)
|
||||
return
|
||||
@@ -719,27 +720,35 @@ var/global/list/light_type_cache = list()
|
||||
set_light(brightness_range * bulb_emergency_brightness_mul, max(bulb_emergency_pow_min, bulb_emergency_pow_mul * (cell.charge / cell.maxcharge)), bulb_emergency_colour)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/light/proc/flicker(var/amount = rand(10, 20))
|
||||
/obj/machinery/light/proc/flicker(var/amount = rand(10, 20), var/flicker_color)
|
||||
if(flickering) return
|
||||
if(on && status == LIGHT_OK)
|
||||
flickering = 1
|
||||
do_flicker(amount)
|
||||
do_flicker(amount, flicker_color, brightness_color, brightness_color_ns)
|
||||
|
||||
/obj/machinery/light/proc/do_flicker(remaining_flicks)
|
||||
/obj/machinery/light/proc/do_flicker(remaining_flicks, flicker_color, original_color, original_color_ns)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
PRIVATE_PROC(TRUE)
|
||||
if(status != LIGHT_OK)
|
||||
flickering = 0
|
||||
return
|
||||
on = !on
|
||||
if(flicker_color && brightness_color != flicker_color)
|
||||
brightness_color = flicker_color
|
||||
brightness_color_ns = flicker_color
|
||||
update(0) //Yes. This is done here and then immediately followed up with another update(0). Why does it need that? I have no clue. But a single update(0) does not work.
|
||||
update(0)
|
||||
if(!on) // Only play when the light turns off.
|
||||
playsound(src, 'sound/effects/light_flicker.ogg', 50, 1)
|
||||
if(remaining_flicks > 0)
|
||||
remaining_flicks--
|
||||
addtimer(CALLBACK(src, PROC_REF(do_flicker), remaining_flicks), rand(5, 15), TIMER_DELETE_ME)
|
||||
addtimer(CALLBACK(src, PROC_REF(do_flicker), remaining_flicks, flicker_color, original_color, original_color_ns), rand(5, 15), TIMER_DELETE_ME)
|
||||
return
|
||||
//All this happens after our final flicker.
|
||||
on = (status == LIGHT_OK)
|
||||
brightness_color = original_color
|
||||
brightness_color_ns = original_color_ns
|
||||
update(0)
|
||||
update(0)
|
||||
flickering = 0
|
||||
|
||||
|
||||
@@ -174,46 +174,46 @@
|
||||
/obj/item/gun/proc/special_check(var/mob/user)
|
||||
|
||||
if(!isliving(user))
|
||||
return 0
|
||||
return FALSE
|
||||
if(!user.IsAdvancedToolUser())
|
||||
return 0
|
||||
return FALSE
|
||||
if(isanimal(user))
|
||||
var/mob/living/simple_mob/S = user
|
||||
if(!S.IsHumanoidToolUser(src))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
var/mob/living/M = user
|
||||
if(istype(M))
|
||||
if(M.has_modifier_of_type(/datum/modifier/underwater_stealth))
|
||||
to_chat(user,span_warning("You cannot use guns whilst hiding underwater!"))
|
||||
return 0
|
||||
to_chat(user, span_warning("You cannot use guns whilst hiding underwater!"))
|
||||
return FALSE
|
||||
else if(M.has_modifier_of_type(/datum/modifier/phased_out))
|
||||
to_chat(user, span_warning("You cannot use guns whilst incorporeal!"))
|
||||
return FALSE
|
||||
else if(M.has_modifier_of_type(/datum/modifier/rednet))
|
||||
to_chat(user,span_warning("Your gun refuses to fire!"))
|
||||
return 0
|
||||
to_chat(user, span_warning("Your gun refuses to fire!"))
|
||||
return FALSE
|
||||
else if(M.has_modifier_of_type(/datum/modifier/trait/thickdigits))
|
||||
to_chat(user,span_warning("Your hands can't pull the trigger!!"))
|
||||
return 0
|
||||
to_chat(user, span_warning("Your hands can't pull the trigger!!"))
|
||||
return FALSE
|
||||
else if(M.has_modifier_of_type(/datum/modifier/shield_projection/melee_focus))
|
||||
to_chat(user,span_warning("The shield projection around you prevents you from using anything but melee!!"))
|
||||
return 0
|
||||
to_chat(user, span_warning("The shield projection around you prevents you from using anything but melee!!"))
|
||||
return FALSE
|
||||
if(dna_lock && attached_lock.stored_dna)
|
||||
if(!authorized_user(user))
|
||||
if(attached_lock.safety_level == 0)
|
||||
to_chat(M, span_danger("\The [src] buzzes in dissapointment and displays an invalid DNA symbol."))
|
||||
return 0
|
||||
return FALSE
|
||||
if(!attached_lock.exploding)
|
||||
if(attached_lock.safety_level == 1)
|
||||
to_chat(M, span_danger("\The [src] hisses in dissapointment."))
|
||||
visible_message(span_game(span_say(span_name("\The [src]") + " announces, \"Self-destruct occurring in ten seconds.\"")), span_game(span_say(span_name("\The [src]") + " announces, \"Self-destruct occurring in ten seconds.\"")))
|
||||
attached_lock.exploding = 1
|
||||
spawn(100)
|
||||
explosion(src, 0, 0, 3, 4)
|
||||
sleep(1)
|
||||
qdel(src)
|
||||
return 0
|
||||
addtimer(CALLBACK(src, PROC_REF(lock_explosion)), 10 SECONDS, TIMER_DELETE_ME)
|
||||
return FALSE
|
||||
if(HULK in M.mutations)
|
||||
to_chat(M, span_danger("Your fingers are much too large for the trigger guard!"))
|
||||
return 0
|
||||
return FALSE
|
||||
if((CLUMSY in M.mutations) && prob(40)) //Clumsy handling
|
||||
var/obj/P = consume_next_projectile()
|
||||
if(P)
|
||||
@@ -227,8 +227,12 @@
|
||||
M.drop_item()
|
||||
else
|
||||
handle_click_empty(user)
|
||||
return 0
|
||||
return 1
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/gun/proc/lock_explosion()
|
||||
explosion(src, 0, 0, 3, 4)
|
||||
QDEL_IN(src, 1)
|
||||
|
||||
/obj/item/gun/emp_act(severity)
|
||||
for(var/obj/O in contents)
|
||||
|
||||
@@ -53,9 +53,11 @@
|
||||
return 1
|
||||
|
||||
/obj/structure/table/MouseDrop_T(obj/O, mob/user, src_location, over_location, src_control, over_control, params)
|
||||
if(user.is_incorporeal())
|
||||
if(user.stat)
|
||||
return
|
||||
if(ismob(O.loc)) //If placing an item
|
||||
if(can_reinforce && isliving(user) && istype(O, /obj/item/stack/material) && user.get_active_hand() == O && Adjacent(user))
|
||||
reinforce_table(O, user)
|
||||
else if(ismob(O.loc)) //If placing an item
|
||||
if(!isitem(O) || user.get_active_hand() != O)
|
||||
return ..()
|
||||
if(isrobot(user))
|
||||
|
||||
@@ -193,12 +193,6 @@ var/list/table_icon_cache = list()
|
||||
visible_message(span_infoplain(span_bold("\The [user]") + " scratches at \the [src]!"))
|
||||
return ..()
|
||||
|
||||
/obj/structure/table/MouseDrop_T(obj/item/stack/material/what, mob/user)
|
||||
if(can_reinforce && isliving(user) && (!user.stat) && istype(what) && user.get_active_hand() == what && Adjacent(user))
|
||||
reinforce_table(what, user)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/table/proc/reinforce_table(obj/item/stack/material/S, mob/user)
|
||||
if(reinforced)
|
||||
to_chat(user, span_warning("\The [src] is already reinforced!"))
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 76 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,47 @@
|
||||
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
|
||||
"a" = (
|
||||
/turf/template_noop,
|
||||
/area/template_noop)
|
||||
"B" = (
|
||||
/turf/simulated/floor/weird_things/dark,
|
||||
/area/template_noop)
|
||||
"O" = (
|
||||
/obj/structure/dark_portal/minion,
|
||||
/turf/template_noop,
|
||||
/area/template_noop)
|
||||
|
||||
(1,1,1) = {"
|
||||
a
|
||||
B
|
||||
B
|
||||
a
|
||||
a
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
B
|
||||
a
|
||||
B
|
||||
B
|
||||
B
|
||||
"}
|
||||
(3,1,1) = {"
|
||||
B
|
||||
B
|
||||
O
|
||||
B
|
||||
a
|
||||
"}
|
||||
(4,1,1) = {"
|
||||
B
|
||||
B
|
||||
B
|
||||
B
|
||||
a
|
||||
"}
|
||||
(5,1,1) = {"
|
||||
B
|
||||
a
|
||||
B
|
||||
B
|
||||
a
|
||||
"}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+9
-1
@@ -578,8 +578,16 @@
|
||||
#include "code\datums\components\reagent_hose\datum.dm"
|
||||
#include "code\datums\components\reagent_hose\inflation.dm"
|
||||
#include "code\datums\components\reagent_hose\item.dm"
|
||||
#include "code\datums\components\species\shadekin.dm"
|
||||
#include "code\datums\components\species\xenochimera.dm"
|
||||
#include "code\datums\components\species\shadekin\shadekin.dm"
|
||||
#include "code\datums\components\species\shadekin\helpers\comp_helpers.dm"
|
||||
#include "code\datums\components\species\shadekin\powers\create_shade.dm"
|
||||
#include "code\datums\components\species\shadekin\powers\dark_maw.dm"
|
||||
#include "code\datums\components\species\shadekin\powers\dark_respite.dm"
|
||||
#include "code\datums\components\species\shadekin\powers\phase_shift.dm"
|
||||
#include "code\datums\components\species\shadekin\powers\regenerate_other.dm"
|
||||
#include "code\datums\components\species\shadekin\powers\dark_tunnel\dark_tunnel_structures.dm"
|
||||
#include "code\datums\components\species\shadekin\powers\dark_tunnel\dark_tunneling.dm"
|
||||
#include "code\datums\components\traits\burninlight.dm"
|
||||
#include "code\datums\components\traits\drippy.dm"
|
||||
#include "code\datums\components\traits\gargoyle.dm"
|
||||
|
||||
Reference in New Issue
Block a user