diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index 015727c1a8..23db2a5a35 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -838,8 +838,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" diff --git a/code/__defines/shadekin.dm b/code/__defines/shadekin.dm index 8e3c3a0583..62833a785a 100644 --- a/code/__defines/shadekin.dm +++ b/code/__defines/shadekin.dm @@ -1,13 +1,3 @@ -#define NOT_WHILE_SHIFTED 1 -#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_DARK_TUNNEL 0x40 //CHOMPEdit Add - Dark Tunneling -#define AB_DARK_RESPITE 0x80 //CHOMPEdit Add - Dark Respite - //Porting over the type system of the mobs #define BLUE_EYES 1 #define RED_EYES 2 diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm index 9261004031..9ecd324377 100644 --- a/code/_helpers/global_lists_vr.dm +++ b/code/_helpers/global_lists_vr.dm @@ -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) diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm index 2ea2a4885f..8ef0c3b3a6 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -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) diff --git a/code/_onclick/hud/ability_screen_objects.dm b/code/_onclick/hud/ability_screen_objects.dm index c13e5cf979..05e48d8bd3 100644 --- a/code/_onclick/hud/ability_screen_objects.dm +++ b/code/_onclick/hud/ability_screen_objects.dm @@ -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//////////// diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 8cd701e089..b99532ba10 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -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. diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index 6f2a0f8367..4ed75090fa 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -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 diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index b884dd4e7c..e477e4826c 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -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() diff --git a/code/_onclick/hud/screen_objects_vr.dm b/code/_onclick/hud/screen_objects_vr.dm index d4073fb6fd..2df745ef53 100644 --- a/code/_onclick/hud/screen_objects_vr.dm +++ b/code/_onclick/hud/screen_objects_vr.dm @@ -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)) diff --git a/code/datums/components/species/shadekin.dm b/code/datums/components/species/shadekin.dm deleted file mode 100644 index 797e4c19b4..0000000000 --- a/code/datums/components/species/shadekin.dm +++ /dev/null @@ -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 diff --git a/code/datums/components/species/shadekin/helpers/comp_helpers.dm b/code/datums/components/species/shadekin/helpers/comp_helpers.dm new file mode 100644 index 0000000000..59ab4b8d80 --- /dev/null +++ b/code/datums/components/species/shadekin/helpers/comp_helpers.dm @@ -0,0 +1,213 @@ +///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) + nutrition_conversion_scaling = 0.5 + if(RED_EYES) + set_light_and_darkness(-0.5,0.5) + nutrition_conversion_scaling = 2 + if(PURPLE_EYES) + set_light_and_darkness(-0.5,1) + nutrition_conversion_scaling = 1 + if(YELLOW_EYES) + set_light_and_darkness(-2,3) + nutrition_conversion_scaling = 0.5 + if(GREEN_EYES) + set_light_and_darkness(0.125,2) + nutrition_conversion_scaling = 0.5 + if(ORANGE_EYES) + set_light_and_darkness(-0.25,0.75) + nutrition_conversion_scaling = 1.5 + +///Sets our eye color. +/datum/component/shadekin/proc/set_shadekin_eyecolor() + if(!ishuman(owner)) + return eye_color //revert to default if we're not a human + + var/mob/living/carbon/human/H = owner + var/eyecolor_rgb = rgb(H.r_eyes, H.g_eyes, H.b_eyes) + + var/list/hsv_color = rgb2num(eyecolor_rgb, COLORSPACE_HSV) + var/eyecolor_hue = hsv_color[1] + var/eyecolor_sat = hsv_color[2] + var/eyecolor_val = hsv_color[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) + + var/list/rgb_color = rgb2num(eyecolor_rgb) + H.r_eyes = rgb_color[1] + H.g_eyes = rgb_color[2] + H.b_eyes = rgb_color[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) + +/datum/component/shadekin/proc/handle_nutrition_conversion(dark_gains) + if(!nutrition_energy_conversion) + return + if(shadekin_get_energy() == 100 && dark_gains > 0) + owner.nutrition += dark_gains * 5 * nutrition_conversion_scaling + else if(shadekin_get_energy() < 50 && owner.nutrition > 500) + owner.nutrition -= nutrition_conversion_scaling * 50 + dark_gains += nutrition_conversion_scaling + +/datum/component/shadekin/proc/attack_dephase(var/turf/T = null, atom/dephaser) + // no assigned dephase-target, just use our own + if(!T) + T = get_turf(owner) + + if(!in_phase || doing_phase) + return FALSE + + // make sure it's possible to be dephased (and we're in phase) + if(!T || !T.CanPass(owner,T)) + return FALSE + + + log_admin("[key_name_admin(owner)] was stunned out of phase at [T.x],[T.y],[T.z] by [dephaser.name], last touched by [dephaser.forensic_data?.get_lastprint()].") + message_admins("[key_name_admin(owner)] was stunned out of phase at [T.x],[T.y],[T.z] by [dephaser.name], last touched by [dephaser.forensic_data?.get_lastprint()]. (JMP)", 1) + // start the dephase + owner.phase_in(T, src) + shadekin_adjust_energy(-20) // loss of energy for the interception + // apply a little extra stun for good measure + owner.Weaken(3) + +/mob/living/carbon/human/is_incorporeal() + var/datum/component/shadekin/SK = get_shadekin_component() + if(SK && SK.in_phase) //Shadekin + return TRUE + return ..() + +///Proc that takes in special considerations, such as 'no abilities in VR' and the such +///Returns TRUE if we try to do something forbidden +/datum/component/shadekin/proc/special_considerations(allow_vr) + if(!allow_vr && istype(get_area(owner), /area/vr)) + to_chat(owner, span_danger("The VR systems cannot comprehend this power! This is useless to you!")) + return TRUE + return FALSE diff --git a/code/datums/components/species/shadekin/powers/create_shade.dm b/code/datums/components/species/shadekin/powers/create_shade.dm new file mode 100644 index 0000000000..506d0f274c --- /dev/null +++ b/code/datums/components/species/shadekin/powers/create_shade.dm @@ -0,0 +1,68 @@ +////////////////////// +/// 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(SK.special_considerations(TRUE)) + 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 diff --git a/code/datums/components/species/shadekin/powers/dark_maw.dm b/code/datums/components/species/shadekin/powers/dark_maw.dm new file mode 100644 index 0000000000..edf5cdda4a --- /dev/null +++ b/code/datums/components/species/shadekin/powers/dark_maw.dm @@ -0,0 +1,207 @@ +/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(SK.special_considerations()) + 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) diff --git a/code/datums/components/species/shadekin/powers/dark_respite.dm b/code/datums/components/species/shadekin/powers/dark_respite.dm new file mode 100644 index 0000000000..d71b42dab6 --- /dev/null +++ b/code/datums/components/species/shadekin/powers/dark_respite.dm @@ -0,0 +1,107 @@ +//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(SK.special_considerations()) + 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 diff --git a/code/modules/mob/living/carbon/human/species/shadekin/dark_portal_ch.dm b/code/datums/components/species/shadekin/powers/dark_tunnel/dark_tunnel_structures.dm similarity index 71% rename from code/modules/mob/living/carbon/human/species/shadekin/dark_portal_ch.dm rename to code/datums/components/species/shadekin/powers/dark_tunnel/dark_tunnel_structures.dm index 193be8f2e2..959489135a 100644 --- a/code/modules/mob/living/carbon/human/species/shadekin/dark_portal_ch.dm +++ b/code/datums/components/species/shadekin/powers/dark_tunnel/dark_tunnel_structures.dm @@ -1,11 +1,8 @@ -// Kind of like a combination of all the teleporter stuff all in one package. -// And without the power requirements or accuracy loss, -// though it requires a shadekin to open the minion portals. - -// Also yes this needs to be outside of modular chomp because there are variables that have to be map overriden. +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 = 'modular_chomp/icons/obj/shadekin_portal.dmi' + icon = 'icons/obj/shadekin_portal.dmi' density = TRUE anchored = TRUE var/locked = null @@ -16,51 +13,15 @@ /obj/structure/dark_portal/proc/close_portal() return -/obj/structure/dark_portal/proc/is_shadekin(mob/shade_maybe) - if(istype(shade_maybe, /mob/living/simple_mob/shadekin)) - return TRUE - else if(istype(shade_maybe, /mob/living/carbon/human)) - var/mob/living/carbon/human/carbonkin_maybe = shade_maybe - if(carbonkin_maybe.get_species() == SPECIES_SHADEKIN) - return TRUE - return FALSE - -/obj/structure/dark_portal/proc/get_ability_flags(mob/shade_maybe) - if(istype(shade_maybe, /mob/living/simple_mob/shadekin)) - var/mob/living/simple_mob/shadekin/shadekin = shade_maybe - return shadekin.ability_flags - else if(istype(shade_maybe, /mob/living/carbon/human)) - var/mob/living/carbon/human/carbonkin_maybe = shade_maybe - return carbonkin_maybe.ability_flags - return 0 - -/obj/structure/dark_portal/proc/get_shadekin_energy(mob/shade_maybe) - if(istype(shade_maybe, /mob/living/simple_mob/shadekin)) - var/mob/living/simple_mob/shadekin/shadekin = shade_maybe - return shadekin.comp.dark_energy - else if(istype(shade_maybe, /mob/living/carbon/human)) - var/mob/living/carbon/human/carbonkin_maybe = shade_maybe - return carbonkin_maybe.shadekin_get_energy() - return 0 - -/obj/structure/dark_portal/proc/adjust_shadekin_energy(mob/shade_maybe, amount) - if(istype(shade_maybe, /mob/living/simple_mob/shadekin)) - var/mob/living/simple_mob/shadekin/shadekin = shade_maybe - shadekin.comp.dark_energy += amount - return - else if(istype(shade_maybe, /mob/living/carbon/human)) - var/mob/living/carbon/human/carbonkin_maybe = shade_maybe - return carbonkin_maybe.shadekin_adjust_energy(amount) - return 0 - /obj/structure/dark_portal/proc/teleport(atom/movable/M as mob|obj) if(!locked) return - if(is_shadekin(M)) - if(get_ability_flags(M) & AB_DARK_RESPITE) + 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!")) - var/mob/living/user = M - user.Stun(10) + to_check.Stun(10) return do_teleport(M, locked, precision, local = FALSE, bohsafe = TRUE) @@ -77,8 +38,6 @@ var/list/destination_station_areas // Override this in map files! var/list/destination_wilderness_areas // Override this in map files! -GLOBAL_LIST_BOILERPLATE(all_darkportal_hubs, /obj/structure/dark_portal/hub) - /obj/structure/dark_portal/hub/Initialize(mapload) . = ..() locked = src @@ -93,12 +52,15 @@ GLOBAL_LIST_BOILERPLATE(all_darkportal_hubs, /obj/structure/dark_portal/hub) locked_name = src.name precision = 1 -/obj/structure/dark_portal/hub/attack_hand(mob/user) - if(is_shadekin(user)) - if(get_ability_flags(user) & AB_DARK_RESPITE) +/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(get_ability_flags(user) & AB_PHASE_SHIFTED) + if(SK.in_phase) to_chat(user, span_warning("You can't use this while phase shifted!")) return if(locked != src) @@ -172,20 +134,21 @@ GLOBAL_LIST_BOILERPLATE(all_darkportal_hubs, /obj/structure/dark_portal/hub) /obj/structure/dark_portal/minion icon_state = "minion0" -GLOBAL_LIST_BOILERPLATE(all_darkportal_minions, /obj/structure/dark_portal/minion) - /obj/structure/dark_portal/minion/close_portal() locked = null locked_name = "" precision = 1 icon_state = "minion0" -/obj/structure/dark_portal/minion/attack_hand(mob/user) - if(is_shadekin(user)) - if(get_ability_flags(user) & AB_DARK_RESPITE) +/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(get_ability_flags(user) & AB_PHASE_SHIFTED) + if(SK.in_phase) to_chat(user, span_warning("You can't use this while phase shifted!")) return FALSE if(icon_state == "minion1") @@ -195,21 +158,19 @@ GLOBAL_LIST_BOILERPLATE(all_darkportal_minions, /obj/structure/dark_portal/minio if(confirm == "Yes") close_portal() return - if(get_shadekin_energy(user) < 10) + 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) - adjust_shadekin_energy(user, -10) + 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" - spawn(3000) - if(locked == target) - close_portal() + 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) @@ -220,9 +181,8 @@ GLOBAL_LIST_BOILERPLATE(all_darkportal_minions, /obj/structure/dark_portal/minio locked = L[desc] locked_name = desc icon_state = "minion1" - spawn(3000) - if(locked == L[desc]) - close_portal() + 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") @@ -230,6 +190,14 @@ GLOBAL_LIST_BOILERPLATE(all_darkportal_minions, /obj/structure/dark_portal/minio 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") diff --git a/code/datums/components/species/shadekin/powers/dark_tunnel/dark_tunneling.dm b/code/datums/components/species/shadekin/powers/dark_tunnel/dark_tunneling.dm new file mode 100644 index 0000000000..261bb61782 --- /dev/null +++ b/code/datums/components/species/shadekin/powers/dark_tunnel/dark_tunneling.dm @@ -0,0 +1,93 @@ +//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(SK.special_considerations()) + 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)) + if(SK.created_dark_tunnel) //check again because the user may have queued this up multiple times. + to_chat(src, span_warning("You have already made a tunnel to the Dark!")) + return FALSE + 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)) diff --git a/code/datums/components/species/shadekin/powers/phase_shift.dm b/code/datums/components/species/shadekin/powers/phase_shift.dm index 4580277558..73597d2dc6 100644 --- a/code/datums/components/species/shadekin/powers/phase_shift.dm +++ b/code/datums/components/species/shadekin/powers/phase_shift.dm @@ -27,6 +27,8 @@ var/datum/component/shadekin/SK = get_shadekin_component() if(!SK) return FALSE + if(SK.special_considerations()) + return FALSE if(stat) to_chat(src, span_warning("Can't use that ability in your state!")) return FALSE @@ -50,10 +52,24 @@ 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! + for(var/mob/living/thing in orange(7, src)) //Fun fact, doing two typed loops is faster than doing one untyped loop. Check it with Tracy! + if(istype(thing, /mob/living/carbon/human)) + var/mob/living/carbon/human/watchers = thing + if(watchers in oviewers(7,src)) + var/datum/component/shadekin/watcher_SK = watchers.get_shadekin_component() + if(!watcher_SK && !(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! + if(istype(thing, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/watchers = thing + var/datum/component/shadekin/watcher_SK = watchers.get_shadekin_component() //you never know, man. + if(watchers in oviewers(7,src)) + if(!watcher_SK && !watchers.stat && !isbelly(watchers.loc)) + watcher++ //The robot is watching us! + if(SK.camera_counts_as_watcher) + for(var/obj/machinery/camera/watchers in orange(7, src)) + if(watchers.can_use()) + if(src in watchers.can_see()) + watcher++ //The camera is watching us! ability_cost = CLAMP(ability_cost/(0.01+darkness*2),50, 80)//This allows for 1 watcher in full light if(watcher>0) @@ -163,6 +179,8 @@ held_lights.update_brightness() SK.doing_phase = FALSE + if(SK.flicker_time < 5 || SK.flicker_distance < 5 || SK.flicker_break_chance < 5) + Stun(SK.calculate_stun()) if(!SK.flicker_time) return //Early return. No time, no flickering. //Affect nearby lights diff --git a/code/datums/components/species/shadekin/powers/regenerate_other.dm b/code/datums/components/species/shadekin/powers/regenerate_other.dm new file mode 100644 index 0000000000..2d910a0d0f --- /dev/null +++ b/code/datums/components/species/shadekin/powers/regenerate_other.dm @@ -0,0 +1,68 @@ +////////////////////////// +/// 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(SK.special_considerations()) + 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) diff --git a/code/datums/components/species/shadekin/shadekin.dm b/code/datums/components/species/shadekin/shadekin.dm new file mode 100644 index 0000000000..d768c771e7 --- /dev/null +++ b/code/datums/components/species/shadekin/shadekin.dm @@ -0,0 +1,281 @@ +//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 + ///If cameras count as watchers for us + var/camera_counts_as_watcher = 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 + camera_counts_as_watcher = 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 + . = ..() + +/datum/component/shadekin/proc/recalc_values() + set_shadekin_eyecolor() //Gets what eye color we are. + set_eye_energy() //Sets the energy values based on our eye color. + +///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 + + handle_nutrition_conversion(dark_gains) + + shadekin_adjust_energy(dark_gains) + + //Update huds + update_shadekin_hud() + +/datum/component/shadekin/proc/calculate_stun() + var/stun_time = 3 + if(flicker_time > 0) + stun_time -= min(flicker_time / 5, 1) + if(flicker_distance > 0) + stun_time -= min(flicker_distance / 5, 1) + if(flicker_break_chance > 0) + stun_time -= min(flicker_break_chance / 5, 1) + return stun_time + +/datum/component/shadekin/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ShadekinConfig", "Shadekin Config") + ui.open() + +/datum/component/shadekin/tgui_data(mob/user) + var/data = list( + "stun_time" = calculate_stun(), + "flicker_time" = flicker_time, + "flicker_color" = flicker_color, + "flicker_break_chance" = flicker_break_chance, + "flicker_distance" = flicker_distance, + ) + + return data + + +/datum/component/shadekin/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("adjust_time") + var/new_time = text2num(params["val"]) + new_time = CLAMP(new_time, 2, 20) + if(!isnum(new_time)) + return FALSE + flicker_time = new_time + ui.user.write_preference_directly(/datum/preference/numeric/living/flicker_time, new_time) + return TRUE + if("adjust_color") + var/set_new_color = tgui_color_picker(ui.user, "Select a color you wish the lights to flicker as (Default is #E0EFF0)", "Color Selector", flicker_color) + if(!set_new_color) + return FALSE + flicker_color = set_new_color + ui.user.write_preference_directly(/datum/preference/color/living/flicker_color, set_new_color) + return TRUE + if("adjust_break") + var/new_break_chance = text2num(params["val"]) + new_break_chance = CLAMP(new_break_chance, 0, 25) + if(!isnum(new_break_chance)) + return FALSE + flicker_break_chance = new_break_chance + ui.user.write_preference_directly(/datum/preference/numeric/living/flicker_break_chance, new_break_chance) + return TRUE + if("adjust_distance") + var/new_distance = text2num(params["val"]) + new_distance = CLAMP(new_distance, 4, 10) + if(!isnum(new_distance)) + return FALSE + flicker_distance = new_distance + ui.user.write_preference_directly(/datum/preference/numeric/living/flicker_distance, new_distance) + return TRUE + +/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 + + SK.tgui_interact(src) diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index edd4776841..5234d3f5e7 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -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, bohsafe=FALSE) //CHOMPStation 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,17 +19,17 @@ 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/bohsafe = FALSE //CHOMP Add - if true, can teleport safely with a BoH + 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, bohsafe=FALSE) //CHOMPStation 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,bohsafe) //CHOMPStation Edit +/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout,local,bohsafe) if(!setTeleatom(ateleatom)) return 0 if(!setDestination(adestination)) @@ -40,7 +40,7 @@ var/bluespace_item_types = newlist(/obj/item/storage/backpack/holding, setEffects(aeffectin,aeffectout) setForceTeleport(afteleport) setSounds(asoundin,asoundout) - src.local = local // VOREStation Add + src.local = local return 1 //must succeed @@ -123,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) @@ -142,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, bohsafe=0, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) //CHOMP edit +/datum/teleport/instant/New(ateleatom, adestination, aprecision=0, afteleport=1, bohsafe=0, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) if(..()) teleport() return @@ -152,20 +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) - //CHOMP add start var/datum/effect/effect/system/spark_spread/aeffect2 = new - aeffect2.set_up(5, 1, teleatom) //This looks stupid, but it doesn't work unless I do - //CHOMP add end + aeffect2.set_up(5, 1, teleatom) effectin = effectin || aeffect - effectout = effectout || aeffect2 //CHOMP edit + effectout = effectout || aeffect2 return 1 else return ..() /datum/teleport/instant/science/setPrecision(aprecision) ..() - if(bohsafe) //CHOMPedit - return 1 //CHOMPedit + if(bohsafe) + return 1 var/list/bluespace_things = newlist() @@ -174,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) @@ -202,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)) @@ -239,4 +224,3 @@ var/bluespace_item_types = newlist(/obj/item/storage/backpack/holding, return 0 else return 1 - //VOREStation Edit End diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 0e0830211f..7f73884d50 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -2693,6 +2693,12 @@ var/list/the_station_areas = list ( 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 //CHOMPSTATION AREAS //Moved hangars to here from Southern cross areas. diff --git a/code/game/area/Space Station 13 areas_ch.dm b/code/game/area/Space Station 13 areas_ch.dm index 2b21d07a43..9a784fd63c 100644 --- a/code/game/area/Space Station 13 areas_ch.dm +++ b/code/game/area/Space Station 13 areas_ch.dm @@ -73,13 +73,6 @@ name = "\improper Particle Accelerator Lab" icon_state = "toxlab" -/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 - /area/security/nuke_storage flags = PHASE_SHIELDED diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index b39856f8e6..74bbcf278c 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -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(check_rights_for(ourmob.client, R_HOLDER)) + if(check_rights_for(ourmob.client, R_HOLDER)) //If we're an admin, we don't get affected by phase blockers. 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) + SK.attack_dephase(ourmob.loc, src) /area/proc/isAlwaysIndoors() return FALSE diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index e5911bc219..9a51cebec9 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1015,6 +1015,8 @@ About the new airlock wires panel: /obj/machinery/door/airlock/CtrlClick(mob/user) //Hold door open user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(user.is_incorporeal()) + return if(!Adjacent(user)) return if(user.a_intent == I_HURT) diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index 2682899ebf..f6c0a670be 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -25,9 +25,11 @@ GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal) if(AM.is_incorporeal()) if(!event) return - if(iscarbon(AM)) - var/mob/living/carbon/human/H = AM - H.attack_dephase(null, src) + if(isliving(AM)) + var/mob/living/L = AM + var/datum/component/shadekin/SK = L.get_shadekin_component() + if(SK) + SK.attack_dephase(null, src) if(ismob(AM) && !(isliving(AM))) return //do not send ghosts, zshadows, ai eyes, etc spawn(0) diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index e17f759a2e..8f0435c7ea 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -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." diff --git a/code/modules/client/preferences/types/character/general/06_special.dm b/code/modules/client/preferences/types/character/general/06_special.dm new file mode 100644 index 0000000000..65c7f30329 --- /dev/null +++ b/code/modules/client/preferences/types/character/general/06_special.dm @@ -0,0 +1,61 @@ +/datum/preference/color/living/flicker_color + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "flicker_color" + can_randomize = FALSE + +/datum/preference/color/living/flicker_color/create_default_value() + return "#E0EFF0" + +/datum/preference/color/living/flicker_color/apply_to_living(mob/living/target, value) + var/datum/component/shadekin/our_SK = target.get_shadekin_component() + if(our_SK) + our_SK.flicker_color = value + +/datum/preference/numeric/living/flicker_time + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "flicker_time" + can_randomize = FALSE + minimum = 2 + maximum = 20 + +/datum/preference/numeric/living/flicker_time/create_default_value() + return 10 + +/datum/preference/numeric/living/flicker_time/apply_to_living(mob/living/target, value) + var/datum/component/shadekin/our_SK = target.get_shadekin_component() + if(our_SK) + our_SK.flicker_time = value + +/datum/preference/numeric/living/flicker_break_chance + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "flicker_break_chance" + can_randomize = FALSE + minimum = 0 + maximum = 25 + +/datum/preference/numeric/living/flicker_break_chance/create_default_value() + return 0 + +/datum/preference/numeric/living/flicker_break_chance/apply_to_living(mob/living/target, value) + var/datum/component/shadekin/our_SK = target.get_shadekin_component() + if(our_SK) + our_SK.flicker_break_chance = value + +/datum/preference/numeric/living/flicker_distance + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "flicker_distance" + can_randomize = FALSE + minimum = 4 + maximum = 10 + +/datum/preference/numeric/living/flicker_distance/create_default_value() + return 4 + +/datum/preference/numeric/living/flicker_distance/apply_to_living(mob/living/target, value) + var/datum/component/shadekin/our_SK = target.get_shadekin_component() + if(our_SK) + our_SK.flicker_distance = value diff --git a/code/modules/emotes/definitions/audible.dm b/code/modules/emotes/definitions/audible.dm index a09f4004eb..2e9732d966 100644 --- a/code/modules/emotes/definitions/audible.dm +++ b/code/modules/emotes/definitions/audible.dm @@ -287,3 +287,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) diff --git a/code/modules/emotes/definitions/visible.dm b/code/modules/emotes/definitions/visible.dm index 78cfa4ec7d..780d1373c0 100644 --- a/code/modules/emotes/definitions/visible.dm +++ b/code/modules/emotes/definitions/visible.dm @@ -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) diff --git a/code/modules/emotes/emote_mob.dm b/code/modules/emotes/emote_mob.dm index d48f0bd089..fb00512cdb 100644 --- a/code/modules/emotes/emote_mob.dm +++ b/code/modules/emotes/emote_mob.dm @@ -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() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 3f8bae4706..47efb73133 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -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("") diff --git a/code/modules/mob/living/carbon/human/human_defines_vr.dm b/code/modules/mob/living/carbon/human/human_defines_vr.dm index bd2ffa7b64..08744e8a35 100644 --- a/code/modules/mob/living/carbon/human/human_defines_vr.dm +++ b/code/modules/mob/living/carbon/human/human_defines_vr.dm @@ -14,43 +14,3 @@ 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 var/list/body_writing // assoc list by BP_ key - -/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) diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index b7fb7b67cc..b568fc40b2 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -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")])" diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm index d9a99b835c..514722b2fc 100644 --- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm +++ b/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm @@ -10,8 +10,7 @@ but next to no verifiable evidence to their existence. However, they have recently been more verifiably \ documented in the Virgo system, following a mining bombardment of Virgo 3. The crew of NSB Adephagia have \ taken to calling these creatures 'Shadekin', and the name has generally stuck and spread. " //TODO: Something that's not wiki copypaste -//CHOMPStation Removal TFF 12/24/19 - Wikilinks removed -// wikilink = "https://wiki.vore-station.net/Shadekin" + wikilink = "https://wiki.vore-station.net/Shadekin" catalogue_data = list(/datum/category_item/catalogue/fauna/shadekin) language = LANGUAGE_SHADEKIN @@ -22,9 +21,6 @@ unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/claws/shadekin, /datum/unarmed_attack/bite/sharp/shadekin) rarity_value = 15 //INTERDIMENSIONAL FLUFFERS - // male_scream_sound = null //CHOMPedit - // female_scream_sound = null //CHOMPedit - inherent_verbs = list(/mob/proc/adjust_hive_range) siemens_coefficient = 1 @@ -62,7 +58,7 @@ // has_glowing_eyes = TRUE //Applicable through neutral taits. - //death_message = "phases to somewhere far away!" //CHOMPEdit Removed + death_message = "phases to somewhere far away!" speech_bubble_appearance = "ghost" genders = list(MALE, FEMALE, PLURAL, NEUTER) @@ -72,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 @@ -105,164 +100,116 @@ 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, - /datum/power/shadekin/dark_tunneling, //CHOMPEdit Add - Dark Tunneling - /datum/power/shadekin/dark_respite, //CHOMPEdit Add - Dark Respite - /datum/power/shadekin/dark_maw) //CHOMPEdit Add - Dark Maw - var/list/shadekin_ability_datums = list() - var/list/active_dark_maws = list() //CHOMPEdit - Add dark maws - var/kin_type - var/energy_light = 0.25 - var/energy_dark = 0.75 - var/nutrition_conversion_scaling = 0.5 //CHOMPEdit - Add nutrition <-> dark energy conversion - var/phase_gentle = TRUE //CHOMPEdit - Add gentle phasing, defaults to on. - var/manual_respite = FALSE //CHOMPEdit - Dark Respite - var/respite_activating = FALSE //CHOMPEdit - Dark Respite - var/nutrition_energy_conversion = TRUE //CHOMPEdit - Add toggle to nutrition and energy conversions - species_component = /datum/component/shadekin + species_component = /datum/component/shadekin/full //CHOMPEdit: Enabling full shadekin. + component_requires_late_recalc = TRUE -/datum/species/shadekin/New() - ..() - for(var/power in shadekin_abilities) - var/datum/power/shadekin/SKP = new power(src) - shadekin_ability_datums.Add(SKP) - -//CHOMPEdit Begin - Actually phase to the Dark on death /datum/species/shadekin/handle_death(var/mob/living/carbon/human/H) - H.clear_dark_maws() //CHOMPEdit - clear dark maws on death or similar - if(respite_activating) - return TRUE - var/area/current_area = get_area(H) - if((H.ability_flags & AB_DARK_RESPITE) || H.has_modifier_of_type(/datum/modifier/dark_respite) || current_area.flag_check(AREA_LIMIT_DARK_RESPITE)) - return - if(!LAZYLEN(GLOB.latejoin_thedark)) - log_and_message_admins("[H] died outside of the dark but there were no valid floors to warp to") - return - - H.visible_message("\The [H.name] phases to somewhere far away!") - var/obj/effect/temp_visual/shadekin/phase_out/phaseanimout = new /obj/effect/temp_visual/shadekin/phase_out(H.loc) - phaseanimout.dir = H.dir - respite_activating = TRUE - - H.drop_l_hand() - H.drop_r_hand() - - H.shadekin_set_energy(0) - H.ability_flags |= AB_DARK_RESPITE - H.invisibility = INVISIBILITY_SHADEKIN - - H.adjustFireLoss(-(H.getFireLoss() * 0.75)) - H.adjustBruteLoss(-(H.getBruteLoss() * 0.75)) - H.adjustToxLoss(-(H.getToxLoss() * 0.75)) - H.adjustCloneLoss(-(H.getCloneLoss() * 0.75)) - H.germ_level = 0 // CHOMPAdd - Take away the germs, or we'll die AGAIN - H.vessel.add_reagent(REAGENT_ID_BLOOD,blood_volume-H.vessel.total_volume) - for(var/obj/item/organ/external/bp in H.organs) - bp.bandage() - bp.disinfect() - H.nutrition = 0 - H.invisibility = INVISIBILITY_SHADEKIN - BITRESET(H.hud_updateflag, HEALTH_HUD) - BITRESET(H.hud_updateflag, STATUS_HUD) - BITRESET(H.hud_updateflag, LIFE_HUD) - - if(istype(H.loc, /obj/belly)) - //Yay digestion... presumably... - var/obj/belly/belly = H.loc - add_attack_logs(belly.owner, H, "Digested in [lowertext(belly.name)]") - to_chat(belly.owner, span_notice("\The [H.name] suddenly vanishes within your [belly.name]")) - H.forceMove(pick(GLOB.latejoin_thedark)) - if(H.ability_flags & AB_PHASE_SHIFTED) - H.phase_shift() - else - var/obj/effect/temp_visual/shadekin/phase_in/phaseanim = new /obj/effect/temp_visual/shadekin/phase_in(H.loc) - phaseanim.dir = H.dir - H.invisibility = initial(H.invisibility) - respite_activating = FALSE - belly.owner.handle_belly_update() // CHOMPEdit - H.clear_fullscreen("belly") - if(H.hud_used) - if(!H.hud_used.hud_shown) - H.toggle_hud_vis() - H.stop_sound_channel(CHANNEL_PREYLOOP) - H.add_modifier(/datum/modifier/dark_respite, 10 MINUTES) - H.muffled = FALSE - H.forced_psay = FALSE - - - spawn(5 MINUTES) - H.ability_flags &= ~AB_DARK_RESPITE - to_chat(H, span_notice("You feel like you can leave the Dark again")) + var/special_handling = TRUE //varswitch for downstream //CHOMPEdit - Enable. + H.clear_dark_maws() //clear dark maws on death or similar + if(!special_handling) + spawn(1) + for(var/obj/item/W in H) + H.drop_from_inventory(W) + qdel(H) else - H.add_modifier(/datum/modifier/dark_respite, 25 MINUTES) + var/datum/component/shadekin/SK = H.get_shadekin_component() + if(!SK) + return + if(SK.respite_activating) + return TRUE + var/area/current_area = get_area(H) + if((SK.in_dark_respite) || H.has_modifier_of_type(/datum/modifier/dark_respite) || current_area.flag_check(AREA_LIMIT_DARK_RESPITE)) + return + if(!LAZYLEN(GLOB.latejoin_thedark)) + log_and_message_admins("[H] died outside of the dark but there were no valid floors to warp to") + return - spawn(1 SECOND) + H.visible_message("\The [H.name] phases to somewhere far away!") + var/obj/effect/temp_visual/shadekin/phase_out/phaseanimout = new /obj/effect/temp_visual/shadekin/phase_out(H.loc) + phaseanimout.dir = H.dir + SK.respite_activating = TRUE + + H.drop_l_hand() + H.drop_r_hand() + + SK.shadekin_set_energy(0) + SK.in_dark_respite = TRUE + H.invisibility = INVISIBILITY_SHADEKIN + + H.adjustFireLoss(-(H.getFireLoss() * 0.75)) + H.adjustBruteLoss(-(H.getBruteLoss() * 0.75)) + H.adjustToxLoss(-(H.getToxLoss() * 0.75)) + H.adjustCloneLoss(-(H.getCloneLoss() * 0.75)) + H.germ_level = 0 //Take away the germs, or we'll die AGAIN + H.vessel.add_reagent(REAGENT_ID_BLOOD,blood_volume-H.vessel.total_volume) + for(var/obj/item/organ/external/bp in H.organs) + bp.bandage() + bp.disinfect() + for(var/obj/item/organ/internal/I in H.internal_organs) //other wise their organs stay mush + I.damage = 0 + I.status = 0 + if(I.organ_tag == O_EYES) + H.sdisabilities &= ~BLIND + if(I.organ_tag == O_LUNGS) + H.SetLosebreath(0) + H.nutrition = 0 + H.invisibility = INVISIBILITY_SHADEKIN + BITRESET(H.hud_updateflag, HEALTH_HUD) + BITRESET(H.hud_updateflag, STATUS_HUD) + BITRESET(H.hud_updateflag, LIFE_HUD) + + if(istype(H.loc, /obj/belly)) + //Yay digestion... presumably... + var/obj/belly/belly = H.loc + add_attack_logs(belly.owner, H, "Digested in [lowertext(belly.name)]") + to_chat(belly.owner, span_notice("\The [H.name] suddenly vanishes within your [belly.name]")) H.forceMove(pick(GLOB.latejoin_thedark)) - if(H.ability_flags & AB_PHASE_SHIFTED) + if(SK.in_phase) H.phase_shift() else var/obj/effect/temp_visual/shadekin/phase_in/phaseanim = new /obj/effect/temp_visual/shadekin/phase_in(H.loc) phaseanim.dir = H.dir H.invisibility = initial(H.invisibility) - respite_activating = FALSE + SK.respite_activating = FALSE + belly.owner.handle_belly_update() + H.clear_fullscreen("belly") + if(H.hud_used) + if(!H.hud_used.hud_shown) + H.toggle_hud_vis() + H.stop_sound_channel(CHANNEL_PREYLOOP) + H.add_modifier(/datum/modifier/dark_respite, 10 MINUTES) + H.muffled = FALSE + H.forced_psay = FALSE - spawn(15 MINUTES) - H.ability_flags &= ~AB_DARK_RESPITE - to_chat(H, span_notice("You feel like you can leave the Dark again")) - return TRUE - -/datum/modifier/dark_respite - name = "Dark Respite" - pain_immunity = 1 - -/datum/modifier/dark_respite/tick() - if(istype(src.holder, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = src.holder - if(H.nutrition) - H.add_chemical_effect(CE_BLOODRESTORE, 5) - H.nutrition = max(H.nutrition - 5, 0) - - if(istype(get_area(H), /area/shadekin)) - if(!src.pain_immunity) - src.pain_immunity = 1 - //Very good healing, but only in the Dark. - H.adjustFireLoss((-0.25)) - H.adjustBruteLoss((-0.25)) - H.adjustToxLoss((-0.25)) - H.heal_organ_damage(3, 0) - H.add_chemical_effect(CE_ANTIBIOTIC, ANTIBIO_SUPER) //CHOMP Edit - increased ANTIBIO from Normal to 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 + addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/living, can_leave_dark)), 5 MINUTES, TIMER_DELETE_ME) else - var/datum/species/shadekin/SK = H.species - if(SK.manual_respite) - to_chat(H, span_notice("As you leave the Dark, you stop focusing the Dark on healing yourself.")) - SK.manual_respite = FALSE - src.expire() - if(src.pain_immunity) - src.pain_immunity = 0 -//CHOMPEdit End + H.add_modifier(/datum/modifier/dark_respite, 25 MINUTES) + + addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/living, enter_the_dark)), 1 SECOND, TIMER_DELETE_ME) + + addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/living, can_leave_dark)), 15 MINUTES, TIMER_DELETE_ME) + + return TRUE + + +/mob/living/proc/enter_the_dark() + var/datum/component/shadekin/SK = get_shadekin_component() + if(!SK) + return + SK.respite_activating = FALSE + SK.in_dark_respite = TRUE + + forceMove(pick(GLOB.latejoin_thedark)) + invisibility = initial(invisibility) + SK.respite_activating = FALSE + +/mob/living/proc/can_leave_dark() + var/datum/component/shadekin/SK = get_shadekin_component() + if(!SK) + return + SK.in_dark_respite = FALSE + to_chat(src, span_notice("You feel like you can leave the Dark again")) /datum/species/shadekin/get_bodytype() return SPECIES_SHADEKIN @@ -270,241 +217,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() - ) - add_verb(H,/mob/living/carbon/human/proc/phase_strength_toggle ) //CHOMPEdit - Add gentle phasing //CHOMPEdit - add_verb(H,/mob/living/carbon/human/proc/nutrition_conversion_toggle ) //CHOMPEdit - Add nutrition conversion toggle //CHOMPEdit - add_verb(H,/mob/living/carbon/human/proc/clear_dark_maws ) //CHOMPEdit - Add Dark maw clearing //CHOMPEdit - -/datum/species/shadekin/proc/handle_shade(var/mob/living/carbon/human/H) - //CHOMPEdit begin - No energy during dark respite - if(H.has_modifier_of_type(/datum/modifier/dark_respite)) - set_energy(H, 0) - update_shadekin_hud(H) - return - //CHOMPEdit End - - //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 - //CHOMPEdit begin - dark in bellies - if(isbelly(H.loc)) - brightness = 0 - //CHOMPEdit end - darkness = 1-brightness //Invert - var/is_dark = (darkness >= 0.5) || istype(get_area(H), /area/shadekin) //CHOMPEdit - Dark provides health - - 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 - //CHOMPEdit begin - Energy <-> nutrition conversion - if(nutrition_energy_conversion && get_energy(H) == 100 && dark_gains > 0) - H.nutrition += dark_gains * 5 * nutrition_conversion_scaling - else if(nutrition_energy_conversion && get_energy(H) < 50 && H.nutrition > 500) - H.nutrition -= nutrition_conversion_scaling * 50 - dark_gains += nutrition_conversion_scaling - //CHOMPEdit end - - 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. - //CHOMPEdit - Dark Respite - if(H.ability_flags & AB_DARK_RESPITE || H.has_modifier_of_type(/datum/modifier/dark_respite)) - return FALSE - //CHOMPEdit - Dark Respite - 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 = 75 //ChompEDIT - balance tweaks - energy_light = 0.5 - energy_dark = 0.5 - nutrition_conversion_scaling = 0.5 //CHOMPEdit - Add nutrition <-> dark energy conversion + total_health = 75 //CHOMPEdit if(RED_EYES) - total_health = 150 //ChompEDIT - balance tweaks - energy_light = -1 - energy_dark = 0.5 //ChompEDIT - nutrition_conversion_scaling = 2 //CHOMPEdit - Add nutrition <-> dark energy conversion + total_health = 150 //CHOMPEdit if(PURPLE_EYES) - total_health = 100 //ChompEDIT - balance tweaks - energy_light = -0.5 - energy_dark = 1 - nutrition_conversion_scaling = 1 //CHOMPEdit - Add nutrition <-> dark energy conversion + total_health = 150 if(YELLOW_EYES) - total_health = 50 //ChompEDIT - balance tweaks - energy_light = -2 - energy_dark = 3 - nutrition_conversion_scaling = 0.5 //CHOMPEdit - Add nutrition <-> dark energy conversion + total_health = 50 //CHOMPEdit if(GREEN_EYES) - total_health = 100 //ChompEDIT - balance tweaks - energy_light = 0.125 - energy_dark = 2 - nutrition_conversion_scaling = 0.5 //CHOMPEdit - Add nutrition <-> dark energy conversion + total_health = 100 if(ORANGE_EYES) - total_health = 125 //ChompEDIT - balance tweaks - energy_light = -0.5 - energy_dark = 0.5 //ChompEDIT - nutrition_conversion_scaling = 1.5 //CHOMPEdit - Add nutrition <-> dark energy conversion + total_health = 125 //CHOMPEdit 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 diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm index 076fbdf98d..0aaff0e9dd 100644 --- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm +++ b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm @@ -1,473 +1,5 @@ -/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" - /* //CHOMPEdit - Moved below //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 - */ //CHOMPEdit 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((get_area(src).flags & PHASE_SHIELDED)) //CHOMPEdit Start - Mapping tools to control phasing - to_chat(src,span_warning("This area is preventing you from phasing!")) - return FALSE - //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 FALSE - //RS Port #658 End //CHOMPEdit End - - 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 - //CHOMPEdit Start - Nerf to phasing - for(var/thing in orange(7, src)) - if(istype(thing, /mob/living/carbon/human)) - var/mob/living/carbon/human/watchers = thing - if((watchers in oviewers(7,src)) && watchers.species != SPECIES_SHADEKIN) // And they can see us... (And aren't themselves a shadekin) - 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! - else if(istype(thing, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/watchers = thing - if(watchers in oviewers(7,src)) - if(!watchers.stat && !isbelly(watchers.loc)) - watcher++ //The robot is watching us! - else if(istype(thing, /obj/machinery/camera)) - var/obj/machinery/camera/watchers = thing - if(watchers.can_use()) - if(src in watchers.can_see()) - watcher++ //CHOMPEdit End - The camera is 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] observers with a cost of [ability_cost] in a darkness level of [darkness]") //CHOMPEdit Start - More clear phase info. - // inform about the observers affecting phasing - if(darkness<=0.4 && watcher>=2) - to_chat(src, span_warning("You have a few observers in a well-lit area! This may prevent phasing. (Working cameras count towards observers)")) - else if(watcher>=3) - to_chat(src, span_warning("You have a large number of observers! This may prevent phasing. (Working cameras count towards observers)")) //CHOMPEdit End - - - var/datum/species/shadekin/SK = species - /* if(!istype(SK)) //CHOMPEdit Removal - Moved to shadekin_ability_check - 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!")) */ //CHOMPEdit End - if(!shadekin_ability_check()) - return FALSE - // Prevent bugs when spamming phase button - 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) - see_invisible_default = initial(see_invisible_default) //CHOMPEdit Add - Allow seeing phased entities while phased. (Port upstream) - incorporeal_move = initial(incorporeal_move) - density = initial(density) - force_max_speed = initial(force_max_speed) - can_pull_size = initial(can_pull_size) //CHOMPEdit Start - Resetting pull ability after phasing back in (Port upstream) - can_pull_mobs = initial(can_pull_mobs) - hovering = initial(hovering) //CHOMPEdit End - 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) - var/datum/species/shadekin/SK = species //CHOMPEdit Add - Eye check. (Port upstream) - canmove = original_canmove - alpha = initial(alpha) - remove_modifiers_of_type(/datum/modifier/shadekin_phase_vision) - remove_modifiers_of_type(/datum/modifier/shadekin_phase) //CHOMPEdit Add - Shadekin probably shouldn't be hit while phasing (Port upstream) - - //Potential phase-in vore - if(can_be_drop_pred || can_be_drop_prey) //Toggleable in vore panel //CHOMPEdit Start - Dropprey and phasevore checks. (Port upstream when possible) - var/list/potentials = living_mobs(0) - 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]!")) - else if(can_be_drop_prey && istype(target) && devourable && target.can_be_drop_pred && target.phase_vore && target.vore_selected && phase_vore) - 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]!")) //CHOMPEdit End - Dropprey and phasevore checks. - - ability_flags &= ~AB_PHASE_SHIFTING - - //Affect nearby lights - var/destroy_lights = 0 - - //CHOMPEdit Start - Add back light destruction & gentle phasing (Port upstream but replace with my variable toggle for full 100% control over chance, color, etc ~Diana) - if(SK.get_shadekin_eyecolor(src) == RED_EYES) - destroy_lights = 80 - else if(SK.get_shadekin_eyecolor(src) == PURPLE_EYES) - destroy_lights = 25 - - // Add gentle phasing - if(SK.phase_gentle) // gentle case: No light destruction. Flicker in 4 tile radius once. - for(var/obj/machinery/light/L in GLOB.machines) - if(L.z != z || get_dist(src,L) > 4) - continue - L.flicker(1) - Stun(1) - else //CHOMPEdit End - 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 - - var/list/allowed_implants = list( //CHOMPEdit Start - Implant dropping - /obj/item/implant/sizecontrol, - /obj/item/implant/compliance, - ) - for(var/obj/item/organ/external/organ in organs) - for(var/obj/item/O in organ.implants) - if(is_type_in_list(O, allowed_implants)) - continue - if(O == nif) - nif.unimplant(src) - O.forceMove(drop_location()) - organ.implants -= O - if(!has_embedded_objects()) - clear_alert("embeddedobject") - //CHOMPEdit End - // change - ability_flags |= AB_PHASE_SHIFTED - ability_flags |= AB_PHASE_SHIFTING - throwpass = TRUE - automatic_custom_emote(VISIBLE_MESSAGE,"phases out!") - name = get_visible_name() - - //CHOMPEdit Start - Unequipping slots when phasing in, and preventing pulling stuff while phased. - if(l_hand) - unEquip(l_hand) - if(r_hand) - unEquip(r_hand) - if(back) - unEquip(back) - - can_pull_size = 0 - can_pull_mobs = MOB_PULL_NONE - hovering = TRUE - //CHOMPEdit End - 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) - add_modifier(/datum/modifier/shadekin_phase) //CHOMPEdit Add - Shadekin probably shouldn't be hit while phasing - 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 - - /* CHOMPEdit Start - general shadekin ability check - 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 - //CHOMPEdit Start - Dark Respite - else if((ability_flags & AB_DARK_RESPITE || has_modifier_of_type(/datum/modifier/dark_respite)) && !(ability_flags & AB_PHASE_SHIFTED)) - to_chat(src, span_warning("You can't use that so soon after an emergency warp!")) - return FALSE - */ - if(!shadekin_ability_check()) - return FALSE - //CHOMPEdit End - 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 - - /* CHOMPEdit Start - general shadekin ability check - 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 - //CHOMPEdit Start - Dark Respite - else if((ability_flags & AB_DARK_RESPITE || has_modifier_of_type(/datum/modifier/dark_respite)) && !(ability_flags & AB_PHASE_SHIFTED)) - to_chat(src, span_warning("You can't use that so soon after an emergency warp!")) - return FALSE - */ - if(!shadekin_ability_check()) - return FALSE - //CHOMPEdit End - 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 - - // 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) ) - return FALSE - - - log_admin("[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()].") - 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()]. (JMP)", 1) - // start the dephase - phase_in(T) - shadekin_adjust_energy(-20) // loss of energy for the interception - // apply a little extra stun for good measure - src.Weaken(3) diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_hud.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_hud.dm index 820fbb0c6b..9749228db1 100644 --- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_hud.dm +++ b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_hud.dm @@ -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) diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_yw.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_yw.dm deleted file mode 100644 index 742e17b410..0000000000 --- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_yw.dm +++ /dev/null @@ -1,95 +0,0 @@ -/datum/species/shadekin_yw - name = SPECIES_SHADEKIN_YW - name_plural = "Shadekin" - icobase = 'icons/mob/human_races/r_shadekin_vr.dmi' - deform = 'icons/mob/human_races/r_shadekin_vr.dmi' - tail = "tail" - icobase_tail = 1 - blurb = "A shadekin, where their powers have been stripped down, their access to the bluespace warp, removed." - catalogue_data = list(/datum/category_item/catalogue/fauna/shadekin) - - language = LANGUAGE_SHADEKIN - assisted_langs = list() - unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/claws/shadekin, /datum/unarmed_attack/bite/sharp/shadekin) - rarity_value = 10 //INTERDIMENSIONAL FLUFFERS - - darksight = 10 - - slowdown = -0.5 - - brute_mod = 0.9 // Naturally sturdy. - burn_mod = 1.2 // Furry - - warning_low_pressure = 50 - hazard_low_pressure = -1 - - warning_high_pressure = 300 - hazard_high_pressure = INFINITY - - cold_level_1 = -1 //Immune to cold - cold_level_2 = -1 - cold_level_3 = -1 - - flags = NO_SCAN - spawn_flags = SPECIES_CAN_JOIN - - reagent_tag = IS_SHADEKIN // for shadekin-unqiue chem interactions - - flesh_color = "#FFC896" - blood_color = "#A10808" - base_color = "#f0f0f0" - color_mult = 1 - - has_glowing_eyes = TRUE - - male_cough_sounds = null - female_cough_sounds = null - //male_sneeze_sound = null //CHOMPStation Edit Disable - //female_sneeze_sound = null //CHOMPStation Edit Disable - - speech_bubble_appearance = "ghost" - - genders = list(PLURAL, NEUTER) //no sexual dymorphism - ambiguous_genders = TRUE //but just in case - - breath_type = null - poison_type = null - - vision_flags = SEE_SELF - appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_SKIN_COLOR | HAS_EYE_COLOR | HAS_UNDERWEAR - - move_trail = /obj/effect/decal/cleanable/blood/tracks/paw - - has_organ = list( - O_HEART = /obj/item/organ/internal/heart, - O_VOICE = /obj/item/organ/internal/voicebox, - O_LIVER = /obj/item/organ/internal/liver, - O_KIDNEYS = /obj/item/organ/internal/kidneys, - O_BRAIN = /obj/item/organ/internal/brain/shadekin, - O_EYES = /obj/item/organ/internal/eyes, - O_STOMACH = /obj/item/organ/internal/stomach, - O_INTESTINE = /obj/item/organ/internal/intestine - ) - - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head/vr/shadekin), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shadekin_yw/get_bodytype() - return SPECIES_SHADEKIN - -/datum/species/shadekin_yw/get_random_name() - return "shadekin" - -/datum/species/shadekin_yw/can_breathe_water() - return TRUE //they dont quite breathe diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_zz_ch.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_zz_ch.dm deleted file mode 100644 index 28b9e61964..0000000000 --- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_zz_ch.dm +++ /dev/null @@ -1,19 +0,0 @@ -//This file is so i can make upates to shadekin and easily push to vorestation, just a main override file - -/datum/species/shadekin/handle_environment_special(var/mob/living/carbon/human/H) - handle_shade(H) - handle_phased(H) - -//Slippery fingers, if we ever make some update that makes all held items non useable for kins we can remove this. -/datum/species/shadekin/proc/handle_phased(var/mob/living/carbon/human/shadekin/H) - if(H.ability_flags & AB_PHASE_SHIFTED) - if(H.l_hand) - H.drop_l_hand() - - if(H.r_hand) - H.drop_r_hand() - - return 1 //we are phased and applied all handling - - else - return 0 //we aren't phased ignore all diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index 6d683049e4..c9fa313750 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -359,6 +359,7 @@ var/food_preference_bonus = 0 var/datum/component/species_component = null // The component that this species uses. Example: Xenochimera use /datum/component/xenochimera + var/component_requires_late_recalc = FALSE // If TRUE, the component will do special recalculation stuff at the end of update_icons_body() // For Lleill and Hanner var/lleill_energy = 200 @@ -595,12 +596,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) diff --git a/code/modules/mob/living/carbon/human/species/species_attack.dm b/code/modules/mob/living/carbon/human/species/species_attack.dm index 0793ed6355..14dbe653f3 100644 --- a/code/modules/mob/living/carbon/human/species/species_attack.dm +++ b/code/modules/mob/living/carbon/human/species/species_attack.dm @@ -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 diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index ce71a722c2..d74458c419 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -376,6 +376,10 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) //see UpdateDamageIcon() update_wing_showing() update_vore_belly_sprite() update_vore_tail_sprite() + if(species && species.component_requires_late_recalc) + var/datum/component/shadekin/SK = GetComponent(/datum/component/shadekin) + if(SK) + SK.recalc_values() /mob/living/carbon/human/proc/update_skin() if(QDESTROYING(src)) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 2974708678..d683c40d11 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -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) @@ -1255,7 +1264,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() diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_modules.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_modules.dm index 2d003a0359..2442bd2c76 100644 --- a/code/modules/mob/living/silicon/robot/dogborg/dog_modules.dm +++ b/code/modules/mob/living/silicon/robot/dogborg/dog_modules.dm @@ -417,9 +417,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)) diff --git a/code/modules/mob/living/silicon/robot/robot_simple_items.dm b/code/modules/mob/living/silicon/robot/robot_simple_items.dm index 28310b2804..9ca16ceb48 100644 --- a/code/modules/mob/living/silicon/robot/robot_simple_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_simple_items.dm @@ -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)) diff --git a/code/modules/mob/living/simple_mob/life.dm b/code/modules/mob/living/simple_mob/life.dm index 0db68c421c..d41fa3c6af 100644 --- a/code/modules/mob/living/simple_mob/life.dm +++ b/code/modules/mob/living/simple_mob/life.dm @@ -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 diff --git a/code/modules/mob/living/simple_mob/simple_hud.dm b/code/modules/mob/living/simple_mob/simple_hud.dm index 63225d7b42..2225906bfc 100644 --- a/code/modules/mob/living/simple_mob/simple_hud.dm +++ b/code/modules/mob/living/simple_mob/simple_hud.dm @@ -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 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm index 5a61d873d1..18cead5ba9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm @@ -109,7 +109,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" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/mobs_monsters/clowns/bus.dm b/code/modules/mob/living/simple_mob/subtypes/vore/mobs_monsters/clowns/bus.dm index b3fbd7b3a5..9c5574afb5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/mobs_monsters/clowns/bus.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/mobs_monsters/clowns/bus.dm @@ -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) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/mobs_monsters/clowns/c_shift.dm b/code/modules/mob/living/simple_mob/subtypes/vore/mobs_monsters/clowns/c_shift.dm index e02e8b9a5f..89ccd56b5b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/mobs_monsters/clowns/c_shift.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/mobs_monsters/clowns/c_shift.dm @@ -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) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_objects.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_objects.dm index 32633d9b29..b39871d468 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_objects.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_objects.dm @@ -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,15 +46,10 @@ if(my_kin.stat) to_chat(my_kin,span_warning("Can't use that ability in your state!")) return FALSE - //CHOMPEdit Begin - Dark Respite - if(my_kin.ability_flags & AB_DARK_RESPITE) - to_chat(src, span_warning("You can't use that so soon after an emergency warp!")) - return FALSE - //CHOMPEdit End - 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) @@ -80,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) @@ -141,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() @@ -172,38 +166,3 @@ if(!..()) return */ - -//CHOMPEdit start - Add dark tunneling ability -/obj/effect/shadekin_ability/dark_tunneling - ability_name = "Dark Tunneling" - desc = "Make a passage to the dark. (Once)" - icon_state = "minion0" - cost = 100 - shift_mode = NOT_WHILE_SHIFTED - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/shadekin_ability/dark_tunneling/do_ability() - if(my_kin.ability_flags & AB_DARK_TUNNEL) - to_chat(src, span_warning("You have already made a tunnel to the Dark!")) - return FALSE - if(!..()) - return - if(!my_kin.dark_tunneling()) - my_kin.comp.dark_energy += cost //Refund due to abort - else - my_kin.comp.dark_energy += 10 //Refund enough to open the dark portal -//CHOMPEdit End - -//CHOMPEdit start - Add Dark Maw ability -/obj/effect/shadekin_ability/dark_maw - ability_name = "Dark Maw" - desc = "Create a trap to capture others, or steal people from phase" - icon_state = "ling_absorb_dna" - cost = 20 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/shadekin_ability/dark_maw/do_ability() - if(!..()) - return - if(!my_kin.dark_maw()) - my_kin.comp.dark_energy += cost //Refund due to abort -//CHOMPEdit End diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm index 524f03eeb8..d795415634 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm @@ -1,20 +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 - //CHOMPAdd Start - if((get_area(src).flags & PHASE_SHIELDED)) - to_chat(src,span_warning("This area is preventing you from phasing!")) - return FALSE - //CHOMPAdd End - //RS Port #658 Start if(!check_rights_for(client, R_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!")) @@ -22,136 +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() - - /* CHOMPRemove Start - //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) - */// CHOMPRemove End - handle_phasein_flicker() // CHOMPEdit, special handle for phase-in light flicker - -/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) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/rakshasa_abilities.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/rakshasa_abilities.dm deleted file mode 100644 index a0b4ab3f05..0000000000 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/rakshasa_abilities.dm +++ /dev/null @@ -1,454 +0,0 @@ -/mob/living/simple_mob/shadekin/Initialize(mapload) - var/list/ability_types = subtypesof(/obj/effect/shadekin_ability) - if(name == "Rakshasa") - ability_types += subtypesof(/obj/effect/rakshasa_ability) - shadekin_abilities = list() - for(var/type in ability_types) - shadekin_abilities += new type(src) - - update_icon() - - return ..() - - -/obj/effect/rakshasa_ability - name = "" - desc = "" - icon = 'icons/mob/screen_spells.dmi' - var/ability_name = "FIX ME" - var/cost = 50 - var/mob/living/simple_mob/shadekin/my_kin - var/shift_mode = NOT_WHILE_SHIFTED - var/ab_sound - -/obj/effect/rakshasa_ability/Initialize(mapload) - . = ..() - my_kin = loc - if(!istype(my_kin)) - return INITIALIZE_HINT_QDEL - loc = null - -/obj/effect/rakshasa_ability/Destroy() - my_kin = null - return ..() - -/obj/effect/rakshasa_ability/proc/atom_button_text() - var/shift_denial - - if(shift_mode == NOT_WHILE_SHIFTED && (my_kin.ability_flags & AB_PHASE_SHIFTED)) - shift_denial = "Physical Only" - else if(shift_mode == ONLY_WHILE_SHIFTED && !(my_kin.ability_flags & AB_PHASE_SHIFTED)) - shift_denial = "Shifted Only" - - if(shift_denial) - name = shift_denial - else - name = my_kin.comp.dark_energy >= cost ? "Activate" : "No Energy" - return src - -/obj/effect/rakshasa_ability/Click(var/location, var/control, var/params) - if(my_kin.stat) return - - var/list/clickprops = params2list(params) - var/opts = clickprops["shift"] - - if(opts) - to_chat(my_kin,span_notice("[name] (Cost: [cost]%) - [desc]")) - else - do_ability(my_kin) - -/obj/effect/rakshasa_ability/proc/do_ability() - 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)) - 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)) - to_chat(my_kin,span_warning("Can only use that ability while phase shifted!")) - return FALSE - else if(my_kin.comp.dark_energy < cost) - to_chat(my_kin,span_warning("Not enough energy for that ability!")) - return FALSE - - my_kin.comp.dark_energy -= cost - if(ab_sound) - playsound(src,ab_sound,75,1) - - return TRUE - - - - -/obj/effect/rakshasa_ability/phase_shift2 - ability_name = "Goo Shift" - desc = "Shift yourself with goopy effects." - icon_state = "tech_passwall" - cost = 100 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/rakshasa_ability/phase_shift2/do_ability() - if(!..()) - return - new /obj/effect/decal/cleanable/blood/oil(loc, src) - playsound(src, 'sound/rakshasa/Corrosion1.ogg', 100, 1) - my_kin.rakshasa_shift() - if(my_kin.ability_flags & AB_PHASE_SHIFTED) - cost = 0 //Shifting back is free (but harmful in light) - new /obj/effect/decal/cleanable/blood/oil(my_kin.loc) - var/goo_sounds = list ( - 'sound/rakshasa/Decay1.ogg', - 'sound/rakshasa/Decay2.ogg', - 'sound/rakshasa/Decay3.ogg' - ) - var/sound = pick(goo_sounds) - playsound(my_kin.loc, sound, 100, 1) - else - cost = initial(cost) - new /obj/effect/decal/cleanable/blood/oil(my_kin.loc) - playsound(my_kin.loc, 'sound/rakshasa/Emerge0.ogg', 100, 1) -///////////////////////////////////////////////////////////////// -/obj/effect/rakshasa_ability/phase_shift3 - ability_name = "Goo Shift No Emerge" - desc = "Shift yourself with goopy effects." - icon_state = "tech_passwall" - cost = 100 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/rakshasa_ability/phase_shift3/do_ability() - if(!..()) - return - new /obj/effect/decal/cleanable/blood/oil(loc, src) - playsound(src, 'sound/rakshasa/Corrosion1.ogg', 100, 1) - my_kin.rakshasa_shift() - if(my_kin.ability_flags & AB_PHASE_SHIFTED) - cost = 0 //Shifting back is free (but harmful in light) - new /obj/effect/decal/cleanable/blood/oil(my_kin.loc) - var/goo_sounds = list ( - 'sound/rakshasa/Decay1.ogg', - 'sound/rakshasa/Decay2.ogg', - 'sound/rakshasa/Decay3.ogg' - ) - var/sound = pick(goo_sounds) - playsound(my_kin.loc, sound, 100, 1) - else - cost = initial(cost) - new /obj/effect/decal/cleanable/blood/oil(my_kin.loc) - var/goo_sounds = list ( - 'sound/rakshasa/Decay1.ogg', - 'sound/rakshasa/Decay2.ogg', - 'sound/rakshasa/Decay3.ogg' - ) - var/sound = pick(goo_sounds) - playsound(my_kin.loc, sound, 100, 1) -///////////////////////////////////////////////////////////////// -/obj/effect/rakshasa_ability/phase_shift4 - ability_name = "Stealth Shift" - desc = "Stealthy. No light effects, no goo." - icon_state = "tech_passwall" - cost = 100 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/rakshasa_ability/phase_shift4/do_ability() - if(!..()) - return - my_kin.stealth_shift() - if(my_kin.ability_flags & AB_PHASE_SHIFTED) - cost = 0 //Shifting back is free (but harmful in light) - else - cost = initial(cost) -///////////////////////////////////////////////////////////////// -/obj/effect/rakshasa_ability/drip_oil - ability_name = "Goo Drip" - desc = "Drip some goo." - icon_state = "" - cost = 0 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/rakshasa_ability/drip_oil/do_ability() - if(!..()) - return - new /obj/effect/decal/cleanable/blood/oil(my_kin.loc) - var/goo_sounds = list ( - 'sound/rakshasa/Decay1.ogg', - 'sound/rakshasa/Decay2.ogg', - 'sound/rakshasa/Decay3.ogg' - ) - var/sound = pick(goo_sounds) - playsound(my_kin.loc, sound, 100, 1) -///////////////////////////////////////////////////////////////// -/obj/effect/rakshasa_ability/laugh - ability_name = "Laugh" - desc = "Laugh." - icon_state = "" - cost = 0 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/rakshasa_ability/laugh/do_ability() - if(!..()) - return - playsound(my_kin.loc, 'sound/rakshasa/Laugh.ogg', 100, 1) -///////////////////////////////////////////////////////////////// -/obj/effect/rakshasa_ability/no - ability_name = "Say No" - desc = "Say no!" - icon_state = "" - cost = 0 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/rakshasa_ability/no/do_ability() - if(!..()) - return - playsound(my_kin.loc, 'sound/rakshasa/No.ogg', 100, 1) -///////////////////////////////////////////////////////////////// -/obj/effect/rakshasa_ability/emergesound - ability_name = "Heavy Breathing" - desc = "Heavy Breathing." - icon_state = "" - cost = 0 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/rakshasa_ability/emergesound/do_ability() - if(!..()) - return - playsound(my_kin.loc, 'sound/rakshasa/Breath1.ogg', 100, 1) -///////////////////////////////////////////////////////////////// -/obj/effect/rakshasa_ability/trapsound - ability_name = "Raspy Breathing" - desc = "Raspy Breathing." - icon_state = "" - cost = 0 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/rakshasa_ability/trapsound/do_ability() - if(!..()) - return - var/goo_sounds = list ( - 'sound/rakshasa/Breath2.ogg' - ) - var/sound = pick(goo_sounds) - playsound(my_kin.loc, sound, 100, 1) -///////////////////////////////////////////////////////////////// -/obj/effect/rakshasa_ability/dripsound - ability_name = "Goop Drip Sound" - desc = "Do the drip sound without actually dripping." - icon_state = "" - cost = 0 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/rakshasa_ability/dripsound/do_ability() - if(!..()) - return - var/goo_sounds = list ( - 'sound/rakshasa/Decay1.ogg', - 'sound/rakshasa/Decay2.ogg', - 'sound/rakshasa/Decay3.ogg', - 'sound/rakshasa/Corrosion1.ogg', - 'sound/rakshasa/Corrosion2.ogg', - 'sound/rakshasa/Corrosion3.ogg' - ) - var/sound = pick(goo_sounds) - playsound(my_kin.loc, sound, 100, 1) - -///////////////////////////////////////////////////////////////// -/obj/effect/rakshasa_ability/trap - ability_name = "Lay Trap" - desc = "Lay a trap that will notify you when someone steps in it." - icon_state = "" - cost = 0 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/rakshasa_ability/trap/do_ability() - if(!..()) - return - var/goo_sounds = list ( - 'sound/rakshasa/Corrosion1.ogg', - 'sound/rakshasa/Corrosion2.ogg', - 'sound/rakshasa/Corrosion3.ogg' - ) - var/sound = pick(goo_sounds) - playsound(my_kin.loc, sound, 100, 1) - new /obj/structure/gootrap (my_kin.loc) - -///////////////////////////////////////////////////////////////// -/obj/effect/rakshasa_ability/flicker - ability_name = "Flicker Lights" - desc = "Flicker the lights." - icon_state = "" - cost = 0 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/rakshasa_ability/flicker/do_ability() - if(!..()) - return - my_kin.rakshasa_flicker() - - -//////////////////////////////////////////////////////////////// -//PROCS -//PHASE SHIFTING -//////////////////////////////////////////////////////////////// -//Rakshasa's phase shifts are now more stealthy. No announcement on them phasing in or out. Also, an extra phaseshift with no lights flickering. For flavor. There's probably a better way of doing this, butt fuck it. -/mob/living/simple_mob/shadekin/proc/rakshasa_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 - name = real_name - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.escapable = initial(B.escapable) - - overlays.Cut() - 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) - sleep(5) //The duration of the TP animation - 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_warning("\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 - 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 - real_name = name - name = "Something" - - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.escapable = FALSE - - overlays.Cut() - 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/proc/stealth_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 - name = real_name - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.escapable = initial(B.escapable) - - overlays.Cut() - 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) - sleep(5) //The duration of the TP animation - 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_warning("\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() - -//Shifting out - else - ability_flags |= AB_PHASE_SHIFTED - real_name = name - name = "Something" - - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.escapable = FALSE - - overlays.Cut() - 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 - - -//LIGHT FLICKERING -//////////////////////////////////////////////////////////////// -//A flicker proc. Because apparently putting this straight into the ability button doesn't work. -/mob/living/simple_mob/shadekin/proc/rakshasa_flicker() - for(var/obj/machinery/light/L in GLOB.machines) - if(L.z != z || get_dist(src,L) > 10) - continue - L.flicker(10) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm index fee09ec497..85603d29be 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm @@ -71,17 +71,12 @@ 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 var/check_timer = 0 var/doing_phase = FALSE // Prevent bugs when spamming phase button - var/respite_activating = FALSE //CHOMPEdit - Dark Respite - var/list/active_dark_maws = list() - /mob/living/simple_mob/shadekin/Initialize(mapload) //You spawned the prototype, and want a totally random one. if(type == /mob/living/simple_mob/shadekin) @@ -100,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") @@ -128,11 +124,6 @@ if(eye_desc) desc += " This one has [eye_desc]!" - var/list/ability_types = subtypesof(/obj/effect/shadekin_ability) - shadekin_abilities = list() - for(var/type in ability_types) - shadekin_abilities += new type(src) - update_icon() add_verb(src, /mob/proc/adjust_hive_range) @@ -204,11 +195,11 @@ /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 - if(. && nutrition > initial(nutrition) && comp.dark_energy < 100 && !(ability_flags | AB_DARK_RESPITE)) //CHOMPEdit - Dark Respite + if(. && nutrition > initial(nutrition) && comp.dark_energy < 100) nutrition = max(0, nutrition-5) comp.dark_energy = min(100,comp.dark_energy+1) if(!client && check_for_observer && check_timer++ > 5) @@ -218,9 +209,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() @@ -233,100 +224,91 @@ add_overlay(tailimage) add_overlay(eye_icon_state) -/mob/living/simple_mob/shadekin/update_misc_tabs() - ..() - var/list/L = list() - for(var/obj/effect/shadekin_ability/A as anything in shadekin_abilities) - var/client/C = client - var/img - if(C && istype(C)) //sanity checks - if(A.ability_name in C.misc_cache) - img = C.misc_cache[A.ability_name] - else - img = icon2html(A,C,sourceonly=TRUE) - C.misc_cache[A.ability_name] = img - - L[++L.len] = list("[A.ability_name]", A.ability_name, img, A.atom_button_text(), REF(A)) - misc_tabs["Shadekin"] = L - //They phase back to the dark when killed /mob/living/simple_mob/shadekin/death(gibbed, deathmessage = "phases to somewhere far away!") - //CHOMPEdit Begin - Dark Respite - if(respite_activating) - return - //CHOMPEdit End - cut_overlays() - flick("tp_out",src) - - //CHOMPEdit Begin - Actually phase to the dark on death - var/area/current_area = get_area(src) - if((ability_flags & AB_DARK_RESPITE) || current_area.flag_check(AREA_LIMIT_DARK_RESPITE)) + var/special_handling = TRUE //varswitch for downstream + if(!special_handling) + cut_overlays() 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) - - src.visible_message("\The [src.name] [deathmessage]") - respite_activating = TRUE - - drop_l_hand() - drop_r_hand() - - comp.dark_energy = 0 - ability_flags |= AB_DARK_RESPITE - 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) - respite_activating = FALSE - belly.owner.handle_belly_update() // CHOMPEdit - clear_fullscreen("belly") - if(hud_used) - if(!hud_used.hud_shown) - toggle_hud_vis() - stop_sound_channel(CHANNEL_PREYLOOP) - - - spawn(10 MINUTES) - ability_flags &= ~AB_DARK_RESPITE - movement_cooldown = initial(movement_cooldown) - to_chat(src, span_notice("You feel like you can leave the Dark again")) + flick("tp_out",src) + QDEL_IN(src, 1 SECOND) + . = ..(FALSE, deathmessage) else - spawn(1 SECOND) - respite_activating = FALSE - forceMove(pick(GLOB.latejoin_thedark)) - update_icon() - flick("tp_in",src) - invisibility = initial(invisibility) - respite_activating = FALSE + 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("\The [src.name] [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/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/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")) - spawn(15 MINUTES) - ability_flags &= ~AB_DARK_RESPITE - movement_cooldown = initial(movement_cooldown) - to_chat(src, span_notice("You feel like you can leave the Dark again")) - //CHOMPEdit End /* //VOREStation AI Temporary Removal //Blue-eyes want to nom people to heal them @@ -342,101 +324,40 @@ //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) + comp.nutrition_conversion_scaling = 0.5 + if(RED_EYES) + comp.set_light_and_darkness(-0.5,0.5) + comp.nutrition_conversion_scaling = 2 + if(PURPLE_EYES) + comp.set_light_and_darkness(-0.5,1) + comp.nutrition_conversion_scaling = 1 + if(YELLOW_EYES) + comp.set_light_and_darkness(-2,3) + comp.nutrition_conversion_scaling = 0.5 + if(GREEN_EYES) + comp.set_light_and_darkness(0.125,2) + comp.nutrition_conversion_scaling = 0.5 + if(ORANGE_EYES) + comp.set_light_and_darkness(-0.25,0.75) + comp.nutrition_conversion_scaling = 1.5 /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)) - - //CHOMPEdit Begin - Dark Respite - if(ability_flags & AB_DARK_RESPITE) - comp.dark_energy = 0 - //CHOMPEdit End - 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() @@ -519,26 +440,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) . = ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types_ch.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types_ch.dm index 003017917f..c2cbd1bfb5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types_ch.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types_ch.dm @@ -56,17 +56,26 @@ The hot goop you float in makes it hard to breathe; all consuming of space and light. \ Your body feels hot, growing weaker, more tired. Draining. Darkness coming. \ You struggle to stay awake while floating helplessly in the goo." - player_msg = "You are Rakshasa. You are not to directly harm crew, only consume. You never speak a language." + player_msg = "You are Rakshasa. You are not to directly harm crew, only consume. You never speak a language. You have special emotes: 'evillaugh', 'evilno', 'evilbreath', 'evilbreath2', and 'goodripsound'" maxHealth = 1000000000000 health = 1000000000000 eye_state = "BLUE EYES" eye_icon_state = "e_rakshasa" + comp = /datum/component/shadekin/full/rakshasa /mob/living/simple_mob/shadekin/red/rakshasa/Initialize(mapload) . = ..() if(comp) comp.dark_energy_infinite = TRUE +/mob/living/simple_mob/shadekin/red/rakshasa/get_available_emotes() + . = global._simple_mob_default_emotes.Copy() + . += /decl/emote/audible/evil_laugh + . += /decl/emote/audible/evil_no + . += /decl/emote/audible/evil_breathing + . += /decl/emote/audible/evil_breathing_2 + . += /decl/emote/audible/goodripsound + /mob/living/simple_mob/shadekin/blue/luna name = "Luna" desc = "She appears to be a fuzzy critter of some sort. Her eyes shimmer a dark blue, glancing around curiously." diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 4d59f9a03f..67749d2383 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -409,6 +409,7 @@ var/global/list/light_type_cache = list() else base_state = "flamp" ..() + /obj/machinery/light/proc/set_alert_atmos() if(!shows_alerts) return @@ -736,27 +737,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 diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index ebfb27f5e0..5963915f77 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -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) diff --git a/code/modules/tables/interactions.dm b/code/modules/tables/interactions.dm index 01f4e18c48..01a8302edd 100644 --- a/code/modules/tables/interactions.dm +++ b/code/modules/tables/interactions.dm @@ -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)) diff --git a/code/modules/tables/tables.dm b/code/modules/tables/tables.dm index 16bdecf8eb..d07c5d24a3 100644 --- a/code/modules/tables/tables.dm +++ b/code/modules/tables/tables.dm @@ -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!")) diff --git a/icons/mob/shadekin_abilities.dmi b/icons/mob/shadekin_abilities.dmi new file mode 100644 index 0000000000..94675194bc Binary files /dev/null and b/icons/mob/shadekin_abilities.dmi differ diff --git a/icons/obj/Shadekin_powers.dmi b/icons/obj/Shadekin_powers.dmi new file mode 100644 index 0000000000..09fc2fe407 Binary files /dev/null and b/icons/obj/Shadekin_powers.dmi differ diff --git a/icons/obj/shadekin_portal.dmi b/icons/obj/shadekin_portal.dmi new file mode 100644 index 0000000000..ca7f1eb07c Binary files /dev/null and b/icons/obj/shadekin_portal.dmi differ diff --git a/maps/submaps/shelters/dark_portal.dmm b/maps/submaps/shelters/dark_portal.dmm new file mode 100644 index 0000000000..3757fe2915 --- /dev/null +++ b/maps/submaps/shelters/dark_portal.dmm @@ -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 +"} diff --git a/modular_chomp/code/game/machinery/bluespace_denier.dm b/modular_chomp/code/game/machinery/bluespace_denier.dm index 8a5f81b2af..59da71752c 100644 --- a/modular_chomp/code/game/machinery/bluespace_denier.dm +++ b/modular_chomp/code/game/machinery/bluespace_denier.dm @@ -57,13 +57,11 @@ last_pulse = world.time use_power(1500) - for (var/mob/O in viewers(src, null)) - if(get_dist(src, O) > range) + for(var/mob/living/O in range(range, src)) + var/datum/component/shadekin/SK = O.get_shadekin_component() + if(!SK) continue - if(ishuman(O)) - var/mob/living/carbon/human/H = O - if(H.get_species() == SPECIES_SHADEKIN && (H.ability_flags & AB_PHASE_SHIFTED)) - H.attack_dephase(null, src) + SK.attack_dephase(null, src) //Won't dephase them if they're not in phase. It has built in checks. /obj/machinery/bluespace_denier/emp_act(severity) if(stat & (BROKEN|NOPOWER)) diff --git a/modular_chomp/code/global.dm b/modular_chomp/code/global.dm index 40f38c63bb..c6a5de4aaa 100644 --- a/modular_chomp/code/global.dm +++ b/modular_chomp/code/global.dm @@ -2,8 +2,3 @@ GLOBAL_LIST_INIT(shell_module_blacklist, list( "Sci-borg", "Research" )) -GLOBAL_LIST_EMPTY(latejoin_gatewaystation) -GLOBAL_LIST_EMPTY(latejoin_plainspath) -GLOBAL_LIST_EMPTY(latejoin_fueldepot) -GLOBAL_LIST_EMPTY(latejoin_tyrvillage) -GLOBAL_LIST_EMPTY(latejoin_thedark) diff --git a/modular_chomp/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm b/modular_chomp/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm deleted file mode 100644 index 3612af699b..0000000000 --- a/modular_chomp/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm +++ /dev/null @@ -1,386 +0,0 @@ -/mob/living/carbon/human/proc/shadekin_ability_check() - var/datum/species/shadekin/SK = species - if(!istype(SK)) - to_chat(src, span_warning("Only a shadekin can use that!")) - return FALSE - - if(stat) - to_chat(src, span_warning("Can't use that ability in your state!")) - return FALSE - - if((ability_flags & AB_DARK_RESPITE || has_modifier_of_type(/datum/modifier/dark_respite)) && !(ability_flags & AB_PHASE_SHIFTED)) - to_chat(src, span_warning("You can't use that so soon after an emergency warp!")) - return FALSE - return TRUE - -//toggle proc for toggling gentle/normal phasing -/mob/living/carbon/human/proc/phase_strength_toggle() - set name = "Toggle Phase Strength" - set desc = "Toggle strength of phase. Gentle but slower, or faster but destructive to lights." - set category = "Abilities.Shadekin" - - var/datum/species/shadekin/SK = species - if(!istype(SK)) - to_chat(src, span_warning("Only a shadekin can use that!")) - return FALSE - - if(SK.phase_gentle) - to_chat(src, span_notice("Phasing toggled to Normal. You may damage lights.")) - SK.phase_gentle = 0 - else - to_chat(src, span_notice("Phasing toggled to Gentle. You won't damage lights, but concentrating on that incurs a short stun.")) - SK.phase_gentle = 1 - -/datum/power/shadekin/dark_tunneling - name = "Dark Tunneling (100) (Once)" - desc = "Make a passage to the dark." - verbpath = /mob/living/carbon/human/proc/dark_tunneling - ability_icon_state = "minion0" - -/mob/living/carbon/human/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 - - if(!shadekin_ability_check()) - return FALSE - - if(ability_flags & AB_PHASE_SHIFTED) - to_chat(src, span_warning("You can't use that while phase shifted!")) - return FALSE - - if(ability_flags & AB_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) - ability_flags |= AB_DARK_TUNNEL - shadekin_adjust_energy(-(ability_cost - 10)) //Leaving enough energy to actually activate the portal - return TRUE - return FALSE - -/datum/power/shadekin/dark_respite - name = "Dark Respite (Only in Dark)" - desc = "Focus yourself on healing any injuries sustained." - verbpath = /mob/living/carbon/human/proc/dark_respite - ability_icon_state = "ling_anatomic_panacea" - -/mob/living/carbon/human/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/species/shadekin/SK = species - if(!istype(SK)) - to_chat(src, span_warning("Only a shadekin can use that!")) - 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(stat) - to_chat(src, span_warning("Can't use that ability in your state!")) - return FALSE - - if(ability_flags & AB_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(ability_flags & AB_PHASE_SHIFTED) - 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/map_template/shelter/dark_portal - name = "Dark Portal" - shelter_id = "dark_portal" - description = "A portal to a section of the Dark" - mappath = "modular_chomp/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)) - -/obj/effect/abstract/dark_maw - var/mob/living/owner = null - var/obj/belly/target = null - icon = 'modular_chomp/icons/obj/Shadekin_powers_2.dmi' - icon_state = "dark_maw_waiting" - -/obj/effect/abstract/dark_maw/Initialize(mapload, var/mob/living/user, var/trigger_now = 0) - . = ..() - - if(!isturf(loc)) - return INITIALIZE_HINT_QDEL - - 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 - - if(istype(user)) - owner = user - target = owner.vore_selected - - var/mob/living/target_user = null - for(var/mob/living/L in T) - if(L != owner && !L.incorporeal_move) - 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 - var/mob/living/carbon/human/carbon_owner = owner - var/mob/living/simple_mob/shadekin/sm_owner = owner - if(istype(carbon_owner)) - var/datum/species/shadekin/SK = carbon_owner.species - if(istype(SK)) - SK.active_dark_maws += src - else if(istype(sm_owner)) - sm_owner.active_dark_maws += src - flick("dark_maw", src) - START_PROCESSING(SSobj, src) - -/obj/effect/abstract/dark_maw/Destroy() - STOP_PROCESSING(SSobj, src) - if(istype(owner)) - var/mob/living/carbon/human/carbon_owner = owner - var/mob/living/simple_mob/shadekin/sm_owner = owner - if(istype(carbon_owner)) - var/datum/species/shadekin/SK = carbon_owner.species - if(istype(SK)) - SK.active_dark_maws -= src - else if(istype(sm_owner)) - sm_owner.active_dark_maws -= src - target = null - owner = 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.incorporeal_move && (!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 = 'modular_chomp/icons/obj/Shadekin_powers_2.dmi' - icon_state = "dark_net" - - escape_time = 30 SECONDS - -/obj/effect/energy_net/dark/user_unbuckle_mob(mob/living/buckled_mob, mob/user) - if(istype(user,/mob/living/simple_mob/shadekin)) - visible_message(span_danger("[user] dissipates \the [src] with a touch!")) - unbuckle_mob(buckled_mob) - return - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - var/datum/species/shadekin/SK = H.species - if(istype(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) - -/datum/power/shadekin/dark_maw - name = "Dark Maw (20)" - desc = "Create a trap to capture others, or steal people from phase" - verbpath = /mob/living/carbon/human/proc/dark_maw - ability_icon_state = "dark_maw_ic" - -/mob/living/carbon/human/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 - - if(!shadekin_ability_check()) - return FALSE - - if(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(ability_flags & AB_PHASE_SHIFTED) - new /obj/effect/abstract/dark_maw(loc, src, 1) - else - new /obj/effect/abstract/dark_maw(loc, src) - shadekin_adjust_energy(-ability_cost) - - return TRUE - return FALSE - -/mob/living/carbon/human/proc/clear_dark_maws() - set name = "Dispel dark maws" - set desc = "Dispel any active dark maws in place" - set category = "Abilities.Shadekin" - - var/datum/species/shadekin/SK = species - if(!istype(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() - -/mob/living/carbon/human/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/species/shadekin/SK = species - if(!istype(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 - -/datum/modifier/shadekin_phase - name = "Shadekin Phasing" - evasion = 100 diff --git a/modular_chomp/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm b/modular_chomp/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm deleted file mode 100644 index 05d339e91f..0000000000 --- a/modular_chomp/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm +++ /dev/null @@ -1,113 +0,0 @@ -/mob/living/simple_mob/shadekin - var/phase_gentle = 0 - -/mob/living/simple_mob/shadekin/Login() - . = ..() - add_verb(src,/mob/living/simple_mob/shadekin/proc/phase_strength_toggle) - -/mob/living/simple_mob/shadekin/proc/dark_tunneling() - var/template_id = "dark_portal" - var/datum/map_template/shelter/template - - 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, 600)) //60 seconds - 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) - ability_flags &= AB_DARK_TUNNEL - return TRUE - else - return FALSE - -/mob/living/simple_mob/shadekin/proc/dark_maw() - 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 - else 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(ability_flags & AB_PHASE_SHIFTED) - new /obj/effect/abstract/dark_maw(loc, src, 1) - else - new /obj/effect/abstract/dark_maw(loc, src) - - return TRUE - else - return FALSE - -// Allow horizontal resting -/mob/living/simple_mob/shadekin/update_transform() - update_transform_horizontal() - -//custom light flicker proc -/mob/living/simple_mob/shadekin/proc/handle_phasein_flicker() - if(phase_gentle) // gentle case: No light destruction. Flicker in 4 tile radius for 3s. Weaken for 3sec after - for(var/obj/machinery/light/L in GLOB.machines) - if(L.z != z || get_dist(src,L) > 4) - continue - L.flicker(3) - src.Stun(3) - else //normal case. Flicker in 10 tile radius for 10s. chance to destroy light based on eye type. - 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) - -//toggle proc for toggling gentle/normal phasing -/mob/living/simple_mob/shadekin/proc/phase_strength_toggle() - set name = "Toggle Phase Strength" - set desc = "Toggle strength of phase. Gentle but slower, or faster but destructive to lights." - set category = "Abilities.Shadekin" - - if(phase_gentle) - to_chat(src, span_notice("Phasing toggled to Normal. You may damage lights.")) - phase_gentle = 0 - else - to_chat(src, span_notice("Phasing toggled to Gentle. You won't damage lights, but concentrating on that incurs a short stun.")) - phase_gentle = 1 diff --git a/modular_chomp/code/modules/telesci/bscrystal.dm b/modular_chomp/code/modules/telesci/bscrystal.dm index c4a6b3db4a..495dcb64bf 100644 --- a/modular_chomp/code/modules/telesci/bscrystal.dm +++ b/modular_chomp/code/modules/telesci/bscrystal.dm @@ -1,24 +1,24 @@ // This proc turns the BSC or arti-BSC into a phased-creature mine. /obj/item/bluespace_crystal/Crossed(atom/movable/M) . = ..() - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(H.get_species() == SPECIES_SHADEKIN && (H.ability_flags & AB_PHASE_SHIFTED)) + if(istype(M, /mob/living)) + var/mob/living/L = M + var/datum/component/shadekin/SK = L.get_shadekin_component() + if(SK && SK.in_phase) var/turf/T = get_turf(src) visible_message(span_notice("[src] fizzles and disappears as something interacts with it!")) playsound(src, pick('sound/effects/Glassbr1.ogg', 'sound/effects/Glassbr2.ogg', 'sound/effects/Glassbr3.ogg'), 50, 1) var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() s.set_up(5, 1, T) s.start() - H.attack_dephase(T, src) + SK.attack_dephase(T, src) qdel(src) // This proc is the 'Dephase grenade' check. range is changeable. 0=self, 1=3x3, 2=5x5, 3=7x7... /obj/item/bluespace_crystal/proc/dephase_shadekin() var/turf/T = get_turf(src) - for(var/thing in range(3, T)) - if(istype(thing, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = thing - if(H.get_species() == SPECIES_SHADEKIN && (H.ability_flags & AB_PHASE_SHIFTED)) - H.attack_dephase(null, src) + for(var/mob/living/living in range(3, T)) + var/datum/component/shadekin/SK = living.get_shadekin_component() + if(SK && SK.in_phase) + SK.attack_dephase(null, src) diff --git a/modular_chomp/maps/southern_cross/southern_cross_overrides.dm b/modular_chomp/maps/southern_cross/southern_cross_overrides.dm index 6715b3475a..6e408953c5 100644 --- a/modular_chomp/maps/southern_cross/southern_cross_overrides.dm +++ b/modular_chomp/maps/southern_cross/southern_cross_overrides.dm @@ -17,20 +17,6 @@ . = ..() GLOB.blacklisted_areas = typecacheof(list(/area/centcom, /area/shadekin, /area/vr)) -/mob/living/carbon/human/shadekin_ability_check() - . = ..() - if(. && istype(get_area(src), /area/vr)) - to_chat(src, span_danger("The VR systems cannot comprehend this power! This is useless to you!")) - . = FALSE - -/obj/effect/shadekin_ability/dark_tunneling/do_ability() - if(istype(get_area(my_kin), /area/vr)) - to_chat(my_kin, span_danger("The VR systems cannot comprehend this power! This is useless to you!")) - return FALSE - else - ..() - - /obj/item/disposable_teleporter/attack_self(mob/user as mob)//Prevents people from using technomancer gear to escape to station from the VR pods. if(istype(get_area(user), /area/vr)) to_chat(user, span_danger("\The [src] does not appear to work in VR! This is useless to you!")) diff --git a/sound/mob/spooky/breath1.ogg b/sound/mob/spooky/breath1.ogg new file mode 100644 index 0000000000..463c299896 Binary files /dev/null and b/sound/mob/spooky/breath1.ogg differ diff --git a/sound/mob/spooky/breath2.ogg b/sound/mob/spooky/breath2.ogg new file mode 100644 index 0000000000..a3a3c2c663 Binary files /dev/null and b/sound/mob/spooky/breath2.ogg differ diff --git a/sound/mob/spooky/corrosion1.ogg b/sound/mob/spooky/corrosion1.ogg new file mode 100644 index 0000000000..912e535639 Binary files /dev/null and b/sound/mob/spooky/corrosion1.ogg differ diff --git a/sound/mob/spooky/corrosion2.ogg b/sound/mob/spooky/corrosion2.ogg new file mode 100644 index 0000000000..dfd24cc9d8 Binary files /dev/null and b/sound/mob/spooky/corrosion2.ogg differ diff --git a/sound/mob/spooky/corrosion3.ogg b/sound/mob/spooky/corrosion3.ogg new file mode 100644 index 0000000000..cef4bf5c2d Binary files /dev/null and b/sound/mob/spooky/corrosion3.ogg differ diff --git a/sound/mob/spooky/decay1.ogg b/sound/mob/spooky/decay1.ogg new file mode 100644 index 0000000000..e65a838e48 Binary files /dev/null and b/sound/mob/spooky/decay1.ogg differ diff --git a/sound/mob/spooky/decay2.ogg b/sound/mob/spooky/decay2.ogg new file mode 100644 index 0000000000..b1e0a10e2f Binary files /dev/null and b/sound/mob/spooky/decay2.ogg differ diff --git a/sound/mob/spooky/decay3.ogg b/sound/mob/spooky/decay3.ogg new file mode 100644 index 0000000000..cfe8b52925 Binary files /dev/null and b/sound/mob/spooky/decay3.ogg differ diff --git a/sound/mob/spooky/emerge.ogg b/sound/mob/spooky/emerge.ogg new file mode 100644 index 0000000000..bf665de908 Binary files /dev/null and b/sound/mob/spooky/emerge.ogg differ diff --git a/sound/mob/spooky/laugh.ogg b/sound/mob/spooky/laugh.ogg new file mode 100644 index 0000000000..13b6bc8e5d Binary files /dev/null and b/sound/mob/spooky/laugh.ogg differ diff --git a/sound/mob/spooky/no.ogg b/sound/mob/spooky/no.ogg new file mode 100644 index 0000000000..1b835e46bc Binary files /dev/null and b/sound/mob/spooky/no.ogg differ diff --git a/tgui/packages/tgui/interfaces/ShadekinConfig.tsx b/tgui/packages/tgui/interfaces/ShadekinConfig.tsx new file mode 100644 index 0000000000..a50931a6cf --- /dev/null +++ b/tgui/packages/tgui/interfaces/ShadekinConfig.tsx @@ -0,0 +1,132 @@ +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { + Box, + Button, + ColorBox, + LabeledList, + NoticeBox, + NumberInput, + Section, + Stack, + Tooltip, +} from 'tgui-core/components'; + +type Data = { + stun_time: number; + flicker_time: number; + flicker_color: string | null; + flicker_break_chance: number; + flicker_distance: number; +}; + +export const ShadekinConfig = (props) => { + const { act, data } = useBackend(); + + const { + stun_time, + flicker_time, + flicker_color, + flicker_break_chance, + flicker_distance, + } = data; + + const isSubtle = + flicker_time < 5 || flicker_break_chance < 5 || flicker_distance < 5; + + return ( + + + + {isSubtle && ( + + Subtle Phasing, causes {stun_time} s stun. + + )} + +
+ + + + + act('adjust_time', { val: value })} + unit="x" + /> + + + + ? + + + + + + + + + + + + + + + + + + + act('adjust_break', { val: value }) + } + unit="%" + /> + + + + ? + + + + + + + + + act('adjust_distance', { val: value }) + } + unit="tiles" + /> + + + + ? + + + + + +
+
+
+
+
+ ); +}; diff --git a/vorestation.dme b/vorestation.dme index 3354b9fd11..60fb590346 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -611,8 +611,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" @@ -2333,6 +2341,7 @@ #include "code\modules\client\preferences\types\character\antagonism\01_basic.dm" #include "code\modules\client\preferences\types\character\general\01_basic.dm" #include "code\modules\client\preferences\types\character\general\03_body.dm" +#include "code\modules\client\preferences\types\character\general\06_special.dm" #include "code\modules\client\preferences\types\game\admin.dm" #include "code\modules\client\preferences\types\game\adult.dm" #include "code\modules\client\preferences\types\game\auto_fit_viewport.dm" @@ -3357,7 +3366,6 @@ #include "code\modules\mob\living\carbon\human\species\outsider\shadow.dm" #include "code\modules\mob\living\carbon\human\species\outsider\skeleton.dm" #include "code\modules\mob\living\carbon\human\species\outsider\vox.dm" -#include "code\modules\mob\living\carbon\human\species\shadekin\dark_portal_ch.dm" #include "code\modules\mob\living\carbon\human\species\shadekin\shadekin.dm" #include "code\modules\mob\living\carbon\human\species\shadekin\shadekin_abilities.dm" #include "code\modules\mob\living\carbon\human\species\shadekin\shadekin_hud.dm" @@ -3783,9 +3791,7 @@ #include "code\modules\mob\living\simple_mob\subtypes\vore\mobs_monsters\clowns\honkelemental.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\mobs_monsters\clowns\regularclowns.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\morph\morph.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\ability_objects.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\ability_procs.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\rakshasa_abilities.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\rakshasa_trap.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\shadekin.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\types.dm" @@ -5044,7 +5050,6 @@ #include "modular_chomp\code\modules\mob\living\carbon\human\species\species.dm" #include "modular_chomp\code\modules\mob\living\carbon\human\species\species_shapeshift_ch.dm" #include "modular_chomp\code\modules\mob\living\carbon\human\species\outsider\vox.dm" -#include "modular_chomp\code\modules\mob\living\carbon\human\species\shadekin\shadekin_abilities.dm" #include "modular_chomp\code\modules\mob\living\carbon\human\species\station\prommie_blob.dm" #include "modular_chomp\code\modules\mob\living\carbon\human\species\station\species_attack.dm" #include "modular_chomp\code\modules\mob\living\carbon\human\species\station\station.dm" @@ -5143,7 +5148,6 @@ #include "modular_chomp\code\modules\mob\living\simple_mob\subtypes\vore\xeyakin.dm" #include "modular_chomp\code\modules\mob\living\simple_mob\subtypes\vore\gateway\candy.dm" #include "modular_chomp\code\modules\mob\living\simple_mob\subtypes\vore\plants\pitcher.dm" -#include "modular_chomp\code\modules\mob\living\simple_mob\subtypes\vore\shadekin\shadekin.dm" #include "modular_chomp\code\modules\mob\new_player\sprite_accessories.dm" #include "modular_chomp\code\modules\mob\new_player\sprite_accessories_ear.dm" #include "modular_chomp\code\modules\mob\new_player\sprite_accessories_extra.dm"