This commit is contained in:
Poojawa
2018-01-31 06:56:28 -06:00
parent fb5de29784
commit a7f0ffb6f0
78 changed files with 2093 additions and 1191 deletions
+1 -1
View File
@@ -335,7 +335,7 @@ proc/get_message_output(type, target_ckey)
if(!query_message_read.warn_execute())
return
if("watchlist entry")
message_admins("<font color='red'><B>Notice: </B></font><font color='blue'>[key_name_admin(target_ckey)] is on the watchlist and has just connected - Reason: [text]</font>")
message_admins("<font color='red'><B>Notice: </B></font><font color='blue'>[key_name_admin(target_ckey)] has been on the watchlist since [timestamp] and has just connected - Reason: [text]</font>")
send2irc_adminless_only("Watchlist", "[key_name(target_ckey)] is on the watchlist and has just connected - Reason: [text]")
if("memo")
output += "<span class='memo'>Memo by <span class='prefix'>[admin_ckey]</span> on [timestamp]"
@@ -558,7 +558,10 @@ Congratulations! You are now trained for invasive xenobiology research!"}
if(temp)
helptext = "<span class='warning'>Experimental gland detected!</span>"
else
helptext = "<span class='notice'>Subject suitable for experiments.</span>"
if (L.getorganslot(ORGAN_SLOT_HEART))
helptext = "<span class='notice'>Subject suitable for experiments.</span>"
else
helptext = "<span class='warning'>Subject unsuitable for experiments.</span>"
to_chat(user, "<span class='notice'>Probing result:</span>[species]")
to_chat(user, "[helptext]")
@@ -53,3 +53,48 @@
var/obj/item/organ/heart/gland/gland = tool
gland.Insert(target, 2)
return 1
/datum/surgery/pacify
name = "violence neutralization"
steps = list(/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
/datum/surgery_step/saw,
/datum/surgery_step/clamp_bleeders,
/datum/surgery_step/pacify,
/datum/surgery_step/close)
species = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
possible_locs = list("head")
requires_bodypart_type = 0
/datum/surgery/pacify/can_start(mob/user, mob/living/carbon/target)
if(!ishuman(user))
return FALSE
var/mob/living/carbon/human/H = user
. = FALSE
if(!(H.dna.species.id == "abductor"))
. = TRUE
for(var/obj/item/implant/abductor/A in H.implants)
. = TRUE
var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN)
if(!B)
to_chat(user, "<span class='warning'>It's hard to do surgery on someone's brain when they don't have one.</span>")
return FALSE
/datum/surgery_step/pacify
name = "rewire brain"
implements = list(/obj/item/hemostat = 100, /obj/item/screwdriver = 35, /obj/item/pen = 15)
time = 40
/datum/surgery_step/pacify/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user] begins to reshape [target]'s brain.", "<span class='notice'>You begin to reshape [target]'s brain...</span>")
/datum/surgery_step/pacify/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user] successfully reshapes [target]'s brain!", "<span class='notice'>You succeed in reshaping [target]'s brain.</span>")
target.gain_trauma(/datum/brain_trauma/severe/pacifism)
return TRUE
/datum/surgery_step/pacify/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user] successfully reshapes [target]'s brain!", "<span class='notice'>You screwed up, and rewired [target]'s brain the wrong way around...</span>")
target.gain_trauma_type(BRAIN_TRAUMA_SEVERE)
return FALSE
@@ -8,7 +8,7 @@
var/cooldown_low = 300
var/cooldown_high = 300
var/next_activation = 0
var/uses // -1 For inifinite
var/uses // -1 For infinite
var/human_only = 0
var/active = 0
@@ -104,9 +104,9 @@
/obj/item/organ/heart/gland/heals/activate()
to_chat(owner, "<span class='notice'>You feel curiously revitalized.</span>")
owner.adjustBruteLoss(-20)
owner.adjustToxLoss(-20, FALSE, TRUE)
owner.heal_bodypart_damage(20, 20, TRUE)
owner.adjustOxyLoss(-20)
owner.adjustFireLoss(-20)
/obj/item/organ/heart/gland/slime
cooldown_low = 600
@@ -116,18 +116,22 @@
mind_control_uses = 1
mind_control_duration = 2400
/obj/item/organ/heart/gland/slime/Insert(mob/living/carbon/M, special = 0)
..()
owner.faction |= "slime"
owner.grant_language(/datum/language/slime)
/obj/item/organ/heart/gland/slime/activate()
to_chat(owner, "<span class='warning'>You feel nauseous!</span>")
owner.vomit(20)
var/mob/living/simple_animal/slime/Slime
Slime = new(get_turf(owner), "grey")
var/mob/living/simple_animal/slime/Slime = new(get_turf(owner), "grey")
Slime.Friends = list(owner)
Slime.Leader = owner
/obj/item/organ/heart/gland/mindshock
cooldown_low = 300
cooldown_high = 300
cooldown_low = 400
cooldown_high = 700
uses = -1
icon_state = "mindshock"
mind_control_uses = 1
@@ -140,21 +144,30 @@
for(var/mob/living/carbon/H in orange(4,T))
if(H == owner)
continue
to_chat(H, "<span class='alien'>You hear a buzz in your head.</span>")
H.confused += 20
switch(pick(1,3))
if(1)
to_chat(H, "<span class='userdanger'>You hear a loud buzz in your head, silencing your thoughts!</span>")
H.Stun(50)
if(2)
to_chat(H, "<span class='warning'>You hear an annoying buzz in your head.</span>")
H.confused += 15
H.adjustBrainLoss(10, 160)
if(3)
H.hallucination += 80
/obj/item/organ/heart/gland/pop
cooldown_low = 900
cooldown_high = 1800
uses = -1
human_only = 1
human_only = TRUE
icon_state = "species"
mind_control_uses = 5
mind_control_duration = 300
/obj/item/organ/heart/gland/pop/activate()
to_chat(owner, "<span class='notice'>You feel unlike yourself.</span>")
var/species = pick(list(/datum/species/lizard, /datum/species/jelly/slime, /datum/species/pod, /datum/species/fly, /datum/species/jelly))
randomize_human(owner)
var/species = pick(list(/datum/species/human, /datum/species/lizard, /datum/species/moth, /datum/species/fly))
owner.set_species(species)
/obj/item/organ/heart/gland/ventcrawling
@@ -169,7 +182,6 @@
to_chat(owner, "<span class='notice'>You feel very stretchy.</span>")
owner.ventcrawler = VENTCRAWLER_ALWAYS
/obj/item/organ/heart/gland/viral
cooldown_low = 1800
cooldown_high = 2400
@@ -180,30 +192,55 @@
/obj/item/organ/heart/gland/viral/activate()
to_chat(owner, "<span class='warning'>You feel sick.</span>")
var/virus_type = pick(/datum/disease/beesease, /datum/disease/brainrot, /datum/disease/magnitis)
var/datum/disease/D = new virus_type()
D.carrier = TRUE
owner.viruses += D
D.affected_mob = owner
var/datum/disease/advance/A = random_virus(pick(2,6),6)
A.carrier = TRUE
owner.viruses += A
A.affected_mob = owner
owner.med_hud_set_status()
/obj/item/organ/heart/gland/viral/proc/random_virus(max_symptoms, max_level)
if(max_symptoms > SYMPTOM_LIMIT)
max_symptoms = SYMPTOM_LIMIT
var/datum/disease/advance/A = new(FALSE, null)
A.symptoms = list()
var/list/datum/symptom/possible_symptoms = list()
for(var/symptom in subtypesof(/datum/symptom))
var/datum/symptom/S = symptom
if(initial(S.level) > max_level)
continue
if(initial(S.level) <= 0) //unobtainable symptoms
continue
possible_symptoms += S
for(var/i in 1 to max_symptoms)
var/datum/symptom/chosen_symptom = pick_n_take(possible_symptoms)
if(chosen_symptom)
var/datum/symptom/S = new chosen_symptom
A.symptoms += S
A.Refresh() //just in case someone already made and named the same disease
return A
/obj/item/organ/heart/gland/emp //TODO : Replace with something more interesting
cooldown_low = 900
cooldown_high = 1600
uses = 10
/obj/item/organ/heart/gland/trauma //TODO : Replace with something more interesting
cooldown_low = 800
cooldown_high = 1200
uses = 5
icon_state = "emp"
mind_control_uses = 1
mind_control_uses = 3
mind_control_duration = 1800
/obj/item/organ/heart/gland/emp/activate()
/obj/item/organ/heart/gland/trauma/activate()
to_chat(owner, "<span class='warning'>You feel a spike of pain in your head.</span>")
empulse(get_turf(owner), 2, 5, 1)
if(prob(33))
owner.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRUE)
else
if(prob(20))
owner.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRUE)
else
owner.gain_trauma_type(BRAIN_TRAUMA_MILD, TRUE)
/obj/item/organ/heart/gland/spiderman
cooldown_low = 450
cooldown_high = 900
uses = 10
uses = -1
icon_state = "spider"
mind_control_uses = 2
mind_control_duration = 2400
@@ -211,7 +248,8 @@
/obj/item/organ/heart/gland/spiderman/activate()
to_chat(owner, "<span class='warning'>You feel something crawling in your skin.</span>")
owner.faction |= "spiders"
new /obj/structure/spider/spiderling(owner.loc)
var/obj/structure/spider/spiderling/S = new(owner.drop_location())
S.directive = "Protect your nest inside [owner.real_name]."
/obj/item/organ/heart/gland/egg
cooldown_low = 300
@@ -225,71 +263,60 @@
/obj/item/organ/heart/gland/egg/activate()
to_chat(owner, "<span class='boldannounce'>You lay an egg!</span>")
var/obj/item/reagent_containers/food/snacks/egg/egg = new(owner.loc)
var/obj/item/reagent_containers/food/snacks/egg/egg = new(owner.drop_location())
egg.reagents.add_reagent("sacid",20)
egg.desc += " It smells bad."
/obj/item/organ/heart/gland/bloody
cooldown_low = 200
cooldown_high = 400
/obj/item/organ/heart/gland/electric
cooldown_low = 800
cooldown_high = 1200
uses = -1
mind_control_uses = 1
mind_control_duration = 450
mind_control_uses = 2
mind_control_duration = 900
/obj/item/organ/heart/gland/bloody/activate()
owner.blood_volume -= 20
owner.visible_message("<span class='danger'>[owner]'s skin erupts with blood!</span>",\
"<span class='userdanger'>Blood pours from your skin!</span>")
/obj/item/organ/heart/gland/electric/Insert(mob/living/carbon/M, special = 0)
..()
owner.add_trait(TRAIT_SHOCKIMMUNE, "abductor_gland")
for(var/turf/T in oview(3,owner)) //Make this respect walls and such
owner.add_splatter_floor(T)
for(var/mob/living/carbon/human/H in oview(3,owner)) //Blood decals for simple animals would be neat. aka Carp with blood on it.
H.add_mob_blood(owner)
/obj/item/organ/heart/gland/electric/Remove(mob/living/carbon/M, special = 0)
owner.remove_trait(TRAIT_SHOCKIMMUNE, "abductor_gland")
..()
/obj/item/organ/heart/gland/electric/activate()
owner.visible_message("<span class='danger'>[owner]'s skin starts emitting electric arcs!</span>",\
"<span class='warning'>You feel electric energy building up inside you!</span>")
playsound(get_turf(owner), "sparks", 100, 1, -1)
addtimer(CALLBACK(src, .proc/zap), rand(30, 100))
/obj/item/organ/heart/gland/bodysnatch
cooldown_low = 600
cooldown_high = 600
human_only = 1
uses = 1
mind_control_uses = 1
mind_control_duration = 600
/obj/item/organ/heart/gland/electric/proc/zap()
tesla_zap(owner, 4, 8000, FALSE, TRUE)
playsound(get_turf(owner), 'sound/magic/lightningshock.ogg', 50, 1)
/obj/item/organ/heart/gland/bodysnatch/activate()
to_chat(owner, "<span class='warning'>You feel something moving around inside you...</span>")
//spawn cocoon with clone greytide snpc inside
if(ishuman(owner))
var/obj/structure/spider/cocoon/abductor/C = new (get_turf(owner))
C.Copy(owner)
C.Start()
owner.adjustBruteLoss(40)
owner.add_splatter_floor()
/obj/item/organ/heart/gland/chem
cooldown_low = 50
cooldown_high = 50
uses = -1
mind_control_uses = 3
mind_control_duration = 1200
var/list/possible_reagents = list()
/obj/structure/spider/cocoon/abductor
name = "slimy cocoon"
desc = "Something is moving inside."
icon = 'icons/effects/effects.dmi'
icon_state = "cocoon_large3"
color = rgb(10,120,10)
density = TRUE
var/hatch_time = 0
/obj/structure/spider/cocoon/abductor/proc/Copy(mob/living/carbon/human/H)
var/mob/living/carbon/human/interactive/greytide/clone = new(src)
clone.hardset_dna(H.dna.uni_identity,H.dna.struc_enzymes,H.real_name, H.dna.blood_type, H.dna.species, H.dna.features)
/obj/structure/spider/cocoon/abductor/proc/Start()
hatch_time = world.time + 600
START_PROCESSING(SSobj, src)
/obj/structure/spider/cocoon/abductor/process()
if(world.time > hatch_time)
STOP_PROCESSING(SSobj, src)
for(var/mob/M in contents)
src.visible_message("<span class='warning'>[src] hatches!</span>")
M.forceMove(drop_location())
qdel(src)
/obj/item/organ/heart/gland/chem/Initialize()
..()
for(var/X in subtypesof(/datum/reagent/drug))
var/datum/reagent/R = X
possible_reagents += initial(R.id)
for(var/X in subtypesof(/datum/reagent/medicine))
var/datum/reagent/R = X
possible_reagents += initial(R.id)
for(var/X in typesof(/datum/reagent/toxin))
var/datum/reagent/R = X
possible_reagents += initial(R.id)
/obj/item/organ/heart/gland/chem/activate()
var/chem_to_add = pick(possible_reagents)
owner.reagents.add_reagent(chem_to_add, 2)
owner.adjustToxLoss(-2, TRUE, TRUE)
..()
/obj/item/organ/heart/gland/plasma
cooldown_low = 1200
@@ -27,7 +27,10 @@ GLOBAL_LIST_EMPTY(hivemind_bank)
chemical_cost = 10
dna_cost = -1
/obj/effect/proc_holder/changeling/hivemind_upload/sting_action(var/mob/user)
/obj/effect/proc_holder/changeling/hivemind_upload/sting_action(var/mob/living/user)
if (user.has_trait(CHANGELING_HIVEMIND_MUTE))
to_chat(user, "<span class='warning'>The poison in the air hinders our ability to interact with the hivemind.</span>")
return
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
var/list/names = list()
for(var/datum/changelingprofile/prof in changeling.stored_profiles)
@@ -61,6 +64,9 @@ GLOBAL_LIST_EMPTY(hivemind_bank)
/obj/effect/proc_holder/changeling/hivemind_download/can_sting(mob/living/carbon/user)
if(!..())
return
if (user.has_trait(CHANGELING_HIVEMIND_MUTE))
to_chat(user, "<span class='warning'>The poison in the air hinders our ability to interact with the hivemind.</span>")
return
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
var/datum/changelingprofile/first_prof = changeling.stored_profiles[1]
if(first_prof.name == user.real_name)//If our current DNA is the stalest, we gotta ditch it.
@@ -8,7 +8,7 @@
anchored = TRUE
density = FALSE
opacity = 0
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
/obj/effect/clockwork/Initialize()
. = ..()
@@ -39,3 +39,6 @@
/obj/effect/clockwork/servant_blocker/singularity_pull()
return
/obj/effect/clockwork/servant_blocker/ex_act(severity, target)
return
@@ -7,6 +7,7 @@
if(GLOB.ratvar_approaches)
amount *= 0.75 //The herald's beacon reduces power costs by 25% across the board!
GLOB.clockwork_power = GLOB.ratvar_awakens ? INFINITY : max(0, GLOB.clockwork_power + amount)
GLOB.clockwork_power = CLAMP(GLOB.clockwork_power, 0, MAX_CLOCKWORK_POWER)
for(var/obj/effect/clockwork/sigil/transmission/T in GLOB.all_clockwork_objects)
T.update_icon()
var/power_overwhelming = GLOB.clockwork_power
@@ -43,8 +43,7 @@
/obj/item/clockwork/slab/cyborg //three scriptures, plus a spear and fabricator
clockwork_desc = "A divine link to the Celestial Derelict, allowing for limited recital of scripture."
quickbound = list(/datum/clockwork_scripture/ranged_ability/judicial_marker, /datum/clockwork_scripture/ranged_ability/linked_vanguard, \
/datum/clockwork_scripture/create_object/stargazer)
quickbound = list(/datum/clockwork_scripture/ranged_ability/judicial_marker, /datum/clockwork_scripture/ranged_ability/linked_vanguard)
maximum_quickbound = 6 //we usually have one or two unique scriptures, so if ratvar is up let us bind one more
actions_types = list()
@@ -63,10 +62,10 @@
/obj/item/clockwork/slab/cyborg/janitor //five scriptures, plus a fabricator
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/sigil_of_transgression, \
/datum/clockwork_scripture/create_object/ocular_warden, /datum/clockwork_scripture/create_object/mania_motor, /datum/clockwork_scripture/create_object/stargazer)
/datum/clockwork_scripture/create_object/ocular_warden, /datum/clockwork_scripture/create_object/mania_motor)
/obj/item/clockwork/slab/cyborg/service //five scriptures, plus xray vision
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, /datum/clockwork_scripture/create_object/stargazer, \
quickbound = list(/datum/clockwork_scripture/abscond, /datum/clockwork_scripture/create_object/replicant, \
/datum/clockwork_scripture/spatial_gateway, /datum/clockwork_scripture/create_object/clockwork_obelisk)
/obj/item/clockwork/slab/cyborg/miner //two scriptures, plus a spear and xray vision
@@ -425,7 +424,8 @@
data["scripturecolors"] = "<font color=#DAAA18>Scriptures in <b>yellow</b> are related to construction and building.</font><br>\
<font color=#6E001A>Scriptures in <b>red</b> are related to attacking and offense.</font><br>\
<font color=#1E8CE1>Scriptures in <b>blue</b> are related to healing and defense.</font><br>\
<font color=#AF0AAF>Scriptures in <b>purple</b> are niche but still important!</font>"
<font color=#AF0AAF>Scriptures in <b>purple</b> are niche but still important!</font><br>\
<font color=#DAAA18><i>Scriptures with italicized names are important to success.</i></font>"
generate_all_scripture()
data["scripture"] = list()
@@ -439,6 +439,8 @@
"required" = "([DisplayPower(S.power_cost)][S.special_power_text ? "+ [replacetext(S.special_power_text, "POWERCOST", "[DisplayPower(S.special_power_cost)]")]" : ""])",
"type" = "[S.type]",
"quickbind" = S.quickbind)
if(S.important)
temp_info["name"] = "<i>[temp_info["name"]]</i>"
var/found = quickbound.Find(S.type)
if(found)
temp_info["bound"] = "<b>[found]</b>"
@@ -30,6 +30,6 @@
var/obj/item/stock_parts/cell/cell = apc.cell
if(cell && (cell.charge / cell.maxcharge > COG_MAX_SIPHON_THRESHOLD))
cell.use(1)
adjust_clockwork_power(1) //Power is shared, so only do it once; this runs very quickly so it's about 1W/second
adjust_clockwork_power( ) //Power is shared, so only do it once; this runs very quickly so it's about 5 W/second
#undef COG_MAX_SIPHON_THRESHOLD
@@ -28,6 +28,7 @@ Applications: 8 servants, 3 caches, and 100 CV
var/quickbind = FALSE //if this scripture can be quickbound to a clockwork slab
var/quickbind_desc = "This shouldn't be quickbindable. File a bug report!"
var/primary_component
var/important = FALSE //important scripture will be italicized in the slab's interface
var/sort_priority = 1 //what position the scripture should have in a list of scripture. Should be based off of component costs/reqs, but you can't initial() lists.
//messages for offstation scripture recital, courtesy ratvar's generals(and neovgre)
@@ -5,7 +5,7 @@
//Sigil of Transmission: Creates a sigil of transmission that can drain and store power for clockwork structures.
/datum/clockwork_scripture/create_object/sigil_of_transmission
descname = "Powers Nearby Structures - Important!"
descname = "Powers Nearby Structures"
name = "Sigil of Transmission"
desc = "Places a sigil that can drain and will store energy to power clockwork structures."
invocations = list("Divinity...", "...power our creations!")
@@ -19,6 +19,7 @@
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 1
important = TRUE
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Transmission, which can drain and will store power for clockwork structures."
@@ -98,7 +99,7 @@
if(ishuman(L) && L.stat != DEAD)
human_servants++
construct_limit = human_servants / 4 //1 per 4 human servants, and a maximum of 3 marauders
construct_limit = CLAMP(construct_limit - recent_marauders, 1, 3)
construct_limit = CLAMP(construct_limit - recent_marauders, 1, 3)
if(recent_marauders)
to_chat(invoker, "<span class='warning'>The Hierophant Network needs [MARAUDER_SCRIPTURE_SCALING_THRESHOLD / 10] seconds to recover from marauder summoning; recent summoning has limited the number of available marauders by [recent_marauders]!</span>")
@@ -3,36 +3,9 @@
/////////////
//Stargazer: Creates a stargazer, a cheap power generator that utilizes starlight.
/datum/clockwork_scripture/create_object/stargazer
descname = "Generates Power From Starlight - Important!"
name = "Stargazer"
desc = "Forms a weak structure that generates power every second while within three tiles of starlight."
invocations = list("Capture their inferior light for us!")
channel_time = 50
power_cost = 50
object_path = /obj/structure/destructible/clockwork/stargazer
creator_message = "<span class='brass'>You form a stargazer, which will generate power near starlight.</span>"
observer_message = "<span class='warning'>A large lantern-shaped machine forms!</span>"
usage_tip = "For obvious reasons, make sure to place this near a window or somewhere else that can see space!"
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 1
quickbind = TRUE
quickbind_desc = "Creates a stargazer, which generates power when near starlight."
/datum/clockwork_scripture/create_object/stargazer/check_special_requirements()
var/area/A = get_area(invoker)
if(A.outdoors || A.map_name == "Space" || !A.blob_allowed)
to_chat(invoker, "<span class='danger'>Stargazers can't be built off-station.</span>")
return
return ..()
//Integration Cog: Creates an integration cog that can be inserted into APCs to passively siphon power.
/datum/clockwork_scripture/create_object/integration_cog
descname = "APC Power Siphoner"
descname = "Power Generation"
name = "Integration Cog"
desc = "Fabricates an integration cog, which can be used on an open APC to replace its innards and passively siphon its power."
invocations = list("Take that which sustains them!")
@@ -41,11 +14,12 @@
whispered = TRUE
object_path = /obj/item/clockwork/integration_cog
creator_message = "<span class='brass'>You form an integration cog, which can be inserted into an open APC to passively siphon power.</span>"
usage_tip = "Tampering isn't visible unless the APC is opened."
usage_tip = "Tampering isn't visible unless the APC is opened. You can use the cog on a locked APC to unlock it."
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 2
sort_priority = 1
important = TRUE
quickbind = TRUE
quickbind_desc = "Creates an integration cog, which can be used to siphon power from an open APC."
@@ -65,7 +39,7 @@
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 3
sort_priority = 2
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Transgression, which will briefly stun and slow the next non-Servant to cross it."
@@ -85,7 +59,7 @@
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 4
sort_priority = 3
quickbind = TRUE
quickbind_desc = "Creates a Sigil of Submission, which will convert non-Servants that remain on it."
@@ -102,13 +76,14 @@
usage_tip = "The light can be used from up to two tiles away. Damage taken will GREATLY REDUCE the stun's duration."
tier = SCRIPTURE_DRIVER
primary_component = BELLIGERENT_EYE
sort_priority = 5
sort_priority = 4
slab_overlay = "volt"
ranged_type = /obj/effect/proc_holder/slab/kindle
ranged_message = "<span class='brass'><i>You charge the clockwork slab with divine energy.</i>\n\
<b>Left-click a target within melee range to stun!\n\
Click your slab to cancel.</b></span>"
timeout_time = 150
important = TRUE
quickbind = TRUE
quickbind_desc = "Stuns and mutes a target from a short range."
@@ -125,13 +100,14 @@
usage_tip = "The manacles are about as strong as zipties, and break when removed."
tier = SCRIPTURE_DRIVER
primary_component = BELLIGERENT_EYE
sort_priority = 6
sort_priority = 5
ranged_type = /obj/effect/proc_holder/slab/hateful_manacles
slab_overlay = "hateful_manacles"
ranged_message = "<span class='neovgre_small'><i>You charge the clockwork slab with divine energy.</i>\n\
<b>Left-click a target within melee range to shackle!\n\
Click your slab to cancel.</b></span>"
timeout_time = 200
important = TRUE
quickbind = TRUE
quickbind_desc = "Applies handcuffs to a struck target."
@@ -148,7 +124,7 @@
usage_tip = "You cannot reactivate Vanguard while still shielded by it."
tier = SCRIPTURE_DRIVER
primary_component = VANGUARD_COGWHEEL
sort_priority = 7
sort_priority = 6
quickbind = TRUE
quickbind_desc = "Allows you to temporarily absorb stuns. All stuns absorbed will affect you when disabled."
@@ -180,7 +156,7 @@
usage_tip = "The Compromise is very fast to invoke, and will remove holy water from the target Servant."
tier = SCRIPTURE_DRIVER
primary_component = VANGUARD_COGWHEEL
sort_priority = 8
sort_priority = 7
quickbind = TRUE
quickbind_desc = "Allows you to convert a Servant's brute, burn, and oxygen damage to half toxin damage.<br><b>Click your slab to disable.</b>"
slab_overlay = "compromise"
@@ -192,7 +168,7 @@
//Abscond: Used to return to Reebe.
/datum/clockwork_scripture/abscond
descname = "Return to Reebe - Important!"
descname = "Return to Reebe"
name = "Abscond"
desc = "Yanks you through space, returning you to home base."
invocations = list("As we bid farewell, and return to the stars...", "...we shall find our way home.")
@@ -204,7 +180,8 @@
usage_tip = "This can't be used while on Reebe, for obvious reasons."
tier = SCRIPTURE_DRIVER
primary_component = GEIS_CAPACITOR
sort_priority = 9
sort_priority = 8
important = TRUE
quickbind = TRUE
quickbind_desc = "Returns you to Reebe."
@@ -246,7 +223,7 @@
//Replicant: Creates a new clockwork slab.
/datum/clockwork_scripture/create_object/replicant
descname = "New Clockwork Slab - Important!"
descname = "New Clockwork Slab"
name = "Replicant"
desc = "Creates a new clockwork slab."
invocations = list("Metal, become greater!")
@@ -259,7 +236,8 @@
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = GEIS_CAPACITOR
sort_priority = 10
sort_priority = 9
important = TRUE
quickbind = TRUE
quickbind_desc = "Creates a new Clockwork Slab."
@@ -279,6 +257,6 @@
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = GEIS_CAPACITOR
sort_priority = 11
sort_priority = 10
quickbind = TRUE
quickbind_desc = "Creates a pair of Wraith Spectacles, which grant true sight but cause gradual vision loss."
@@ -5,7 +5,7 @@
//Replica Fabricator: Creates a replica fabricator, used to convert objects and repair clockwork structures.
/datum/clockwork_scripture/create_object/replica_fabricator
descname = "Creates Brass and Converts Objects - Important!"
descname = "Creates Brass and Converts Objects"
name = "Replica Fabricator"
desc = "Forms a device that, when used on certain objects, replaces them with their Ratvarian equivalents. It requires power to function."
invocations = list("With this device...", "...his presence shall be made known.")
@@ -19,6 +19,7 @@
space_allowed = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 1
important = TRUE
quickbind = TRUE
quickbind_desc = "Creates a Replica Fabricator, which can convert various objects to Ratvarian variants."
@@ -99,7 +100,7 @@
//Clockwork Armaments: Grants the invoker the ability to call forth a Ratvarian spear and clockwork armor.
/datum/clockwork_scripture/clockwork_armaments
descname = "Summonable Armor and Weapons - Important!"
descname = "Summonable Armor and Weapons"
name = "Clockwork Armaments"
desc = "Allows the invoker to summon clockwork armor and a Ratvarian spear at will. The spear's attacks will generate Vitality, used for healing."
invocations = list("Grant me armaments...", "...from the forge of Armorer!")
@@ -110,6 +111,7 @@
tier = SCRIPTURE_SCRIPT
primary_component = VANGUARD_COGWHEEL
sort_priority = 5
important = TRUE
quickbind = TRUE
quickbind_desc = "Permanently binds clockwork armor and a Ratvarian spear to you."
+20 -20
View File
@@ -6,6 +6,7 @@
antagpanel_category = "Cult"
var/datum/action/innate/cult/comm/communion = new
var/datum/action/innate/cult/mastervote/vote = new
var/datum/action/innate/cult/blood_magic/magic = new
job_rank = ROLE_CULTIST
var/ignore_implant = FALSE
var/give_equipment = FALSE
@@ -58,7 +59,7 @@
var/mob/living/current = owner.current
add_objectives()
if(give_equipment)
equip_cultist()
equip_cultist(TRUE)
SSticker.mode.cult += owner // Only add after they've been given objectives
SSticker.mode.update_cult_icons_added(owner)
current.log_message("<font color=#960000>Has been converted to the cult of Nar'Sie!</font>", INDIVIDUAL_ATTACK_LOG)
@@ -67,18 +68,16 @@
current.client.images += cult_team.blood_target_image
/datum/antagonist/cult/proc/equip_cultist(tome=FALSE)
/datum/antagonist/cult/proc/equip_cultist(metal=TRUE)
var/mob/living/carbon/H = owner.current
if(!istype(H))
return
if (owner.assigned_role == "Clown")
to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
H.dna.remove_mutation(CLOWNMUT)
if(tome)
. += cult_give_item(/obj/item/tome, H)
else
. += cult_give_item(/obj/item/paper/talisman/supply, H)
. += cult_give_item(/obj/item/melee/cultblade/dagger, H)
if(metal)
. += cult_give_item(/obj/item/stack/sheet/runed_metal/ten, H)
to_chat(owner, "These will help you start the cult on this station. Use them well, and remember - you are not the only one.</span>")
@@ -110,10 +109,11 @@
current = mob_override
current.faction |= "cult"
current.grant_language(/datum/language/narsie)
current.verbs += /mob/living/proc/cult_help
if(!cult_team.cult_mastered)
if(!cult_team.cult_master)
vote.Grant(current)
communion.Grant(current)
if(ishuman(current))
magic.Grant(current)
current.throw_alert("bloodsense", /obj/screen/alert/bloodsense)
/datum/antagonist/cult/remove_innate_effects(mob/living/mob_override)
@@ -123,9 +123,9 @@
current = mob_override
current.faction -= "cult"
current.remove_language(/datum/language/narsie)
current.verbs -= /mob/living/proc/cult_help
vote.Remove(current)
communion.Remove(current)
magic.Remove(current)
current.clear_alert("bloodsense")
/datum/antagonist/cult/on_removal()
@@ -153,16 +153,16 @@
/datum/antagonist/cult/get_admin_commands()
. = ..()
.["Tome"] = CALLBACK(src,.proc/admin_give_tome)
.["Amulet"] = CALLBACK(src,.proc/admin_give_amulet)
.["Dagger"] = CALLBACK(src,.proc/admin_give_dagger)
.["Dagger and Metal"] = CALLBACK(src,.proc/admin_give_metal)
/datum/antagonist/cult/proc/admin_give_tome(mob/admin)
if(equip_cultist(owner.current,1))
to_chat(admin, "<span class='danger'>Spawning tome failed!</span>")
/datum/antagonist/cult/proc/admin_give_dagger(mob/admin)
if(!equip_cultist(FALSE))
to_chat(admin, "<span class='danger'>Spawning dagger failed!</span>")
/datum/antagonist/cult/proc/admin_give_amulet(mob/admin)
if (equip_cultist(owner.current))
to_chat(admin, "<span class='danger'>Spawning amulet failed!</span>")
/datum/antagonist/cult/proc/admin_give_metal(mob/admin)
if (!equip_cultist(TRUE))
to_chat(admin, "<span class='danger'>Spawning runed metal failed!</span>")
/datum/antagonist/cult/master
ignore_implant = TRUE
@@ -218,7 +218,7 @@
var/blood_target_reset_timer
var/cult_vote_called = FALSE
var/cult_mastered = FALSE
var/mob/living/cult_master
var/reckoning_complete = FALSE
@@ -326,4 +326,4 @@
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
/datum/team/cult/is_gamemode_hero()
return SSticker.mode.name == "cult"
return SSticker.mode.name == "cult"
+106 -41
View File
@@ -14,6 +14,7 @@
/datum/action/innate/cult/comm
name = "Communion"
desc = "Whispered words that all cultists can hear.<br><b>Warning:</b>Nearby non-cultists can still hear you."
button_icon_state = "cult_comms"
/datum/action/innate/cult/comm/Activate()
@@ -23,7 +24,7 @@
cultist_commune(usr, input)
/proc/cultist_commune(mob/living/user, message)
/datum/action/innate/cult/comm/proc/cultist_commune(mob/living/user, message)
var/my_message
if(!message)
return
@@ -33,10 +34,7 @@
var/span = "cult italic"
if(user.mind && user.mind.has_antag_datum(/datum/antagonist/cult/master))
span = "cultlarge"
if(ishuman(user))
title = "Master"
else
title = "Lord"
title = "Master"
else if(!ishuman(user))
title = "Construct"
my_message = "<span class='[span]'><b>[title] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]:</b> [message]</span>"
@@ -50,38 +48,26 @@
log_talk(user,"CULT:[key_name(user)] : [message]",LOGSAY)
/mob/living/proc/cult_help()
set category = "Cultist"
set name = "How to Play Cult"
var/text = ""
text += "<center><font color='red' size=3><b><i>Tenets of the Dark One</i></b></font></center><br><br><br>"
/datum/action/innate/cult/comm/spirit
name = "Spiritual Communion"
desc = "Conveys a message from the spirit realm that all cultists can hear."
text += "<font color='red'><b>I. SECRECY</b></font><br>Your cult is a SECRET organization. Your success DEPENDS on keeping your cult's members and locations SECRET for as long as possible. This means that your tome should be hidden \
in your bag and never brought out in public. You should never create runes where other crew might find them, and you should avoid using talismans or other cult magic with witnesses around.<br><br>"
/datum/action/innate/cult/comm/spirit/IsAvailable()
if(iscultist(owner.mind.current))
return TRUE
text += "<font color='red'><b>II. TOME</b></font><br>You start with a unique talisman in your bag. This supply talisman can be used 3 times, and creates starter equipment for your cult. The most critical of the talisman's functions is \
the power to create a tome. This tome is your most important item and summoning one (in secret) is your FIRST PRIORITY. It lets you talk to fellow cultists and create runes, which in turn is essential to growing the cult's power.<br><br>"
text += "<font color='red'><b>III. RUNES</b></font><br>Runes are powerful sources of cult magic. Your tome will allow you to draw runes with your blood. Those runes, when hit with an empty hand, will attempt to \
trigger the rune's magic. Runes are essential for the cult to convert new members, create powerful minions, or call upon incredibly powerful magic. Some runes require more than one cultist to use.<br><br>"
text += "<font color='red'><b>IV. TALISMANS</b></font><br>Talismans are a mobile source of cult magic that are NECESSARY to achieve success as a cult. Your starting talisman can produce certain talismans, but you will need \
to use the -create talisman- rune (with ordinary paper on top) to get more talismans. Talismans are EXTREMELY powerful, therefore creating more talismans in a HIDDEN location should be one of your TOP PRIORITIES.<br><br>"
text += "<font color='red'><b>V. GROW THE CULT</b></font><br>There are certain basic strategies that all cultists should master. STUN talismans are the foundation of a successful cult. If you intend to convert the stunned person \
you should use cuffs or a talisman of shackling on them and remove their headset before they recover (it takes about 10 seconds to recover). If you intend to sacrifice the victim, striking them quickly and repeatedly with your tome \
will knock them out before they can recover. Sacrificed victims will their soul behind in a shard, these shards can be used on construct shells to make powerful servants for the cult. Remember you need TWO cultists standing near a \
conversion rune to convert someone. Your construct minions cannot trigger most runes, but they will count as cultists in helping you trigger more powerful runes like conversion or blood boil.<br><br>"
text += "<font color='red'><b>VI. VICTORY</b></font><br>You have two ultimate goals as a cultist, sacrifice your target, and summon Nar-Sie. Sacrificing the target involves killing that individual and then placing \
their corpse on a sacrifice rune and triggering that rune with THREE cultists. Do NOT lose the target's corpse! Only once the target is sacrificed can Nar-Sie be summoned. Summoning Nar-Sie will take nearly one minute \
just to draw the massive rune needed. Do not create the rune until your cult is ready, the crew will receive the NAME and LOCATION of anyone who attempts to create the Nar-Sie rune. Once the Nar-Sie rune is drawn \
you must gathered 9 cultists (or constructs) over the rune and then click it to bring the Dark One into this world!<br><br>"
var/datum/browser/popup = new(usr, "mind", "", 800, 600)
popup.set_content(text)
popup.open()
return 1
/datum/action/innate/cult/comm/spirit/cultist_commune(mob/living/user, message)
var/my_message
if(!message)
return
my_message = "<span class='cultboldtalic'>The [user.name]: [message]</span>"
for(var/i in GLOB.player_list)
var/mob/M = i
if(iscultist(M))
to_chat(M, my_message)
else if(M in GLOB.dead_mob_list)
var/link = FOLLOW_LINK(M, user)
to_chat(M, "[link] [my_message]")
/datum/action/innate/cult/mastervote
name = "Assert Leadership"
@@ -139,7 +125,7 @@
if(!B.current.incapacitated())
to_chat(B.current, "<span class='cultlarge'>[Nominee] could not win the cult's support and shall continue to serve as an acolyte.</span>")
return FALSE
team.cult_mastered = TRUE
team.cult_master = Nominee
SSticker.mode.remove_cultist(Nominee.mind, TRUE)
Nominee.mind.add_antag_datum(/datum/antagonist/cult/master)
for(var/datum/mind/B in team.members)
@@ -171,7 +157,7 @@
if(!is_blocked_turf(T, TRUE))
destinations += T
if(!LAZYLEN(destinations))
to_chat(owner, "<span class='warning'>You need more space to summon the cult!</span>")
to_chat(owner, "<span class='warning'>You need more space to summon your cult!</span>")
return
if(do_after(owner, 30, target = owner))
for(var/datum/mind/B in antag.cult_team.members)
@@ -234,8 +220,6 @@
..()
/datum/action/innate/cult/master/cultmark/IsAvailable()
if(!owner.mind || !owner.mind.has_antag_datum(/datum/antagonist/cult/master))
return FALSE
if(cooldown > world.time)
if(!CM.active)
to_chat(owner, "<span class='cultlarge'><b>You need to wait [DisplayTimeText(cooldown - world.time)] before you can mark another target!</b></span>")
@@ -278,6 +262,9 @@
var/datum/antagonist/cult/C = caller.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
if(target in view(7, get_turf(ranged_ability_user)))
if(C.cult_team.blood_target)
to_chat(ranged_ability_user, "<span class='cult'>The cult has already designated a target!</span>")
return FALSE
C.cult_team.blood_target = target
var/area/A = get_area(target)
attached_action.cooldown = world.time + attached_action.base_cooldown
@@ -288,7 +275,7 @@
C.cult_team.blood_target_image.pixel_y = -target.pixel_y
for(var/datum/mind/B in SSticker.mode.cult)
if(B.current && B.current.stat != DEAD && B.current.client)
to_chat(B.current, "<span class='cultlarge'><b>Master [ranged_ability_user] has marked [C.cult_team.blood_target] in the [A.name] as the cult's top priority, get there immediately!</b></span>")
to_chat(B.current, "<span class='cultlarge'><b>[ranged_ability_user] has marked [C.cult_team.blood_target] in the [A.name] as the cult's top priority, get there immediately!</b></span>")
SEND_SOUND(B.current, sound(pick('sound/hallucinations/over_here2.ogg','sound/hallucinations/over_here3.ogg'),0,1,75))
B.current.client.images += C.cult_team.blood_target_image
attached_action.owner.update_action_buttons_icon()
@@ -307,6 +294,82 @@
team.blood_target = null
/datum/action/innate/cult/master/cultmark/ghost
name = "Mark a Blood Target for the Cult"
desc = "Marks a target for the entire cult to track."
/datum/action/innate/cult/master/cultmark/IsAvailable()
if(istype(owner, /mob/dead/observer) && iscultist(owner.mind.current))
return TRUE
else
qdel(src)
/datum/action/innate/cult/ghostmark //Ghost version
name = "Blood Mark your Target"
desc = "Marks whatever you are orbitting - for the entire cult to track."
button_icon_state = "cult_mark"
var/tracking = FALSE
var/cooldown = 0
var/base_cooldown = 600
/datum/action/innate/cult/ghostmark/IsAvailable()
if(istype(owner, /mob/dead/observer) && iscultist(owner.mind.current))
return TRUE
else
qdel(src)
/datum/action/innate/cult/ghostmark/proc/reset_button()
if(owner)
name = "Blood Mark your Target"
desc = "Marks whatever you are orbitting - for the entire cult to track."
button_icon_state = "cult_mark"
owner.update_action_buttons_icon()
SEND_SOUND(owner, 'sound/magic/enter_blood.ogg')
to_chat(owner,"<span class='cultbold'>Your previous mark is gone - you are now ready to create a new blood mark.</span>")
/datum/action/innate/cult/ghostmark/Activate()
var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
if(C.cult_team.blood_target)
if(cooldown>world.time)
reset_blood_target(C.cult_team)
to_chat(owner, "<span class='cultbold'>You have cleared the cult's blood target!</span>")
qdel(C.cult_team.blood_target_reset_timer)
return
else
to_chat(owner, "<span class='cultbold'>The cult has already designated a target!</span>")
return
if(cooldown>world.time)
to_chat(owner, "<span class='cultbold'>You aren't ready to place another blood mark yet!</span>")
return
if(owner.orbiting && owner.orbiting.orbiting)
target = owner.orbiting.orbiting
else
target = get_turf(owner)
if(!target)
return
C.cult_team.blood_target = target
var/area/A = get_area(target)
cooldown = world.time + base_cooldown
addtimer(CALLBACK(owner, /mob.proc/update_action_buttons_icon), base_cooldown)
C.cult_team.blood_target_image = image('icons/effects/cult_target.dmi', target, "glow", ABOVE_MOB_LAYER)
C.cult_team.blood_target_image.appearance_flags = RESET_COLOR
C.cult_team.blood_target_image.pixel_x = -target.pixel_x
C.cult_team.blood_target_image.pixel_y = -target.pixel_y
SEND_SOUND(owner, sound(pick('sound/hallucinations/over_here2.ogg','sound/hallucinations/over_here3.ogg'),0,1,75))
owner.client.images += C.cult_team.blood_target_image
for(var/datum/mind/B in SSticker.mode.cult)
if(B.current && B.current.stat != DEAD && B.current.client)
to_chat(B.current, "<span class='cultlarge'><b>[owner] has marked [C.cult_team.blood_target] in the [A.name] as the cult's top priority, get there immediately!</b></span>")
SEND_SOUND(B.current, sound(pick('sound/hallucinations/over_here2.ogg','sound/hallucinations/over_here3.ogg'),0,1,75))
B.current.client.images += C.cult_team.blood_target_image
to_chat(owner,"<span class='cultbold'>You have marked the [target] for the cult! It will last for [base_cooldown/10] seconds.</span>")
name = "Clear the Blood Mark"
desc = "Remove the Blood Mark you previously set."
button_icon_state = "emp"
owner.update_action_buttons_icon()
C.cult_team.blood_target_reset_timer = addtimer(CALLBACK(GLOBAL_PROC, .proc/reset_blood_target,C.cult_team), base_cooldown, TIMER_STOPPABLE)
addtimer(CALLBACK(src, .proc/reset_button), base_cooldown)
//////// ELDRITCH PULSE /////////
@@ -342,6 +405,7 @@
QDEL_NULL(PM)
return ..()
/datum/action/innate/cult/master/pulse/Activate()
PM.toggle(owner) //the important bit
return TRUE
@@ -352,9 +416,10 @@
var/datum/action/innate/cult/master/pulse/attached_action
/obj/effect/proc_holder/pulse/Destroy()
QDEL_NULL(attached_action)
attached_action = null
return ..()
/obj/effect/proc_holder/pulse/proc/toggle(mob/user)
if(active)
remove_ranged_ability("<span class='cult'>You cease your preparations...</span>")
+419 -49
View File
@@ -1,3 +1,33 @@
/obj/item/tome
name = "arcane tome"
desc = "An old, dusty tome with frayed edges and a sinister-looking cover."
icon_state ="tome"
throw_speed = 2
throw_range = 5
w_class = WEIGHT_CLASS_SMALL
/obj/item/melee/cultblade/dagger
name = "ritual dagger"
desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods."
icon = 'icons/obj/wizard.dmi'
icon_state = "render"
item_state = "cultdagger"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
inhand_x_dimension = 32
inhand_y_dimension = 32
w_class = WEIGHT_CLASS_SMALL
force = 15
throwforce = 25
armour_penetration = 35
actions_types = list(/datum/action/item_action/cult_dagger)
/obj/item/melee/cultblade/dagger/Initialize()
..()
var/image/I = image(icon = 'icons/effects/blood.dmi' , icon_state = null, loc = src)
I.override = TRUE
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "cult_dagger", I)
/obj/item/melee/cultblade
name = "eldritch longsword"
desc = "A sword humming with unholy energy. It glows with a dim red light."
@@ -49,29 +79,6 @@
user.apply_damage(30, BRUTE, pick("l_arm", "r_arm"))
user.dropItemToGround(src)
/obj/item/melee/cultblade/dagger
name = "sacrificial dagger"
desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods."
icon = 'icons/obj/wizard.dmi'
icon_state = "render"
item_state = "knife"
lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi'
inhand_x_dimension = 32
inhand_y_dimension = 32
w_class = WEIGHT_CLASS_SMALL
force = 15
throwforce = 25
embedding = list("embed_chance" = 75)
/obj/item/melee/cultblade/dagger/attack(mob/living/target, mob/living/carbon/human/user)
..()
if(iscarbon(target))
var/mob/living/carbon/C = target
C.bleed(50)
if(is_servant_of_ratvar(C) && C.reagents)
C.reagents.add_reagent("heparin", 1)
/obj/item/twohanded/required/cult_bastard
name = "bloody bastard sword"
desc = "An enormous sword used by Nar-Sien cultists to rapidly harvest the souls of non-believers."
@@ -106,6 +113,12 @@
jaunt = new(src)
linked_action = new(src)
/obj/item/twohanded/required/cult_bastard/examine(mob/user)
if(contents.len)
desc+="<br><b>There are [contents.len] souls trapped within the sword's core.</b>"
else
desc+="<br>The sword appears to be quite lifeless."
/obj/item/twohanded/required/cult_bastard/can_be_pulled(user)
return FALSE
@@ -146,7 +159,7 @@
/obj/item/twohanded/required/cult_bastard/IsReflect()
if(spinning)
playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1)
playsound(src, pick('sound/weapons/effects/ric1.ogg', 'sound/weapons/effects/ric2.ogg', 'sound/weapons/effects/ric3.ogg', 'sound/weapons/effects/ric4.ogg', 'sound/weapons/effects/ric5.ogg'), 100, 1)
return TRUE
else
..()
@@ -155,7 +168,7 @@
if(prob(final_block_chance))
if(attack_type == PROJECTILE_ATTACK)
owner.visible_message("<span class='danger'>[owner] deflects [attack_text] with [src]!</span>")
playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 100, 1)
playsound(src, pick('sound/weapons/effects/ric1.ogg', 'sound/weapons/effects/ric2.ogg', 'sound/weapons/effects/ric3.ogg', 'sound/weapons/effects/ric4.ogg', 'sound/weapons/effects/ric5.ogg'), 100, 1)
return TRUE
else
playsound(src, 'sound/weapons/parry.ogg', 75, 1)
@@ -165,23 +178,22 @@
/obj/item/twohanded/required/cult_bastard/afterattack(atom/target, mob/user, proximity, click_parameters)
. = ..()
if(dash_toggled)
if(dash_toggled && !proximity)
jaunt.Teleport(user, target)
return
if(!proximity)
return
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(H.stat != CONSCIOUS)
var/obj/item/device/soulstone/SS = new /obj/item/device/soulstone(src)
SS.attack(H, user)
if(!LAZYLEN(SS.contents))
if(proximity)
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(H.stat != CONSCIOUS)
var/obj/item/device/soulstone/SS = new /obj/item/device/soulstone(src)
SS.attack(H, user)
if(!LAZYLEN(SS.contents))
qdel(SS)
if(istype(target, /obj/structure/constructshell) && contents.len)
var/obj/item/device/soulstone/SS = contents[1]
if(istype(SS))
SS.transfer_soul("CONSTRUCT",target,user)
qdel(SS)
if(istype(target, /obj/structure/constructshell) && contents.len)
var/obj/item/device/soulstone/SS = contents[1]
if(istype(SS))
SS.transfer_soul("CONSTRUCT",target,user)
qdel(SS)
/datum/action/innate/dash/cult
name = "Rend the Veil"
@@ -241,10 +253,20 @@
/obj/item/restraints/legcuffs/bola/cult
name = "nar'sien bola"
desc = "A strong bola, bound with dark magic. Throw it to trip and slow your victim."
desc = "A strong bola, bound with dark magic that allows it to pass harmlessly through Nar'sien cultists. Throw it to trip and slow your victim."
icon_state = "bola_cult"
breakouttime = 45
knockdown = 10
breakouttime = 60
knockdown = 20
/obj/item/restraints/legcuffs/bola/cult/pickup(mob/living/user)
if(!iscultist(user))
to_chat(user, "<span class='warning'>The bola seems to take on a life of its own!</span>")
throw_impact(user)
/obj/item/restraints/legcuffs/bola/cult/throw_impact(atom/hit_atom)
if(iscultist(hit_atom))
return
. = ..()
/obj/item/clothing/head/culthood
@@ -253,7 +275,7 @@
desc = "A torn, dust-caked hood. Strange letters line the inside."
flags_inv = HIDEFACE|HIDEHAIR|HIDEEARS
flags_cover = HEADCOVERSEYES
armor = list(melee = 30, bullet = 10, laser = 5,energy = 5, bomb = 0, bio = 0, rad = 0, fire = 10, acid = 10)
armor = list(melee = 40, bullet = 30, laser = 40,energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10)
cold_protection = HEAD
min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT
heat_protection = HEAD
@@ -266,7 +288,7 @@
item_state = "cultrobes"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
allowed = list(/obj/item/tome, /obj/item/melee/cultblade)
armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10)
armor = list(melee = 40, bullet = 30, laser = 40,energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10)
flags_inv = HIDEJUMPSUIT
cold_protection = CHEST|GROIN|LEGS|ARMS
min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
@@ -341,7 +363,10 @@
prefix = "darkened"
/obj/item/sharpener/cult/update_icon()
var/old_state = icon_state
icon_state = "cult_sharpener[used ? "_used" : ""]"
if(old_state != icon_state)
playsound(get_turf(src), 'sound/items/unsheath.ogg', 25, 1)
/obj/item/clothing/suit/hooded/cultrobes/cult_shield
name = "empowered cultist armor"
@@ -431,12 +456,11 @@
user.adjustBruteLoss(25)
user.dropItemToGround(src, TRUE)
/obj/item/clothing/glasses/night/cultblind
desc = "May nar-sie guide you through the darkness and shield you from the light."
/obj/item/clothing/glasses/hud/health/night/cultblind
desc = "may Nar-Sie guide you through the darkness and shield you from the light."
name = "zealot's blindfold"
icon_state = "blindfold"
item_state = "blindfold"
darkness_view = 8
flash_protect = 1
/obj/item/clothing/glasses/night/cultblind/equipped(mob/living/user, slot)
@@ -448,12 +472,13 @@
user.Knockdown(100)
user.blind_eyes(30)
/obj/item/reagent_containers/food/drinks/bottle/unholywater
/obj/item/reagent_containers/glass/beaker/unholywater
name = "flask of unholy water"
desc = "Toxic to nonbelievers; reinvigorating to the faithful - this flask may be sipped or thrown."
icon = 'icons/obj/drinks.dmi'
icon_state = "holyflask"
color = "#333333"
list_reagents = list("unholywater" = 40)
list_reagents = list("unholywater" = 50)
/obj/item/device/shuttle_curse
name = "cursed orb"
@@ -604,3 +629,348 @@
..()
to_chat(user, "<span class='warning'>\The [src] can only transport items!</span>")
/obj/item/twohanded/cult_spear
name = "blood halberd"
desc = "A sickening spear composed entirely of crystallized blood."
icon_state = "bloodspear0"
lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
slot_flags = 0
force = 17
force_wielded = 24
throwforce = 40
throw_speed = 2
armour_penetration = 30
block_chance = 30
attack_verb = list("attacked", "impaled", "stabbed", "torn", "gored")
sharpness = IS_SHARP
hitsound = 'sound/weapons/bladeslice.ogg'
var/datum/action/innate/cult/spear/spear_act
/obj/item/twohanded/cult_spear/Destroy()
if(spear_act)
qdel(spear_act)
..()
/obj/item/twohanded/cult_spear/update_icon()
icon_state = "bloodspear[wielded]"
/obj/item/twohanded/cult_spear/throw_impact(atom/target)
var/turf/T = get_turf(target)
if(isliving(target))
var/mob/living/L = target
if(iscultist(L))
playsound(src, 'sound/weapons/throwtap.ogg', 50)
if(L.put_in_active_hand(src))
L.visible_message("<span class='warning'>[L] catches [src] out of the air!</span>")
else
L.visible_message("<span class='warning'>[src] bounces off of [L], as if repelled by an unseen force!</span>")
else if(!..())
if(!L.null_rod_check())
if(is_servant_of_ratvar(L))
L.Knockdown(100)
else
L.Knockdown(50)
break_spear(T)
else
..()
/obj/item/twohanded/cult_spear/proc/break_spear(turf/T)
if(src)
if(!T)
T = get_turf(src)
if(T)
T.visible_message("<span class='warning'>[src] shatters and melts back into blood!</span>")
new /obj/effect/temp_visual/cult/sparks(T)
new /obj/effect/decal/cleanable/blood/splatter(T)
playsound(T, 'sound/effects/glassbr3.ogg', 100)
qdel(src)
/obj/item/twohanded/cult_spear/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(wielded)
final_block_chance *= 2
if(prob(final_block_chance))
if(attack_type == PROJECTILE_ATTACK)
owner.visible_message("<span class='danger'>[owner] deflects [attack_text] with [src]!</span>")
playsound(src, pick('sound/weapons/effects/ric1.ogg', 'sound/weapons/effects/ric2.ogg', 'sound/weapons/effects/ric3.ogg', 'sound/weapons/effects/ric4.ogg', 'sound/weapons/effects/ric5.ogg'), 100, 1)
return TRUE
else
playsound(src, 'sound/weapons/parry.ogg', 100, 1)
owner.visible_message("<span class='danger'>[owner] parries [attack_text] with [src]!</span>")
return TRUE
return FALSE
/datum/action/innate/cult/spear
name = "Bloody Bond"
desc = "Call the blood spear back to your hand!"
background_icon_state = "bg_demon"
button_icon_state = "bloodspear"
var/obj/item/twohanded/cult_spear/spear
var/cooldown = 0
/datum/action/innate/cult/spear/Grant(mob/user, obj/blood_spear)
. = ..()
spear = blood_spear
button.screen_loc = "6:157,4:-2"
button.moved = "6:157,4:-2"
/datum/action/innate/cult/spear/Activate()
if(owner == spear.loc || cooldown > world.time)
return
var/ST = get_turf(spear)
var/OT = get_turf(owner)
if(get_dist(OT, ST) > 10)
to_chat(owner,"<span class='cult'>The spear is too far away!</span>")
else
cooldown = world.time + 20
if(isliving(spear.loc))
var/mob/living/L = spear.loc
L.dropItemToGround(spear)
L.visible_message("<span class='warning'>An unseen force pulls the blood spear from [L]'s hands!</span>")
spear.throw_at(owner, 10, 2, owner)
/obj/item/gun/ballistic/shotgun/boltaction/enchanted/arcane_barrage/blood
name = "blood bolt barrage"
desc = "Blood for blood."
color = "#ff0000"
guns_left = 24
mag_type = /obj/item/ammo_box/magazine/internal/boltaction/enchanted/arcane_barrage/blood
fire_sound = 'sound/magic/wand_teleport.ogg'
flags_1 = NOBLUDGEON_1 | DROPDEL_1
/obj/item/ammo_box/magazine/internal/boltaction/enchanted/arcane_barrage/blood
ammo_type = /obj/item/ammo_casing/magic/arcane_barrage/blood
/obj/item/ammo_casing/magic/arcane_barrage/blood
projectile_type = /obj/item/projectile/magic/arcane_barrage/blood
/obj/item/projectile/magic/arcane_barrage/blood
name = "blood bolt"
icon_state = "mini_leaper"
damage_type = BRUTE
impact_effect_type = /obj/effect/temp_visual/dir_setting/bloodsplatter
/obj/item/projectile/magic/arcane_barrage/blood/Collide(atom/target)
colliding = TRUE
var/turf/T = get_turf(target)
playsound(T, 'sound/effects/splat.ogg', 50, TRUE)
if(iscultist(target))
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(H.stat != DEAD)
H.reagents.add_reagent("unholywater", 4)
if(isshade(target) || isconstruct(target))
var/mob/living/simple_animal/M = target
if(M.health+5 < M.maxHealth)
M.adjustHealth(-5)
new /obj/effect/temp_visual/cult/sparks(T)
qdel(src)
colliding = FALSE
else
..()
/obj/item/blood_beam
name = "\improper magical aura"
desc = "Sinister looking aura that distorts the flow of reality around it."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "disintegrate"
item_state = null
flags_1 = ABSTRACT_1 | NODROP_1 | DROPDEL_1
w_class = WEIGHT_CLASS_HUGE
throwforce = 0
throw_range = 0
throw_speed = 0
var/charging = FALSE
var/firing = FALSE
var/angle
/obj/item/blood_beam/afterattack(atom/A, mob/living/user, flag, params)
if(firing || charging)
return
var/C = user.client
if(ishuman(user) && C)
angle = mouse_angle_from_client(C)
else
qdel(src)
return
charging = TRUE
INVOKE_ASYNC(src, .proc/charge, user)
if(do_after(user, 90, target = user))
firing = TRUE
INVOKE_ASYNC(src, .proc/pewpew, user, params)
var/obj/structure/emergency_shield/invoker/N = new(user.loc)
if(do_after(user, 90, target = user))
user.Knockdown(40)
to_chat(user, "<span class='cult italic'>You have exhausted the power of this spell!</span>")
firing = FALSE
if(N)
qdel(N)
qdel(src)
charging = FALSE
/obj/item/blood_beam/proc/charge(mob/user)
var/obj/O
playsound(src, 'sound/magic/lightning_chargeup.ogg', 100, 1)
for(var/i in 1 to 12)
if(!charging)
break
if(i > 1)
sleep(15)
if(i < 4)
O = new /obj/effect/temp_visual/cult/rune_spawn/rune1/inner(user.loc, 30, "#ff0000")
else
O = new /obj/effect/temp_visual/cult/rune_spawn/rune5(user.loc, 30, "#ff0000")
new /obj/effect/temp_visual/dir_setting/cult/phase/out(user.loc, user.dir)
if(O)
qdel(O)
/obj/item/blood_beam/proc/pewpew(mob/user, params)
var/turf/targets_from = get_turf(src)
var/spread = 40
var/second = FALSE
var/set_angle = angle
for(var/i in 1 to 12)
if(second)
set_angle = angle - spread
spread -= 8
else
sleep(15)
set_angle = angle + spread
second = !second //Handles beam firing in pairs
if(!firing)
break
playsound(src, 'sound/magic/exit_blood.ogg', 75, 1)
new /obj/effect/temp_visual/dir_setting/cult/phase(user.loc, user.dir)
var/turf/temp_target = get_turf_in_angle(set_angle, targets_from, 40)
for(var/turf/T in getline(targets_from,temp_target))
if (locate(/obj/effect/blessing, T))
temp_target = T
playsound(T, 'sound/machines/clockcult/ark_damage.ogg', 50, 1)
new /obj/effect/temp_visual/at_shield(T, T)
break
T.narsie_act(TRUE, TRUE)
for(var/mob/living/target in T.contents)
if(iscultist(target))
new /obj/effect/temp_visual/cult/sparks(T)
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(H.stat != DEAD)
H.reagents.add_reagent("unholywater", 7)
if(isshade(target) || isconstruct(target))
var/mob/living/simple_animal/M = target
if(M.health+15 < M.maxHealth)
M.adjustHealth(-15)
else
M.health = M.maxHealth
else
var/mob/living/L = target
if(L.density)
L.Knockdown(20)
L.adjustBruteLoss(45)
playsound(L, 'sound/hallucinations/wail.ogg', 50, 1)
L.emote("scream")
var/datum/beam/current_beam = new(user,temp_target,time=7,beam_icon_state="blood_beam",btype=/obj/effect/ebeam/blood)
INVOKE_ASYNC(current_beam, /datum/beam.proc/Start)
/obj/effect/ebeam/blood
name = "blood beam"
/obj/item/shield/mirror
name = "mirror shield"
desc = "An infamous shield used by Nar'sien sects to confuse and disorient their enemies. Its edges are weighted for use as a throwing weapon - capable of disabling multiple foes with preternatural accuracy."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "mirror_shield" // eshield1 for expanded
lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi'
force = 5
throwforce = 15
throw_speed = 1
throw_range = 6
w_class = WEIGHT_CLASS_BULKY
attack_verb = list("bumped", "prodded")
hitsound = 'sound/weapons/smash.ogg'
var/illusions = 3
/obj/item/shield/mirror/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(iscultist(owner))
if(istype(hitby, /obj/item/projectile))
var/obj/item/projectile/P = hitby
if(P.damage >= 35)
var/turf/T = get_turf(owner)
T.visible_message("<span class='warning'>The sheer force from [P] shatters the mirror shield!</span>")
new /obj/effect/temp_visual/cult/sparks(T)
playsound(T, 'sound/effects/glassbr3.ogg', 100)
owner.Knockdown(20)
qdel(src)
return FALSE
if(P.is_reflectable)
return FALSE //To avoid reflection chance double-dipping with block chance
. = ..()
if(.)
playsound(src, 'sound/weapons/parry.ogg', 100, 1)
if(illusions > 0)
illusions--
addtimer(CALLBACK(src, /obj/item/shield/mirror.proc/readd), 450)
if(prob(60))
var/mob/living/simple_animal/hostile/illusion/M = new(owner.loc)
M.faction = list("cult")
M.Copy_Parent(owner, 70, 10, 5)
M.move_to_delay = owner.movement_delay()
else
var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc)
E.Copy_Parent(owner, 70, 10)
E.GiveTarget(owner)
E.Goto(owner, owner.movement_delay(), E.minimum_distance)
return TRUE
else
if(prob(50))
var/mob/living/simple_animal/hostile/illusion/H = new(owner.loc)
H.Copy_Parent(owner, 100, 20, 5)
H.faction = list("cult")
H.GiveTarget(owner)
H.move_to_delay = owner.movement_delay()
to_chat(owner, "<span class='danger'><b>[src] betrays you!</b></span>")
return FALSE
/obj/item/shield/mirror/proc/readd()
illusions++
if(illusions == initial(illusions) && isliving(loc))
var/mob/living/holder = loc
to_chat(holder, "<span class='cult italic'>The shield's illusions are back at full strength!</span>")
/obj/item/shield/mirror/IsReflect()
if(prob(block_chance))
return TRUE
return FALSE
/obj/item/shield/mirror/throw_impact(atom/target, throwingdatum)
var/turf/T = get_turf(target)
var/datum/thrownthing/D = throwingdatum
if(isliving(target))
var/mob/living/L = target
if(iscultist(L))
playsound(src, 'sound/weapons/throwtap.ogg', 50)
if(L.put_in_active_hand(src))
L.visible_message("<span class='warning'>[L] catches [src] out of the air!</span>")
else
L.visible_message("<span class='warning'>[src] bounces off of [L], as if repelled by an unseen force!</span>")
else if(!..())
if(!L.null_rod_check())
if(is_servant_of_ratvar(L))
L.Knockdown(60)
else
L.Knockdown(30)
if(D.thrower)
for(var/mob/living/Next in orange(2, T))
if(!Next.density || iscultist(Next))
continue
throw_at(Next, 3, 1, D.thrower)
return
throw_at(D.thrower, 7, 1, D.thrower)
else
..()
@@ -2,10 +2,32 @@
density = TRUE
anchored = TRUE
icon = 'icons/obj/cult.dmi'
light_power = 2
var/cooldowntime = 0
break_sound = 'sound/hallucinations/veryfar_noise.ogg'
debris = list(/obj/item/stack/sheet/runed_metal = 1)
/obj/structure/destructible/cult/proc/conceal() //for spells that hide cult presence
density = FALSE
visible_message("<span class='danger'>[src] fades away.</span>")
invisibility = INVISIBILITY_OBSERVER
alpha = 100 //To help ghosts distinguish hidden runes
light_range = 0
light_power = 0
update_light()
STOP_PROCESSING(SSfastprocess, src)
/obj/structure/destructible/cult/proc/reveal() //for spells that reveal cult presence
density = initial(density)
invisibility = 0
visible_message("<span class='danger'>[src] suddenly appears!</span>")
alpha = initial(alpha)
light_range = initial(light_range)
light_power = initial(light_power)
update_light()
START_PROCESSING(SSfastprocess, src)
/obj/structure/destructible/cult/examine(mob/user)
..()
to_chat(user, "<span class='notice'>\The [src] is [anchored ? "":"not "]secured to the floor.</span>")
@@ -33,7 +55,7 @@
..()
/obj/structure/destructible/cult/attackby(obj/I, mob/user, params)
if(istype(I, /obj/item/tome) && iscultist(user))
if(istype(I, /obj/item/melee/cultblade/dagger) && iscultist(user))
anchored = !anchored
to_chat(user, "<span class='notice'>You [anchored ? "":"un"]secure \the [src] [anchored ? "to":"from"] the floor.</span>")
if(!anchored)
@@ -62,31 +84,32 @@
to_chat(user, "<span class='warning'>You're pretty sure you know exactly what this is used for and you can't seem to touch it.</span>")
return
if(!anchored)
to_chat(user, "<span class='cult italic'>You need to anchor [src] to the floor with a tome first.</span>")
to_chat(user, "<span class='cultitalic'>You need to anchor [src] to the floor with your dagger first.</span>")
return
if(cooldowntime > world.time)
to_chat(user, "<span class='cult italic'>The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].</span>")
return
var/choice = alert(user,"You study the schematics etched into the forge...",,"Eldritch Whetstone","Zealot's Blindfold","Flask of Unholy Water")
var/pickedtype
var/choice = alert(user,"You study the schematics etched into the altar...",,"Eldritch Whetstone","Construct Shells","Flask of Unholy Water")
var/list/pickedtype = list()
switch(choice)
if("Eldritch Whetstone")
pickedtype = /obj/item/sharpener/cult
if("Zealot's Blindfold")
pickedtype = /obj/item/clothing/glasses/night/cultblind
pickedtype += /obj/item/sharpener/cult
if("Construct Shells")
pickedtype += /obj/structure/constructshell
pickedtype += /obj/structure/constructshell
if("Flask of Unholy Water")
pickedtype = /obj/item/reagent_containers/food/drinks/bottle/unholywater
pickedtype += /obj/item/reagent_containers/glass/beaker/unholywater
if(src && !QDELETED(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time)
cooldowntime = world.time + 2400
var/obj/item/N = new pickedtype(get_turf(src))
to_chat(user, "<span class='cult italic'>You kneel before the altar and your faith is rewarded with an [N]!</span>")
for(var/N in pickedtype)
new N(get_turf(src))
to_chat(user, "<span class='cultitalic'>You kneel before the altar and your faith is rewarded with the [choice]!</span>")
/obj/structure/destructible/cult/forge
name = "daemon forge"
desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie."
icon_state = "forge"
light_range = 3
light_range = 2
light_color = LIGHT_COLOR_LAVA
break_message = "<span class='warning'>The force breaks apart into shards with a howling scream!</span>"
@@ -95,30 +118,35 @@
to_chat(user, "<span class='warning'>The heat radiating from [src] pushes you back.</span>")
return
if(!anchored)
to_chat(user, "<span class='cult italic'>You need to anchor [src] to the floor with a tome first.</span>")
to_chat(user, "<span class='cultitalic'>You need to anchor [src] to the floor with your dagger first.</span>")
return
if(cooldowntime > world.time)
to_chat(user, "<span class='cult italic'>The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].</span>")
return
var/choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Bastard Sword")
var/pickedtype
var/choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Mirror Shield")
if(user.mind.has_antag_datum(/datum/antagonist/cult/master))
choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Bastard Sword")
var/list/pickedtype = list()
switch(choice)
if("Shielded Robe")
pickedtype = /obj/item/clothing/suit/hooded/cultrobes/cult_shield
pickedtype += /obj/item/clothing/suit/hooded/cultrobes/cult_shield
if("Flagellant's Robe")
pickedtype = /obj/item/clothing/suit/hooded/cultrobes/berserker
pickedtype += /obj/item/clothing/suit/hooded/cultrobes/berserker
if("Bastard Sword")
if((world.time - SSticker.round_start_time) >= 12000)
pickedtype = /obj/item/twohanded/required/cult_bastard
pickedtype += /obj/item/twohanded/required/cult_bastard
else
cooldowntime = 12000 - (world.time - SSticker.round_start_time)
to_chat(user, "<span class='cult italic'>The forge fires are not yet hot enough for this weapon, give it another [DisplayTimeText(cooldowntime)].</span>")
cooldowntime = 0
return
if("Mirror Shield")
pickedtype += /obj/item/shield/mirror
if(src && !QDELETED(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time)
cooldowntime = world.time + 2400
var/obj/item/N = new pickedtype(get_turf(src))
to_chat(user, "<span class='cult italic'>You work the forge as dark knowledge guides your hands, creating [N]!</span>")
for(var/N in pickedtype)
new N(get_turf(src))
to_chat(user, "<span class='cultitalic'>You work the forge as dark knowledge guides your hands, creating the [choice]!</span>")
@@ -126,7 +154,7 @@
name = "pylon"
desc = "A floating crystal that slowly heals those faithful to Nar'Sie."
icon_state = "pylon"
light_range = 5
light_range = 1.5
light_color = LIGHT_COLOR_RED
break_sound = 'sound/effects/glassbr2.ogg'
break_message = "<span class='warning'>The blood-red crystal falls to the floor and shatters!</span>"
@@ -159,7 +187,7 @@
if(isshade(L) || isconstruct(L))
var/mob/living/simple_animal/M = L
if(M.health < M.maxHealth)
M.adjustHealth(-1)
M.adjustHealth(-3)
if(ishuman(L) && L.blood_volume < BLOOD_VOLUME_NORMAL)
L.blood_volume += 1.0
CHECK_TICK
@@ -199,7 +227,7 @@
name = "archives"
desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl."
icon_state = "tomealtar"
light_range = 1.4
light_range = 1.5
light_color = LIGHT_COLOR_FIRE
break_message = "<span class='warning'>The books and tomes of the archives burn into ash as the desk shatters!</span>"
@@ -208,16 +236,16 @@
to_chat(user, "<span class='warning'>All of these books seem to be gibberish.</span>")
return
if(!anchored)
to_chat(user, "<span class='cult italic'>You need to anchor [src] to the floor with a tome first.</span>")
to_chat(user, "<span class='cultitalic'>You need to anchor [src] to the floor with your dagger first.</span>")
return
if(cooldowntime > world.time)
to_chat(user, "<span class='cult italic'>The magic in [src] is weak, it will be ready to use again in [DisplayTimeText(cooldowntime - world.time)].</span>")
return
var/choice = alert(user,"You flip through the black pages of the archives...",,"Supply Talisman","Shuttle Curse","Veil Walker Set")
var/choice = alert(user,"You flip through the black pages of the archives...",,"Zealot's Blindfold","Shuttle Curse","Veil Walker Set")
var/list/pickedtype = list()
switch(choice)
if("Supply Talisman")
pickedtype += /obj/item/paper/talisman/supply/weak
if("Zealot's Blindfold")
pickedtype += /obj/item/clothing/glasses/hud/health/night/cultblind
if("Shuttle Curse")
pickedtype += /obj/item/device/shuttle_curse
if("Veil Walker Set")
@@ -226,8 +254,8 @@
if(src && !QDELETED(src) && anchored && pickedtype.len && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time)
cooldowntime = world.time + 2400
for(var/N in pickedtype)
var/obj/item/D = new N(get_turf(src))
to_chat(user, "<span class='cult italic'>You summon [D] from the archives!</span>")
new N(get_turf(src))
to_chat(user, "<span class='cultitalic'>You summon the [choice] from the archives!</span>")
/obj/effect/gateway
name = "gateway"
+30 -151
View File
@@ -1,19 +1,11 @@
/*
This file contains the arcane tome files.
This file contains the cult dagger and rune list code
*/
/obj/item/tome
name = "arcane tome"
desc = "An old, dusty tome with frayed edges and a sinister-looking cover."
icon_state ="tome"
throw_speed = 2
throw_range = 5
w_class = WEIGHT_CLASS_SMALL
/obj/item/tome/Initialize()
/obj/item/melee/cultblade/dagger/Initialize()
. = ..()
if(!LAZYLEN(GLOB.rune_types))
GLOB.rune_types = list()
@@ -22,19 +14,15 @@ This file contains the arcane tome files.
var/obj/effect/rune/R = i_can_do_loops_now_thanks_remie
GLOB.rune_types[initial(R.cultist_name)] = R //Uses the cultist name for displaying purposes
/obj/item/tome/examine(mob/user)
/obj/item/melee/cultblade/dagger/examine(mob/user)
..()
if(iscultist(user) || isobserver(user))
to_chat(user, "<span class='cult'>The scriptures of the Geometer. Allows the scribing of runes and access to the knowledge archives of the cult of Nar-Sie.</span>")
to_chat(user, "<span class='cult'>Striking a cult structure will unanchor or reanchor it.</span>")
to_chat(user, "<span class='cult'>Striking another cultist with it will purge holy water from them.</span>")
to_chat(user, "<span class='cult'>Striking a noncultist, however, will sear their flesh.</span>")
to_chat(user, "<span class='cult'>Striking a noncultist, however, will tear their flesh.</span>")
/obj/item/tome/attack(mob/living/M, mob/living/user)
if(!istype(M))
return
if(!iscultist(user))
return ..()
/obj/item/melee/cultblade/dagger/attack(mob/living/M, mob/living/user)
if(iscultist(M))
if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion
to_chat(user, "<span class='cult'>You remove the taint from [M].</span>" )
@@ -42,136 +30,16 @@ This file contains the arcane tome files.
M.reagents.del_reagent("holywater")
M.reagents.add_reagent("unholywater",holy2unholy)
add_logs(user, M, "smacked", src, " removing the holy water from them")
return
M.take_bodypart_damage(0, 15) //Used to be a random between 5 and 20
playsound(M, 'sound/weapons/sear.ogg', 50, 1)
M.visible_message("<span class='danger'>[user] strikes [M] with the arcane tome!</span>", \
"<span class='userdanger'>[user] strikes you with the tome, searing your flesh!</span>")
flick("tome_attack", src)
user.do_attack_animation(M)
add_logs(user, M, "smacked", src)
return FALSE
. = ..()
/obj/item/tome/attack_self(mob/user)
/obj/item/melee/cultblade/dagger/attack_self(mob/user)
if(!iscultist(user))
to_chat(user, "<span class='warning'>[src] seems full of unintelligible shapes, scribbles, and notes. Is this some sort of joke?</span>")
to_chat(user, "<span class='warning'>[src] is covered in unintelligible shapes and markings.</span>")
return
open_tome(user)
scribe_rune(user)
/obj/item/tome/proc/open_tome(mob/user)
var/choice = alert(user,"You open the tome...",,"Scribe Rune","More Information","Cancel")
switch(choice)
if("More Information")
read_tome(user)
if("Scribe Rune")
scribe_rune(user)
if("Cancel")
return
/obj/item/tome/proc/read_tome(mob/user)
var/text = ""
text += "<center><font color='red' size=3><b><i>Archives of the Dark One</i></b></font></center><br><br><br>"
text += "A rune's name and effects can be revealed by examining the rune.<br><br>"
text += "<font color='red'><b>Create Talisman</b></font><br>This rune is one of the most important runes the cult has, being the only way to create new talismans. A blank sheet of paper must be on top of the rune. After \
invoking it and choosing which talisman you desire, the paper will be converted, after some delay into a talisman.<br><br>"
text += "<font color='red'><b>Teleport</b></font><br>This rune is unique in that it requires a keyword before the scribing can begin. When invoked, it will find any other Teleport runes; \
If any are found, the user can choose which rune to send to. Upon activation, the rune teleports everything above it to the selected rune, provided the selected rune is unblocked.<br><br>"
text += "<font color='red'><b>Offer</b></font><br><b>This rune is necessary to achieve your goals.</b> Placing a noncultist above it will convert them if it can and sacrifice them otherwise. \
It requires two invokers to convert a target and three to sacrifice a living target or the sacrifice target.<br>\
Successful conversions will produce a tome for the new cultist, in addition to healing them.<br> \
Successful sacrifices will please the Geometer, can complete your objective if it sacrificed the sacrifice target, and will attempt to place the target into a soulstone.<br><br>"
text += "<font color='red'><b>Raise Dead</b></font><br>This rune requires the corpse of a cultist placed upon the rune, and one person sacrificed for each revival you wish to do.\
Provided there are remaining revivals from those sacrificed, invoking the rune will revive the cultist placed upon it.<br><br>"
text += "<font color='red'><b>Electromagnetic Disruption</b></font><br>Robotic lifeforms have time and time again been the downfall of fledgling cults. This rune may allow you to gain the upper \
hand against these pests. By using the rune, a large electromagnetic pulse will be emitted from the rune's location. The size of the EMP will grow significantly for each additional adjacent cultist when the \
rune is activated.<br><br>"
text += "<font color='red'><b>Astral Communion</b></font><br>This rune is perhaps the most ingenious rune that is usable by a single person. Upon invoking the rune, the \
user's spirit will be ripped from their body. In this state, the user's physical body will be locked in place to the rune itself - any attempts to move it will result in the rune pulling it back. \
The body will also take constant damage while in this form, and may even die. The user's spirit will contain their consciousness, and will allow them to freely wander the station as a ghost. This may \
also be used to commune with the dead.<br><br>"
text += "<font color='red'><b>Form Barrier</b></font><br>While simple, this rune serves an important purpose in defense and hindering passage. When invoked, the rune will draw a small amount of blood from the user \
and make the space above the rune completely dense, rendering it impassable for about a minute and a half. This effect will spread to other nearby Barrier Runes. \
The rune may be invoked again to undo this effect and allow passage again.<br><br>"
text += "<font color='red'><b>Summon Cultist</b></font><br>This rune allows the cult to free other cultists with ease. When invoked, it will allow the user to summon a single cultist to the rune from \
any location. It requires two invokers, and will damage each invoker slightly.<br><br>"
text += "<font color='red'><b>Blood Boil</b></font><br>When invoked, this rune will rapidly do a massive amount of damage to all non-cultist viewers, but it will also emit a burst of flame once it finishes. \
It requires three invokers.<br><br>"
text += "<font color='red'><b>Drain Life</b></font><br>This rune will drain the life of every living creature above the rune, healing the invoker for each creature drained by it.<br><br>"
text += "<font color='red'><b>Manifest Spirit</b></font><br>This rune allows you to summon spirits as humanoid fighters. When invoked, a spirit above the rune will be brought to life as a human, wearing nothing, that seeks only to serve you and the Geometer. \
However, the spirit's link to reality is fragile - you must remain on top of the rune, and you will slowly take damage. Upon stepping off the rune, all summoned spirits will dissipate, dropping their items to the ground. You may manifest \
multiple spirits with one rune, but you will rapidly take damage in doing so.<br><br>"
text += "<font color='red'><b><i>Summon Nar-Sie</i></b></font><br><b>This rune is necessary to achieve your goals.</b> On attempting to scribe it, it will produce shields around you and alert everyone you are attempting to scribe it; it takes a very long time to scribe, \
and does massive damage to the one attempting to scribe it.<br>Invoking it requires 9 invokers and the sacrifice of a specific crewmember, and once invoked, will summon the Geometer, Nar-Sie herself. \
This will complete your objectives.<br><br><br>"
text += "<font color='red'><b>Talisman of Teleportation</b></font><br>The talisman form of the Teleport rune will transport the invoker to a selected Teleport rune once.<br><br>"
text += "<font color='red'><b>Talisman of Construction</b></font><br>This talisman is the main way of creating construct shells. To use it, one must strike 25 sheets of metal with the talisman. The sheets will then be twisted into a construct shell, ready to receive a soul to occupy it.<br><br>"
text += "<font color='red'><b>Talisman of Tome Summoning</b></font><br>This talisman will produce a single tome at your feet.<br><br>"
text += "<font color='red'><b>Talisman of Veiling/Revealing</b></font><br>This talisman will hide runes on its first use, and on the second, will reveal runes.<br><br>"
text += "<font color='red'><b>Talisman of Disguising</b></font><br>This talisman will permanently disguise all nearby runes as crayon runes.<br><br>"
text += "<font color='red'><b>Talisman of Electromagnetic Pulse</b></font><br>This talisman will EMP anything else nearby. It disappears after one use.<br><br>"
text += "<font color='red'><b>Talisman of Stunning</b></font><br>Attacking a target will knock them down for a long duration in addition to inhibiting their speech. \
Robotic lifeforms will suffer the effects of a heavy electromagnetic pulse instead.<br><br>"
text += "<font color='red'><b>Talisman of Armaments</b></font><br>The Talisman of Arming will equip the user with armored robes, a backpack, an eldritch longsword, an empowered bola, and a pair of boots. Any items that cannot \
be equipped will not be summoned. Attacking a fellow cultist with it will instead equip them.<br><br>"
text += "<font color='red'><b>Talisman of Horrors</b></font><br>The Talisman of Horror, unlike other talismans, can be applied at range, without the victim noticing. It will cause the victim to have severe hallucinations after a short while.<br><br>"
text += "<font color='red'><b>Talisman of Shackling</b></font><br>The Talisman of Shackling must be applied directly to the victim, it has 4 uses and cuffs victims with magic shackles that disappear when removed.<br><br>"
text += "In addition to these runes, the cult has a small selection of equipment and constructs.<br><br>"
text += "<font color='red'><b>Equipment:</b></font><br><br>"
text += "<font color='red'><b>Cult Blade</b></font><br>Cult blades are sharp weapons that, notably, cannot be used by noncultists. These blades are produced by the Talisman of Arming.<br><br>"
text += "<font color='red'><b>Cult Bola</b></font><br>Cult bolas are strong bolas, useful for snaring targets. These bolas are produced by the Talisman of Arming.<br><br>"
text += "<font color='red'><b>Cult Robes</b></font><br>Cult robes are heavily armored robes. These robes are produced by the Talisman of Arming.<br><br>"
text += "<font color='red'><b>Soulstone</b></font><br>A soulstone is a simple piece of magic, produced either via the starter talisman or by sacrificing humans. Using it on an unconscious or dead human, or on a Shade, will trap their soul in the stone, allowing its use in construct shells. \
<br>The soul within can also be released as a Shade by using it in-hand.<br><br>"
text += "<font color='red'><b>Construct Shell</b></font><br>A construct shell is useless on its own, but placing a filled soulstone within it allows you to produce your choice of a <b>Wraith</b>, a <b>Juggernaut</b>, or an <b>Artificer</b>. \
<br>Each construct has uses, detailed below in Constructs. Construct shells can be produced via the starter talisman or the Rite of Fabrication.<br><br>"
text += "<font color='red'><b>Constructs:</b></font><br><br>"
text += "<font color='red'><b>Shade</b></font><br>While technically not a construct, the Shade is produced when released from a soulstone. It is quite fragile and has weak melee attacks, but is fully healed when recaptured by a soulstone.<br><br>"
text += "<font color='red'><b>Wraith</b></font><br>The Wraith is a fast, lethal melee attacker which can jaunt through walls. However, it is only slightly more durable than a shade.<br><br>"
text += "<font color='red'><b>Juggernaut</b></font><br>The Juggernaut is a slow, but durable, melee attacker which can produce temporary forcewalls. It will also reflect most lethal energy weapons.<br><br>"
text += "<font color='red'><b>Artificer</b></font><br>The Artificer is a weak and fragile construct, able to heal other constructs, shades, or itself, produce more <font color='red'><b>soulstones</b></font> and <font color='red'><b>construct shells</b></font>, \
construct fortifying cult walls and flooring, and finally, it can release a few indiscriminate stunning missiles.<br><br>"
text += "<font color='red'><b>Harvester</b></font><br>If you see one, know that you have done all you can and your life is void.<br><br>"
var/datum/browser/popup = new(user, "tome", "", 800, 600)
popup.set_content(text)
popup.open()
return 1
/obj/item/tome/proc/scribe_rune(mob/living/user)
/obj/item/melee/cultblade/dagger/proc/scribe_rune(mob/living/user)
var/turf/Turf = get_turf(user)
var/chosen_keyword
var/obj/effect/rune/rune_to_scribe
@@ -183,9 +51,6 @@ This file contains the arcane tome files.
if(!user_antag)
return
var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives
var/datum/objective/sacrifice/sac_objective = locate() in user_antag.cult_team.objectives
if(!check_rune_turf(Turf, user))
return
entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in GLOB.rune_types
@@ -203,9 +68,21 @@ This file contains the arcane tome files.
A = get_area(src)
if(!src || QDELETED(src) || !Adjacent(user) || user.incapacitated() || !check_rune_turf(Turf, user))
return
//AAAAAAAAAAAAAAAH, i'm rewriting enough for now so TODO: remove this shit
if(ispath(rune_to_scribe, /obj/effect/rune/apocalypse))
if((world.time - SSticker.round_start_time) <= 6000)
var/wait = 6000 - (world.time - SSticker.round_start_time)
to_chat(user, "<span class='cult italic'>The veil is not yet weak enough for this rune - it will be available in [DisplayTimeText(wait)].</span>")
return
var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives
if(!(A in summon_objective.summon_spots))
to_chat(user, "<span class='cultlarge'>The Apocalypse rune will remove a ritual site (where Nar-sie can be summoned), it can only be scribed in [english_list(summon_objective.summon_spots)]!</span>")
return
if(summon_objective.summon_spots.len < 2)
to_chat(user, "<span class='cultlarge'>Only one ritual site remains - it must be reserved for the final summoning!</span>")
return
if(ispath(rune_to_scribe, /obj/effect/rune/narsie))
var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives
var/datum/objective/sacrifice/sac_objective = locate() in user_antag.cult_team.objectives
if(!summon_objective)
to_chat(user, "<span class='warning'>Nar-Sie does not wish to be summoned!</span>")
return
@@ -257,7 +134,7 @@ This file contains the arcane tome files.
to_chat(user, "<span class='cult'>The [lowertext(R.cultist_name)] rune [R.cultist_desc]</span>")
SSblackbox.record_feedback("tally", "cult_runes_scribed", 1, R.cultist_name)
/obj/item/tome/proc/check_rune_turf(turf/T, mob/user)
/obj/item/melee/cultblade/dagger/proc/check_rune_turf(turf/T, mob/user)
if(isspaceturf(T))
to_chat(user, "<span class='warning'>You cannot scribe runes in space!</span>")
return FALSE
@@ -266,14 +143,16 @@ This file contains the arcane tome files.
to_chat(user, "<span class='cult'>There is already a rune here.</span>")
return FALSE
if(!is_station_level(T.z) && !is_mining_level(T.z))
to_chat(user, "<span class='warning'>The veil is not weak enough here.</span>")
return FALSE
var/area/A = get_area(T)
if(A && !A.blob_allowed)
to_chat(user, "<span class='warning'>There's a passage in [src] specifically forbidding oyster consumption, triple-frying, and building outside of designated cult zones.</span>")
return FALSE
return TRUE
@@ -1,10 +1,12 @@
//after a delay, creates a rune below you. for constructs creating runes.
/datum/action/innate/cult/create_rune
background_icon_state = "bg_cult"
name = "Summon Rune"
desc = "Summons a rune"
background_icon_state = "bg_demon"
var/obj/effect/rune/rune_type
var/cooldown = 0
var/base_cooldown = 900
var/scribe_time = 100
var/base_cooldown = 1800
var/scribe_time = 60
var/damage_interrupt = TRUE
var/action_interrupt = TRUE
var/obj/effect/temp_visual/cult/rune_spawn/rune_word_type
@@ -17,57 +19,100 @@
return FALSE
return ..()
/datum/action/innate/cult/create_rune/Activate()
var/chosen_keyword
if(!isturf(owner.loc))
to_chat(owner, "<span class='warning>You need more space to scribe a rune!</span>")
return
if(initial(rune_type.req_keyword))
chosen_keyword = stripped_input(owner, "Enter a keyword for the new rune.", "Words of Power")
if(!chosen_keyword)
return
//the outer ring is always the same across all runes
var/obj/effect/temp_visual/cult/rune_spawn/R1 = new(owner.loc, scribe_time, rune_color)
//the rest are not always the same, so we need types for em
var/obj/effect/temp_visual/cult/rune_spawn/R2
if(rune_word_type)
R2 = new rune_word_type(owner.loc, scribe_time, rune_color)
var/obj/effect/temp_visual/cult/rune_spawn/R3
if(rune_innerring_type)
R3 = new rune_innerring_type(owner.loc, scribe_time, rune_color)
var/obj/effect/temp_visual/cult/rune_spawn/R4
if(rune_center_type)
R4 = new rune_center_type(owner.loc, scribe_time, rune_color)
/datum/action/innate/cult/create_rune/proc/turf_check(turf/T)
if(!T)
return FALSE
if(isspaceturf(T))
to_chat(owner, "<span class='warning'>You cannot scribe runes in space!</span>")
return FALSE
if(locate(/obj/effect/rune) in T)
to_chat(owner, "<span class='cult'>There is already a rune here.</span>")
return FALSE
if(!is_station_level(T.z) && !is_mining_level(T.z))
to_chat(owner, "<span class='warning'>The veil is not weak enough here.</span>")
return FALSE
return TRUE
cooldown = base_cooldown + world.time
owner.update_action_buttons_icon()
addtimer(CALLBACK(owner, /mob.proc/update_action_buttons_icon), base_cooldown)
var/list/health
if(damage_interrupt && isliving(owner))
var/mob/living/L = owner
health = list("health" = L.health)
var/scribe_mod = scribe_time
if(istype(get_turf(owner), /turf/open/floor/engine/cult))
scribe_mod *= 0.5
if(do_after(owner, scribe_mod, target = owner, extra_checks = CALLBACK(owner, /mob.proc/break_do_after_checks, health, action_interrupt)))
var/obj/effect/rune/new_rune = new rune_type(owner.loc)
new_rune.keyword = chosen_keyword
else
qdel(R1)
if(R2)
qdel(R2)
if(R3)
qdel(R3)
if(R4)
qdel(R4)
cooldown = 0
/datum/action/innate/cult/create_rune/Activate()
var/turf/T = get_turf(owner)
if(turf_check(T))
var/chosen_keyword
if(initial(rune_type.req_keyword))
chosen_keyword = stripped_input(owner, "Enter a keyword for the new rune.", "Words of Power")
if(!chosen_keyword)
return
//the outer ring is always the same across all runes
var/obj/effect/temp_visual/cult/rune_spawn/R1 = new(T, scribe_time, rune_color)
//the rest are not always the same, so we need types for em
var/obj/effect/temp_visual/cult/rune_spawn/R2
if(rune_word_type)
R2 = new rune_word_type(T, scribe_time, rune_color)
var/obj/effect/temp_visual/cult/rune_spawn/R3
if(rune_innerring_type)
R3 = new rune_innerring_type(T, scribe_time, rune_color)
var/obj/effect/temp_visual/cult/rune_spawn/R4
if(rune_center_type)
R4 = new rune_center_type(T, scribe_time, rune_color)
cooldown = base_cooldown + world.time
owner.update_action_buttons_icon()
addtimer(CALLBACK(owner, /mob.proc/update_action_buttons_icon), base_cooldown)
var/list/health
if(damage_interrupt && isliving(owner))
var/mob/living/L = owner
health = list("health" = L.health)
var/scribe_mod = scribe_time
if(istype(T, /turf/open/floor/engine/cult))
scribe_mod *= 0.5
playsound(T, 'sound/magic/enter_blood.ogg', 100, FALSE)
if(do_after(owner, scribe_mod, target = owner, extra_checks = CALLBACK(owner, /mob.proc/break_do_after_checks, health, action_interrupt)))
var/obj/effect/rune/new_rune = new rune_type(owner.loc)
new_rune.keyword = chosen_keyword
else
qdel(R1)
if(R2)
qdel(R2)
if(R3)
qdel(R3)
if(R4)
qdel(R4)
cooldown = 0
owner.update_action_buttons_icon()
//teleport rune
/datum/action/innate/cult/create_rune/tele
name = "Summon Teleport Rune"
desc = "Summons a teleport rune to your location, as though it has been there all along..."
button_icon_state = "telerune"
rune_type = /obj/effect/rune/teleport
rune_word_type = /obj/effect/temp_visual/cult/rune_spawn/rune2
rune_innerring_type = /obj/effect/temp_visual/cult/rune_spawn/rune2/inner
rune_center_type = /obj/effect/temp_visual/cult/rune_spawn/rune2/center
rune_color = RUNE_COLOR_TELEPORT
/datum/action/innate/cult/create_rune/wall
name = "Summon Barrier Rune"
desc = "Summons an active barrier rune to your location, as though it has been there all along..."
button_icon_state = "barrier"
rune_type = /obj/effect/rune/wall
rune_word_type = /obj/effect/temp_visual/cult/rune_spawn/rune4
rune_innerring_type = /obj/effect/temp_visual/cult/rune_spawn/rune4/inner
rune_center_type = /obj/effect/temp_visual/cult/rune_spawn/rune4/center
rune_color = RUNE_COLOR_DARKRED
/datum/action/innate/cult/create_rune/wall/Activate()
. = ..()
var/obj/effect/rune/wall/W = locate(/obj/effect/rune/wall) in owner.loc
if(W)
W.spread_density()
/datum/action/innate/cult/create_rune/revive
name = "Summon Revive Rune"
desc = "Summons a revive rune to your location, as though it has been there all along..."
button_icon_state = "revive"
rune_type = /obj/effect/rune/raise_dead
rune_word_type = /obj/effect/temp_visual/cult/rune_spawn/rune1
rune_innerring_type = /obj/effect/temp_visual/cult/rune_spawn/rune1/inner
rune_center_type = /obj/effect/temp_visual/cult/rune_spawn/rune1/center
rune_color = RUNE_COLOR_MEDIUMRED
+410 -332
View File
@@ -1,16 +1,15 @@
GLOBAL_LIST_EMPTY(sacrificed) //a mixed list of minds and mobs
GLOBAL_LIST(rune_types) //Every rune that can be drawn by tomes
GLOBAL_LIST(rune_types) //Every rune that can be drawn by ritual daggers
GLOBAL_LIST_EMPTY(teleport_runes)
GLOBAL_LIST_EMPTY(wall_runes)
/*
This file contains runes.
Runes are used by the cult to cause many different effects and are paramount to their success.
They are drawn with an arcane tome in blood, and are distinguishable to cultists and normal crew by examining.
They are drawn with a ritual dagger in blood, and are distinguishable to cultists and normal crew by examining.
Fake runes can be drawn in crayon to fool people.
Runes can either be invoked by one's self or with many different cultists. Each rune has a specific incantation that the cultists will say when invoking it.
To draw a rune, use an arcane tome.
*/
@@ -31,7 +30,7 @@ To draw a rune, use an arcane tome.
var/req_cultists_text //if we have a description override for required cultists to invoke
var/rune_in_use = FALSE // Used for some runes, this is for when you want a rune to not be usable when in use.
var/scribe_delay = 50 //how long the rune takes to create
var/scribe_delay = 40 //how long the rune takes to create
var/scribe_damage = 0.1 //how much damage you take doing it
var/allow_excess_invokers = FALSE //if we allow excess invokers when being invoked
@@ -45,6 +44,9 @@ To draw a rune, use an arcane tome.
. = ..()
if(set_keyword)
keyword = set_keyword
var/image/I = image(icon = 'icons/effects/blood.dmi', icon_state = null, loc = src)
I.override = TRUE
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "cult_runes", I)
/obj/effect/rune/examine(mob/user)
..()
@@ -56,9 +58,11 @@ To draw a rune, use an arcane tome.
to_chat(user, "<b>Keyword:</b> [keyword]")
/obj/effect/rune/attackby(obj/I, mob/user, params)
if(istype(I, /obj/item/tome) && iscultist(user))
to_chat(user, "<span class='notice'>You carefully erase the [lowertext(cultist_name)] rune.</span>")
qdel(src)
if(istype(I, /obj/item/melee/cultblade/dagger) && iscultist(user))
SEND_SOUND(user,'sound/items/sheath.ogg')
if(do_after(user, 15, target = src))
to_chat(user, "<span class='notice'>You carefully erase the [lowertext(cultist_name)] rune.</span>")
qdel(src)
else if(istype(I, /obj/item/nullrod))
user.say("BEGONE FOUL MAGIKS!!")
to_chat(user, "<span class='danger'>You disrupt the magic of [src] with [I].</span>")
@@ -72,6 +76,7 @@ To draw a rune, use an arcane tome.
if(invokers.len >= req_cultists)
invoke(invokers)
else
to_chat(user, "<span class='danger'>You need [req_cultists - invokers.len] more adjacent cultists to use this rune in such a manner.</span>")
fail_invoke()
/obj/effect/rune/attack_animal(mob/living/simple_animal/M)
@@ -81,12 +86,12 @@ To draw a rune, use an arcane tome.
else
to_chat(M, "<span class='warning'>You are unable to invoke the rune!</span>")
/obj/effect/rune/proc/talismanhide() //for talisman of revealing/hiding
/obj/effect/rune/proc/conceal() //for talisman of revealing/hiding
visible_message("<span class='danger'>[src] fades away.</span>")
invisibility = INVISIBILITY_OBSERVER
alpha = 100 //To help ghosts distinguish hidden runes
/obj/effect/rune/proc/talismanreveal() //for talisman of revealing/hiding
/obj/effect/rune/proc/reveal() //for talisman of revealing/hiding
invisibility = 0
visible_message("<span class='danger'>[src] suddenly appears!</span>")
alpha = initial(alpha)
@@ -186,136 +191,6 @@ structure_check() searches for nearby cultist structures required for the invoca
return B
return 0
//Rite of Binding: A paper on top of the rune to a talisman.
/obj/effect/rune/imbue
cultist_name = "Create Talisman"
cultist_desc = "transforms paper into powerful magic talismans."
invocation = "H'drak v'loso, mir'kanas verbot!"
icon_state = "3"
color = RUNE_COLOR_TALISMAN
/obj/effect/rune/imbue/invoke(var/list/invokers)
var/mob/living/user = invokers[1] //the first invoker is always the user
var/list/papers_on_rune = checkpapers()
var/entered_talisman_name
var/obj/item/paper/talisman/talisman_type
var/list/possible_talismans = list()
if(!papers_on_rune.len)
to_chat(user, "<span class='cult italic'>There must be a blank paper on top of [src]!</span>")
fail_invoke()
log_game("Talisman Creation rune failed - no blank papers on rune")
return
if(rune_in_use)
to_chat(user, "<span class='cult italic'>[src] can only support one ritual at a time!</span>")
fail_invoke()
log_game("Talisman Creation rune failed - already in use")
return
for(var/I in subtypesof(/obj/item/paper/talisman) - /obj/item/paper/talisman/malformed - /obj/item/paper/talisman/supply - /obj/item/paper/talisman/supply/weak - /obj/item/paper/talisman/summon_tome)
var/obj/item/paper/talisman/J = I
var/talisman_cult_name = initial(J.cultist_name)
if(talisman_cult_name)
possible_talismans[talisman_cult_name] = J //This is to allow the menu to let cultists select talismans by name
entered_talisman_name = input(user, "Choose a talisman to imbue.", "Talisman Choices") as null|anything in possible_talismans
talisman_type = possible_talismans[entered_talisman_name]
if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated() || rune_in_use || !talisman_type)
return
papers_on_rune = checkpapers()
if(!papers_on_rune.len)
to_chat(user, "<span class='cult italic'>There must be a blank paper on top of [src]!</span>")
fail_invoke()
log_game("Talisman Creation rune failed - no blank papers on rune")
return
var/obj/item/paper/paper_to_imbue = papers_on_rune[1]
..()
visible_message("<span class='warning'>Dark power begins to channel into the paper!</span>")
rune_in_use = TRUE
if(do_after(user, initial(talisman_type.creation_time), target = paper_to_imbue))
new talisman_type(get_turf(src))
visible_message("<span class='warning'>[src] glows with power, and bloody images form themselves on [paper_to_imbue].</span>")
qdel(paper_to_imbue)
rune_in_use = FALSE
/obj/effect/rune/imbue/proc/checkpapers()
. = list()
for(var/obj/item/paper/P in get_turf(src))
if(!P.info && !istype(P, /obj/item/paper/talisman))
. |= P
/obj/effect/rune/teleport
cultist_name = "Teleport"
cultist_desc = "warps everything above it to another chosen teleport rune."
invocation = "Sas'so c'arta forbici!"
icon_state = "2"
color = RUNE_COLOR_TELEPORT
req_keyword = TRUE
var/listkey
/obj/effect/rune/teleport/Initialize(mapload, set_keyword)
. = ..()
var/area/A = get_area(src)
var/locname = initial(A.name)
listkey = set_keyword ? "[set_keyword] [locname]":"[locname]"
GLOB.teleport_runes += src
/obj/effect/rune/teleport/Destroy()
GLOB.teleport_runes -= src
return ..()
/obj/effect/rune/teleport/invoke(var/list/invokers)
var/mob/living/user = invokers[1] //the first invoker is always the user
var/list/potential_runes = list()
var/list/teleportnames = list()
for(var/R in GLOB.teleport_runes)
var/obj/effect/rune/teleport/T = R
if(T != src && !is_away_level(T.z))
potential_runes[avoid_assoc_duplicate_keys(T.listkey, teleportnames)] = T
if(!potential_runes.len)
to_chat(user, "<span class='warning'>There are no valid runes to teleport to!</span>")
log_game("Teleport rune failed - no other teleport runes")
fail_invoke()
return
var/turf/T = get_turf(src)
if(is_away_level(T.z))
to_chat(user, "<span class='cult italic'>You are not in the right dimension!</span>")
log_game("Teleport rune failed - user in away mission")
fail_invoke()
return
var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked
var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to?
if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated() || !actual_selected_rune)
fail_invoke()
return
var/turf/target = get_turf(actual_selected_rune)
if(is_blocked_turf(target, TRUE))
to_chat(user, "<span class='warning'>The target rune is blocked. Attempting to teleport to it would be massively unwise.</span>")
fail_invoke()
return
var/movedsomething = FALSE
var/moveuserlater = FALSE
for(var/atom/movable/A in T)
if(A == user)
moveuserlater = TRUE
movedsomething = TRUE
continue
if(!A.anchored)
movedsomething = TRUE
A.forceMove(target)
if(movedsomething)
..()
visible_message("<span class='warning'>There is a sharp crack of inrushing air, and everything above the rune disappears!</span>", null, "<i>You hear a sharp crack.</i>")
to_chat(user, "<span class='cult'>You[moveuserlater ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].</span>")
if(moveuserlater)
user.forceMove(target)
target.visible_message("<span class='warning'>There is a boom of outrushing air as something appears above the rune!</span>", null, "<i>You hear a boom.</i>")
else
fail_invoke()
//Rite of Offering: Converts or sacrifices a target.
/obj/effect/rune/convert
cultist_name = "Offer"
@@ -392,12 +267,17 @@ structure_check() searches for nearby cultist structures required for the invoca
[brutedamage || burndamage ? "even as [convertee.p_their()] wounds heal and close" : "as the markings below [convertee.p_them()] glow a bloody red"]!</span>", \
"<span class='cultlarge'><i>AAAAAAAAAAAAAA-</i></span>")
SSticker.mode.add_cultist(convertee.mind, 1)
new /obj/item/tome(get_turf(src))
new /obj/item/melee/cultblade/dagger(get_turf(src))
convertee.mind.special_role = "Cultist"
to_chat(convertee, "<span class='cult italic'><b>Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible, truth. The veil of reality has been ripped away \
and something evil takes root.</b></span>")
to_chat(convertee, "<span class='cult italic'><b>Assist your new compatriots in their dark dealings. Your goal is theirs, and theirs is yours. You serve the Geometer above all else. Bring it back.\
</b></span>")
if(ishuman(convertee))
var/mob/living/carbon/human/H = convertee
H.uncuff()
H.stuttering = 0
H.cultslurring = 0
return 1
/obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers)
@@ -450,9 +330,135 @@ structure_check() searches for nearby cultist structures required for the invoca
sacrificial.gib()
return TRUE
/obj/effect/rune/empower
cultist_name = "Empower"
cultist_desc = "allows cultists to prepare greater amounts of blood magic at far less of a cost."
invocation = "H'drak v'loso, mir'kanas verbot!"
icon_state = "3"
color = RUNE_COLOR_TALISMAN
construct_invoke = FALSE
/obj/effect/rune/empower/invoke(var/list/invokers)
. = ..()
var/mob/living/user = invokers[1] //the first invoker is always the user
for(var/datum/action/innate/cult/blood_magic/BM in user.actions)
BM.Activate()
/obj/effect/rune/teleport
cultist_name = "Teleport"
cultist_desc = "warps everything above it to another chosen teleport rune."
invocation = "Sas'so c'arta forbici!"
icon_state = "2"
color = RUNE_COLOR_TELEPORT
req_keyword = TRUE
light_power = 4
var/obj/effect/temp_visual/cult/portal/inner_portal //The portal "hint" for off-station teleportations
var/obj/effect/temp_visual/cult/rune_spawn/rune2/outer_portal
var/listkey
/obj/effect/rune/teleport/Initialize(mapload, set_keyword)
. = ..()
var/area/A = get_area(src)
var/locname = initial(A.name)
listkey = set_keyword ? "[set_keyword] [locname]":"[locname]"
GLOB.teleport_runes += src
/obj/effect/rune/teleport/Destroy()
GLOB.teleport_runes -= src
return ..()
/obj/effect/rune/teleport/invoke(var/list/invokers)
var/mob/living/user = invokers[1] //the first invoker is always the user
var/list/potential_runes = list()
var/list/teleportnames = list()
for(var/R in GLOB.teleport_runes)
var/obj/effect/rune/teleport/T = R
if(T != src && !is_away_level(T.z))
potential_runes[avoid_assoc_duplicate_keys(T.listkey, teleportnames)] = T
if(!potential_runes.len)
to_chat(user, "<span class='warning'>There are no valid runes to teleport to!</span>")
log_game("Teleport rune failed - no other teleport runes")
fail_invoke()
return
var/turf/T = get_turf(src)
if(is_away_level(T.z))
to_chat(user, "<span class='cult italic'>You are not in the right dimension!</span>")
log_game("Teleport rune failed - user in away mission")
fail_invoke()
return
var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked
var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to?
if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated() || !actual_selected_rune)
fail_invoke()
return
var/turf/target = get_turf(actual_selected_rune)
if(is_blocked_turf(target, TRUE))
to_chat(user, "<span class='warning'>The target rune is blocked. Attempting to teleport to it would be massively unwise.</span>")
fail_invoke()
return
var/movedsomething = FALSE
var/moveuserlater = FALSE
for(var/atom/movable/A in T)
if(ishuman(A))
new /obj/effect/temp_visual/dir_setting/cult/phase/out(T, A.dir)
new /obj/effect/temp_visual/dir_setting/cult/phase(target, A.dir)
if(A == user)
moveuserlater = TRUE
movedsomething = TRUE
continue
if(!A.anchored)
movedsomething = TRUE
A.forceMove(target)
if(movedsomething)
..()
visible_message("<span class='warning'>There is a sharp crack of inrushing air, and everything above the rune disappears!</span>", null, "<i>You hear a sharp crack.</i>")
to_chat(user, "<span class='cult'>You[moveuserlater ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].</span>")
if(moveuserlater)
user.forceMove(target)
if(is_mining_level(z) && !is_mining_level(target.z)) //No effect if you stay on lavaland
actual_selected_rune.handle_portal("lava")
else
var/area/A = get_area(T)
if(A.map_name == "Space")
actual_selected_rune.handle_portal("space")
target.visible_message("<span class='warning'>There is a boom of outrushing air as something appears above the rune!</span>", null, "<i>You hear a boom.</i>")
else
fail_invoke()
/obj/effect/rune/teleport/proc/handle_portal(portal_type)
var/turf/T = get_turf(src)
if(inner_portal)
qdel(inner_portal) //We need fresh effects/animations
if(outer_portal)
qdel(outer_portal)
playsound(T, pick('sound/effects/sparks1.ogg', 'sound/effects/sparks2.ogg', 'sound/effects/sparks3.ogg', 'sound/effects/sparks4.ogg'), 100, TRUE, 14)
inner_portal = new /obj/effect/temp_visual/cult/portal(T)
if(portal_type == "space")
light_color = RUNE_COLOR_TELEPORT
desc += "<br><b>A tear in reality reveals a black void interspersed with dots of light... something recently teleported here from space!</b>"
else
inner_portal.icon_state = "lava"
light_color = LIGHT_COLOR_FIRE
desc += "<br><b>A tear in reality reveals a coursing river of lava... something recently teleported here from the Lavaland Mines!</b>"
outer_portal = new(T, 600, color)
light_range = 4
update_light()
addtimer(CALLBACK(src, .proc/close_portal), 600, TIMER_UNIQUE)
/obj/effect/rune/teleport/proc/close_portal()
desc = initial(desc)
light_range = 0
update_light()
//Ritual of Dimensional Rending: Calls forth the avatar of Nar-Sie upon the station.
/obj/effect/rune/narsie
cultist_name = "Summon Nar-Sie"
cultist_name = "Nar-Sie"
cultist_desc = "tears apart dimensional barriers, calling forth the Geometer. Requires 9 invokers."
invocation = "TOK-LYR RQA-NAP G'OLT-ULOFT!!"
req_cultists = 9
@@ -473,7 +479,7 @@ structure_check() searches for nearby cultist structures required for the invoca
GLOB.poi_list -= src
. = ..()
/obj/effect/rune/narsie/talismanhide() //can't hide this, and you wouldn't want to
/obj/effect/rune/narsie/conceal() //can't hide this, and you wouldn't want to
return
/obj/effect/rune/narsie/invoke(var/list/invokers)
@@ -481,12 +487,17 @@ structure_check() searches for nearby cultist structures required for the invoca
return
if(!is_station_level(z))
return
var/mob/living/user = invokers[1]
var/datum/antagonist/cult/user_antag = user.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives
var/area/place = get_area(src)
if(!(place in summon_objective.summon_spots))
to_chat(user, "<span class='cultlarge'>The Geometer can only be summoned where the veil is weak - in [english_list(summon_objective.summon_spots)]!</span>")
return
if(locate(/obj/singularity/narsie) in GLOB.poi_list)
for(var/M in invokers)
to_chat(M, "<span class='warning'>[src] fizzles uselessly, Nar-Sie is already on this plane!</span>")
playsound(src, 'sound/items/welder.ogg', 50, 1)
log_game("Summon Nar-Sie rune at [COORD(src)] failed - already summoned")
to_chat(M, "<span class='warning'>Nar-Sie is already on this plane!</span>")
log_game("Nar-Sie rune failed - already summoned")
return
//BEGIN THE SUMMONING
used = TRUE
@@ -494,20 +505,16 @@ structure_check() searches for nearby cultist structures required for the invoca
sound_to_playing_players('sound/effects/dimensional_rend.ogg')
var/turf/T = get_turf(src)
sleep(40)
if(locate(/obj/singularity/narsie) in GLOB.poi_list)
T.visible_message("<span class='warning'>A faint fizzle could be heard echoing along with a soft chorus of screams and chanting...</span>")
playsound(T, 'sound/items/welder.ogg', 25, 1)
return
if(src)
color = RUNE_COLOR_RED
new /obj/singularity/narsie/large/cult(T) //Causes Nar-Sie to spawn even if the rune has been removed
/obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
if((istype(I, /obj/item/tome) && iscultist(user)))
if((istype(I, /obj/item/melee/cultblade/dagger) && iscultist(user)))
user.visible_message("<span class='warning'>[user.name] begins erasing [src]...</span>", "<span class='notice'>You begin erasing [src]...</span>")
if(do_after(user, 50, target = src)) //Prevents accidental erasures.
log_game("Summon Narsie rune erased by [user.mind.key] (ckey) with a tome")
message_admins("[key_name_admin(user)] erased a Narsie rune with a tome")
log_game("Summon Narsie rune erased by [user.mind.key] (ckey) with [I.name]")
message_admins("[key_name_admin(user)] erased a Narsie rune with [I.name]")
..()
else
if(istype(I, /obj/item/nullrod)) //Begone foul magiks. You cannot hinder me.
@@ -517,7 +524,7 @@ structure_check() searches for nearby cultist structures required for the invoca
//Rite of Resurrection: Requires a dead or inactive cultist. When reviving the dead, you can only perform one revival for every sacrifice your cult has carried out.
/obj/effect/rune/raise_dead
cultist_name = "Revive Cultist"
cultist_name = "Revive"
cultist_desc = "requires a dead, mindless, or inactive cultist placed upon the rune. Provided there have been sufficient sacrifices, they will be given a new life."
invocation = "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" //Depends on the name of the user - see below
icon_state = "1"
@@ -545,14 +552,13 @@ structure_check() searches for nearby cultist structures required for the invoca
to_chat(user, "<span class='cult italic'>There are no dead cultists on the rune!</span>")
log_game("Raise Dead rune failed - no cultists to revive")
fail_invoke()
rune_in_use = FALSE
return
if(potential_revive_mobs.len > 1)
mob_to_revive = input(user, "Choose a cultist to revive.", "Cultist to Revive") as null|anything in potential_revive_mobs
else
mob_to_revive = potential_revive_mobs[1]
if(QDELETED(src) || !validness_checks(mob_to_revive, user))
rune_in_use = FALSE
fail_invoke()
return
if(user.name == "Herbert West")
invocation = "To life, to life, I bring them!"
@@ -563,12 +569,11 @@ structure_check() searches for nearby cultist structures required for the invoca
if(LAZYLEN(GLOB.sacrificed) <= revives_used)
to_chat(user, "<span class='warning'>Your cult must carry out another sacrifice before it can revive a cultist!</span>")
fail_invoke()
rune_in_use = FALSE
return
revives_used++
mob_to_revive.revive(1, 1) //This does remove traits and such, but the rune might actually see some use because of it!
mob_to_revive.grab_ghost()
else if(!mob_to_revive.client || mob_to_revive.client.is_afk())
if(!mob_to_revive.client || mob_to_revive.client.is_afk())
set waitfor = FALSE
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", ROLE_CULTIST, null, ROLE_CULTIST, 50, mob_to_revive)
var/mob/dead/observer/theghost = null
@@ -578,6 +583,10 @@ structure_check() searches for nearby cultist structures required for the invoca
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(mob_to_revive)]) to replace an AFK player.")
mob_to_revive.ghostize(0)
mob_to_revive.key = theghost.key
else
fail_invoke()
return
SEND_SOUND(mob_to_revive, 'sound/ambience/antag/bloodcult.ogg')
to_chat(mob_to_revive, "<span class='cultlarge'>\"PASNAR SAVRAE YAM'TOTH. Arise.\"</span>")
mob_to_revive.visible_message("<span class='warning'>[mob_to_revive] draws in a huge breath, red light shining from [mob_to_revive.p_their()] eyes.</span>", \
"<span class='cultlarge'>You awaken suddenly from the void. You're alive!</span>")
@@ -590,140 +599,29 @@ structure_check() searches for nearby cultist structures required for the invoca
if(!Adjacent(user) || user.incapacitated())
return FALSE
if(QDELETED(target_mob))
fail_invoke()
return FALSE
if(!(target_mob in T.contents))
to_chat(user, "<span class='cult italic'>The cultist to revive has been moved!</span>")
fail_invoke()
log_game("Raise Dead rune failed - revival target moved")
return FALSE
var/mob/dead/observer/ghost = target_mob.get_ghost(TRUE)
if(!ghost && (!target_mob.mind || !target_mob.mind.active))
to_chat(user, "<span class='cult italic'>The corpse to revive has no spirit!</span>")
fail_invoke()
log_game("Raise Dead rune failed - revival target has no ghost")
return FALSE
if(!GLOB.sacrificed.len || GLOB.sacrificed.len <= revives_used)
to_chat(user, "<span class='warning'>You have sacrificed too few people to revive a cultist!</span>")
fail_invoke()
log_game("Raise Dead rune failed - too few sacrificed")
return FALSE
return TRUE
/obj/effect/rune/raise_dead/fail_invoke()
..()
rune_in_use = FALSE
for(var/mob/living/M in range(1,src))
if(iscultist(M) && M.stat == DEAD)
M.visible_message("<span class='warning'>[M] twitches.</span>")
//Rite of Disruption: Emits an EMP blast.
/obj/effect/rune/emp
cultist_name = "Electromagnetic Disruption"
cultist_desc = "emits a large electromagnetic pulse, increasing in size for each cultist invoking it, hindering electronics and disabling silicons."
invocation = "Ta'gh fara'qha fel d'amar det!"
icon_state = "5"
allow_excess_invokers = TRUE
color = RUNE_COLOR_EMP
/obj/effect/rune/emp/invoke(var/list/invokers)
var/turf/E = get_turf(src)
..()
visible_message("<span class='warning'>[src] glows blue for a moment before vanishing.</span>")
switch(invokers.len)
if(1 to 2)
playsound(E, 'sound/items/welder2.ogg', 25, 1)
for(var/M in invokers)
to_chat(M, "<span class='warning'>You feel a minute vibration pass through you...</span>")
if(3 to 6)
playsound(E, 'sound/magic/disable_tech.ogg', 50, 1)
for(var/M in invokers)
to_chat(M, "<span class='danger'>Your hair stands on end as a shockwave emanates from the rune!</span>")
if(7 to INFINITY)
playsound(E, 'sound/magic/disable_tech.ogg', 100, 1)
for(var/M in invokers)
var/mob/living/L = M
to_chat(L, "<span class='userdanger'>You chant in unison and a colossal burst of energy knocks you backward!</span>")
L.Knockdown(40)
qdel(src) //delete before pulsing because it's a delay reee
empulse(E, 9*invokers.len, 12*invokers.len) // Scales now, from a single room to most of the station depending on # of chanters
//Rite of Spirit Sight: Separates one's spirit from their body. They will take damage while it is active.
/obj/effect/rune/spirit
cultist_name = "Spirit Sight"
cultist_desc = "severs the link between one's spirit and body. This effect is taxing and one's physical body will take damage while this is active."
invocation = "Fwe'sh mah erl nyag r'ya!"
icon_state = "7"
color = RUNE_COLOR_DARKRED
rune_in_use = FALSE //One at a time, please!
construct_invoke = FALSE
var/mob/living/affecting = null
/obj/effect/rune/spirit/Destroy()
affecting = null
return ..()
/obj/effect/rune/spirit/examine(mob/user)
..()
if(affecting)
to_chat(user, "<span class='cult italic'>A translucent field encases [affecting] above the rune!</span>")
/obj/effect/rune/spirit/can_invoke(mob/living/user)
if(rune_in_use)
to_chat(user, "<span class='cult italic'>[src] cannot support more than one body!</span>")
log_game("Spirit Sight rune failed - more than one user")
return list()
var/turf/T = get_turf(src)
if(!(user in T))
to_chat(user, "<span class='cult italic'>You must be standing on top of [src]!</span>")
log_game("Spirit Sight rune failed - user not standing on rune")
return list()
return ..()
/obj/effect/rune/spirit/invoke(var/list/invokers)
var/mob/living/user = invokers[1]
..()
var/turf/T = get_turf(src)
rune_in_use = TRUE
affecting = user
affecting.add_atom_colour(RUNE_COLOR_DARKRED, ADMIN_COLOUR_PRIORITY)
affecting.visible_message("<span class='warning'>[affecting] freezes statue-still, glowing an unearthly red.</span>", \
"<span class='cult'>You see what lies beyond. All is revealed. While this is a wondrous experience, your physical form will waste away in this state. Hurry...</span>")
affecting.ghostize(1)
while(!QDELETED(affecting))
affecting.apply_damage(0.1, BRUTE)
if(!(affecting in T))
user.visible_message("<span class='warning'>A spectral tendril wraps around [affecting] and pulls [affecting.p_them()] back to the rune!</span>")
Beam(affecting, icon_state="drainbeam", time=2)
affecting.forceMove(get_turf(src)) //NO ESCAPE :^)
if(affecting.key)
affecting.visible_message("<span class='warning'>[affecting] slowly relaxes, the glow around [affecting.p_them()] dimming.</span>", \
"<span class='danger'>You are re-united with your physical form. [src] releases its hold over you.</span>")
affecting.remove_atom_colour(ADMIN_COLOUR_PRIORITY, RUNE_COLOR_DARKRED)
affecting.Knockdown(60)
break
if(affecting.stat == UNCONSCIOUS)
if(prob(1))
var/mob/dead/observer/G = affecting.get_ghost()
to_chat(G, "<span class='cult italic'>You feel the link between you and your body weakening... you must hurry!</span>")
else if(affecting.stat == DEAD)
affecting.remove_atom_colour(ADMIN_COLOUR_PRIORITY, RUNE_COLOR_DARKRED)
var/mob/dead/observer/G = affecting.get_ghost()
to_chat(G, "<span class='cult italic'><b>You suddenly feel your physical form pass on. [src]'s exertion has killed you!</b></span>")
break
sleep(1)
affecting = null
rune_in_use = FALSE
//Rite of the Corporeal Shield: When invoked, becomes solid and cannot be passed. Invoke again to undo.
/obj/effect/rune/wall
cultist_name = "Form Barrier"
cultist_name = "Barrier"
cultist_desc = "when invoked, makes a temporary invisible wall to block passage. Can be invoked again to reverse this."
invocation = "Khari'd! Eske'te tannin!"
icon_state = "1"
color = RUNE_COLOR_MEDIUMRED
icon_state = "4"
color = RUNE_COLOR_DARKRED
CanAtmosPass = ATMOS_PASS_DENSITY
var/density_timer
var/datum/timedevent/density_timer
var/recharging = FALSE
/obj/effect/rune/wall/Initialize(mapload, set_keyword)
@@ -732,8 +630,10 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/wall/examine(mob/user)
..()
if(density)
to_chat(user, "<span class='cult italic'>There is a barely perceptible shimmering of the air above [src].</span>")
if(density && iscultist(user))
var/datum/timedevent/TMR = active_timers[1]
if(TMR)
to_chat(user, "<span class='cultitalic'>The air above this rune has hardened into a barrier that will last [DisplayTimeText(TMR.timeToRun - world.time)].</span>")
/obj/effect/rune/wall/Destroy()
density = FALSE
@@ -767,7 +667,7 @@ structure_check() searches for nearby cultist structures required for the invoca
W.density = TRUE
W.update_state()
W.spread_density()
density_timer = addtimer(CALLBACK(src, .proc/lose_density), 900, TIMER_STOPPABLE)
density_timer = addtimer(CALLBACK(src, .proc/lose_density), 3000, TIMER_STOPPABLE)
/obj/effect/rune/wall/proc/lose_density()
if(density)
@@ -804,7 +704,7 @@ structure_check() searches for nearby cultist structures required for the invoca
invocation = "N'ath reth sh'yro eth d'rekkathnor!"
req_cultists = 2
invoke_damage = 10
icon_state = "5"
icon_state = "3"
color = RUNE_COLOR_SUMMON
/obj/effect/rune/summon/invoke(var/list/invokers)
@@ -849,7 +749,7 @@ structure_check() searches for nearby cultist structures required for the invoca
cultist_desc = "boils the blood of non-believers who can see the rune, rapidly dealing extreme amounts of damage. Requires 3 invokers."
invocation = "Dedo ol'btoh!"
icon_state = "4"
color = RUNE_COLOR_MEDIUMRED
color = RUNE_COLOR_BURNTORANGE
light_color = LIGHT_COLOR_LAVA
req_cultists = 3
invoke_damage = 10
@@ -909,19 +809,20 @@ structure_check() searches for nearby cultist structures required for the invoca
//Rite of Spectral Manifestation: Summons a ghost on top of the rune as a cultist human with no items. User must stand on the rune at all times, and takes damage for each summoned ghost.
/obj/effect/rune/manifest
cultist_name = "Manifest Spirit"
cultist_desc = "manifests a spirit as a servant of the Geometer. The invoker must not move from atop the rune, and will take damage for each summoned spirit."
cultist_name = "Spirit Realm"
cultist_desc = "manifests a spirit servant of the Geometer and allows you to ascend as a spirit yourself. The invoker must not move from atop the rune, and will take damage for each summoned spirit."
invocation = "Gal'h'rfikk harfrandid mud'gib!" //how the fuck do you pronounce this
icon_state = "6"
icon_state = "7"
invoke_damage = 10
construct_invoke = FALSE
color = RUNE_COLOR_MEDIUMRED
var/ghost_limit = 5
color = RUNE_COLOR_DARKRED
var/mob/living/affecting = null
var/ghost_limit = 4
var/ghosts = 0
/obj/effect/rune/manifest/Initialize()
. = ..()
notify_ghosts("Manifest rune created in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src)
/obj/effect/rune/manifest/can_invoke(mob/living/user)
if(!(user in get_turf(src)))
@@ -934,60 +835,91 @@ structure_check() searches for nearby cultist structures required for the invoca
fail_invoke()
log_game("Manifest rune failed - user is a ghost")
return list()
if(ghosts >= ghost_limit)
to_chat(user, "<span class='cult italic'>You are sustaining too many ghosts to summon more!</span>")
fail_invoke()
log_game("Manifest rune failed - too many summoned ghosts")
return list()
var/list/ghosts_on_rune = list()
for(var/mob/dead/observer/O in get_turf(src))
if(O.client && !jobban_isbanned(O, ROLE_CULTIST))
ghosts_on_rune += O
if(!ghosts_on_rune.len)
to_chat(user, "<span class='cult italic'>There are no spirits near [src]!</span>")
fail_invoke()
log_game("Manifest rune failed - no nearby ghosts")
return list()
return ..()
/obj/effect/rune/manifest/invoke(var/list/invokers)
. = ..()
var/mob/living/user = invokers[1]
var/list/ghosts_on_rune = list()
for(var/mob/dead/observer/O in get_turf(src))
if(O.client && !jobban_isbanned(O, ROLE_CULTIST))
ghosts_on_rune += O
var/mob/dead/observer/ghost_to_spawn = pick(ghosts_on_rune)
var/mob/living/carbon/human/cult_ghost/new_human = new(get_turf(src))
new_human.real_name = ghost_to_spawn.real_name
new_human.alpha = 150 //Makes them translucent
new_human.equipOutfit(/datum/outfit/ghost_cultist) //give them armor
new_human.apply_status_effect(STATUS_EFFECT_SUMMONEDGHOST) //ghosts can't summon more ghosts
..()
ghosts++
playsound(src, 'sound/magic/exit_blood.ogg', 50, 1)
visible_message("<span class='warning'>A cloud of red mist forms above [src], and from within steps... a [new_human.gender == FEMALE ? "wo":""]man.</span>")
to_chat(user, "<span class='cult italic'>Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely...</span>")
var/turf/T = get_turf(src)
var/obj/structure/emergency_shield/invoker/N = new(T)
var/choice = alert(user,"You tear open a connection to the spirit realm...",,"Summon a Cult Ghost","Ascend as a Dark Spirit","Cancel")
if(choice == "Summon a Cult Ghost")
notify_ghosts("Manifest rune invoked in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src)
var/list/ghosts_on_rune = list()
for(var/mob/dead/observer/O in get_turf(src))
if(O.client && !jobban_isbanned(O, ROLE_CULTIST))
ghosts_on_rune += O
if(!ghosts_on_rune.len)
to_chat(user, "<span class='cultitalic'>There are no spirits near [src]!</span>")
fail_invoke()
log_game("Manifest rune failed - no nearby ghosts")
return list()
var/mob/dead/observer/ghost_to_spawn = pick(ghosts_on_rune)
var/mob/living/carbon/human/cult_ghost/new_human = new(get_turf(src))
new_human.real_name = ghost_to_spawn.real_name
new_human.alpha = 150 //Makes them translucent
new_human.equipOutfit(/datum/outfit/ghost_cultist) //give them armor
new_human.apply_status_effect(STATUS_EFFECT_SUMMONEDGHOST) //ghosts can't summon more ghosts
new_human.see_invisible = SEE_INVISIBLE_OBSERVER
ghosts++
if(ghosts >= ghost_limit)
to_chat(user, "<span class='cultitalic'>You are sustaining too many ghosts to summon more!</span>")
fail_invoke()
log_game("Manifest rune failed - too many summoned ghosts")
return list()
playsound(src, 'sound/magic/exit_blood.ogg', 50, 1)
visible_message("<span class='warning'>A cloud of red mist forms above [src], and from within steps... a [new_human.gender == FEMALE ? "wo":""]man.</span>")
to_chat(user, "<span class='cultitalic'>Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely...</span>")
var/obj/structure/emergency_shield/invoker/N = new(T)
new_human.key = ghost_to_spawn.key
SSticker.mode.add_cultist(new_human.mind, 0)
to_chat(new_human, "<span class='cultitalic'><b>You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar-Sie, and you are to serve them at all costs.</b></span>")
new_human.key = ghost_to_spawn.key
SSticker.mode.add_cultist(new_human.mind, 0)
to_chat(new_human, "<span class='cult italic'><b>You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar-Sie, and you are to serve them at all costs.</b></span>")
while(!QDELETED(src) && !QDELETED(user) && !QDELETED(new_human) && (user in T))
if(user.stat || new_human.InCritical())
break
user.apply_damage(0.1, BRUTE)
sleep(1)
while(!QDELETED(src) && !QDELETED(user) && !QDELETED(new_human) && (user in T))
if(user.stat || new_human.InCritical())
break
user.apply_damage(0.1, BRUTE)
sleep(1)
qdel(N)
ghosts--
if(new_human)
new_human.visible_message("<span class='warning'>[new_human] suddenly dissolves into bones and ashes.</span>", \
"<span class='cultlarge'>Your link to the world fades. Your form breaks apart.</span>")
for(var/obj/I in new_human)
new_human.dropItemToGround(I, TRUE)
new_human.dust()
qdel(N)
ghosts--
if(new_human)
new_human.visible_message("<span class='warning'>[new_human] suddenly dissolves into bones and ashes.</span>", \
"<span class='cultlarge'>Your link to the world fades. Your form breaks apart.</span>")
for(var/obj/I in new_human)
new_human.dropItemToGround(I, TRUE)
new_human.dust()
else if(choice == "Ascend as a Dark Spirit")
affecting = user
affecting.add_atom_colour(RUNE_COLOR_DARKRED, ADMIN_COLOUR_PRIORITY)
affecting.visible_message("<span class='warning'>[affecting] freezes statue-still, glowing an unearthly red.</span>", \
"<span class='cult'>You see what lies beyond. All is revealed. In this form you find that your voice booms louder and you can mark targets for the entire cult</span>")
var/mob/dead/observer/G = affecting.ghostize(1)
var/datum/action/innate/cult/comm/spirit/CM = new
var/datum/action/innate/cult/ghostmark/GM = new
G.name = "Dark Spirit of [G.name]"
G.color = "red"
CM.Grant(G)
GM.Grant(G)
while(!QDELETED(affecting))
if(!(affecting in T))
user.visible_message("<span class='warning'>A spectral tendril wraps around [affecting] and pulls [affecting.p_them()] back to the rune!</span>")
Beam(affecting, icon_state="drainbeam", time=2)
affecting.forceMove(get_turf(src)) //NO ESCAPE :^)
if(affecting.key)
affecting.visible_message("<span class='warning'>[affecting] slowly relaxes, the glow around [affecting.p_them()] dimming.</span>", \
"<span class='danger'>You are re-united with your physical form. [src] releases its hold over you.</span>")
affecting.Knockdown(40)
break
if(affecting.health <= 10)
to_chat(G, "<span class='cultitalic'>Your body can no longer sustain the connection!</span>")
break
sleep(5)
CM.Remove(G)
GM.Remove(G)
affecting.remove_atom_colour(ADMIN_COLOUR_PRIORITY, RUNE_COLOR_DARKRED)
affecting.grab_ghost()
affecting = null
rune_in_use = FALSE
/mob/living/carbon/human/cult_ghost/spill_organs(no_brain, no_organs, no_bodyparts) //cult ghosts never drop a brain
no_brain = TRUE
@@ -997,3 +929,149 @@ structure_check() searches for nearby cultist structures required for the invoca
. = ..()
for(var/obj/item/organ/brain/B in .) //they're not that smart, really
. -= B
/obj/effect/rune/apocalypse
cultist_name = "Apocalypse"
cultist_desc = "a harbinger of the end times. Grows in strength with the cult's desperation - but at the risk of... side effects."
invocation = "Ta'gh fara'qha fel d'amar det!"
icon = 'icons/effects/96x96.dmi'
icon_state = "apoc"
pixel_x = -32
pixel_y = -32
allow_excess_invokers = TRUE
color = RUNE_COLOR_DARKRED
req_cultists = 3
scribe_delay = 100
/obj/effect/rune/apocalypse/invoke(var/list/invokers)
if(rune_in_use)
return
. = ..()
var/area/place = get_area(src)
var/mob/living/user = invokers[1]
var/datum/antagonist/cult/user_antag = user.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives
if(summon_objective.summon_spots.len <= 1)
to_chat(user, "<span class='cultlarge'>Only one ritual site remains - it must be reserved for the final summoning!</span>")
return
if(!(place in summon_objective.summon_spots))
to_chat(user, "<span class='cultlarge'>The Apocalypse rune will remove a ritual site, where Nar-sie can be summoned, it can only be scribed in [english_list(summon_objective.summon_spots)]!</span>")
return
summon_objective.summon_spots -= place
rune_in_use = TRUE
var/turf/T = get_turf(src)
new /obj/effect/temp_visual/dir_setting/curse/grasp_portal/fading(T)
var/intensity = 0
for(var/mob/living/M in GLOB.player_list)
if(iscultist(M))
intensity++
intensity = max(60, 360 - (360*(intensity/GLOB.player_list.len + 0.3)**2)) //significantly lower intensity for "winning" cults
var/duration = intensity*10
playsound(T, 'sound/magic/enter_blood.ogg', 100, 1)
visible_message("<span class='warning'>A colossal shockwave of energy bursts from the rune, disintegrating it in the process!</span>")
for(var/mob/living/L in range(src, 3))
L.Knockdown(30)
empulse(T, 0.42*(intensity), 1)
var/list/images = list()
var/zmatch = T.z
var/datum/atom_hud/AH = GLOB.huds[DATA_HUD_SECURITY_ADVANCED]
for(var/mob/living/M in GLOB.alive_mob_list)
if(M.z != zmatch)
continue
if(ishuman(M))
if(!iscultist(M))
AH.remove_hud_from(M)
addtimer(CALLBACK(GLOBAL_PROC, .proc/hudFix, M), duration)
var/image/A = image('icons/mob/mob.dmi',M,"cultist", ABOVE_MOB_LAYER)
A.override = 1
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/noncult, "human_apoc", A, FALSE)
addtimer(CALLBACK(M,/atom/.proc/remove_alt_appearance,"human_apoc",TRUE), duration)
images += A
SEND_SOUND(M, pick(sound('sound/ambience/antag/bloodcult.ogg'),sound('sound/spookoween/ghost_whisper.ogg'),sound('sound/spookoween/ghosty_wind.ogg')))
else
var/construct = pick("floater","artificer","behemoth")
var/image/B = image('icons/mob/mob.dmi',M,construct, ABOVE_MOB_LAYER)
B.override = 1
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/noncult, "mob_apoc", B, FALSE)
addtimer(CALLBACK(M,/atom/.proc/remove_alt_appearance,"mob_apoc",TRUE), duration)
images += B
if(!iscultist(M))
if(M.client)
var/image/C = image('icons/effects/cult_effects.dmi',M,"bloodsparkles", ABOVE_MOB_LAYER)
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/cult, "cult_apoc", C, FALSE)
addtimer(CALLBACK(M,/atom/.proc/remove_alt_appearance,"cult_apoc",TRUE), duration)
images += C
else
to_chat(M, "<span class='cultlarge'>An Apocalypse Rune was invoked in the [place.name], it is no longer available as a summoning site!</span>")
SEND_SOUND(M, 'sound/effects/pope_entry.ogg')
image_handler(images, duration)
if(intensity>=285) // Based on the prior formula, this means the cult makes up <15% of current players
var/outcome = rand(1,100)
switch(outcome)
if(1 to 10)
var/datum/round_event_control/disease_outbreak/D = new()
var/datum/round_event_control/mice_migration/M = new()
D.runEvent()
M.runEvent()
if(11 to 20)
var/datum/round_event_control/radiation_storm/RS = new()
RS.runEvent()
if(21 to 30)
var/datum/round_event_control/brand_intelligence/BI = new()
BI.runEvent()
if(31 to 40)
var/datum/round_event_control/immovable_rod/R = new()
R.runEvent()
R.runEvent()
R.runEvent()
if(41 to 50)
var/datum/round_event_control/meteor_wave/MW = new()
MW.runEvent()
if(51 to 60)
var/datum/round_event_control/spider_infestation/SI = new()
SI.runEvent()
if(61 to 70)
var/datum/round_event_control/anomaly/anomaly_flux/AF
var/datum/round_event_control/anomaly/anomaly_grav/AG
var/datum/round_event_control/anomaly/anomaly_pyro/AP
var/datum/round_event_control/anomaly/anomaly_vortex/AV
AF.runEvent()
AG.runEvent()
AP.runEvent()
AV.runEvent()
if(71 to 80)
var/datum/round_event_control/spacevine/SV = new()
var/datum/round_event_control/grey_tide/GT = new()
SV.runEvent()
GT.runEvent()
if(81 to 100)
var/datum/round_event_control/portal_storm_narsie/N = new()
N.runEvent()
qdel(src)
/obj/effect/rune/apocalypse/proc/image_handler(var/list/images, duration)
var/end = world.time + duration
set waitfor = 0
while(end>world.time)
for(var/image/I in images)
I.override = FALSE
animate(I, alpha = 0, time = 25, flags = ANIMATION_PARALLEL)
sleep(35)
for(var/image/I in images)
animate(I, alpha = 255, time = 25, flags = ANIMATION_PARALLEL)
sleep(25)
for(var/image/I in images)
if(I.icon_state != "bloodsparkles")
I.override = TRUE
sleep(190)
/proc/hudFix(mob/living/carbon/human/target)
if(!target || !target.client)
return
var/obj/O = target.get_item_by_slot(slot_glasses)
if(istype(O, /obj/item/clothing/glasses/hud/security))
var/datum/atom_hud/AH = GLOB.huds[DATA_HUD_SECURITY_ADVANCED]
AH.add_hud_to(target)
@@ -2,7 +2,7 @@
name = "Spawn Swarmer Shell"
typepath = /datum/round_event/spawn_swarmer
weight = 7
max_occurrences = 0 //nononononono
max_occurrences = 1 //Only once okay fam
earliest_start = 18000 //30 minutes
min_players = 15
@@ -98,11 +98,11 @@
var/list/new_overlay_types = tile_graphic()
var/list/atmos_overlay_types = src.atmos_overlay_types // Cache for free performance
#if DM_VERSION >= 513
/*#if DM_VERSION >= 513
#warning 512 is stable now for sure, remove the old code
#endif
#endif*/
#if DM_VERSION >= 512
/*#if DM_VERSION >= 512
if (atmos_overlay_types)
for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added
vars["vis_contents"] -= overlay
@@ -112,7 +112,7 @@
vars["vis_contents"] += new_overlay_types - atmos_overlay_types //don't add overlays that already exist
else
vars["vis_contents"] += new_overlay_types
#else
#else*/
if (atmos_overlay_types)
for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added
cut_overlay(overlay)
@@ -122,7 +122,7 @@
add_overlay(new_overlay_types - atmos_overlay_types) //don't add overlays that already exist
else
add_overlay(new_overlay_types)
#endif
//#endif
UNSETEMPTY(new_overlay_types)
src.atmos_overlay_types = new_overlay_types
@@ -98,7 +98,7 @@
/datum/gas/tritium = new/datum/tlv/dangerous,
/datum/gas/stimulum = new/datum/tlv/dangerous,
/datum/gas/nitryl = new/datum/tlv/dangerous,
/datum/gas/pluoxium = new/datum/tlv/dangerous
/datum/gas/pluoxium = new/datum/tlv(-1, -1, 135, 140) // Partial pressure, kpa
)
/obj/machinery/airalarm/server // No checks here.
@@ -134,7 +134,7 @@
/datum/gas/tritium = new/datum/tlv/dangerous,
/datum/gas/stimulum = new/datum/tlv/dangerous,
/datum/gas/nitryl = new/datum/tlv/dangerous,
/datum/gas/pluoxium = new/datum/tlv/dangerous
/datum/gas/pluoxium = new/datum/tlv(-1, -1, 135, 140) // Partial pressure, kpa
)
/obj/machinery/airalarm/engine
@@ -88,6 +88,8 @@
/datum/pipeline/proc/addMember(obj/machinery/atmospherics/A, obj/machinery/atmospherics/N)
if(istype(A, /obj/machinery/atmospherics/pipe))
var/obj/machinery/atmospherics/pipe/P = A
if(P.parent)
merge(P.parent)
P.parent = src
var/list/adjacent = P.pipeline_expansion()
for(var/obj/machinery/atmospherics/pipe/I in adjacent)
+17 -12
View File
@@ -17,6 +17,9 @@ You can set verify to TRUE if you want send() to sleep until the client has the
//When sending mutiple assets, how many before we give the client a quaint little sending resources message
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
//When passively preloading assets, how many to send at once? Too high creates noticable lag where as too low can flood the client's cache with "verify" files
#define ASSET_CACHE_PRELOAD_CONCURRENT 3
/client
var/list/cache = list() // List of all assets sent to this client by the asset cache.
var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement.
@@ -42,12 +45,9 @@ You can set verify to TRUE if you want send() to sleep until the client has the
return 0
client << browse_rsc(SSassets.cache[asset_name], asset_name)
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
if (client)
client.cache += asset_name
if(!verify)
client.cache += asset_name
return 1
if (!client)
return 0
client.sending |= asset_name
var/job = ++client.last_asset_job
@@ -94,12 +94,10 @@ You can set verify to TRUE if you want send() to sleep until the client has the
if (asset in SSassets.cache)
client << browse_rsc(SSassets.cache[asset], asset)
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
if (client)
client.cache += unreceived
if(!verify) // Can't access the asset cache browser, rip.
client.cache += unreceived
return 1
if (!client)
return 0
client.sending |= unreceived
var/job = ++client.last_asset_job
@@ -125,12 +123,19 @@ You can set verify to TRUE if you want send() to sleep until the client has the
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
//The proc calls procs that sleep for long times.
/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
var/concurrent_tracker = 1
for(var/file in files)
if (!client)
break
if (register_asset)
register_asset(file,files[file])
send_asset(client,file)
register_asset(file, files[file])
if (concurrent_tracker >= ASSET_CACHE_PRELOAD_CONCURRENT)
concurrent_tracker = 1
send_asset(client, file)
else
concurrent_tracker++
send_asset(client, file, verify=FALSE)
stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
+27 -4
View File
@@ -409,11 +409,11 @@
item_color = "black"
desc = "A pair of black shoes."
flags_1 = NOSLIP_1
/obj/item/clothing/shoes/chameleon/noslip/broken/Initialize()
. = ..()
chameleon_action.emp_randomise(INFINITY)
chameleon_action.emp_randomise(INFINITY)
/obj/item/gun/energy/laser/chameleon
name = "practice laser gun"
desc = "A modified version of the basic laser gun, this one fires less concentrated energy bolts designed for target practice."
@@ -431,6 +431,8 @@
var/list/ammo_copy_vars
var/list/gun_copy_vars
var/badmin_mode = FALSE
var/can_hitscan = FALSE
var/hitscan_mode = FALSE
var/static/list/blacklisted_vars = list("locs", "loc", "contents", "x", "y", "z")
/obj/item/gun/energy/laser/chameleon/Initialize()
@@ -441,7 +443,7 @@
chameleon_action.chameleon_blacklist = typecacheof(/obj/item/gun/magic, ignore_root_path = FALSE)
chameleon_action.initialize_disguises()
projectile_copy_vars = list("name", "icon", "icon_state", "item_state", "speed", "color", "hitsound", "forcedodge", "impact_effect_type", "range", "suppressed", "hitsound_wall", "impact_effect_type", "pass_flags")
projectile_copy_vars = list("name", "icon", "icon_state", "item_state", "speed", "color", "hitsound", "forcedodge", "impact_effect_type", "range", "suppressed", "hitsound_wall", "impact_effect_type", "pass_flags", "tracer_type", "muzzle_type", "impact_type")
chameleon_projectile_vars = list("name" = "practice laser", "icon" = 'icons/obj/projectiles.dmi', "icon_state" = "laser")
gun_copy_vars = list("fire_sound", "burst_size", "fire_delay")
chameleon_gun_vars = list()
@@ -493,6 +495,11 @@
if(istype(chambered, /obj/item/ammo_casing/energy/chameleon))
var/obj/item/ammo_casing/energy/chameleon/AC = chambered
AC.projectile_vars = chameleon_projectile_vars.Copy()
if(!P.tracer_type)
can_hitscan = FALSE
set_hitscan(FALSE)
else
can_hitscan = TRUE
if(badmin_mode)
qdel(chambered.BB)
chambered.projectile_type = P.type
@@ -529,6 +536,22 @@
var/obj/item/ammo_casing/AC = new /obj/item/ammo_casing/syringegun(src)
set_chameleon_ammo(AC)
/obj/item/gun/energy/laser/chameleon/attack_self(mob/user)
. = ..()
if(!can_hitscan)
to_chat(user, "<span class='warning'>[src]'s current disguised gun does not allow it to enable high velocity mode!</span>")
return
if(!chambered)
to_chat(user, "<span class='warning'>Unknown error in energy lens: Please reset chameleon disguise and try again.</span>")
return
set_hitscan(!hitscan_mode)
to_chat(user, "<span class='notice'>You toggle [src]'s high velocity beam mode to [hitscan_mode? "on" : "off"].</span>")
/obj/item/gun/energy/laser/chameleon/proc/set_hitscan(hitscan)
var/obj/item/ammo_casing/energy/chameleon/AC = chambered
AC.hitscan_mode = hitscan
hitscan_mode = hitscan
/obj/item/gun/energy/laser/chameleon/proc/get_chameleon_projectile(guntype)
reset_chameleon_vars()
var/obj/item/gun/G = new guntype(src)
+5
View File
@@ -422,6 +422,11 @@
result = /obj/structure/curtain
category = CAT_MISC
/datum/crafting_recipe/extendohand
name = "Extendo-Hand"
reqs = list(/obj/item/bodypart/r_arm/robot = 1, /obj/item/clothing/gloves/boxing = 1)
result = /obj/item/extendohand
category = CAT_MISC
/datum/crafting_recipe/chemical_payload
name = "Chemical Payload (C4)"
@@ -318,6 +318,11 @@ h1.alert, h2.alert {color: #000000;}
.green {color: #03ff39;}
.shadowling {color: #3b2769;}
.cult {color: #960000;}
.cultitalic {color: #960000; font-style: italic;}
.cultbold {color: #960000; font-style: italic; font-weight: bold;}
.cultboldtalic {color: #960000; font-weight: bold; font-size: 24px;}
.cultlarge {color: #960000; font-weight: bold; font-size: 24px;}
.narsie {color: #960000; font-weight: bold; font-size: 120px;}
.narsiesmall {color: #960000; font-weight: bold; font-size: 48px;}
+3 -3
View File
@@ -212,7 +212,7 @@
// Cell recharging trait. Charges all mob's power cells to (potency*rate)% mark when eaten.
// Generates sparks on squash.
// Small (potency*rate*5) chance to shock squish or slip target for (potency*rate*5) damage.
// Multiplies max charge by (rate*1000) when used in potato power cells.
// Also affects plant batteries see capatative cell production datum
name = "Electrical Activity"
rate = 0.2
@@ -355,8 +355,8 @@
// The secret of potato supercells!
var/datum/plant_gene/trait/cell_charge/CG = G.seed.get_gene(/datum/plant_gene/trait/cell_charge)
if(CG) // 10x charge for deafult cell charge gene - 20 000 with 100 potency.
pocell.maxcharge *= CG.rate*1000
if(CG) // Cell charge max is now 40MJ or otherwise known as 400KJ (Same as bluespace powercells)
pocell.maxcharge *= CG.rate*100
pocell.charge = pocell.maxcharge
pocell.name = "[G.name] battery"
pocell.desc = "A rechargeable plant-based power cell. This one has a rating of [DisplayEnergy(pocell.maxcharge)], and you should not swallow it."
@@ -22,59 +22,60 @@
if(!io.holder.assembly)
to_chat(user, "<span class='warning'>\The [io.holder] needs to be secured inside an assembly first.</span>")
return
if(mode == WIRE)
selected_io = io
to_chat(user, "<span class='notice'>You attach a data wire to \the [selected_io.holder]'s [selected_io.name] data channel.</span>")
mode = WIRING
update_icon()
else if(mode == WIRING)
if(io == selected_io)
to_chat(user, "<span class='warning'>Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.</span>")
return
if(io.io_type != selected_io.io_type)
to_chat(user, "<span class='warning'>Those two types of channels are incompatable. The first is a [selected_io.io_type], \
while the second is a [io.io_type].</span>")
return
if(io.holder.assembly && io.holder.assembly != selected_io.holder.assembly)
to_chat(user, "<span class='warning'>Both \the [io.holder] and \the [selected_io.holder] need to be inside the same assembly.</span>")
return
selected_io.connect_pin(io)
switch(mode)
if(WIRE)
selected_io = io
to_chat(user, "<span class='notice'>You attach a data wire to \the [selected_io.holder]'s [selected_io.name] data channel.</span>")
mode = WIRING
update_icon()
if(WIRING)
if(io == selected_io)
to_chat(user, "<span class='warning'>Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.</span>")
return
if(io.io_type != selected_io.io_type)
to_chat(user, "<span class='warning'>Those two types of channels are incompatible. The first is a [selected_io.io_type], \
while the second is a [io.io_type].</span>")
return
if(io.holder.assembly && io.holder.assembly != selected_io.holder.assembly)
to_chat(user, "<span class='warning'>Both \the [io.holder] and \the [selected_io.holder] need to be inside the same assembly.</span>")
return
selected_io.connect_pin(io)
to_chat(user, "<span class='notice'>You connect \the [selected_io.holder]'s [selected_io.name] to \the [io.holder]'s [io.name].</span>")
mode = WIRE
update_icon()
selected_io.holder.interact(user) // This is to update the UI.
selected_io = null
else if(mode == UNWIRE)
selected_io = io
if(!io.linked.len)
to_chat(user, "<span class='warning'>There is nothing connected to \the [selected_io] data channel.</span>")
selected_io = null
return
to_chat(user, "<span class='notice'>You prepare to detach a data wire from \the [selected_io.holder]'s [selected_io.name] data channel.</span>")
mode = UNWIRING
update_icon()
return
else if(mode == UNWIRING)
if(io == selected_io)
to_chat(user, "<span class='warning'>You can't wire a pin into each other, so unwiring \the [selected_io.holder] from \
the same pin is rather moot.</span>")
return
if(selected_io in io.linked)
selected_io.disconnect_pin(io)
to_chat(user, "<span class='notice'>You disconnect \the [selected_io.holder]'s [selected_io.name] from \
\the [io.holder]'s [io.name].</span>")
to_chat(user, "<span class='notice'>You connect \the [selected_io.holder]'s [selected_io.name] to \the [io.holder]'s [io.name].</span>")
mode = WIRE
update_icon()
selected_io.holder.interact(user) // This is to update the UI.
selected_io = null
mode = UNWIRE
if(UNWIRE)
selected_io = io
if(!io.linked.len)
to_chat(user, "<span class='warning'>There is nothing connected to \the [selected_io] data channel.</span>")
selected_io = null
return
to_chat(user, "<span class='notice'>You prepare to detach a data wire from \the [selected_io.holder]'s [selected_io.name] data channel.</span>")
mode = UNWIRING
update_icon()
else
to_chat(user, "<span class='warning'>\The [selected_io.holder]'s [selected_io.name] and \the [io.holder]'s \
[io.name] are not connected.</span>")
return
if(UNWIRING)
if(io == selected_io)
to_chat(user, "<span class='warning'>You can't wire a pin into each other, so unwiring \the [selected_io.holder] from \
the same pin is rather moot.</span>")
return
if(selected_io in io.linked)
selected_io.disconnect_pin(io)
to_chat(user, "<span class='notice'>You disconnect \the [selected_io.holder]'s [selected_io.name] from \
\the [io.holder]'s [io.name].</span>")
selected_io.holder.interact(user) // This is to update the UI.
selected_io = null
mode = UNWIRE
update_icon()
else
to_chat(user, "<span class='warning'>\The [selected_io.holder]'s [selected_io.name] and \the [io.holder]'s \
[io.name] are not connected.</span>")
return
/obj/item/device/integrated_electronics/wirer/attack_self(mob/user)
switch(mode)
if(WIRE)
@@ -97,4 +98,4 @@
#undef WIRE
#undef WIRING
#undef UNWIRE
#undef UNWIRING
#undef UNWIRING
+1 -1
View File
@@ -6,7 +6,7 @@
keys_held[_key] = world.time
var/movement = SSinput.movement_keys[_key]
if(!(next_move_dir_sub & movement))
if(!(next_move_dir_sub & movement) && !keys_held["Ctrl"])
next_move_dir_add |= movement
// Client-level keybindings are ones anyone should be able to do at any time
+3 -3
View File
@@ -304,7 +304,7 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
dat += "<A href='?src=[REF(src)];switchscreen=0'>(Return to main menu)</A><BR>"
if(8)
dat += "<h3>Accessing Forbidden Lore Vault v 1.3</h3>"
dat += "Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.<p>"
dat += "Are you absolutely sure you want to proceed? EldritchRelics Inc. takes no responsibilities for loss of sanity resulting from this action.<p>"
dat += "<A href='?src=[REF(src)];arccheckout=1'>Yes.</A><BR>"
dat += "<A href='?src=[REF(src)];switchscreen=0'>No.</A><BR>"
@@ -322,11 +322,11 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
var/spook = pick("blood", "brass")
var/turf/T = get_turf(src)
if(spook == "blood")
new /obj/item/tome(T)
new /obj/item/melee/cultblade/dagger(T)
else
new /obj/item/clockwork/slab(T)
to_chat(user, "<span class='warning'>Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a [spook == "blood" ? "dusty old tome" : "strange metal tablet"] sitting on the desk. You don't really remember printing it.[spook == "brass" ? " And how did it print something made of metal?" : ""]</span>")
to_chat(user, "<span class='warning'>Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a [spook == "blood" ? "sinister dagger" : "strange metal tablet"] sitting on the desk. You don't even remember where it came from...</span>")
user.visible_message("[user] stares at the blank screen for a few moments, [user.p_their()] expression frozen in fear. When [user.p_they()] finally awaken[user.p_s()] from it, [user.p_they()] look[user.p_s()] a lot older.", 2)
/obj/machinery/computer/libraryconsole/bookmanagement/attackby(obj/item/W, mob/user, params)
+23 -6
View File
@@ -4,8 +4,8 @@
/obj/effect/baseturf_helper //Set the baseturfs of every turf in the /area/ it is placed.
name = "baseturf editor"
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "syndballoon"
icon = 'icons/effects/mapping_helpers.dmi'
icon_state = ""
var/baseturf = null
layer = POINT_LAYER
@@ -72,7 +72,7 @@
return ..()
whitelist = typecacheof(whitelist)
return ..()
/obj/effect/baseturf_helper/picky/replace_baseturf(turf/thing)
if(!whitelist[thing.type])
return
@@ -86,18 +86,35 @@
name = "picky lavaland basalt baseturf helper"
baseturf = /turf/open/floor/plating/asteroid/basalt/lava_land_surface
/obj/effect/mapping_helpers
icon = 'icons/effects/mapping_helpers.dmi'
icon_state = ""
/obj/effect/mapping_helpers/Initialize()
..()
return INITIALIZE_HINT_QDEL
//needs to do its thing before spawn_rivers() is called
INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
/obj/effect/mapping_helpers/no_lava
icon_state = "no_lava"
/obj/effect/mapping_helpers/no_lava/Initialize()
. = ..()
var/turf/T = get_turf(src)
T.flags_1 |= NO_LAVA_GEN_1
//Contains the list of planetary z-levels defined by the planet_z helper.
GLOBAL_LIST_EMPTY(z_is_planet)
/obj/effect/mapping_helpers/planet_z //adds the map it is on to the z_is_planet list
name = "planet z helper"
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "syndballoon"
layer = POINT_LAYER
/obj/effect/mapping_helpers/planet_z/Initialize()
. = ..()
var/turf/T = get_turf(src)
GLOB.z_is_planet["[T.z]"] = TRUE
return INITIALIZE_HINT_QDEL
@@ -910,7 +910,18 @@
if(used)
return
used = TRUE
var/choice = input(user,"Who do you want dead?","Choose Your Victim") as null|anything in GLOB.player_list
var/list/da_list = list()
for(var/I in GLOB.alive_mob_list & GLOB.player_list)
var/mob/living/L = I
da_list[L.real_name] = L
var/choice = input(user,"Who do you want dead?","Choose Your Victim") as null|anything in da_list
choice = da_list[choice]
if(!choice)
return
if(!(isliving(choice)))
to_chat(user, "[choice] is already dead!")
+1 -1
View File
@@ -55,7 +55,7 @@
new /datum/data/mining_equipment("Drone Melee Upgrade", /obj/item/device/mine_bot_ugprade, 400),
new /datum/data/mining_equipment("Drone Health Upgrade", /obj/item/device/mine_bot_ugprade/health, 400),
new /datum/data/mining_equipment("Drone Ranged Upgrade", /obj/item/device/mine_bot_ugprade/cooldown, 600),
new /datum/data/mining_equipment("Drone AI Upgrade", /obj/item/slimepotion/sentience/mining, 1000),
new /datum/data/mining_equipment("Drone AI Upgrade", /obj/item/slimepotion/slime/sentience/mining, 1000),
new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 2500),
)
+1 -1
View File
@@ -255,7 +255,7 @@
//AI
/obj/item/slimepotion/sentience/mining
/obj/item/slimepotion/slime/sentience/mining
name = "minebot AI upgrade"
desc = "Can be used to grant sentience to minebots."
icon_state = "door_electronics"
+13 -15
View File
@@ -2,7 +2,7 @@
name = "Man-Machine Interface"
desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity, that nevertheless has become standard-issue on Nanotrasen stations."
icon = 'icons/obj/assemblies.dmi'
icon_state = "mmi_empty"
icon_state = "mmi_off"
w_class = WEIGHT_CLASS_NORMAL
var/braintype = "Cyborg"
var/obj/item/device/radio/radio = null //Let's give it a radio.
@@ -15,21 +15,19 @@
var/overrides_aicore_laws = FALSE // Whether the laws on the MMI, if any, override possible pre-existing laws loaded on the AI core.
/obj/item/device/mmi/update_icon()
if(brain)
if(istype(brain, /obj/item/organ/brain/alien))
if(brainmob && brainmob.stat == DEAD)
icon_state = "mmi_alien_dead"
else
icon_state = "mmi_alien"
braintype = "Xenoborg" //HISS....Beep.
else
if(brainmob && brainmob.stat == DEAD)
icon_state = "mmi_dead"
else
icon_state = "mmi_full"
braintype = "Cyborg"
if(!brain)
icon_state = "mmi_off"
return
if(istype(brain, /obj/item/organ/brain/alien))
icon_state = "mmi_brain_alien"
braintype = "Xenoborg" //HISS....Beep.
else
icon_state = "mmi_empty"
icon_state = "mmi_brain"
braintype = "Cyborg"
if(brainmob && brainmob.stat != DEAD)
add_overlay("mmi_alive")
else
add_overlay("mmi_dead")
/obj/item/device/mmi/Initialize()
. = ..()
@@ -213,6 +213,8 @@
/mob/living/carbon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, override = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
if(tesla_shock && (flags_2 & TESLA_IGNORE_2))
return FALSE
if(has_trait(TRAIT_SHOCKIMMUNE))
return FALSE
shock_damage *= siemens_coeff
if(dna && dna.species)
shock_damage *= dna.species.siemens_coeff
@@ -121,13 +121,13 @@
var/datum/action/innate/swap_body/swap_body
/datum/species/jelly/slime/on_species_loss(mob/living/carbon/C)
if(slime_split)
/* if(slime_split)
slime_split.Remove(C)
if(swap_body)
swap_body.Remove(C)
bodies -= C // This means that the other bodies maintain a link
// so if someone mindswapped into them, they'd still be shared.
bodies = null
bodies = null */
C.blood_volume = min(C.blood_volume, BLOOD_VOLUME_NORMAL)
..()
@@ -146,26 +146,28 @@
/datum/species/jelly/slime/spec_death(gibbed, mob/living/carbon/human/H)
if(slime_split)
var/datum/mind/M
for(var/mob/living/L in bodies)
if(L.mind && L.mind.active)
M = L.mind
if(!M || M != H.mind)
if(!H.mind || !H.mind.active)
return
var/list/available_bodies = (bodies - H)
for(var/mob/living/L in available_bodies)
if(!swap_body.can_swap(L))
available_bodies -= L
if(!LAZYLEN(available_bodies))
return
swap_body.swap_to_dupe(M, pick(available_bodies))
swap_body.swap_to_dupe(H.mind, pick(available_bodies))
//If you're cloned you get your body pool back
/datum/species/jelly/slime/copy_properties_from(datum/species/jelly/slime/old_species)
bodies = old_species.bodies
/datum/species/jelly/slime/spec_life(mob/living/carbon/human/H)
/* if(H.blood_volume >= BLOOD_VOLUME_SLIME_SPLIT)
if(H.blood_volume >= BLOOD_VOLUME_SLIME_SPLIT)
if(prob(5))
to_chat(H, "<span class='notice'>You feel very bloated!</span>")*/
if(H.nutrition >= NUTRITION_LEVEL_WELL_FED)
to_chat(H, "<span class='notice'>You feel very bloated!</span>")
else if(H.nutrition >= NUTRITION_LEVEL_WELL_FED)
H.blood_volume += 3
H.nutrition -= 2.5
@@ -222,7 +224,7 @@
spare.domutcheck()
spare.Move(get_step(H.loc, pick(NORTH,SOUTH,EAST,WEST)))
H.blood_volume = BLOOD_VOLUME_SAFE
H.blood_volume *= 0.45
H.notransform = 0
var/datum/species/jelly/slime/origin_datum = H.dna.species
@@ -286,24 +288,31 @@
stat = "Unconscious"
if(DEAD)
stat = "Dead"
var/current = body.mind
var/is_conscious = (body.stat == CONSCIOUS)
var/occupied
if(body == H)
occupied = "owner"
else if(body.mind && body.mind.active)
occupied = "stranger"
else
occupied = "available"
L["status"] = stat
L["exoticblood"] = body.blood_volume
L["name"] = body.name
L["ref"] = "[REF(body)]"
L["is_current"] = current
L["occupied"] = occupied
var/button
if(current)
if(occupied == "owner")
button = "selected"
else if(is_conscious)
else if(occupied == "stranger")
button = "danger"
else if(can_swap(body))
button = null
else
button = "disabled"
L["swap_button_state"] = button
L["swappable"] = !current && is_conscious
L["swappable"] = (occupied == "available") && can_swap(body)
data["bodies"] += list(L)
@@ -315,32 +324,47 @@
var/mob/living/carbon/human/H = owner
if(!isslimeperson(owner))
return
var/datum/species/jelly/slime/SS = H.dna.species
var/datum/mind/M
for(var/mob/living/L in SS.bodies)
if(L.mind && L.mind.active)
M = L.mind
if(!M)
if(!H.mind || !H.mind.active)
return
if(!isslimeperson(M.current))
return
switch(action)
if("swap")
var/mob/living/carbon/human/selected = locate(params["ref"])
if(!(selected in SS.bodies))
if(!can_swap(selected))
return
if(!selected || QDELETED(selected) || !isslimeperson(selected))
SS.bodies -= selected
return
if(M.current == selected)
return
if(selected.stat != CONSCIOUS)
return
swap_to_dupe(M, selected)
SStgui.close_uis(src)
swap_to_dupe(H.mind, selected)
/datum/action/innate/swap_body/proc/can_swap(mob/living/carbon/human/dupe)
var/mob/living/carbon/human/H = owner
if(!isslimeperson(H))
return FALSE
var/datum/species/jelly/slime/SS = H.dna.species
if(QDELETED(dupe)) //Is there a body?
SS.bodies -= dupe
return FALSE
if(!isslimeperson(dupe)) //Is it a slimeperson?
SS.bodies -= dupe
return FALSE
if(dupe.stat == DEAD) //Is it alive?
return FALSE
if(dupe.stat != CONSCIOUS) //Is it awake?
return FALSE
if(dupe.mind && dupe.mind.active) //Is it unoccupied?
return FALSE
if(!(dupe in SS.bodies)) //Do we actually own it?
return FALSE
return TRUE
/datum/action/innate/swap_body/proc/swap_to_dupe(datum/mind/M, mob/living/carbon/human/dupe)
if(!can_swap(dupe)) //sanity check
return
if(M.current.stat == CONSCIOUS)
M.current.visible_message("<span class='notice'>[M.current] \
stops moving and starts staring vacantly into space.</span>",
@@ -17,8 +17,8 @@
speedmod = 1
damage_overlay_type = ""//let's not show bloody wounds or burns over bones.
var/internal_fire = FALSE //If the bones themselves are burning clothes won't help you much
disliked_food = NONE
liked_food = NONE
disliked_food = FRUIT
liked_food = VEGETABLES
/datum/species/plasmaman/spec_life(mob/living/carbon/human/H)
var/datum/gas_mixture/environment = H.loc.return_air()
@@ -8,12 +8,10 @@
attack_sound = 'sound/weapons/slice.ogg'
miss_sound = 'sound/weapons/slashmiss.ogg'
burnmod = 1.25
heatmod = 1.55
heatmod = 1.5
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/plant
disliked_food = NONE
liked_food = NONE
toxic_food = NONE
disliked_food = MEAT | DAIRY
liked_food = VEGETABLES | FRUIT | GRAIN
/datum/species/pod/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
@@ -36,21 +34,18 @@
if(H.nutrition > NUTRITION_LEVEL_FULL)
H.nutrition = NUTRITION_LEVEL_FULL
if(light_amount > 0.2) //if there's enough light, heal
H.heal_overall_damage(0.75,0)
H.adjustOxyLoss(-0.5)
if(H.nutrition < NUTRITION_LEVEL_STARVING + 55)
H.adjustOxyLoss(5) //can eat to negate this unfortunately
H.adjustToxLoss(3)
H.heal_overall_damage(1,1)
H.adjustToxLoss(-1)
H.adjustOxyLoss(-1)
if(H.nutrition < NUTRITION_LEVEL_STARVING + 50)
H.take_overall_damage(2,0)
/datum/species/pod/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H)
if(chem.id == "plantbgone")
H.adjustToxLoss(5)
H.adjustToxLoss(3)
H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM)
H.confused = max(H.confused, 1)
return TRUE
return 1
/datum/species/pod/on_hit(obj/item/projectile/P, mob/living/carbon/human/H)
switch(P.type)
@@ -303,6 +303,8 @@
/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
if(tesla_shock && (flags_2 & TESLA_IGNORE_2))
return FALSE
if(has_trait(TRAIT_SHOCKIMMUNE))
return FALSE
if(shock_damage > 0)
if(!illusion)
adjustFireLoss(shock_damage)
+1 -1
View File
@@ -381,4 +381,4 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
if(.)
return .
. = ..()
. = ..()
@@ -552,6 +552,16 @@
can_be_pushed = FALSE
hat_offset = 3
/obj/item/robot_module/syndicate/rebuild_modules()
..()
var/mob/living/silicon/robot/Syndi = loc
Syndi.faction -= "silicon" //ai turrets
/obj/item/robot_module/syndicate/remove_module(obj/item/I, delete_after)
..()
var/mob/living/silicon/robot/Syndi = loc
Syndi.faction += "silicon" //ai is your bff now!
/obj/item/robot_module/syndicate_medical
name = "Syndicate Medical"
basic_modules = list(
@@ -41,6 +41,7 @@
/mob/living/silicon/Initialize()
. = ..()
GLOB.silicon_mobs += src
faction += "silicon"
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_to_hud(src)
diag_hud_set_status()
@@ -37,12 +37,28 @@
var/seeking = FALSE
var/can_repair_constructs = FALSE
var/can_repair_self = FALSE
var/runetype
/mob/living/simple_animal/hostile/construct/Initialize()
. = ..()
update_health_hud()
var/spellnum = 1
for(var/spell in construct_spells)
AddSpell(new spell(null))
var/the_spell = new spell(null)
AddSpell(the_spell)
var/obj/effect/proc_holder/spell/S = mob_spell_list[spellnum]
var/pos = 2+spellnum*31
if(construct_spells.len >= 4)
pos -= 31*(construct_spells.len - 4)
S.action.button.screen_loc = "6:[pos],4:-2"
S.action.button.moved = "6:[pos],4:-2"
spellnum++
if(runetype)
var/datum/action/innate/cult/create_rune/CR = new runetype(src)
CR.Grant(src)
var/pos = 2+spellnum*31
CR.button.screen_loc = "6:[pos],4:-2"
CR.button.moved = "6:[pos],4:-2"
/mob/living/simple_animal/hostile/construct/Login()
..()
@@ -104,22 +120,24 @@
desc = "A massive, armored construct built to spearhead attacks and soak up enemy fire."
icon_state = "behemoth"
icon_living = "behemoth"
maxHealth = 250
health = 250
maxHealth = 200
health = 200
response_harm = "harmlessly punches"
harm_intent_damage = 0
obj_damage = 90
melee_damage_lower = 30
melee_damage_upper = 30
attacktext = "smashes their armored gauntlet into"
speed = 3
speed = 2.5
environment_smash = ENVIRONMENT_SMASH_WALLS
attack_sound = 'sound/weapons/punch3.ogg'
status_flags = 0
mob_size = MOB_SIZE_LARGE
force_threshold = 11
construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall)
playstyle_string = "<span class='big bold'>You are a Juggernaut.</span><b> Though slow, your shell can withstand extreme punishment, \
construct_spells = list(/obj/effect/proc_holder/spell/targeted/forcewall/cult,
/obj/effect/proc_holder/spell/dumbfire/juggernaut)
runetype = /datum/action/innate/cult/create_rune/wall
playstyle_string = "<b>You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \
create shield walls, rip apart enemies and walls alike, and even deflect energy weapons.</b>"
/mob/living/simple_animal/hostile/construct/armored/hostile //actually hostile, will move around, hit things
@@ -164,15 +182,17 @@
desc = "A wicked, clawed shell constructed to assassinate enemies and sow chaos behind enemy lines."
icon_state = "floating"
icon_living = "floating"
maxHealth = 75
health = 75
maxHealth = 65
health = 65
melee_damage_lower = 25
melee_damage_upper = 25
retreat_distance = 2 //AI wraiths will move in and out of combat
attacktext = "slashes"
attack_sound = 'sound/weapons/bladeslice.ogg'
construct_spells = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift)
playstyle_string = "<span class='big bold'>You are a Wraith.</span><b> Though relatively fragile, you are fast, deadly, can phase through walls, and your attacks will lower the cooldown on phasing.</b>"
runetype = /datum/action/innate/cult/create_rune/tele
playstyle_string = "<b>You are a Wraith. Though relatively fragile, you are fast, deadly, can phase through walls, and your attacks will lower the cooldown on phasing.</b>"
var/attack_refund = 10 //1 second per attack
var/crit_refund = 50 //5 seconds when putting a target into critical
var/kill_refund = 250 //full refund on kills
@@ -226,7 +246,9 @@
/obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone,
/obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser,
/obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser)
playstyle_string = "<span class='big bold'>You are an Artificer.</span><b> You are incredibly weak and fragile, but you are able to construct fortifications, \
runetype = /datum/action/innate/cult/create_rune/revive
playstyle_string = "<b>You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, \
use magic missile, repair allied constructs, shades, and yourself (by clicking on them), \
<i>and, most important of all,</i> create new constructs by producing soulstones to capture souls, \
and shells to place those soulstones into.</b>"
@@ -299,9 +321,9 @@
attacktext = "butchers"
attack_sound = 'sound/weapons/bladeslice.ogg'
construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/area_conversion,
/obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall)
playstyle_string = "<span class='big bold'>You are a Harvester.</span><b> You are incapable of directly killing humans, but your attacks will remove their limbs: \
Bring those who still cling to this world of illusion back to the Geometer so they may know Truth. Your form and any you are pulling can pass through runed walls effortlessly.</b>"
/obj/effect/proc_holder/spell/targeted/forcewall/cult)
playstyle_string = "<B>You are a Harvester. You are incapable of directly killing humans, but your attacks will remove their limbs: \
Bring those who still cling to this world of illusion back to the Geometer so they may know Truth. Your form and any you are pulling can pass through runed walls effortlessly.</B>"
can_repair_constructs = TRUE
@@ -376,7 +398,7 @@
if(summon_objective.check_completion())
the_construct.master = C.cult_team.blood_target
if(!the_construct.master)
to_chat(the_construct, "<span class='cult italic'>You have no master to seek!</span>")
the_construct.seeking = FALSE
@@ -43,12 +43,13 @@
/mob/living/simple_animal/mouse/death(gibbed, toast)
if(!ckey)
..(1)
var/obj/item/reagent_containers/food/snacks/deadmouse/M = new(loc)
M.icon_state = icon_dead
M.name = name
if(toast)
M.add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY)
M.desc = "It's toast."
if(!gibbed)
var/obj/item/reagent_containers/food/snacks/deadmouse/M = new(loc)
M.icon_state = icon_dead
M.name = name
if(toast)
M.add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY)
M.desc = "It's toast."
qdel(src)
else
..(gibbed)
@@ -14,7 +14,7 @@ If it fails to warp to a target, it may summon up to 6 slaughterlings from the b
If it does not summon all 6 slaughterlings, it will instead charge at its target, dealing massive damage to anything it hits and spraying a stream of blood.
At half health, it will either charge three times or warp, then charge, instead of doing a single charge.
When Bubblegum dies, it leaves behind a chest that can contain three things:
When Bubblegum dies, it leaves behind a H.E.C.K. mining suit as well as a chest that can contain three things:
1. A bottle that, when activated, drives everyone nearby into a frenzy
2. A contract that marks for death the chosen target
3. A spellblade that can slice off limbs at range
@@ -25,7 +25,7 @@ Difficulty: Hard
/mob/living/simple_animal/hostile/megafauna/bubblegum
name = "bubblegum"
desc = "In what passes for a heirarchy among slaughter demons, this one is king."
desc = "In what passes for a hierarchy among slaughter demons, this one is king."
health = 2500
maxHealth = 2500
attacktext = "rends"
@@ -366,7 +366,7 @@
r_pocket = /obj/item/restraints/legcuffs/bola/cult
l_pocket = /obj/item/melee/cultblade/dagger
glasses = /obj/item/clothing/glasses/night/cultblind
backpack_contents = list(/obj/item/reagent_containers/food/drinks/bottle/unholywater = 1, /obj/item/device/cult_shift = 1, /obj/item/device/flashlight/flare/culttorch = 1, /obj/item/stack/sheet/runed_metal = 15)
backpack_contents = list(/obj/item/reagent_containers/glass/beaker/unholywater = 1, /obj/item/device/cult_shift = 1, /obj/item/device/flashlight/flare/culttorch = 1, /obj/item/stack/sheet/runed_metal = 15)
. = ..()
@@ -27,8 +27,6 @@
healable = 0
gender = NEUTER
nutrition = 700
see_in_dark = 8
verb_say = "blorbles"
@@ -93,6 +91,7 @@
create_reagents(100)
set_colour(new_colour)
. = ..()
nutrition = 700
/mob/living/simple_animal/slime/Destroy()
for (var/A in actions)
+27 -24
View File
@@ -68,32 +68,35 @@
return S.duration - world.time
return 0
/mob/living/proc/Sleeping(amount, updating = TRUE) //Can't go below remaining duration
var/datum/status_effect/incapacitating/sleeping/S = IsSleeping()
if(S)
S.duration = max(world.time + amount, S.duration)
else if(amount > 0)
S = apply_status_effect(STATUS_EFFECT_SLEEPING, amount, updating)
return S
/mob/living/proc/SetSleeping(amount, updating = TRUE) //Sets remaining duration
var/datum/status_effect/incapacitating/sleeping/S = IsSleeping()
if(amount <= 0)
/mob/living/proc/Sleeping(amount, updating = TRUE, ignore_sleepimmune = FALSE) //Can't go below remaining duration
if((!has_trait(TRAIT_SLEEPIMMUNE)) || ignore_sleepimmune)
var/datum/status_effect/incapacitating/sleeping/S = IsSleeping()
if(S)
qdel(S)
else if(S)
S.duration = world.time + amount
else
S = apply_status_effect(STATUS_EFFECT_SLEEPING, amount, updating)
return S
S.duration = max(world.time + amount, S.duration)
else if(amount > 0)
S = apply_status_effect(STATUS_EFFECT_SLEEPING, amount, updating)
return S
/mob/living/proc/AdjustSleeping(amount, updating = TRUE) //Adds to remaining duration
var/datum/status_effect/incapacitating/sleeping/S = IsSleeping()
if(S)
S.duration += amount
else if(amount > 0)
S = apply_status_effect(STATUS_EFFECT_SLEEPING, amount, updating)
return S
/mob/living/proc/SetSleeping(amount, updating = TRUE, ignore_sleepimmune = FALSE) //Sets remaining duration
if((!has_trait(TRAIT_SLEEPIMMUNE)) || ignore_sleepimmune)
var/datum/status_effect/incapacitating/sleeping/S = IsSleeping()
if(amount <= 0)
if(S)
qdel(S)
else if(S)
S.duration = world.time + amount
else
S = apply_status_effect(STATUS_EFFECT_SLEEPING, amount, updating)
return S
/mob/living/proc/AdjustSleeping(amount, updating = TRUE, ignore_sleepimmune = FALSE) //Adds to remaining duration
if((!has_trait(TRAIT_SLEEPIMMUNE)) || ignore_sleepimmune)
var/datum/status_effect/incapacitating/sleeping/S = IsSleeping()
if(S)
S.duration += amount
else if(amount > 0)
S = apply_status_effect(STATUS_EFFECT_SLEEPING, amount, updating)
return S
/////////////////////////////////// RESTING ////////////////////////////////////
-7
View File
@@ -69,10 +69,6 @@
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/paper)
assets.send(user)
if(istype(src, /obj/item/paper/talisman)) //Talismans cannot be read
if(!iscultist(user) && !user.stat)
to_chat(user, "<span class='danger'>There are indecipherable images scrawled on the paper in what looks to be... <i>blood?</i></span>")
return
if(in_range(user, src) || isobserver(user))
if(user.is_literate())
user << browse("<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[info]<HR>[stamps]</BODY></HTML>", "window=[name]")
@@ -297,9 +293,6 @@
else
to_chat(user, "<span class='notice'>You don't know how to read or write.</span>")
return
if(istype(src, /obj/item/paper/talisman/))
to_chat(user, "<span class='warning'>[P]'s ink fades away shortly after it is written.</span>")
return
else if(istype(P, /obj/item/stamp))
+1 -1
View File
@@ -77,7 +77,7 @@
set_security_level("delta")
SSshuttle.registerHostileEnvironment(src)
SSshuttle.lockdown = TRUE
sleep(1150)
sleep(850)
if(resolved == FALSE)
resolved = TRUE
sound_to_playing_players('sound/machines/alarm.ogg')
+10 -1
View File
@@ -12,6 +12,7 @@
/obj/item/ammo_casing/energy/chameleon
projectile_type = /obj/item/projectile/energy/chameleon
e_cost = 0
var/hitscan_mode = FALSE
var/list/projectile_vars = list()
/obj/item/ammo_casing/energy/chameleon/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
@@ -19,8 +20,10 @@
if(!BB)
newshot()
for(var/V in projectile_vars)
if(BB.vars[V])
if(BB.vars.Find(V))
BB.vars[V] = projectile_vars[V]
if(hitscan_mode)
BB.hitscan = TRUE
/obj/item/ammo_casing/energy/laser
projectile_type = /obj/item/projectile/beam/laser
@@ -69,10 +72,16 @@
projectile_type = /obj/item/projectile/beam/lasertag/bluetag
select_name = "bluetag"
/obj/item/ammo_casing/energy/laser/bluetag/hitscan
projectile_type = /obj/item/projectile/beam/lasertag/bluetag/hitscan
/obj/item/ammo_casing/energy/laser/redtag
projectile_type = /obj/item/projectile/beam/lasertag/redtag
select_name = "redtag"
/obj/item/ammo_casing/energy/laser/redtag/hitscan
projectile_type = /obj/item/projectile/beam/lasertag/redtag/hitscan
/obj/item/ammo_casing/energy/xray
projectile_type = /obj/item/projectile/beam/xray
e_cost = 50
+9 -74
View File
@@ -42,7 +42,7 @@
var/lastangle = 0
var/aiming_lastangle = 0
var/mob/current_user = null
var/list/obj/effect/projectile_beam/current_tracers
var/list/obj/effect/projectile/tracer/current_tracers
var/structure_piercing = 2 //Amount * 2. For some reason structures aren't respecting this unless you have it doubled. Probably with the objects in question's Bump() code instead of this but I'll deal with this later.
var/structure_bleed_coeff = 0.7
@@ -546,93 +546,28 @@
/obj/item/projectile/beam/beam_rifle/hitscan
icon_state = ""
var/tracer_type = /obj/effect/projectile_beam/tracer
var/list/beam_segments //assoc list of datum/point or datum/point/vector, start = end.
hitscan = TRUE
tracer_type = /obj/effect/projectile/tracer/tracer/beam_rifle
var/constant_tracer = FALSE
var/beam_index
/obj/item/projectile/beam/beam_rifle/hitscan/Destroy()
if(loc)
var/datum/point/pcache = trajectory.copy_to()
beam_segments[beam_index] = pcache
generate_tracers(constant_tracer)
return ..()
/obj/item/projectile/beam/beam_rifle/hitscan/Collide(atom/target)
var/datum/point/pcache = trajectory.copy_to()
. = ..()
if(. && !QDELETED(src)) //successful touch and not destroyed.
beam_segments[beam_index] = pcache
beam_index = pcache
beam_segments[beam_index] = null
/obj/item/projectile/beam/beam_rifle/hitscan/before_z_change(turf/oldloc, turf/newloc)
var/datum/point/pcache = trajectory.copy_to()
beam_segments[beam_index] = pcache
beam_index = RETURN_PRECISE_POINT(newloc)
beam_segments[beam_index] = null
return ..()
/obj/item/projectile/beam/beam_rifle/hitscan/proc/generate_tracers(highlander = FALSE, cleanup = TRUE)
/obj/item/projectile/beam/beam_rifle/hitscan/generate_hitscan_tracers(cleanup = TRUE, duration = 5, highlander)
set waitfor = FALSE
if(isnull(highlander))
highlander = constant_tracer
if(highlander && istype(gun))
QDEL_LIST(gun.current_tracers)
for(var/datum/point/p in beam_segments)
gun.current_tracers += generate_projectile_beam_between_points(p, beam_segments[p], tracer_type, color, 0)
gun.current_tracers += generate_tracer_between_points(p, beam_segments[p], tracer_type, color, 0)
else
for(var/datum/point/p in beam_segments)
generate_projectile_beam_between_points(p, beam_segments[p], tracer_type, color, 5)
generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration)
if(cleanup)
QDEL_LIST(beam_segments)
beam_segments = null
QDEL_NULL(beam_index)
/obj/item/projectile/beam/beam_rifle/hitscan/fire(setAngle, atom/direct_target) //oranges didn't let me make this a var the first time around so copypasta time
set waitfor = FALSE
var/turf/starting = get_turf(src)
trajectory = new(starting.x, starting.y, starting.z, 0, 0, setAngle? setAngle : Angle, 33)
if(!log_override && firer && original)
add_logs(firer, original, "fired at", src, " [get_area(src)]")
fired = TRUE
if(setAngle)
Angle = setAngle
var/safety = 0 //The code works fine, but... just in case...
var/turf/c2
beam_segments = list() //initialize segment list with the list for the first segment
beam_index = RETURN_PRECISE_POINT(src)
beam_segments[beam_index] = null //record start.
if(spread)
Angle += (rand() - 0.5) * spread
while(loc)
if(paused || QDELETED(src))
return
if(++safety > (range * 3)) //If it's looping for way, way too long...
qdel(src)
stack_trace("WARNING: [type] projectile encountered infinite recursion in [__FILE__]/[__LINE__]!")
return //Kill!
var/matrix/M = new
M.Turn(Angle)
transform = M
trajectory.increment()
var/turf/T = trajectory.return_turf()
if(T.z != loc.z)
before_z_change(loc, T)
trajectory_ignore_forcemove = TRUE
forceMove(T)
trajectory_ignore_forcemove = FALSE
else
step_towards(src, T)
animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW)
if(can_hit_target(original, permutated))
Collide(original)
Range()
c2 = get_turf(src)
if(istype(c2))
cached = c2
/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam
tracer_type = /obj/effect/projectile_beam/tracer/aiming
tracer_type = /obj/effect/projectile/tracer/tracer/aiming
name = "aiming beam"
hitsound = null
hitsound_wall = null
+10 -4
View File
@@ -116,19 +116,25 @@
icon_state = "bluetag"
desc = "A retro laser gun modified to fire harmless blue beams of light. Sound effects included!"
ammo_type = list(/obj/item/ammo_casing/energy/laser/bluetag)
clumsy_check = 0
item_flags = NONE
clumsy_check = FALSE
pin = /obj/item/device/firing_pin/tag/blue
ammo_x_offset = 2
selfcharge = 1
selfcharge = TRUE
/obj/item/gun/energy/laser/bluetag/hitscan
ammo_type = list(/obj/item/ammo_casing/energy/laser/bluetag/hitscan)
/obj/item/gun/energy/laser/redtag
name = "laser tag gun"
icon_state = "redtag"
desc = "A retro laser gun modified to fire harmless beams red of light. Sound effects included!"
ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag)
clumsy_check = 0
item_flags = NONE
clumsy_check = FALSE
pin = /obj/item/device/firing_pin/tag/red
ammo_x_offset = 2
selfcharge = 1
selfcharge = TRUE
/obj/item/gun/energy/laser/redtag/hitscan
ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag/hitscan)
+93 -14
View File
@@ -1,5 +1,6 @@
#define MOVES_HITSCAN -1 //Not actually hitscan but close as we get.
#define MOVES_HITSCAN -1 //Not actually hitscan but close as we get without actual hitscan.
#define MUZZLE_EFFECT_PIXEL_INCREMENT 17 //How many pixels to move the muzzle flash up so your character doesn't look like they're shitting out lasers.
/obj/item/projectile
name = "projectile"
@@ -37,6 +38,7 @@
var/speed = 0.8 //Amount of deciseconds it takes for projectile to travel
var/pixel_speed = 33 //pixels per move - DO NOT FUCK WITH THIS UNLESS YOU ABSOLUTELY KNOW WHAT YOU ARE DOING OR UNEXPECTED THINGS /WILL/ HAPPEN!
var/Angle = 0
var/original_angle = 0 //Angle at firing
var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle
var/spread = 0 //amount (in degrees) of projectile spread
animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy
@@ -46,6 +48,15 @@
var/colliding = FALSE //pause processing..
//Hitscan
var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
var/list/beam_segments //assoc list of datum/point or datum/point/vector, start = end. Used for hitscan effect generation.
var/datum/point/beam_index
var/turf/hitscan_last //last turf touched during hitscanning.
var/tracer_type
var/muzzle_type
var/impact_type
var/ignore_source_check = FALSE
var/damage = 10
@@ -53,7 +64,7 @@
var/nodamage = 0 //Determines if the projectile will skip any damage inflictions
var/flag = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb
var/projectile_type = /obj/item/projectile
var/range = 50 //This will de-increment every step. When 0, it will delete the projectile.
var/range = 50 //This will de-increment every step. When 0, it will deletze the projectile.
var/is_reflectable = FALSE // Can it be reflected or not?
//Effects
var/stun = 0
@@ -111,7 +122,7 @@
if(!nodamage && (damage_type == BRUTE || damage_type == BURN) && iswallturf(target_loca) && prob(75))
var/turf/closed/wall/W = target_loca
if(impact_effect_type)
if(impact_effect_type && !hitscan)
new impact_effect_type(target_loca, hitx, hity)
W.add_dent(WALL_DENT_SHOT, hitx, hity)
@@ -119,7 +130,7 @@
return 0
if(!isliving(target))
if(impact_effect_type)
if(impact_effect_type && !hitscan)
new impact_effect_type(target_loca, hitx, hity)
return 0
@@ -136,7 +147,7 @@
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir)
if(prob(33))
L.add_splatter_floor(target_loca)
else if(impact_effect_type)
else if(impact_effect_type && !hitscan)
new impact_effect_type(target_loca, hitx, hity)
var/organ_hit_text = ""
@@ -173,14 +184,22 @@
/obj/item/projectile/proc/on_ricochet(atom/A)
return
/obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache)
beam_segments[beam_index] = pcache
beam_index = pcache
beam_segments[beam_index] = null
/obj/item/projectile/Collide(atom/A)
colliding = TRUE
var/datum/point/pcache = trajectory.copy_to()
if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max)
ricochets++
if(A.handle_ricochet(src))
on_ricochet(A)
ignore_source_check = TRUE
range = initial(range)
if(hitscan)
store_hitscan_collision(pcache)
return TRUE
if(firer && !ignore_source_check)
if(A == firer || (A == firer.loc && ismecha(A))) //cannot shoot yourself or your mech
@@ -275,7 +294,9 @@
return getline(current, ending)
/obj/item/projectile/proc/before_z_change(turf/oldloc, turf/newloc)
return
var/datum/point/pcache = trajectory.copy_to()
if(hitscan)
store_hitscan_collision(pcache)
/obj/item/projectile/Process_Spacemove(var/movement_dir = 0)
return TRUE //Bullets don't drift in space
@@ -324,13 +345,17 @@
return
var/turf/target = locate(CLAMP(starting + xo, 1, world.maxx), CLAMP(starting + yo, 1, world.maxy), starting.z)
setAngle(Get_Angle(src, target))
original_angle = Angle
if(!nondirectional_sprite)
var/matrix/M = new
M.Turn(Angle)
transform = M
forceMove(starting)
trajectory = new(starting.x, starting.y, starting.z, 0, 0, Angle, pixel_speed)
last_projectile_move = world.time
fired = TRUE
if(hitscan)
process_hitscan()
if(!isprocessing)
START_PROCESSING(SSprojectiles, src)
pixel_move(1) //move it now!
@@ -350,11 +375,29 @@
if(trajectory && !trajectory_ignore_forcemove && isturf(target))
trajectory.initialize_location(target.x, target.y, target.z, 0, 0)
/obj/item/projectile/proc/pixel_move(moves, trajectory_multiplier = 1)
/obj/item/projectile/proc/record_hitscan_start(datum/point/pcache)
beam_segments = list() //initialize segment list with the list for the first segment
beam_index = pcache
beam_segments[beam_index] = null //record start.
/obj/item/projectile/proc/process_hitscan()
var/safety = range * 3
record_hitscan_start(RETURN_POINT_VECTOR_INCREMENT(src, Angle, MUZZLE_EFFECT_PIXEL_INCREMENT, 1))
while(loc && !QDELETED(src))
if(paused)
stoplag(1)
continue
if(safety-- <= 0)
qdel(src)
stack_trace("WARNING: [type] projectile encountered infinite recursion during hitscanning in [__FILE__]/[__LINE__]!")
return //Kill!
pixel_move(1, 1, TRUE)
/obj/item/projectile/proc/pixel_move(moves, trajectory_multiplier = 1, hitscanning = FALSE)
if(!loc || !trajectory)
return
last_projectile_move = world.time
if(!nondirectional_sprite)
if(!nondirectional_sprite && !hitscanning)
var/matrix/M = new
M.Turn(Angle)
transform = M
@@ -365,14 +408,18 @@
trajectory_ignore_forcemove = TRUE
forceMove(T)
trajectory_ignore_forcemove = FALSE
pixel_x = trajectory.return_px()
pixel_y = trajectory.return_py()
if(!hitscanning)
pixel_x = trajectory.return_px()
pixel_y = trajectory.return_py()
else
step_towards(src, T)
pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier
pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier
animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW)
if(!hitscanning)
pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier
pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier
if(!hitscanning)
animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW)
if(isturf(loc))
hitscan_last = loc
if(can_hit_target(original, permutated))
Collide(original)
Range()
@@ -446,8 +493,40 @@
Collide(AM)
/obj/item/projectile/Destroy()
if(hitscan)
if(loc)
var/datum/point/pcache = trajectory.copy_to()
beam_segments[beam_index] = pcache
generate_hitscan_tracers()
STOP_PROCESSING(SSprojectiles, src)
return ..()
/obj/item/projectile/proc/generate_hitscan_tracers(cleanup = TRUE, duration = 3)
if(!length(beam_segments))
return
if(tracer_type)
for(var/datum/point/p in beam_segments)
generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration)
if(muzzle_type && duration > 0)
var/datum/point/p = beam_segments[1]
var/atom/movable/thing = new muzzle_type
p.move_atom_to_src(thing)
var/matrix/M = new
M.Turn(original_angle)
thing.transform = M
QDEL_IN(thing, duration)
if(impact_type && duration > 0)
var/datum/point/p = beam_segments[beam_segments[beam_segments.len]]
var/atom/movable/thing = new impact_type
p.move_atom_to_src(thing)
var/matrix/M = new
M.Turn(Angle)
thing.transform = M
QDEL_IN(thing, duration)
if(cleanup)
QDEL_LIST(beam_segments)
beam_segments = null
QDEL_NULL(beam_index)
/obj/item/projectile/experience_pressure_difference()
return
@@ -16,11 +16,17 @@
is_reflectable = TRUE
/obj/item/projectile/beam/laser
tracer_type = /obj/effect/projectile/tracer/laser
muzzle_type = /obj/effect/projectile/muzzle/laser
impact_type = /obj/effect/projectile/impact/laser
/obj/item/projectile/beam/laser/heavylaser
name = "heavy laser"
icon_state = "heavylaser"
damage = 40
tracer_type = /obj/effect/projectile/tracer/heavy_laser
muzzle_type = /obj/effect/projectile/muzzle/heavy_laser
impact_type = /obj/effect/projectile/impact/heavy_laser
/obj/item/projectile/beam/laser/on_hit(atom/target, blocked = FALSE)
. = ..()
@@ -54,6 +60,9 @@
impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
light_color = LIGHT_COLOR_GREEN
tracer_type = /obj/effect/projectile/tracer/xray
muzzle_type = /obj/effect/projectile/muzzle/xray
impact_type = /obj/effect/projectile/impact/xray
/obj/item/projectile/beam/disabler
name = "disabler beam"
@@ -65,6 +74,9 @@
eyeblur = 0
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
light_color = LIGHT_COLOR_BLUE
tracer_type = /obj/effect/projectile/tracer/disabler
muzzle_type = /obj/effect/projectile/muzzle/disabler
impact_type = /obj/effect/projectile/impact/disabler
/obj/item/projectile/beam/pulse
name = "pulse"
@@ -72,6 +84,9 @@
damage = 50
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
light_color = LIGHT_COLOR_BLUE
tracer_type = /obj/effect/projectile/tracer/pulse
muzzle_type = /obj/effect/projectile/muzzle/pulse
impact_type = /obj/effect/projectile/impact/pulse
/obj/item/projectile/beam/pulse/on_hit(atom/target, blocked = FALSE)
. = ..()
@@ -126,10 +141,22 @@
suit_types = list(/obj/item/clothing/suit/bluetag)
impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser
light_color = LIGHT_COLOR_RED
tracer_type = /obj/effect/projectile/tracer/laser
muzzle_type = /obj/effect/projectile/muzzle/laser
impact_type = /obj/effect/projectile/impact/laser
/obj/item/projectile/beam/lasertag/redtag/hitscan
hitscan = TRUE
/obj/item/projectile/beam/lasertag/bluetag
icon_state = "bluelaser"
suit_types = list(/obj/item/clothing/suit/redtag)
tracer_type = /obj/effect/projectile/tracer/laser/blue
muzzle_type = /obj/effect/projectile/muzzle/laser/blue
impact_type = /obj/effect/projectile/impact/laser/blue
/obj/item/projectile/beam/lasertag/bluetag/hitscan
hitscan = TRUE
/obj/item/projectile/beam/instakill
name = "instagib laser"
@@ -19,6 +19,9 @@
jitter = 20
hitsound = 'sound/weapons/taserhit.ogg'
range = 7
tracer_type = /obj/effect/projectile/tracer/stun
muzzle_type = /obj/effect/projectile/muzzle/stun
impact_type = /obj/effect/projectile/impact/stun
/obj/item/projectile/energy/electrode/on_hit(atom/target, blocked = FALSE)
. = ..()
@@ -184,6 +184,9 @@
var/pressure_decrease_active = FALSE
var/pressure_decrease = 0.25
var/mine_range = 3 //mines this many additional tiles of rock
tracer_type = /obj/effect/projectile/tracer/plasma_cutter
muzzle_type = /obj/effect/projectile/muzzle/plasma_cutter
impact_type = /obj/effect/projectile/impact/plasma_cutter
/obj/item/projectile/plasma/Initialize()
. = ..()
@@ -245,21 +245,31 @@
/datum/reagent/drug/bath_salts
name = "Bath Salts"
id = "bath_salts"
description = "Makes you nearly impervious to stuns and grants a stamina regeneration buff, but you will be a nearly uncontrollable tramp-bearded raving lunatic."
description = "Makes you impervious to stuns and grants a stamina regeneration buff, but you will be a nearly uncontrollable tramp-bearded raving lunatic."
reagent_state = LIQUID
color = "#FAFAFA"
overdose_threshold = 20
addiction_threshold = 10
taste_description = "salt" // because they're bathsalts?
/datum/reagent/drug/bath_salts/on_mob_add(mob/M)
..()
if(isliving(M))
var/mob/living/L = M
L.add_trait(TRAIT_STUNIMMUNE, id)
L.add_trait(TRAIT_SLEEPIMMUNE, id)
/datum/reagent/drug/bath_salts/on_mob_delete(mob/M)
if(isliving(M))
var/mob/living/L = M
L.remove_trait(TRAIT_STUNIMMUNE, id)
L.remove_trait(TRAIT_SLEEPIMMUNE, id)
..()
/datum/reagent/drug/bath_salts/on_mob_life(mob/living/M)
var/high_message = pick("You feel amped up.", "You feel ready.", "You feel like you can push it to the limit.")
if(prob(5))
to_chat(M, "<span class='notice'>[high_message]</span>")
M.AdjustStun(-60, 0)
M.AdjustKnockdown(-60, 0)
M.AdjustUnconscious(-60, 0)
M.adjustStaminaLoss(-5, 0)
M.adjustBrainLoss(0.5)
M.adjustToxLoss(0.1, 0)
@@ -333,7 +343,7 @@
/datum/reagent/drug/aranesp
name = "Aranesp"
id = "aranesp"
description = "Amps you up and gets you going, fixes all stamina damage you might have but can cause toxin and oxygen damage.."
description = "Amps you up and gets you going, fixes all stamina damage you might have but can cause toxin and oxygen damage."
reagent_state = LIQUID
color = "#78FFF0"
@@ -1205,6 +1205,22 @@
overdose_threshold = 20
addiction_threshold = 5
/datum/reagent/medicine/ketrazine/on_mob_add(mob/M)
..()
if(isliving(M))
var/mob/living/L = M
L.add_trait(TRAIT_SLEEPIMMUNE, id)
L.add_trait(TRAIT_IGNORESLOWDOWN, id)
L.add_trait(TRAIT_GOTTAGOFAST, id)
/datum/reagent/medicine/ketrazine/on_mob_delete(mob/M)
if(isliving(M))
var/mob/living/L = M
L.remove_trait(TRAIT_SLEEPIMMUNE, id)
L.remove_trait(TRAIT_IGNORESLOWDOWN, id)
L.remove_trait(TRAIT_GOTTAGOFAST, id)
..()
/datum/reagent/medicine/ketrazine/on_mob_life(mob/living/M)
M.adjustToxLoss(-3*REM, 0)
M.adjustBruteLoss(-5*REM, 0)
@@ -200,6 +200,11 @@
data = 1
data++
M.jitteriness = min(M.jitteriness+4,10)
if(iscultist(M))
for(var/datum/action/innate/cult/blood_magic/BM in M.actions)
to_chat(M, "<span class='cultlarge'>Your blood rites falter as holy water scours your body!</span>")
for(var/datum/action/innate/cult/blood_spell/BS in BM.spells)
qdel(BS)
if(data >= 30) // 12 units, 54 seconds @ metabolism 0.4 units & tick rate 1.8 sec
if(!M.stuttering)
M.stuttering = 1
@@ -216,7 +221,7 @@
"You can't save him. Nothing can save him now", "It seems that Nar-Sie will triumph after all")].</span>")
if("emote")
M.visible_message("<span class='warning'>[M] [pick("whimpers quietly", "shivers as though cold", "glances around in paranoia")].</span>")
if(data >= 75) // 30 units, 135 seconds
if(data >= 60) // 30 units, 135 seconds
if(iscultist(M) || is_servant_of_ratvar(M))
if(iscultist(M))
SSticker.mode.remove_cultist(M.mind, FALSE, TRUE)
@@ -245,7 +250,7 @@
/datum/reagent/fuel/unholywater/reaction_mob(mob/living/M, method=TOUCH, reac_volume)
if(method == TOUCH || method == VAPOR)
M.reagents.add_reagent("unholywater", (reac_volume/4))
M.reagents.add_reagent(id,reac_volume/4)
return
return ..()
@@ -255,17 +260,20 @@
M.AdjustUnconscious(-20, 0)
M.AdjustStun(-40, 0)
M.AdjustKnockdown(-40, 0)
M.adjustStaminaLoss(-10, 0)
M.adjustToxLoss(-2, 0)
M.adjustOxyLoss(-2, 0)
M.adjustBruteLoss(-2, 0)
M.adjustFireLoss(-2, 0)
else
if(ishuman(M) && M.blood_volume < BLOOD_VOLUME_NORMAL)
M.blood_volume += 3
else // Will deal about 90 damage when 50 units are thrown
M.adjustBrainLoss(3, 150)
M.adjustToxLoss(1, 0)
M.adjustToxLoss(2, 0)
M.adjustFireLoss(2, 0)
M.adjustOxyLoss(2, 0)
M.adjustBruteLoss(2, 0)
holder.remove_reagent(src.id, 1)
holder.remove_reagent(id, 1)
. = 1
/datum/reagent/hellwater //if someone has this in their system they've really pissed off an eldrich god
@@ -1763,3 +1771,32 @@
var/mob/living/L = M
L.remove_trait(TRAIT_PACIFISM, id)
..()
/datum/reagent/pax/borg
name = "synth-pax"
id = "synthpax"
description = "A colorless liquid that suppresses violence on the subjects. Cheaper to synthetize, but wears out faster than normal Pax."
metabolization_rate = 1.5 * REAGENTS_METABOLISM
/datum/reagent/bz_metabolites
name = "BZ metabolites"
id = "bz_metabolites"
description = "A harmless metabolite of BZ gas"
color = "#FAFF00"
taste_description = "acrid cinnamon"
metabolization_rate = 0.2 * REAGENTS_METABOLISM
/datum/reagent/bz_metabolites/on_mob_add(mob/living/L)
..()
L.add_trait(CHANGELING_HIVEMIND_MUTE, id)
/datum/reagent/bz_metabolites/on_mob_delete(mob/living/L)
..()
L.remove_trait(CHANGELING_HIVEMIND_MUTE, id)
/datum/reagent/bz_metabolites/on_mob_life(mob/living/L)
if(L.mind)
var/datum/antagonist/changeling/changeling = L.mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling)
changeling.chem_charges = max(changeling.chem_charges-2, 0)
return ..()
@@ -194,7 +194,7 @@
required_other = 1
/datum/chemical_reaction/slime/slimestabilizer/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/stabilizer(get_turf(holder.my_atom))
new /obj/item/slimepotion/slime/stabilizer(get_turf(holder.my_atom))
..()
/datum/chemical_reaction/slime/slimefoam
@@ -326,7 +326,7 @@
required_other = 1
/datum/chemical_reaction/slime/slimepsteroid/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/steroid(get_turf(holder.my_atom))
new /obj/item/slimepotion/slime/steroid(get_turf(holder.my_atom))
..()
/datum/chemical_reaction/slime/slimeregen
@@ -358,7 +358,7 @@
required_other = 1
/datum/chemical_reaction/slime/slimemutator/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/mutator(get_turf(holder.my_atom))
new /obj/item/slimepotion/slime/mutator(get_turf(holder.my_atom))
..()
/datum/chemical_reaction/slime/slimebloodlust
@@ -394,7 +394,7 @@
required_other = 1
/datum/chemical_reaction/slime/docility/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/docility(get_turf(holder.my_atom))
new /obj/item/slimepotion/slime/docility(get_turf(holder.my_atom))
..()
/datum/chemical_reaction/slime/gender
@@ -465,7 +465,7 @@
required_other = 1
/datum/chemical_reaction/slime/slimepotion2/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/sentience(get_turf(holder.my_atom))
new /obj/item/slimepotion/slime/sentience(get_turf(holder.my_atom))
..()
//Adamantine
@@ -2,7 +2,7 @@
name = "blood pack"
desc = "Contains blood used for transfusion. Must be attached to an IV drip."
icon = 'icons/obj/bloodpack.dmi'
icon_state = "empty"
icon_state = "bloodpack"
volume = 200
var/blood_type = null
var/labelled = 0
@@ -31,14 +31,14 @@
name = "blood pack"
/obj/item/reagent_containers/blood/update_icon()
var/percent = round((reagents.total_volume / volume) * 100)
switch(percent)
if(0 to 9)
icon_state = "empty"
if(10 to 50)
icon_state = "half"
if(51 to INFINITY)
icon_state = "full"
cut_overlays()
var/v = min(round(reagents.total_volume / volume * 10), 10)
if(v > 0)
var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "bloodpack1")
filling.icon_state = "bloodpack[v]"
filling.color = mix_color_from_reagents(reagents.reagent_list)
add_overlay(filling)
/obj/item/reagent_containers/blood/random
icon_state = "random_bloodpack"
@@ -225,13 +225,13 @@ Borg Shaker
/obj/item/reagent_containers/borghypo/peace
name = "Peace Hypospray"
reagent_ids = list("dizzysolution","tiresolution","pax")
reagent_ids = list("dizzysolution","tiresolution","synthpax")
accepts_reagent_upgrades = FALSE
/obj/item/reagent_containers/borghypo/peace/hacked
desc = "Everything's peaceful in death!"
icon_state = "borghypo_s"
reagent_ids = list("dizzysolution","tiresolution","pax","tirizene","sulfonal","sodium_thiopental","cyanide","neurotoxin2")
reagent_ids = list("dizzysolution","tiresolution","synthpax","tirizene","sulfonal","sodium_thiopental","cyanide","neurotoxin2")
accepts_reagent_upgrades = FALSE
/obj/item/reagent_containers/borghypo/epi
+2 -2
View File
@@ -19,14 +19,14 @@ The currently supporting non-reagent materials. All material amounts are set as
Don't add new keyword/IDs if they are made from an existing one (such as rods which are made from metal). Only add raw materials.
Design Guidlines
Design Guidelines
- When adding new designs, check rdreadme.dm to see what kind of things have already been made and where new stuff is needed.
- A single sheet of anything is 2000 units of material. Materials besides metal/glass require help from other jobs (mining for
other types of metals and chemistry for reagents).
- Add the AUTOLATHE tag to
*/
//DESIGNS ARE GLOBAL. DO NOT CREATE OR DESTROY THEM AT RUNTIME OUTSIDE OF INIT, JUST REFERENCE THEM TO WHATEVER YOU'RE DOING!
//DESIGNS ARE GLOBAL. DO NOT CREATE OR DESTROY THEM AT RUNTIME OUTSIDE OF INIT, JUST REFERENCE THEM TO WHATEVER YOU'RE DOING! //why are you yelling?
/datum/design //Datum for object designs, used in construction
var/name = "Name" //Name of the created object.
@@ -104,6 +104,16 @@
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_ALL
/datum/design/defibrillator_mount
name = "Defibrillator Wall Mount"
desc = "An all-in-one mounted frame for holding defibrillators, complete with ID-locked clamps and recharging cables."
id = "defibmount"
build_type = PROTOLATHE
materials = list(MAT_METAL = 2000, MAT_GLASS = 1000)
build_path = /obj/item/wallframe/defib_mount
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/alienscalpel
name = "Alien Scalpel"
desc = "An advanced scalpel obtained through Abductor technology."
+1 -1
View File
@@ -17,7 +17,7 @@
display_name = "Biological Technology"
description = "What makes us tick." //the MC, silly!
prereq_ids = list("base")
design_ids = list("chem_heater", "chem_master", "chem_dispenser", "sleeper", "pandemic")
design_ids = list("chem_heater", "chem_master", "chem_dispenser", "sleeper", "pandemic", "defibmount")
research_cost = 2500
export_price = 5000
@@ -26,8 +26,11 @@
var/datum/action/innate/slime_pick_up/slime_up_action = new
var/datum/action/innate/feed_slime/feed_slime_action = new
var/datum/action/innate/monkey_recycle/monkey_recycle_action = new
var/datum/action/innate/slime_scan/scan_action = new
var/datum/action/innate/feed_potion/potion_action = new
var/list/stored_slimes = list()
var/obj/item/slimepotion/slime/current_potion
var/max_slimes = 5
var/monkeys = 0
@@ -66,6 +69,16 @@
monkey_recycle_action.Grant(user)
actions += monkey_recycle_action
if(scan_action)
scan_action.target = src
scan_action.Grant(user)
actions += scan_action
if(potion_action)
potion_action.target = src
potion_action.Grant(user)
actions += potion_action
/obj/machinery/computer/camera_advanced/xenobio/attackby(obj/item/O, mob/user, params)
if(istype(O, /obj/item/reagent_containers/food/snacks/monkeycube))
monkeys++
@@ -83,6 +96,16 @@
if (loaded)
to_chat(user, "<span class='notice'>You fill [src] with the monkey cubes stored in [O]. [src] now has [monkeys] monkey cubes stored.</span>")
return
else if(istype(O, /obj/item/slimepotion/slime))
var/replaced = FALSE
if(user && !user.transferItemToLoc(O, src))
return
if(!QDELETED(current_potion))
current_potion.forceMove(drop_location())
replaced = TRUE
current_potion = O
to_chat(user, "<span class='notice'>You load [O] in the console's potion slot[replaced ? ", replacing the one that was there before" : ""].</span>")
return
..()
/datum/action/innate/slime_place
@@ -173,3 +196,44 @@
qdel(M)
else
to_chat(owner, "<span class='notice'>Target is not near a camera. Cannot proceed.</span>")
/datum/action/innate/slime_scan
name = "Scan Slime"
icon_icon = 'icons/mob/actions/actions_silicon.dmi'
button_icon_state = "slime_scan"
/datum/action/innate/slime_scan/Activate()
if(!target || !isliving(owner))
return
var/mob/living/C = owner
var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control
if(GLOB.cameranet.checkTurfVis(remote_eye.loc))
for(var/mob/living/simple_animal/slime/S in remote_eye.loc)
slime_scan(S, C)
else
to_chat(owner, "<span class='notice'>Target is not near a camera. Cannot proceed.</span>")
/datum/action/innate/feed_potion
name = "Apply Potion"
icon_icon = 'icons/mob/actions/actions_silicon.dmi'
button_icon_state = "slime_potion"
/datum/action/innate/feed_potion/Activate()
if(!target || !isliving(owner))
return
var/mob/living/C = owner
var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control
var/obj/machinery/computer/camera_advanced/xenobio/X = target
if(QDELETED(X.current_potion))
to_chat(owner, "<span class='notice'>No potion loaded.</span>")
return
if(GLOB.cameranet.checkTurfVis(remote_eye.loc))
for(var/mob/living/simple_animal/slime/S in remote_eye.loc)
X.current_potion.attack(S, C)
break
else
to_chat(owner, "<span class='notice'>Target is not near a camera. Cannot proceed.</span>")
@@ -327,7 +327,7 @@
/obj/item/slime_extract/lightpink/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type)
switch(activation_type)
if(SLIME_ACTIVATE_MINOR)
var/obj/item/slimepotion/docility/O = new(null, 1)
var/obj/item/slimepotion/slime/docility/O = new(null, 1)
if(!user.put_in_active_hand(O))
O.forceMove(user.drop_location())
playsound(user, 'sound/effects/splat.ogg', 50, 1)
@@ -335,7 +335,7 @@
return 150
if(SLIME_ACTIVATE_MAJOR)
var/obj/item/slimepotion/sentience/O = new(null, 1)
var/obj/item/slimepotion/slime/sentience/O = new(null, 1)
if(!user.put_in_active_hand(O))
O.forceMove(user.drop_location())
playsound(user, 'sound/effects/splat.ogg', 50, 1)
@@ -539,19 +539,19 @@
to_chat(user, "<span class='notice'>You cannot transfer [src] to [target]! It appears the potion must be given directly to a slime to absorb.</span>" )
return
/obj/item/slimepotion/docility
/obj/item/slimepotion/slime/docility
name = "docility potion"
desc = "A potent chemical mix that nullifies a slime's hunger, causing it to become docile and tame."
icon = 'icons/obj/chemical.dmi'
icon_state = "potsilver"
/obj/item/slimepotion/docility/attack(mob/living/simple_animal/slime/M, mob/user)
/obj/item/slimepotion/slime/docility/attack(mob/living/simple_animal/slime/M, mob/user)
if(!isslime(M))
to_chat(user, "<span class='warning'>The potion only works on slimes!</span>")
return ..()
if(M.stat)
to_chat(user, "<span class='warning'>The slime is dead!</span>")
return ..()
return
M.docile = 1
M.nutrition = 700
@@ -565,33 +565,31 @@
M.real_name = newname
qdel(src)
/obj/item/slimepotion/sentience
/obj/item/slimepotion/slime/sentience
name = "intelligence potion"
desc = "A miraculous chemical mix that grants human like intelligence to living beings."
icon = 'icons/obj/chemical.dmi'
icon_state = "potpink"
var/list/not_interested = list()
var/being_used = 0
var/being_used = FALSE
var/sentience_type = SENTIENCE_ORGANIC
/obj/item/slimepotion/sentience/afterattack(mob/living/M, mob/user)
if(being_used || !ismob(M) || !user.Adjacent(M))
/obj/item/slimepotion/slime/sentience/attack(mob/living/M, mob/user)
if(being_used || !ismob(M))
return
if(!isanimal(M) || M.ckey) //only works on animals that aren't player controlled
to_chat(user, "<span class='warning'>[M] is already too intelligent for this to work!</span>")
return ..()
return
if(M.stat)
to_chat(user, "<span class='warning'>[M] is dead!</span>")
return ..()
return
var/mob/living/simple_animal/SM = M
if(SM.sentience_type != sentience_type)
to_chat(user, "<span class='warning'>[src] won't work on [SM].</span>")
return ..()
return
to_chat(user, "<span class='notice'>You offer [src] to [SM]...</span>")
being_used = 1
being_used = TRUE
var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]?", ROLE_ALIEN, null, ROLE_ALIEN, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm
var/mob/dead/observer/theghost = null
@@ -608,17 +606,17 @@
qdel(src)
else
to_chat(user, "<span class='notice'>[SM] looks interested for a moment, but then looks back down. Maybe you should try again later.</span>")
being_used = 0
being_used = FALSE
..()
/obj/item/slimepotion/sentience/proc/after_success(mob/living/user, mob/living/simple_animal/SM)
/obj/item/slimepotion/slime/sentience/proc/after_success(mob/living/user, mob/living/simple_animal/SM)
return
/obj/item/slimepotion/sentience/nuclear
/obj/item/slimepotion/slime/sentience/nuclear
name = "syndicate intelligence potion"
desc = "A miraculous chemical mix that grants human like intelligence to living beings. It has been modified with Syndicate technology to also grant an internal radio implant to the target and authenticate with identification systems."
/obj/item/slimepotion/sentience/nuclear/after_success(mob/living/user, mob/living/simple_animal/SM)
/obj/item/slimepotion/slime/sentience/nuclear/after_success(mob/living/user, mob/living/simple_animal/SM)
var/obj/item/implant/radio/imp = new(src)
imp.implant(SM, user)
@@ -667,25 +665,25 @@
SM.name = "[SM.name] as [user.real_name]"
qdel(src)
/obj/item/slimepotion/steroid
/obj/item/slimepotion/slime/steroid
name = "slime steroid"
desc = "A potent chemical mix that will cause a baby slime to generate more extract."
icon = 'icons/obj/chemical.dmi'
icon_state = "potred"
/obj/item/slimepotion/steroid/attack(mob/living/simple_animal/slime/M, mob/user)
/obj/item/slimepotion/slime/steroid/attack(mob/living/simple_animal/slime/M, mob/user)
if(!isslime(M))//If target is not a slime.
to_chat(user, "<span class='warning'>The steroid only works on baby slimes!</span>")
return ..()
if(M.is_adult) //Can't steroidify adults
to_chat(user, "<span class='warning'>Only baby slimes can use the steroid!</span>")
return ..()
return
if(M.stat)
to_chat(user, "<span class='warning'>The slime is dead!</span>")
return ..()
return
if(M.cores >= 5)
to_chat(user, "<span class='warning'>The slime already has the maximum amount of extract!</span>")
return ..()
return
to_chat(user, "<span class='notice'>You feed the slime the steroid. It will now produce one more extract.</span>")
M.cores++
@@ -697,46 +695,46 @@
icon = 'icons/obj/chemical.dmi'
icon_state = "potpurple"
/obj/item/slimepotion/stabilizer
/obj/item/slimepotion/slime/stabilizer
name = "slime stabilizer"
desc = "A potent chemical mix that will reduce the chance of a slime mutating."
icon = 'icons/obj/chemical.dmi'
icon_state = "potcyan"
/obj/item/slimepotion/stabilizer/attack(mob/living/simple_animal/slime/M, mob/user)
/obj/item/slimepotion/slime/stabilizer/attack(mob/living/simple_animal/slime/M, mob/user)
if(!isslime(M))
to_chat(user, "<span class='warning'>The stabilizer only works on slimes!</span>")
return ..()
if(M.stat)
to_chat(user, "<span class='warning'>The slime is dead!</span>")
return ..()
return
if(M.mutation_chance == 0)
to_chat(user, "<span class='warning'>The slime already has no chance of mutating!</span>")
return ..()
return
to_chat(user, "<span class='notice'>You feed the slime the stabilizer. It is now less likely to mutate.</span>")
M.mutation_chance = CLAMP(M.mutation_chance-15,0,100)
qdel(src)
/obj/item/slimepotion/mutator
/obj/item/slimepotion/slime/mutator
name = "slime mutator"
desc = "A potent chemical mix that will increase the chance of a slime mutating."
icon = 'icons/obj/chemical.dmi'
icon_state = "potgreen"
/obj/item/slimepotion/mutator/attack(mob/living/simple_animal/slime/M, mob/user)
/obj/item/slimepotion/slime/mutator/attack(mob/living/simple_animal/slime/M, mob/user)
if(!isslime(M))
to_chat(user, "<span class='warning'>The mutator only works on slimes!</span>")
return ..()
if(M.stat)
to_chat(user, "<span class='warning'>The slime is dead!</span>")
return ..()
return
if(M.mutator_used)
to_chat(user, "<span class='warning'>This slime has already consumed a mutator, any more would be far too unstable!</span>")
return ..()
return
if(M.mutation_chance == 100)
to_chat(user, "<span class='warning'>The slime is already guaranteed to mutate!</span>")
return ..()
return
to_chat(user, "<span class='notice'>You feed the slime the mutator. It is now more likely to mutate.</span>")
M.mutation_chance = CLAMP(M.mutation_chance+12,0,100)
+1 -1
View File
@@ -104,7 +104,6 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
pass_flags = PASSTABLE
density = FALSE
opacity = 0
base_action = /datum/action/spell_action/spell
var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit?
@@ -151,6 +150,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
action_icon = 'icons/mob/actions/actions_spells.dmi'
action_icon_state = "spell_default"
action_background_icon_state = "bg_spell"
base_action = /datum/action/spell_action/spell
/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0,mob/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell
@@ -82,7 +82,7 @@
desc = "This spell reaches into Nar-Sie's realm, summoning one of the legendary fragments across time and space."
school = "conjuration"
charge_max = 3000
charge_max = 2400
clothes_req = 0
invocation = "none"
invocation_type = "none"
@@ -95,30 +95,26 @@
/obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone/cult
cult_req = 1
charge_max = 4000
charge_max = 3600
/obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone/noncult
summon_type = list(/obj/item/device/soulstone/anybody)
/obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall
/obj/effect/proc_holder/spell/targeted/forcewall/cult
name = "Shield"
desc = "This spell creates a temporary forcefield to shield yourself and allies from incoming fire."
school = "transmutation"
charge_max = 300
clothes_req = 0
charge_max = 400
clothes_req = FALSE
invocation = "none"
invocation_type = "none"
range = 0
summon_type = list(/obj/effect/forcefield/cult)
summon_lifespan = 200
wall_type = /obj/effect/forcefield/cult
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_icon_state = "cultforcewall"
action_background_icon_state = "bg_demon"
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift
name = "Phase Shift"
desc = "This spell allows you to pass through walls."
@@ -279,4 +275,38 @@
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/golem
charge_max = 800
jaunt_in_type = /obj/effect/temp_visual/dir_setting/cult/phase
jaunt_out_type = /obj/effect/temp_visual/dir_setting/cult/phase/out
jaunt_out_type = /obj/effect/temp_visual/dir_setting/cult/phase/out
/obj/effect/proc_holder/spell/dumbfire/juggernaut
name = "Gauntlet Echo"
desc = "Channels energy into your gauntlet - firing its essence forward in a slow-moving but devastating blow."
proj_icon_state = "cursehand0"
proj_name = "Shadowfist"
proj_type = "/obj/effect/proc_holder/spell/targeted/inflict_handler/juggernaut" //IMPORTANT use only subtypes of this
proj_lifespan = 15
proj_step_delay = 7
charge_max = 350
clothes_req = FALSE
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_icon_state = "cultfist"
action_background_icon_state = "bg_demon"
sound = 'sound/weapons/resonator_blast.ogg'
proj_trigger_range = 0
ignore_factions = list("cult")
/obj/effect/proc_holder/spell/targeted/inflict_handler/juggernaut
name = "Gauntlet Echo"
amt_dam_brute = 30
amt_knockdown = 50
sound = 'sound/weapons/punch3.ogg'
/obj/effect/proc_holder/spell/targeted/inflict_handler/juggernaut/cast(list/targets,mob/user = usr)
var/turf/T = get_turf(src)
playsound(T, 'sound/weapons/resonator_blast.ogg', 100, FALSE)
new /obj/effect/temp_visual/cult/sac(T)
for(var/obj/O in range(src,1))
if(O.density && !istype(O, /obj/structure/destructible/cult))
O.take_damage(90, BRUTE, "gauntlet echo", 0)
new /obj/effect/temp_visual/cult/turf/floor
..()
+13 -2
View File
@@ -22,6 +22,7 @@
var/proj_lifespan = 100 //in deciseconds * proj_step_delay
var/proj_step_delay = 1 //lower = faster
var/list/ignore_factions = list() //Faction types that will be ignored
/obj/effect/proc_holder/spell/dumbfire/choose_targets(mob/user = usr)
@@ -74,8 +75,18 @@
var/mob/living/L = locate(/mob/living) in range(projectile, proj_trigger_range) - user
if(L && L.stat != DEAD)
projectile.cast(L.loc,user=user)
break
if(!ignore_factions.len)
projectile.cast(L.loc,user=user)
break
else
var/faction_check = FALSE
for(var/faction in L.faction)
if(ignore_factions.Find(faction))
faction_check = TRUE
break
if(!faction_check)
projectile.cast(L.loc,user=user)
break
if(proj_trail && projectile)
proj_trail(projectile)
@@ -29,7 +29,6 @@
/obj/effect/forcefield/wizard/Initialize(mapload, mob/summoner)
. = ..()
wizard = summoner
QDEL_IN(src, 300)
/obj/effect/forcefield/wizard/CanPass(atom/movable/mover, turf/target)
if(mover == wizard)
+3
View File
@@ -249,10 +249,13 @@
var/bz_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/bz][MOLES])
if(bz_pp > BZ_trip_balls_min)
H.hallucination += 20
H.reagents.add_reagent("bz_metabolites",5)
if(prob(33))
H.adjustBrainLoss(3, 150)
else if(bz_pp > 0.01)
H.hallucination += 5//Removed at 2 per tick so this will slowly build up
H.reagents.add_reagent("bz_metabolites",1)
// Tritium
+11 -2
View File
@@ -1061,7 +1061,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
/datum/uplink_item/device_tools/potion
name = "Syndicate Sentience Potion"
item = /obj/item/slimepotion/sentience/nuclear
item = /obj/item/slimepotion/slime/sentience/nuclear
desc = "A potion recovered at great risk by undercover syndicate operatives and then subsequently modified with syndicate technology. Using it will make any animal sentient, and bound to serve you, as well as implanting an internal radio for communication and an internal ID card for opening doors."
cost = 4
include_modes = list(/datum/game_mode/nuclear)
@@ -1211,6 +1211,15 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
item = /obj/item/storage/box/hug/reverse_revolver
restricted_roles = list("Clown")
/datum/uplink_item/role_restricted/reverse_bear_trap
name = "Reverse Bear Trap"
desc = "An ingenious execution device worn on (or forced onto) the head. Arming it starts a 1-minute kitchen timer mounted on the bear trap. When it goes off, the trap's jaws will \
violently open, instantly killing anyone wearing it by tearing their jaws in half. To arm, attack someone with it while they're not wearing headgear, and you will force it onto their \
head after three seconds uninterrupted."
cost = 5
item = /obj/item/device/reverse_bear_trap
restricted_roles = list("Clown")
/datum/uplink_item/role_restricted/mimery
name = "Guide to Advanced Mimery Series"
desc = "The classical two part series on how to further hone your mime skills. Upon studying the series, the user should be able to make 3x1 invisible walls, and shoot bullets out of their fingers. Obviously only works for Mimes."
@@ -1372,7 +1381,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
exclude_modes = list(/datum/game_mode/nuclear)
cant_discount = TRUE
var/starting_crate_value = 50
/datum/uplink_item/badass/surplus/super
name = "Super Surplus Crate"
desc = "A dusty SUPER-SIZED from the back of the Syndicate warehouse. Rumored to contain a valuable assortment of items, \