Merge branch 'master' into spike-loot-differentces

This commit is contained in:
Trilbyspaceclone
2020-08-27 03:41:14 -04:00
committed by GitHub
560 changed files with 13682 additions and 5938 deletions
+5 -1
View File
@@ -171,7 +171,11 @@ GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
/client/proc/cmd_display_overlay_log,
/client/proc/reload_configuration,
/datum/admins/proc/create_or_modify_area,
/client/proc/generate_wikichem_list //DO NOT PRESS UNLESS YOU WANT SUPERLAG
#ifdef REFERENCE_TRACKING
/datum/admins/proc/view_refs,
/datum/admins/proc/view_del_failures,
#endif
/client/proc/generate_wikichem_list, //DO NOT PRESS UNLESS YOU WANT SUPERLAG
)
GLOBAL_PROTECT(admin_verbs_debug)
GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release))
+1
View File
@@ -254,6 +254,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//message from the initiator without a target, all admins will see this
//won't bug irc
/datum/admin_help/proc/MessageNoRecipient(msg)
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
var/ref_src = "[REF(src)]"
//Message to be sent to all admins
var/admin_msg = "<span class='adminnotice'><span class='adminhelp'>Ticket [TicketHref("#[id]", ref_src)]</span><b>: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]:</b> <span class='linkify'>[keywords_lookup(msg)]</span></span>"
+6 -6
View File
@@ -55,7 +55,7 @@
if(AH)
message_admins("[key_name_admin(src)] has started replying to [key_name(C, 0, 0)]'s admin help.")
var/msg = stripped_multiline_input(src,"Message:", "Private message to [key_name(C, 0, 0)]")
var/msg = input(src,"Message:", "Private message to [C.holder?.fakekey ? "an Administrator" : key_name(C, 0, 0)].") as message|null
if (!msg)
message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help.")
return
@@ -90,7 +90,7 @@
if(!ircreplyamount) //to prevent people from spamming irc/discord
return
if(!msg)
msg = stripped_multiline_input(src,"Message:", "Private message to Administrator")
msg = input(src,"Message:", "Private message to Administrator") as message|null
if(!msg)
return
@@ -112,7 +112,7 @@
//get message text, limit it's length.and clean/escape html
if(!msg)
msg = stripped_multiline_input(src,"Message:", "Private message to [key_name(recipient, 0, 0)]")
msg = input(src,"Message:", "Private message to [recipient.holder?.fakekey ? "an Administrator" : key_name(recipient, 0, 0)].") as message|null
msg = trim(msg)
if(!msg)
return
@@ -133,7 +133,7 @@
//clean the message if it's not sent by a high-rank admin
if(!check_rights(R_SERVER|R_DEBUG,0)||irc)//no sending html to the poor bots
msg = trim(sanitize(msg), MAX_MESSAGE_LEN)
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
if(!msg)
return
@@ -188,10 +188,10 @@
//AdminPM popup for ApocStation and anybody else who wants to use it. Set it with POPUP_ADMIN_PM in config.txt ~Carn
if(CONFIG_GET(flag/popup_admin_pm))
spawn() //so we don't hold the caller proc up
spawn() //so we don't hold the caller proc up. Please functionalize this
var/sender = src
var/sendername = key
var/reply = stripped_multiline_input(recipient, msg,"Admin PM from-[sendername]", "") //show message and await a reply
var/reply = input(recipient, msg,"Admin PM from-[sendername]", "") as message|null //show message and await a reply
if(recipient && reply)
if(sender)
recipient.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them
@@ -0,0 +1,225 @@
#ifdef REFERENCE_TRACKING
GLOBAL_LIST_EMPTY(deletion_failures)
/world/proc/enable_reference_tracking()
if (fexists(EXTOOLS))
call(EXTOOLS, "ref_tracking_initialize")()
/proc/get_back_references(datum/D)
CRASH("/proc/get_back_references not hooked by extools, reference tracking will not function!")
/proc/get_forward_references(datum/D)
CRASH("/proc/get_forward_references not hooked by extools, reference tracking will not function!")
/proc/clear_references(datum/D)
return
/datum/admins/proc/view_refs(atom/D in world) //it actually supports datums as well but byond no likey
set category = "Debug"
set name = "View References"
if(!check_rights(R_DEBUG) || !D)
return
var/list/backrefs = get_back_references(D)
if(isnull(backrefs))
var/datum/browser/popup = new(usr, "ref_view", "<div align='center'>Error</div>")
popup.set_content("Reference tracking not enabled")
popup.open(FALSE)
return
var/list/frontrefs = get_forward_references(D)
var/list/dat = list()
dat += "<h1>References of \ref[D] - [D]</h1><br><a href='?_src_=vars;[HrefToken()];[VV_HK_VIEW_REFERENCES]=TRUE;[VV_HK_TARGET]=[REF(D)]'>\[Refresh\]</a><hr>"
dat += "<h3>Back references - these things hold references to this object.</h3>"
dat += "<table>"
dat += "<tr><th>Ref</th><th>Type</th><th>Variable Name</th><th>Follow</th>"
for(var/ref in backrefs)
var/datum/backreference = ref
if(isnull(backreference))
dat += "<tr><td>GC'd Reference</td></tr>"
if(istype(backreference))
dat += "<tr><td><a href='?_src_=vars;[HrefToken()];Vars=[REF(backreference)]'>[REF(backreference)]</td><td>[backreference.type]</td><td>[backrefs[backreference]]</td><td><a href='?_src_=vars;[HrefToken()];[VV_HK_VIEW_REFERENCES]=TRUE;[VV_HK_TARGET]=[REF(backreference)]'>\[Follow\]</a></td></tr>"
else if(islist(backreference))
dat += "<tr><td><a href='?_src_=vars;[HrefToken()];Vars=[REF(backreference)]'>[REF(backreference)]</td><td>list</td><td>[backrefs[backreference]]</td><td><a href='?_src_=vars;[HrefToken()];[VV_HK_VIEW_REFERENCES]=TRUE;[VV_HK_TARGET]=[REF(backreference)]'>\[Follow\]</a></td></tr>"
else
dat += "<tr><td>Weird reference type. Add more debugging checks.</td></tr>"
dat += "</table><hr>"
dat += "<h3>Forward references - this object is referencing those things.</h3>"
dat += "<table>"
dat += "<tr><th>Variable name</th><th>Ref</th><th>Type</th><th>Follow</th>"
for(var/ref in frontrefs)
var/datum/backreference = frontrefs[ref]
dat += "<tr><td>[ref]</td><td><a href='?_src_=vars;[HrefToken()];Vars=[REF(backreference)]'>[REF(backreference)]</a></td><td>[backreference.type]</td><td><a href='?_src_=vars;[HrefToken()];[VV_HK_VIEW_REFERENCES]=TRUE;[VV_HK_TARGET]=[REF(backreference)]'>\[Follow\]</a></td></tr>"
dat += "</table><hr>"
dat = dat.Join()
var/datum/browser/popup = new(usr, "ref_view", "<div align='center'>References of \ref[D]</div>")
popup.set_content(dat)
popup.open(FALSE)
/datum/admins/proc/view_del_failures()
set category = "Debug"
set name = "View Deletion Failures"
if(!check_rights(R_DEBUG))
return
var/list/dat = list("<table>")
for(var/t in GLOB.deletion_failures)
if(isnull(t))
dat += "<tr><td>GC'd Reference | <a href='byond://?src=[REF(src)];[HrefToken(TRUE)];delfail_clearnulls=TRUE'>Clear Nulls</a></td></tr>"
continue
var/datum/thing = t
dat += "<tr><td>\ref[thing] | [thing.type][thing.gc_destroyed ? " (destroyed)" : ""] [ADMIN_VV(thing)]</td></tr>"
dat += "</table><hr>"
dat = dat.Join()
var/datum/browser/popup = new(usr, "del_failures", "<div align='center'>Deletion Failures</div>")
popup.set_content(dat)
popup.open(FALSE)
/datum/proc/find_references()
testing("Beginning search for references to a [type].")
var/list/backrefs = get_back_references(src)
for(var/ref in backrefs)
if(isnull(ref))
log_world("## TESTING: Datum reference found, but gone now.")
continue
if(islist(ref))
log_world("## TESTING: Found [type] \ref[src] in list.")
continue
var/datum/datum_ref = ref
if(!istype(datum_ref))
log_world("## TESTING: Found [type] \ref[src] in unknown type reference: [datum_ref].")
return
log_world("## TESTING: Found [type] \ref[src] in [datum_ref.type][datum_ref.gc_destroyed ? " (destroyed)" : ""]")
message_admins("Found [type] \ref[src] [ADMIN_VV(src)] in [datum_ref.type][datum_ref.gc_destroyed ? " (destroyed)" : ""] [ADMIN_VV(datum_ref)]")
testing("Completed search for references to a [type].")
#endif
#ifdef LEGACY_REFERENCE_TRACKING
/datum/verb/legacy_find_refs()
set category = "Debug"
set name = "Find References"
set src in world
find_references(FALSE)
/datum/proc/find_references_legacy(skip_alert)
running_find_references = type
if(usr?.client)
if(usr.client.running_find_references)
testing("CANCELLED search for references to a [usr.client.running_find_references].")
usr.client.running_find_references = null
running_find_references = null
//restart the garbage collector
SSgarbage.can_fire = TRUE
SSgarbage.next_fire = world.time + world.tick_lag
return
if(!skip_alert && alert("Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", "Yes", "No") != "Yes")
running_find_references = null
return
//this keeps the garbage collector from failing to collect objects being searched for in here
SSgarbage.can_fire = FALSE
if(usr?.client)
usr.client.running_find_references = type
testing("Beginning search for references to a [type].")
last_find_references = world.time
DoSearchVar(GLOB) //globals
for(var/datum/thing in world) //atoms (don't beleive its lies)
DoSearchVar(thing, "World -> [thing]")
for(var/datum/thing) //datums
DoSearchVar(thing, "World -> [thing]")
for(var/client/thing) //clients
DoSearchVar(thing, "World -> [thing]")
testing("Completed search for references to a [type].")
if(usr?.client)
usr.client.running_find_references = null
running_find_references = null
//restart the garbage collector
SSgarbage.can_fire = TRUE
SSgarbage.next_fire = world.time + world.tick_lag
/datum/verb/qdel_then_find_references()
set category = "Debug"
set name = "qdel() then Find References"
set src in world
qdel(src, TRUE) //force a qdel
if(!running_find_references)
find_references(TRUE)
/datum/verb/qdel_then_if_fail_find_references()
set category = "Debug"
set name = "qdel() then Find References if GC failure"
set src in world
qdel_and_find_ref_if_fail(src, TRUE)
/datum/proc/DoSearchVar(potential_container, container_name, recursive_limit = 64)
if(usr?.client && !usr.client.running_find_references)
return
if(!recursive_limit)
return
if(istype(potential_container, /datum))
var/datum/datum_container = potential_container
if(datum_container.last_find_references == last_find_references)
return
datum_container.last_find_references = last_find_references
var/list/vars_list = datum_container.vars
for(var/varname in vars_list)
if (varname == "vars")
continue
var/variable = vars_list[varname]
if(variable == src)
testing("Found [type] \ref[src] in [datum_container.type]'s [varname] var. [container_name]")
else if(islist(variable))
DoSearchVar(variable, "[container_name] -> list", recursive_limit - 1)
else if(islist(potential_container))
var/normal = IS_NORMAL_LIST(potential_container)
for(var/element_in_list in potential_container)
if(element_in_list == src)
testing("Found [type] \ref[src] in list [container_name].")
else if(element_in_list && !isnum(element_in_list) && normal && potential_container[element_in_list] == src)
testing("Found [type] \ref[src] in list [container_name]\[[element_in_list]\]")
else if(islist(element_in_list))
DoSearchVar(element_in_list, "[container_name] -> list", recursive_limit - 1)
#ifndef FIND_REF_NO_CHECK_TICK
CHECK_TICK
#endif
/proc/qdel_and_find_ref_if_fail(datum/thing_to_del, force = FALSE)
SSgarbage.reference_find_on_fail[REF(thing_to_del)] = TRUE
qdel(thing_to_del, force)
#endif
@@ -45,6 +45,16 @@
usr.client.admin_delete(target)
if (isturf(src)) // show the turf that took its place
usr.client.debug_variables(src)
return
#ifdef REFERENCE_TRACKING
if(href_list[VV_HK_VIEW_REFERENCES])
var/datum/D = locate(href_list[VV_HK_TARGET])
if(!D)
to_chat(usr, "<span class='warning'>Unable to locate item.</span>")
return
usr.client.holder.view_refs(target)
return
#endif
if(href_list[VV_HK_MARK])
usr.client.mark_datum(target)
if(href_list[VV_HK_ADDCOMPONENT])
@@ -61,6 +61,7 @@
"Set len" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_SET_LENGTH),
"Shuffle" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_SHUFFLE),
"Show VV To Player" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_EXPOSE),
"View References" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_VIEW_REFERENCES),
"---"
)
for(var/i in 1 to length(dropdownoptions))
@@ -14,6 +14,8 @@ GLOBAL_LIST_EMPTY(antagonists)
var/list/objectives = list()
var/antag_memory = ""//These will be removed with antag datum
var/antag_moodlet //typepath of moodlet that the mob will gain with their status
var/antag_hud_type
var/antag_hud_name
/// If above 0, this is the multiplier for the speed at which we hijack the shuttle. Do not directly read, use hijack_speed().
var/hijack_speed = 0
@@ -77,6 +79,17 @@ GLOBAL_LIST_EMPTY(antagonists)
hud.leave_hud(mob_override)
set_antag_hud(mob_override, null)
// Handles adding and removing the clumsy mutation from clown antags. Gets called in apply/remove_innate_effects
/datum/antagonist/proc/handle_clown_mutation(mob/living/mob_override, message, removing = TRUE)
var/mob/living/carbon/human/H = mob_override
if(H && istype(H) && owner.assigned_role == "Clown")
if(removing) // They're a clown becoming an antag, remove clumsy
H.dna.remove_mutation(CLOWNMUT)
if(!silent && message)
to_chat(H, "<span class='boldnotice'>[message]</span>")
else
H.dna.add_mutation(CLOWNMUT) // We're removing their antag status, add back clumsy
//Assign default team and creates one for one of a kind team antagonists
/datum/antagonist/proc/create_team(datum/team/team)
return
@@ -268,7 +281,6 @@ GLOBAL_LIST_EMPTY(antagonists)
. = CONFIG_GET(keyed_list/antag_threat)[lowertext(name)]
if(. == null)
return threat
return threat
//This one is created by admin tools for custom objectives
/datum/antagonist/custom
@@ -37,14 +37,14 @@ GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear))
description = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras."
id = "science_tool"
build_path = /obj/item/abductor/gizmo
/*
/datum/abductor_gear/advanced_baton
name = "Advanced Baton"
description = "A quad-mode baton used for incapacitation and restraining of specimens."
id = "advanced_baton"
cost = 2
build_path = /obj/item/melee/baton/abductor //does not exist?
*/
build_path = /obj/item/abductor/baton //does not exist?
/datum/abductor_gear/superlingual_matrix
name = "Superlingual Matrix"
description = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something."
@@ -30,6 +30,22 @@
/// Possible gear to be dispensed
var/list/possible_gear
/obj/machinery/abductor/console/Initialize(mapload)
. = ..()
possible_gear = get_abductor_gear()
/**
* get_abductor_gear: Returns a list of a filtered abductor gear sorted by categories
*/
/obj/machinery/abductor/console/proc/get_abductor_gear()
var/list/filtered_modules = list()
for(var/path in GLOB.abductor_gear)
var/datum/abductor_gear/AG = new path
if(!filtered_modules[AG.category])
filtered_modules[AG.category] = list()
filtered_modules[AG.category][AG] = AG
return filtered_modules
/obj/machinery/abductor/console/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!HAS_TRAIT(user, TRAIT_ABDUCTOR_TRAINING) && !HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_TRAINING))
to_chat(user, "<span class='warning'>You start mashing alien buttons at random!</span>")
+7 -1
View File
@@ -4,11 +4,17 @@
antagpanel_category = "Blob"
show_to_ghosts = TRUE
job_rank = ROLE_BLOB
threat = 20
threat = 50
var/datum/action/innate/blobpop/pop_action
var/starting_points_human_blob = 60
var/point_rate_human_blob = 2
/datum/antagonist/blob/threat()
. = ..()
if(isovermind(owner.current))
var/mob/camera/blob/overmind = owner.current
. *= (overmind.blobs_legit.len / overmind.max_count)
/datum/antagonist/blob/roundend_report()
var/basic_report = ..()
//Display max blobpoints for blebs that lost
@@ -75,7 +75,6 @@
desc = "A floating, fragile spore."
icon_state = "blobpod"
icon_living = "blobpod"
threat = 0.2
health = 30
maxHealth = 30
verb_say = "psychically pulses"
@@ -28,7 +28,6 @@
if(istype(owner.current.loc, /obj/structure/closet/crate/coffin))
Torpor_Begin()
// Wait before next pass
FreeAllVassals() // Free my Vassals! (if I haven't yet)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -308,6 +307,7 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HUMAN FOOD
@@ -23,6 +23,7 @@
ADD_TRAIT(user, TRAIT_PIERCEIMMUNE, "fortitude")
ADD_TRAIT(user, TRAIT_NODISMEMBER, "fortitude")
ADD_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude")
ADD_TRAIT(user, TRAIT_NORUNNING, "fortitude")
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
fortitude_resist = max(0.3, 0.7 - level_current * 0.1)
@@ -53,6 +54,7 @@
REMOVE_TRAIT(user, TRAIT_PIERCEIMMUNE, "fortitude")
REMOVE_TRAIT(user, TRAIT_NODISMEMBER, "fortitude")
REMOVE_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude")
REMOVE_TRAIT(user, TRAIT_NORUNNING, "fortitude")
if(!ishuman(owner))
return
var/mob/living/carbon/human/H = owner
@@ -89,17 +89,17 @@
creation_message = "<span class='alloy bold'>The cogscarab clicks and whirrs as it hops up and springs to life!</span>"
construct_type = /mob/living/simple_animal/drone/cogscarab
w_class = WEIGHT_CLASS_SMALL
var/infinite_resources = TRUE
var/infinite_resources = FALSE //No.
var/static/obj/item/seasonal_hat //Share it with all other scarabs, since we're from the same cult!
/obj/item/clockwork/construct_chassis/cogscarab/Initialize()
. = ..()
if(GLOB.servants_active)
infinite_resources = FALSE //For any that are somehow spawned in late
infinite_resources = FALSE //This check is relatively irrelevant until *someone* makes the infinite resources var default to true again, so, leaving it in.
/obj/item/clockwork/construct_chassis/cogscarab/pre_spawn()
if(infinite_resources)
//During rounds where they can't interact with the station, let them experiment with builds
//During rounds where they can't interact with the station, let them experiment with builds, if an admin allows them to.
construct_type = /mob/living/simple_animal/drone/cogscarab/ratvar
if(!seasonal_hat)
var/obj/item/drone_shell/D = locate() in GLOB.poi_list
@@ -6,7 +6,6 @@
unique_name = 1
minbodytemp = 0
unsuitable_atmos_damage = 0
threat = 1
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) //Robotic
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
healable = FALSE
@@ -9,7 +9,6 @@
desc = "The stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield."
icon_state = "clockwork_marauder"
mob_biotypes = MOB_HUMANOID
threat = 3
health = 120
maxHealth = 120
force_threshold = 8
@@ -97,7 +97,7 @@
desc = "Charges your slab with divine energy, allowing you to overwhelm a target with Ratvar's light."
invocations = list("Divinity, show them your light!")
whispered = TRUE
channel_time = 15 // I think making kindle channel a third of the time less is a good make up for the fact that it silences people for such a little amount of time.
channel_time = 25 //2.5 seconds should be a okay compromise between being able to use it when needed, and not being able to just pause in combat for a second and hardstunning your enemy
power_cost = 125
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
@@ -137,6 +137,30 @@
quickbind_desc = "Applies handcuffs to a struck target."
//Belligerent: Channeled for up to fifteen times over thirty seconds. Forces non-servants that can hear the chant to walk, doing minor damage. Nar-Sian cultists are burned.
/datum/clockwork_scripture/channeled/belligerent
descname = "Channeled, Area Slowdown"
name = "Belligerent"
desc = "Forces all nearby non-servants to walk rather than run, doing minor damage. Chanted every two seconds for up to thirty seconds."
chant_invocations = list("Punish their blindness!", "Take time, make slow!", "Kneel before The Justiciar!", "Halt their charges!", "Cease the tides!")
chant_amount = 15
chant_interval = 20
channel_time = 20
power_cost = 300
usage_tip = "Useful for crowd control in a populated area and disrupting mass movement."
tier = SCRIPTURE_DRIVER
primary_component = BELLIGERENT_EYE
sort_priority = 7
quickbind = TRUE
quickbind_desc = "Forces nearby non-Servants to walk, doing minor damage with each chant.<br><b>Maximum 15 chants.</b>"
/datum/clockwork_scripture/channeled/belligerent/chant_effects(chant_number)
for(var/mob/living/carbon/C in hearers(7, invoker))
C.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
new /obj/effect/temp_visual/ratvar/belligerent(get_turf(invoker))
return TRUE
//Vanguard: Provides twenty seconds of greatly increased stamina regeneration and stun immunity. At the end of the twenty seconds, 25% of all stuns absorbed aswell as 50% of healed stamloss are applied to the invoker.
/datum/clockwork_scripture/vanguard
descname = "Self Stun Immunity"
@@ -149,7 +173,7 @@
usage_tip = "You cannot reactivate Vanguard while still shielded by it."
tier = SCRIPTURE_DRIVER
primary_component = VANGUARD_COGWHEEL
sort_priority = 7
sort_priority = 8
quickbind = TRUE
quickbind_desc = "Allows you to temporarily have quickly regenerating stamina and absorb stuns. Part of the stuns absorbed and staminaloss healed will affect you when disabled."
@@ -181,7 +205,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 = 9
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"
@@ -255,7 +279,7 @@
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = GEIS_CAPACITOR
sort_priority = 10
sort_priority = 11
important = TRUE
quickbind = TRUE
quickbind_desc = "Creates a new Clockwork Slab."
@@ -276,7 +300,7 @@
tier = SCRIPTURE_DRIVER
space_allowed = TRUE
primary_component = GEIS_CAPACITOR
sort_priority = 11
sort_priority = 12
quickbind = TRUE
quickbind_desc = "Creates a pair of Wraith Spectacles, which grant true sight but cause gradual vision loss."
@@ -295,7 +319,7 @@
usage_tip = "This gateway is strictly one-way and will only allow things through the invoker's portal."
tier = SCRIPTURE_DRIVER
primary_component = GEIS_CAPACITOR
sort_priority = 9
sort_priority = 10
quickbind = TRUE
quickbind_desc = "Allows you to create a one-way Spatial Gateway to a living Servant or Clockwork Obelisk."
@@ -459,26 +459,3 @@
invoker.light_range = 0
invoker.update_light()
return ..()
//Belligerent: Channeled for up to fifteen times over thirty seconds. Forces non-servants that can hear the chant to walk, doing minor damage. Nar-Sian cultists are burned.
/datum/clockwork_scripture/channeled/belligerent
descname = "Channeled, Area Slowdown"
name = "Belligerent"
desc = "Forces all nearby non-servants to walk rather than run, doing minor damage. Chanted every two seconds for up to thirty seconds."
chant_invocations = list("Punish their blindness!", "Take time, make slow!", "Kneel before The Justiciar!", "Halt their charges!", "Cease the tides!")
chant_amount = 15
chant_interval = 20
channel_time = 20
power_cost = 300
usage_tip = "Useful for crowd control in a populated area and disrupting mass movement."
tier = SCRIPTURE_DRIVER
primary_component = BELLIGERENT_EYE
sort_priority = 1
quickbind = TRUE
quickbind_desc = "Forces nearby non-Servants to walk, doing minor damage with each chant.<br><b>Maximum 15 chants.</b>"
/datum/clockwork_scripture/channeled/belligerent/chant_effects(chant_number)
for(var/mob/living/carbon/C in hearers(7, invoker))
C.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
new /obj/effect/temp_visual/ratvar/belligerent(get_turf(invoker))
return TRUE
@@ -0,0 +1,229 @@
/datum/antagonist/heretic
name = "Heretic"
roundend_category = "Heretics"
antagpanel_category = "Heretic"
antag_moodlet = /datum/mood_event/heretics
job_rank = ROLE_HERETIC
antag_hud_type = ANTAG_HUD_HERETIC
antag_hud_name = "heretic"
var/give_equipment = TRUE
var/list/researched_knowledge = list()
var/total_sacrifices = 0
var/ascended = FALSE
/datum/antagonist/heretic/admin_add(datum/mind/new_owner,mob/admin)
give_equipment = TRUE
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has heresized [key_name_admin(new_owner)].")
log_admin("[key_name(admin)] has heresized [key_name(new_owner)].")
/datum/antagonist/heretic/greet()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ecult_op.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change
to_chat(owner, "<span class='boldannounce'>You are the Heretic!</span><br>\
<B>The old ones gave you these tasks to fulfill:</B>")
owner.announce_objectives()
to_chat(owner, "<span class='cult'>The book whispers, the forbidden knowledge walks once again!<br>\
Your book allows you to research abilities, but be careful, you cannot undo what has been done.<br>\
You gain charges by either collecting influences or sacrificing people tracked by the living heart<br> \
You can find a basic guide at : https://tgstation13.org/wiki/Heresy_101 </span>")
/datum/antagonist/heretic/on_gain()
var/mob/living/current = owner.current
if(ishuman(current))
forge_primary_objectives()
gain_knowledge(/datum/eldritch_knowledge/spell/basic)
gain_knowledge(/datum/eldritch_knowledge/living_heart)
gain_knowledge(/datum/eldritch_knowledge/codex_cicatrix)
gain_knowledge(/datum/eldritch_knowledge/eldritch_blade)
current.log_message("has been converted to the cult of the forgotten ones!", LOG_ATTACK, color="#960000")
GLOB.reality_smash_track.AddMind(owner)
START_PROCESSING(SSprocessing,src)
if(give_equipment)
equip_cultist()
owner.teach_crafting_recipe(/datum/crafting_recipe/heretic/codex)
return ..()
/datum/antagonist/heretic/on_removal()
for(var/X in researched_knowledge)
var/datum/eldritch_knowledge/EK = researched_knowledge[X]
EK.on_lose(owner.current)
if(!silent)
to_chat(owner.current, "<span class='userdanger'>Your mind begins to flare as the otherwordly knowledge escapes your grasp!</span>")
owner.current.log_message("has renounced the cult of the old ones!", LOG_ATTACK, color="#960000")
GLOB.reality_smash_track.RemoveMind(owner)
STOP_PROCESSING(SSprocessing,src)
return ..()
/datum/antagonist/heretic/proc/equip_cultist()
var/mob/living/carbon/H = owner.current
if(!istype(H))
return
. += ecult_give_item(/obj/item/forbidden_book, H)
. += ecult_give_item(/obj/item/living_heart, H)
/datum/antagonist/heretic/proc/ecult_give_item(obj/item/item_path, mob/living/carbon/human/H)
var/list/slots = list(
"backpack" = SLOT_IN_BACKPACK,
"left pocket" = SLOT_L_STORE,
"right pocket" = SLOT_R_STORE
)
var/T = new item_path(H)
var/item_name = initial(item_path.name)
var/where = H.equip_in_one_of_slots(T, slots)
if(!where)
to_chat(H, "<span class='userdanger'>Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1).</span>")
return FALSE
else
to_chat(H, "<span class='danger'>You have a [item_name] in your [where].</span>")
if(where == "backpack")
SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H)
return TRUE
/datum/antagonist/heretic/process()
for(var/X in researched_knowledge)
var/datum/eldritch_knowledge/EK = researched_knowledge[X]
EK.on_life(owner.current)
/datum/antagonist/heretic/proc/forge_primary_objectives()
var/list/assasination = list()
var/list/protection = list()
for(var/i in 1 to 2)
var/pck = pick("assasinate","protect")
switch(pck)
if("assasinate")
var/datum/objective/assassinate/A = new
A.owner = owner
var/list/owners = A.get_owners()
A.find_target(owners,protection)
assasination += A.target
objectives += A
if("protect")
var/datum/objective/protect/P = new
P.owner = owner
var/list/owners = P.get_owners()
P.find_target(owners,assasination)
protection += P.target
objectives += P
var/datum/objective/sacrifice_ecult/SE = new
SE.owner = owner
SE.update_explanation_text()
objectives += SE
var/datum/objective/escape/escape_objective = new
escape_objective.owner = owner
objectives += escape_objective
/datum/antagonist/heretic/apply_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
if(mob_override)
current = mob_override
add_antag_hud(antag_hud_type, antag_hud_name, current)
handle_clown_mutation(current, mob_override ? null : "Knowledge described in the book allowed you to overcome your clownish nature, allowing you to use complex items effectively.")
current.faction |= "heretics"
/datum/antagonist/heretic/remove_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
if(mob_override)
current = mob_override
remove_antag_hud(antag_hud_type, current)
handle_clown_mutation(current, removing = FALSE)
current.faction -= "heretics"
/datum/antagonist/heretic/get_admin_commands()
. = ..()
.["Equip"] = CALLBACK(src,.proc/equip_cultist)
/datum/antagonist/heretic/roundend_report()
var/list/parts = list()
var/cultiewin = TRUE
parts += printplayer(owner)
parts += "<b>Sacrifices Made:</b> [total_sacrifices]"
if(length(objectives))
var/count = 1
for(var/o in objectives)
var/datum/objective/objective = o
if(objective.check_completion())
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</b></span>"
else
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
cultiewin = FALSE
count++
if(ascended)
parts += "<span class='greentext big'>HERETIC HAS ASCENDED!</span>"
else
if(cultiewin)
parts += "<span class='greentext'>The heretic was successful!</span>"
else
parts += "<span class='redtext'>The heretic has failed.</span>"
parts += "<b>Knowledge Researched:</b> "
var/list/knowledge_message = list()
var/list/knowledge = get_all_knowledge()
for(var/X in knowledge)
var/datum/eldritch_knowledge/EK = knowledge[X]
knowledge_message += "[EK.name]"
parts += knowledge_message.Join(", ")
return parts.Join("<br>")
////////////////
// Knowledge //
////////////////
/datum/antagonist/heretic/proc/gain_knowledge(datum/eldritch_knowledge/EK)
if(get_knowledge(EK))
return FALSE
var/datum/eldritch_knowledge/initialized_knowledge = new EK
researched_knowledge[initialized_knowledge.type] = initialized_knowledge
initialized_knowledge.on_gain(owner.current)
return TRUE
/datum/antagonist/heretic/proc/get_researchable_knowledge()
var/list/researchable_knowledge = list()
var/list/banned_knowledge = list()
for(var/X in researched_knowledge)
var/datum/eldritch_knowledge/EK = researched_knowledge[X]
researchable_knowledge |= EK.next_knowledge
banned_knowledge |= EK.banned_knowledge
banned_knowledge |= EK.type
researchable_knowledge -= banned_knowledge
return researchable_knowledge
/datum/antagonist/heretic/proc/get_knowledge(wanted)
return researched_knowledge[wanted]
/datum/antagonist/heretic/proc/get_all_knowledge()
return researched_knowledge
////////////////
// Objectives //
////////////////
/datum/objective/sacrifice_ecult
name = "sacrifice"
/datum/objective/sacrifice_ecult/update_explanation_text()
. = ..()
target_amount = rand(2,4)
explanation_text = "Sacrifice at least [target_amount] people."
/datum/objective/sacrifice_ecult/check_completion()
if(!owner)
return FALSE
var/datum/antagonist/heretic/cultie = owner.has_antag_datum(/datum/antagonist/heretic)
if(!cultie)
return FALSE
return cultie.total_sacrifices >= target_amount
@@ -0,0 +1,145 @@
/obj/item/forbidden_book
name = "Codex Cicatrix"
desc = "Book describing the secrets of the veil."
icon = 'icons/obj/eldritch.dmi'
icon_state = "book"
item_state = "book"
w_class = WEIGHT_CLASS_SMALL
///Last person that touched this
var/mob/living/last_user
///how many charges do we have?
var/charge = 0
///Where we cannot create the rune?
var/static/list/blacklisted_turfs = typecacheof(list(/turf/closed,/turf/open/space,/turf/open/lava))
/obj/item/forbidden_book/Destroy()
last_user = null
. = ..()
/obj/item/forbidden_book/examine(mob/user)
. = ..()
if(!IS_HERETIC(user))
return
. += "The Tome holds [charge] charges."
. += "Use it on the floor to create a transmutation rune, used to perform rituals."
. += "Hit an influence in the black part with it to gain a charge."
. += "Hit a transmutation rune to destroy it."
/obj/item/forbidden_book/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
if(!proximity_flag || !IS_HERETIC(user))
return
if(istype(target,/obj/effect/eldritch))
remove_rune(target,user)
if(istype(target,/obj/effect/reality_smash))
get_power_from_influence(target,user)
if(istype(target,/turf/open))
draw_rune(target,user)
///Gives you a charge and destroys a corresponding influence
/obj/item/forbidden_book/proc/get_power_from_influence(atom/target, mob/user)
var/obj/effect/reality_smash/RS = target
to_chat(target, "<span class='danger'>You start drawing power from influence...</span>")
if(do_after(user,10 SECONDS,TRUE,RS))
qdel(RS)
charge += 1
///Draws a rune on a selected turf
/obj/item/forbidden_book/proc/draw_rune(atom/target,mob/user)
for(var/turf/T in range(1,target))
if(is_type_in_typecache(T, blacklisted_turfs))
to_chat(target, "<span class='warning'>The terrain doesn't support runes!</span>")
return
var/A = get_turf(target)
to_chat(user, "<span class='danger'>You start drawing a rune...</span>")
if(do_after(user,30 SECONDS,FALSE, user))
new /obj/effect/eldritch/big(A)
///Removes runes from the selected turf
/obj/item/forbidden_book/proc/remove_rune(atom/target,mob/user)
to_chat(user, "<span class='danger'>You start removing a rune...</span>")
if(do_after(user,2 SECONDS,FALSE, user))
qdel(target)
/obj/item/forbidden_book/ui_interact(mob/user, datum/tgui/ui = null)
if(!IS_HERETIC(user))
return FALSE
last_user = user
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
icon_state = "book_open"
flick("book_opening", src)
ui = new(user, src, "ForbiddenLore", name)
ui.open()
/obj/item/forbidden_book/ui_data(mob/user)
var/datum/antagonist/heretic/cultie = user.mind.has_antag_datum(/datum/antagonist/heretic)
var/list/to_know = list()
for(var/Y in cultie.get_researchable_knowledge())
to_know += new Y
var/list/known = cultie.get_all_knowledge()
var/list/data = list()
var/list/lore = list()
data["charges"] = charge
for(var/X in to_know)
lore = list()
var/datum/eldritch_knowledge/EK = X
lore["type"] = EK.type
lore["name"] = EK.name
lore["cost"] = EK.cost
lore["disabled"] = EK.cost <= charge ? FALSE : TRUE
lore["path"] = EK.route
lore["state"] = "Research"
lore["flavour"] = EK.gain_text
lore["desc"] = EK.desc
data["to_know"] += list(lore)
for(var/X in known)
lore = list()
var/datum/eldritch_knowledge/EK = known[X]
lore["name"] = EK.name
lore["cost"] = EK.cost
lore["disabled"] = TRUE
lore["path"] = EK.route
lore["state"] = "Researched"
lore["flavour"] = EK.gain_text
lore["desc"] = EK.desc
data["to_know"] += list(lore)
if(!length(data["to_know"]))
data["to_know"] = null
return data
/obj/item/forbidden_book/ui_act(action, params)
. = ..()
if(.)
return
switch(action)
if("research")
var/datum/antagonist/heretic/cultie = last_user.mind.has_antag_datum(/datum/antagonist/heretic)
var/ekname = params["name"]
for(var/X in cultie.get_researchable_knowledge())
var/datum/eldritch_knowledge/EK = X
if(initial(EK.name) != ekname)
continue
if(cultie.gain_knowledge(EK))
charge -= text2num(params["cost"])
return TRUE
update_icon() // Not applicable to all objects.
/obj/item/forbidden_book/ui_close(mob/user)
flick("book_closing",src)
icon_state = initial(icon_state)
return ..()
/obj/item/forbidden_book/debug
charge = 100
@@ -0,0 +1,289 @@
/obj/effect/eldritch
name = "Generic rune"
desc = "Weird combination of shapes and symbols etched into the floor itself. The indentation is filled with thick black tar-like fluid."
anchored = TRUE
icon_state = ""
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
layer = SIGIL_LAYER
///Used mainly for summoning ritual to prevent spamming the rune to create millions of monsters.
var/is_in_use = FALSE
/obj/effect/eldritch/attack_hand(mob/living/user)
. = ..()
if(.)
return
try_activate(user)
/obj/effect/eldritch/proc/try_activate(mob/living/user)
if(!IS_HERETIC(user))
return
if(!is_in_use)
INVOKE_ASYNC(src, .proc/activate , user)
/obj/effect/eldritch/attacked_by(obj/item/I, mob/living/user)
. = ..()
if(istype(I,/obj/item/nullrod))
qdel(src)
/obj/effect/eldritch/proc/activate(mob/living/user)
is_in_use = TRUE
// Have fun trying to read this proc.
var/datum/antagonist/heretic/cultie = user.mind.has_antag_datum(/datum/antagonist/heretic)
var/list/knowledge = cultie.get_all_knowledge()
var/list/atoms_in_range = list()
for(var/A in range(1, src))
var/atom/atom_in_range = A
if(istype(atom_in_range,/area))
continue
if(istype(atom_in_range,/turf)) // we dont want turfs
continue
if(istype(atom_in_range,/mob/living))
var/mob/living/living_in_range = atom_in_range
if(living_in_range.stat != DEAD || living_in_range == user) // we only accept corpses, no living beings allowed.
continue
atoms_in_range += atom_in_range
for(var/X in knowledge)
var/datum/eldritch_knowledge/current_eldritch_knowledge = knowledge[X]
//has to be done so that we can freely edit the local_required_atoms without fucking up the eldritch knowledge
var/list/local_required_atoms = list()
if(!current_eldritch_knowledge.required_atoms || current_eldritch_knowledge.required_atoms.len == 0)
continue
local_required_atoms += current_eldritch_knowledge.required_atoms
var/list/selected_atoms = list()
if(!current_eldritch_knowledge.recipe_snowflake_check(atoms_in_range,drop_location(),selected_atoms))
continue
for(var/LR in local_required_atoms)
var/list/local_required_atom_list = LR
for(var/LAIR in atoms_in_range)
var/atom/local_atom_in_range = LAIR
if(is_type_in_list(local_atom_in_range,local_required_atom_list))
selected_atoms |= local_atom_in_range
local_required_atoms -= list(local_required_atom_list)
if(length(local_required_atoms) > 0)
continue
flick("[icon_state]_active",src)
playsound(user, 'sound/magic/castsummon.ogg', 75, TRUE)
//we are doing this since some on_finished_recipe subtract the atoms from selected_atoms making them invisible permanently.
var/list/atoms_to_disappear = selected_atoms.Copy()
for(var/to_disappear in atoms_to_disappear)
var/atom/atom_to_disappear = to_disappear
//temporary so we dont have to deal with the bs of someone picking those up when they may be deleted
atom_to_disappear.invisibility = INVISIBILITY_ABSTRACT
if(current_eldritch_knowledge.on_finished_recipe(user,selected_atoms,loc))
current_eldritch_knowledge.cleanup_atoms(selected_atoms)
is_in_use = FALSE
for(var/to_appear in atoms_to_disappear)
var/atom/atom_to_appear = to_appear
//we need to reappear the item just in case the ritual didnt consume everything... or something.
atom_to_appear.invisibility = initial(atom_to_appear.invisibility)
return
is_in_use = FALSE
to_chat(user,"<span class='warning'>Your ritual failed! You used either wrong components or are missing something important!</span>")
/obj/effect/eldritch/big
name = "transmutation circle"
icon = 'icons/effects/96x96.dmi'
icon_state = "eldritch_rune1"
pixel_x = -32 //So the big ol' 96x96 sprite shows up right
pixel_y = -32
/**
* #Reality smash tracker
*
* Stupid fucking list holder, DONT create new ones, it will break the game, this is automnatically created whenever eldritch cultists are created.
*
* Tracks relevant data, generates relevant data, useful tool
*/
/datum/reality_smash_tracker
///list of tracked reality smashes
var/list/smashes = list()
///List of mobs with ability to see the smashes
var/list/targets = list()
/datum/reality_smash_tracker/Destroy(force, ...)
if(GLOB.reality_smash_track == src)
stack_trace("/datum/reality_smash_tracker was deleted. Heretics may no longer access any influences. Fix it or call coder support")
QDEL_LIST(smashes)
targets.Cut()
return ..()
/**
* Automatically fixes the target and smash network
*
* Fixes any bugs that are caused by late Generate() or exchanging clients
*/
/datum/reality_smash_tracker/proc/ReworkNetwork()
listclearnulls(smashes)
for(var/mind in targets)
if(isnull(mind))
stack_trace("A null somehow landed in a list of minds")
continue
for(var/X in smashes)
var/obj/effect/reality_smash/reality_smash = X
reality_smash.AddMind(mind)
/**
* Generates a set amount of reality smashes based on the N value
*
* Automatically creates more reality smashes
*/
/datum/reality_smash_tracker/proc/_Generate()
var/targ_len = length(targets)
var/smash_len = length(smashes)
var/number = targ_len * 6 - smash_len
for(var/i in 0 to number)
var/turf/chosen_location = get_safe_random_station_turf()
//we also dont want them close to each other, at least 1 tile of seperation
var/obj/effect/reality_smash/what_if_i_have_one = locate() in range(1, chosen_location)
var/obj/effect/broken_illusion/what_if_i_had_one_but_got_used = locate() in range(1, chosen_location)
if(what_if_i_have_one || what_if_i_had_one_but_got_used) //we dont want to spawn
continue
var/obj/effect/reality_smash/RS = new/obj/effect/reality_smash(chosen_location)
smashes += RS
ReworkNetwork()
/**
* Adds a mind to the list of people that can see the reality smashes
*
* Use this whenever you want to add someone to the list
*/
/datum/reality_smash_tracker/proc/AddMind(var/datum/mind/M)
RegisterSignal(M.current,COMSIG_MOB_CLIENT_LOGIN,.proc/ReworkNetwork)
targets |= M
_Generate()
for(var/X in smashes)
var/obj/effect/reality_smash/reality_smash = X
reality_smash.AddMind(M)
/**
* Removes a mind from the list of people that can see the reality smashes
*
* Use this whenever you want to remove someone from the list
*/
/datum/reality_smash_tracker/proc/RemoveMind(var/datum/mind/M)
UnregisterSignal(M.current,COMSIG_MOB_CLIENT_LOGIN)
targets -= M
for(var/obj/effect/reality_smash/RS in smashes)
RS.RemoveMind(M)
/obj/effect/broken_illusion
name = "pierced reality"
icon = 'icons/effects/eldritch.dmi'
icon_state = "pierced_illusion"
anchored = TRUE
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
/obj/effect/broken_illusion/attack_hand(mob/living/user)
if(!ishuman(user))
return ..()
var/mob/living/carbon/human/human_user = user
if(IS_HERETIC(human_user))
to_chat(human_user,"<span class='boldwarning'>You know better than to tempt forces out of your control.</span>")
else
var/obj/item/bodypart/arm = human_user.get_active_hand()
if(prob(25))
to_chat(human_user,"<span class='userdanger'>An otherwordly presence tears your arm apart into atoms as you try to touch the hole in the very fabric of reality!</span>")
arm.dismember()
qdel(arm)
else
to_chat(human_user,"<span class='danger'>You pull your hand away from the hole as eldritch energy flails out, trying to latch onto existence itself!</span>")
/obj/effect/broken_illusion/attack_tk(mob/user)
if(!ishuman(user))
return
var/mob/living/carbon/human/human_user = user
if(IS_HERETIC(human_user))
to_chat(human_user,"<span class='boldwarning'>You know better than to tempt forces out of your control.</span>")
else
//a very elaborate way to suicide
to_chat(human_user,"<span class='userdanger'>Eldritch energy lashes out, piercing your fragile mind, tearing it to pieces!</span>")
human_user.ghostize()
var/obj/item/bodypart/head/head = locate() in human_user.bodyparts
if(head)
head.dismember()
qdel(head)
else
human_user.gib()
var/datum/effect_system/reagents_explosion/explosion = new()
explosion.set_up(1, get_turf(human_user), 1, 0)
explosion.start()
/obj/effect/broken_illusion/examine(mob/user)
if(!IS_HERETIC(user) && ishuman(user))
var/mob/living/carbon/human/human_user = user
to_chat(human_user,"<span class='userdanger'>Your brain hurts when you look at this!</span>")
human_user.adjustOrganLoss(ORGAN_SLOT_BRAIN,30)
. = ..()
/obj/effect/reality_smash
name = "/improper reality smash"
icon = 'icons/effects/eldritch.dmi'
anchored = TRUE
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
///We cannot use icon_state since this is invisible, functions the same way but with custom behaviour.
var/image_state = "reality_smash"
///Who can see us?
var/list/minds = list()
///Tracked image
var/image/img
/obj/effect/reality_smash/Initialize()
. = ..()
img = image(icon, src, image_state, OBJ_LAYER)
generate_name()
/obj/effect/reality_smash/Destroy()
on_destroy()
return ..()
///Custom effect that happens on destruction
/obj/effect/reality_smash/proc/on_destroy()
for(var/cm in minds)
var/datum/mind/cultie = cm
if(cultie.current?.client)
cultie.current.client.images -= img
//clear the list
minds -= cultie
GLOB.reality_smash_track.smashes -= src
img = null
new /obj/effect/broken_illusion(drop_location())
///Makes the mind able to see this effect
/obj/effect/reality_smash/proc/AddMind(var/datum/mind/cultie)
minds |= cultie
if(cultie.current.client)
cultie.current.client.images |= img
///Makes the mind not able to see this effect
/obj/effect/reality_smash/proc/RemoveMind(var/datum/mind/cultie)
minds -= cultie
if(cultie.current.client)
cultie.current.client.images -= img
///Generates random name
/obj/effect/reality_smash/proc/generate_name()
var/static/list/prefix = list("Omniscient","Thundering","Enlightening","Intrusive","Rejectful","Atomized","Subtle","Rising","Lowering","Fleeting","Towering","Blissful","Arrogant","Threatening","Peaceful","Aggressive")
var/static/list/postfix = list("Flaw","Presence","Crack","Heat","Cold","Memory","Reminder","Breeze","Grasp","Sight","Whisper","Flow","Touch","Veil","Thought","Imperfection","Blemish","Blush")
name = pick(prefix) + " " + pick(postfix)
@@ -0,0 +1,142 @@
/obj/item/living_heart
name = "living heart"
desc = "Link to the worlds beyond."
icon = 'icons/obj/eldritch.dmi'
icon_state = "living_heart"
w_class = WEIGHT_CLASS_SMALL
///Target
var/mob/living/carbon/human/target
/obj/item/living_heart/attack_self(mob/user)
. = ..()
if(!IS_HERETIC(user))
return
if(!target)
to_chat(user,"<span class='warning'>No target could be found. Put the living heart on the rune and use the rune to recieve a target.</span>")
return
var/dist = get_dist(user.loc,target.loc)
var/dir = get_dir(user.loc,target.loc)
switch(dist)
if(0 to 15)
to_chat(user,"<span class='warning'>[target.real_name] is near you. They are to the [dir2text(dir)] of you!</span>")
if(16 to 31)
to_chat(user,"<span class='warning'>[target.real_name] is somewhere in your vicinty. They are to the [dir2text(dir)] of you!</span>")
if(32 to 127)
to_chat(user,"<span class='warning'>[target.real_name] is far away from you. They are to the [dir2text(dir)] of you!</span>")
else
to_chat(user,"<span class='warning'>[target.real_name] is beyond our reach.</span>")
if(target.stat == DEAD)
to_chat(user,"<span class='warning'>[target.real_name] is dead. Bring them onto a transmutation rune!</span>")
/obj/item/melee/sickly_blade
name = "eldritch blade"
desc = "A sickly green crescent blade, decorated with an ornamental eye. You feel like you're being watched..."
icon = 'icons/obj/eldritch.dmi'
icon_state = "eldritch_blade"
item_state = "eldritch_blade"
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
inhand_y_dimension = 64
flags_1 = CONDUCT_1
sharpness = SHARP_EDGED
w_class = WEIGHT_CLASS_NORMAL
force = 17
throwforce = 10
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "rended")
/obj/item/melee/sickly_blade/attack(mob/living/M, mob/living/user)
if(!IS_HERETIC(user))
to_chat(user,"<span class='danger'>You feel a pulse of some alien intellect lash out at your mind!</span>")
var/mob/living/carbon/human/human_user = user
human_user.AdjustParalyzed(5 SECONDS)
return FALSE
return ..()
/obj/item/melee/sickly_blade/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
var/datum/antagonist/heretic/cultie = user.mind.has_antag_datum(/datum/antagonist/heretic)
if(!cultie || !proximity_flag)
return
var/list/knowledge = cultie.get_all_knowledge()
for(var/X in knowledge)
var/datum/eldritch_knowledge/eldritch_knowledge_datum = knowledge[X]
eldritch_knowledge_datum.on_eldritch_blade(target,user,proximity_flag,click_parameters)
/obj/item/melee/sickly_blade/rust
name = "rusted blade"
desc = "This crescent blade is decrepit, wasting to dust. Yet still it bites, catching flesh with jagged, rotten teeth."
icon_state = "rust_blade"
item_state = "rust_blade"
embedding = list("pain_mult" = 4, "embed_chance" = 75, "fall_chance" = 10, "ignore_throwspeed_threshold" = TRUE)
throwforce = 17
/obj/item/melee/sickly_blade/ash
name = "ashen blade"
desc = "Molten and unwrought, a hunk of metal warped to cinders and slag. Unmade, it aspires to be more than it is, and shears soot-filled wounds with a blunt edge."
icon_state = "ash_blade"
item_state = "ash_blade"
force = 20
/obj/item/melee/sickly_blade/flesh
name = "flesh blade"
desc = "A crescent blade born from a fleshwarped creature. Keenly aware, it seeks to spread to others the excruciations it has endured from dead origins."
icon_state = "flesh_blade"
item_state = "flesh_blade"
wound_bonus = 5
bare_wound_bonus = 15
/obj/item/clothing/neck/eldritch_amulet
name = "warm eldritch medallion"
desc = "A strange medallion. Peering through the crystalline surface, the world around you melts away. You see your own beating heart, and the pulse of a thousand others."
icon = 'icons/obj/eldritch.dmi'
icon_state = "eye_medalion"
w_class = WEIGHT_CLASS_SMALL
///What trait do we want to add upon equipiing
var/trait = TRAIT_THERMAL_VISION
/obj/item/clothing/neck/eldritch_amulet/equipped(mob/user, slot)
. = ..()
if(ishuman(user) && user.mind && slot == SLOT_NECK && IS_HERETIC(user))
ADD_TRAIT(user, trait, CLOTHING_TRAIT)
user.update_sight()
/obj/item/clothing/neck/eldritch_amulet/dropped(mob/user)
. = ..()
REMOVE_TRAIT(user, trait, CLOTHING_TRAIT)
user.update_sight()
/obj/item/clothing/neck/eldritch_amulet/piercing
name = "piercing eldritch medallion"
desc = "A strange medallion. Peering through the crystalline surface, the light refracts into new and terrifying spectrums of color. You see yourself, reflected off cascading mirrors, warped into improbable shapes."
trait = TRAIT_XRAY_VISION
/obj/item/clothing/head/hooded/cult_hoodie/eldritch
name = "ominous hood"
icon_state = "eldritch"
desc = "A torn, dust-caked hood. Strange eyes line the inside."
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
flash_protect = 2
/obj/item/clothing/suit/hooded/cultrobes/eldritch
name = "ominous armor"
desc = "A ragged, dusty set of robes. Strange eyes line the inside."
icon_state = "eldritch_armor"
item_state = "eldritch_armor"
flags_inv = HIDESHOES|HIDEJUMPSUIT
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS
allowed = list(/obj/item/melee/sickly_blade, /obj/item/forbidden_book)
hoodtype = /obj/item/clothing/head/hooded/cult_hoodie/eldritch
// slightly better than normal cult robes
armor = list("melee" = 50, "bullet" = 50, "laser" = 50,"energy" = 50, "bomb" = 35, "bio" = 20, "rad" = 0, "fire" = 20, "acid" = 20)
/obj/item/reagent_containers/glass/beaker/eldritch
name = "flask of eldritch essence"
desc = "Toxic to the close minded. Healing to those with knowledge of the beyond."
icon = 'icons/obj/eldritch.dmi'
icon_state = "eldrich_flask"
list_reagents = list(/datum/reagent/eldritch = 50)
@@ -0,0 +1,303 @@
/**
* #Eldritch Knwoledge
*
* Datum that makes eldritch cultist interesting.
*
* Eldritch knowledge aren't instantiated anywhere roundstart, and are initalized and destroyed as the round goes on.
*/
/datum/eldritch_knowledge
///Name of the knowledge
var/name = "Basic knowledge"
///Description of the knowledge
var/desc = "Basic knowledge of forbidden arts."
///What shows up
var/gain_text = ""
///Cost of knowledge in souls
var/cost = 0
///Next knowledge in the research tree
var/list/next_knowledge = list()
///What knowledge is incompatible with this. This will simply make it impossible to research knowledges that are in banned_knowledge once this gets researched.
var/list/banned_knowledge = list()
///Used with rituals, how many items this needs
var/list/required_atoms = list()
///What do we get out of this
var/list/result_atoms = list()
///What path is this on defaults to "Side"
var/route = PATH_SIDE
/datum/eldritch_knowledge/New()
. = ..()
var/list/temp_list
for(var/X in required_atoms)
var/atom/A = X
temp_list += list(typesof(A))
required_atoms = temp_list
/**
* What happens when this is assigned to an antag datum
*
* This proc is called whenever a new eldritch knowledge is added to an antag datum
*/
/datum/eldritch_knowledge/proc/on_gain(mob/user)
to_chat(user, "<span class='warning'>[gain_text]</span>")
return
/**
* What happens when you loose this
*
* This proc is called whenever antagonist looses his antag datum, put cleanup code in here
*/
/datum/eldritch_knowledge/proc/on_lose(mob/user)
return
/**
* What happens every tick
*
* This proc is called on SSprocess in eldritch cultist antag datum. SSprocess happens roughly every second
*/
/datum/eldritch_knowledge/proc/on_life(mob/user)
return
/**
* Special check for recipes
*
* If you are adding a more complex summoning or something that requires a special check that parses through all the atoms in an area override this.
*/
/datum/eldritch_knowledge/proc/recipe_snowflake_check(list/atoms,loc)
return TRUE
/**
* What happens once the recipe is succesfully finished
*
* By default this proc creates atoms from result_atoms list. Override this is you want something else to happen.
*/
/datum/eldritch_knowledge/proc/on_finished_recipe(mob/living/user,list/atoms,loc)
if(result_atoms.len == 0)
return FALSE
for(var/A in result_atoms)
new A(loc)
return TRUE
/**
* Used atom cleanup
*
* Overide this proc if you dont want ALL ATOMS to be destroyed. useful in many situations.
*/
/datum/eldritch_knowledge/proc/cleanup_atoms(list/atoms)
for(var/X in atoms)
var/atom/A = X
if(!isliving(A))
atoms -= A
qdel(A)
return
/**
* Mansus grasp act
*
* Gives addtional effects to mansus grasp spell
*/
/datum/eldritch_knowledge/proc/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
return FALSE
/**
* Sickly blade act
*
* Gives addtional effects to sickly blade weapon
*/
/datum/eldritch_knowledge/proc/on_eldritch_blade(target,user,proximity_flag,click_parameters)
return
//////////////
///Subtypes///
//////////////
/datum/eldritch_knowledge/spell
var/obj/effect/proc_holder/spell/spell_to_add
/datum/eldritch_knowledge/spell/on_gain(mob/user)
var/obj/effect/proc_holder/S = new spell_to_add
user.mind.AddSpell(S)
return ..()
/datum/eldritch_knowledge/spell/on_lose(mob/user)
user.mind.RemoveSpell(spell_to_add)
return ..()
/datum/eldritch_knowledge/curse
var/timer = 5 MINUTES
var/list/fingerprints = list()
/datum/eldritch_knowledge/curse/recipe_snowflake_check(list/atoms, loc)
fingerprints = list()
for(var/X in atoms)
var/atom/A = X
fingerprints |= A.fingerprints
listclearnulls(fingerprints)
if(fingerprints.len == 0)
return FALSE
return TRUE
/datum/eldritch_knowledge/curse/on_finished_recipe(mob/living/user,list/atoms,loc)
var/list/compiled_list = list()
for(var/H in GLOB.human_list)
var/mob/living/carbon/human/human_to_check = H
if(fingerprints[md5(human_to_check.dna.uni_identity)])
compiled_list |= human_to_check.real_name
compiled_list[human_to_check.real_name] = human_to_check
if(compiled_list.len == 0)
to_chat(user, "<span class='warning'>The items don't posses required fingerprints.</span>")
return FALSE
var/chosen_mob = input("Select the person you wish to curse","Your target") as null|anything in sortList(compiled_list, /proc/cmp_mob_realname_dsc)
if(!chosen_mob)
return FALSE
curse(compiled_list[chosen_mob])
addtimer(CALLBACK(src, .proc/uncurse, compiled_list[chosen_mob]),timer)
return TRUE
/datum/eldritch_knowledge/curse/proc/curse(mob/living/chosen_mob)
return
/datum/eldritch_knowledge/curse/proc/uncurse(mob/living/chosen_mob)
return
/datum/eldritch_knowledge/summon
//Mob to summon
var/mob/living/mob_to_summon
/datum/eldritch_knowledge/summon/on_finished_recipe(mob/living/user,list/atoms,loc)
//we need to spawn the mob first so that we can use it in pollCandidatesForMob, we will move it from nullspace down the code
var/mob/living/summoned = new mob_to_summon(loc)
message_admins("[summoned.name] is being summoned by [user.real_name] in [loc]")
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [summoned.name]", ROLE_HERETIC, null, FALSE, 100, summoned)
if(!LAZYLEN(candidates))
to_chat(user,"<span class='warning'>No ghost could be found...</span>")
qdel(summoned)
return FALSE
var/mob/dead/observer/C = pick(candidates)
log_game("[key_name_admin(C)] has taken control of ([key_name_admin(summoned)]), their master is [user.real_name]")
summoned.ghostize(FALSE)
summoned.key = C.key
summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster)
var/datum/antagonist/heretic_monster/heretic_monster = summoned.mind.has_antag_datum(/datum/antagonist/heretic_monster)
var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic)
heretic_monster.set_owner(master)
return TRUE
//Ascension knowledge
/datum/eldritch_knowledge/final
var/finished = FALSE
/datum/eldritch_knowledge/final/recipe_snowflake_check(list/atoms, loc,selected_atoms)
if(finished)
return FALSE
var/counter = 0
for(var/mob/living/carbon/human/H in atoms)
selected_atoms |= H
counter++
if(counter == 3)
return TRUE
return FALSE
/datum/eldritch_knowledge/final/on_finished_recipe( mob/living/user, list/atoms, loc)
finished = TRUE
return TRUE
/datum/eldritch_knowledge/final/cleanup_atoms(list/atoms)
. = ..()
for(var/mob/living/carbon/human/H in atoms)
atoms -= H
H.gib()
///////////////
///Base lore///
///////////////
/datum/eldritch_knowledge/spell/basic
name = "Break of Dawn"
desc = "Starts your journey in the mansus. Allows you to select a target using a living heart on a transmutation rune."
gain_text = "Gates of Mansus open up to your mind."
next_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh)
cost = 0
spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/mansus_grasp
required_atoms = list(/obj/item/living_heart)
route = "Start"
/datum/eldritch_knowledge/spell/basic/recipe_snowflake_check(list/atoms, loc)
. = ..()
for(var/obj/item/living_heart/LH in atoms)
if(!LH.target)
return TRUE
if(LH.target in atoms)
return TRUE
return FALSE
/datum/eldritch_knowledge/spell/basic/on_finished_recipe(mob/living/user, list/atoms, loc)
. = TRUE
var/mob/living/carbon/carbon_user = user
for(var/obj/item/living_heart/LH in atoms)
if(LH.target && LH.target.stat == DEAD)
to_chat(carbon_user,"<span class='danger'>Your patrons accepts your offer...</span>")
var/mob/living/carbon/human/H = LH.target
H.become_husk()
LH.target = null
var/datum/antagonist/heretic/EC = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
EC.total_sacrifices++
for(var/X in carbon_user.get_all_gear())
if(!istype(X,/obj/item/forbidden_book))
continue
var/obj/item/forbidden_book/FB = X
FB.charge++
FB.charge++
break
if(!LH.target)
var/datum/objective/A = new
A.owner = user.mind
var/datum/mind/targeted = A.find_target()//easy way, i dont feel like copy pasting that entire block of code
LH.target = targeted.current
qdel(A)
if(LH.target)
to_chat(user,"<span class='warning'>Your new target has been selected, go and sacrifice [LH.target.real_name]!</span>")
else
to_chat(user,"<span class='warning'>target could not be found for living heart.</span>")
/datum/eldritch_knowledge/spell/basic/cleanup_atoms(list/atoms)
return
/datum/eldritch_knowledge/living_heart
name = "Living Heart"
desc = "Allows you to create additional living hearts, using a heart, a pool of blood and a poppy. Living hearts when used on a transmutation rune will grant you a person to hunt and sacrifice on the rune. Every sacrifice gives you an additional charge in the book."
gain_text = "Disconnected, yet it still beats."
cost = 0
required_atoms = list(/obj/item/organ/heart,/obj/effect/decal/cleanable/blood,/obj/item/reagent_containers/food/snacks/grown/poppy)
result_atoms = list(/obj/item/living_heart)
route = "Start"
/datum/eldritch_knowledge/codex_cicatrix
name = "Codex Cicatrix"
desc = "Allows you to create a spare Codex Cicatrix if you have lost one, using a bible, human skin, a pen and a pair of eyes."
gain_text = "Their hands are at your throat, yet you see them not."
cost = 0
required_atoms = list(/obj/item/organ/eyes,/obj/item/stack/sheet/animalhide/human,/obj/item/storage/book/bible,/obj/item/pen)
result_atoms = list(/obj/item/forbidden_book)
route = "Start"
/datum/eldritch_knowledge/eldritch_blade
name = "Eldritch Blade"
desc = "Allows you to create a sickly, eldritch blade by transmuting a glass shard and a metal rod atop a transmutation rune."
gain_text = "The first step starts with sacrifice."
cost = 0
required_atoms = list(/obj/item/shard,/obj/item/stack/rods)
result_atoms = list(/obj/item/melee/sickly_blade)
route = "Start"
@@ -0,0 +1,668 @@
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash
name = "Ashen Passage"
desc = "Low range spell allowing you to pass through a few walls."
school = "transmutation"
invocation = "DULK'ES PRE'ZIMAS"
invocation_type = "whisper"
charge_max = 150
range = -1
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "ash_shift"
action_background_icon_state = "bg_ecult"
jaunt_in_time = 13
jaunt_duration = 10
jaunt_in_type = /obj/effect/temp_visual/dir_setting/ash_shift
jaunt_out_type = /obj/effect/temp_visual/dir_setting/ash_shift/out
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash/long
jaunt_duration = 50
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash/play_sound()
return
/obj/effect/temp_visual/dir_setting/ash_shift
name = "ash_shift"
icon = 'icons/mob/mob.dmi'
icon_state = "ash_shift2"
duration = 13
/obj/effect/temp_visual/dir_setting/ash_shift/out
icon_state = "ash_shift"
/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp
name = "Mansus Grasp"
desc = "Touch spell that allows you to channel the power of the Old Gods through you."
hand_path = /obj/item/melee/touch_attack/mansus_fist
school = "evocation"
charge_max = 150
clothes_req = FALSE
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "mansus_grasp"
action_background_icon_state = "bg_ecult"
/obj/item/melee/touch_attack/mansus_fist
name = "Mansus Grasp"
desc = "A sinister looking aura that distorts the flow of reality around it. Causes knockdown, major stamina damage aswell as some Brute. It gains additional beneficial effects with certain knowledges you can research."
icon_state = "disintegrate"
item_state = "disintegrate"
catchphrase = "T'IESA SIE'KTI VISATA"
/obj/item/melee/touch_attack/mansus_fist/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
if(!proximity_flag || target == user)
return
playsound(user, 'sound/items/welder.ogg', 75, TRUE)
if(ishuman(target))
var/mob/living/carbon/human/tar = target
if(tar.anti_magic_check())
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
return ..()
var/datum/mind/M = user.mind
var/datum/antagonist/heretic/cultie = M.has_antag_datum(/datum/antagonist/heretic)
var/use_charge = FALSE
if(iscarbon(target))
use_charge = TRUE
var/mob/living/carbon/C = target
C.adjustBruteLoss(15)
C.DefaultCombatKnockdown(50, override_stamdmg = 0)
C.adjustStaminaLoss(60)
var/list/knowledge = cultie.get_all_knowledge()
for(var/X in knowledge)
var/datum/eldritch_knowledge/EK = knowledge[X]
if(EK.on_mansus_grasp(target, user, proximity_flag, click_parameters))
use_charge = TRUE
if(use_charge)
return ..()
/obj/effect/proc_holder/spell/aoe_turf/rust_conversion
name = "Aggressive Spread"
desc = "Spreads rust onto nearby turfs."
school = "transmutation"
charge_max = 300 //twice as long as mansus grasp
clothes_req = FALSE
invocation = "PLI'STI MINO DOMI'KA"
invocation_type = "whisper"
range = 3
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "corrode"
action_background_icon_state = "bg_ecult"
/obj/effect/proc_holder/spell/aoe_turf/rust_conversion/cast(list/targets, mob/user = usr)
playsound(user, 'sound/items/welder.ogg', 75, TRUE)
for(var/turf/T in targets)
///What we want is the 3 tiles around the user and the tile under him to be rusted, so min(dist,1)-1 causes us to get 0 for these tiles, rest of the tiles are based on chance
var/chance = 100 - (max(get_dist(T,user),1)-1)*100/(range+1)
if(!prob(chance))
continue
T.rust_heretic_act()
/obj/effect/proc_holder/spell/aoe_turf/rust_conversion/small
name = "Rust Conversion"
desc = "Spreads rust onto nearby turfs."
range = 2
/obj/effect/proc_holder/spell/targeted/touch/blood_siphon
name = "Blood Siphon"
desc = "Touch spell that heals you while damaging the enemy, has a chance to transfer wounds between you and your enemy."
hand_path = /obj/item/melee/touch_attack/blood_siphon
school = "evocation"
charge_max = 150
clothes_req = FALSE
invocation_type = "none"
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "blood_siphon"
action_background_icon_state = "bg_ecult"
/obj/item/melee/touch_attack/blood_siphon
name = "Blood Siphon"
desc = "A sinister looking aura that distorts the flow of reality around it."
icon_state = "disintegrate"
item_state = "disintegrate"
catchphrase = "SUN'AI'KINI'MAS"
/obj/item/melee/touch_attack/blood_siphon/afterattack(atom/target, mob/user, proximity_flag, proximity)
if(!proximity_flag)
return
playsound(user, 'sound/effects/curseattack.ogg', 75, TRUE)
if(ishuman(target))
var/mob/living/carbon/human/tar = target
if(tar.anti_magic_check())
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
return ..()
var/mob/living/carbon/C2 = user
if(isliving(target))
var/mob/living/L = target
L.adjustBruteLoss(20)
C2.adjustBruteLoss(-20)
if(iscarbon(target))
var/mob/living/carbon/C1 = target
for(var/obj/item/bodypart/bodypart in C2.bodyparts)
for(var/i in bodypart.wounds)
var/datum/wound/iter_wound = i
if(prob(50))
continue
var/obj/item/bodypart/target_bodypart = locate(bodypart.type) in C1.bodyparts
if(!target_bodypart)
continue
iter_wound.remove_wound()
iter_wound.apply_wound(target_bodypart)
C1.blood_volume -= 20
if(C2.blood_volume < BLOOD_VOLUME_MAXIMUM) //we dont want to explode after all
C2.blood_volume += 20
return ..()
/obj/effect/proc_holder/spell/aimed/rust_wave
name = "Patron's Reach"
desc = "Channels energy into your gauntlet - firing it results in a wave of rust being created in it's wake."
projectile_type = /obj/item/projectile/magic/spell/rust_wave
charge_max = 350
clothes_req = FALSE
action_icon = 'icons/mob/actions/actions_ecult.dmi'
base_icon_state = "rust_wave"
action_icon_state = "rust_wave"
action_background_icon_state = "bg_ecult"
sound = 'sound/effects/curse5.ogg'
active_msg = "You extend your hand out, preparing to send out a wave of rust."
deactive_msg = "You extinguish that energy, for now..."
invocation = "RUD'ZI VAR'ZTAS"
invocation_type = "whisper"
/obj/item/projectile/magic/spell/rust_wave
name = "rust bolt"
icon_state = "eldritch_projectile"
alpha = 180
damage = 30
damage_type = TOX
nodamage = 0
hitsound = 'sound/effects/curseattack.ogg'
range = 15
/obj/item/projectile/magic/spell/rust_wave/Moved(atom/OldLoc, Dir)
. = ..()
playsound(src, 'sound/items/welder.ogg', 75, TRUE)
var/list/turflist = list()
var/turf/T1
turflist += get_turf(src)
T1 = get_step(src,turn(dir,90))
turflist += T1
turflist += get_step(T1,turn(dir,90))
T1 = get_step(src,turn(dir,-90))
turflist += T1
turflist += get_step(T1,turn(dir,-90))
for(var/X in turflist)
if(!X || prob(25))
continue
var/turf/T = X
T.rust_heretic_act()
/obj/effect/proc_holder/spell/aimed/rust_wave/short
name = "Small Patron's Reach"
projectile_type = /obj/item/projectile/magic/spell/rust_wave/short
/obj/item/projectile/magic/spell/rust_wave/short
range = 7
/obj/effect/proc_holder/spell/pointed/cleave
name = "Cleave"
desc = "Causes severe bleeding on a target and people around them"
school = "transmutation"
charge_max = 350
clothes_req = FALSE
invocation = "PLES'TI VI'RIBUS"
invocation_type = "whisper"
range = 9
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "cleave"
action_background_icon_state = "bg_ecult"
/obj/effect/proc_holder/spell/pointed/cleave/cast(list/targets, mob/user)
if(!targets.len)
to_chat(user, "<span class='warning'>No target found in range!</span>")
return FALSE
if(!can_target(targets[1], user))
return FALSE
for(var/mob/living/carbon/human/C in range(1,targets[1]))
targets |= C
for(var/X in targets)
var/mob/living/carbon/human/target = X
if(target == user)
continue
if(target.anti_magic_check())
to_chat(user, "<span class='warning'>The spell had no effect!</span>")
target.visible_message("<span class='danger'>[target]'s veins flash with fire, but their magic protection repulses the blaze!</span>", \
"<span class='danger'>Your veins flash with fire, but your magic protection repels the blaze!</span>")
continue
target.visible_message("<span class='danger'>[target]'s veins are shredded from within as an unholy blaze erupts from their blood!</span>", \
"<span class='danger'>Your veins burst from within and unholy flame erupts from your blood!</span>")
var/obj/item/bodypart/bodypart = pick(target.bodyparts)
var/datum/wound/slash/critical/crit_wound = new
crit_wound.apply_wound(bodypart)
target.adjustFireLoss(20)
new /obj/effect/temp_visual/cleave(target.drop_location())
/obj/effect/proc_holder/spell/pointed/cleave/can_target(atom/target, mob/user, silent)
. = ..()
if(!.)
return FALSE
if(!istype(target,/mob/living/carbon/human))
if(!silent)
to_chat(user, "<span class='warning'>You are unable to cleave [target]!</span>")
return FALSE
return TRUE
/obj/effect/proc_holder/spell/pointed/cleave/long
charge_max = 650
/obj/effect/proc_holder/spell/pointed/touch/mad_touch
name = "Touch of Madness"
desc = "Touch spell that drains your enemies sanity."
school = "transmutation"
charge_max = 150
clothes_req = FALSE
invocation_type = "none"
range = 2
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "mad_touch"
action_background_icon_state = "bg_ecult"
/obj/effect/proc_holder/spell/pointed/touch/mad_touch/can_target(atom/target, mob/user, silent)
. = ..()
if(!.)
return FALSE
if(!istype(target,/mob/living/carbon/human))
if(!silent)
to_chat(user, "<span class='warning'>You are unable to touch [target]!</span>")
return FALSE
return TRUE
/obj/effect/proc_holder/spell/pointed/touch/mad_touch/cast(list/targets, mob/user)
. = ..()
for(var/mob/living/carbon/target in targets)
if(ishuman(targets))
var/mob/living/carbon/human/tar = target
if(tar.anti_magic_check())
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
return
if(target.mind && !target.mind.has_antag_datum(/datum/antagonist/heretic))
to_chat(user,"<span class='warning'>[target.name] has been cursed!</span>")
SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
/obj/effect/proc_holder/spell/pointed/ash_final
name = "Nightwatcher's Rite"
desc = "Powerful spell that releases 5 streams of fire away from you."
school = "transmutation"
invocation = "IGNIS'INTI"
invocation_type = "whisper"
charge_max = 300
range = 15
clothes_req = FALSE
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "flames"
action_background_icon_state = "bg_ecult"
/obj/effect/proc_holder/spell/pointed/ash_final/cast(list/targets, mob/user)
for(var/X in targets)
var/T
T = line_target(-25, range, X, user)
INVOKE_ASYNC(src, .proc/fire_line, user,T)
T = line_target(10, range, X, user)
INVOKE_ASYNC(src, .proc/fire_line, user,T)
T = line_target(0, range, X, user)
INVOKE_ASYNC(src, .proc/fire_line, user,T)
T = line_target(-10, range, X, user)
INVOKE_ASYNC(src, .proc/fire_line, user,T)
T = line_target(25, range, X, user)
INVOKE_ASYNC(src, .proc/fire_line, user,T)
return ..()
/obj/effect/proc_holder/spell/pointed/ash_final/proc/line_target(offset, range, atom/at , atom/user)
if(!at)
return
var/angle = ATAN2(at.x - user.x, at.y - user.y) + offset
var/turf/T = get_turf(user)
for(var/i in 1 to range)
var/turf/check = locate(user.x + cos(angle) * i, user.y + sin(angle) * i, user.z)
if(!check)
break
T = check
return (getline(user, T) - get_turf(user))
/obj/effect/proc_holder/spell/pointed/ash_final/proc/fire_line(atom/source, list/turfs)
var/list/hit_list = list()
for(var/turf/T in turfs)
if(istype(T, /turf/closed))
break
for(var/mob/living/L in T.contents)
if(L.anti_magic_check())
L.visible_message("<span class='danger'>Spell bounces off of [L]!</span>","<span class='danger'>The spell bounces off of you!</span>")
continue
if(L in hit_list || L == source)
continue
hit_list += L
L.adjustFireLoss(20)
to_chat(L, "<span class='userdanger'>You're hit by [source]'s fire breath!</span>")
new /obj/effect/hotspot(T)
T.hotspot_expose(700,50,1)
// deals damage to mechs
for(var/obj/mecha/M in T.contents)
if(M in hit_list)
continue
hit_list += M
M.take_damage(45, BURN, "melee", 1)
sleep(1.5)
/obj/effect/proc_holder/spell/targeted/shapeshift/eldritch
invocation_type = "none"
clothes_req = FALSE
action_background_icon_state = "bg_ecult"
sound = 'sound/magic/enter_blood.ogg'
possible_shapes = list(/mob/living/simple_animal/mouse,\
/mob/living/simple_animal/pet/dog/corgi,\
/mob/living/simple_animal/hostile/carp,\
/mob/living/simple_animal/bot/secbot, \
/mob/living/simple_animal/pet/fox,\
/mob/living/simple_animal/pet/cat )
/obj/effect/proc_holder/spell/targeted/emplosion/eldritch
name = "Energetic Pulse"
invocation_type = "none"
clothes_req = FALSE
action_background_icon_state = "bg_ecult"
range = -1
include_user = TRUE
charge_max = 300
emp_heavy = 6
emp_light = 10
sound = 'sound/effects/lingscreech.ogg'
/obj/effect/proc_holder/spell/aoe_turf/fire_cascade
name = "Fire Cascade"
desc = "creates hot turfs around you."
school = "transmutation"
charge_max = 300 //twice as long as mansus grasp
clothes_req = FALSE
invocation = "IGNIS'SAVARIN"
invocation_type = "whisper"
range = 4
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "fire_ring"
action_background_icon_state = "bg_ecult"
/obj/effect/proc_holder/spell/aoe_turf/fire_cascade/cast(list/targets, mob/user = usr)
INVOKE_ASYNC(src, .proc/fire_cascade, user,range)
/obj/effect/proc_holder/spell/aoe_turf/fire_cascade/proc/fire_cascade(atom/centre,max_range)
playsound(get_turf(centre), 'sound/items/welder.ogg', 75, TRUE)
var/_range = 1
for(var/i = 0, i <= max_range,i++)
for(var/turf/T in spiral_range_turfs(_range,centre))
new /obj/effect/hotspot(T)
T.hotspot_expose(700,50,1)
for(var/mob/living/livies in T.contents - centre)
livies.adjustFireLoss(10)
_range++
sleep(3)
/obj/effect/proc_holder/spell/aoe_turf/fire_cascade/big
range = 6
/obj/effect/proc_holder/spell/targeted/telepathy/eldritch
invocation = ""
invocation_type = "whisper"
clothes_req = FALSE
action_background_icon_state = "bg_ecult"
/obj/effect/proc_holder/spell/targeted/fire_sworn
name = "Oath of Fire"
desc = "For a minute you will passively create a ring of fire around you."
invocation = "IGNIS'AISTRA'LISTRE"
invocation_type = "whisper"
clothes_req = FALSE
action_background_icon_state = "bg_ecult"
range = -1
include_user = TRUE
charge_max = 700
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "fire_ring"
///how long it lasts
var/duration = 1 MINUTES
///who casted it right now
var/mob/current_user
///Determines if you get the fire ring effect
var/has_fire_ring = FALSE
/obj/effect/proc_holder/spell/targeted/fire_sworn/cast(list/targets, mob/user)
. = ..()
current_user = user
has_fire_ring = TRUE
addtimer(CALLBACK(src, .proc/remove, user), duration, TIMER_OVERRIDE|TIMER_UNIQUE)
/obj/effect/proc_holder/spell/targeted/fire_sworn/proc/remove()
has_fire_ring = FALSE
/obj/effect/proc_holder/spell/targeted/fire_sworn/process()
. = ..()
if(!has_fire_ring)
return
for(var/turf/T in range(1,current_user))
new /obj/effect/hotspot(T)
T.hotspot_expose(700,50,1)
for(var/mob/living/livies in T.contents - current_user)
livies.adjustFireLoss(5)
/obj/effect/proc_holder/spell/targeted/worm_contract
name = "Force Contract"
desc = "Forces all the worm parts to collapse onto a single turf"
invocation_type = "none"
clothes_req = FALSE
action_background_icon_state = "bg_ecult"
range = -1
include_user = TRUE
charge_max = 300
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "worm_contract"
/obj/effect/proc_holder/spell/targeted/worm_contract/cast(list/targets, mob/user)
. = ..()
if(!istype(user,/mob/living/simple_animal/hostile/eldritch/armsy))
to_chat(user, "<span class='userdanger'>You try to contract your muscles but nothing happens...</span>")
var/mob/living/simple_animal/hostile/eldritch/armsy/armsy = user
armsy.contract_next_chain_into_single_tile()
/obj/effect/temp_visual/cleave
icon = 'icons/effects/eldritch.dmi'
icon_state = "cleave"
duration = 6
/obj/effect/temp_visual/eldritch_smoke
icon = 'icons/effects/eldritch.dmi'
icon_state = "smoke"
duration = 10
/obj/effect/proc_holder/spell/targeted/fiery_rebirth
name = "Nightwatcher's Rebirth"
desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a person is in critical condition it finishes them off."
invocation = "PETHRO'MINO'IGNI"
invocation_type = "whisper"
clothes_req = FALSE
action_background_icon_state = "bg_ecult"
range = -1
include_user = TRUE
charge_max = 600
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "smoke"
/obj/effect/proc_holder/spell/targeted/fiery_rebirth/cast(list/targets, mob/user)
if(!ishuman(user))
return
var/mob/living/carbon/human/human_user = user
for(var/mob/living/carbon/target in view(7,user))
if(target.stat == DEAD || !target.on_fire)
continue
//This is essentially a death mark, use this to finish your opponent quicker.
if(target.InCritical())
target.death()
target.adjustFireLoss(20)
new /obj/effect/temp_visual/eldritch_smoke(target.drop_location())
human_user.ExtinguishMob()
human_user.adjustBruteLoss(-10, FALSE)
human_user.adjustFireLoss(-10, FALSE)
human_user.adjustStaminaLoss(-10, FALSE)
human_user.adjustToxLoss(-10, FALSE)
human_user.adjustOxyLoss(-10)
/obj/effect/proc_holder/spell/pointed/manse_link
name = "Mansus Link"
desc = "Piercing through reality, connecting minds. This spell allows you to add people to a mansus net, allowing them to communicate with eachother"
school = "transmutation"
charge_max = 300
clothes_req = FALSE
invocation = "SUSEI' METO MIN'TIS"
invocation_type = "whisper"
range = 10
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "mansus_link"
action_background_icon_state = "bg_ecult"
/obj/effect/proc_holder/spell/pointed/manse_link/can_target(atom/target, mob/user, silent)
if(!isliving(target))
return FALSE
return TRUE
/obj/effect/proc_holder/spell/pointed/manse_link/cast(list/targets, mob/user)
var/mob/living/simple_animal/hostile/eldritch/raw_prophet/originator = user
var/mob/living/target = targets[1]
to_chat(originator, "<span class='notice'>You begin linking [target]'s mind to yours...</span>")
to_chat(target, "<span class='warning'>You feel your mind being pulled... connected... intertwined with the very fabric of reality...</span>")
if(!do_after(originator, 6 SECONDS, target))
return
if(!originator.link_mob(target))
to_chat(originator, "<span class='warning'>You can't seem to link [target]'s mind...</span>")
to_chat(target, "<span class='warning'>The foreign presence leaves your mind.</span>")
return
to_chat(originator, "<span class='notice'>You connect [target]'s mind to your mansus link!</span>")
/datum/action/innate/mansus_speech
name = "Mansus Link"
desc = "Send a psychic message to everyone connected to your mansus link."
button_icon_state = "link_speech"
icon_icon = 'icons/mob/actions/actions_slime.dmi'
background_icon_state = "bg_ecult"
var/mob/living/simple_animal/hostile/eldritch/raw_prophet/originator
/datum/action/innate/mansus_speech/New(_originator)
. = ..()
originator = _originator
/datum/action/innate/mansus_speech/Activate()
var/mob/living/living_owner = owner
if(!originator?.linked_mobs[living_owner])
CRASH("Uh oh the mansus link got somehow activated without it being linked to a raw prophet or the mob not being in a list of mobs that should be able to do it.")
var/message = sanitize(input("Message:", "Telepathy from the Manse") as text|null)
if(QDELETED(living_owner))
return
if(!originator?.linked_mobs[living_owner])
to_chat(living_owner, "<span class='warning'>The link seems to have been severed...</span>")
Remove(living_owner)
return
if(message)
var/msg = "<i><font color=#568b00>\[Mansus Link\] <b>[living_owner]:</b> [message]</font></i>"
log_directed_talk(living_owner, originator, msg, LOG_SAY, "Mansus Link")
to_chat(originator.linked_mobs, msg)
for(var/dead_mob in GLOB.dead_mob_list)
var/link = FOLLOW_LINK(dead_mob, living_owner)
to_chat(dead_mob, "[link] [msg]")
/obj/effect/proc_holder/spell/pointed/trigger/blind/eldritch
range = 10
invocation = "AK'LIS"
action_background_icon_state = "bg_ecult"
/obj/effect/temp_visual/dir_setting/entropic
icon = 'icons/effects/160x160.dmi'
icon_state = "entropic_plume"
duration = 3 SECONDS
/obj/effect/temp_visual/dir_setting/entropic/setDir(dir)
. = ..()
switch(dir)
if(NORTH)
pixel_x = -64
if(SOUTH)
pixel_x = -64
pixel_y = -128
if(EAST)
pixel_y = -64
if(WEST)
pixel_y = -64
pixel_x = -128
/obj/effect/temp_visual/glowing_rune
icon = 'icons/effects/eldritch.dmi'
icon_state = "small_rune_1"
duration = 1 MINUTES
layer = LOW_SIGIL_LAYER
/obj/effect/temp_visual/glowing_rune/Initialize()
. = ..()
pixel_y = rand(-6,6)
pixel_x = rand(-6,6)
icon_state = "small_rune_[rand(12)]"
update_icon()
/obj/effect/proc_holder/spell/cone/staggered/entropic_plume
name = "Entropic Plume"
desc = "Spews forth a disorienting plume that causes enemies to strike each other, briefly blinds them(increasing with range) and poisons them(decreasing with range). Also spreads rust in the path of the plume."
school = "illusion"
invocation = "RU'KAS NU'DYTI"
invocation_type = "whisper"
clothes_req = FALSE
action_background_icon_state = "bg_ecult"
action_icon = 'icons/mob/actions/actions_ecult.dmi'
action_icon_state = "entropic_plume"
charge_max = 300
cone_levels = 5
respect_density = TRUE
/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/cast(list/targets,mob/user = usr)
. = ..()
new /obj/effect/temp_visual/dir_setting/entropic(get_step(user,user.dir), user.dir)
/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/do_turf_cone_effect(turf/target_turf, level)
. = ..()
target_turf.rust_heretic_act()
/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/do_mob_cone_effect(mob/living/victim, level)
. = ..()
if(victim.anti_magic_check() || IS_HERETIC(victim) || victim.mind?.has_antag_datum(/datum/antagonist/heretic_monster))
return
victim.apply_status_effect(STATUS_EFFECT_AMOK)
victim.apply_status_effect(STATUS_EFFECT_CLOUDSTRUCK, (level*10))
if(iscarbon(victim))
var/mob/living/carbon/carbon_victim = victim
carbon_victim.reagents.add_reagent(/datum/reagent/eldritch, min(1, 6-level))
/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/calculate_cone_shape(current_level)
if(current_level == cone_levels)
return 5
else if(current_level == cone_levels-1)
return 3
else
return 2
@@ -0,0 +1,43 @@
///Tracking reasons
/datum/antagonist/heretic_monster
name = "Eldritch Horror"
roundend_category = "Heretics"
antagpanel_category = "Heretic Beast"
antag_moodlet = /datum/mood_event/heretics
job_rank = ROLE_HERETIC
antag_hud_type = ANTAG_HUD_HERETIC
antag_hud_name = "heretic_beast"
var/datum/antagonist/master
/datum/antagonist/heretic_monster/admin_add(datum/mind/new_owner,mob/admin)
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has heresized [key_name_admin(new_owner)].")
log_admin("[key_name(admin)] has heresized [key_name(new_owner)].")
/datum/antagonist/heretic_monster/greet()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ecult_op.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change
to_chat(owner, "<span class='boldannounce'>You became an Eldritch Horror!</span>")
/datum/antagonist/heretic_monster/on_removal()
if(owner)
to_chat(owner, "<span class='boldannounce'>Your master is no longer [master.owner.current.real_name]</span>")
owner = null
return ..()
/datum/antagonist/heretic_monster/proc/set_owner(datum/antagonist/_master)
master = _master
var/datum/objective/master_obj = new
master_obj.owner = src
master_obj.explanation_text = "Assist your master in any way you can!"
objectives += master_obj
owner.announce_objectives()
to_chat(owner, "<span class='boldannounce'>Your master is [master.owner.current.real_name]</span>")
return
/datum/antagonist/heretic_monster/apply_innate_effects(mob/living/mob_override)
. = ..()
add_antag_hud(antag_hud_type, antag_hud_name, owner.current)
/datum/antagonist/heretic_monster/remove_innate_effects(mob/living/mob_override)
. = ..()
remove_antag_hud(antag_hud_type, owner.current)
@@ -0,0 +1,183 @@
/datum/eldritch_knowledge/base_ash
name = "Nightwatcher's Secret"
desc = "Inducts you into the Path of Ash. Allows you to transmute a match with an eldritch blade into an ashen blade."
gain_text = "The City guard knows their watch. If you ask them at night they may tell you about the ashy lantern."
banned_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/final/rust_final,/datum/eldritch_knowledge/final/flesh_final)
next_knowledge = list(/datum/eldritch_knowledge/ashen_grasp)
required_atoms = list(/obj/item/melee/sickly_blade,/obj/item/match)
result_atoms = list(/obj/item/melee/sickly_blade/ash)
cost = 1
route = PATH_ASH
/datum/eldritch_knowledge/spell/ashen_shift
name = "Ashen Shift"
gain_text = "Ash is all the same, how can one man master it all?"
desc = "A short range jaunt that will enable you to escape from danger."
cost = 1
spell_to_add = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash
next_knowledge = list(/datum/eldritch_knowledge/ash_mark,/datum/eldritch_knowledge/essence,/datum/eldritch_knowledge/ashen_eyes)
route = PATH_ASH
/datum/eldritch_knowledge/ashen_grasp
name = "Grasp of Ash"
gain_text = "Gates have opened, minds have flooded, yet I remain."
desc = "Empowers your mansus grasp to knock enemies down and throw them away."
cost = 1
next_knowledge = list(/datum/eldritch_knowledge/spell/ashen_shift)
route = PATH_ASH
/datum/eldritch_knowledge/ashen_grasp/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
if(!iscarbon(target))
return
var/mob/living/carbon/C = target
var/datum/status_effect/eldritch/E = C.has_status_effect(/datum/status_effect/eldritch/rust) || C.has_status_effect(/datum/status_effect/eldritch/ash) || C.has_status_effect(/datum/status_effect/eldritch/flesh)
if(E)
. = TRUE
E.on_effect()
for(var/X in user.mind.spell_list)
if(!istype(X,/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp))
continue
var/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp/MG = X
MG.charge_counter = min(round(MG.charge_counter + MG.charge_max * 0.75),MG.charge_max) // refunds 75% of charge.
var/atom/throw_target = get_edge_target_turf(C, user.dir)
if(!C.anchored)
. = TRUE
C.throw_at(throw_target, rand(4,8), 14, user)
return
/datum/eldritch_knowledge/ashen_eyes
name = "Ashen Eyes"
gain_text = "Piercing eyes may guide me through the mundane."
desc = "Allows you to craft thermal vision amulet by transmutating eyes with a glass shard."
cost = 1
next_knowledge = list(/datum/eldritch_knowledge/spell/ashen_shift,/datum/eldritch_knowledge/flesh_ghoul)
required_atoms = list(/obj/item/organ/eyes,/obj/item/shard)
result_atoms = list(/obj/item/clothing/neck/eldritch_amulet)
/datum/eldritch_knowledge/ash_mark
name = "Mark of Ash"
gain_text = "Spread the famine."
desc = "Your sickly blade now applies ash mark on hit. Use your mansus grasp to proc the mark. Mark of Ash causes stamina damage, and fire loss, and spreads to a nearby carbon. Damage decreases with how many times the mark has spread."
cost = 2
next_knowledge = list(/datum/eldritch_knowledge/curse/blindness)
banned_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/flesh_mark)
route = PATH_ASH
/datum/eldritch_knowledge/ash_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters)
. = ..()
if(isliving(target))
var/mob/living/living_target = target
living_target.apply_status_effect(/datum/status_effect/eldritch/ash,5)
/datum/eldritch_knowledge/curse/blindness
name = "Curse of Blindness"
gain_text = "The blind man walks through the world, unnoticed by the masses."
desc = "Curse someone with 2 minutes of complete blindness by sacrificing a pair of eyes, a screwdriver and a pool of blood, with an object that the victim has touched with their bare hands."
cost = 1
required_atoms = list(/obj/item/organ/eyes,/obj/item/screwdriver,/obj/effect/decal/cleanable/blood)
next_knowledge = list(/datum/eldritch_knowledge/curse/corrosion,/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/curse/paralysis)
timer = 2 MINUTES
route = PATH_ASH
/datum/eldritch_knowledge/curse/blindness/curse(mob/living/chosen_mob)
. = ..()
chosen_mob.become_blind(MAGIC_TRAIT)
/datum/eldritch_knowledge/curse/blindness/uncurse(mob/living/chosen_mob)
. = ..()
chosen_mob.cure_blind(MAGIC_TRAIT)
/datum/eldritch_knowledge/spell/flame_birth
name = "Fiery Rebirth"
gain_text = "Nightwatcher was a man of principles, and yet he arose from the chaos he vowed to protect from."
desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a person is in critical condition it finishes them off."
cost = 1
spell_to_add = /obj/effect/proc_holder/spell/targeted/fiery_rebirth
next_knowledge = list(/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/final/ash_final)
route = PATH_ASH
/datum/eldritch_knowledge/ash_blade_upgrade
name = "Blazing Steel"
gain_text = "May the sun burn the heretics."
desc = "Your blade of choice will now add firestacks."
cost = 2
next_knowledge = list(/datum/eldritch_knowledge/spell/flame_birth)
banned_knowledge = list(/datum/eldritch_knowledge/rust_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade)
route = PATH_ASH
/datum/eldritch_knowledge/ash_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters)
. = ..()
if(iscarbon(target))
var/mob/living/carbon/C = target
C.adjust_fire_stacks(1)
C.IgniteMob()
/datum/eldritch_knowledge/curse/corrosion
name = "Curse of Corrosion"
gain_text = "Cursed land, cursed man, cursed mind."
desc = "Curse someone for 2 minutes of vomiting and major organ damage. Using a wirecutter, a spill of blood, a heart, left arm and a right arm, and an item that the victim touched with their bare hands."
cost = 1
required_atoms = list(/obj/item/wirecutters,/obj/effect/decal/cleanable/blood,/obj/item/organ/heart,/obj/item/bodypart/l_arm,/obj/item/bodypart/r_arm)
next_knowledge = list(/datum/eldritch_knowledge/curse/blindness,/datum/eldritch_knowledge/spell/area_conversion)
timer = 2 MINUTES
/datum/eldritch_knowledge/curse/corrosion/curse(mob/living/chosen_mob)
. = ..()
chosen_mob.apply_status_effect(/datum/status_effect/corrosion_curse)
/datum/eldritch_knowledge/curse/corrosion/uncurse(mob/living/chosen_mob)
. = ..()
chosen_mob.remove_status_effect(/datum/status_effect/corrosion_curse)
/datum/eldritch_knowledge/curse/paralysis
name = "Curse of Paralysis"
gain_text = "Corrupt their flesh, make them bleed."
desc = "Curse someone for 5 minutes of inability to walk. Using a knife, pool of blood, left leg, right leg, a hatchet and an item that the victim touched with their bare hands. "
cost = 1
required_atoms = list(/obj/item/kitchen/knife,/obj/effect/decal/cleanable/blood,/obj/item/bodypart/l_leg,/obj/item/bodypart/r_leg,/obj/item/hatchet)
next_knowledge = list(/datum/eldritch_knowledge/curse/blindness,/datum/eldritch_knowledge/summon/raw_prophet)
timer = 5 MINUTES
/datum/eldritch_knowledge/curse/paralysis/curse(mob/living/chosen_mob)
. = ..()
ADD_TRAIT(chosen_mob,TRAIT_PARALYSIS_L_LEG,MAGIC_TRAIT)
ADD_TRAIT(chosen_mob,TRAIT_PARALYSIS_R_LEG,MAGIC_TRAIT)
chosen_mob.update_mobility()
/datum/eldritch_knowledge/curse/paralysis/uncurse(mob/living/chosen_mob)
. = ..()
REMOVE_TRAIT(chosen_mob,TRAIT_PARALYSIS_L_LEG,MAGIC_TRAIT)
REMOVE_TRAIT(chosen_mob,TRAIT_PARALYSIS_R_LEG,MAGIC_TRAIT)
chosen_mob.update_mobility()
/datum/eldritch_knowledge/spell/cleave
name = "Blood Cleave"
gain_text = "At first I was unfamiliar with these instruments of war, but the priest told me how to use them."
desc = "Grants a spell that will inflict wounds and bleeding upon the target, as well as in a short radius around them."
cost = 1
spell_to_add = /obj/effect/proc_holder/spell/pointed/cleave
next_knowledge = list(/datum/eldritch_knowledge/spell/entropic_plume,/datum/eldritch_knowledge/spell/flame_birth)
/datum/eldritch_knowledge/final/ash_final
name = "Ashlord's Rite"
gain_text = "The forgotten lords have spoken! The Lord of Ash has come! Fear the flame!"
desc = "Bring three corpses onto a transmutation rune, after ascending you will become immune to fire, space, temperature and other environmental hazards. You will develop resistance to all other damages. You will be granted two spells, one which can bring forth a cascade of massive fire, and another which will surround your body in precious flames for a minute."
required_atoms = list(/mob/living/carbon/human)
cost = 5
route = PATH_ASH
var/list/trait_list = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOFIRE,TRAIT_RADIMMUNE,TRAIT_GENELESS,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_BOMBIMMUNE)
/datum/eldritch_knowledge/final/ash_final/on_finished_recipe(mob/living/user, list/atoms, loc)
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the blaze, for Ashbringer [user.real_name] has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/fire_cascade/big)
user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/fire_sworn)
var/mob/living/carbon/human/H = user
H.physiology.brute_mod *= 0.5
H.physiology.burn_mod *= 0.5
var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic)
ascension.ascended = TRUE
for(var/X in trait_list)
ADD_TRAIT(user,X,MAGIC_TRAIT)
return ..()
@@ -0,0 +1,252 @@
/datum/eldritch_knowledge/base_flesh
name = "Principle of Hunger"
desc = "Inducts you into the Path of Flesh. Allows you to transmute a pool of blood with your eldritch blade into a Blade of Flesh."
gain_text = "Hundred's of us starved, but I.. I found the strength in my greed."
banned_knowledge = list(/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/final/ash_final,/datum/eldritch_knowledge/final/rust_final)
next_knowledge = list(/datum/eldritch_knowledge/flesh_grasp)
required_atoms = list(/obj/item/melee/sickly_blade,/obj/effect/decal/cleanable/blood)
result_atoms = list(/obj/item/melee/sickly_blade/flesh)
cost = 1
route = PATH_FLESH
/datum/eldritch_knowledge/flesh_ghoul
name = "Imperfect Ritual"
desc = "Allows you to resurrect the dead as voiceless dead by sacrificing them on the transmutation rune with a poppy. Voiceless dead are mute and have 50 HP. You can only have 2 at a time."
gain_text = "I found notes... notes of a ritual, scraps, unfinished, and yet... I still did it."
cost = 1
required_atoms = list(/mob/living/carbon/human,/obj/item/reagent_containers/food/snacks/grown/poppy)
next_knowledge = list(/datum/eldritch_knowledge/flesh_mark,/datum/eldritch_knowledge/armor,/datum/eldritch_knowledge/ashen_eyes)
route = PATH_FLESH
var/max_amt = 2
var/current_amt = 0
var/list/ghouls = list()
/datum/eldritch_knowledge/flesh_ghoul/on_finished_recipe(mob/living/user,list/atoms,loc)
var/mob/living/carbon/human/humie = locate() in atoms
if(QDELETED(humie) || humie.stat != DEAD)
return
if(length(ghouls) >= max_amt)
return
if(HAS_TRAIT(humie,TRAIT_HUSK))
return
humie.grab_ghost()
if(!humie.mind || !humie.client)
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [humie.real_name], a voiceless dead.", ROLE_HERETIC, null, ROLE_HERETIC, 50,humie)
if(!LAZYLEN(candidates))
return
var/mob/dead/observer/C = pick(candidates)
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(humie)]) to replace an AFK player.")
humie.ghostize(0)
humie.key = C.key
ADD_TRAIT(humie,TRAIT_MUTE,MAGIC_TRAIT)
log_game("[key_name_admin(humie)] has become a voiceless dead, their master is [user.real_name]")
humie.revive(full_heal = TRUE, admin_revive = TRUE)
humie.setMaxHealth(75)
humie.health = 75 // Voiceless dead are much tougher than ghouls
humie.become_husk()
humie.faction |= "heretics"
var/datum/antagonist/heretic_monster/heretic_monster = humie.mind.add_antag_datum(/datum/antagonist/heretic_monster)
var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic)
heretic_monster.set_owner(master)
atoms -= humie
RegisterSignal(humie,COMSIG_MOB_DEATH,.proc/remove_ghoul)
ghouls += humie
/datum/eldritch_knowledge/flesh_ghoul/proc/remove_ghoul(datum/source)
var/mob/living/carbon/human/humie = source
ghouls -= humie
humie.mind.remove_antag_datum(/datum/antagonist/heretic_monster)
UnregisterSignal(source,COMSIG_MOB_DEATH)
/datum/eldritch_knowledge/flesh_grasp
name = "Grasp of Flesh"
gain_text = "'My newfound desire, it drove me to do great things,' The Priest said."
desc = "Empowers your Mansus Grasp to be able to create a single ghoul out of a dead player. You cannot raise the same person twice. Ghouls have only 50 HP and look like husks."
cost = 1
next_knowledge = list(/datum/eldritch_knowledge/flesh_ghoul)
var/ghoul_amt = 6
var/list/spooky_scaries
route = PATH_FLESH
/datum/eldritch_knowledge/flesh_grasp/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
if(!ishuman(target) || target == user)
return
var/mob/living/carbon/human/human_target = target
var/datum/status_effect/eldritch/eldritch_effect = human_target.has_status_effect(/datum/status_effect/eldritch/rust) || human_target.has_status_effect(/datum/status_effect/eldritch/ash) || human_target.has_status_effect(/datum/status_effect/eldritch/flesh)
if(eldritch_effect)
. = TRUE
eldritch_effect.on_effect()
if(iscarbon(target))
var/mob/living/carbon/carbon_target = target
var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts)
var/datum/wound/slash/severe/crit_wound = new
crit_wound.apply_wound(bodypart)
if(QDELETED(human_target) || human_target.stat != DEAD)
return
human_target.grab_ghost()
if(!human_target.mind || !human_target.client)
to_chat(user, "<span class='warning'>There is no soul connected to this body...</span>")
return
if(HAS_TRAIT(human_target, TRAIT_HUSK))
to_chat(user, "<span class='warning'>You cannot revive a dead ghoul!</span>")
return
if(LAZYLEN(spooky_scaries) >= ghoul_amt)
to_chat(user, "<span class='warning'>Your patron cannot support more ghouls on this plane!</span>")
return
LAZYADD(spooky_scaries, human_target)
log_game("[key_name_admin(human_target)] has become a ghoul, their master is [user.real_name]")
//we change it to true only after we know they passed all the checks
. = TRUE
RegisterSignal(human_target,COMSIG_MOB_DEATH,.proc/remove_ghoul)
human_target.revive(full_heal = TRUE, admin_revive = TRUE)
human_target.setMaxHealth(40)
human_target.health = 40
human_target.become_husk()
human_target.faction |= "heretics"
var/datum/antagonist/heretic_monster/heretic_monster = human_target.mind.add_antag_datum(/datum/antagonist/heretic_monster)
var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic)
heretic_monster.set_owner(master)
return
/datum/eldritch_knowledge/flesh_grasp/proc/remove_ghoul(datum/source)
var/mob/living/carbon/human/humie = source
spooky_scaries -= humie
humie.mind.remove_antag_datum(/datum/antagonist/heretic_monster)
UnregisterSignal(source, COMSIG_MOB_DEATH)
/datum/eldritch_knowledge/flesh_mark
name = "Mark of Flesh"
gain_text = "I saw them, the marked ones. The screams... the silence."
desc = "Your sickly blade now applies a mark of flesh to those cut by it. Once marked, using your Mansus Grasp upon them will cause additional bleeding from the target."
cost = 2
next_knowledge = list(/datum/eldritch_knowledge/summon/raw_prophet)
banned_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/ash_mark)
route = PATH_FLESH
/datum/eldritch_knowledge/flesh_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters)
. = ..()
if(isliving(target))
var/mob/living/living_target = target
living_target.apply_status_effect(/datum/status_effect/eldritch/flesh)
/datum/eldritch_knowledge/flesh_blade_upgrade
name = "Bleeding Steel"
gain_text = "It rained blood, that's when I understood the gravekeeper's advice."
desc = "Your blade will now cause additional bleeding to those hit by it."
cost = 2
next_knowledge = list(/datum/eldritch_knowledge/summon/stalker)
banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/rust_blade_upgrade)
route = PATH_FLESH
/datum/eldritch_knowledge/flesh_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters)
. = ..()
if(iscarbon(target))
var/mob/living/carbon/carbon_target = target
var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts)
var/datum/wound/slash/severe/crit_wound = new
crit_wound.apply_wound(bodypart)
/datum/eldritch_knowledge/summon/raw_prophet
name = "Raw Ritual"
gain_text = "The uncanny man walks alone in the valley, I was able to call his aid."
desc = "You can now summon a Raw Prophet using eyes, a left arm, right arm and a pool of blood using a transmutation circle. Raw prophets have increased seeing range, and can see through walls. They can jaunt long distances, though they are fragile."
cost = 1
required_atoms = list(/obj/item/organ/eyes,/obj/item/bodypart/l_arm,/obj/item/bodypart/r_arm,/obj/effect/decal/cleanable/blood)
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/raw_prophet
next_knowledge = list(/datum/eldritch_knowledge/flesh_blade_upgrade,/datum/eldritch_knowledge/spell/blood_siphon,/datum/eldritch_knowledge/curse/paralysis)
route = PATH_FLESH
/datum/eldritch_knowledge/summon/stalker
name = "Lonely Ritual"
gain_text = "I was able to combine my greed and desires to summon an eldritch beast I have not seen before."
desc = "You can now summon a Stalker using a knife, a flower, a pen and a piece of paper using a transmutation circle. Stalkers possess the ability to shapeshift into various forms while assuming the vigor and powers of that form."
cost = 1
required_atoms = list(/obj/item/kitchen/knife,/obj/item/reagent_containers/food/snacks/grown/poppy,/obj/item/pen,/obj/item/paper)
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/stalker
next_knowledge = list(/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/summon/rusty,/datum/eldritch_knowledge/final/flesh_final)
route = PATH_FLESH
/datum/eldritch_knowledge/summon/ashy
name = "Ashen Ritual"
gain_text = "I combined principle of hunger with desire of destruction. The eyeful lords have noticed me."
desc = "You can now summon an Ashen One by transmuting a pile of ash, a head and a book using a transmutation circle. They possess the ability to jaunt short distances and create a cascade of flames."
cost = 1
required_atoms = list(/obj/effect/decal/cleanable/ash,/obj/item/bodypart/head,/obj/item/book)
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/ash_spirit
next_knowledge = list(/datum/eldritch_knowledge/summon/stalker,/datum/eldritch_knowledge/spell/flame_birth)
/datum/eldritch_knowledge/summon/rusty
name = "Rusted Ritual"
gain_text = "I combined principle of hunger with desire of corruption. The rusted hills call my name."
desc = "You can now summon a Rust Walker transmuting a vomit pool, a head, and a book using a transmutation circle. Rust Walkers possess the ability to spread rust and can fire bolts of rust to further corrode the area."
cost = 1
required_atoms = list(/obj/effect/decal/cleanable/vomit,/obj/item/bodypart/head,/obj/item/book)
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/rust_spirit
next_knowledge = list(/datum/eldritch_knowledge/summon/stalker,/datum/eldritch_knowledge/spell/entropic_plume)
/datum/eldritch_knowledge/spell/blood_siphon
name = "Blood Siphon"
gain_text = "Our blood is all the same after all, the owl told me."
desc = "You are granted a spell that drains some of the targets health, and returns it to you. It also has a chance to transfer any wounds you possess onto the target."
cost = 1
spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/blood_siphon
next_knowledge = list(/datum/eldritch_knowledge/summon/raw_prophet,/datum/eldritch_knowledge/spell/area_conversion)
/datum/eldritch_knowledge/final/flesh_final
name = "Priest's Final Hymn"
gain_text = "Man of this world. Hear me! For the time of the lord of arms has come!"
desc = "Bring three corpses to a transmutation rune to either ascend as The Lord of the Night or summon a single Terror of the Night, however you cannot ascend more than once."
required_atoms = list(/mob/living/carbon/human)
cost = 5
route = PATH_FLESH
/datum/eldritch_knowledge/final/flesh_final/on_finished_recipe(mob/living/user, list/atoms, loc)
var/alert_ = alert(user,"Do you want to ascend as the lord of the night or just summon a terror of the night?","...","Yes","No")
user.SetImmobilized(10 HOURS) // no way someone will stand 10 hours in a spot, just so he can move while the alert is still showing.
switch(alert_)
if("No")
var/mob/living/summoned = new /mob/living/simple_animal/hostile/eldritch/armsy(loc)
message_admins("[summoned.name] is being summoned by [user.real_name] in [loc]")
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [summoned.real_name]", ROLE_HERETIC, null, ROLE_HERETIC, 100,summoned)
user.SetImmobilized(0)
if(LAZYLEN(candidates) == 0)
to_chat(user,"<span class='warning'>No ghost could be found...</span>")
qdel(summoned)
return FALSE
var/mob/dead/observer/ghost_candidate = pick(candidates)
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the dark, for vassal of arms has ascended! Terror of the night has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
log_game("[key_name_admin(ghost_candidate)] has taken control of ([key_name_admin(summoned)]).")
summoned.ghostize(FALSE)
summoned.key = ghost_candidate.key
summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster)
var/datum/antagonist/heretic_monster/monster = summoned.mind.has_antag_datum(/datum/antagonist/heretic_monster)
var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic)
monster.set_owner(master)
master.ascended = TRUE
if("Yes")
var/mob/living/summoned = new /mob/living/simple_animal/hostile/eldritch/armsy/prime(loc,TRUE,10)
summoned.ghostize(0)
user.SetImmobilized(0)
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the dark, for king of arms has ascended! Lord of the night has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
log_game("[user.real_name] ascended as [summoned.real_name]")
var/mob/living/carbon/carbon_user = user
var/datum/antagonist/heretic/ascension = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
ascension.ascended = TRUE
carbon_user.mind.transfer_to(summoned, TRUE)
carbon_user.gib()
return ..()
@@ -0,0 +1,209 @@
/datum/eldritch_knowledge/base_rust
name = "Blacksmith's Tale"
desc = "Inducts you into the Path of Rust. Allows you to transmute an eldritch blade with any trash item into a Blade of Rust."
gain_text = "'Let me tell you a story,' The Blacksmith said as he gazed into his rusty blade."
banned_knowledge = list(/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/final/ash_final,/datum/eldritch_knowledge/final/flesh_final)
next_knowledge = list(/datum/eldritch_knowledge/rust_fist)
required_atoms = list(/obj/item/melee/sickly_blade,/obj/item/trash)
result_atoms = list(/obj/item/melee/sickly_blade/rust)
cost = 1
route = PATH_RUST
/datum/eldritch_knowledge/rust_fist
name = "Grasp of Rust"
desc = "Empowers your Mansus Grasp to deal 500 damage to non-living matter and rust any structure it touches. Destroys already rusted structures."
gain_text = "Rust grows on the ceiling of the mansus."
cost = 1
next_knowledge = list(/datum/eldritch_knowledge/rust_regen)
var/rust_force = 500
var/static/list/blacklisted_turfs = typecacheof(list(/turf/closed,/turf/open/space,/turf/open/lava,/turf/open/chasm,/turf/open/floor/plating/rust))
route = PATH_RUST
/datum/eldritch_knowledge/rust_fist/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
if(ishuman(target))
var/mob/living/carbon/human/H = target
var/datum/status_effect/eldritch/E = H.has_status_effect(/datum/status_effect/eldritch/rust) || H.has_status_effect(/datum/status_effect/eldritch/ash) || H.has_status_effect(/datum/status_effect/eldritch/flesh)
if(E)
E.on_effect()
H.adjustOrganLoss(pick(ORGAN_SLOT_BRAIN,ORGAN_SLOT_EARS,ORGAN_SLOT_EYES,ORGAN_SLOT_LIVER,ORGAN_SLOT_LUNGS,ORGAN_SLOT_STOMACH,ORGAN_SLOT_HEART),25)
target.rust_heretic_act()
target.emp_act(EMP_HEAVY)
return TRUE
/datum/eldritch_knowledge/spell/area_conversion
name = "Aggressive Spread"
desc = "Spreads rust to nearby turfs. Destroys already rusted walls."
gain_text = "All wise men know not to touch the bound king."
cost = 1
spell_to_add = /obj/effect/proc_holder/spell/aoe_turf/rust_conversion
next_knowledge = list(/datum/eldritch_knowledge/rust_blade_upgrade,/datum/eldritch_knowledge/curse/corrosion,/datum/eldritch_knowledge/spell/blood_siphon,/datum/eldritch_knowledge/spell/rust_wave)
route = PATH_RUST
/datum/eldritch_knowledge/spell/rust_wave
name = "Patron's Reach"
desc = "You can now send a bolt of rust that corrupts the immediate area, and poisons the first target hit."
gain_text = "Messengers of hope fear the rustbringer."
cost = 1
spell_to_add = /obj/effect/proc_holder/spell/aimed/rust_wave
route = PATH_RUST
/datum/eldritch_knowledge/rust_regen
name = "Leeching Walk"
desc = "Passively heals you when you are on rusted tiles."
gain_text = "'The strength was unparalleled, unnatural.' The Blacksmith was smiling."
cost = 1
next_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/armor,/datum/eldritch_knowledge/essence)
route = PATH_RUST
/datum/eldritch_knowledge/rust_regen/on_life(mob/user)
. = ..()
var/turf/user_loc_turf = get_turf(user)
if(!istype(user_loc_turf, /turf/open/floor/plating/rust) || !isliving(user))
return
var/mob/living/living_user = user
living_user.adjustBruteLoss(-3, FALSE)
living_user.adjustFireLoss(-3, FALSE)
living_user.adjustToxLoss(-3, FALSE)
living_user.adjustOxyLoss(-1, FALSE)
living_user.adjustStaminaLoss(-6)
/datum/eldritch_knowledge/rust_mark
name = "Mark of Rust"
desc = "Your eldritch blade now applies a rust mark. Rust marks have a chance to deal between 0 to 200 damage to 75% of enemies items. To activate the mark use your Mansus Grasp on it."
gain_text = "Lords of the depths help those in dire need at a cost."
cost = 2
next_knowledge = list(/datum/eldritch_knowledge/spell/area_conversion)
banned_knowledge = list(/datum/eldritch_knowledge/ash_mark,/datum/eldritch_knowledge/flesh_mark)
route = PATH_RUST
/datum/eldritch_knowledge/rust_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters)
. = ..()
if(isliving(target))
var/mob/living/living_target = target
living_target.apply_status_effect(/datum/status_effect/eldritch/rust)
/datum/eldritch_knowledge/rust_blade_upgrade
name = "Toxic Steel"
gain_text = "Let the blade guide you through the flesh."
desc = "Your blade of choice will now add toxin to enemies bloodstream."
cost = 2
next_knowledge = list(/datum/eldritch_knowledge/spell/entropic_plume)
banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade)
route = PATH_RUST
/datum/eldritch_knowledge/rust_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters)
. = ..()
if(iscarbon(target))
var/mob/living/carbon/carbon_target = target
carbon_target.reagents.add_reagent(/datum/reagent/eldritch, 5)
/datum/eldritch_knowledge/spell/entropic_plume
name = "Entropic Plume"
desc = "You can now send a befuddling plume that blinds, poisons and makes enemies strike each other, while also converting the immediate area into rust."
gain_text = "Messengers of hope fear the rustbringer."
cost = 1
spell_to_add = /obj/effect/proc_holder/spell/cone/staggered/entropic_plume
next_knowledge = list(/datum/eldritch_knowledge/final/rust_final,/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/rusty)
route = PATH_RUST
/datum/eldritch_knowledge/armor
name = "Armorer's Ritual"
desc = "You can now create eldritch armor using a built table and a gas mask on top of a transmutation rune."
gain_text = "For I am the heir to the throne of doom."
cost = 1
next_knowledge = list(/datum/eldritch_knowledge/rust_regen,/datum/eldritch_knowledge/flesh_ghoul)
required_atoms = list(/obj/structure/table,/obj/item/clothing/mask/gas)
result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch)
/datum/eldritch_knowledge/essence
name = "Priest's Ritual"
desc = "You can now transmute a tank of water into a bottle of eldritch fluid."
gain_text = "This is an old recipe, i got it from an owl."
cost = 1
next_knowledge = list(/datum/eldritch_knowledge/rust_regen,/datum/eldritch_knowledge/spell/ashen_shift)
required_atoms = list(/obj/structure/reagent_dispensers/watertank)
result_atoms = list(/obj/item/reagent_containers/glass/beaker/eldritch)
/datum/eldritch_knowledge/final/rust_final
name = "Rustbringer's Oath"
desc = "Bring three corpses onto a transmutation rune. After you finish the ritual, rust will now automatically spread from the rune. Your healing on rust is also tripled, while you become more resilient overall."
gain_text = "Champion of rust. Corruptor of steel. Fear the dark for Rustbringer has come!"
cost = 5
required_atoms = list(/mob/living/carbon/human)
route = PATH_RUST
/datum/eldritch_knowledge/final/rust_final/on_finished_recipe(mob/living/user, list/atoms, loc)
var/mob/living/carbon/human/H = user
H.physiology.brute_mod *= 0.5
H.physiology.burn_mod *= 0.5
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the decay, for Rustbringer [user.real_name] has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
new /datum/rust_spread(loc)
var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic)
ascension.ascended = TRUE
return ..()
/datum/eldritch_knowledge/final/rust_final/on_life(mob/user)
. = ..()
if(!finished)
return
var/mob/living/carbon/human/human_user = user
human_user.adjustBruteLoss(-6, FALSE)
human_user.adjustFireLoss(-6, FALSE)
human_user.adjustToxLoss(-6, FALSE)
human_user.adjustOxyLoss(-6, FALSE)
human_user.adjustStaminaLoss(-20)
/**
* #Rust spread datum
*
* Simple datum that automatically spreads rust around it
*
* Simple implementation of automatically growing entity
*/
/datum/rust_spread
var/list/edge_turfs = list()
var/list/turfs = list()
var/static/list/blacklisted_turfs = typecacheof(list(/turf/open/indestructible,/turf/closed/indestructible,/turf/open/space,/turf/open/lava,/turf/open/chasm))
var/spread_per_tick = 6
/datum/rust_spread/New(loc)
. = ..()
var/turf/turf_loc = get_turf(loc)
turf_loc.rust_heretic_act()
turfs += turf_loc
START_PROCESSING(SSprocessing,src)
/datum/rust_spread/Destroy(force, ...)
STOP_PROCESSING(SSprocessing,src)
return ..()
/datum/rust_spread/process()
compile_turfs()
var/turf/T
for(var/i in 0 to spread_per_tick)
T = pick(edge_turfs)
T.rust_heretic_act()
turfs += get_turf(T)
/**
* Compile turfs
*
* Recreates all edge_turfs as well as normal turfs.
*/
/datum/rust_spread/proc/compile_turfs()
edge_turfs = list()
for(var/X in turfs)
if(!istype(X,/turf/closed/wall/rust) && !istype(X,/turf/closed/wall/r_wall/rust) && !istype(X,/turf/open/floor/plating/rust))
turfs -=X
continue
for(var/turf/T in range(1,X))
if(T in turfs)
continue
if(is_type_in_typecache(T,blacklisted_turfs))
continue
edge_turfs += T
@@ -16,6 +16,7 @@
icon_state = "daemon"
icon_living = "daemon"
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
mob_size = MOB_SIZE_LARGE
speed = 1
a_intent = INTENT_HARM
stop_automated_movement = 1
@@ -70,7 +70,6 @@
icon_living = "swarmer"
icon_dead = "swarmer_unactivated"
icon_gib = null
threat = 0.5
wander = 0
harm_intent_damage = 5
minbodytemp = 0
@@ -161,12 +161,12 @@
/datum/spellbook_entry/blind
name = "Blind"
spell_type = /obj/effect/proc_holder/spell/targeted/trigger/blind
spell_type = /obj/effect/proc_holder/spell/pointed/trigger/blind
cost = 1
/datum/spellbook_entry/mindswap
name = "Mindswap"
spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer
spell_type = /obj/effect/proc_holder/spell/pointed/mind_transfer
category = "Mobility"
/datum/spellbook_entry/forcewall
@@ -246,7 +246,7 @@
/datum/spellbook_entry/barnyard
name = "Barnyard Curse"
spell_type = /obj/effect/proc_holder/spell/targeted/barnyardcurse
spell_type = /obj/effect/proc_holder/spell/pointed/barnyardcurse
/datum/spellbook_entry/charge
name = "Charge"
+1 -1
View File
@@ -177,7 +177,7 @@
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.")
if(APPRENTICE_ROBELESS)
owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/pointed/mind_transfer(null))
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.")
if(APPRENTICE_MARTIAL)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/touch/nuclear_fist(null))
+12
View File
@@ -16,6 +16,18 @@
var/datum/team/xeno/xeno_team
threat = 3
/datum/antagonist/xeno/threat()
. = 1
if(isalienhunter(owner))
. = 2
else if(isaliensentinel(owner))
. = 4
else if(isalienroyal(owner))
if(isalienqueen(owner))
. = 8
else
. = 6
/datum/antagonist/xeno/create_team(datum/team/xeno/new_team)
if(!new_team)
for(var/datum/antagonist/xeno/X in GLOB.antagonists)
+1 -1
View File
@@ -6,5 +6,5 @@
zone = BODY_ZONE_PRECISE_GROIN
slot = ORGAN_SLOT_WOMB
genital_flags = GENITAL_INTERNAL|GENITAL_FUID_PRODUCTION
fluid_id = /datum/reagent/consumable/femcum
fluid_id = /datum/reagent/consumable/semen/femcum
linked_organ_slot = ORGAN_SLOT_VAGINA
+1
View File
@@ -122,6 +122,7 @@ obj/item/dildo/flared/huge
name = "literal horse cock"
desc = "THIS THING IS HUGE!"
dildo_size = 4
force = 10
obj/item/dildo/custom
name = "customizable dildo"
+3 -1
View File
@@ -264,7 +264,7 @@
name = "pipes"
/datum/asset/spritesheet/pipes/register()
for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi'))
for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi', 'icons/obj/plumbing/fluid_ducts.dmi'))
InsertAll("", each, GLOB.alldirs)
..()
@@ -388,9 +388,11 @@
Insert("polycrystal", 'icons/obj/telescience.dmi', "polycrystal")
..()
/datum/asset/spritesheet/mafia
name = "mafia"
/datum/asset/spritesheet/mafia/register()
InsertAll("", 'icons/obj/mafia.dmi')
..()
+1 -1
View File
@@ -310,7 +310,7 @@
/datum/export/gear/combatgloves
cost = 80
unit_name = "combat gloves"
export_types = list(/obj/item/clothing/gloves/tackler/combat, /obj/item/clothing/gloves/tackler/dolphin, /obj/item/clothing/gloves/fingerless/pugilist/rapid, /obj/item/clothing/gloves/krav_maga)
export_types = list(/obj/item/clothing/gloves/tackler/combat, /obj/item/clothing/gloves/tackler/dolphin, /obj/item/clothing/gloves/krav_maga)
include_subtypes = TRUE
/datum/export/gear/bonegloves
+21 -21
View File
@@ -2,11 +2,11 @@
/datum/export/robotics
include_subtypes = FALSE
k_elasticity = 1/50
k_elasticity = 1/200
/datum/export/implant
include_subtypes = FALSE
k_elasticity = 1/50
k_elasticity = 1/200
/datum/export/organs
include_subtypes = TRUE
@@ -34,8 +34,8 @@
export_types = list(/obj/item/organ/cyberimp/brain/anti_stun)
/datum/export/implant/breathtube
cost = 150
k_elasticity = 300/20 //Large before depleating
cost = 175
k_elasticity = 1/350 //Large before depleating
unit_name = "breath implant"
export_types = list(/obj/item/organ/cyberimp/mouth/breathing_tube)
@@ -71,35 +71,35 @@
export_types = list(/obj/item/organ/cyberimp/arm/gun/laser, /obj/item/organ/cyberimp/arm/gun/taser, /obj/item/organ/cyberimp/arm/esword, /obj/item/organ/cyberimp/arm/medibeam, /obj/item/organ/cyberimp/arm/combat, /obj/item/organ/cyberimp/arm/flash, /obj/item/organ/cyberimp/arm/baton)
include_subtypes = TRUE
/datum/export/orgains/heart
/datum/export/organs/heart
cost = 250
unit_name = "heart"
export_types = list(/obj/item/organ/heart)
exclude_types = list(/obj/item/organ/heart/cursed, /obj/item/organ/heart/cybernetic)
/datum/export/orgains/tongue
/datum/export/organs/tongue
cost = 75
unit_name = "tongue"
export_types = list(/obj/item/organ/tongue)
/datum/export/orgains/eyes
/datum/export/organs/eyes
cost = 50 //So many things take your eyes out anyways
unit_name = "eyes"
export_types = list(/obj/item/organ/eyes)
exclude_types = list(/obj/item/organ/eyes/robotic)
/datum/export/orgains/stomach
/datum/export/organs/stomach
cost = 50 //can be replaced
unit_name = "stomach"
export_types = list(/obj/item/organ/stomach)
/datum/export/orgains/lungs
/datum/export/organs/lungs
cost = 150
unit_name = "lungs"
export_types = list(/obj/item/organ/lungs)
exclude_types = list(/obj/item/organ/lungs/cybernetic, /obj/item/organ/lungs/cybernetic/upgraded)
/datum/export/orgains/liver
/datum/export/organs/liver
cost = 175
unit_name = "liver"
export_types = list(/obj/item/organ/liver)
@@ -116,33 +116,33 @@
unit_name = "upgraded cybernetic organ"
export_types = list(/obj/item/organ/lungs/cybernetic/upgraded, /obj/item/organ/liver/cybernetic/upgraded)
/datum/export/organs/tail //Shhh
/datum/export/organs/tail // yeah have fun pulling this off someone without catching a bwoink
cost = 500
unit_name = "error shipment failer"
unit_name = "organic tail"
export_types = list(/obj/item/organ/tail)
/datum/export/orgains/vocal_cords
/datum/export/organs/vocal_cords
cost = 500
unit_name = "vocal cords"
export_types = list(/obj/item/organ/vocal_cords) //These are gotten via different races
/datum/export/robotics/lims
cost = 30
unit_name = "robotic lim replacement"
/datum/export/robotics/limbs
cost = 60
unit_name = "robotic limb replacement"
export_types = list(/obj/item/bodypart/l_arm/robot, /obj/item/bodypart/r_arm/robot, /obj/item/bodypart/l_leg/robot, /obj/item/bodypart/r_leg/robot, /obj/item/bodypart/chest/robot, /obj/item/bodypart/head/robot)
/datum/export/robotics/surpluse
cost = 40
unit_name = "robotic lim replacement"
cost = 50
unit_name = "robotic limb replacement"
export_types = list(/obj/item/bodypart/l_arm/robot/surplus, /obj/item/bodypart/r_arm/robot/surplus, /obj/item/bodypart/l_leg/robot/surplus, /obj/item/bodypart/r_leg/robot/surplus)
/datum/export/robotics/surplus_upgraded
cost = 50
unit_name = "upgraded robotic lim replacement"
cost = 80
unit_name = "upgraded robotic limb replacement"
export_types = list(/obj/item/bodypart/l_arm/robot/surplus_upgraded, /obj/item/bodypart/r_arm/robot/surplus_upgraded, /obj/item/bodypart/l_leg/robot/surplus_upgraded, /obj/item/bodypart/r_leg/robot/surplus_upgraded)
/datum/export/robotics/surgery_gear_basic
cost = 10
cost = 50
unit_name = "surgery tool"
export_types = list(/obj/item/retractor, /obj/item/hemostat, /obj/item/cautery, /obj/item/surgicaldrill, /obj/item/scalpel, /obj/item/circular_saw, /obj/item/bonesetter, /obj/item/surgical_drapes)
+1
View File
@@ -102,6 +102,7 @@
export_types = list(/obj/item/stock_parts/cell/high/slime/hypercharged)
//Glass working stuff
// i'd just like to say how i despise the previous coder's fetish for their funny glasswork
/datum/export/glasswork_dish
cost = 300
+2 -2
View File
@@ -138,11 +138,11 @@
message = "of bones"
export_types = list(/obj/item/stack/sheet/bone)
/datum/export/stack/bronze
/datum/export/stack/sheet/bronze
unit_name = "tiles"
cost = 5
message = "of brozne"
export_types = list(/obj/item/stack/tile/bronze)
export_types = list(/obj/item/stack/sheet/bronze)
/datum/export/stack/brass
unit_name = "tiles"
+31 -33
View File
@@ -5,7 +5,7 @@
/datum/export/weapon/makeshift_shield
cost = 30
unit_name = "unknown shield"
unit_name = "nonstandard shield"
export_types = list(/obj/item/shield/riot, /obj/item/shield/riot/roman, /obj/item/shield/riot/buckler, /obj/item/shield/makeshift)
/datum/export/weapon/riot_shield
@@ -37,7 +37,7 @@
/datum/export/weapon/taser
cost = 200
unit_name = "advanced taser"
unit_name = "hybrid taser"
export_types = list(/obj/item/gun/energy/e_gun/advtaser)
/datum/export/weapon/laser
@@ -104,12 +104,12 @@
/datum/export/weapon/aeg
cost = 200 //Endless power
unit_name = "advance engery gun"
unit_name = "advanced energy gun"
export_types = list(/obj/item/gun/energy/e_gun/nuclear)
/datum/export/weapon/deconer
cost = 600
unit_name = "deconer"
unit_name = "decloner"
export_types = list(/obj/item/gun/energy/decloner)
/datum/export/weapon/ntsniper
@@ -123,9 +123,8 @@
export_types = list(/obj/item/gun/syringe/rapidsyringe)
/datum/export/weapon/temp_gun
cost = 175 //Its just smaller
cost = 175
unit_name = "small temperature gun"
k_elasticity = 1/30 //Its just a smaller temperature gun, easy to mass make
export_types = list(/obj/item/gun/energy/temperature)
/datum/export/weapon/flowergun
@@ -139,8 +138,7 @@
export_types = list(/obj/item/gun/energy/xray)
/datum/export/weapon/ioncarbine
cost = 200
k_elasticity = 1/30 //Its just a smaller temperature gun, easy to mass make
cost = 200
unit_name = "ion carbine"
export_types = list(/obj/item/gun/energy/ionrifle/carbine)
@@ -194,7 +192,7 @@
export_types = list(/obj/item/firing_pin/test_range)
/datum/export/weapon/techslug
cost = 25
cost = 30
k_elasticity = 0
unit_name = "advanced shotgun shell"
export_types = list(/obj/item/ammo_casing/shotgun/dragonsbreath, /obj/item/ammo_casing/shotgun/meteorslug, /obj/item/ammo_casing/shotgun/pulseslug, /obj/item/ammo_casing/shotgun/frag12, /obj/item/ammo_casing/shotgun/ion, /obj/item/ammo_casing/shotgun/laserslug)
@@ -215,7 +213,7 @@
/datum/export/weapon/bow_teaching
cost = 500
unit_name = "stone tablets"
unit_name = "bowyery tablet"
export_types = list(/obj/item/book/granter/crafting_recipe/bone_bow)
/datum/export/weapon/quiver
@@ -230,48 +228,48 @@
/datum/export/weapon/pistol
cost = 120
unit_name = "illegal firearm"
unit_name = "nonstandard sidearm"
export_types = list(/obj/item/gun/ballistic/automatic/pistol)
/datum/export/weapon/revolver
cost = 200
unit_name = "large handgun"
unit_name = "large-caliber revolver"
export_types = list(/obj/item/gun/ballistic/revolver)
exclude_types = list(/obj/item/gun/ballistic/revolver/russian, /obj/item/gun/ballistic/revolver/doublebarrel)
/datum/export/weapon/rocketlauncher
cost = 1000
unit_name = "rocketlauncher"
unit_name = "PML-9 rocket-propelled grenade launcher"
export_types = list(/obj/item/gun/ballistic/rocketlauncher)
/datum/export/weapon/antitank
cost = 300
unit_name = "hand cannon"
unit_name = "anti-tank pistol"
export_types = list(/obj/item/gun/ballistic/automatic/pistol/antitank/syndicate)
/datum/export/weapon/clownstuff
cost = 500
unit_name = "clown war tech"
export_types = list(/obj/item/pneumatic_cannon/pie/selfcharge, /obj/item/shield/energy/bananium, /obj/item/melee/transforming/energy/sword/bananium, )
unit_name = "clown combat equipment"
export_types = list(/obj/item/pneumatic_cannon/pie/selfcharge, /obj/item/shield/energy/bananium, /obj/item/melee/transforming/energy/sword/bananium)
/datum/export/weapon/bulldog
cost = 400
unit_name = "drum loaded shotgun"
unit_name = "drum-fed compact combat shotgun"
export_types = list(/obj/item/gun/ballistic/automatic/shotgun/bulldog)
/datum/export/weapon/smg
cost = 350
unit_name = "automatic c-20r"
unit_name = "C-20r sub-machine gun"
export_types = list(/obj/item/gun/ballistic/automatic/c20r)
/datum/export/weapon/duelsaber
cost = 360 //Get it?
unit_name = "energy saber"
cost = 360
unit_name = "double-bladed energy saber"
export_types = list(/obj/item/dualsaber)
/datum/export/weapon/esword
cost = 130
unit_name = "energy sword"
unit_name = "energy saber"
export_types = list(/obj/item/melee/transforming/energy/sword/cx/traitor, /obj/item/melee/transforming/energy/sword/saber)
/datum/export/weapon/rapier
@@ -286,32 +284,32 @@
/datum/export/weapon/gloves
cost = 90
unit_name = "star struck gloves"
unit_name = "anomalous armwraps"
export_types = list(/obj/item/clothing/gloves/fingerless/pugilist/rapid)
/datum/export/weapon/l6
cost = 500
unit_name = "law 6 saw"
unit_name = "Aussec Armory L6 SAW"
export_types = list(/obj/item/gun/ballistic/automatic/l6_saw)
/datum/export/weapon/m90
cost = 400
unit_name = "assault class weapon"
unit_name = "M90-gl carbine"
export_types = list(/obj/item/gun/ballistic/automatic/m90)
/datum/export/weapon/powerglove
cost = 100
unit_name = "hydraulic glove"
unit_name = "pneumatic gauntlet"
export_types = list(/obj/item/melee/powerfist)
/datum/export/weapon/sniper
cost = 750
unit_name = ".50 sniper"
unit_name = "anti-materiel rifle"
export_types = list(/obj/item/gun/ballistic/automatic/sniper_rifle/syndicate)
/datum/export/weapon/ebow
cost = 600
unit_name = "mini crossbow"
unit_name = "compact energy crossbow"
export_types = list(/obj/item/gun/energy/kinetic_accelerator/crossbow)
/datum/export/weapon/m10mm
@@ -333,12 +331,12 @@
/datum/export/weapon/smg_mag
cost = 45
unit_name = "smg magazine"
unit_name = "SMG/carbine magazine"
export_types = list(/obj/item/ammo_box/magazine/smgm45, /obj/item/ammo_box/magazine/m556)
/datum/export/weapon/l6sawammo
cost = 60
unit_name = "law 6 saw ammo box"
unit_name = "L6 SAW ammo box"
export_types = list(/obj/item/ammo_box/magazine/mm195x129)
include_subtypes = TRUE
@@ -355,13 +353,13 @@
/datum/export/weapon/fletcher_ammo
cost = 60
unit_name = "illegal ammo magazines"
unit_name = "flechette launcher magazine"
export_types = list(/obj/item/ammo_box/magazine/flechette)
include_subtypes = TRUE
/datum/export/weapon/dj_a_pizzabomb
cost = -6000
unit_name = "Repair Costs"
unit_name = "undeclared ordinance and subsequent repair costs"
export_types = list(/obj/item/pizzabox/bomb, /obj/item/sbeacondrop/bomb)
/datum/export/weapon/real_toolbox
@@ -371,12 +369,12 @@
/datum/export/weapon/melee
cost = 50
unit_name = "unlisted weapon"
unit_name = "any other melee weapon"
export_types = list(/obj/item/melee)
include_subtypes = TRUE
/datum/export/weapon/gun
cost = 50
unit_name = "unlisted weapon"
unit_name = "any other weapon"
export_types = list(/obj/item/gun)
include_subtypes = TRUE
+5 -3
View File
@@ -158,11 +158,10 @@
/datum/supply_pack/security/armory/russian
name = "Russian Surplus Crate"
desc = "Hello Comrade, we have the most modern russian military equipment the black market can offer, for the right price of course. Sadly we couldnt remove the lock so it requires Armory access to open."
desc = "Hello Comrade, we have the most modern Russian military equipment the black market can offer, for the right price of course. Sadly we couldn't remove the lock so it requires Armory access to open."
cost = 7500
contraband = TRUE
contains = list(/obj/item/reagent_containers/food/snacks/rationpack,
/obj/item/ammo_box/magazine/m10mm/rifle,
/obj/item/clothing/suit/armor/vest/russian,
/obj/item/clothing/head/helmet/rus_helmet,
/obj/item/clothing/shoes/russian,
@@ -172,7 +171,10 @@
/obj/item/clothing/mask/russian_balaclava,
/obj/item/clothing/head/helmet/rus_ushanka,
/obj/item/clothing/suit/armor/vest/russian_coat,
/obj/item/gun/ballistic/automatic/surplus)
/obj/effect/spawner/bundle/crate/mosin,
/obj/item/storage/toolbox/ammo,
/obj/effect/spawner/bundle/crate/surplusrifle,
/obj/item/storage/toolbox/ammo/surplus)
crate_name = "surplus military crate"
/datum/supply_pack/security/armory/russian/fill(obj/structure/closet/crate/C)
+9 -4
View File
@@ -100,7 +100,7 @@
crate_name = "surplus russian clothing"
crate_type = /obj/structure/closet/crate/internals
/datum/supply_pack/security/russianmosin
/datum/supply_pack/security/russian_partisan
name = "Russian Partisan Gear"
desc = "An old russian partisan equipment crate, comes with a full russian outfit, a loaded surplus rifle and a second magazine."
contraband = TRUE
@@ -112,12 +112,17 @@
/obj/item/clothing/suit/armor/bulletproof,
/obj/item/clothing/head/helmet/alt,
/obj/item/clothing/gloves/tackler/combat/insulated,
/obj/item/clothing/mask/gas,
/obj/item/ammo_box/magazine/m10mm/rifle,
/obj/item/gun/ballistic/automatic/surplus)
/obj/item/clothing/mask/gas)
crate_name = "surplus russian gear"
crate_type = /obj/structure/closet/crate/internals
/datum/supply_pack/security/russian_partisan/fill(obj/structure/closet/crate/C)
..()
if(prob(20))
new /obj/effect/spawner/bundle/crate/mosin(C)
else
new /obj/effect/spawner/bundle/crate/surplusrifle(C)
/datum/supply_pack/security/sechardsuit
name = "Sec Hardsuit"
desc = "One Sec Hardsuit with a small air tank and mask."
+3
View File
@@ -993,3 +993,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
screen -= S
qdel(S)
char_render_holders = null
/client/proc/can_have_part(part_name)
return prefs.pref_species.mutant_bodyparts[part_name] || (part_name in GLOB.unlocked_mutant_parts)
+30 -323
View File
@@ -507,310 +507,19 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["tail_lizard"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Tail</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=tail_lizard;task=input'>[features["tail_lizard"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["mam_tail"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Tail</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=mam_tail;task=input'>[features["mam_tail"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["tail_human"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Tail</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=tail_human;task=input'>[features["tail_human"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["meat_type"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Meat Type</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=meats;task=input'>[features["meat_type"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["snout"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Snout</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=snout;task=input'>[features["snout"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["horns"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Horns</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=horns;task=input'>[features["horns"]]</a>"
dat += "<span style='border:1px solid #161616; background-color: #[features["horns_color"]];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=horns_color;task=input'>Change</a><BR>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
if(pref_species.mutant_bodyparts["frills"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Frills</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=frills;task=input'>[features["frills"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["spines"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Spines</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=spines;task=input'>[features["spines"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["body_markings"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Body Markings</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=body_markings;task=input'>[features["body_markings"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["mam_body_markings"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Species Markings</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=mam_body_markings;task=input'>[features["mam_body_markings"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
if(pref_species.mutant_bodyparts["mam_ears"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Ears</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=mam_ears;task=input'>[features["mam_ears"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["ears"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Ears</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=ears;task=input'>[features["ears"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["mam_snouts"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Snout</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=mam_snouts;task=input'>[features["mam_snouts"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["legs"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Legs</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=legs;task=input'>[features["legs"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["deco_wings"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Decorative wings</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=deco_wings;task=input'>[features["deco_wings"]]</a>"
dat += "<span style='border:1px solid #161616; background-color: #[features["wings_color"]];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=wings_color;task=input'>Change</a><BR>"
if(pref_species.mutant_bodyparts["insect_wings"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Insect wings</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=insect_wings;task=input'>[features["insect_wings"]]</a>"
dat += "<span style='border:1px solid #161616; background-color: #[features["wings_color"]];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=wings_color;task=input'>Change</a><BR>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["insect_fluff"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Insect Fluff</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=insect_fluffs;task=input'>[features["insect_fluff"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["taur"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Tauric Body</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=taur;task=input'>[features["taur"]]</a>"
if(pref_species.mutant_bodyparts["insect_markings"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Insect markings</h3>"
dat += "<a href='?_src_=prefs;preference=insect_markings;task=input'>[features["insect_markings"]]</a><BR>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["wings"] && GLOB.r_wings_list.len >1)
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Wings</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=wings;task=input'>[features["wings"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["xenohead"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Caste Head</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=xenohead;task=input'>[features["xenohead"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["xenotail"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Tail</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=xenotail;task=input'>[features["xenotail"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["xenodorsal"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Dorsal Spines</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=xenodorsal;task=input'>[features["xenodorsal"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["ipc_screen"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Screen</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=ipc_screen;task=input'>[features["ipc_screen"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(pref_species.mutant_bodyparts["ipc_antenna"])
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>Antenna</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=ipc_antenna;task=input'>[features["ipc_antenna"]]</a>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
for(var/mutant_part in GLOB.all_mutant_parts)
if(parent.can_have_part(mutant_part))
if(!mutant_category)
dat += APPEARANCE_CATEGORY_COLUMN
dat += "<h3>[GLOB.all_mutant_parts[mutant_part]]</h3>"
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=[mutant_part];task=input'>[features[mutant_part]]</a>"
var/color_type = GLOB.colored_mutant_parts[mutant_part] //if it can be coloured, show the appropriate button
if(color_type)
dat += "<span style='border:1px solid #161616; background-color: #[features[color_type]];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=[color_type];task=input'>Change</a><BR>"
mutant_category++
if(mutant_category >= MAX_MUTANT_ROWS)
dat += "</td>"
mutant_category = 0
if(mutant_category)
dat += "</td>"
@@ -856,7 +565,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/tauric_shape = FALSE
if(features["cock_taur"])
var/datum/sprite_accessory/penis/P = GLOB.cock_shapes_list[features["cock_shape"]]
if(P.taur_icon && pref_species.mutant_bodyparts["taur"])
if(P.taur_icon && parent.can_have_part("taur"))
var/datum/sprite_accessory/taur/T = GLOB.taur_list[features["taur"]]
if(T.taur_mode & P.accepted_taurs)
tauric_shape = TRUE
@@ -1082,9 +791,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "</b></center></td></tr>"
dat += "<tr><td colspan=4><hr></td></tr>"
dat += "<tr><td colspan=4><center><b>"
if(!length(GLOB.loadout_categories[gear_category]))
dat += "No subcategories detected. Something is horribly wrong!"
else
@@ -1092,7 +801,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(!subcategories.Find(gear_subcategory))
gear_subcategory = subcategories[1]
var/firstsubcat = FALSE
var/firstsubcat = TRUE
for(var/subcategory in subcategories)
if(firstsubcat)
firstsubcat = FALSE
@@ -1808,14 +1517,14 @@ GLOBAL_LIST_EMPTY(preferences_datums)
pref_species = new newtype()
//let's ensure that no weird shit happens on species swapping.
custom_species = null
if(!pref_species.mutant_bodyparts["body_markings"])
if(!parent.can_have_part("body_markings"))
features["body_markings"] = "None"
if(!pref_species.mutant_bodyparts["mam_body_markings"])
if(!parent.can_have_part("mam_body_markings"))
features["mam_body_markings"] = "None"
if(pref_species.mutant_bodyparts["mam_body_markings"])
if(parent.can_have_part("mam_body_markings"))
if(features["mam_body_markings"] == "None")
features["mam_body_markings"] = "Plain"
if(pref_species.mutant_bodyparts["tail_lizard"])
if(parent.can_have_part("tail_lizard"))
features["tail_lizard"] = "Smooth"
if(pref_species.id == "felinid")
features["mam_tail"] = "Cat"
@@ -1927,7 +1636,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("mam_tail")
var/list/snowflake_tails_list = list()
for(var/path in GLOB.mam_tails_list)
var/datum/sprite_accessory/mam_tails/instance = GLOB.mam_tails_list[path]
var/datum/sprite_accessory/tails/mam_tails/instance = GLOB.mam_tails_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
@@ -1943,7 +1652,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["tail_human"] = "None"
features["tail_lizard"] = "None"
if("meats")
if("meat_type")
var/new_meat
new_meat = input(user, "Choose your character's meat type:", "Character Preference") as null|anything in GLOB.meat_types
if(new_meat)
@@ -1952,7 +1661,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("snout")
var/list/snowflake_snouts_list = list()
for(var/path in GLOB.snouts_list)
var/datum/sprite_accessory/mam_snouts/instance = GLOB.snouts_list[path]
var/datum/sprite_accessory/snouts/mam_snouts/instance = GLOB.snouts_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
@@ -1969,7 +1678,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("mam_snouts")
var/list/snowflake_mam_snouts_list = list()
for(var/path in GLOB.mam_snouts_list)
var/datum/sprite_accessory/mam_snouts/instance = GLOB.mam_snouts_list[path]
var/datum/sprite_accessory/snouts/mam_snouts/instance = GLOB.mam_snouts_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
@@ -2048,7 +1757,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(new_deco_wings)
features["deco_wings"] = new_deco_wings
if("insect_fluffs")
if("insect_fluff")
var/new_insect_fluff
new_insect_fluff = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.insect_fluffs_list
if(new_insect_fluff)
@@ -2118,7 +1827,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("mam_ears")
var/list/snowflake_ears_list = list()
for(var/path in GLOB.mam_ears_list)
var/datum/sprite_accessory/mam_ears/instance = GLOB.mam_ears_list[path]
var/datum/sprite_accessory/ears/mam_ears/instance = GLOB.mam_ears_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if(!show_mismatched_markings && S.recommended_species && !S.recommended_species.Find(pref_species.id))
@@ -2196,7 +1905,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("cock_shape")
var/new_shape
var/list/hockeys = list()
if(pref_species.mutant_bodyparts["taur"])
if(parent.can_have_part("taur"))
var/datum/sprite_accessory/taur/T = GLOB.taur_list[features["taur"]]
for(var/A in GLOB.cock_shapes_list)
var/datum/sprite_accessory/penis/P = GLOB.cock_shapes_list[A]
@@ -2383,8 +2092,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
else
features["body_model"] = chosengender
gender = chosengender
facial_hair_style = random_facial_hair_style(gender)
hair_style = random_hair_style(gender)
if("body_size")
var/min = CONFIG_GET(number/body_size_min)
@@ -2805,10 +2512,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.dna.nameless = character.nameless
character.dna.custom_species = character.custom_species
if(pref_species.mutant_bodyparts["meat_type"])
if((parent && parent.can_have_part("meat_type")) || pref_species.mutant_bodyparts["meat_type"])
character.type_of_meat = GLOB.meat_types[features["meat_type"]]
if(character.dna.species.mutant_bodyparts["legs"] && (character.dna.features["legs"] == "Digitigrade" || character.dna.features["legs"] == "Avian"))
if(((parent && parent.can_have_part("legs")) || pref_species.mutant_bodyparts["legs"]) && (character.dna.features["legs"] == "Digitigrade" || character.dna.features["legs"] == "Avian"))
pref_species.species_traits |= DIGITIGRADE
else
pref_species.species_traits -= DIGITIGRADE
+5 -1
View File
@@ -5,7 +5,7 @@
// You do not need to raise this if you are adding new values that have sane defaults.
// Only raise this value when changing the meaning/format/name/layout of an existing value
// where you would want the updater procs below to run
#define SAVEFILE_VERSION_MAX 33
#define SAVEFILE_VERSION_MAX 35
/*
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
@@ -200,6 +200,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
features["silicon_flavor_text"] = html_encode(features["silicon_flavor_text"])
features["ooc_notes"] = html_encode(features["ooc_notes"])
if(current_version < 35)
if(S["species"] == "lizard")
features["mam_snouts"] = features["snout"]
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
if(!ckey)
return
+14 -11
View File
@@ -118,8 +118,7 @@
// Set the clothing's integrity back to 100%, remove all damage to bodyparts, and generally fix it up
/obj/item/clothing/proc/repair(mob/user, params)
damaged_clothes = CLOTHING_PRISTINE
update_clothes_damaged_state(FALSE)
update_clothes_damaged_state(CLOTHING_PRISTINE)
obj_integrity = max_integrity
name = initial(name) // remove "tattered" or "shredded" if there's a prefix
body_parts_covered = initial(body_parts_covered)
@@ -196,7 +195,7 @@
if(3 to INFINITY) // take better care of your shit, dude
name = "tattered [initial(name)]"
update_clothes_damaged_state()
update_clothes_damaged_state(CLOTHING_DAMAGED)
/obj/item/clothing/Destroy()
user_vars_remembered = null //Oh god somebody put REFERENCES in here? not to worry, we'll clean it up
@@ -257,7 +256,7 @@
how_cool_are_your_threads += "Adding or removing items from [src] makes no noise.\n"
how_cool_are_your_threads += "</span>"
. += how_cool_are_your_threads.Join()
if(LAZYLEN(armor_list))
armor_list.Cut()
if(armor.bio)
@@ -346,10 +345,16 @@
var/mob/M = loc
to_chat(M, "<span class='warning'>Your [name] starts to fall apart!</span>")
/obj/item/clothing/proc/update_clothes_damaged_state(damaging = TRUE)
var/index = "[REF(initial(icon))]-[initial(icon_state)]"
var/static/list/damaged_clothes_icons = list()
if(damaging)
//This mostly exists so subtypes can call appriopriate update icon calls on the wearer.
/obj/item/clothing/proc/update_clothes_damaged_state(damaged_state = CLOTHING_DAMAGED)
damaged_clothes = damaged_state
update_icon()
/obj/item/clothing/update_overlays()
. = ..()
if(damaged_clothes)
var/index = "[REF(initial(icon))]-[initial(icon_state)]"
var/static/list/damaged_clothes_icons = list()
var/icon/damaged_clothes_icon = damaged_clothes_icons[index]
if(!damaged_clothes_icon)
damaged_clothes_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply damaged effect to the initial icon_state for each object
@@ -357,9 +362,7 @@
damaged_clothes_icon.Blend(icon('icons/effects/item_damage.dmi', "itemdamaged"), ICON_MULTIPLY) //adds damage effect and the remaining white areas become transparant
damaged_clothes_icon = fcopy_rsc(damaged_clothes_icon)
damaged_clothes_icons[index] = damaged_clothes_icon
add_overlay(damaged_clothes_icon, TRUE)
else
cut_overlay(damaged_clothes_icons[index], TRUE)
. += damaged_clothes_icon
/*
SEE_SELF // can see self, no matter what
@@ -199,12 +199,16 @@
/obj/item/clothing/gloves/evening
name = "evening gloves"
desc = "Thin, pretty gloves intended for use in regal feminine attire, but knowing Space China these are just for some maid fetish."
desc = "Thin, pretty gloves intended for use in regal feminine attire. A tag on the hem claims they were 'maid' in Space China, these were probably intended for use in some maid fetish."
icon_state = "evening"
item_state = "evening"
strip_delay = 40
equip_delay_other = 20
transfer_prints = TRUE
cold_protection = HANDS
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
strip_mod = 0.9
custom_price = PRICE_ALMOST_CHEAP
/obj/item/clothing/gloves/evening/black
name = "midnight gloves"
desc = "Thin, pretty gloves intended for use in sexy feminine attire. A tag on the hem claims they pair great with black stockings."
icon_state = "eveningblack"
item_state = "eveningblack"
+19
View File
@@ -72,6 +72,25 @@
siemens_coefficient = 0
permeability_coefficient = 0.05
/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator
name = "insidious guerrilla gloves"
desc = "Specialized combat gloves for carrying people around. Transfers tactical kidnapping and tackling knowledge to the user via the use of nanochips."
icon_state = "infiltrator"
item_state = "infiltrator"
siemens_coefficient = 0
permeability_coefficient = 0.05
resistance_flags = FIRE_PROOF | ACID_PROOF
var/carrytrait = TRAIT_QUICKER_CARRY
/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator/equipped(mob/user, slot)
. = ..()
if(slot == SLOT_GLOVES)
ADD_TRAIT(user, carrytrait, GLOVE_TRAIT)
/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator/dropped(mob/user)
. = ..()
REMOVE_TRAIT(user, carrytrait, GLOVE_TRAIT)
/obj/item/clothing/gloves/tackler/rocket
name = "rocket gloves"
desc = "The ultimate in high risk, high reward, perfect for when you need to stop a criminal from fifty feet away or die trying. Banned in most Spinward gridiron football and rugby leagues."
+4
View File
@@ -12,6 +12,10 @@
/obj/item/clothing/mask/balaclava/attack_self(mob/user)
adjustmask(user)
/obj/item/clothing/mask/balaclava/breath
name = "breathaclava"
clothing_flags = ALLOWINTERNALS
/obj/item/clothing/mask/infiltrator
name = "insidious balaclava"
desc = "An incredibly suspicious balaclava made with Syndicate nanofibers to absorb impacts slightly while obfuscating the voice and face using a garbled vocoder."
+7 -6
View File
@@ -543,6 +543,7 @@
cold_protection = HEAD
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
flags_inv = HIDEHAIR|HIDEEARS
rad_flags = RAD_NO_CONTAMINATE
/obj/item/clothing/suit/hooded/wintercoat/centcom
name = "centcom winter coat"
@@ -624,7 +625,7 @@
desc = "An arctic white winter coat with a small blue caduceus instead of a plastic zipper tab. Snazzy."
icon_state = "coatmedical"
item_state = "coatmedical"
allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45)
hoodtype = /obj/item/clothing/head/hooded/winterhood/medical
@@ -637,7 +638,7 @@
desc = "An arctic white winter coat with a small blue caduceus instead of a plastic zipper tab. The normal liner is replaced with an exceptionally thick, soft layer of fur."
icon_state = "coatcmo"
item_state = "coatcmo"
allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
armor = list("melee" = 5, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 0)
hoodtype = /obj/item/clothing/head/hooded/winterhood/cmo
@@ -650,7 +651,7 @@
desc = "A lab-grade winter coat made with acid resistant polymers. For the enterprising chemist who was exiled to a frozen wasteland on the go."
icon_state = "coatchemistry"
item_state = "coatchemistry"
allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 30, "rad" = 0, "fire" = 30, "acid" = 45)
hoodtype = /obj/item/clothing/head/hooded/winterhood/chemistry
@@ -663,7 +664,7 @@
desc = "A white winter coat with green markings. Warm, but wont fight off the common cold or any other disease. Might make people stand far away from you in the hallway. The zipper tab looks like an oversized bacteriophage."
icon_state = "coatviro"
item_state = "coatviro"
allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 30, "rad" = 0, "fire" = 0, "acid" = 0)
hoodtype = /obj/item/clothing/head/hooded/winterhood/viro
@@ -676,7 +677,7 @@
desc = "A winter coat with blue markings. Warm, but probably won't protect from biological agents. For the cozy doctor on the go."
icon_state = "coatparamed"
item_state = "coatparamed"
allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45)
hoodtype = /obj/item/clothing/head/hooded/winterhood/paramedic
@@ -1062,4 +1063,4 @@
name = "Samurai outfit"
desc = "An outfit used by traditional japanese warriors."
icon_state = "samurai"
item_state = "samurai"
item_state = "samurai"
+1 -1
View File
@@ -3,7 +3,7 @@
typepath = /datum/round_event/ghost_role/alien_infestation
weight = 5
gamemode_blacklist = list("dynamic")
min_players = 10
min_players = 25
max_occurrences = 1
/datum/round_event/ghost_role/alien_infestation
@@ -27,7 +27,6 @@
"How do I vore people?",
"ERP?",
"Not epic bros...")
threat = 5
/datum/round_event/brand_intelligence/announce(fake)
+3 -3
View File
@@ -25,7 +25,7 @@
ship_name = pick(strings(PIRATE_NAMES_FILE, "ship_names"))
/datum/round_event/pirates/announce(fake)
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", "commandreport") // CITADEL EDIT metabreak
priority_announce("A business proposition has been downloaded and printed out at all communication consoles.", "Incoming Business Proposition", "commandreport")
if(fake)
return
threat_message = new
@@ -49,6 +49,7 @@
else
priority_announce("Trying to cheat us? You'll regret this!",sender_override = ship_name)
if(!shuttle_spawned)
priority_announce("You won't listen to reason? Then we'll take what's yours or die trying!",sender_override = ship_name)
spawn_shuttle()
/datum/round_event/pirates/start()
@@ -83,8 +84,7 @@
announce_to_ghosts(M)
else
announce_to_ghosts(spawner)
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", "commandreport") //CITADEL EDIT also metabreak here too
priority_announce("Unidentified ship detected near the station.")
//Shuttle equipment
-2
View File
@@ -30,7 +30,6 @@
icon_dead = "magicarp_dead"
icon_gib = "magicarp_gib"
ranged = 1
threat = 4
retreat_distance = 2
minimum_distance = 0 //Between shots they can and will close in to nash
projectiletype = /obj/item/projectile/magic
@@ -52,7 +51,6 @@
color = "#00FFFF"
maxHealth = 75
health = 75
threat = 7
/mob/living/simple_animal/hostile/carp/ranged/chaos/Shoot()
projectiletype = pick(allowed_projectile_types)
+1 -1
View File
@@ -94,7 +94,7 @@
shuffle_inplace(mobs)
var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new /obj/effect/proc_holder/spell/targeted/mind_transfer
var/obj/effect/proc_holder/spell/pointed/mind_transfer/swapper = new /obj/effect/proc_holder/spell/pointed/mind_transfer
while(mobs.len > 1)
var/mob/living/carbon/human/H = pick(mobs)
mobs -= H
+4 -1
View File
@@ -97,9 +97,12 @@ All foods are distributed among various categories. Use common sense.
return
/obj/item/reagent_containers/food/snacks/attack(mob/living/M, mob/living/user, def_zone)
/obj/item/reagent_containers/food/snacks/attack(mob/living/M, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
if(user.a_intent == INTENT_HARM)
return ..()
INVOKE_ASYNC(src, .proc/attempt_forcefeed, M, user)
/obj/item/reagent_containers/food/snacks/proc/attempt_forcefeed(mob/living/M, mob/living/user)
if(!eatverb)
eatverb = pick("bite","chew","nibble","gnaw","gobble","chomp")
if(!reagents.total_volume) //Shouldn't be needed but it checks to see if it has anything left in it.
@@ -190,7 +190,6 @@
icon_dead = "scary_clown"
icon_gib = "scary_clown"
speak = list("...", ". . .")
threat = 3
maxHealth = 120
health = 120
emote_see = list("silently stares")
+2 -2
View File
@@ -3,9 +3,9 @@
desc = "An advanced device designed to manipulate plant genetic makeup."
icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "dnamod"
density = TRUE
circuit = /obj/item/circuitboard/machine/plantgenes
pass_flags = PASSTABLE
pass_flags = PASSTABLE | LETPASSTHROW
flags_1 = DEFAULT_RICOCHET_1
var/obj/item/seeds/seed
var/obj/item/disk/plantgene/disk
+3 -5
View File
@@ -14,9 +14,7 @@
icon_dead = "cannabis-dead" // Same for the dead icon
genes = list(/datum/plant_gene/trait/repeated_harvest)
mutatelist = list(/obj/item/seeds/cannabis/rainbow,
/obj/item/seeds/cannabis/death,
/obj/item/seeds/cannabis/white,
/obj/item/seeds/cannabis/ultimate)
/obj/item/seeds/cannabis/death)
reagents_add = list(/datum/reagent/drug/space_drugs = 0.15, /datum/reagent/toxin/lipolicide = 0.35) // gives u the munchies
@@ -27,7 +25,7 @@
species = "megacannabis"
plantname = "Rainbow Weed"
product = /obj/item/reagent_containers/food/snacks/grown/cannabis/rainbow
mutatelist = list()
mutatelist = list(/obj/item/seeds/cannabis/ultimate)
reagents_add = list(/datum/reagent/toxin/mindbreaker = 0.15, /datum/reagent/toxin/lipolicide = 0.35)
rarity = 40
@@ -38,7 +36,7 @@
species = "blackcannabis"
plantname = "Deathweed"
product = /obj/item/reagent_containers/food/snacks/grown/cannabis/death
mutatelist = list()
mutatelist = list(/obj/item/seeds/cannabis/white)
reagents_add = list(/datum/reagent/toxin/cyanide = 0.35, /datum/reagent/drug/space_drugs = 0.15, /datum/reagent/toxin/lipolicide = 0.15)
rarity = 40
-20
View File
@@ -33,26 +33,6 @@
filling_color = "#00FF00"
juice_results = list(/datum/reagent/consumable/limejuice = 0)
// Electric Lime
/obj/item/seeds/lime/electric
name = "pack of electric lime seeds"
desc = "Electrically sour seeds."
icon_state = "seed-electriclime"
species = "electric lime"
plantname = "Electric Lime Tree"
growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi'
icon_grow = "lime-grow"
icon_dead = "lime-dead"
icon_harvest = "lime-harvest"
product = /obj/item/reagent_containers/food/snacks/grown/citrus/lime/electric
genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/cell_charge, /datum/plant_gene/trait/glow/green)
/obj/item/reagent_containers/food/snacks/grown/citrus/lime/electric
seed = /obj/item/seeds/lime/electric
name = "electric lime"
desc = "It's so sour, you'll be shocked!"
icon_state = "electriclime"
// Orange
/obj/item/seeds/orange
name = "pack of orange seeds"
+3
View File
@@ -9,6 +9,9 @@
potency = 25
growthstages = 3
growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi'
icon_grow = "garlic-grow"
icon_harvest = "garlic-harvest"
icon_dead = "garlic-dead"
reagents_add = list(/datum/reagent/consumable/garlic = 0.15, /datum/reagent/consumable/nutriment = 0.1)
/obj/item/reagent_containers/food/snacks/grown/garlic
@@ -51,6 +51,7 @@
icon_grow = "fairygrass-grow"
icon_dead = "fairygrass-dead"
genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/glow/blue)
mutatelist = list (/obj/item/seeds/grass/carpet)
reagents_add = list(/datum/reagent/consumable/nutriment = 0.02, /datum/reagent/hydrogen = 0.05, /datum/reagent/drug/space_drugs = 0.15)
/obj/item/reagent_containers/food/snacks/grown/grass/fairy
@@ -99,7 +100,7 @@
species = "carpet"
plantname = "Carpet"
product = /obj/item/reagent_containers/food/snacks/grown/grass/carpet
mutatelist = list()
mutatelist = list(/obj/item/seeds/grass/fairy)
rarity = 10
/obj/item/reagent_containers/food/snacks/grown/grass/carpet
+4 -1
View File
@@ -44,7 +44,7 @@
filling_color = "#4582B4"
grind_results = list(/datum/reagent/toxin/teapowder = 0, /datum/reagent/medicine/salglu_solution = 0)
// Kitty drugs
// Catnip
/obj/item/seeds/tea/catnip
name = "pack of catnip seeds"
icon_state = "seed-catnip"
@@ -52,6 +52,9 @@
species = "catnip"
plantname = "Catnip Plant"
growthstages = 3
icon_grow = "catnip-grow"
icon_harvest = "catnip-harvest"
icon_dead = "tea-dead"
product = /obj/item/reagent_containers/food/snacks/grown/tea/catnip
reagents_add = list(/datum/reagent/pax/catnip = 0.1, /datum/reagent/consumable/nutriment/vitamin = 0.06, /datum/reagent/toxin/teapowder = 0.3)
rarity = 50
+9 -4
View File
@@ -103,6 +103,7 @@
throwforce = 5
throw_speed = 2
throw_range = 3
attack_speed = CLICK_CD_MELEE
w_class = WEIGHT_CLASS_BULKY
flags_1 = CONDUCT_1
armour_penetration = 20
@@ -125,9 +126,12 @@
playsound(src,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1)
return (BRUTELOSS)
/obj/item/scythe/pre_attack(atom/A, mob/living/user, params)
/obj/item/scythe/pre_attack(atom/A, mob/living/user, params, attackchain_flags, damage_multiplier)
. = ..()
if(. & STOP_ATTACK_PROC_CHAIN)
return
if(swiping || !istype(A, /obj/structure/spacevine) || get_turf(A) == get_turf(user))
return ..()
return
else
var/turf/user_turf = get_turf(user)
var/dir_to_target = get_dir(user_turf, get_turf(A))
@@ -138,11 +142,12 @@
var/turf/T = get_step(user_turf, turn(dir_to_target, i))
for(var/obj/structure/spacevine/V in T)
if(user.Adjacent(V))
melee_attack_chain(user, V)
melee_attack_chain(user, V, attackchain_flags = ATTACK_IGNORE_CLICKDELAY)
stam_gain += 5 //should be hitcost
swiping = FALSE
stam_gain += 2 //Initial hitcost
user.adjustStaminaLoss(-stam_gain)
user.DelayNextAction()
// *************************************
// Nutrient defines for hydroponics
@@ -192,4 +197,4 @@
/obj/item/reagent_containers/glass/bottle/killer/pestkiller
name = "bottle of pest spray"
desc = "Contains a pesticide."
list_reagents = list(/datum/reagent/toxin/pestkiller = 50)
list_reagents = list(/datum/reagent/toxin/pestkiller = 50)
+14 -10
View File
@@ -30,7 +30,7 @@
var/self_sufficiency_req = 20 //Required total dose to make a self-sufficient hydro tray. 1:1 with earthsblood.
var/self_sufficiency_progress = 0
var/self_sustaining = FALSE //If the tray generates nutrients and water on its own
var/canirrigate = TRUE //tin
/obj/machinery/hydroponics/constructable
name = "hydroponics tray"
@@ -847,12 +847,13 @@
if (!anchored)
to_chat(user, "<span class='warning'>Anchor the tray first!</span>")
return
using_irrigation = !using_irrigation
O.play_tool_sound(src)
user.visible_message("<span class='notice'>[user] [using_irrigation ? "" : "dis"]connects [src]'s irrigation hoses.</span>", \
"<span class='notice'>You [using_irrigation ? "" : "dis"]connect [src]'s irrigation hoses.</span>")
for(var/obj/machinery/hydroponics/h in range(1,src))
h.update_icon()
if(canirrigate)
using_irrigation = !using_irrigation
O.play_tool_sound(src)
user.visible_message("<span class='notice'>[user] [using_irrigation ? "" : "dis"]connects [src]'s irrigation hoses.</span>", \
"<span class='notice'>You [using_irrigation ? "" : "dis"]connect [src]'s irrigation hoses.</span>")
for(var/obj/machinery/hydroponics/h in range(1,src))
h.update_icon()
else if(istype(O, /obj/item/shovel/spade))
if(!myseed && !weedlevel)
@@ -910,11 +911,14 @@
harvest = 0
lastproduce = age
if(istype(myseed, /obj/item/seeds/replicapod))
to_chat(user, "<span class='notice'>You harvest from the [myseed.plantname].</span>")
if(user)//runtimes
to_chat(user, "<span class='notice'>You harvest from the [myseed.plantname].</span>")
else if(myseed.getYield() <= 0)
to_chat(user, "<span class='warning'>You fail to harvest anything useful!</span>")
if(user)
to_chat(user, "<span class='warning'>You fail to harvest anything useful!</span>")
else
to_chat(user, "<span class='notice'>You harvest [myseed.getYield()] items from the [myseed.plantname].</span>")
if(user)
to_chat(user, "<span class='notice'>You harvest [myseed.getYield()] items from the [myseed.plantname].</span>")
if(!myseed.get_gene(/datum/plant_gene/trait/repeated_harvest))
qdel(myseed)
myseed = null
+25
View File
@@ -190,6 +190,31 @@ obj/item/seeds/proc/is_gene_forbidden(typepath)
parent.update_tray(user)
return result
/obj/item/seeds/proc/harvest_userless()
var/obj/machinery/hydroponics/parent = loc //for ease of access
var/t_amount = 0
var/list/result = list()
var/output_loc = parent.loc
var/product_name
while(t_amount < getYield())
var/obj/item/reagent_containers/food/snacks/grown/t_prod = new product(output_loc, src)
if(parent.myseed.plantname != initial(parent.myseed.plantname))
t_prod.name = lowertext(parent.myseed.plantname)
if(productdesc)
t_prod.desc = productdesc
t_prod.seed.name = parent.myseed.name
t_prod.seed.desc = parent.myseed.desc
t_prod.seed.plantname = parent.myseed.plantname
result.Add(t_prod) // User gets a consumable
if(!t_prod)
return
t_amount++
product_name = parent.myseed.plantname
if(getYield() >= 1)
SSblackbox.record_feedback("tally", "food_harvested", getYield(), product_name)
parent.investigate_log("autmoatic harvest of [getYield()] of [src], with seed traits [english_list(genes)] and reagents_add [english_list(reagents_add)] and potency [potency].", INVESTIGATE_BOTANY)
parent.update_tray()
return result
/obj/item/seeds/proc/prepare_result(var/obj/item/reagent_containers/food/snacks/grown/T)
if(!T.reagents)
+134 -37
View File
@@ -2,6 +2,12 @@
#define MUSIC_MAXLINES 1000
#define MUSIC_MAXLINECHARS 300
/**
* # Song datum
*
* These are the actual backend behind instruments.
* They attach to an atom and provide the editor + playback functionality.
*/
/datum/song
/// Name of the song
var/name = "Untitled"
@@ -15,6 +21,9 @@
/// delay between notes in deciseconds
var/tempo = 5
/// How far we can be heard
var/instrument_range = 15
/// Are we currently playing?
var/playing = FALSE
@@ -53,17 +62,24 @@
/////////////////// Playing variables ////////////////
/**
* Only used in synthesized playback - The chords we compiled. Non assoc list of lists:
* list(list(key1, key2, key3..., tempo_divisor), list(key1, key2..., tempo_divisor), ...)
* tempo_divisor always exists
* if key1 (and so if there's no keys) doesn't exist it's a rest
* Build by compile_chords()
* Must be rebuilt on instrument switch.
* Compilation happens when we start playing and is cleared after we finish playing.
* Format: list of chord lists, with chordlists having (key1, key2, key3, tempodiv)
*/
var/list/compiled_chords
/// Current section of a long chord we're on, so we don't need to make a billion chords, one for every unit ticklag.
var/elapsed_delay
/// Amount of delay to wait before playing the next chord
var/delay_by
/// Current chord we're on.
var/current_chord
/// Channel as text = current volume percentage but it's 0 to 100 instead of 0 to 1.
var/list/channels_playing = list()
/// List of channels that aren't being used, as text. This is to prevent unnecessary freeing and reallocations from SSsounds/SSinstruments.
var/list/channels_idle = list()
/// Person playing us
var/mob/user_playing
//////////////////////////////////////////////////////
/// Last world.time we checked for who can hear us
@@ -72,8 +88,6 @@
var/list/hearing_mobs
/// If this is enabled, some things won't be strictly cleared when they usually are (liked compiled_chords on play stop)
var/debug_mode = FALSE
/// Last time we processed decay
var/last_process_decay
/// Max sound channels to occupy
var/max_sound_channels = CHANNELS_PER_INSTRUMENT
/// Current channels, so we can save a length() call.
@@ -113,7 +127,7 @@
var/cached_exponential_dropoff = 1.045
/////////////////////////////////////////////////////////////////////////
/datum/song/New(atom/parent, list/instrument_ids)
/datum/song/New(atom/parent, list/instrument_ids, new_range)
SSinstruments.on_song_new(src)
lines = list()
tempo = sanitize_tempo(tempo)
@@ -125,6 +139,8 @@
hearing_mobs = list()
volume = clamp(volume, min_volume, max_volume)
update_sustain()
if(new_range)
instrument_range = new_range
/datum/song/Destroy()
stop_playing()
@@ -135,12 +151,15 @@
parent = null
return ..()
/**
* Checks and stores which mobs can hear us. Terminates sounds for mobs that leave our range.
*/
/datum/song/proc/do_hearcheck()
last_hearcheck = world.time
var/list/old = hearing_mobs.Copy()
hearing_mobs.len = 0
var/turf/source = get_turf(parent)
for(var/mob/M in get_hearers_in_view(15, source))
for(var/mob/M in get_hearers_in_view(instrument_range, source))
if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS))
continue
hearing_mobs[M] = get_dist(M, source)
@@ -148,10 +167,15 @@
for(var/i in exited)
terminate_sound_mob(i)
/// I can either be a datum, id, or path (if the instrument has no id).
/**
* Sets our instrument, caching anything necessary for faster accessing. Accepts an ID, typepath, or instantiated instrument datum.
*/
/datum/song/proc/set_instrument(datum/instrument/I)
terminate_all_sounds()
var/old_legacy
if(using_instrument)
using_instrument.songs_using -= src
old_legacy = (using_instrument.instrument_flags & INSTRUMENT_LEGACY)
using_instrument = null
cached_samples = null
cached_legacy_ext = null
@@ -162,7 +186,7 @@
if(istype(I))
using_instrument = I
I.songs_using += src
var/instrument_legacy = CHECK_BITFIELD(I.instrument_flags, INSTRUMENT_LEGACY)
var/instrument_legacy = (I.instrument_flags & INSTRUMENT_LEGACY)
if(instrument_legacy)
cached_legacy_ext = I.legacy_instrument_ext
cached_legacy_dir = I.legacy_instrument_path
@@ -170,23 +194,37 @@
else
cached_samples = I.samples
legacy = FALSE
if(isnull(old_legacy) || (old_legacy != instrument_legacy))
if(playing)
compile_chords()
/// THIS IS A BLOCKING CALL.
/**
* Attempts to start playing our song.
*/
/datum/song/proc/start_playing(mob/user)
if(playing)
return
if(!using_instrument?.ready())
to_chat(user, "<span class='warning'>An error has occured with [src]. Please reset the instrument.</span>")
return
compile_chords()
if(!length(compiled_chords))
to_chat(user, "<span class='warning'>Song is empty.</span>")
return
playing = TRUE
updateDialog()
updateDialog(user_playing)
//we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak.
//wrap the rest of the stuff to ensure stop_playing() is called.
last_process_decay = world.time
do_hearcheck()
elapsed_delay = 0
delay_by = 0
current_chord = 1
user_playing = user
START_PROCESSING(SSinstruments, src)
. = do_play_lines(user)
stop_playing()
/**
* Stops playing, terminating all sounds if in synthesized mode. Clears hearing_mobs.
*/
/datum/song/proc/stop_playing()
if(!playing)
return
@@ -196,42 +234,93 @@
STOP_PROCESSING(SSinstruments, src)
terminate_all_sounds(TRUE)
hearing_mobs.len = 0
updateDialog()
user_playing = null
/// THIS IS A BLOCKING CALL.
/datum/song/proc/do_play_lines(user)
if(!playing)
/**
* Processes our song.
*/
/datum/song/proc/process_song(wait)
if(!length(compiled_chords) || should_stop_playing(user_playing))
stop_playing()
return
do_hearcheck()
if(legacy)
do_play_lines_legacy(user)
else
do_play_lines_synthesized(user)
var/list/chord = compiled_chords[current_chord]
if(++elapsed_delay >= delay_by)
play_chord(chord)
elapsed_delay = 0
delay_by = tempodiv_to_delay(chord[length(chord)])
current_chord++
if(current_chord > length(compiled_chords))
if(repeat)
repeat--
current_chord = 1
return
else
stop_playing()
return
/**
* Converts a tempodiv to ticks to elapse before playing the next chord, taking into account our tempo.
*/
/datum/song/proc/tempodiv_to_delay(tempodiv)
if(!tempodiv)
tempodiv = 1 // no division by 0. some song converters tend to use 0 for when it wants to have no div, for whatever reason.
return max(1, round((tempo/tempodiv) / world.tick_lag, 1))
/**
* Compiles chords.
*/
/datum/song/proc/compile_chords()
legacy? compile_legacy() : compile_synthesized()
/**
* Plays a chord.
*/
/datum/song/proc/play_chord(list/chord)
// last value is timing information
for(var/i in 1 to (length(chord) - 1))
legacy? playkey_legacy(chord[i][1], chord[i][2], chord[i][3], user_playing) : playkey_synth(chord[i], user_playing)
/**
* Checks if we should halt playback.
*/
/datum/song/proc/should_stop_playing(mob/user)
return QDELETED(parent) || !using_instrument || !playing
/**
* Sanitizes tempo to a value that makes sense and fits the current world.tick_lag.
*/
/datum/song/proc/sanitize_tempo(new_tempo)
new_tempo = abs(new_tempo)
return clamp(round(new_tempo, world.tick_lag), world.tick_lag, 5 SECONDS)
/**
* Gets our beats per minute based on our tempo.
*/
/datum/song/proc/get_bpm()
return 600 / tempo
/**
* Sets our tempo from a beats-per-minute, sanitizing it to a valid number first.
*/
/datum/song/proc/set_bpm(bpm)
tempo = sanitize_tempo(600 / bpm)
/// Updates the window for our user. Override in subtypes.
/datum/song/proc/updateDialog(mob/user = usr)
/**
* Updates the window for our users. Override down the line.
*/
/datum/song/proc/updateDialog(mob/user)
ui_interact(user)
/datum/song/process(wait)
if(!playing)
return PROCESS_KILL
var/delay = world.time - last_process_decay
process_decay(delay)
last_process_decay = world.time
// it's expected this ticks at every world.tick_lag. if it lags, do not attempt to catch up.
process_song(world.tick_lag)
process_decay(world.tick_lag)
/**
* Updates our cached linear/exponential falloff stuff, saving calculations down the line.
*/
/datum/song/proc/update_sustain()
// Exponential is easy
cached_exponential_dropoff = sustain_exponential_dropoff
@@ -241,21 +330,33 @@
var/volume_decrease_per_decisecond = volume_diff / target_duration
cached_linear_dropoff = volume_decrease_per_decisecond
/**
* Setter for setting output volume.
*/
/datum/song/proc/set_volume(volume)
src.volume = clamp(volume, max(0, min_volume), min(100, max_volume))
update_sustain()
updateDialog()
/**
* Setter for setting how low the volume has to get before a note is considered "dead" and dropped
*/
/datum/song/proc/set_dropoff_volume(volume)
sustain_dropoff_volume = clamp(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100)
update_sustain()
updateDialog()
/**
* Setter for setting exponential falloff factor.
*/
/datum/song/proc/set_exponential_drop_rate(drop)
sustain_exponential_dropoff = clamp(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX)
update_sustain()
updateDialog()
/**
* Setter for setting linear falloff duration.
*/
/datum/song/proc/set_linear_falloff_duration(duration)
sustain_linear_duration = clamp(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN)
update_sustain()
@@ -277,10 +378,8 @@
// subtype for handheld instruments, like violin
/datum/song/handheld
/datum/song/handheld/updateDialog(mob/user = usr)
if(user.machine != src)
return
parent.ui_interact(user)
/datum/song/handheld/updateDialog(mob/user)
parent.ui_interact(user || usr)
/datum/song/handheld/should_stop_playing(mob/user)
. = ..()
@@ -292,10 +391,8 @@
// subtype for stationary structures, like pianos
/datum/song/stationary
/datum/song/stationary/updateDialog(mob/user = usr)
if(user.machine != src)
return
parent.ui_interact(user)
/datum/song/stationary/updateDialog(mob/user)
parent.ui_interact(user || usr)
/datum/song/stationary/should_stop_playing(mob/user)
. = ..()
+48 -44
View File
@@ -1,48 +1,52 @@
/// Playing legacy instruments - None of the "advanced" like sound reservations and decay are invoked.
/datum/song/proc/do_play_lines_legacy(mob/user)
while(repeat >= 0)
var/cur_oct[7]
var/cur_acc[7]
for(var/i = 1 to 7)
cur_oct[i] = 3
cur_acc[i] = "n"
/**
* Compiles our lines into "chords" with filenames for legacy playback. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
*/
/datum/song/proc/compile_legacy()
if(!length(src.lines))
return
var/list/lines = src.lines //cache for hyepr speed!
compiled_chords = list()
var/list/octaves = list(3, 3, 3, 3, 3, 3, 3)
var/list/accents = list("n", "n", "n", "n", "n", "n", "n")
for(var/line in lines)
var/list/chords = splittext(lowertext(line), ",")
for(var/chord in chords)
var/list/compiled_chord = list()
var/tempodiv = 1
var/list/notes_tempodiv = splittext(chord, "/")
var/len = length(notes_tempodiv)
if(len >= 2)
tempodiv = text2num(notes_tempodiv[2])
if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that.
var/list/notes = splittext(notes_tempodiv[1], "-")
for(var/note in notes)
if(length(note) == 0)
continue
// 1-7, A-G
var/key = text2ascii(note) - 96
if((key < 1) || (key > 7))
continue
for(var/i in 2 to length(note))
var/oct_acc = copytext(note, i, i + 1)
var/num = text2num(oct_acc)
if(!num) //it's an accidental
accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks.
else //octave
octaves[key] = clamp(num, octave_min, octave_max)
compiled_chord[++compiled_chord.len] = list(key, accents[key], octaves[key])
compiled_chord += tempodiv //this goes last
if(length(compiled_chord))
compiled_chords[++compiled_chords.len] = compiled_chord
for(var/line in lines)
for(var/beat in splittext(lowertext(line), ","))
if(should_stop_playing(user))
return
var/list/notes = splittext(beat, "/")
if(length(notes)) //because some jack-butts are going to do ,,,, to symbolize 3 rests instead of something reasonable like ,/1.
for(var/note in splittext(notes[1], "-"))
if(length(note) == 0)
continue
var/cur_note = text2ascii(note) - 96
if(cur_note < 1 || cur_note > 7)
continue
for(var/i=2 to length(note))
var/ni = copytext(note,i,i+1)
if(!text2num(ni))
if(ni == "#" || ni == "b" || ni == "n")
cur_acc[cur_note] = ni
else if(ni == "s")
cur_acc[cur_note] = "#" // so shift is never required
else
cur_oct[cur_note] = text2num(ni)
playnote_legacy(cur_note, cur_acc[cur_note], cur_oct[cur_note])
if(notes.len >= 2 && text2num(notes[2]))
sleep(sanitize_tempo(tempo / text2num(notes[2])))
else
sleep(tempo)
if(should_stop_playing(user))
return
repeat--
updateDialog()
repeat = 0
// note is a number from 1-7 for A-G
// acc is either "b", "n", or "#"
// oct is 1-8 (or 9 for C)
/datum/song/proc/playnote_legacy(note, acc as text, oct)
/**
* Proc to play a legacy note. Just plays the sound to hearing mobs (and does hearcheck if necessary), no fancy channel/sustain/management.
*
* Arguments:
* * note is a number from 1-7 for A-G
* * acc is either "b", "n", or "#"
* * oct is 1-8 (or 9 for C)
*/
/datum/song/proc/playkey_legacy(note, acc as text, oct, mob/user)
// handle accidental -> B<>C of E<>F
if(acc == "b" && (note == 3 || note == 6)) // C or F
if(note == 3)
@@ -1,27 +1,7 @@
/datum/song/proc/do_play_lines_synthesized(mob/user)
compile_lines()
while(repeat >= 0)
if(should_stop_playing(user))
return
var/warned = FALSE
for(var/_chord in compiled_chords)
if(should_stop_playing(user))
return
var/list/chord = _chord
var/tempodiv = chord[chord.len]
for(var/i in 1 to chord.len - 1)
var/key = chord[i]
if(!playkey_synth(key))
if(!warned)
warned = TRUE
to_chat(user, "<span class='boldwarning'>Your instrument has ran out of channels. You might be playing your song too fast or be setting sustain to too high of a value. This warning will be suppressed for the rest of this cycle.</span>")
sleep(sanitize_tempo(tempo / (tempodiv || 1)))
repeat--
updateDialog()
repeat = 0
/// C-Db2-A-A4/2,A-B#4-C/3,/4,A,A-B-C as an example
/datum/song/proc/compile_lines()
/**
* Compiles our lines into "chords" with numbers. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
*/
/datum/song/proc/compile_synthesized()
if(!length(src.lines))
return
var/list/lines = src.lines //cache for hyepr speed!
@@ -57,10 +37,12 @@
compiled_chord += tempodiv //this goes last
if(length(compiled_chord))
compiled_chords[++compiled_chords.len] = compiled_chord
CHECK_TICK
return compiled_chords
/datum/song/proc/playkey_synth(key)
/**
* Plays a specific numerical key from our instrument to anyone who can hear us.
* Does a hearing check if enough time has passed.
*/
/datum/song/proc/playkey_synth(key, mob/user)
if(can_noteshift)
key = clamp(key + note_shift, key_min, key_max)
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
@@ -83,6 +65,9 @@
M.playsound_local(get_turf(parent), null, volume, FALSE, K.frequency, INSTRUMENT_DISTANCE_NO_FALLOFF, channel, null, copy, distance_multiplier = INSTRUMENT_DISTANCE_FALLOFF_BUFF)
// Could do environment and echo later but not for now
/**
* Stops all sounds we are "responsible" for. Only works in synthesized mode.
*/
/datum/song/proc/terminate_all_sounds(clear_channels = TRUE)
for(var/i in hearing_mobs)
terminate_sound_mob(i)
@@ -93,10 +78,16 @@
using_sound_channels = 0
SSsounds.free_datum_channels(src)
/**
* Stops all sounds we are responsible for in a given person. Only works in synthesized mode.
*/
/datum/song/proc/terminate_sound_mob(mob/M)
for(var/channel in channels_playing)
M.stop_sound_channel(text2num(channel))
/**
* Pops a channel we have reserved so we don't have to release and re-request them from SSsounds every time we play a note. This is faster.
*/
/datum/song/proc/pop_channel()
if(length(channels_idle)) //just pop one off of here if we have one available
. = text2num(channels_idle[1])
@@ -108,6 +99,12 @@
if(!isnull(.))
using_sound_channels++
/**
* Decays our channels and updates their volumes to mobs who can hear us.
*
* Arguments:
* * wait_ds - the deciseconds we should decay by. This is to compensate for any lag, as otherwise songs would get pretty nasty during high time dilation.
*/
/datum/song/proc/process_decay(wait_ds)
var/linear_dropoff = cached_linear_dropoff * wait_ds
var/exponential_dropoff = cached_exponential_dropoff ** wait_ds
+1
View File
@@ -46,6 +46,7 @@
var/minimal_player_age = 0
var/outfit = null
var/plasma_outfit = null //the outfit given to plasmamen
var/exp_requirements = 0
@@ -12,6 +12,7 @@
exp_type = EXP_TYPE_CREW
outfit = /datum/outfit/job/atmos
plasma_outfit = /datum/outfit/plasmaman/atmospherics
access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS,
ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM)
+1
View File
@@ -11,6 +11,7 @@
exp_type_department = EXP_TYPE_SERVICE // This is so the jobs menu can work properly
outfit = /datum/outfit/job/bartender
plasma_outfit = /datum/outfit/plasmaman/bar
access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_BAR, ACCESS_MINERAL_STOREROOM)
+1
View File
@@ -10,6 +10,7 @@
selection_color = "#bbe291"
outfit = /datum/outfit/job/botanist
plasma_outfit = /datum/outfit/plasmaman/botany
access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_HYDROPONICS, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM)
+1
View File
@@ -17,6 +17,7 @@
outfit = /datum/outfit/job/captain
plasma_outfit = /datum/outfit/plasmaman/captain
access = list() //See get_access()
minimal_access = list() //See get_access()
@@ -10,6 +10,7 @@
selection_color = "#ca8f55"
outfit = /datum/outfit/job/cargo_tech
plasma_outfit = /datum/outfit/plasmaman/cargo
access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MINING,
ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM)
+1
View File
@@ -10,6 +10,7 @@
selection_color = "#dddddd"
outfit = /datum/outfit/job/chaplain
plasma_outfit = /datum/outfit/plasmaman/chaplain
access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE)
minimal_access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_THEATRE)
+1
View File
@@ -12,6 +12,7 @@
exp_requirements = 60
outfit = /datum/outfit/job/chemist
plasma_outfit = /datum/outfit/plasmaman/chemist
access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_MINERAL_STOREROOM)
@@ -17,6 +17,7 @@
exp_type_department = EXP_TYPE_ENGINEERING
outfit = /datum/outfit/job/ce
plasma_outfit = /datum/outfit/plasmaman/ce
access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS,
ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EVA,
@@ -17,6 +17,7 @@
exp_type_department = EXP_TYPE_MEDICAL
outfit = /datum/outfit/job/cmo
plasma_outfit = /datum/outfit/plasmaman/cmo
access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_HEADS, ACCESS_MINERAL_STOREROOM,
ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE,
+1
View File
@@ -10,6 +10,7 @@
selection_color = "#dddddd"
outfit = /datum/outfit/job/clown
plasma_outfit = /datum/outfit/plasmaman/clown
access = list(ACCESS_THEATRE)
minimal_access = list(ACCESS_THEATRE)
+1
View File
@@ -11,6 +11,7 @@
var/cooks = 0 //Counts cooks amount
outfit = /datum/outfit/job/cook
plasma_outfit = /datum/outfit/plasmaman/chef
access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_MINERAL_STOREROOM)
+1
View File
@@ -10,6 +10,7 @@
selection_color = "#dddddd"
outfit = /datum/outfit/job/curator
plasma_outfit = /datum/outfit/plasmaman/curator
access = list(ACCESS_LIBRARY)
minimal_access = list(ACCESS_LIBRARY, ACCESS_CONSTRUCTION, ACCESS_MINING_STATION)
+1
View File
@@ -14,6 +14,7 @@
exp_type = EXP_TYPE_CREW
outfit = /datum/outfit/job/detective
plasma_outfit = /datum/outfit/plasmaman/detective
access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM)
@@ -12,6 +12,7 @@
exp_requirements = 60
outfit = /datum/outfit/job/geneticist
plasma_outfit = /datum/outfit/plasmaman/genetics
access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_ROBOTICS, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE)
minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM)
@@ -17,6 +17,7 @@
exp_type_department = EXP_TYPE_SERVICE
outfit = /datum/outfit/job/hop
plasma_outfit = /datum/outfit/plasmaman/hop
access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_COURT, ACCESS_WEAPONS,
ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS,
@@ -17,6 +17,8 @@
exp_type_department = EXP_TYPE_SECURITY
outfit = /datum/outfit/job/hos
plasma_outfit = /datum/outfit/plasmaman/hos
mind_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM)
access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP,
+1
View File
@@ -10,6 +10,7 @@
selection_color = "#bbe291"
outfit = /datum/outfit/job/janitor
plasma_outfit = /datum/outfit/plasmaman/janitor
access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM)
+1
View File
@@ -11,6 +11,7 @@
var/lawyers = 0 //Counts lawyer amount
outfit = /datum/outfit/job/lawyer
plasma_outfit = /datum/outfit/plasmaman/bar //yes, this is correct, there's no 'lawyer' plasmeme outfit
access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS)
minimal_access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS)
@@ -10,6 +10,7 @@
selection_color = "#74b5e0"
outfit = /datum/outfit/job/doctor
plasma_outfit = /datum/outfit/plasmaman/medical
access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_VIROLOGY, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CLONING, ACCESS_MINERAL_STOREROOM)
+1
View File
@@ -10,6 +10,7 @@
selection_color = "#dddddd"
outfit = /datum/outfit/job/mime
plasma_outfit = /datum/outfit/plasmaman/mime
access = list(ACCESS_THEATRE)
minimal_access = list(ACCESS_THEATRE)
@@ -17,6 +17,7 @@
exp_type = EXP_TYPE_CREW
outfit = /datum/outfit/job/rd
plasma_outfit = /datum/outfit/plasmaman/rd
access = list(ACCESS_RD, ACCESS_HEADS, ACCESS_TOX, ACCESS_GENETICS, ACCESS_MORGUE,
ACCESS_TOX_STORAGE, ACCESS_TELEPORTER, ACCESS_SEC_DOORS,
@@ -12,6 +12,7 @@
exp_type = EXP_TYPE_CREW
outfit = /datum/outfit/job/roboticist
plasma_outfit = /datum/outfit/plasmaman/robotics
access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM, ACCESS_XENOBIOLOGY, ACCESS_GENETICS)
minimal_access = list(ACCESS_ROBOTICS, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM)
+1
View File
@@ -12,6 +12,7 @@
exp_type = EXP_TYPE_CREW
outfit = /datum/outfit/job/scientist
plasma_outfit = /datum/outfit/plasmaman/science
access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE, ACCESS_GENETICS)
minimal_access = list(ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_MINERAL_STOREROOM)
@@ -14,6 +14,7 @@
exp_type = EXP_TYPE_CREW
outfit = /datum/outfit/job/security
plasma_outfit = /datum/outfit/plasmaman/security
access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP, ACCESS_MINERAL_STOREROOM) // See /datum/job/officer/get_access()
@@ -12,6 +12,7 @@
outfit = /datum/outfit/job/miner
plasma_outfit = /datum/outfit/plasmaman/mining
access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MINING,
ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM)
@@ -12,6 +12,7 @@
exp_type = EXP_TYPE_CREW
outfit = /datum/outfit/job/engineer
plasma_outfit = /datum/outfit/plasmaman/engineering
access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS,
ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_TCOMSAT, ACCESS_MINERAL_STOREROOM)
@@ -12,6 +12,7 @@
exp_requirements = 60
outfit = /datum/outfit/job/virologist
plasma_outfit = /datum/outfit/plasmaman/viro
access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_MEDICAL, ACCESS_VIROLOGY, ACCESS_MINERAL_STOREROOM)
+1
View File
@@ -14,6 +14,7 @@
exp_type = EXP_TYPE_CREW
outfit = /datum/outfit/job/warden
plasma_outfit = /datum/outfit/plasmaman/warden
access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP, ACCESS_FORENSICS_LOCKERS, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_WEAPONS, ACCESS_ENTER_GENPOP, ACCESS_LEAVE_GENPOP, ACCESS_MINERAL_STOREROOM) // See /datum/job/warden/get_access()
+31 -7
View File
@@ -67,6 +67,8 @@
var/max_player = MAFIA_MAX_PLAYER_COUNT
///Required player count
var/required_player = 5
///Prioritizes clients to have cool antag roles
var/low_pop_mode = FALSE
/datum/mafia_controller/New()
. = ..()
@@ -93,7 +95,7 @@
* * setup_list: list of all the datum setups (fancy list of roles) that would work for the game
* * ready_players: list of filtered, sane players (so not playing or disconnected) for the game to put into roles
*/
/datum/mafia_controller/proc/prepare_game(setup_list,ready_players)
/datum/mafia_controller/proc/prepare_game(setup_list, ready_players)
var/list/possible_maps = subtypesof(/datum/map_template/mafia)
var/turf/spawn_area = get_turf(locate(/obj/effect/landmark/mafia_game_area) in GLOB.landmarks_list)
@@ -116,18 +118,40 @@
landmarks += possible_spawn
current_setup_text = list()
var/list/boring_roles = list()
var/list/not_boring_roles = list()
for(var/rtype in setup_list)
for(var/i in 1 to setup_list[rtype])
all_roles += new rtype(src)
var/datum/mafia_role/role = new rtype(src)
all_roles += role
if(role.role_type == TOWN_PROTECT || role.role_type == TOWN_INVEST || role.role_type == MAFIA_SPECIAL || role.role_type == MAFIA_REGULAR)
not_boring_roles += role
else
boring_roles += role
var/datum/mafia_role/rp = rtype
current_setup_text += "[initial(rp.name)] x[setup_list[rtype]]"
var/list/spawnpoints = landmarks.Copy()
for(var/datum/mafia_role/role in all_roles)
role.assigned_landmark = pick_n_take(spawnpoints)
if(!debug)
if(length(ready_players) < 7 || low_pop_mode)
//do normal assign
for(var/datum/mafia_role/role in not_boring_roles)
role.assigned_landmark = pick_n_take(spawnpoints)
role.player_key = pick_n_take(ready_players)
else
role.player_key = pop(ready_players)
//shame!
for(var/datum/mafia_role/role in boring_roles)
role.assigned_landmark = pick_n_take(spawnpoints)
role.player_key = pick_n_take(ready_players)
else //go run the normal one
for(var/datum/mafia_role/role in all_roles)
role.assigned_landmark = pick_n_take(spawnpoints)
if(!debug)
role.player_key = pick_n_take(ready_players)
else
role.player_key = pop(ready_players)
/datum/mafia_controller/proc/send_message(msg,team)
for(var/datum/mafia_role/R in all_roles)

Some files were not shown because too many files have changed in this diff Show More