initial commit
@@ -26,6 +26,7 @@
|
||||
#define ROLE_SERVANT_OF_RATVAR "servant of Ratvar"
|
||||
#define ROLE_BROTHER "blood brother"
|
||||
#define ROLE_BRAINWASHED "brainwashed victim"
|
||||
#define ROLE_SHADOWLING "shadowling" // Yogs
|
||||
|
||||
//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR.
|
||||
//The gamemode specific ones are just so the gamemodes can query whether a player is old enough
|
||||
@@ -47,7 +48,8 @@ GLOBAL_LIST_INIT(special_roles, list(
|
||||
ROLE_REVENANT,
|
||||
ROLE_ABDUCTOR,
|
||||
ROLE_DEVIL = /datum/game_mode/devil,
|
||||
ROLE_SERVANT_OF_RATVAR = /datum/game_mode/clockwork_cult
|
||||
ROLE_SERVANT_OF_RATVAR = /datum/game_mode/clockwork_cult,
|
||||
ROLE_SHADOWLING = /datum/game_mode/shadowling
|
||||
))
|
||||
|
||||
//Job defines for what happens when you fail to qualify for any job during job selection
|
||||
|
||||
2
code/__DEFINES/~yogs_defines/antagonists.dm
Normal file
@@ -0,0 +1,2 @@
|
||||
#define ANTAG_DATUM_THRALL /datum/antagonist/thrall
|
||||
#define ANTAG_DATUM_SLING /datum/antagonist/shadowling
|
||||
3
code/__DEFINES/~yogs_defines/is_helpers.dm
Normal file
@@ -0,0 +1,3 @@
|
||||
#define is_thrall(M) (istype(M, /mob/living) && M.mind && M.mind.has_antag_datum(/datum/antagonist/thrall))
|
||||
#define is_shadow(M) (istype(M, /mob/living) && M.mind && M.mind.has_antag_datum(/datum/antagonist/shadowling))
|
||||
#define is_shadow_or_thrall(M) (is_thrall(M) || is_shadow(M))
|
||||
@@ -22,6 +22,7 @@
|
||||
<a href='?src=[REF(src)];[HrefToken()];makeAntag=centcom'>Make CentCom Response Team (Requires Ghosts)</a><br>
|
||||
<a href='?src=[REF(src)];[HrefToken()];makeAntag=abductors'>Make Abductor Team (Requires Ghosts)</a><br>
|
||||
<a href='?src=[REF(src)];[HrefToken()];makeAntag=revenant'>Make Revenant (Requires Ghost)</a><br>
|
||||
<a href='?src=[REF(src)];[HrefToken()];makeAntag=shadowling'>Make Shadowling</a><br>
|
||||
"}
|
||||
|
||||
var/datum/browser/popup = new(usr, "oneclickantag", "Quick-Create Antagonist", 400, 400)
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
var/light_burn_msg = "numb"
|
||||
var/medium_burn_msg = "blistered"
|
||||
var/heavy_burn_msg = "peeling away"
|
||||
|
||||
|
||||
var/bandaged // yogs - FALSE if the limb is not bandaged, TRUE if the limb is bandaged
|
||||
|
||||
/obj/item/bodypart/examine(mob/user)
|
||||
@@ -396,6 +396,12 @@
|
||||
limb.icon_state = "[species_id]_[body_zone]_[icon_gender]"
|
||||
else
|
||||
limb.icon_state = "[species_id]_[body_zone]"
|
||||
if(should_draw_yogs) //yogs start
|
||||
limb.icon = 'yogstation/icons/mob/mutant_bodyparts.dmi'
|
||||
if(should_draw_gender)
|
||||
limb.icon_state = "[species_id]_[body_zone]_[icon_gender]"
|
||||
else
|
||||
limb.icon_state = "[species_id]_[body_zone]" //yogs end
|
||||
if(aux_zone)
|
||||
aux = image(limb.icon, "[species_id]_[aux_zone]", -aux_layer, image_dir)
|
||||
. += aux
|
||||
|
||||
@@ -102,8 +102,10 @@
|
||||
#include "code\__DEFINES\wires.dm"
|
||||
#include "code\__DEFINES\~yogs_defines\access.dm"
|
||||
#include "code\__DEFINES\~yogs_defines\admin.dm"
|
||||
#include "code\__DEFINES\~yogs_defines\antagonists.dm"
|
||||
#include "code\__DEFINES\~yogs_defines\components.dm"
|
||||
#include "code\__DEFINES\~yogs_defines\flags.dm"
|
||||
#include "code\__DEFINES\~yogs_defines\is_helpers.dm"
|
||||
#include "code\__DEFINES\~yogs_defines\jobs.dm"
|
||||
#include "code\__DEFINES\~yogs_defines\keybindings.dm"
|
||||
#include "code\__DEFINES\~yogs_defines\preferences.dm"
|
||||
@@ -2695,10 +2697,12 @@
|
||||
#include "yogstation\code\datums\components\walks.dm"
|
||||
#include "yogstation\code\datums\diseases\advance\symptoms\confusion.dm"
|
||||
#include "yogstation\code\datums\diseases\advance\symptoms\heal.dm"
|
||||
#include "yogstation\code\datums\mood_events\generic_positive_events.dm"
|
||||
#include "yogstation\code\datums\ruins\free_miners.dm"
|
||||
#include "yogstation\code\datums\ruins\station.dm"
|
||||
#include "yogstation\code\game\world.dm"
|
||||
#include "yogstation\code\game\area\Space_Station_13_areas.dm"
|
||||
#include "yogstation\code\game\gamemodes\shadowling\shadowling.dm"
|
||||
#include "yogstation\code\game\gamemodes\traitor\double_agents.dm"
|
||||
#include "yogstation\code\game\gamemodes\wizard\wizard.dm"
|
||||
#include "yogstation\code\game\machinery\computer\arcade.dm"
|
||||
@@ -2735,8 +2739,15 @@
|
||||
#include "yogstation\code\modules\admin\verbs\adminhelp.dm"
|
||||
#include "yogstation\code\modules\admin\verbs\adminsay.dm"
|
||||
#include "yogstation\code\modules\admin\verbs\fix_air.dm"
|
||||
#include "yogstation\code\modules\admin\verbs\one_click_antag.dm"
|
||||
#include "yogstation\code\modules\antagonists\_common\antag_datum.dm"
|
||||
#include "yogstation\code\modules\antagonists\abductor\equipment\abduction_outfits.dm"
|
||||
#include "yogstation\code\modules\antagonists\shadowling\ascendant_shadowling.dm"
|
||||
#include "yogstation\code\modules\antagonists\shadowling\shadowling.dm"
|
||||
#include "yogstation\code\modules\antagonists\shadowling\shadowling_abilities.dm"
|
||||
#include "yogstation\code\modules\antagonists\shadowling\shadowling_items.dm"
|
||||
#include "yogstation\code\modules\antagonists\shadowling\special_shadowling_abilities.dm"
|
||||
#include "yogstation\code\modules\antagonists\shadowling\thrall.dm"
|
||||
#include "yogstation\code\modules\antagonists\slaughter\slaughter.dm"
|
||||
#include "yogstation\code\modules\antagonists\swarmer\swarmer_event.dm"
|
||||
#include "yogstation\code\modules\atmospherics\unary_devices\vent_pump.dm"
|
||||
@@ -2785,6 +2796,7 @@
|
||||
#include "yogstation\code\modules\mob\say.dm"
|
||||
#include "yogstation\code\modules\mob\dead\new_player\new_player.dm"
|
||||
#include "yogstation\code\modules\mob\living\carbon\human\life.dm"
|
||||
#include "yogstation\code\modules\mob\living\carbon\human\species.dm"
|
||||
#include "yogstation\code\modules\mob\living\carbon\human\species_types\plantpeople.dm"
|
||||
#include "yogstation\code\modules\mob\living\silicon\silicon.dm"
|
||||
#include "yogstation\code\modules\mob\living\silicon\ai\ai.dm"
|
||||
@@ -2807,6 +2819,7 @@
|
||||
#include "yogstation\code\modules\stock_market\logs.dm"
|
||||
#include "yogstation\code\modules\stock_market\stockmarket.dm"
|
||||
#include "yogstation\code\modules\stock_market\stocks.dm"
|
||||
#include "yogstation\code\modules\surgery\organs\shadowling_organs.dm"
|
||||
#include "yogstation\code\modules\uplink\uplink_item.dm"
|
||||
#include "yogstation\code\modules\webhook\webhook.dm"
|
||||
#include "yogstation\interface\interface.dm"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/datum/mood_event/thrall
|
||||
description = "<span class='shadowling'><i>The darkness is true. I love them. I love the masters.</i></span>\n"
|
||||
mood_change = 40
|
||||
|
||||
/datum/mood_event/sling
|
||||
description = "<span class='shadowling'><b>The keys to reality are within my grasp.</b></span>\n"
|
||||
mood_change = 20
|
||||
244
yogstation/code/game/gamemodes/shadowling/shadowling.dm
Normal file
@@ -0,0 +1,244 @@
|
||||
#define LIGHT_HEAL_THRESHOLD 2
|
||||
#define LIGHT_DAMAGE_TAKEN 7
|
||||
#define LIGHT_DAM_THRESHOLD 0.25
|
||||
|
||||
/*
|
||||
SHADOWLING: A gamemode based on previously-run events
|
||||
Aliens called shadowlings are on the station.
|
||||
These shadowlings can 'enthrall' crew members and enslave them.
|
||||
They also burn in the light but heal rapidly whilst in the dark.
|
||||
The game will end under two conditions:
|
||||
1. The shadowlings die
|
||||
2. The emergency shuttle docks at CentCom
|
||||
Shadowling strengths:
|
||||
- The dark
|
||||
- Hard vacuum (They are not affected by it, but are affected by starlight!)
|
||||
- Their thralls who are not harmed by the light
|
||||
- Stealth
|
||||
Shadowling weaknesses:
|
||||
- The light
|
||||
- Fire
|
||||
- Enemy numbers
|
||||
- Burn-based weapons and items (flashbangs, lasers, etc.)
|
||||
Shadowlings start off disguised as normal crew members, and they only have two abilities: Hatch and Enthrall.
|
||||
They can still enthrall and perhaps complete their objectives in this form.
|
||||
Hatch will, after a short time, cast off the human disguise and assume the shadowling's true identity.
|
||||
They will then assume the normal shadowling form and gain their abilities.
|
||||
The shadowling will seem OP, and that's because it kinda is. Being restricted to the dark while being alone most of the time is extremely difficult and as such the shadowling needs powerful abilities.
|
||||
Made by Xhuis
|
||||
*/
|
||||
/*
|
||||
GAMEMODE
|
||||
*/
|
||||
/datum/game_mode
|
||||
var/list/datum/mind/shadows = list()
|
||||
var/list/datum/mind/thralls = list()
|
||||
var/required_thralls = 15 //How many thralls are needed (this is changed in pre_setup, so it scales based on pop)
|
||||
var/shadowling_ascended = FALSE //If at least one shadowling has ascended
|
||||
var/thrall_ratio = 1
|
||||
|
||||
/datum/game_mode/proc/replace_jobbaned_player(mob/living/M, role_type, pref)
|
||||
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [role_type]?", "[role_type]", null, pref, 50, M)
|
||||
var/mob/dead/observer/theghost = null
|
||||
if(candidates.len)
|
||||
theghost = pick(candidates)
|
||||
to_chat(M, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
|
||||
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbaned player.")
|
||||
M.ghostize(0)
|
||||
M.key = theghost.key
|
||||
|
||||
/datum/game_mode/shadowling
|
||||
name = "shadowling"
|
||||
config_tag = "shadowling"
|
||||
antag_flag = ROLE_SHADOWLING
|
||||
required_players = 26
|
||||
required_enemies = 3
|
||||
recommended_enemies = 3
|
||||
enemy_minimum_age = 14
|
||||
restricted_jobs = list("AI", "Cyborg")
|
||||
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain")
|
||||
|
||||
/datum/game_mode/shadowling/announce()
|
||||
to_chat(world, "<b>The current game mode is - Shadowling!</b>")
|
||||
to_chat(world, "<b>There are alien <span class='shadowling'>shadowlings</span> on the station. Crew: Kill the shadowlings before they can enthrall the crew. Shadowlings: Enthrall the crew while remaining in hiding.</b>")
|
||||
|
||||
/datum/game_mode/shadowling/pre_setup()
|
||||
if(CONFIG_GET(flag/protect_roles_from_antagonist))
|
||||
restricted_jobs += protected_jobs
|
||||
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
|
||||
restricted_jobs += "Assistant"
|
||||
var/shadowlings = max(3, round(num_players()/14))
|
||||
while(shadowlings)
|
||||
var/datum/mind/shadow = pick(antag_candidates)
|
||||
shadows += shadow
|
||||
antag_candidates -= shadow
|
||||
shadow.special_role = "Shadowling"
|
||||
shadow.restricted_roles = restricted_jobs
|
||||
shadowlings--
|
||||
var/thrall_scaling = round(num_players() / 3)
|
||||
required_thralls = CLAMP(thrall_scaling, 15, 30)
|
||||
thrall_ratio = required_thralls / 15
|
||||
return TRUE
|
||||
|
||||
/datum/game_mode/shadowling/generate_report()
|
||||
return "Sightings of strange alien creatures have been observed in your area. These aliens supposedly possess the ability to enslave unwitting personnel and leech from their power. \
|
||||
Be wary of dark areas and ensure all lights are kept well-maintained. Closely monitor all crew for suspicious behavior and perform dethralling surgery if they have obvious tells. Investigate all \
|
||||
reports of odd or suspicious sightings in maintenance."
|
||||
|
||||
/datum/game_mode/shadowling/post_setup()
|
||||
for(var/T in shadows)
|
||||
var/datum/mind/shadow = T
|
||||
log_game("[shadow.key] (ckey) has been selected as a Shadowling.")
|
||||
shadow.current.add_sling()
|
||||
. = ..()
|
||||
return
|
||||
|
||||
/datum/game_mode/shadowling/proc/check_shadow_victory()
|
||||
return shadowling_ascended
|
||||
|
||||
/datum/game_mode/shadowling/proc/check_shadow_death()
|
||||
for(var/SM in get_antag_minds(/datum/antagonist/shadowling))
|
||||
var/datum/mind/shadow_mind = SM
|
||||
if(istype(shadow_mind))
|
||||
var/turf/T = get_turf(shadow_mind.current)
|
||||
if((shadow_mind) && (shadow_mind.current.stat != DEAD) && T && is_station_level(T.z) && ishuman(shadow_mind.current))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/game_mode/shadowling/check_finished()
|
||||
. = ..()
|
||||
if(check_shadow_death())
|
||||
return TRUE
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_shadowling()
|
||||
var/text = ""
|
||||
if(shadows.len)
|
||||
text += "<br><span class='big'><b>The shadowlings were:</b></span>"
|
||||
for(var/S in shadows)
|
||||
var/datum/mind/shadow = S
|
||||
text += printplayer(shadow)
|
||||
text += "<br>"
|
||||
if(thralls.len)
|
||||
text += "<br><span class='big'><b>The thralls were:</b></span>"
|
||||
for(var/T in thralls)
|
||||
var/datum/mind/thrall = T
|
||||
text += printplayer(thrall)
|
||||
text += "<br>"
|
||||
to_chat(world, text)
|
||||
|
||||
/*
|
||||
MISCELLANEOUS
|
||||
*/
|
||||
/datum/species/shadow/ling
|
||||
//Normal shadowpeople but with enhanced effects
|
||||
name = "Shadowling"
|
||||
id = "shadowling"
|
||||
say_mod = "chitters"
|
||||
species_traits = list(NOBLOOD,NO_UNDERWEAR,NO_DNA_COPY,NOTRANSSTING,NOEYES)
|
||||
inherent_traits = list(TRAIT_NOGUNS, TRAIT_RESISTCOLD, TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE, TRAIT_NOBREATH, TRAIT_RADIMMUNE, TRAIT_VIRUSIMMUNE, TRAIT_PIERCEIMMUNE)
|
||||
no_equip = list(SLOT_WEAR_MASK, SLOT_GLASSES, SLOT_GLOVES, SLOT_SHOES, SLOT_W_UNIFORM, SLOT_S_STORE)
|
||||
nojumpsuit = TRUE
|
||||
mutanteyes = /obj/item/organ/eyes/night_vision/alien/sling
|
||||
burnmod = 1.5 //1.5x burn damage, 2x is excessive
|
||||
heatmod = 1.5
|
||||
var/mutable_appearance/eyes_overlay
|
||||
var/shadow_charges = 3
|
||||
var/last_charge = 0
|
||||
|
||||
/datum/species/shadow/ling/on_species_gain(mob/living/carbon/human/C)
|
||||
C.draw_yogs_parts(TRUE)
|
||||
eyes_overlay = mutable_appearance('yogstation/icons/mob/sling.dmi', "eyes", 25)
|
||||
C.add_overlay(eyes_overlay)
|
||||
. = ..()
|
||||
|
||||
/datum/species/shadow/ling/on_species_loss(mob/living/carbon/human/C)
|
||||
C.draw_yogs_parts(FALSE)
|
||||
if(eyes_overlay)
|
||||
C.cut_overlay(eyes_overlay)
|
||||
QDEL_NULL(eyes_overlay)
|
||||
. = ..()
|
||||
|
||||
/datum/species/shadow/ling/spec_life(mob/living/carbon/human/H)
|
||||
H.nutrition = NUTRITION_LEVEL_WELL_FED //i aint never get hongry
|
||||
if(isturf(H.loc))
|
||||
var/turf/T = H.loc
|
||||
var/light_amount = T.get_lumcount()
|
||||
if(light_amount > LIGHT_DAM_THRESHOLD) //Can survive in very small light levels. Also doesn't take damage while incorporeal, for shadow walk purposes
|
||||
H.take_overall_damage(0, LIGHT_DAMAGE_TAKEN)
|
||||
if(H.stat != DEAD)
|
||||
to_chat(H, "<span class='userdanger'>The light burns you!</span>") //Message spam to say "GET THE FUCK OUT"
|
||||
H.playsound_local(get_turf(H), 'sound/weapons/sear.ogg', 150, 1, pressure_affected = FALSE)
|
||||
else if (light_amount < LIGHT_HEAL_THRESHOLD)
|
||||
H.heal_overall_damage(5,5)
|
||||
H.adjustToxLoss(-5)
|
||||
H.adjustBrainLoss(-25) //Shad O. Ling gibbers, "CAN U BE MY THRALL?!!"
|
||||
H.adjustCloneLoss(-1)
|
||||
H.SetKnockdown(0)
|
||||
H.SetStun(0)
|
||||
var/charge_time = 400 - ((SSticker.mode.thralls && SSticker.mode.thralls.len) || 0)*10
|
||||
if(world.time >= charge_time+last_charge)
|
||||
shadow_charges = min(shadow_charges + 1, 3)
|
||||
last_charge = world.time
|
||||
|
||||
/datum/species/shadow/ling/bullet_act(obj/item/projectile/P, mob/living/carbon/human/H)
|
||||
var/turf/T = H.loc
|
||||
if(istype(T) && shadow_charges > 0)
|
||||
var/light_amount = T.get_lumcount()
|
||||
if(light_amount < LIGHT_DAM_THRESHOLD)
|
||||
H.visible_message("<span class='danger'>The shadows around [H] ripple as they absorb \the [P]!</span>")
|
||||
playsound(T, "bullet_miss", 75, 1)
|
||||
shadow_charges = min(shadow_charges - 1, 0)
|
||||
return -1
|
||||
return 0
|
||||
|
||||
/datum/species/shadow/ling/lesser //Empowered thralls. Obvious, but powerful
|
||||
name = "Lesser Shadowling"
|
||||
id = "l_shadowling"
|
||||
say_mod = "chitters"
|
||||
species_traits = list(NOBLOOD,NO_DNA_COPY,NOTRANSSTING,NOEYES)
|
||||
inherent_traits = list(TRAIT_NOBREATH, TRAIT_RADIMMUNE)
|
||||
burnmod = 1.1
|
||||
heatmod = 1.1
|
||||
|
||||
/datum/species/shadow/ling/lesser/spec_life(mob/living/carbon/human/H)
|
||||
H.nutrition = NUTRITION_LEVEL_WELL_FED //i aint never get hongry
|
||||
if(isturf(H.loc))
|
||||
var/turf/T = H.loc
|
||||
var/light_amount = T.get_lumcount()
|
||||
if(light_amount > LIGHT_DAM_THRESHOLD && !H.incorporeal_move)
|
||||
H.take_overall_damage(0, LIGHT_DAMAGE_TAKEN/2)
|
||||
else if (light_amount < LIGHT_HEAL_THRESHOLD)
|
||||
H.heal_overall_damage(2,2)
|
||||
H.adjustToxLoss(-5)
|
||||
H.adjustBrainLoss(-25)
|
||||
H.adjustCloneLoss(-1)
|
||||
|
||||
/datum/game_mode/proc/update_shadow_icons_added(datum/mind/shadow_mind)
|
||||
var/datum/atom_hud/antag/shadow_hud = GLOB.huds[ANTAG_HUD_SHADOW]
|
||||
shadow_hud.join_hud(shadow_mind.current)
|
||||
set_antag_hud(shadow_mind.current, ((is_shadow(shadow_mind.current)) ? "shadowling" : "thrall"))
|
||||
|
||||
/datum/game_mode/proc/update_shadow_icons_removed(datum/mind/shadow_mind)
|
||||
var/datum/atom_hud/antag/shadow_hud = GLOB.huds[ANTAG_HUD_SHADOW]
|
||||
shadow_hud.leave_hud(shadow_mind.current)
|
||||
set_antag_hud(shadow_mind.current, null)
|
||||
|
||||
/mob/living/proc/add_thrall()
|
||||
if(!istype(mind))
|
||||
return FALSE
|
||||
return mind.add_antag_datum(ANTAG_DATUM_THRALL)
|
||||
|
||||
/mob/living/proc/add_sling()
|
||||
if(!istype(mind))
|
||||
return FALSE
|
||||
return mind.add_antag_datum(ANTAG_DATUM_SLING)
|
||||
|
||||
/mob/living/proc/remove_thrall()
|
||||
if(!istype(mind))
|
||||
return FALSE
|
||||
return mind.remove_antag_datum(ANTAG_DATUM_THRALL)
|
||||
|
||||
/mob/living/proc/remove_sling()
|
||||
if(!istype(mind))
|
||||
return FALSE
|
||||
return mind.remove_antag_datum(ANTAG_DATUM_SLING)
|
||||
40
yogstation/code/modules/admin/verbs/debug.dm
Normal file
@@ -0,0 +1,40 @@
|
||||
/client/proc/callproc_datum_fast(datum/A as null|area|mob|obj|turf)
|
||||
set category = "Debug"
|
||||
set name = "Fast ProcCall"
|
||||
set waitfor = 0
|
||||
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
|
||||
var/procstring = input("Proc call, eg: \"vomit(0, 1, 0, 5)\"","Proc:", null) as text|null
|
||||
if(!procstring)
|
||||
return
|
||||
var/regex/procname_regex = regex("^(\[a-zA-z\]+)(?=\\()")
|
||||
var/has_procname = procname_regex.Find(procstring)
|
||||
if(!has_procname)
|
||||
to_chat(usr, "<font color='red'>Error: Fast ProcCall: Could not parse function name in [procstring].</font>")
|
||||
return
|
||||
var/procname = procname_regex.group[1]
|
||||
if(!hascall(A,procname))
|
||||
to_chat(usr, "<font color='red'>Error: Fast ProcCall: Type [A.type] has no proc named [procname].</font>")
|
||||
return
|
||||
|
||||
var/regex/pain = regex("(\"(\[^"\\\\\]*(?:\\\\.\[^\"\\\\\]*)*)"|\\d+)")
|
||||
pain.Find(procstring)
|
||||
var/list/arguments = list()
|
||||
|
||||
for(var/i=1 to pain.group.len)
|
||||
var
|
||||
if(!A || !IsValidSrc(A))
|
||||
to_chat(usr, "<span class='warning'>Error: callproc_datum(): owner of proc no longer exists.</span>")
|
||||
return
|
||||
log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
|
||||
var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
|
||||
message_admins(msg)
|
||||
admin_ticket_log(A, msg)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Atom ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc
|
||||
. = get_callproc_returnval(returnval,procname)
|
||||
if(.)
|
||||
to_chat(usr, .)
|
||||
27
yogstation/code/modules/admin/verbs/one_click_antag.dm
Normal file
@@ -0,0 +1,27 @@
|
||||
//Shadowling
|
||||
/datum/admins/proc/makeShadowling()
|
||||
var/datum/game_mode/shadowling/temp = new
|
||||
if(CONFIG_GET(flag/protect_roles_from_antagonist))
|
||||
temp.restricted_jobs += temp.protected_jobs
|
||||
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
|
||||
temp.restricted_jobs += "Assistant"
|
||||
var/list/mob/living/carbon/human/candidates = list()
|
||||
var/mob/living/carbon/human/H
|
||||
for(var/mob/living/carbon/human/applicant in GLOB.player_list)
|
||||
if(ROLE_SHADOWLING in applicant.client.prefs.be_special)
|
||||
if(!applicant.stat || !applicant.mind)
|
||||
continue
|
||||
if(applicant.mind.special_role)
|
||||
continue
|
||||
if(jobban_isbanned(applicant, "shadowling") || jobban_isbanned(applicant, "Syndicate"))
|
||||
continue
|
||||
if(!temp.age_check(applicant.client) || applicant.job in temp.restricted_jobs)
|
||||
continue
|
||||
if(is_shadow_or_thrall(applicant))
|
||||
continue
|
||||
candidates += applicant
|
||||
if(candidates.len)
|
||||
H = pick(candidates)
|
||||
H.add_sling()
|
||||
return TRUE
|
||||
return FALSE
|
||||
@@ -0,0 +1,46 @@
|
||||
/mob/living/simple_animal/ascendant_shadowling
|
||||
name = "ascendant shadowling"
|
||||
desc = "HOLY SHIT RUN THE FUCK AWAY- <span class='shadowling'>RAAAAAAA!</span>"
|
||||
icon = 'yogstation/icons/mob/mob.dmi'
|
||||
icon_state = "shadowling_ascended"
|
||||
icon_living = "shadowling_ascended"
|
||||
verb_say = "telepathically thunders"
|
||||
verb_ask = "telepathically thunders"
|
||||
verb_exclaim = "telepathically thunders"
|
||||
verb_yell = "telepathically thunders"
|
||||
force_threshold = INFINITY //Can't die by normal means
|
||||
health = 9999
|
||||
maxHealth = 9999
|
||||
speed = 0
|
||||
see_in_dark = 8
|
||||
see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
response_help = "pokes"
|
||||
response_disarm = "flails at"
|
||||
response_harm = "flails at"
|
||||
harm_intent_damage = 0
|
||||
melee_damage_lower = 60 //Was 35, buffed
|
||||
melee_damage_upper = 60
|
||||
attacktext = "rends"
|
||||
attack_sound = 'sound/weapons/slash.ogg'
|
||||
minbodytemp = 0
|
||||
maxbodytemp = INFINITY
|
||||
environment_smash = 3
|
||||
faction = list("faithless")
|
||||
|
||||
/mob/living/simple_animal/ascendant_shadowling/Process_Spacemove(movement_dir = 0)
|
||||
return TRUE //copypasta from carp code
|
||||
|
||||
/mob/living/simple_animal/ascendant_shadowling/get_spans()
|
||||
return ..() | list(SPAN_REALLYBIG, SPAN_YELL) //MAKES THEM SHOUT WHEN THEY TALK
|
||||
|
||||
/mob/living/simple_animal/ascendant_shadowling/ex_act(severity)
|
||||
return FALSE //You think an ascendant can be hurt by bombs? HA
|
||||
|
||||
/mob/living/simple_animal/ascendant_shadowling/singularity_act()
|
||||
to_chat(src, "<span class='shadowling>NO NO NO AAAAAAAAAAAAAAAAAAA-</span>")
|
||||
to_chat(world, "<span class='shadowling'><b>\"<font size=6>NO!</font> <font size=5>I will</font> <font size=4>not be.... destroyed</font> <font size=3>by a....</font> <font size=2>AAAAAAA-</font>\"</span>")
|
||||
for(var/X in GLOB.alive_mob_list)
|
||||
var/mob/M = X
|
||||
to_chat(M, "<span class='notice'><i><b>You feel a woosh as newly released energy temporarily distorts space itself...</b></i></span>")
|
||||
SEND_SOUND(M, sound('sound/hallucinations/wail.ogg'))
|
||||
. = ..()
|
||||
107
yogstation/code/modules/antagonists/shadowling/shadowling.dm
Normal file
@@ -0,0 +1,107 @@
|
||||
/datum/antagonist/shadowling
|
||||
name = "Shadowling"
|
||||
job_rank = ROLE_SHADOWLING
|
||||
roundend_category = "shadowlings"
|
||||
antagpanel_category = "Shadowlings"
|
||||
antag_moodlet = /datum/mood_event/sling
|
||||
var/list/objectives_given = list()
|
||||
|
||||
/datum/antagonist/shadowling/on_gain()
|
||||
. = ..()
|
||||
SSticker.mode.update_shadow_icons_added(owner)
|
||||
SSticker.mode.shadows += owner
|
||||
owner.special_role = "Shadowling"
|
||||
message_admins("[key_name_admin(owner.current)] was made into a shadowling!")
|
||||
log_game("[key_name(owner.current)] was made into a shadowling!")
|
||||
var/mob/living/carbon/human/S = owner.current
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_hatch(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_hivemind(null))
|
||||
if(owner.assigned_role == "Clown")
|
||||
to_chat(S, "<span class='notice'>Your alien nature has allowed you to overcome your clownishness.</span>")
|
||||
S.dna.remove_mutation(CLOWNMUT)
|
||||
var/datum/objective/ascend/O = new
|
||||
O.update_explanation_text()
|
||||
owner.objectives += O
|
||||
objectives_given += O
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/shadowling/on_removal()
|
||||
for(var/O in objectives_given)
|
||||
owner.objectives -= O
|
||||
SSticker.mode.update_shadow_icons_removed(owner)
|
||||
SSticker.mode.shadows -= owner
|
||||
message_admins("[key_name_admin(owner.current)] was de-shadowlinged!")
|
||||
log_game("[key_name(owner.current)] was de-shadowlinged!")
|
||||
owner.special_role = null
|
||||
for(var/X in owner.spell_list)
|
||||
var/obj/effect/proc_holder/spell/S = X
|
||||
owner.RemoveSpell(S)
|
||||
var/mob/living/M = owner.current
|
||||
if(issilicon(M))
|
||||
M.audible_message("<span class='notice'>[M] lets out a short blip.</span>", \
|
||||
"<span class='userdanger'>You have been turned into a robot! You are no longer a shadowling! Though you try, you cannot remember anything about your time as one...</span>")
|
||||
else
|
||||
M.visible_message("<span class='big'>[M] screams and contorts!</span>", \
|
||||
"<span class='userdanger'>THE LIGHT-- YOUR MIND-- <i>BURNS--</i></span>")
|
||||
spawn(30)
|
||||
if(QDELETED(M))
|
||||
return
|
||||
M.visible_message("<span class='warning'>[M] suddenly bloats and explodes!</span>", \
|
||||
"<span class='warning bold'>AAAAAAAAA<font size=3>AAAAAAAAAAAAA</font><font size=4>AAAAAAAAAAAA----</font></span>")
|
||||
playsound(M, 'sound/magic/Disintegrate.ogg', 100, 1)
|
||||
M.gib()
|
||||
|
||||
/datum/antagonist/shadowling/greet()
|
||||
to_chat(owner, "<br> <span class='shadowling bold big'>You are a shadowling!</span>")
|
||||
to_chat(owner, "<b>Currently, you are disguised as an employee aboard [station_name()].</b>")
|
||||
to_chat(owner, "<b>In your limited state, you have three abilities: Enthrall, Hatch, and Hivemind Commune.</b>")
|
||||
to_chat(owner, "<b>Any other shadowlings are your allies. You must assist them as they shall assist you.</b>")
|
||||
to_chat(owner, "<b>You require [SSticker.mode.required_thralls || 15] thralls to ascend.</b><br>")
|
||||
SEND_SOUND(owner.current, sound('yogstation/sound/ambience/antag/sling.ogg'))
|
||||
|
||||
/datum/antagonist/shadowling/proc/check_shadow_death()
|
||||
for(var/SM in get_antag_minds(/datum/antagonist/shadowling))
|
||||
var/datum/mind/shadow_mind = SM
|
||||
if(istype(shadow_mind))
|
||||
var/turf/T = get_turf(shadow_mind.current)
|
||||
if((shadow_mind) && (shadow_mind.current.stat != DEAD) && T && is_station_level(T.z) && ishuman(shadow_mind.current))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/shadowling/roundend_report()
|
||||
var/list/parts = list()
|
||||
if(SSticker.mode.shadowling_ascended) //Doesn't end instantly - this is hacky and I don't know of a better way ~X
|
||||
parts += "<span class='greentext big'>The shadowlings have ascended and taken over the station!</span>"
|
||||
else if(!SSticker.mode.shadowling_ascended && check_shadow_death()) //If the shadowlings have ascended, they can not lose the round
|
||||
parts += "<span class='redtext big'>The shadowlings have been killed by the crew!</span>"
|
||||
else if(!SSticker.mode.shadowling_ascended && SSshuttle.emergency.mode >= SHUTTLE_ESCAPE)
|
||||
parts += "<span class='redtext big'>The crew escaped the station before the shadowlings could ascend!</span>"
|
||||
else
|
||||
parts += "<span class='redtext big'>The shadowlings have failed!</span>"
|
||||
if(objectives.len)
|
||||
parts += "<b>The shadowlings' objectives were:</b>"
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</span>"
|
||||
else
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
count++
|
||||
if(LAZYLEN(SSticker.mode.shadows))
|
||||
parts += printplayerlist(SSticker.mode.shadows)
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
|
||||
/datum/objective/ascend
|
||||
explanation_text = "Ascend to your true form by use of the Ascendance ability. This may only be used with 15 or more collective thralls, while hatched, and is unlocked with the Collective Mind ability."
|
||||
|
||||
/datum/objective/ascend/check_completion()
|
||||
return (SSticker && SSticker.mode && SSticker.mode.shadowling_ascended)
|
||||
|
||||
/datum/objective/ascend/update_explanation_text()
|
||||
explanation_text = "Ascend to your true form by use of the Ascendance ability. This may only be used with [SSticker.mode.required_thralls] or more collective thralls, while hatched, and is unlocked with the Collective Mind ability."
|
||||
|
||||
/mob/living/carbon/human/Stat()
|
||||
. = ..()
|
||||
if(statpanel("Status") && (dna && dna.species) && istype(dna.species, /datum/species/shadow/ling))
|
||||
var/datum/species/shadow/ling/SL = dna.species
|
||||
stat("Shadowy Shield Charges", SL.shadow_charges)
|
||||
@@ -0,0 +1,900 @@
|
||||
#define EMPOWERED_THRALL_LIMIT 5
|
||||
|
||||
/obj/effect/proc_holder/spell/proc/shadowling_check(var/mob/living/carbon/human/H)
|
||||
if(!H || !istype(H)) return
|
||||
if(H.dna && H.dna.species && H.dna.species.id == "shadowling" && is_shadow(H)) return TRUE
|
||||
if(H.dna && H.dna.species && H.dna.species.id == "l_shadowling" && is_thrall(H)) return TRUE
|
||||
if(!is_shadow_or_thrall(usr)) to_chat(usr, "<span class='warning'>You can't wrap your head around how to do this.</span>")
|
||||
else if(is_thrall(usr)) to_chat(usr, "<span class='warning'>You aren't powerful enough to do this.</span>")
|
||||
else if(is_shadow(usr)) to_chat(usr, "<span class='warning'>Your telepathic ability is suppressed. Hatch or use Rapid Re-Hatch first.</span>")
|
||||
return 0
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sling //Stuns and mutes a human target for 10 seconds
|
||||
ranged_mousepointer = 'icons/effects/cult_target.dmi'
|
||||
var/mob/living/user
|
||||
var/mob/living/target
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sling/Click()
|
||||
var/mob/living/user = usr
|
||||
if(!istype(user))
|
||||
return
|
||||
var/msg
|
||||
if(!can_cast(user))
|
||||
msg = "<span class='warning'>You can no longer cast [name]!</span>"
|
||||
remove_ranged_ability(msg)
|
||||
return
|
||||
if(active)
|
||||
remove_ranged_ability()
|
||||
else
|
||||
add_ranged_ability(user, null, TRUE)
|
||||
if(action)
|
||||
action.UpdateButtonIcon()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sling/InterceptClickOn(mob/living/caller, params, atom/t)
|
||||
if(!isliving(t))
|
||||
to_chat(caller, "<span class='warning'>You may only use this ability on living things!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
user = caller
|
||||
target = t
|
||||
if(!shadowling_check(user))
|
||||
revert_cast()
|
||||
return
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sling/revert_cast()
|
||||
. = ..()
|
||||
remove_ranged_ability()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sling/start_recharge()
|
||||
. = ..()
|
||||
if(action)
|
||||
action.UpdateButtonIcon()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sling/glare //Stuns and mutes a human target for 10 seconds
|
||||
name = "Glare"
|
||||
desc = "Disrupts the target's motor and speech abilities."
|
||||
panel = "Shadowling Abilities"
|
||||
charge_max = 300
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
action_icon_state = "glare"
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sling/glare/InterceptClickOn(mob/living/caller, params, atom/t)
|
||||
. = ..()
|
||||
if(!target)
|
||||
return
|
||||
if(target.stat)
|
||||
to_chat(usr, "<span class='warning'>[target] must be conscious!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(is_shadow_or_thrall(target))
|
||||
to_chat(usr, "<span class='warning'>You cannot glare at allies!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
var/mob/living/carbon/human/M = target
|
||||
usr.visible_message("<span class='warning'><b>[usr]'s eyes flash a purpleish-red!</b></span>")
|
||||
var/distance = get_dist(target, usr)
|
||||
if (distance <= 1) //Melee
|
||||
target.visible_message("<span class='danger'>[target] suddendly collapses...</span>")
|
||||
to_chat(target, "<span class='userdanger'>A purple light flashes across your vision, and you lose control of your movements!</span>")
|
||||
target.Stun(100)
|
||||
M.silent += 10
|
||||
else //Distant glare
|
||||
var/loss = 100 - (distance * 10)
|
||||
target.adjustStaminaLoss(loss)
|
||||
target.stuttering = loss
|
||||
to_chat(target, "<span class='userdanger'>A purple light flashes across your vision, and exhaustion floods your body...</span>")
|
||||
target.visible_message("<span class='danger'>[target] looks very tired...</span>")
|
||||
charge_counter = 0
|
||||
start_recharge()
|
||||
remove_ranged_ability()
|
||||
usr = null
|
||||
target = null
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/veil //Puts out most nearby lights except for flares and yellow slime cores
|
||||
name = "Veil"
|
||||
desc = "Extinguishes most nearby light sources."
|
||||
panel = "Shadowling Abilities"
|
||||
charge_max = 150 //Short cooldown because people can just turn the lights back on
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
range = 5
|
||||
action_icon_state = "veil"
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
var/admin_override = FALSE //Requested by Shadowlight213. Allows anyone to cast the spell, not just shadowlings.
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/proc/extinguishItem(obj/item/I, cold = FALSE) //Does not darken items held by mobs due to mobs having separate luminosity, use extinguishMob() or write your own proc.
|
||||
var/blacklisted_lights = list(/obj/item/flashlight/flare, /obj/item/flashlight/slime, /obj/item/electronic_assembly)
|
||||
if(istype(I, /obj/item/flashlight))
|
||||
var/obj/item/flashlight/F = I
|
||||
if(F.on)
|
||||
if(cold)
|
||||
if(is_type_in_list(F, blacklisted_lights))
|
||||
F.visible_message("<span class='warning'>The sheer cold shatters [F]!</span>")
|
||||
qdel(F)
|
||||
else
|
||||
return
|
||||
if(is_type_in_list(I, blacklisted_lights))
|
||||
I.visible_message("<span class='danger'>[I] dims slightly before scattering the shadows around it.</span>")
|
||||
return F.brightness_on //Necessary because flashlights become 0-luminosity when held. I don't make the rules of lightcode.
|
||||
F.on = FALSE
|
||||
F.update_brightness()
|
||||
else if(istype(I, /obj/item/pda))
|
||||
var/obj/item/pda/P = I
|
||||
P.fon = FALSE
|
||||
I.set_light(0)
|
||||
return I.luminosity
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/proc/extinguishMob(mob/living/H, cold = FALSE)
|
||||
var/blacklistLuminosity = 0
|
||||
for(var/obj/item/F in H)
|
||||
if(cold)
|
||||
extinguishItem(F, TRUE)
|
||||
blacklistLuminosity += extinguishItem(F)
|
||||
H.set_light(blacklistLuminosity) //I hate lightcode for making me do it this way
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/veil/cast(list/targets,mob/user = usr)
|
||||
if(!shadowling_check(user) && !admin_override)
|
||||
revert_cast()
|
||||
return
|
||||
to_chat(user, "<span class='shadowling'>You silently disable all nearby lights.</span>")
|
||||
for(var/turf/T in view(5))
|
||||
for(var/obj/item/F in T.contents)
|
||||
extinguishItem(F)
|
||||
for(var/obj/machinery/light/L in T.contents)
|
||||
L.on = FALSE
|
||||
L.visible_message("<span class='warning'>[L] flickers and falls dark.</span>")
|
||||
L.update(0)
|
||||
L.set_light(0)
|
||||
for(var/obj/machinery/computer/C in T.contents)
|
||||
C.set_light(0)
|
||||
C.visible_message("<span class='warning'>[C] grows dim, its screen barely readable.</span>")
|
||||
for(var/mob/living/H in T.contents)
|
||||
extinguishMob(H)
|
||||
for(var/mob/living/silicon/robot/borg in T.contents)
|
||||
if(!borg.lamp_cooldown)
|
||||
borg.update_headlamp(TRUE, INFINITY)
|
||||
to_chat(borg, "<span class='danger'>Your headlamp is fried! You'll need a human to help replace it.</span>")
|
||||
for(var/obj/machinery/camera/cam in T.contents)
|
||||
cam.set_light(0)
|
||||
if(prob(10))
|
||||
cam.emp_act(2)
|
||||
for(var/obj/structure/glowshroom/G in orange(7, user)) //High radius because glowshroom spam wrecks shadowlings
|
||||
G.visible_message("<span class='warning'>[G] withers away!</span>")
|
||||
qdel(G)
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/flashfreeze //Stuns and freezes nearby people - a bit more effective than a changeling's cryosting
|
||||
name = "Icy Veins"
|
||||
desc = "Instantly freezes the blood of nearby people, stunning them and causing burn damage."
|
||||
panel = "Shadowling Abilities"
|
||||
range = 3
|
||||
charge_max = 250
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "icy_veins"
|
||||
sound = 'sound/effects/ghost2.ogg'
|
||||
var/special_lights = list(/obj/item/flashlight/flare, /obj/item/flashlight/slime)
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/flashfreeze/cast(list/targets,mob/user = usr)
|
||||
if(!shadowling_check(user))
|
||||
revert_cast()
|
||||
return
|
||||
to_chat(user, "<span class='shadowling'>You freeze the nearby air.</span>")
|
||||
for(var/turf/T in targets)
|
||||
for(var/mob/living/carbon/M in T.contents)
|
||||
if(is_shadow_or_thrall(M))
|
||||
if(M == user) //No message for the user, of course
|
||||
continue
|
||||
else
|
||||
to_chat(M, "<span class='danger'>You feel a blast of paralyzingly cold air wrap around you and flow past, but you are unaffected!</span>")
|
||||
continue
|
||||
to_chat(M, "<span class='userdanger'>A wave of shockingly cold air engulfs you!</span>")
|
||||
M.Stun(2)
|
||||
M.apply_damage(10, BURN)
|
||||
if(M.bodytemperature)
|
||||
M.bodytemperature -= 200 //Extreme amount of initial cold
|
||||
if(M.reagents)
|
||||
M.reagents.add_reagent("frostoil", 15) //Half of a cryosting
|
||||
extinguishMob(M, TRUE)
|
||||
for(var/obj/item/F in T.contents)
|
||||
extinguishItem(F, TRUE)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/enthrall //Turns a target into the shadowling's slave. This overrides all previous loyalties
|
||||
name = "Enthrall"
|
||||
desc = "Allows you to enslave a conscious, non-braindead, non-catatonic human to your will. This takes some time to cast."
|
||||
panel = "Shadowling Abilities"
|
||||
charge_max = 0
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
range = 1 //Adjacent to user
|
||||
action_icon_state = "enthrall"
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
var/enthralling = FALSE
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/enthrall/cast(list/targets,mob/living/carbon/human/user = usr)
|
||||
listclearnulls(SSticker.mode.thralls)
|
||||
if(!(user.mind in SSticker.mode.shadows)) return
|
||||
if(user.dna.species.id != "shadowling")
|
||||
if(SSticker.mode.thralls.len >= 5)
|
||||
revert_cast()
|
||||
return
|
||||
for(var/mob/living/carbon/human/target in targets)
|
||||
if(!in_range(user, target))
|
||||
to_chat(user, "<span class='warning'>You need to be closer to enthrall [target]!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(!target.key || !target.mind)
|
||||
to_chat(user, "<span class='warning'>The target has no mind!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(target.stat)
|
||||
to_chat(user, "<span class='warning'>The target must be conscious!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(is_shadow_or_thrall(target))
|
||||
to_chat(user, "<span class='warning'>You can not enthrall allies!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(!ishuman(target))
|
||||
to_chat(user, "<span class='warning'>You can only enthrall humans!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(enthralling)
|
||||
to_chat(user, "<span class='warning'>You are already enthralling!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(!target.client)
|
||||
to_chat(user, "<span class='warning'>[target]'s mind is vacant of activity.</span>")
|
||||
enthralling = TRUE
|
||||
for(var/progress = 0, progress <= 3, progress++)
|
||||
switch(progress)
|
||||
if(1)
|
||||
to_chat(user, "<span class='notice'>You place your hands to [target]'s head...</span>")
|
||||
user.visible_message("<span class='warning'>[user] places their hands onto the sides of [target]'s head!</span>")
|
||||
if(2)
|
||||
to_chat(user, "<span class='notice'>You begin preparing [target]'s mind as a blank slate...</span>")
|
||||
user.visible_message("<span class='warning'>[user]'s palms flare a bright red against [target]'s temples!</span>")
|
||||
to_chat(target, "<span class='danger'>A terrible red light floods your mind. You collapse as conscious thought is wiped away.</span>")
|
||||
target.Knockdown(120)
|
||||
if(target.has_trait(TRAIT_MINDSHIELD))
|
||||
to_chat(user, "<span class='notice'>They are protected by an implant. You begin to shut down the nanobots in their brain - this will take some time...</span>")
|
||||
user.visible_message("<span class='warning'>[user] pauses, then dips their head in concentration!</span>")
|
||||
to_chat(target, "<span class='boldannounce'>You feel your mental protection faltering!</span>")
|
||||
if(!do_mob(user, target, 650)) //65 seconds to remove a loyalty implant. yikes!
|
||||
to_chat(user, "<span class='warning'>The enthralling has been interrupted - your target's mind returns to its previous state.</span>")
|
||||
to_chat(target, "<span class='userdanger'>You wrest yourself away from [user]'s hands and compose yourself!</span>")
|
||||
enthralling = FALSE
|
||||
return
|
||||
to_chat(user, "<span class='notice'>The nanobots composing the mindshield implant have been rendered inert. Now to continue.</span>")
|
||||
user.visible_message("<span class='warning'>[user] relaxes again.</span>")
|
||||
for(var/obj/item/implant/mindshield/L in target)
|
||||
if(L)
|
||||
qdel(L)
|
||||
to_chat(target, "<span class='boldannounce'>Your mental protection unexpectedly falters, dims, dies.</span>")
|
||||
if(3)
|
||||
to_chat(user, "<span class='notice'>You begin planting the tumor that will control the new thrall...</span>")
|
||||
user.visible_message("<span class='warning'>A strange energy passes from [user]'s hands into [target]'s head!</span>")
|
||||
to_chat(target, "<span class='boldannounce'>You feel your memories twisting, morphing. A sense of horror dominates your mind.</span>")
|
||||
if(!do_mob(user, target, 70)) //around 21 seconds total for enthralling, 86 for someone with a loyalty implant
|
||||
to_chat(user, "<span class='warning'>The enthralling has been interrupted - your target's mind returns to its previous state.</span>")
|
||||
to_chat(target, "<span class='userdanger'>You wrest yourself away from [user]'s hands and compose yourself!</span>")
|
||||
enthralling = FALSE
|
||||
return
|
||||
enthralling = FALSE
|
||||
to_chat(user, "<span class='shadowling'>You have enthralled <b>[target.real_name]</b>!</span>")
|
||||
target.visible_message("<span class='big'>[target] looks to have experienced a revelation!</span>", \
|
||||
"<span class='warning'>False faces all d<b>ark not real not real not--</b></span>")
|
||||
target.setOxyLoss(0) //In case the shadowling was choking them out
|
||||
target.mind.special_role = "thrall"
|
||||
var/obj/item/organ/internal/shadowtumor/ST = new
|
||||
ST.Insert(target, FALSE, FALSE)
|
||||
target.add_thrall()
|
||||
if(target.reagents.has_reagent("frostoil")) //Stabilize body temp incase the sling froze them earlier
|
||||
target.reagents.remove_reagent("frostoil")
|
||||
to_chat(target, "<span class='notice'>You feel warmer... It feels good.</span>")
|
||||
target.bodytemperature = 310
|
||||
|
||||
/obj/effect/proc_holder/spell/self/shadowling_hivemind //Lets a shadowling talk to its allies
|
||||
name = "Hivemind Commune"
|
||||
desc = "Allows you to silently communicate with all other shadowlings and thralls."
|
||||
panel = "Shadowling Abilities"
|
||||
charge_max = 0
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "commune"
|
||||
|
||||
/obj/effect/proc_holder/spell/self/shadowling_hivemind/cast(mob/living/user,mob/user = usr)
|
||||
if(!is_shadow(user))
|
||||
to_chat(user, "<span class='warning'>You must be a shadowling to do that!</span>")
|
||||
return
|
||||
var/text = stripped_input(user, "What do you want to say your thralls and fellow shadowlings?.", "Hive Chat", "")
|
||||
if(!text)
|
||||
return
|
||||
var/my_message = "<font size=2><span class='shadowling'><b>\[Shadowling\]</b><i> [user.real_name]</i>: [text]</span></font>"
|
||||
for(var/mob/M in GLOB.mob_list)
|
||||
if(is_shadow_or_thrall(M))
|
||||
to_chat(M, my_message)
|
||||
if(M in GLOB.dead_mob_list)
|
||||
to_chat(M, "<a href='?src=[REF(M)];follow=[REF(user)]'>(F)</a> [my_message]")
|
||||
log_say("[user.real_name]/[user.key] : [text]")
|
||||
|
||||
/obj/effect/proc_holder/spell/self/shadowling_regenarmor //Resets a shadowling's species to normal, removes genetic defects, and re-equips their armor
|
||||
name = "Rapid Re-Hatch"
|
||||
desc = "Re-forms protective chitin that may be lost during cloning or similar processes."
|
||||
panel = "Shadowling Abilities"
|
||||
charge_max = 600
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "regen_armor"
|
||||
|
||||
/obj/effect/proc_holder/spell/self/shadowling_regenarmor/cast(mob/living/carbon/human/user)
|
||||
if(!is_shadow(user))
|
||||
to_chat(user, "<span class='warning'>You must be a shadowling to do this!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
user.visible_message("<span class='warning'>[user]'s skin suddenly bubbles and shifts around their body!</span>", \
|
||||
"<span class='shadowling'>You regenerate your protective armor and cleanse your form of defects.</span>")
|
||||
user.setCloneLoss(0)
|
||||
user.equip_to_slot_or_del(new /obj/item/clothing/suit/space/shadowling(user), SLOT_WEAR_SUIT)
|
||||
user.equip_to_slot_or_del(new /obj/item/clothing/head/shadowling(user), SLOT_HEAD)
|
||||
user.set_species(/datum/species/shadow/ling)
|
||||
|
||||
/obj/effect/proc_holder/spell/self/collective_mind //Lets a shadowling bring together their thralls' strength, granting new abilities and a headcount
|
||||
name = "Collective Hivemind"
|
||||
desc = "Gathers the power of all of your thralls and compares it to what is needed for ascendance. Also gains you new abilities."
|
||||
panel = "Shadowling Abilities"
|
||||
charge_max = 300 //30 second cooldown to prevent spam
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
action_icon_state = "collective_mind"
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
var/blind_smoke_acquired = FALSE
|
||||
var/screech_acquired = FALSE
|
||||
var/reviveThrallAcquired = FALSE
|
||||
|
||||
/obj/effect/proc_holder/spell/self/collective_mind/cast(mob/living/carbon/human/user)
|
||||
if(!shadowling_check(user))
|
||||
revert_cast()
|
||||
return
|
||||
var/thralls = 0
|
||||
var/victory_threshold = SSticker.mode.required_thralls
|
||||
var/mob/M
|
||||
to_chat(user, "<span class='shadowling'><b>You focus your telepathic energies abound, harnessing and drawing together the strength of your thralls.</b></span>")
|
||||
for(M in GLOB.alive_mob_list)
|
||||
if(is_thrall(M))
|
||||
thralls++
|
||||
to_chat(M, "<span class='shadowling'>You feel hooks sink into your mind and pull.</span>")
|
||||
if(!do_after(user, 30, target = user))
|
||||
to_chat(user, "<span class='warning'>Your concentration has been broken. The mental hooks you have sent out now retract into your mind.</span>")
|
||||
return
|
||||
if(thralls >= CEILING(3 * SSticker.mode.thrall_ratio, 1) && !screech_acquired)
|
||||
screech_acquired = TRUE
|
||||
to_chat(user, "<span class='shadowling'><i>The power of your thralls has granted you the <b>Sonic Screech</b> ability. This ability will shatter nearby windows and deafen enemies, plus stunning silicon lifeforms.</span>")
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/unearthly_screech(null))
|
||||
if(thralls >= CEILING(5 * SSticker.mode.thrall_ratio, 1) && !blind_smoke_acquired)
|
||||
blind_smoke_acquired = TRUE
|
||||
to_chat(user, "<span class='shadowling'><i>The power of your thralls has granted you the <b>Blinding Smoke</b> ability. It will create a choking cloud that will blind any non-thralls who enter. \
|
||||
</i></span>")
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/self/blindness_smoke(null))
|
||||
if(thralls >= CEILING(9 * SSticker.mode.thrall_ratio, 1) && !reviveThrallAcquired)
|
||||
reviveThrallAcquired = TRUE
|
||||
to_chat(user, "<span class='shadowling'><i>The power of your thralls has granted you the <b>Black Recuperation</b> ability. This will, after a short time, bring a dead thrall completely back to life \
|
||||
with no bodily defects.</i></span>")
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/revive_thrall(null))
|
||||
if(thralls < victory_threshold)
|
||||
to_chat(user, "<span class='shadowling'>You do not have the power to ascend. You require [victory_threshold] thralls, but only [thralls] living thralls are present.</span>")
|
||||
else if(thralls >= victory_threshold)
|
||||
to_chat(user, "<span class='shadowling'><b>You are now powerful enough to ascend. Use the Ascendance ability when you are ready. <i>This will kill all of your thralls.</i></span>")
|
||||
to_chat(user, "<span class='shadowling'><b>You may find Ascendance in the Shadowling Evolution tab.</b></span>")
|
||||
for(M in GLOB.alive_mob_list)
|
||||
if(is_shadow(M))
|
||||
var/obj/effect/proc_holder/spell/self/collective_mind/CM
|
||||
if(CM in M.mind.spell_list)
|
||||
M.mind.spell_list -= CM
|
||||
qdel(CM)
|
||||
M.mind.RemoveSpell(/obj/effect/proc_holder/spell/self/shadowling_hatch)
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_ascend(null))
|
||||
if(M == user)
|
||||
to_chat(user, "<span class='shadowling'><i>You project this power to the rest of the shadowlings.</i></span>")
|
||||
else
|
||||
to_chat(M, "<span class='shadowling'><b>[user.real_name] has coalesced the strength of the thralls. You can draw upon it at any time to ascend. (Shadowling Evolution Tab)</b></span>") //Tells all the other shadowlings
|
||||
|
||||
/obj/effect/proc_holder/spell/self/blindness_smoke //Spawns a cloud of smoke that blinds non-thralls/shadows and grants slight healing to shadowlings and their allies
|
||||
name = "Blindness Smoke"
|
||||
desc = "Spews a cloud of smoke which will blind enemies."
|
||||
panel = "Shadowling Abilities"
|
||||
charge_max = 600
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
action_icon_state = "black_smoke"
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
sound = 'sound/effects/bamf.ogg'
|
||||
|
||||
/obj/effect/proc_holder/spell/self/blindness_smoke/cast(mob/living/carbon/human/user) //Extremely hacky
|
||||
if(!shadowling_check(user))
|
||||
revert_cast()
|
||||
return
|
||||
user.visible_message("<span class='warning'>[user] bends over and coughs out a cloud of black smoke!</span>")
|
||||
to_chat(user, "<span class='shadowling'>You regurgitate a vast cloud of blinding smoke.</span>")
|
||||
var/obj/item/reagent_containers/glass/beaker/large/B = new /obj/item/reagent_containers/glass/beaker/large(user.loc) //hacky
|
||||
B.reagents.clear_reagents() //Just in case!
|
||||
B.invisibility = INFINITY //This ought to do the trick
|
||||
B.reagents.add_reagent("blindness_smoke", 10)
|
||||
var/datum/effect_system/smoke_spread/chem/S = new
|
||||
S.attach(B)
|
||||
if(S)
|
||||
S.set_up(B.reagents, 4, 0, B.loc)
|
||||
S.start()
|
||||
qdel(B)
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/unearthly_screech //Damages nearby windows, confuses nearby carbons, and outright stuns silly cones
|
||||
name = "Sonic Screech"
|
||||
desc = "Deafens, stuns, and confuses nearby people. Also shatters windows."
|
||||
panel = "Shadowling Abilities"
|
||||
range = 7
|
||||
charge_max = 300
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
action_icon_state = "screech"
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
sound = 'sound/effects/screech.ogg'
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/unearthly_screech/cast(list/targets,mob/user = usr)
|
||||
if(!shadowling_check(user))
|
||||
revert_cast()
|
||||
return
|
||||
user.audible_message("<span class='warning'><b>[user] lets out a horrible scream!</b></span>")
|
||||
for(var/turf/T in targets)
|
||||
for(var/mob/target in T.contents)
|
||||
if(is_shadow_or_thrall(target))
|
||||
if(target == user) //No message for the user, of course
|
||||
continue
|
||||
else
|
||||
continue
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/M = target
|
||||
to_chat(M, "<span class='danger'><b>A spike of pain drives into your head and scrambles your thoughts!</b></span>")
|
||||
M.confused += 10
|
||||
M.adjustEarDamage(0, 30)//as bad as a changeling shriek
|
||||
else if(issilicon(target))
|
||||
var/mob/living/silicon/S = target
|
||||
to_chat(S, "<span class='warning'><b>ERROR $!(@ ERROR )#^! SENSORY OVERLOAD \[$(!@#</b></span>")
|
||||
playsound(S, 'sound/machines/warning-buzzer.ogg', 50, 1)
|
||||
var/datum/effect_system/spark_spread/sp = new /datum/effect_system/spark_spread
|
||||
sp.set_up(5, 1, S)
|
||||
sp.start()
|
||||
S.Knockdown(6)
|
||||
for(var/obj/structure/window/W in T.contents)
|
||||
W.take_damage(rand(80, 100))
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/revive_thrall //Completely revives a dead thrall
|
||||
name = "Black Recuperation"
|
||||
desc = "Revives or empowers a thrall."
|
||||
panel = "Shadowling Abilities"
|
||||
range = 1
|
||||
charge_max = 600
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
include_user = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "revive_thrall"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/revive_thrall/cast(list/targets,mob/user = usr)
|
||||
if(!shadowling_check(user))
|
||||
revert_cast()
|
||||
return
|
||||
for(var/mob/living/carbon/human/thrallToRevive in targets)
|
||||
var/choice = alert(user,"Empower a living thrall or revive a dead one?",,"Empower","Revive","Cancel")
|
||||
switch(choice)
|
||||
if("Empower")
|
||||
if(!is_thrall(thrallToRevive))
|
||||
to_chat(user, "<span class='warning'>[thrallToRevive] is not a thrall.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(thrallToRevive.stat != CONSCIOUS)
|
||||
to_chat(user, "<span class='warning'>[thrallToRevive] must be conscious to become empowered.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(thrallToRevive.dna.species.id == "l_shadowling")
|
||||
to_chat(user, "<span class='warning'>[thrallToRevive] is already empowered.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
var/empowered_thralls = 0
|
||||
for(var/datum/mind/M in SSticker.mode.thralls)
|
||||
if(!ishuman(M.current))
|
||||
return
|
||||
var/mob/living/carbon/human/H = M.current
|
||||
if(H.dna.species.id == "l_shadowling")
|
||||
empowered_thralls++
|
||||
if(empowered_thralls >= EMPOWERED_THRALL_LIMIT)
|
||||
to_chat(user, "<span class='warning'>You cannot spare this much energy. There are too many empowered thralls.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
user.visible_message("<span class='danger'>[user] places their hands over [thrallToRevive]'s face, red light shining from beneath.</span>", \
|
||||
"<span class='shadowling'>You place your hands on [thrallToRevive]'s face and begin gathering energy...</span>")
|
||||
to_chat(thrallToRevive, "<span class='userdanger'>[user] places their hands over your face. You feel energy gathering. Stand still...</span>")
|
||||
if(!do_mob(user, thrallToRevive, 80))
|
||||
to_chat(user, "<span class='warning'>Your concentration snaps. The flow of energy ebbs.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
to_chat(user, "<span class='shadowling'><b><i>You release a massive surge of power into [thrallToRevive]!</b></i></span>")
|
||||
user.visible_message("<span class='boldannounce'><i>Red lightning surges into [thrallToRevive]'s face!</i></span>")
|
||||
playsound(thrallToRevive, 'sound/weapons/Egloves.ogg', 50, 1)
|
||||
playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1)
|
||||
user.Beam(thrallToRevive,icon_state="red_lightning",time=1)
|
||||
thrallToRevive.Knockdown(5)
|
||||
thrallToRevive.visible_message("<span class='warning'><b>[thrallToRevive] collapses, their skin and face distorting!</span>", \
|
||||
"<span class='userdanger'><i>AAAAAAAAAAAAAAAAAAAGH-</i></span>")
|
||||
if (!do_mob(user, thrallToRevive, 5))
|
||||
thrallToRevive.Unconscious(600)
|
||||
thrallToRevive.visible_message("<span class='warning'><b>[thrallToRevive] gasps, and passes out!</b></span>", "<span class='warning'><i>That... feels nice....</i></span>")
|
||||
to_chat(user, "<span class='warning'>We have been interrupted! [thrallToRevive] will need to rest to recover.</span>")
|
||||
return
|
||||
thrallToRevive.visible_message("<span class='warning'>[thrallToRevive] slowly rises, no longer recognizable as human.</span>", \
|
||||
"<span class='shadowling'><b>You feel new power flow into you. You have been gifted by your masters. You now closely resemble them. You are empowered in \
|
||||
darkness but wither slowly in light. In addition, Lesser Glare and Guise have been upgraded into their true forms.</b></span>")
|
||||
thrallToRevive.set_species(/datum/species/shadow/ling/lesser)
|
||||
thrallToRevive.mind.RemoveSpell(/obj/effect/proc_holder/spell/targeted/lesser_glare)
|
||||
thrallToRevive.mind.RemoveSpell(/obj/effect/proc_holder/spell/self/lesser_shadow_walk)
|
||||
thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/sling/glare(null))
|
||||
if("Revive")
|
||||
if(!is_thrall(thrallToRevive))
|
||||
to_chat(user, "<span class='warning'>[thrallToRevive] is not a thrall.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(thrallToRevive.stat != DEAD)
|
||||
to_chat(user, "<span class='warning'>[thrallToRevive] is not dead.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
user.visible_message("<span class='danger'>[user] kneels over [thrallToRevive], placing their hands on \his chest.</span>", \
|
||||
"<span class='shadowling'>You crouch over the body of your thrall and begin gathering energy...</span>")
|
||||
thrallToRevive.notify_ghost_cloning("Your masters are resuscitating you! Re-enter your corpse if you wish to be brought to life.", source = thrallToRevive)
|
||||
if(!do_mob(user, thrallToRevive, 30))
|
||||
to_chat(user, "<span class='warning'>Your concentration snaps. The flow of energy ebbs.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
to_chat(user, "<span class='shadowling'><b><i>You release a massive surge of power into [thrallToRevive]!</b></i></span>")
|
||||
user.visible_message("<span class='boldannounce'><i>Red lightning surges from [user]'s hands into [thrallToRevive]'s chest!</i></span>")
|
||||
playsound(thrallToRevive, 'sound/weapons/Egloves.ogg', 50, 1)
|
||||
playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1)
|
||||
user.Beam(thrallToRevive,icon_state="red_lightning",time=1)
|
||||
var/b = do_mob(user, thrallToRevive, 20)
|
||||
if(thrallToRevive.revive(full_heal = 1))
|
||||
thrallToRevive.visible_message("<span class='boldannounce'>[thrallToRevive] heaves in breath, dim red light shining in their eyes.</span>", \
|
||||
"<span class='shadowling'><b><i>You have returned. One of your masters has brought you from the darkness beyond.</b></i></span>")
|
||||
thrallToRevive.Knockdown(4)
|
||||
thrallToRevive.emote("gasp")
|
||||
playsound(thrallToRevive, "bodyfall", 50, 1)
|
||||
if (!b)
|
||||
thrallToRevive.Knockdown(50)
|
||||
thrallToRevive.Unconscious(500)
|
||||
thrallToRevive.visible_message("<span class='boldannounce'>[thrallToRevive] collapses in exhaustion.</span>", \
|
||||
"<span class='warning'><b><i>You collapse in exhaustion... nap..... dark.</b></i></span>")
|
||||
else
|
||||
revert_cast()
|
||||
return
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle
|
||||
name = "Destroy Engines"
|
||||
desc = "Extends the time of the emergency shuttle's arrival by fifteen minutes. This can only be used once."
|
||||
panel = "Shadowling Abilities"
|
||||
range = 1
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
charge_max = 600
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "extend_shuttle"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle/cast(list/targets, mob/living/carbon/human/user = usr)
|
||||
if(!shadowling_check(user))
|
||||
revert_cast()
|
||||
return
|
||||
for(var/mob/living/carbon/human/target in targets)
|
||||
if(target.stat)
|
||||
revert_cast()
|
||||
return
|
||||
if(!is_thrall(target))
|
||||
to_chat(user, "<span class='warning'>[target] must be a thrall.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(SSshuttle.emergency.mode != SHUTTLE_CALL)
|
||||
to_chat(user, "span class='warning'>The shuttle must be inbound only to the station.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
var/mob/living/carbon/human/M = target
|
||||
user.visible_message("<span class='warning'>[user]'s eyes flash a bright red!</span>", \
|
||||
"<span class='notice'>You begin to draw [M]'s life force.</span>")
|
||||
M.visible_message("<span class='warning'>[M]'s face falls slack, their jaw slightly distending.</span>", \
|
||||
"<span class='boldannounce'>You are suddenly transported... far, far away...</span>")
|
||||
if(!do_after(user, 50, target = M))
|
||||
to_chat(M, "<span class='warning'>You are snapped back to reality, your haze dissipating!</span>")
|
||||
to_chat(user, "<span class='warning'>You have been interrupted. The draw has failed.</span>")
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You project [M]'s life force toward the approaching shuttle, extending its arrival duration!</span>")
|
||||
M.visible_message("<span class='warning'>[M]'s eyes suddenly flare red. They proceed to collapse on the floor, not breathing.</span>", \
|
||||
"<span class='warning'><b>...speeding by... ...pretty blue glow... ...touch it... ...no glow now... ...no light... ...nothing at all...</span>")
|
||||
M.death()
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
|
||||
var/more_minutes = 9000
|
||||
var/timer = SSshuttle.emergency.timeLeft()
|
||||
timer += more_minutes
|
||||
priority_announce("Major system failure aboard the emergency shuttle. This will extend its arrival time by approximately 15 minutes...", "System Failure", 'sound/misc/notice1.ogg')
|
||||
SSshuttle.emergency.setTimer(timer)
|
||||
user.mind.spell_list.Remove(src) //Can only be used once!
|
||||
qdel(src)
|
||||
|
||||
|
||||
|
||||
// THRALL ABILITIES BEYOND THIS POINT //
|
||||
/obj/effect/proc_holder/spell/targeted/lesser_glare //a defensive ability, nothing else. can't be used to stun people, steal tasers, etc. Just good for escaping
|
||||
name = "Lesser Glare"
|
||||
desc = "Makes a single target dizzy for a bit."
|
||||
panel = "Thrall Abilities"
|
||||
charge_max = 450
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "glare"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/lesser_glare/cast(list/targets,mob/user = usr)
|
||||
for(var/mob/living/target in targets)
|
||||
if(!ishuman(target) || !target)
|
||||
to_chat(user, "<span class='warning'>You nay only glare at humans!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(target.stat)
|
||||
to_chat(user, "<span class='warning'>[target] must be conscious!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(is_shadow_or_thrall(target))
|
||||
to_chat(user, "<span class='warning'>You cannot glare at allies!</span>")
|
||||
revert_cast()
|
||||
return
|
||||
var/mob/living/carbon/human/M = target
|
||||
user.visible_message("<span class='warning'><b>[user]'s eyes flash a bright red!</b></span>")
|
||||
target.visible_message("<span class='danger'>[target] suddendly looks dizzy and nauseous...</span>")
|
||||
if(in_range(target, user))
|
||||
to_chat(target, "<span class='userdanger'>Your gaze is forcibly drawn into [user]'s eyes, and you suddendly feel dizzy and nauseous...</span>")
|
||||
else //Only alludes to the thrall if the target is close by
|
||||
to_chat(target, "<span class='userdanger'>Red lights suddenly dance in your vision, and you suddendly feel dizzy and nauseous...</span>")
|
||||
M.confused += 25
|
||||
M.Jitter(50)
|
||||
if(prob(25))
|
||||
M.vomit(10)
|
||||
|
||||
/obj/effect/proc_holder/spell/self/lesser_shadow_walk //Thrall version of Shadow Walk, only works in darkness, doesn't grant phasing, but gives near-invisibility
|
||||
name = "Guise"
|
||||
desc = "Wraps your form in shadows, making you harder to see."
|
||||
panel = "Thrall Abilities"
|
||||
charge_max = 1200
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "shadow_walk"
|
||||
|
||||
/obj/effect/proc_holder/spell/self/lesser_shadow_walk/proc/reappear(mob/living/carbon/human/user)
|
||||
user.visible_message("<span class='warning'>[user] appears from nowhere!</span>", "<span class='shadowling'>Your shadowy guise slips away.</span>")
|
||||
user.alpha = initial(user.alpha)
|
||||
|
||||
/obj/effect/proc_holder/spell/self/lesser_shadow_walk/cast(mob/living/carbon/human/user)
|
||||
user.visible_message("<span class='warning'>[user] suddenly fades away!</span>", "<span class='shadowling'>You veil yourself in darkness, making you harder to see.</span>")
|
||||
user.alpha = 10
|
||||
addtimer(CALLBACK(src, .proc/reappear, user), 40)
|
||||
|
||||
/obj/effect/proc_holder/spell/self/thrall_night_vision //Toggleable night vision for thralls
|
||||
name = "Thrall Darksight"
|
||||
desc = "Allows you to see in the dark!"
|
||||
action_icon_state = "darksight"
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
clothes_req = FALSE
|
||||
charge_max = 0
|
||||
|
||||
/obj/effect/proc_holder/spell/self/thrall_night_vision/cast(mob/living/carbon/human/user)
|
||||
if(!is_shadow_or_thrall(user))
|
||||
revert_cast()
|
||||
return
|
||||
var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES)
|
||||
if(!eyes)
|
||||
return
|
||||
eyes.sight_flags = initial(eyes.sight_flags)
|
||||
switch(eyes.lighting_alpha)
|
||||
if (LIGHTING_PLANE_ALPHA_VISIBLE)
|
||||
eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
|
||||
if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
|
||||
eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
||||
if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE)
|
||||
eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE
|
||||
else
|
||||
eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
|
||||
eyes.sight_flags &= ~SEE_BLACKNESS
|
||||
user.update_sight()
|
||||
|
||||
/obj/effect/proc_holder/spell/self/lesser_shadowling_hivemind //Lets a thrall talk with their allies
|
||||
name = "Lesser Commune"
|
||||
desc = "Allows you to silently communicate with all other shadowlings and thralls."
|
||||
panel = "Thrall Abilities"
|
||||
charge_max = 50
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "commune"
|
||||
|
||||
/obj/effect/proc_holder/spell/self/lesser_shadowling_hivemind/cast(mob/living/carbon/human/user)
|
||||
if(!is_shadow_or_thrall(user))
|
||||
to_chat(user, "<span class='warning'><b>As you attempt to commune with the others, an agonizing spike of pain drives itself into your head!</b></span>")
|
||||
user.apply_damage(10, BRUTE, "head")
|
||||
return
|
||||
var/text = stripped_input(user, "What do you want to say your masters and fellow thralls?.", "Lesser Commune", "")
|
||||
if(!text)
|
||||
return
|
||||
text = "<span class='shadowling'><b>\[Thrall\]</b><i> [user.real_name]</i>: [text]</span>"
|
||||
for(var/T in GLOB.alive_mob_list)
|
||||
var/mob/M = T
|
||||
if(is_shadow_or_thrall(M))
|
||||
to_chat(M, text)
|
||||
if(isobserver(M))
|
||||
to_chat(M, "<a href='?src=[REF(M)];follow=[REF(user)]'>(F)</a> [text]")
|
||||
log_say("[user.real_name]/[user.key] : [text]")
|
||||
|
||||
|
||||
|
||||
|
||||
// ASCENDANT ABILITIES BEYOND THIS POINT //
|
||||
// YES THEY'RE OP, BUT THEY'VE WON AT THE POINT WHERE THEY HAVE THIS, SO WHATEVER. //
|
||||
/obj/effect/proc_holder/spell/targeted/sling/annihilate //Gibs someone instantly.
|
||||
name = "Annihilate"
|
||||
desc = "Gibs someone instantly."
|
||||
panel = "Ascendant"
|
||||
range = 7
|
||||
charge_max = 0
|
||||
clothes_req = FALSE
|
||||
action_icon_state = "annihilate"
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
sound = 'sound/magic/Staff_Chaos.ogg'
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sling/annihilate/InterceptClickOn(mob/living/caller, params, atom/t)
|
||||
. = ..()
|
||||
var/mob/living/boom = target
|
||||
if(user.incorporeal_move)
|
||||
to_chat(user, "<span class='warning'>You are not in the same plane of existence. Unphase first.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(is_shadow(boom)) //Used to not work on thralls. Now it does so you can PUNISH THEM LIKE THE WRATHFUL GOD YOU ARE.
|
||||
to_chat(user, "<span class='warning'>Making an ally explode seems unwise.<span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(istype(boom, /mob/living/simple_animal/pet/dog/corgi))
|
||||
to_chat(user, "<span class='warning'>Not even we are that bad of monsters..<span>")
|
||||
revert_cast()
|
||||
return
|
||||
if (!boom.is_holding(/obj/item/storage/backpack/holding)) //so people actually have a chance to kill ascended slings without being insta-sploded
|
||||
user.visible_message("<span class='warning'>[user]'s markings flare as they gesture at [boom]!</span>", \
|
||||
"<span class='shadowling'>You direct a lance of telekinetic energy into [boom].</span>")
|
||||
if(iscarbon(boom))
|
||||
playsound(boom, 'sound/magic/Disintegrate.ogg', 100, 1)
|
||||
boom.visible_message("<span class='userdanger'>[boom] explodes!</span>")
|
||||
boom.gib()
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The telekinetic energy is absorbed by the bluespace portal in [boom]'s hand!<span>")
|
||||
to_chat(boom, "<span class='userdanger'>You feel a slight recoil from the bag of holding!<span>")
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sling/hypnosis //Enthralls someone instantly. Nonlethal alternative to Annihilate
|
||||
name = "Hypnosis"
|
||||
desc = "Instantly enthralls a human."
|
||||
panel = "Ascendant"
|
||||
range = 7
|
||||
charge_max = 0
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "enthrall"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sling/hypnosis/InterceptClickOn(mob/living/caller, params, atom/t)
|
||||
. = ..()
|
||||
if(user.incorporeal_move)
|
||||
revert_cast()
|
||||
to_chat(user, "<span class='warning'>You are not in the same plane of existence. Unphase first.</span>")
|
||||
return
|
||||
if(is_shadow_or_thrall(target))
|
||||
to_chat(user, "<span class='warning'>You cannot enthrall an ally.<span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(!target.ckey || !target.mind)
|
||||
to_chat(user, "<span class='warning'>The target has no mind.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(target.stat)
|
||||
to_chat(user, "<span class='warning'>The target must be conscious.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
if(!ishuman(target))
|
||||
to_chat(user, "<span class='warning'>You can only enthrall humans.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
to_chat(user, "<span class='shadowling'>You instantly rearrange <b>[target]</b>'s memories, hyptonitizing them into a thrall.</span>")
|
||||
to_chat(target, "<span class='userdanger'><font size=3>An agonizing spike of pain drives into your mind, and--</font></span>")
|
||||
target.mind.special_role = "thrall"
|
||||
target.add_thrall()
|
||||
|
||||
/obj/effect/proc_holder/spell/self/shadowling_phase_shift //Permanent version of shadow walk with no drawback. Toggleable.
|
||||
name = "Phase Shift"
|
||||
desc = "Phases you into the space between worlds at will, allowing you to move through walls and become invisible."
|
||||
panel = "Ascendant"
|
||||
charge_max = 15
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "shadow_walk"
|
||||
|
||||
/obj/effect/proc_holder/spell/self/shadowling_phase_shift/cast(mob/living/simple_animal/ascendant_shadowling/user)
|
||||
user.incorporeal_move = !user.incorporeal_move
|
||||
if(user.incorporeal_move)
|
||||
user.visible_message("<span class='danger'>[user] suddenly vanishes!</span>", \
|
||||
"<span class='shadowling'>You begin phasing through planes of existence. Use the ability again to return.</span>")
|
||||
user.density = 0
|
||||
user.alpha = 0
|
||||
else
|
||||
user.visible_message("<span class='danger'>[user] suddenly appears from nowhere!</span>", \
|
||||
"<span class='shadowling'>You return from the space between worlds.</span>")
|
||||
user.density = 1
|
||||
user.alpha = 255
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/ascendant_storm //Releases bolts of lightning to everyone nearby
|
||||
name = "Lightning Storm"
|
||||
desc = "Shocks everyone nearby."
|
||||
panel = "Ascendant"
|
||||
range = 6
|
||||
charge_max = 100
|
||||
clothes_req = FALSE
|
||||
action_icon_state = "lightning_storm"
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
sound = 'sound/magic/lightningbolt.ogg'
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/ascendant_storm/cast(list/targets,mob/living/simple_animal/ascendant_shadowling/user = usr)
|
||||
if(user.incorporeal_move)
|
||||
to_chat(user, "<span class='warning'>You are not in the same plane of existence. Unphase first.</span>")
|
||||
revert_cast()
|
||||
return
|
||||
user.visible_message("<span class='warning'><b>A massive ball of lightning appears in [user]'s hands and flares out!</b></span>", \
|
||||
"<span class='shadowling'>You conjure a ball of lightning and release it.</span>")
|
||||
for(var/mob/living/carbon/human/target in view(6))
|
||||
if(is_shadow_or_thrall(target))
|
||||
continue
|
||||
to_chat(target, "<span class='userdanger'>You're struck by a bolt of lightning!</span>")
|
||||
target.apply_damage(10, BURN)
|
||||
playsound(target, 'sound/magic/LightningShock.ogg', 50, 1)
|
||||
target.Knockdown(80)
|
||||
user.Beam(target,icon_state="red_lightning",time=10)
|
||||
|
||||
/obj/effect/proc_holder/spell/self/shadowling_hivemind_ascendant //Large, all-caps text in shadowling chat
|
||||
name = "Ascendant Commune"
|
||||
desc = "Allows you to LOUDLY communicate with all other shadowlings and thralls."
|
||||
panel = "Ascendant"
|
||||
charge_max = 0
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "commune"
|
||||
|
||||
/obj/effect/proc_holder/spell/self/shadowling_hivemind_ascendant/cast(mob/living/carbon/human/user)
|
||||
var/text = stripped_input(user, "What do you want to say to fellow thralls and shadowlings?.", "Hive Chat", "")
|
||||
if(!text)
|
||||
return
|
||||
text = "<font size=4><span class='shadowling'><b>\[Ascendant\]<i> [user.real_name]</i>: [text]</b></span></font>"
|
||||
for(var/mob/M in GLOB.mob_list)
|
||||
if(is_shadow_or_thrall(M))
|
||||
to_chat(M, text)
|
||||
if(isobserver(M))
|
||||
to_chat(M, "<a href='?src=[REF(M)];follow=[REF(user)]'>(F)</a> [text]")
|
||||
log_say("[user.real_name]/[user.key] : [text]")
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sling/instant_enthrall //Enthralls someone instantly. Nonlethal alternative to Annihilate
|
||||
name = "Subjugate"
|
||||
desc = "Instantly enthrall a weakling."
|
||||
panel = "Ascendant"
|
||||
range = 7
|
||||
charge_max = 0
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "gore"
|
||||
@@ -0,0 +1,33 @@
|
||||
/obj/item/clothing/suit/space/shadowling
|
||||
name = "chitin shell"
|
||||
desc = "A dark, semi-transparent shell. Protects against vacuum, but not against the light of the stars." //Still takes damage from spacewalking but is immune to space itself
|
||||
alternate_worn_icon = 'yogstation/icons/mob/suit.dmi'
|
||||
icon = 'yogstation/icons/obj/clothing/suits.dmi'
|
||||
icon_state = "sl_shell"
|
||||
item_state = "sl_shell"
|
||||
body_parts_covered = FULL_BODY //Shadowlings are immune to space
|
||||
cold_protection = FULL_BODY
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
|
||||
flags_inv = HIDEGLOVES | HIDESHOES | HIDEJUMPSUIT
|
||||
slowdown = 0
|
||||
heat_protection = null //You didn't expect a light-sensitive creature to have heat resistance, did you?
|
||||
max_heat_protection_temperature = null
|
||||
armor = list("melee" = 25, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 25, "bio" = 100, "rad" = 100)
|
||||
item_flags = NODROP | ABSTRACT
|
||||
clothing_flags = THICKMATERIAL | STOPSPRESSUREDAMAGE
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
|
||||
/obj/item/clothing/head/shadowling
|
||||
name = "chitin helm"
|
||||
desc = "A helmet-like enclosure of the head."
|
||||
alternate_worn_icon = 'yogstation/icons/mob/suit.dmi'
|
||||
icon = 'yogstation/icons/obj/clothing/suits.dmi'
|
||||
icon_state = "sl_head"
|
||||
item_state = "sl_head"
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
|
||||
heat_protection = HEAD
|
||||
max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT
|
||||
clothing_flags = STOPSPRESSUREDAMAGE
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
item_flags = NODROP | ABSTRACT
|
||||
@@ -0,0 +1,189 @@
|
||||
//In here: Hatch and Ascendance
|
||||
/obj/effect/proc_holder/spell/self/shadowling_hatch
|
||||
name = "Hatch"
|
||||
desc = "Casts off your disguise."
|
||||
panel = "Shadowling Evolution"
|
||||
charge_max = 3000
|
||||
human_req = TRUE
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "hatch"
|
||||
|
||||
/obj/structure/alien/resin/wall/shadowling //For chrysalis
|
||||
name = "chrysalis wall"
|
||||
desc = "Some sort of purple substance in an egglike shape. It pulses and throbs from within and seems impenetrable."
|
||||
max_integrity = INFINITY
|
||||
|
||||
/obj/effect/proc_holder/spell/self/shadowling_hatch/cast(list/targets,mob/user = usr)
|
||||
if(user.stat || !ishuman(user) || !user || !is_shadow(user || isinspace(user)))
|
||||
return
|
||||
var/mob/living/carbon/human/H = user
|
||||
var/hatch_or_no = alert(H,"Are you sure you want to hatch? You cannot undo this!",,"Yes","No")
|
||||
switch(hatch_or_no)
|
||||
if("No")
|
||||
to_chat(H, "<span class='warning'>You decide against hatching for now.")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if("Yes")
|
||||
H.notransform = TRUE
|
||||
H.visible_message("<span class='warning'>[H]'s things suddenly slip off. They hunch over and vomit up a copious amount of purple goo which begins to shape around them!</span>", \
|
||||
"<span class='shadowling'>You remove any equipment which would hinder your hatching and begin regurgitating the resin which will protect you.</span>")
|
||||
var/temp_flags = H.status_flags
|
||||
H.status_flags |= GODMODE //Can't die while hatching
|
||||
H.unequip_everything()
|
||||
if(!do_mob(H,H,50,1))
|
||||
return
|
||||
var/turf/shadowturf = get_turf(user)
|
||||
for(var/turf/open/floor/F in orange(1, user))
|
||||
new /obj/structure/alien/resin/wall/shadowling(F)
|
||||
for(var/obj/structure/alien/resin/wall/shadowling/R in shadowturf) //extremely hacky
|
||||
qdel(R)
|
||||
new /obj/structure/alien/weeds/node(shadowturf) //Dim lighting in the chrysalis -- removes itself afterwards
|
||||
H.visible_message("<span class='warning'>A chrysalis forms around [H], sealing them inside.</span>", \
|
||||
"<span class='shadowling'>You create your chrysalis and begin to contort within.</span>")
|
||||
if(!do_mob(H,H,100,1))
|
||||
return
|
||||
H.visible_message("<span class='warning'><b>The skin on [H]'s back begins to split apart. Black spines slowly emerge from the divide.</b></span>", \
|
||||
"<span class='shadowling'>Spines pierce your back. Your claws break apart your fingers. You feel excruciating pain as your true form begins its exit.</span>")
|
||||
if(!do_mob(H,H,90,1))
|
||||
return
|
||||
H.visible_message("<span class='warning'><b>[H], skin shifting, begins tearing at the walls around them.</b></span>", \
|
||||
"<span class='shadowling'>Your false skin slips away. You begin tearing at the fragile membrane protecting you.</span>")
|
||||
if(!do_mob(H,H,80,1))
|
||||
return
|
||||
playsound(H.loc, 'sound/weapons/slash.ogg', 25, 1)
|
||||
to_chat(H, "<i><b>You rip and slice.</b></i>")
|
||||
if(!do_mob(H,H,10,1))
|
||||
return
|
||||
playsound(H.loc, 'sound/weapons/slashmiss.ogg', 25, 1)
|
||||
to_chat(H, "<i><b>The chrysalis falls like water before you.</b></i>")
|
||||
if(!do_mob(H,H,10,1))
|
||||
return
|
||||
playsound(H.loc, 'sound/weapons/slice.ogg', 25, 1)
|
||||
to_chat(H, "<i><b>You are free!</b></i>")
|
||||
H.status_flags = temp_flags
|
||||
if(!do_mob(H,H,10,1))
|
||||
return
|
||||
playsound(H.loc, 'sound/effects/ghost.ogg', 100, 1)
|
||||
var/newNameId = pick(GLOB.nightmare_names)
|
||||
var/oldName = H.real_name
|
||||
GLOB.nightmare_names.Remove(newNameId)
|
||||
H.real_name = newNameId
|
||||
H.name = user.real_name
|
||||
H.notransform = FALSE
|
||||
to_chat(H, "<i><b><font size=3>YOU LIVE!!!</i></b></font>")
|
||||
var/hatchannounce = "<font size=3><span class='shadowling'><b>[oldName] has hatched into the Shadowling [newNameId]!</b></span></font>"
|
||||
for(var/T in GLOB.alive_mob_list)
|
||||
var/mob/M = T
|
||||
if(is_shadow_or_thrall(M))
|
||||
to_chat(M, hatchannounce)
|
||||
for(var/T in GLOB.dead_mob_list)
|
||||
var/mob/M = T
|
||||
to_chat(M, "<a href='?src=[REF(M)];follow=[REF(user)]'>(F)</a> [hatchannounce]")
|
||||
for(var/obj/structure/alien/resin/wall/shadowling/W in orange(1, H))
|
||||
playsound(W, 'sound/effects/splat.ogg', 50, 1)
|
||||
qdel(W)
|
||||
for(var/obj/structure/alien/weeds/node/N in shadowturf)
|
||||
qdel(N)
|
||||
H.visible_message("<span class='warning'>The chrysalis explodes in a shower of purple flesh and fluid!</span>")
|
||||
H.underwear = "Nude"
|
||||
H.undershirt = "Nude"
|
||||
H.socks = "Nude"
|
||||
H.faction |= "faithless"
|
||||
H.LoadComponent(/datum/component/walk/shadow)
|
||||
|
||||
H.equip_to_slot_or_del(new /obj/item/clothing/suit/space/shadowling(H), SLOT_WEAR_SUIT)
|
||||
H.equip_to_slot_or_del(new /obj/item/clothing/head/shadowling(H), SLOT_HEAD)
|
||||
H.set_species(/datum/species/shadow/ling) //can't be a shadowling without being a shadowling
|
||||
H.mind.RemoveSpell(src)
|
||||
if(!do_mob(H,H,10,1))
|
||||
return
|
||||
to_chat(H, "<span class='shadowling'><b><i>Your powers are awoken. You may now live to your fullest extent. Remember your goal. Cooperate with your thralls and allies.</b></i></span>")
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/enthrall(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/sling/glare(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/veil(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/flashfreeze(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/collective_mind(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_regenarmor(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle(null))
|
||||
|
||||
/obj/effect/proc_holder/spell/self/shadowling_ascend
|
||||
name = "Ascend"
|
||||
desc = "Enters your true form."
|
||||
panel = "Shadowling Evolution"
|
||||
charge_max = 3000
|
||||
clothes_req = FALSE
|
||||
action_icon = 'yogstation/icons/mob/actions.dmi'
|
||||
action_icon_state = "ascend"
|
||||
|
||||
/obj/effect/proc_holder/spell/self/shadowling_ascend/cast(list/targets,mob/user = usr)
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(!shadowling_check(H))
|
||||
return
|
||||
var/hatch_or_no = alert(H,"It is time to ascend. Are you sure about this?",,"Yes","No")
|
||||
switch(hatch_or_no)
|
||||
if("No")
|
||||
to_chat(H, "<span class='warning'>You decide against ascending for now.")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if("Yes")
|
||||
H.notransform = 1
|
||||
H.visible_message("<span class='warning'>[H]'s things suddenly slip off. They gently rise into the air, red light glowing in their eyes.</span>", \
|
||||
"<span class='shadowling'>You rise into the air and get ready for your transformation.</span>")
|
||||
for(var/obj/item/I in H) //drops all items
|
||||
H.unequip_everything(I)
|
||||
if(!do_mob(H,H,50,1))
|
||||
return
|
||||
H.visible_message("<span class='warning'>[H]'s skin begins to crack and harden.</span>", \
|
||||
"<span class='shadowling'>Your flesh begins creating a shield around yourself.</span>")
|
||||
if(!do_mob(H,H,100,1))
|
||||
return
|
||||
H.visible_message("<span class='warning'>The small horns on [H]'s head slowly grow and elongate.</span>", \
|
||||
"<span class='shadowling'>Your body continues to mutate. Your telepathic abilities grow.</span>") //y-your horns are so big, senpai...!~
|
||||
if(!do_mob(H,H,90,1))
|
||||
return
|
||||
H.visible_message("<span class='warning'>[H]'s body begins to violently stretch and contort.</span>", \
|
||||
"<span class='shadowling'>You begin to rend apart the final barriers to godhood.</span>")
|
||||
if(!do_mob(H,H,40,1))
|
||||
return
|
||||
to_chat(H, "<i><b>Yes!</b></i>")
|
||||
if(!do_mob(H,H,10,1))
|
||||
return
|
||||
to_chat(H, "<i><b><span class='big'>YES!!</span></b></i>")
|
||||
if(!do_mob(H,H,10,1))
|
||||
return
|
||||
to_chat(H, "<i><b><span class='reallybig'>YE--</span></b></i>")
|
||||
if(!do_mob(H,H,1,1))
|
||||
return
|
||||
for(var/mob/living/M in orange(7, H))
|
||||
M.Knockdown(10)
|
||||
to_chat(M, "<span class='userdanger'>An immense pressure slams you onto the ground!</span>")
|
||||
to_chat(world, "<font size=5><span class='shadowling'><b>\"VYSHA NERADA YEKHEZET U'RUU!!\"</font></span>")
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
SEND_SOUND(M, sound('sound/hallucinations/veryfar_noise.ogg'))
|
||||
for(var/obj/machinery/power/apc/A in GLOB.apcs_list)
|
||||
A.overload_lighting()
|
||||
var/mob/A = new /mob/living/simple_animal/ascendant_shadowling(H.loc)
|
||||
for(var/X in H.mind.spell_list)
|
||||
var/obj/effect/proc_holder/spell/S = X
|
||||
if(S == src) continue
|
||||
H.mind.RemoveSpell(S)
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/sling/annihilate(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/sling/hypnosis(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_phase_shift(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/ascendant_storm(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_hivemind_ascendant(null))
|
||||
H.mind.transfer_to(A)
|
||||
A.name = H.real_name
|
||||
if(A.real_name)
|
||||
A.real_name = H.real_name
|
||||
H.invisibility = 60 //This is pretty bad, but is also necessary for the shuttle call to function properly
|
||||
H.forceMove(A)
|
||||
if(!SSticker.mode.shadowling_ascended)
|
||||
set_security_level(SEC_LEVEL_DELTA)
|
||||
SSshuttle.emergencyCallTime = 1800
|
||||
SSshuttle.emergency.request(null, 0.3)
|
||||
SSshuttle.emergencyNoRecall = TRUE
|
||||
SSticker.mode.shadowling_ascended = TRUE
|
||||
A.mind.RemoveSpell(src)
|
||||
qdel(H)
|
||||
52
yogstation/code/modules/antagonists/shadowling/thrall.dm
Normal file
@@ -0,0 +1,52 @@
|
||||
GLOBAL_LIST_INIT(thrall_spell_types, typecacheof(/obj/effect/proc_holder/spell/self/lesser_shadowling_hivemind, /obj/effect/proc_holder/spell/targeted/lesser_glare, /obj/effect/proc_holder/spell/self/lesser_shadow_walk, /obj/effect/proc_holder/spell/self/thrall_night_vision))
|
||||
|
||||
/datum/antagonist/thrall
|
||||
name = "Shadowling Thrall"
|
||||
job_rank = ROLE_SHADOWLING
|
||||
roundend_category = "thralls"
|
||||
antagpanel_category = "Shadowlings"
|
||||
antag_moodlet = /datum/mood_event/thrall
|
||||
|
||||
/datum/antagonist/thrall/on_gain()
|
||||
. = ..()
|
||||
SSticker.mode.update_shadow_icons_added(owner)
|
||||
SSticker.mode.thralls += owner
|
||||
owner.special_role = "thrall"
|
||||
message_admins("[key_name_admin(owner.current)] was enthralled by a shadowling!")
|
||||
log_game("[key_name(owner.current)] was enthralled by a shadowling!")
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/self/lesser_shadowling_hivemind(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/lesser_glare(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/self/lesser_shadow_walk(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/self/thrall_night_vision(null))
|
||||
|
||||
/datum/antagonist/thrall/on_removal()
|
||||
SSticker.mode.update_shadow_icons_removed(owner)
|
||||
SSticker.mode.thralls -= owner
|
||||
message_admins("[key_name_admin(owner.current)] was dethralled!")
|
||||
log_game("[key_name(owner.current)] was dethralled!")
|
||||
owner.special_role = null
|
||||
for(var/X in owner.spell_list)
|
||||
var/obj/effect/proc_holder/spell/S = X
|
||||
if(is_type_in_typecache(S, GLOB.thrall_spell_types)) //only remove thrall spells!
|
||||
owner.RemoveSpell(S)
|
||||
var/mob/living/M = owner.current
|
||||
if(issilicon(M))
|
||||
M.audible_message("<span class='notice'>[M] lets out a short blip, followed by a low-pitched beep.</span>", \
|
||||
"<span class='userdanger'>You have been turned into a robot! You are no longer a thrall! Though you try, you cannot remember anything about your servitude...</span>")
|
||||
else
|
||||
M.visible_message("<span class='big'>[M] looks like their mind is their own again!</span>", \
|
||||
"<span class='userdanger'>A piercing white light floods your eyes. Your mind is your own again! Though you try, you cannot remember anything about the shadowlings or your time \
|
||||
under their command...</span>")
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/thrall/greet()
|
||||
to_chat(owner, "<span class='shadowling'><b>You see the truth. Reality has been torn away and you realize what a fool you've been.</b></span>")
|
||||
to_chat(owner, "<span class='shadowling'><b>The shadowlings are your masters.</b> Serve them above all else and ensure they complete their goals.</b></span>")
|
||||
to_chat(owner, "<span class='shadowling'>You may not harm other thralls or the shadowlings. However, you do not need to obey other thralls.</span>")
|
||||
to_chat(owner, "<span class='shadowling'>Your body has been irreversibly altered. The attentive can see this - you may conceal it by wearing a mask.</span>")
|
||||
to_chat(owner, "<span class='shadowling'>Though not nearly as powerful as your masters, you possess some weak powers. These can be found in the Thrall Abilities tab.</span>")
|
||||
to_chat(owner, "<span class='shadowling'>You may communicate with your allies by using the Lesser Commune ability.</span>")
|
||||
SEND_SOUND(owner.current, sound('yogstation/sound/ambience/antag/thrall.ogg'))
|
||||
|
||||
/datum/antagonist/thrall/roundend_report()
|
||||
return "<div class='panel redborder'>[printplayerlist(SSticker.mode.thralls)]</div>"
|
||||
10
yogstation/code/modules/mob/living/carbon/human/species.dm
Normal file
@@ -0,0 +1,10 @@
|
||||
////////////////////
|
||||
/////BODYPARTS/////
|
||||
////////////////////
|
||||
/obj/item/bodypart
|
||||
var/should_draw_yogs = FALSE
|
||||
|
||||
/mob/living/carbon/proc/draw_yogs_parts(do_it)
|
||||
for(var/O in bodyparts)
|
||||
var/obj/item/bodypart/B = O
|
||||
B.should_draw_yogs = do_it
|
||||
71
yogstation/code/modules/surgery/organs/shadowling_organs.dm
Normal file
@@ -0,0 +1,71 @@
|
||||
//Snowflake spot for putting sling organ related stuff
|
||||
/obj/item/organ/eyes/night_vision/alien/sling
|
||||
name = "shadowling eyes"
|
||||
desc = "The eyes of a spooky shadowling!"
|
||||
|
||||
/obj/item/organ/internal/shadowtumor
|
||||
name = "black tumor"
|
||||
desc = "A tiny black mass with red tendrils trailing from it. It seems to shrivel in the light."
|
||||
icon_state = "blacktumor"
|
||||
w_class = 1
|
||||
zone = "head"
|
||||
slot = "brain_tumor"
|
||||
var/organ_health = 3
|
||||
|
||||
/obj/item/organ/internal/shadowtumor/New()
|
||||
..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/organ/internal/shadowtumor/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
..()
|
||||
|
||||
/obj/item/organ/internal/shadowtumor/process()
|
||||
if(isturf(loc))
|
||||
var/turf/T = loc
|
||||
var/light_count = T.get_lumcount()
|
||||
if(light_count > LIGHT_DAM_THRESHOLD && organ_health > 0) //Die in the light
|
||||
organ_health--
|
||||
else if(light_count < LIGHT_HEAL_THRESHOLD && organ_health < 3) //Heal in the dark
|
||||
organ_health++
|
||||
if(organ_health <= 0)
|
||||
visible_message("<span class='warning'>[src] collapses in on itself!</span>")
|
||||
qdel(src)
|
||||
else
|
||||
organ_health += 0.5
|
||||
|
||||
/obj/item/organ/internal/shadowtumor/Insert(mob/living/carbon/M, special, drop_if_replaced)
|
||||
. = ..()
|
||||
M.add_thrall()
|
||||
|
||||
/obj/item/organ/internal/shadowtumor/on_find(mob/living/finder)
|
||||
. = ..()
|
||||
finder.visible_message("<span class='danger'>[finder] opens up [owner]'s skull, revealing a pulsating black mass, with red tendrils attaching it to [owner.p_their()] brain.</span>'")
|
||||
|
||||
/obj/item/organ/internal/shadowtumor/Remove(mob/living/carbon/M, special)
|
||||
if(M.dna.species.id == "l_shadowling") //Empowered thralls cannot be deconverted
|
||||
to_chat(M, "<span class='shadowling'><b><i>NOT LIKE THIS!</i></b></span>")
|
||||
M.visible_message("<span class='danger'>[M] suddenly slams upward and knocks down everyone!</span>")
|
||||
M.resting = FALSE //Remove all stuns
|
||||
M.SetStun(0, 0)
|
||||
M.SetKnockdown(0)
|
||||
M.SetUnconscious(0)
|
||||
for(var/mob/living/user in range(2, src))
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/C = user
|
||||
C.Knockdown(6)
|
||||
C.adjustBruteLoss(20)
|
||||
else if(issilicon(user))
|
||||
var/mob/living/silicon/S = user
|
||||
S.Knockdown(8)
|
||||
S.adjustBruteLoss(20)
|
||||
playsound(S, 'sound/effects/bang.ogg', 50, 1)
|
||||
return FALSE
|
||||
var/obj/item/organ/eyes/eyes = M.getorganslot(ORGAN_SLOT_EYES)
|
||||
eyes.lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
|
||||
eyes.sight_flags = initial(eyes.sight_flags)
|
||||
M.update_sight()
|
||||
M.remove_thrall()
|
||||
M.visible_message("<span class='warning'>A strange black mass falls from [M]'s head!</span>")
|
||||
new /obj/item/organ/internal/shadowtumor(get_turf(M))
|
||||
return ..()
|
||||
BIN
yogstation/icons/mob/actions.dmi
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
yogstation/icons/mob/hud.dmi
Normal file
|
After Width: | Height: | Size: 292 B |
BIN
yogstation/icons/mob/mob.dmi
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
yogstation/icons/mob/mutant_bodyparts.dmi
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
yogstation/icons/mob/sling.dmi
Normal file
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 21 KiB |