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:
Cameron Lennox
2025-07-05 20:04:24 +02:00
committed by GitHub
parent ab408df1e6
commit 5917c7bdee
66 changed files with 1823 additions and 1031 deletions
+2 -1
View File
@@ -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"
+1 -3
View File
@@ -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
+6 -1
View File
@@ -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)
+2
View File
@@ -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)
+5 -4
View File
@@ -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////////////
+25
View File
@@ -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.
+1 -13
View File
@@ -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
+1 -1
View File
@@ -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()
+5 -13
View File
@@ -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>")
+14 -22
View File
@@ -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
+7
View File
@@ -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
View File
@@ -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)
-3
View File
@@ -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)
+2 -1
View File
@@ -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
+10 -1
View File
@@ -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)
. = ..()
+13 -4
View File
@@ -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
+24 -20
View File
@@ -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)
+4 -2
View File
@@ -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))
-6
View File
@@ -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

+47
View File
@@ -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
View File
@@ -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"