Merge branch 'master' into language-tab

This commit is contained in:
Timothy Teakettle
2021-03-01 20:49:16 +00:00
1890 changed files with 88328 additions and 35824 deletions
@@ -0,0 +1,175 @@
/*! Actionspeed modification datums.
How action speed for mobs works
Action speed is now calculated by using modifier datums which are added to mobs. Some of them (nonvariable ones) are globally cached, the variable ones are instanced and changed based on need.
This gives us the ability to have multiple sources of actionspeed, reliabily keep them applied and remove them when they should be
THey can have unique sources and a bunch of extra fancy flags that control behaviour
Previously trying to update action speed was a shot in the dark that usually meant mobs got stuck going faster or slower
Actionspeed modification list is a simple key = datum system. Key will be the datum's ID if it is overridden to not be null, or type if it is not.
DO NOT override datum IDs unless you are going to have multiple types that must overwrite each other. It's more efficient to use types, ID functionality is only kept for cases where dynamic creation of modifiers need to be done.
When update actionspeed is called, the list of items is iterated, according to flags priority and a bunch of conditions
this spits out a final calculated value which is used as a modifer to last_move + modifier for calculating when a mob
can next move
*/
/datum/actionspeed_modifier
/// Whether or not this is a variable modifier. Variable modifiers can NOT be ever auto-cached. ONLY CHECKED VIA INITIAL(), EFFECTIVELY READ ONLY (and for very good reason)
var/variable = FALSE
/// Unique ID. You can never have different modifications with the same ID. By default, this SHOULD NOT be set. Only set it for cases where you're dynamically making modifiers/need to have two types overwrite each other. If unset, uses path (converted to text) as ID.
var/id
/// Higher ones override lower priorities. This is NOT used for ID, ID must be unique, if it isn't unique the newer one overwrites automatically if overriding.
var/priority = 0
var/flags = NONE
/// Multiplicative slowdown
var/multiplicative_slowdown = 0
/// Other modification datums this conflicts with.
var/conflicts_with
/datum/actionspeed_modifier/New()
. = ..()
if(!id)
id = "[type]" //We turn the path into a string.
GLOBAL_LIST_EMPTY(actionspeed_modification_cache)
/// Grabs a STATIC MODIFIER datum from cache. YOU MUST NEVER EDIT THESE DATUMS, OR IT WILL AFFECT ANYTHING ELSE USING IT TOO!
/proc/get_cached_actionspeed_modifier(modtype)
if(!ispath(modtype, /datum/actionspeed_modifier))
CRASH("[modtype] is not a actionspeed modification typepath.")
var/datum/actionspeed_modifier/actionspeed_mod = modtype
if(initial(actionspeed_mod.variable))
CRASH("[modtype] is a variable modifier, and can never be cached.")
actionspeed_mod = GLOB.actionspeed_modification_cache[modtype]
if(!actionspeed_mod)
actionspeed_mod = GLOB.actionspeed_modification_cache[modtype] = new modtype
return actionspeed_mod
///Add a action speed modifier to a mob. If a variable subtype is passed in as the first argument, it will make a new datum. If ID conflicts, it will overwrite the old ID.
/mob/proc/add_actionspeed_modifier(datum/actionspeed_modifier/type_or_datum, update = TRUE)
if(ispath(type_or_datum))
if(!initial(type_or_datum.variable))
type_or_datum = get_cached_actionspeed_modifier(type_or_datum)
else
type_or_datum = new type_or_datum
var/datum/actionspeed_modifier/existing = LAZYACCESS(actionspeed_modification, type_or_datum.id)
if(existing)
if(existing == type_or_datum) //same thing don't need to touch
return TRUE
remove_actionspeed_modifier(existing, FALSE)
if(length(actionspeed_modification))
BINARY_INSERT(type_or_datum.id, actionspeed_modification, /datum/actionspeed_modifier, type_or_datum, priority, COMPARE_VALUE)
LAZYSET(actionspeed_modification, type_or_datum.id, type_or_datum)
if(update)
update_actionspeed()
return TRUE
/// Remove a action speed modifier from a mob, whether static or variable.
/mob/proc/remove_actionspeed_modifier(datum/actionspeed_modifier/type_id_datum, update = TRUE)
var/key
if(ispath(type_id_datum))
key = initial(type_id_datum.id) || "[type_id_datum]" //id if set, path set to string if not.
else if(!istext(type_id_datum)) //if it isn't text it has to be a datum, as it isn't a type.
key = type_id_datum.id
else //assume it's an id
key = type_id_datum
if(!LAZYACCESS(actionspeed_modification, key))
return FALSE
LAZYREMOVE(actionspeed_modification, key)
if(update)
update_actionspeed(FALSE)
return TRUE
/*! Used for variable slowdowns like hunger/health loss/etc, works somewhat like the old list-based modification adds. Returns the modifier datum if successful
How this SHOULD work is:
1. Ensures type_id_datum one way or another refers to a /variable datum. This makes sure it can't be cached. This includes if it's already in the modification list.
2. Instantiate a new datum if type_id_datum isn't already instantiated + in the list, using the type. Obviously, wouldn't work for ID only.
3. Add the datum if necessary using the regular add proc
4. If any of the rest of the args are not null (see: multiplicative slowdown), modify the datum
5. Update if necessary
*/
/mob/proc/add_or_update_variable_actionspeed_modifier(datum/actionspeed_modifier/type_id_datum, update = TRUE, multiplicative_slowdown)
var/modified = FALSE
var/inject = FALSE
var/datum/actionspeed_modifier/final
if(istext(type_id_datum))
final = LAZYACCESS(actionspeed_modification, type_id_datum)
if(!final)
CRASH("Couldn't find existing modification when provided a text ID.")
else if(ispath(type_id_datum))
if(!initial(type_id_datum.variable))
CRASH("Not a variable modifier")
final = LAZYACCESS(actionspeed_modification, initial(type_id_datum.id) || "[type_id_datum]")
if(!final)
final = new type_id_datum
inject = TRUE
modified = TRUE
else
if(!initial(type_id_datum.variable))
CRASH("Not a variable modifier")
final = type_id_datum
if(!LAZYACCESS(actionspeed_modification, final.id))
inject = TRUE
modified = TRUE
if(!isnull(multiplicative_slowdown))
final.multiplicative_slowdown = multiplicative_slowdown
modified = TRUE
if(inject)
add_actionspeed_modifier(final, FALSE)
if(update && modified)
update_actionspeed(TRUE)
return final
///Is there a actionspeed modifier for this mob
/mob/proc/has_actionspeed_modifier(datum/actionspeed_modifier/datum_type_id)
var/key
if(ispath(datum_type_id))
key = initial(datum_type_id.id) || "[datum_type_id]"
else if(istext(datum_type_id))
key = datum_type_id
else
key = datum_type_id.id
return LAZYACCESS(actionspeed_modification, key)
/// Go through the list of actionspeed modifiers and calculate a final actionspeed. ANY ADD/REMOVE DONE IN UPDATE_actionspeed MUST HAVE THE UPDATE ARGUMENT SET AS FALSE!
/mob/proc/update_actionspeed()
. = 0
var/list/conflict_tracker = list()
for(var/key in get_actionspeed_modifiers())
var/datum/actionspeed_modifier/M = actionspeed_modification[key]
var/conflict = M.conflicts_with
var/amt = M.multiplicative_slowdown
if(conflict)
// Conflicting modifiers prioritize the larger slowdown or the larger speedup
// We purposefuly don't handle mixing speedups and slowdowns on the same id
if(abs(conflict_tracker[conflict]) < abs(amt))
conflict_tracker[conflict] = amt
else
continue
. += amt
cached_multiplicative_actions_slowdown = .
///Adds a default action speed
/mob/proc/initialize_actionspeed()
add_or_update_variable_actionspeed_modifier(/datum/actionspeed_modifier/base, multiplicative_slowdown = 1)
/// Get the action speed modifiers list of the mob
/mob/proc/get_actionspeed_modifiers()
. = LAZYCOPY(actionspeed_modification)
for(var/id in actionspeed_mod_immunities)
. -= id
/// Checks if a action speed modifier is valid and not missing any data
/proc/actionspeed_data_null_check(datum/actionspeed_modifier/M) //Determines if a data list is not meaningful and should be discarded.
. = !(M.multiplicative_slowdown)
@@ -0,0 +1,2 @@
/datum/actionspeed_modifier/base
variable = TRUE
@@ -0,0 +1,7 @@
/datum/actionspeed_modifier/low_sanity
multiplicative_slowdown = 0.25
id = ACTIONSPEED_ID_SANITY
/datum/actionspeed_modifier/high_sanity
multiplicative_slowdown = -0.1
id = ACTIONSPEED_ID_SANITY
@@ -0,0 +1,5 @@
/datum/actionspeed_modifier/timecookie
multiplicative_slowdown = -0.05
/datum/actionspeed_modifier/blunt_wound
variable = TRUE
+8
View File
@@ -226,6 +226,14 @@
key_cache[key] = 0
return .
/proc/restore_stickybans()
for (var/banned_ckey in GLOB.stickybanadmintexts)
world.SetConfig("ban", banned_ckey, GLOB.stickybanadmintexts[banned_ckey])
GLOB.stickybanadminexemptions = list()
GLOB.stickybanadmintexts = list()
if (GLOB.stickbanadminexemptiontimerid)
deltimer(GLOB.stickbanadminexemptiontimerid)
GLOB.stickbanadminexemptiontimerid = null
#undef STICKYBAN_MAX_MATCHES
#undef STICKYBAN_MAX_EXISTING_USER_MATCHES
+7 -6
View File
@@ -12,7 +12,7 @@
///////////////////////////////////////////////////////////////////////////////////////////////Panels
/datum/admins/proc/show_player_panel(mob/M in GLOB.mob_list)
set category = "Admin"
set category = "Admin.Game"
set name = "Show Player Panel"
set desc="Edit player (respawn, ban, heal, etc)"
@@ -211,7 +211,7 @@
/datum/admins/proc/access_news_network() //MARKER
set category = "Fun"
set category = "Admin.Events"
set name = "Access Newscaster Network"
set desc = "Allows you to view, add and edit news feeds."
@@ -679,8 +679,8 @@
set category = "Server"
set desc="Respawn basically"
set name="Toggle Respawn"
var/new_nores = !CONFIG_GET(flag/norespawn)
CONFIG_SET(flag/norespawn, new_nores)
var/new_nores = CONFIG_GET(flag/respawns_enabled)
CONFIG_SET(flag/respawns_enabled, !new_nores)
if (!new_nores)
to_chat(world, "<B>You may now respawn.</B>", confidential = TRUE)
else
@@ -783,10 +783,11 @@
if(ispath(chosen, /turf))
T.ChangeTurf(chosen)
else
var/obj/structure/closet/supplypod/centcompod/pod = new()
var/area/pod_storage_area = locate(/area/centcom/supplypod/podStorage) in GLOB.sortedAreas
var/obj/structure/closet/supplypod/centcompod/pod = new(pick(get_area_turfs(pod_storage_area))) //Lets just have it in the pod bay for a moment instead of runtiming
var/atom/A = new chosen(pod)
A.flags_1 |= ADMIN_SPAWNED_1
new /obj/effect/abstract/DPtarget(T, pod)
new /obj/effect/pod_landingzone(T, pod)
log_admin("[key_name(usr)] pod-spawned [chosen] at [AREACOORD(usr)]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Podspawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+1 -1
View File
@@ -6,7 +6,7 @@
/client/proc/investigate_show()
set name = "Investigate"
set category = "Admin"
set category = "Admin.Game"
if(!holder)
return
+7
View File
@@ -44,6 +44,11 @@ GLOBAL_PROTECT(protected_ranks)
/datum/admin_rank/vv_edit_var(var_name, var_value)
return FALSE
/datum/admin_rank/CanProcCall(procname)
. = ..()
if(!check_rights(R_SENSITIVE))
return FALSE
/proc/admin_keyword_to_flag(word, previous_rights=0)
var/flag = 0
switch(ckey(word))
@@ -79,6 +84,8 @@ GLOBAL_PROTECT(protected_ranks)
flag = R_AUTOLOGIN
if("dbranks")
flag = R_DBRANKS
if("sensitive")
flag = R_SENSITIVE
if("@","prev")
flag = previous_rights
return flag
+18 -15
View File
@@ -78,8 +78,11 @@ GLOBAL_PROTECT(admin_verbs_admin)
/client/proc/mark_datum_mapview,
/client/proc/hide_verbs, /*hides all our adminverbs*/
/client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/
/datum/admins/proc/open_borgopanel
/datum/admins/proc/open_borgopanel,
/client/proc/admin_cmd_respawn_return_to_lobby,
/client/proc/admin_cmd_remove_ghost_respawn_timer
)
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel))
GLOBAL_PROTECT(admin_verbs_ban)
GLOBAL_LIST_INIT(admin_verbs_sounds, list(/client/proc/play_local_sound, /client/proc/play_sound, /client/proc/manual_play_web_sound, /client/proc/set_round_end_sound))
@@ -348,7 +351,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
/client/proc/admin_ghost()
set category = "Admin"
set category = "Admin.Game"
set name = "Aghost"
if(!holder)
return FALSE
@@ -379,7 +382,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
/client/proc/invisimin()
set name = "Invisimin"
set category = "Admin"
set category = "Admin.Game"
set desc = "Toggles ghost-like invisibility (Don't abuse this)"
if(holder && mob)
if(mob.invisibility == INVISIBILITY_OBSERVER)
@@ -391,7 +394,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
/client/proc/check_antagonists()
set name = "Check Antagonists"
set category = "Admin"
set category = "Admin.Game"
if(holder)
holder.check_antagonists()
log_admin("[key_name(usr)] checked antagonists.") //for tsar~
@@ -411,14 +414,14 @@ GLOBAL_PROTECT(admin_verbs_hideable)
/client/proc/game_panel()
set name = "Game Panel"
set category = "Admin"
set category = "Admin.Game"
if(holder)
holder.Game()
SSblackbox.record_feedback("tally", "admin_verb", 1, "Game Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/secrets()
set name = "Secrets"
set category = "Admin"
set category = "Admin.Game"
if (holder)
holder.Secrets()
SSblackbox.record_feedback("tally", "admin_verb", 1, "Secrets Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -472,7 +475,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Stealth Mode") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/drop_bomb()
set category = "Special Verbs"
set category = "Admin.Fun"
set name = "Drop Bomb"
set desc = "Cause an explosion of varying strength at your location."
@@ -514,7 +517,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Drop Bomb") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/drop_dynex_bomb()
set category = "Special Verbs"
set category = "Admin.Fun"
set name = "Drop DynEx Bomb"
set desc = "Cause an explosion of varying strength at your location."
@@ -561,7 +564,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
message_admins("[key_name_admin(usr)] has modified Dynamic Explosion Scale: [ex_scale]")
/client/proc/give_spell(mob/T in GLOB.mob_list)
set category = "Fun"
set category = "Admin.Fun"
set name = "Give Spell"
set desc = "Gives a spell to a mob."
@@ -585,7 +588,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
message_admins("<span class='danger'>Spells given to mindless mobs will not be transferred in mindswap or cloning!</span>")
/client/proc/remove_spell(mob/T in GLOB.mob_list)
set category = "Fun"
set category = "Admin.Fun"
set name = "Remove Spell"
set desc = "Remove a spell from the selected mob."
@@ -598,7 +601,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Remove Spell") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/give_disease(mob/living/T in GLOB.mob_living_list)
set category = "Fun"
set category = "Admin.Fun"
set name = "Give Disease"
set desc = "Gives a Disease to a mob."
if(!istype(T))
@@ -613,7 +616,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave [key_name(T)] the disease [D].</span>")
/client/proc/object_say(obj/O in world)
set category = "Special Verbs"
set category = "Admin.Events"
set name = "OSay"
set desc = "Makes an object say something."
var/message = input(usr, "What do you want the message to be?", "Make Sound") as text | null
@@ -625,7 +628,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Object Say") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/togglebuildmodeself()
set name = "Toggle Build Mode Self"
set category = "Special Verbs"
set category = "Admin.Events"
if (!(holder.rank.rights & R_BUILDMODE))
return
if(src.mob)
@@ -634,7 +637,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
/client/proc/check_ai_laws()
set name = "Check AI Laws"
set category = "Admin"
set category = "Admin.Game"
if(holder)
src.holder.output_ai_laws()
@@ -717,7 +720,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
/client/proc/toggle_AI_interact()
set name = "Toggle Admin AI Interact"
set category = "Admin"
set category = "Admin.Game"
set desc = "Allows you to interact with most machines as an AI would as a ghost"
AI_Interact = !AI_Interact
+1 -1
View File
@@ -5,7 +5,7 @@
/datum/verbs/menu/Admin/verb/playerpanel()
set name = "Player Panel"
set desc = "Player Panel"
set category = "Admin"
set category = "Admin.Game"
if(usr.client.holder)
usr.client.holder.player_panel_new()
SSblackbox.record_feedback("tally", "admin_verb", 1, "Player Panel New") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+1 -1
View File
@@ -212,4 +212,4 @@
dat += build_antag_listing()
dat += "</body></html>"
usr << browse(dat.Join(), "window=roundstatus;size=500x500")
usr << browse(dat.Join(), "window=roundstatus;size=500x500")
+6 -1
View File
@@ -33,7 +33,9 @@
H.saved_socks = H.socks
// Mutant randomizing, doesn't affect the mob appearance unless it's the specific mutant.
H.dna.features["mcolor"] = random_short_color()
H.dna.features["mcolor"] = sanitize_hexcolor(random_short_color(), 6)
H.dna.features["mcolor2"] = sanitize_hexcolor(random_short_color(), 6)
H.dna.features["mcolor3"] = sanitize_hexcolor(random_short_color(), 6)
H.dna.features["tail_lizard"] = pick(GLOB.tails_list_lizard)
H.dna.features["snout"] = pick(GLOB.snouts_list)
H.dna.features["horns"] = pick(GLOB.horns_list)
@@ -43,6 +45,9 @@
H.dna.features["insect_wings"] = pick(GLOB.insect_wings_list)
H.dna.features["deco_wings"] = pick(GLOB.deco_wings_list)
H.dna.features["insect_fluff"] = pick(GLOB.insect_fluffs_list)
H.dna.features["arachnid_legs"] = pick(GLOB.arachnid_legs_list)
H.dna.features["arachnid_spinneret"] = pick(GLOB.arachnid_spinneret_list)
H.dna.features["arachnid_mandibles"] = pick(GLOB.arachnid_mandibles_list)
H.dna.features["flavor_text"] = "" //Oh no.
H.dna.features["body_model"] = H.gender
+7
View File
@@ -28,6 +28,11 @@ GLOBAL_PROTECT(href_token)
var/deadmined
/datum/admins/CanProcCall(procname)
. = ..()
if(!check_rights(R_SENSITIVE))
return FALSE
/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected)
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
@@ -147,6 +152,8 @@ GLOBAL_PROTECT(href_token)
return 0
/datum/admins/vv_edit_var(var_name, var_value)
if(var_name == NAMEOF(src, fakekey))
return ..()
return FALSE //nice try trialmin
/*
+1 -1
View File
@@ -287,7 +287,7 @@
R = rank_names[new_rank]
if(!R) //rank with that name doesn't exist yet - make it
if(D)
R = new(new_rank, D.rank.rights) //duplicate our previous admin_rank but with a new name
R = new(new_rank, D.rank.rights, D.rank.exclude_rights, D.rank.can_edit_rights) //duplicate our previous admin_rank but with a new name
else
R = new(new_rank) //blank new admin_rank
GLOB.admin_ranks += R
+1 -1
View File
@@ -131,7 +131,7 @@
for(var/mob/living/mob in thunderdome)
qdel(mob) //Clear mobs
for(var/obj/obj in thunderdome)
if(!istype(obj, /obj/machinery/camera))
if(!istype(obj, /obj/machinery/camera) && !istype(obj, /obj/effect/abstract/proximity_checker))
qdel(obj) //Clear objects
var/area/template = locate(/area/tdome/arena_source)
+7 -9
View File
@@ -52,11 +52,9 @@
edit_emitter(user)
/obj/effect/sound_emitter/AltClick(mob/user)
. = ..()
if(check_rights_for(user.client, R_SOUNDS))
activate(user)
to_chat(user, "<span class='notice'>Sound emitter activated.</span>")
return TRUE
to_chat(user, "<span class='notice'>Sound emitter activated.</span>", confidential = TRUE)
/obj/effect/sound_emitter/proc/edit_emitter(mob/user)
var/dat = ""
@@ -84,20 +82,20 @@
if(!new_label)
return
maptext = new_label
to_chat(user, "<span class='notice'>Label set to [maptext].</span>")
to_chat(user, "<span class='notice'>Label set to [maptext].</span>", confidential = TRUE)
if(href_list["edit_sound_file"])
var/new_file = input(user, "Choose a sound file.", "Sound Emitter") as null|sound
if(!new_file)
return
sound_file = new_file
to_chat(user, "<span class='notice'>New sound file set to [sound_file].</span>")
to_chat(user, "<span class='notice'>New sound file set to [sound_file].</span>", confidential = TRUE)
if(href_list["edit_volume"])
var/new_volume = input(user, "Choose a volume.", "Sound Emitter", sound_volume) as null|num
if(isnull(new_volume))
return
new_volume = clamp(new_volume, 0, 100)
sound_volume = new_volume
to_chat(user, "<span class='notice'>Volume set to [sound_volume]%.</span>")
to_chat(user, "<span class='notice'>Volume set to [sound_volume]%.</span>", confidential = TRUE)
if(href_list["edit_mode"])
var/new_mode
var/mode_list = list("Local (normal sound)" = SOUND_EMITTER_LOCAL, "Direct (not affected by environment/location)" = SOUND_EMITTER_DIRECT)
@@ -105,7 +103,7 @@
if(!new_mode)
return
motus_operandi = mode_list[new_mode]
to_chat(user, "<span class='notice'>Mode set to [motus_operandi].</span>")
to_chat(user, "<span class='notice'>Mode set to [motus_operandi].</span>", confidential = TRUE)
if(href_list["edit_range"])
var/new_range
var/range_list = list("Radius (all mobs within a radius)" = SOUND_EMITTER_RADIUS, "Z-Level (all mobs on the same z)" = SOUND_EMITTER_ZLEVEL, "Global (all players)" = SOUND_EMITTER_GLOBAL)
@@ -113,14 +111,14 @@
if(!new_range)
return
emitter_range = range_list[new_range]
to_chat(user, "<span class='notice'>Range set to [emitter_range].</span>")
to_chat(user, "<span class='notice'>Range set to [emitter_range].</span>", confidential = TRUE)
if(href_list["edit_radius"])
var/new_radius = input(user, "Choose a radius.", "Sound Emitter", sound_volume) as null|num
if(isnull(new_radius))
return
new_radius = clamp(new_radius, 0, 127)
play_radius = new_radius
to_chat(user, "<span class='notice'>Audible radius set to [play_radius].</span>")
to_chat(user, "<span class='notice'>Audible radius set to [play_radius].</span>", confidential = TRUE)
if(href_list["play"])
activate(user)
edit_emitter(user) //Refresh the UI to see our changes
+3 -1
View File
@@ -298,7 +298,9 @@
browse_messages(target_ckey = ckey(target_key), agegate = TRUE)
qdel(query_find_message_secret)
/proc/browse_messages(type, target_ckey, index, linkless = FALSE, filter, agegate = FALSE)
/proc/browse_messages(type, target_ckey, index, linkless = FALSE, filter, agegate = FALSE, override = FALSE)
if((!override || IsAdminAdvancedProcCall()) && !check_rights(R_SENSITIVE))
return
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
+318 -55
View File
@@ -7,7 +7,7 @@
if ("add")
var/list/ban = list()
var/ckey
ban["admin"] = usr.key
ban["admin"] = usr.ckey
ban["type"] = list("sticky")
ban["reason"] = "(InGameBan)([usr.key])" //this will be displayed in dd only
@@ -21,7 +21,8 @@
ban["ckey"] = ckey
if (get_stickyban_from_ckey(ckey))
to_chat(usr, "<span class='adminnotice'>Error: Can not add a stickyban: User already has a current sticky ban</span>")
to_chat(usr, "<span class='adminnotice'>Error: Can not add a stickyban: User already has a current sticky ban</span>", confidential = TRUE)
return
if (data["reason"])
ban["message"] = data["reason"]
@@ -31,7 +32,26 @@
return
ban["message"] = "[reason]"
if(SSdbcore.Connect()) // todo: second wave
// var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery({"
// INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
// VALUES (:ckey, :message, :banning_admin)
// "}, list("ckey" = ckey, "message" = ban["message"], "banning_admin" = usr.ckey))
var/datum/DBQuery/query_create_stickyban = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
VALUES ([ckey], [ban["message"]], [usr.ckey])
"})
if (query_create_stickyban.warn_execute())
ban["fromdb"] = TRUE
qdel(query_create_stickyban)
world.SetConfig("ban",ckey,list2stickyban(ban))
ban = stickyban2list(list2stickyban(ban))
ban["matches_this_round"] = list()
ban["existing_user_matches_this_round"] = list()
ban["admin_matches_this_round"] = list()
ban["pending_matches_this_round"] = list()
SSstickyban.cache[ckey] = ban
log_admin_private("[key_name(usr)] has stickybanned [ckey].\nReason: [ban["message"]]")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has stickybanned [ckey].\nReason: [ban["message"]]</span>")
@@ -43,14 +63,29 @@
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
if (alert("Are you sure you want to remove the sticky ban on [ckey]?","Are you sure","Yes","No") == "No")
return
if (!get_stickyban_from_ckey(ckey))
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
return
world.SetConfig("ban",ckey, null)
SSstickyban.cache -= ckey
if (SSdbcore.Connect())
// SSdbcore.QuerySelect(list(
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = :ckey", list("ckey" = ckey)),
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey", list("ckey" = ckey)),
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = :ckey", list("ckey" = ckey)),
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = :ckey", list("ckey" = ckey))
// ), warn = TRUE, qdel = TRUE)
SSdbcore.QuerySelect(list(
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = [ckey]"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey]"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = [ckey]"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = [ckey]")
), warn = TRUE, qdel = TRUE)
log_admin_private("[key_name(usr)] removed [ckey]'s stickyban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed [ckey]'s stickyban</span>")
@@ -64,42 +99,45 @@
var/alt = ckey(data["alt"])
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
var/found = 0
//we have to do it this way because byond keeps the case in its sticky ban matches WHY!!!
for (var/key in ban["keys"])
if (ckey(key) == alt)
found = 1
break
if (!found)
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>")
var/key = LAZYACCESS(ban["keys"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>", confidential = TRUE)
return
if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them","Are you sure","Yes","No") == "No")
if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them, Use \[E] to exempt them","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
return
found = 0
for (var/key in ban["keys"])
if (ckey(key) == alt)
ban["keys"] -= key
found = 1
break
key = LAZYACCESS(ban["keys"], alt)
if (!found)
to_chat(usr, "<span class='adminnotice'>Error: [alt] link to [ckey]'s sticky ban disappeared.</span>")
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] link to [ckey]'s sticky ban disappeared.</span>", confidential = TRUE)
return
LAZYREMOVE(ban["keys"], alt)
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_remove_stickyban_alt = SSdbcore.NewQuery(
// "DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey AND matched_ckey = :alt",
// list("ckey" = ckey, "alt" = alt)
// )
var/datum/DBQuery/query_remove_stickyban_alt = SSdbcore.NewQuery(
"DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey] AND matched_ckey = [alt]"
)
query_remove_stickyban_alt.warn_execute()
qdel(query_remove_stickyban_alt)
log_admin_private("[key_name(usr)] has disassociated [alt] from [ckey]'s sticky ban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has disassociated [alt] from [ckey]'s sticky ban</span>")
@@ -109,7 +147,7 @@
var/ckey = data["ckey"]
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
var/oldreason = ban["message"]
var/reason = input(usr,"Reason","Reason","[ban["message"]]") as text|null
@@ -118,28 +156,203 @@
//we have to do this again incase something changed while we waited for input
ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
return
ban["message"] = "[reason]"
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_edit_stickyban = SSdbcore.NewQuery(
// "UPDATE [format_table_name("stickyban")] SET reason = :reason WHERE ckey = :ckey",
// list("reason" = reason, "ckey" = ckey)
// )
var/datum/DBQuery/query_edit_stickyban = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban")] SET reason = [reason] WHERE ckey = [ckey]"
)
query_edit_stickyban.warn_execute()
qdel(query_edit_stickyban)
log_admin_private("[key_name(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]</span>")
if ("exempt")
if (!data["ckey"])
return
var/ckey = data["ckey"]
if (!data["alt"])
return
var/alt = ckey(data["alt"])
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
var/key = LAZYACCESS(ban["keys"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>", confidential = TRUE)
return
if (alert("Are you sure you want to exempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
return
key = LAZYACCESS(ban["keys"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt]'s link to [ckey]'s sticky ban disappeared.</span>", confidential = TRUE)
return
LAZYREMOVE(ban["keys"], alt)
key["exempt"] = TRUE
LAZYSET(ban["whitelist"], alt, key)
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_exempt_stickyban_alt = SSdbcore.NewQuery(
// "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = :ckey AND matched_ckey = :alt",
// list("ckey" = ckey, "alt" = alt)
// )
var/datum/DBQuery/query_exempt_stickyban_alt = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
)
query_exempt_stickyban_alt.warn_execute()
qdel(query_exempt_stickyban_alt)
log_admin_private("[key_name(usr)] has exempted [alt] from [ckey]'s sticky ban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has exempted [alt] from [ckey]'s sticky ban</span>")
if ("unexempt")
if (!data["ckey"])
return
var/ckey = data["ckey"]
if (!data["alt"])
return
var/alt = ckey(data["alt"])
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
var/key = LAZYACCESS(ban["whitelist"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not exempt from [ckey]'s sticky ban!</span>", confidential = TRUE)
return
if (alert("Are you sure you want to unexempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
return
key = LAZYACCESS(ban["whitelist"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt]'s exemption from [ckey]'s sticky ban disappeared.</span>", confidential = TRUE)
return
LAZYREMOVE(ban["whitelist"], alt)
key["exempt"] = FALSE
LAZYSET(ban["keys"], alt, key)
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
// var/datum/db_query/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
// "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = :ckey AND matched_ckey = :alt",
// list("ckey" = ckey, "alt" = alt)
// )
var/datum/DBQuery/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
)
query_unexempt_stickyban_alt.warn_execute()
qdel(query_unexempt_stickyban_alt)
log_admin_private("[key_name(usr)] has unexempted [alt] from [ckey]'s sticky ban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has unexempted [alt] from [ckey]'s sticky ban</span>")
if ("timeout")
if (!data["ckey"])
return
if (!SSdbcore.Connect())
to_chat(usr, "<span class='adminnotice'>No database connection!</span>", confidential = TRUE)
return
var/ckey = data["ckey"]
if (alert("Are you sure you want to put [ckey]'s stickyban on timeout until next round (or removed)?","Are you sure","Yes","No") == "No")
return
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
ban["timeout"] = TRUE
world.SetConfig("ban", ckey, null)
var/cachedban = SSstickyban.cache[ckey]
if (cachedban)
cachedban["timeout"] = TRUE
log_admin_private("[key_name(usr)] has put [ckey]'s sticky ban on timeout.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has put [ckey]'s sticky ban on timeout.</span>")
if ("untimeout")
if (!data["ckey"])
return
if (!SSdbcore.Connect())
to_chat(usr, "<span class='adminnotice'>No database connection!</span>", confidential = TRUE)
return
var/ckey = data["ckey"]
if (alert("Are you sure you want to lift the timeout on [ckey]'s stickyban?","Are you sure","Yes","No") == "No")
return
var/ban = get_stickyban_from_ckey(ckey)
var/cachedban = SSstickyban.cache[ckey]
if (cachedban)
cachedban["timeout"] = FALSE
if (!ban)
if (!cachedban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
ban = cachedban
ban["timeout"] = FALSE
world.SetConfig("ban",ckey,list2stickyban(ban))
log_admin_private("[key_name(usr)] has taken [ckey]'s sticky ban off of timeout.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has taken [ckey]'s sticky ban off of timeout.</span>")
if ("revert")
if (!data["ckey"])
return
var/ckey = data["ckey"]
if (alert("Are you sure you want to revert the sticky ban on [ckey] to its state at round start?","Are you sure","Yes","No") == "No")
if (alert("Are you sure you want to revert the sticky ban on [ckey] to its state at round start (or last edit)?","Are you sure","Yes","No") == "No")
return
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
return
var/cached_ban = SSstickyban.cache[ckey]
if (!cached_ban)
to_chat(usr, "<span class='adminnotice'>Error: No cached sticky ban for [ckey] found!</span>")
to_chat(usr, "<span class='adminnotice'>Error: No cached sticky ban for [ckey] found!</span>", confidential = TRUE)
world.SetConfig("ban",ckey,null)
log_admin_private("[key_name(usr)] has reverted [ckey]'s sticky ban to its state at round start.")
@@ -150,14 +363,22 @@
world.SetConfig("ban",ckey,list2stickyban(cached_ban))
/datum/admins/proc/stickyban_gethtml(ckey, ban)
. = {"
/datum/admins/proc/stickyban_gethtml(ckey)
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
return
var/timeout
if (SSdbcore.Connect())
timeout = "<a href='?_src_=holder;[HrefToken()];stickyban=[(ban["timeout"] ? "untimeout" : "timeout")]&ckey=[ckey]'>\[[(ban["timeout"] ? "untimeout" : "timeout" )]\]</a>"
else
timeout = "<a href='?_src_=holder;[HrefToken()];stickyban=revert&ckey=[ckey]'>\[revert\]</a>"
. = list({"
<a href='?_src_=holder;[HrefToken()];stickyban=remove&ckey=[ckey]'>\[-\]</a>
<a href='?_src_=holder;[HrefToken()];stickyban=revert&ckey=[ckey]'>\[revert\]</a>
[timeout]
<b>[ckey]</b>
<br />"
[ban["message"]] <b><a href='?_src_=holder;[HrefToken()];stickyban=edit&ckey=[ckey]'>\[Edit\]</a></b><br />
"}
"})
if (ban["admin"])
. += "[ban["admin"]]<br />"
else
@@ -166,19 +387,24 @@
for (var/key in ban["keys"])
if (ckey(key) == ckey)
continue
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]</li>"
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]<a href='?_src_=holder;[HrefToken()];stickyban=exempt&ckey=[ckey]&alt=[ckey(key)]'>\[E\]</a></li>"
for (var/key in ban["whitelist"])
if (ckey(key) == ckey)
continue
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]<a href='?_src_=holder;[HrefToken()];stickyban=unexempt&ckey=[ckey]&alt=[ckey(key)]'>\[UE\]</a></li>"
. += "</ol>\n"
/datum/admins/proc/stickyban_show()
if(!check_rights(R_BAN))
return
var/list/bans = sortList(world.GetConfig("ban"))
var/banhtml = ""
var/list/bans = sticky_banned_ckeys()
var/list/banhtml = list()
for(var/key in bans)
var/ckey = ckey(key)
var/ban = stickyban2list(world.GetConfig("ban",key))
banhtml += "<br /><hr />\n"
banhtml += stickyban_gethtml(ckey,ban)
banhtml += stickyban_gethtml(ckey)
var/html = {"
<head>
@@ -186,22 +412,49 @@
</head>
<body>
<h2>All Sticky Bans:</h2> <a href='?_src_=holder;[HrefToken()];stickyban=add'>\[+\]</a><br>
[banhtml]
[banhtml.Join("")]
</body>
"}
usr << browse(html,"window=stickybans;size=700x400")
/proc/get_stickyban_from_ckey(var/ckey)
/proc/sticky_banned_ckeys()
if (SSdbcore.Connect() || length(SSstickyban.dbcache))
if (SSstickyban.dbcacheexpire < world.time)
SSstickyban.Populatedbcache()
if (SSstickyban.dbcacheexpire)
return SSstickyban.dbcache.Copy()
return sortList(world.GetConfig("ban"))
/proc/get_stickyban_from_ckey(ckey)
. = list()
if (!ckey)
return null
ckey = ckey(ckey)
. = null
for (var/key in world.GetConfig("ban"))
if (ckey(key) == ckey)
. = stickyban2list(world.GetConfig("ban",key))
break
if (SSdbcore.Connect() || length(SSstickyban.dbcache))
if (SSstickyban.dbcacheexpire < world.time)
SSstickyban.Populatedbcache()
if (SSstickyban.dbcacheexpire)
. = SSstickyban.dbcache[ckey]
//reset the cache incase its a newer ban (but only if we didn't update the cache recently)
if (!. && SSstickyban.dbcacheexpire != world.time+STICKYBAN_DB_CACHE_TIME)
SSstickyban.dbcacheexpire = 1
SSstickyban.Populatedbcache()
. = SSstickyban.dbcache[ckey]
if (.)
var/list/cachedban = SSstickyban.cache["[ckey]"]
if (cachedban)
.["timeout"] = cachedban["timeout"]
/proc/stickyban2list(var/ban)
.["fromdb"] = TRUE
return
. = stickyban2list(world.GetConfig("ban", ckey)) || stickyban2list(world.GetConfig("ban", ckey(ckey))) || list()
if (!length(.))
return null
/proc/stickyban2list(ban, strictdb = TRUE)
if (!ban)
return null
. = params2list(ban)
@@ -212,30 +465,40 @@
var/ckey = ckey(key)
ckeys[ckey] = ckey //to make searching faster.
.["keys"] = ckeys
if (.["whitelist"])
var/keys = splittext(.["whitelist"], ",")
var/ckeys = list()
for (var/key in keys)
var/ckey = ckey(key)
ckeys[ckey] = ckey //to make searching faster.
.["whitelist"] = ckeys
.["type"] = splittext(.["type"], ",")
.["IP"] = splittext(.["IP"], ",")
.["computer_id"] = splittext(.["computer_id"], ",")
. -= "fromdb"
/proc/list2stickyban(var/list/ban)
/proc/list2stickyban(list/ban)
if (!ban || !islist(ban))
return null
. = ban.Copy()
if (.["keys"])
.["keys"] = jointext(.["keys"], ",")
if (.["IP"])
.["IP"] = jointext(.["IP"], ",")
if (.["computer_id"])
.["computer_id"] = jointext(.["computer_id"], ",")
if (.["whitelist"])
.["whitelist"] = jointext(.["whitelist"], ",")
if (.["type"])
.["type"] = jointext(.["type"], ",")
//internal tracking only, shouldn't be stored
. -= "reverting"
. -= "matches_this_round"
. -= "existing_user_matches_this_round"
. -= "admin_matches_this_round"
. -= "matches_this_round"
. -= "reverting"
. -= "pending_matches_this_round"
//storing these can sometimes cause sticky bans to start matching everybody
// and isn't even needed for sticky ban matching, as the hub tracks these separately
. -= "IP"
. -= "computer_id"
. = list2params(.)
+15 -7
View File
@@ -257,9 +257,8 @@
for(var/mob/M in GLOB.player_list)
if(M.ckey == banckey)
playermob = M
break
if(!playermob || M.client) // prioritise mobs with a client to stop the 'oops the dead body with no client got forwarded'
playermob = M
banreason = "(MANUAL BAN) "+banreason
@@ -508,7 +507,7 @@
if("constructwraith")
M.change_mob_type( /mob/living/simple_animal/hostile/construct/wraith , null, null, delmob )
if("shade")
M.change_mob_type( /mob/living/simple_animal/shade , null, null, delmob )
M.change_mob_type( /mob/living/simple_animal/hostile/construct/shade , null, null, delmob )
/////////////////////////////////////new ban stuff
@@ -936,6 +935,12 @@
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_MIND_TRANSFER];jobban4=[REF(M)]'>Mind Transfer Potion</a></td>"
//Respawns
if(jobban_isbanned(M, ROLE_RESPAWN))
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_RESPAWN];jobban4=[REF(M)]'><font color=red>Respawns</font></a></td>"
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_RESPAWN];jobban4=[REF(M)]'>Respawns</a></td>"
dat += "</tr></table>"
usr << browse(dat, "window=jobban2;size=800x450")
return
@@ -1799,12 +1804,15 @@
if(alert(usr, "Send [key_name(M)] back to Lobby?", "Message", "Yes", "No") != "Yes")
return
log_admin("[key_name(usr)] has sent [key_name(M)] back to the Lobby.")
message_admins("[key_name(usr)] has sent [key_name(M)] back to the Lobby.")
log_admin("[key_name(usr)] has sent [key_name(M)] back to the Lobby, removing their respawn restrictions if they existed.")
message_admins("[key_name(usr)] has sent [key_name(M)] back to the Lobby, removing their respawn restrictions if they existed.")
var/mob/dead/new_player/NP = new()
NP.ckey = M.ckey
qdel(M)
if(GLOB.preferences_datums[NP.ckey])
var/datum/preferences/P = GLOB.preferences_datums[NP.ckey]
P.respawn_restrictions_active = FALSE
else if(href_list["tdome1"])
if(!check_rights(R_FUN))
@@ -2475,7 +2483,7 @@
R.activate_module(I)
if(pod)
new /obj/effect/abstract/DPtarget(target, pod)
new /obj/effect/pod_landingzone(target, pod)
if (number == 1)
log_admin("[key_name(usr)] created a [english_list(paths)]")
+1 -1
View File
@@ -782,7 +782,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
if(temp.vars.Find(v))
temp.vars[v] = SDQL_expression(d, set_list[sets])
else
temp.vv_edit_var(v, SDQL_expression(d, set_list[sets]))
temp.vv_edit_var(v, SDQL_expression(d, set_list[sets]), TRUE)
break
if(temp.vars.Find(v) && (istype(temp.vars[v], /datum) || istype(temp.vars[v], /client)))
temp = temp.vars[v]
+3 -3
View File
@@ -174,7 +174,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//is_bwoink is TRUE if this ticket was started by an admin PM
/datum/admin_help/New(msg, client/C, is_bwoink)
//clean the input msg
msg = sanitize(copytext_char(msg,1,MAX_MESSAGE_LEN))
msg = copytext_char(msg,1,MAX_MESSAGE_LEN)
if(!msg || !C || !C.mob)
qdel(src)
return
@@ -263,7 +263,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))
msg = 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>"
@@ -523,7 +523,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
if(handle_spam_prevention(msg,MUTE_ADMINHELP))
return
msg = trim(msg)
msg = sanitize(trim(msg))
if(!msg)
return
+24 -8
View File
@@ -1,7 +1,7 @@
/client/proc/jumptoarea(area/A in GLOB.sortedAreas)
set name = "Jump to Area"
set desc = "Area to jump to"
set category = "Admin"
set category = "Admin.Game"
if(!src.holder)
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
return
@@ -28,7 +28,7 @@
/client/proc/jumptoturf(turf/T in world)
set name = "Jump to Turf"
set category = "Admin"
set category = "Admin.Game"
if(!src.holder)
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
return
@@ -40,7 +40,7 @@
return
/client/proc/jumptomob(mob/M in GLOB.mob_list)
set category = "Admin"
set category = "Admin.Game"
set name = "Jump to Mob"
if(!src.holder)
@@ -59,7 +59,7 @@
to_chat(A, "This mob is not located in the game world.", confidential = TRUE)
/client/proc/jumptocoord(tx as num, ty as num, tz as num)
set category = "Admin"
set category = "Admin.Game"
set name = "Jump to Coordinate"
if (!holder)
@@ -74,7 +74,7 @@
message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]")
/client/proc/jumptokey()
set category = "Admin"
set category = "Admin.Game"
set name = "Jump to Key"
if(!src.holder)
@@ -97,7 +97,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Key") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/Getmob(mob/M in GLOB.mob_list - GLOB.dummy_mob_list)
set category = "Admin"
set category = "Admin.Game"
set name = "Get Mob"
set desc = "Mob to teleport"
if(!src.holder)
@@ -113,7 +113,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Get Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/Getkey()
set category = "Admin"
set category = "Admin.Game"
set name = "Get Key"
set desc = "Key to teleport"
@@ -141,7 +141,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Get Key") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/sendmob(mob/M in sortmobs())
set category = "Admin"
set category = "Admin.Game"
set name = "Send Mob"
if(!src.holder)
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
@@ -158,3 +158,19 @@
else
to_chat(src, "Failed to move mob to a valid location.", confidential = TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Send Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/// Proc to hook user-enacted teleporting behavior and keep logging of the event.
/atom/movable/proc/admin_teleport(atom/new_location, message = TRUE)
if(isnull(new_location))
log_admin("[key_name(usr)] teleported [key_name(src)] to nullspace")
moveToNullspace()
else
log_admin("[key_name(usr)] teleported [key_name(src)] to [AREACOORD(loc)]")
forceMove(new_location)
/mob/admin_teleport(atom/new_location, message = TRUE)
var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(src)] to [isnull(new_location) ? "nullspace" : ADMIN_VERBOSEJMP(loc)]"
if(message)
message_admins(msg)
admin_ticket_log(src, msg)
return ..()
+7 -3
View File
@@ -113,7 +113,6 @@
to_chat(src, "<span class='danger'>Error: Use the admin IRC/Discord channel, nerd.</span>", confidential = TRUE)
return
else
//get message text, limit it's length.and clean/escape html
if(!msg)
@@ -128,11 +127,16 @@
else
if(holder)
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
to_chat(src, "<span class='danger'><b>Message not sent:</b></span><br>[msg]", confidential = TRUE)
to_chat(src, "<span class='danger'><b>Message not sent:</b></span><br>[sanitize(msg)]", confidential = TRUE)
if(recipient_ticket)
recipient_ticket.AddInteraction("<b>No client found, message not sent:</b><br>[msg]")
return
else
//clean the message if it's not sent by a high-rank admin
if(!check_rights(R_SERVER|R_DEBUG,0)||external)//no sending html to the poor bots
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
if(!msg)
return
current_ticket.MessageNoRecipient(msg)
return
@@ -141,7 +145,7 @@
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
return
if (src.handle_spam_prevention(msg,MUTE_ADMINHELP))
if(src.handle_spam_prevention(msg,MUTE_ADMINHELP))
return
//clean the message if it's not sent by a high-rank admin
+1 -1
View File
@@ -1,5 +1,5 @@
/datum/admins/proc/open_borgopanel(borgo in GLOB.silicon_mobs)
set category = "Admin"
set category = "Admin.Game"
set name = "Show Borg Panel"
set desc = "Show borg panel"
+2 -2
View File
@@ -1,6 +1,6 @@
/client/proc/cinematic()
set name = "cinematic"
set category = "Fun"
set category = "Admin.Fun"
set desc = "Shows a cinematic." // Intended for testing but I thought it might be nice for events on the rare occasion Feel free to comment it out if it's not wanted.
set hidden = 1
if(!SSticker)
@@ -8,4 +8,4 @@
var/datum/cinematic/choice = input(src,"Cinematic","Choose",null) as anything in subtypesof(/datum/cinematic)
if(choice)
Cinematic(initial(choice.id),world,null)
Cinematic(initial(choice.id),world,null)
+1 -8
View File
@@ -1,5 +1,5 @@
/client/proc/dsay(msg as text)
set category = "Special Verbs"
set category = "Admin.Game"
set name = "Dsay"
set hidden = 1
if(!holder)
@@ -23,13 +23,6 @@
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[uppertext(holder.rank)]([src.holder.fakekey ? pick(nicknames) : src.key])</span> says, <span class='message'>\"[emoji_parse(msg)]\"</span></span>"
// var/rank_name = holder.rank
// var/admin_name = key
// if(holder.fakekey)
// rank_name = pick(strings("admin_nicknames.json", "ranks", "config")) please use this soon.
// admin_name = pick(strings("admin_nicknames.json", "names", "config"))
// var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[rank_name]([admin_name])</span> says, <span class='message'>\"[emoji_parse(msg)]\"</span></span>"
for (var/mob/M in GLOB.player_list)
if(isnewplayer(M))
continue
+35 -8
View File
@@ -27,7 +27,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Air Status In Location") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_robotize(mob/M in GLOB.mob_list)
set category = "Fun"
set category = "Admin.Fun"
set name = "Make Robot"
if(!SSticker.HasRoundStarted())
@@ -43,7 +43,7 @@
alert("Invalid mob")
/client/proc/cmd_admin_blobize(mob/M in GLOB.mob_list)
set category = "Fun"
set category = "Admin.Fun"
set name = "Make Blob"
if(!SSticker.HasRoundStarted())
@@ -58,7 +58,7 @@
/client/proc/cmd_admin_animalize(mob/M in GLOB.mob_list)
set category = "Fun"
set category = "Admin.Fun"
set name = "Make Simple Animal"
if(!SSticker.HasRoundStarted())
@@ -79,7 +79,7 @@
/client/proc/makepAI(turf/T in GLOB.mob_list)
set category = "Fun"
set category = "Admin.Fun"
set name = "Make pAI"
set desc = "Specify a location to spawn a pAI device, then specify a key to play that pAI"
@@ -106,7 +106,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Make pAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_alienize(mob/M in GLOB.mob_list)
set category = "Fun"
set category = "Admin.Fun"
set name = "Make Alien"
if(!SSticker.HasRoundStarted())
@@ -121,7 +121,7 @@
alert("Invalid mob")
/client/proc/cmd_admin_slimeize(mob/M in GLOB.mob_list)
set category = "Fun"
set category = "Admin.Fun"
set name = "Make slime"
if(!SSticker.HasRoundStarted())
@@ -211,7 +211,7 @@
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has granted [M.key] full access.</span>")
/client/proc/cmd_assume_direct_control(mob/M in GLOB.mob_list)
set category = "Admin"
set category = "Admin.Game"
set name = "Assume direct control"
set desc = "Direct intervention"
@@ -229,6 +229,33 @@
qdel(adminmob)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Assume Direct Control") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_give_direct_control(mob/M in GLOB.mob_list)
set category = "Admin.Game"
set name = "Give direct control"
if(!M)
return
if(M.ckey)
if(alert("This mob is being controlled by [M.key]. Are you sure you wish to give someone else control of it? [M.key] will be made a ghost.",,"Yes","No") != "Yes")
return
var/client/newkey = input(src, "Pick the player to put in control.", "New player") as null|anything in sortList(GLOB.clients)
var/mob/oldmob = newkey.mob
var/delmob = FALSE
if((isobserver(oldmob) || alert("Do you want to delete [newkey]'s old mob?","Delete?","Yes","No") != "No"))
delmob = TRUE
if(!M || QDELETED(M))
to_chat(usr, "<span class='warning'>The target mob no longer exists, aborting.</span>")
return
if(M.ckey)
M.ghostize(FALSE)
M.ckey = newkey.key
M.client?.init_verbs()
if(delmob)
qdel(oldmob)
message_admins("<span class='adminnotice'>[key_name_admin(usr)] gave away direct control of [M] to [newkey].</span>")
log_admin("[key_name(usr)] gave away direct control of [M] to [newkey].")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Direct Control") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_test_atmos_controllers()
set category = "Mapping"
set name = "Test Atmos Monitoring Consoles"
@@ -455,7 +482,7 @@
cmd_admin_areatest(FALSE)
/client/proc/cmd_admin_dress(mob/M in GLOB.mob_list)
set category = "Fun"
set category = "Admin.Events"
set name = "Select equipment"
if(!(ishuman(M) || isobserver(M)))
alert("Invalid mob")
+1 -1
View File
@@ -1,5 +1,5 @@
/client/proc/roll_dices()
set category = "Fun"
set category = "Admin.Fun"
set name = "Roll Dice"
if(!check_rights(R_FUN))
return
+2
View File
@@ -14,6 +14,8 @@
browseserverlogs("[GLOB.log_directory]/")
/client/proc/browseserverlogs(path = "data/logs/")
if(!check_rights(R_SENSITIVE))
return
path = browse_files(path)
if(!path)
return
+16 -11
View File
@@ -5,7 +5,7 @@
var/ntype = text2num(type)
//Add client links
var/dat = ""
var/list/dat = list()
if(M.client)
dat += "<center><p>Client</p></center>"
dat += "<center>"
@@ -46,22 +46,27 @@
var/log_source = M.logging;
if(source == LOGSRC_CLIENT && M.client) //if client doesn't exist just fall back to the mob log
log_source = M.client.player_details.logging //should exist, if it doesn't that's a bug, don't check for it not existing
var/list/concatenated_logs = list()
for(var/log_type in log_source)
var/nlog_type = text2num(log_type)
if(nlog_type & ntype)
var/list/reversed = log_source[log_type]
if(islist(reversed))
reversed = reverseRange(reversed.Copy())
for(var/entry in reversed)
dat += "<font size=2px><b>[entry]</b><br>[reversed[entry]]</font><br>"
dat += "<hr>"
var/list/all_the_entrys = log_source[log_type]
for(var/entry in all_the_entrys)
concatenated_logs += "<b>[entry]</b><br>[all_the_entrys[entry]]"
if(length(concatenated_logs))
sortTim(concatenated_logs, cmp = /proc/cmp_text_dsc) //Sort by timestamp.
dat += "<font size=2px>"
dat += concatenated_logs.Join("<br>")
dat += "</font>"
usr << browse(dat, "window=invidual_logging_[key_name(M)];size=600x480")
var/datum/browser/popup = new(usr, "invidual_logging_[key_name(M)]", "Individual Logs", 600, 600)
popup.set_content(dat.Join())
popup.open()
/proc/individual_logging_panel_link(mob/M, log_type, log_src, label, selected_src, selected_type)
var/slabel = label
if(selected_type == log_type && selected_src == log_src)
slabel = "<b>\[[label]\]</b>"
return "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[log_type];log_src=[log_src]'>[slabel]</a>"
//This is necessary because num2text drops digits and rounds on big numbers. If more defines get added in the future it could break again.
log_type = num2text(log_type, MAX_BITFLAG_DIGITS)
return "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[log_type];log_src=[log_src]'>[slabel]</a>"
+1 -1
View File
@@ -41,4 +41,4 @@
message_admins("[key_name_admin(usr)] is changing the map to [VM.map_name]")
log_admin("[key_name(usr)] is changing the map to [VM.map_name]")
if (SSmapping.changemap(VM) == 0)
message_admins("[key_name_admin(usr)] has changed the map to [VM.map_name]")
message_admins("[key_name_admin(usr)] has changed the map to [VM.map_name]")
+1 -1
View File
@@ -1,7 +1,7 @@
/client/proc/one_click_antag()
set name = "Create Antagonist"
set desc = "Auto-create an antagonist of your choice"
set category = "Admin"
set category = "Admin.Events"
if(holder)
holder.one_click_antag()
+1 -1
View File
@@ -28,4 +28,4 @@ GLOBAL_VAR_INIT(highlander, FALSE)
addtimer(CALLBACK(src, .proc/only_one), 420)
/mob/living/carbon/human/proc/make_scottish()
mind.add_antag_datum(/datum/antagonist/highlander)
mind.add_antag_datum(/datum/antagonist/highlander)
+9 -11
View File
@@ -1,15 +1,13 @@
/client/proc/play_sound(S as sound)
set category = "Fun"
set category = "Admin.Fun"
set name = "Play Global Sound"
if(!check_rights(R_SOUNDS))
return
var/freq = 1
var/vol = input(usr, "What volume would you like the sound to play at?",, 100) as null|num
if(!vol)
return
var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
if(!freq)
freq = 1
vol = clamp(vol, 1, 100)
var/sound/admin_sound = new()
@@ -18,14 +16,14 @@
admin_sound.channel = CHANNEL_ADMIN
admin_sound.frequency = freq
admin_sound.wait = 1
admin_sound.repeat = 0
admin_sound.repeat = FALSE
admin_sound.status = SOUND_STREAM
admin_sound.volume = vol
var/res = alert(usr, "Show the title of this song to the players?",, "Yes","No", "Cancel")
switch(res)
if("Yes")
to_chat(world, "<span class='boldannounce'>An admin played: [S]</span>")
to_chat(world, "<span class='boldannounce'>An admin played: [S]</span>", confidential = TRUE)
if("Cancel")
return
@@ -42,18 +40,18 @@
/client/proc/play_local_sound(S as sound)
set category = "Fun"
set category = "Admin.Fun"
set name = "Play Local Sound"
if(!check_rights(R_SOUNDS))
return
log_admin("[key_name(src)] played a local sound [S]")
message_admins("[key_name_admin(src)] played a local sound [S]")
playsound(get_turf(src.mob), S, 50, 0, 0)
playsound(get_turf(src.mob), S, 50, FALSE, FALSE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Local Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/play_web_sound()
set category = "Fun"
set category = "Admin.Fun"
set name = "Play Internet Sound"
if(!check_rights(R_SOUNDS))
return
@@ -136,7 +134,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Internet Sound")
/client/proc/manual_play_web_sound()
set category = "Fun"
set category = "Admin.Fun"
set name = "Manual Play Internet Sound"
if(!check_rights(R_SOUNDS))
return
@@ -182,7 +180,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Manual Play Internet Sound")
/client/proc/set_round_end_sound(S as sound)
set category = "Fun"
set category = "Admin.Fun"
set name = "Set Round End Sound"
if(!check_rights(R_SOUNDS))
return
+2
View File
@@ -48,6 +48,8 @@
if(C.prefs.toggles & SOUND_PRAYERS)
if(usr.job == "Chaplain")
SEND_SOUND(C, sound('sound/effects/pray.ogg'))
else
SEND_SOUND(C, sound('sound/effects/ding.ogg'))
to_chat(usr, "<span class='info'>You pray to the gods: \"[msg_tmp]\"</span>")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Prayer") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+76 -35
View File
@@ -20,7 +20,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Drop Everything") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list)
set category = "Special Verbs"
set category = "Admin.Events"
set name = "Subtle Message"
if(!ismob(M))
@@ -46,7 +46,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Subtle Message") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_headset_message(mob/M in GLOB.mob_list)
set category = "Special Verbs"
set category = "Admin.Events"
set name = "Headset Message"
admin_headset_message(M)
@@ -128,7 +128,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Modify Antagonist Reputation") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_world_narrate()
set category = "Special Verbs"
set category = "Admin.Events"
set name = "Global Narrate"
if(!check_rights(R_ADMIN))
@@ -144,7 +144,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Global Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_direct_narrate(mob/M)
set category = "Special Verbs"
set category = "Admin.Events"
set name = "Direct Narrate"
if(!check_rights(R_ADMIN))
@@ -169,7 +169,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Direct Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_local_narrate(atom/A)
set category = "Special Verbs"
set category = "Admin.Events"
set name = "Local Narrate"
if(!check_rights(R_ADMIN))
@@ -190,7 +190,7 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Local Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_godmode(mob/M in GLOB.mob_list)
set category = "Special Verbs"
set category = "Admin.Game"
set name = "Godmode"
if(!check_rights(R_ADMIN))
return
@@ -335,7 +335,7 @@ Works kind of like entering the game with a new character. Character receives a
Traitors and the like can also be revived with the previous role mostly intact.
/N */
/client/proc/respawn_character()
set category = "Special Verbs"
set category = "Admin.Game"
set name = "Respawn Character"
set desc = "Respawn a person that has been gibbed/dusted/killed. They must be a ghost for this to work and preferably should not have a body to go back into."
if(!check_rights(R_ADMIN))
@@ -503,7 +503,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
return new_character
/client/proc/cmd_admin_add_freeform_ai_law()
set category = "Fun"
set category = "Admin.Events"
set name = "Add Custom AI law"
if(!check_rights(R_ADMIN))
@@ -546,7 +546,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
SSblackbox.record_feedback("tally", "admin_verb", 1, "Rejuvinate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_create_centcom_report()
set category = "Special Verbs"
set category = "Admin.Events"
set name = "Create Command Report"
if(!check_rights(R_ADMIN))
@@ -572,7 +572,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
SSblackbox.record_feedback("tally", "admin_verb", 1, "Create Command Report") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_change_command_name()
set category = "Special Verbs"
set category = "Admin.Events"
set name = "Change Command Name"
if(!check_rights(R_ADMIN))
@@ -595,7 +595,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
admin_delete(A)
/client/proc/cmd_admin_list_open_jobs()
set category = "Admin"
set category = "Admin.Game"
set name = "Manage Job Slots"
if(!check_rights(R_ADMIN))
@@ -604,7 +604,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
SSblackbox.record_feedback("tally", "admin_verb", 1, "Manage Job Slots") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in world)
set category = "Special Verbs"
set category = "Admin.Fun"
set name = "Explosion"
if(!check_rights(R_ADMIN))
@@ -640,7 +640,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
return
/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in world)
set category = "Special Verbs"
set category = "Admin.Fun"
set name = "EM Pulse"
if(!check_rights(R_ADMIN))
@@ -656,7 +656,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
SSblackbox.record_feedback("tally", "admin_verb", 1, "EM Pulse") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_gib(mob/M in GLOB.mob_list)
set category = "Special Verbs"
set category = "Admin.Fun"
set name = "Gib"
if(!check_rights(R_ADMIN))
@@ -683,7 +683,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
/client/proc/cmd_admin_gib_self()
set name = "Gibself"
set category = "Fun"
set category = "Admin.Fun"
var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No")
if(confirm == "Yes")
@@ -702,14 +702,14 @@ Traitors and the like can also be revived with the previous role mostly intact.
SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Contents") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/toggle_view_range()
set category = "Special Verbs"
set category = "Admin.Game"
set name = "Change View Range"
set desc = "switches between 1x and custom views"
if(view == CONFIG_GET(string/default_view))
change_view(input("Select view range:", "FUCK YE", 7) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128))
if(view_size.getView() == view_size.default)
view_size.setTo(input("Select view range:", "FUCK YE", 7) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128) - 7)
else
change_view(CONFIG_GET(string/default_view))
view_size.resetToDefault(getScreenSize(prefs.widescreenpref))
log_admin("[key_name(usr)] changed their view range to [view].")
//message_admins("\blue [key_name_admin(usr)] changed their view range to [view].") //why? removed by order of XSI
@@ -717,8 +717,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Change View Range", "[view]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/admin_call_shuttle()
set category = "Admin"
set category = "Admin.Events"
set name = "Call Shuttle"
if(EMERGENCY_AT_LEAST_DOCKED)
@@ -738,7 +737,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
return
/client/proc/admin_cancel_shuttle()
set category = "Admin"
set category = "Admin.Events"
set name = "Cancel Shuttle"
if(!check_rights(0))
return
@@ -754,9 +753,51 @@ Traitors and the like can also be revived with the previous role mostly intact.
message_admins("<span class='adminnotice'>[key_name_admin(usr)] admin-recalled the emergency shuttle.</span>")
return
/*
/client/proc/admin_disable_shuttle()
set category = "Admin.Events"
set name = "Disable Shuttle"
if(!check_rights(R_ADMIN))
return
if(SSshuttle.emergency.mode == SHUTTLE_DISABLED)
to_chat(usr, "<span class='warning'>Error, shuttle is already disabled.</span>")
return
if(alert(src, "You sure?", "Confirm", "Yes", "No") != "Yes")
return
message_admins("<span class='adminnotice'>[key_name_admin(usr)] disabled the shuttle.</span>")
SSshuttle.lastMode = SSshuttle.emergency.mode
SSshuttle.lastCallTime = SSshuttle.emergency.timeLeft(1)
SSshuttle.adminEmergencyNoRecall = TRUE
SSshuttle.emergency.setTimer(0)
SSshuttle.emergency.mode = SHUTTLE_DISABLED
priority_announce("Warning: Emergency Shuttle uplink failure, shuttle disabled until further notice.", "Emergency Shuttle Uplink Alert", 'sound/misc/announce_dig.ogg')
/client/proc/admin_enable_shuttle()
set category = "Admin - Events"
set category = "Admin.Events"
set name = "Enable Shuttle"
if(!check_rights(R_ADMIN))
return
if(SSshuttle.emergency.mode != SHUTTLE_DISABLED)
to_chat(usr, "<span class='warning'>Error, shuttle not disabled.</span>")
return
if(alert(src, "You sure?", "Confirm", "Yes", "No") != "Yes")
return
message_admins("<span class='adminnotice'>[key_name_admin(usr)] enabled the emergency shuttle.</span>")
SSshuttle.adminEmergencyNoRecall = FALSE
SSshuttle.emergencyNoRecall = FALSE
if(SSshuttle.lastMode == SHUTTLE_DISABLED) //If everything goes to shit, fix it.
SSshuttle.lastMode = SHUTTLE_IDLE
SSshuttle.emergency.mode = SSshuttle.lastMode
if(SSshuttle.lastCallTime < 10 SECONDS && SSshuttle.lastMode != SHUTTLE_IDLE)
SSshuttle.lastCallTime = 10 SECONDS //Make sure no insta departures.
SSshuttle.emergency.setTimer(SSshuttle.lastCallTime)
priority_announce("Warning: Emergency Shuttle uplink reestablished, shuttle enabled.", "Emergency Shuttle Uplink Alert", 'sound/misc/announce_dig.ogg')
*/
/client/proc/everyone_random()
set category = "Fun"
set category = "Admin.Fun"
set name = "Make Everyone Random"
set desc = "Make everyone have a random appearance. You can only use this before rounds!"
@@ -804,7 +845,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
/client/proc/admin_change_sec_level()
set category = "Special Verbs"
set category = "Admin.Events"
set name = "Set Security Level"
set desc = "Changes the security level. Announcement only, i.e. setting to Delta won't activate nuke"
@@ -821,7 +862,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
/client/proc/toggle_nuke(obj/machinery/nuclearbomb/N in GLOB.nuke_list)
set name = "Toggle Nuke"
set category = "Fun"
set category = "Admin.Events"
set popup_menu = 0
if(!check_rights(R_DEBUG))
return
@@ -1016,7 +1057,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
usr << browse(dat, "window=dressup;size=550x600")
/client/proc/toggle_combo_hud()
set category = "Admin"
set category = "Admin.Game"
set name = "Toggle Combo HUD"
set desc = "Toggles the Admin Combo HUD (antag, sci, med, eng)"
@@ -1051,7 +1092,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
/client/proc/run_weather()
set category = "Fun"
set category = "Admin.Events"
set name = "Run Weather"
set desc = "Triggers a weather on the z-level you choose."
@@ -1075,7 +1116,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
SSblackbox.record_feedback("tally", "admin_verb", 1, "Run Weather")
/client/proc/mass_zombie_infection()
set category = "Fun"
set category = "Admin.Fun"
set name = "Mass Zombie Infection"
set desc = "Infects all humans with a latent organ that will zombify \
them on death."
@@ -1095,7 +1136,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
SSblackbox.record_feedback("tally", "admin_verb", 1, "Mass Zombie Infection")
/client/proc/mass_zombie_cure()
set category = "Fun"
set category = "Admin.Fun"
set name = "Mass Zombie Cure"
set desc = "Removes the zombie infection from all humans, returning them to normal."
if(!check_rights(R_ADMIN))
@@ -1113,7 +1154,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
SSblackbox.record_feedback("tally", "admin_verb", 1, "Mass Zombie Cure")
/client/proc/polymorph_all()
set category = "Fun"
set category = "Admin.Fun"
set name = "Polymorph All"
set desc = "Applies the effects of the bolt of change to every single mob."
@@ -1203,7 +1244,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggled Hub Visibility", "[GLOB.hub_visibility ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_toggle_fov()
set category = "Fun"
set category = "Admin.Fun"
set name = "Enable/Disable Field of Vision"
var/static/busy_toggling_fov = FALSE
@@ -1256,7 +1297,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
/client/proc/smite(mob/living/carbon/human/target as mob)
set name = "Smite"
set category = "Fun"
set category = "Admin.Fun"
if(!check_rights(R_ADMIN) || !check_rights(R_FUN))
return
@@ -1306,7 +1347,8 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
new /obj/effect/immovablerod(startT, endT,target)
if(ADMIN_PUNISHMENT_SUPPLYPOD_QUICK)
var/target_path = input(usr,"Enter typepath of an atom you'd like to send with the pod (type \"empty\" to send an empty pod):" ,"Typepath","/obj/item/reagent_containers/food/snacks/grown/harebell") as null|text
var/obj/structure/closet/supplypod/centcompod/pod = new()
var/area/pod_storage_area = locate(/area/centcom/supplypod/podStorage) in GLOB.sortedAreas
var/obj/structure/closet/supplypod/centcompod/pod = new(pick(get_area_turfs(pod_storage_area))) //Lets not runtime
pod.damage = 40
pod.explosionSize = list(0,0,0,2)
pod.effectStun = TRUE
@@ -1319,7 +1361,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(!delivery)
alert("ERROR: Incorrect / improper path given.")
new delivery(pod)
new /obj/effect/abstract/DPtarget(get_turf(target), pod)
new /obj/effect/pod_landingzone(get_turf(target), pod)
if(ADMIN_PUNISHMENT_SUPPLYPOD)
var/datum/centcom_podlauncher/plaunch = new(usr)
if(!holder)
@@ -1331,7 +1373,6 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
plaunch.temp_pod.damage = 40//bring the mother fuckin ruckus
plaunch.temp_pod.explosionSize = list(0,0,0,2)
plaunch.temp_pod.effectStun = TRUE
plaunch.ui_interact(usr)
return //We return here because punish_log() is handled by the centcom_podlauncher datum
if(ADMIN_PUNISHMENT_MAZING)
@@ -27,4 +27,4 @@
if(!SSdbcore.Connect())
message_admins("Database connection failed: " + SSdbcore.ErrorMsg())
else
message_admins("Database connection re-established")
message_admins("Database connection re-established")
+1 -1
View File
@@ -1,5 +1,5 @@
/datum/admins/proc/open_shuttlepanel()
set category = "Admin"
set category = "Admin.Events"
set name = "Shuttle Manipulator"
set desc = "Opens the shuttle manipulator UI."
+1 -1
View File
@@ -1,5 +1,5 @@
/client/proc/triple_ai()
set category = "Fun"
set category = "Admin.Events"
set name = "Create AI Triumvirate"
if(SSticker.current_state > GAME_STATE_PREGAME)
@@ -93,4 +93,4 @@
return "[header][item]</li>"
#undef VV_HTML_ENCODE
#undef VV_HTML_ENCODE
@@ -104,7 +104,7 @@
if (!thing)
continue
var/datum/D = thing
if (D.vv_edit_var(variable, initial(D.vars[variable])) != FALSE)
if (D.vv_edit_var(variable, initial(D.vars[variable]), TRUE) != FALSE)
accepted++
else
rejected++
@@ -135,7 +135,7 @@
for(var/V in varsvars)
new_value = replacetext(new_value,"\[[V]]","[D.vars[V]]")
if (D.vv_edit_var(variable, new_value) != FALSE)
if (D.vv_edit_var(variable, new_value, TRUE) != FALSE)
accepted++
else
rejected++
@@ -161,7 +161,7 @@
if(many && !new_value)
new_value = new type()
if (D.vv_edit_var(variable, new_value) != FALSE)
if (D.vv_edit_var(variable, new_value, TRUE) != FALSE)
accepted++
else
rejected++
@@ -176,7 +176,7 @@
if (!thing)
continue
var/datum/D = thing
if (D.vv_edit_var(variable, new_value) != FALSE)
if (D.vv_edit_var(variable, new_value, TRUE) != FALSE)
accepted++
else
rejected++
@@ -109,7 +109,7 @@ GLOBAL_LIST_EMPTY(deletion_failures)
set name = "Find References"
set src in world
find_references(FALSE)
find_references_legacy(FALSE)
/datum/proc/find_references_legacy(skip_alert)
@@ -164,7 +164,7 @@ GLOBAL_LIST_EMPTY(deletion_failures)
qdel(src, TRUE) //force a qdel
if(!running_find_references)
find_references(TRUE)
find_references_legacy(TRUE)
/datum/verb/qdel_then_if_fail_find_references()
+1 -1
View File
@@ -98,7 +98,7 @@
L.adjustFireLoss(amount)
newamt = L.getFireLoss()
if("toxin")
L.adjustToxLoss(amount)
L.adjustToxLoss(amount, toxins_type = TOX_OMNI, forced = TRUE)
newamt = L.getToxLoss()
if("oxygen")
L.adjustOxyLoss(amount)
@@ -16,4 +16,4 @@
continue
var/datum/team/T = A.get_team()
if(!team_type || istype(T,team_type))
. |= T
. |= T
@@ -50,4 +50,4 @@
/datum/mind/proc/leave_all_antag_huds()
for(var/datum/atom_hud/antag/hud in GLOB.huds)
if(hud.hudusers[current])
hud.leave_hud(current)
hud.leave_hud(current)
@@ -38,7 +38,7 @@
dat += "<A href='byond://?src=[REF(src)];school=[APPRENTICE_ROBELESS]'>Robeless</A><BR>"
dat += "<I>Your apprentice is training to cast spells without their robes. They know Knock and Mindswap.</I><BR>"
dat += "<A href='byond://?src=[REF(src)];school=[APPRENTICE_MARTIAL]'>Martial Artist</a><BR>"
dat += "<I>Your apprentice is training in ancient martial arts. They know the Plasmafist and Nuclear Fist.</I><BR>"
dat += "<I>Your apprentice is training in ancient martial arts. They know an Inner Mantra and the Nuclear Fist technique.</I><BR>"
user << browse(dat, "window=radio")
onclose(user, "radio")
return
@@ -2,22 +2,27 @@
name = "experimental organ replacement"
steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin, /datum/surgery_step/incise, /datum/surgery_step/extract_organ, /datum/surgery_step/gland_insert)
possible_locs = list(BODY_ZONE_CHEST)
ignore_clothes = 1
ignore_clothes = TRUE
/datum/surgery/organ_extraction/can_start(mob/user, mob/living/carbon/target)
if(!ishuman(user))
return 0
return FALSE
var/mob/living/carbon/human/H = user
if(H.dna.species.id == "abductor")
return 1
return TRUE
for(var/obj/item/implant/abductor/A in H.implants)
return 1
return 0
return TRUE
return FALSE
/datum/surgery/organ_extraction/mechanic
name = "Prosthesis experimental organ replacement"
requires_bodypart_type = BODYPART_ROBOTIC
steps = list(/datum/surgery_step/mechanic_open, /datum/surgery_step/open_hatch, /datum/surgery_step/mechanic_unwrench, /datum/surgery_step/prepare_electronics, /datum/surgery_step/extract_organ, /datum/surgery_step/gland_insert)
/datum/surgery_step/extract_organ
name = "remove heart"
accept_hand = 1
accept_hand = TRUE
time = 32
var/obj/item/organ/IC = null
var/list/organ_types = list(/obj/item/organ/heart)
@@ -34,10 +39,10 @@
user.visible_message("[user] pulls [IC] out of [target]'s [target_zone]!", "<span class='notice'>You pull [IC] out of [target]'s [target_zone].</span>")
user.put_in_hands(IC)
IC.Remove()
return 1
return TRUE
else
to_chat(user, "<span class='warning'>You don't find anything in [target]'s [target_zone]!</span>")
return 1
return TRUE
/datum/surgery_step/gland_insert
name = "insert gland"
@@ -52,4 +57,4 @@
user.temporarilyRemoveItemFromInventory(tool, TRUE)
var/obj/item/organ/heart/gland/gland = tool
gland.Insert(target, 2)
return 1
return TRUE
@@ -110,4 +110,4 @@
active = 0
/obj/item/organ/heart/gland/proc/activate()
return
return
@@ -17,4 +17,4 @@
/obj/item/organ/heart/gland/access/Remove(special = FALSE)
if(!QDELETED(owner))
UnregisterSignal(owner, COMSIG_MOB_ALLOWED)
return ..()
return ..()
@@ -15,4 +15,4 @@
var/mob/living/carbon/human/H = owner
var/datum/species/species = H.dna.species
to_chat(H, "<span class='warning'>You feel your blood heat up for a moment.</span>")
species.exotic_blood = get_random_reagent_id()
species.exotic_blood = get_random_reagent_id()
@@ -17,4 +17,4 @@
var/chem_to_add = pick(possible_reagents)
owner.reagents.add_reagent(chem_to_add, 2)
owner.adjustToxLoss(-5, TRUE, TRUE)
..()
..()
@@ -12,4 +12,4 @@
/obj/item/organ/heart/gland/egg/activate()
owner.visible_message("<span class='alertalien'>[owner] [pick(EGG_LAYING_MESSAGES)]</span>")
var/turf/T = owner.drop_location()
new /obj/item/reagent_containers/food/snacks/egg/gland(T)
new /obj/item/reagent_containers/food/snacks/egg/gland(T)
@@ -24,4 +24,4 @@
/obj/item/organ/heart/gland/electric/proc/zap()
tesla_zap(owner, 4, 8000, ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_MOB_STUN)
playsound(get_turf(owner), 'sound/magic/lightningshock.ogg', 50, TRUE)
playsound(get_turf(owner), 'sound/magic/lightningshock.ogg', 50, TRUE)
@@ -39,7 +39,7 @@
if(!limb)
replace_limb(zone)
return
if((limb.get_damage() >= (limb.max_damage / 2)) || (limb.status == BODYPART_ROBOTIC))
if((limb.get_damage() >= (limb.max_damage / 2)) || limb.is_robotic_limb(FALSE))
replace_limb(zone, limb)
return
@@ -58,7 +58,7 @@
return
var/obj/item/bodypart/chest/chest = owner.get_bodypart(BODY_ZONE_CHEST)
if((chest.get_damage() >= (chest.max_damage / 4)) || (chest.status == BODYPART_ROBOTIC))
if((chest.get_damage() >= (chest.max_damage / 4)) || chest.is_robotic_limb(FALSE))
replace_chest(chest)
return
@@ -158,7 +158,7 @@
addtimer(CALLBACK(src, .proc/keep_replacing_blood), 30)
/obj/item/organ/heart/gland/heal/proc/replace_chest(obj/item/bodypart/chest/chest)
if(chest.status == BODYPART_ROBOTIC)
if(chest.is_robotic_limb(FALSE))
owner.visible_message("<span class='warning'>[owner]'s [chest.name] rapidly expels its mechanical components, replacing them with flesh!</span>", "<span class='userdanger'>Your [chest.name] rapidly expels its mechanical components, replacing them with flesh!</span>")
playsound(owner, 'sound/magic/clockwork/anima_fragment_attack.ogg', 50, TRUE)
var/list/dirs = GLOB.alldirs.Copy()
@@ -175,4 +175,4 @@
var/obj/item/bodypart/chest/new_chest = new(null)
new_chest.replace_limb(owner, TRUE)
qdel(chest)
qdel(chest)
@@ -61,4 +61,4 @@
to_chat(H, "<span class='userdanger'>You feel the compulsion fade, and you <i>completely forget</i> about your previous orders.</span>")
H.clear_alert("mind_control")
active_mind_control = FALSE
return TRUE
return TRUE
@@ -19,4 +19,4 @@
var/turf/open/T = get_turf(owner)
if(istype(T))
T.atmos_spawn_air("plasma=50;TEMP=[T20C]")
owner.vomit()
owner.vomit()
@@ -44,4 +44,4 @@
if(active_mind_control)
to_chat(entangled_mob, "<span class='userdanger'>You feel the compulsion fade, and you completely forget about your previous orders.</span>")
entangled_mob.clear_alert("mind_control")
..()
..()
@@ -23,4 +23,4 @@
var/mob/living/simple_animal/slime/Slime = new(get_turf(owner), "grey")
Slime.Friends = list(owner)
Slime.Leader = owner
Slime.Leader = owner
@@ -11,4 +11,4 @@
to_chat(owner, "<span class='warning'>You feel something crawling in your skin.</span>")
owner.faction |= "spiders"
var/obj/structure/spider/spiderling/S = new(owner.drop_location())
S.directive = "Protect your nest inside [owner.real_name]."
S.directive = "Protect your nest inside [owner.real_name]."
@@ -12,4 +12,4 @@
to_chat(owner, "<span class='notice'>You feel unlike yourself.</span>")
randomize_human(owner)
var/species = pick(list(/datum/species/human, /datum/species/lizard, /datum/species/insect, /datum/species/fly))
owner.set_species(species)
owner.set_species(species)
@@ -9,4 +9,4 @@
/obj/item/organ/heart/gland/ventcrawling/activate()
to_chat(owner, "<span class='notice'>You feel very stretchy.</span>")
owner.ventcrawler = VENTCRAWLER_ALWAYS
owner.AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
@@ -31,4 +31,4 @@
var/datum/symptom/S = new chosen_symptom
A.symptoms += S
A.Refresh() //just in case someone already made and named the same disease
return A
return A
@@ -69,6 +69,7 @@ GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear))
build_path = /obj/item/abductor_machine_beacon/chem_dispenser
category = "Advanced Gear"
/*
/datum/abductor_gear/shrink_ray
name = "Shrink Ray Blaster"
description = "This is a piece of frightening alien tech that enhances the magnetic pull of atoms in a localized space to temporarily make an object shrink. \
@@ -77,3 +78,4 @@ GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear))
cost = 2
build_path = /obj/item/gun/energy/shrink_ray
category = "Advanced Gear"
*/
@@ -9,4 +9,4 @@
/obj/structure/fluff/iced_abductor/Destroy()
var/turf/T = get_turf(src)
new /obj/effect/mob_spawn/human/abductor(T)
. = ..()
. = ..()
@@ -26,7 +26,7 @@
return
flick("blob_resource_glow", src)
if(overmind)
overmind.add_points(1)
overmind.add_points(2)
resource_delay = world.time + 40 + overmind.resource_blobs.len * 2.5 //4 seconds plus a quarter second for each resource blob the overmind has
else
resource_delay = world.time + 40
@@ -45,8 +45,12 @@
desc = "A solid wall of slightly twitching tendrils with a reflective glow."
damaged_desc = "A wall of twitching tendrils with a reflective glow."
icon_state = "blob_glow"
flags_1 = DEFAULT_RICOCHET_1
flags_ricochet = RICOCHET_SHINY
point_return = 8
max_integrity = 100
brute_resist = 1
explosion_block = 2
/obj/structure/blob/shield/reflective/check_projectile_ricochet(obj/item/projectile/P)
return PROJECTILE_RICOCHET_FORCE
@@ -277,3 +277,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
var/datum/antagonist/blob/B = mind.has_antag_datum(/datum/antagonist/blob)
if(!B)
mind.add_antag_datum(/datum/antagonist/blob)
//the same but it's forced to be allowed by default as cameras usually don't allow emoting
/mob/camera/blob/emote(act, m_type=1, message = null, intentional = FALSE, forced = TRUE)
. = ..()
+2 -1
View File
@@ -147,7 +147,7 @@
set category = "Blob"
set name = "Create Factory Blob (60)"
set desc = "Create a spore tower that will spawn spores to harass your enemies."
createSpecial(60, /obj/structure/blob/factory, 7, 1)
createSpecial(60, /obj/structure/blob/factory, 5, 1)
/mob/camera/blob/verb/create_blobbernaut()
set category = "Blob"
@@ -195,6 +195,7 @@
to_chat(blobber, "The <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font> reagent [blobstrain.shortdesc ? "[blobstrain.shortdesc]" : "[blobstrain.description]"]")
else
to_chat(src, "<span class='warning'>You could not conjure a sentience for your blobbernaut. Your points have been refunded. Try again later.</span>")
B.naut = FALSE
add_points(40)
/mob/camera/blob/verb/relocate_core()
@@ -81,4 +81,4 @@
/datum/antagonist/bloodsucker/proc/AssignRandomBane()
return
return
@@ -120,9 +120,14 @@
if(bruteheal + fireheal + toxinheal > 0) // Just a check? Don't heal/spend, and return.
if(mult == 0)
return TRUE
// We have damage. Let's heal (one time)
C.adjustBruteLoss(-bruteheal * mult, forced = TRUE)// Heal BRUTE / BURN in random portions throughout the body.
C.adjustFireLoss(-fireheal * mult, forced = TRUE)
var/list/damaged_parts = C.get_damaged_bodyparts(TRUE,TRUE, status = list(BODYPART_ORGANIC, BODYPART_HYBRID, BODYPART_NANITES))
if(damaged_parts.len)
for(var/obj/item/bodypart/part in damaged_parts) // Heal BRUTE / BURN equally distibuted over all damaged bodyparts.
part.heal_damage((bruteheal * mult)/damaged_parts.len, (fireheal * mult)/damaged_parts.len, only_organic = FALSE, updating_health = FALSE)
C.updatehealth()
C.update_damage_overlays()
C.adjustToxLoss(-toxinheal * mult * 2, forced = TRUE) //Toxin healing because vamps arent immune
//C.heal_overall_damage(bruteheal * mult, fireheal * mult) // REMOVED: We need to FORCE this, because otherwise, vamps won't heal EVER. Swapped to above.
AddBloodVolume((bruteheal * -0.5 + fireheal * -1 + toxinheal * -0.2) / mult * costMult) // Costs blood to heal
@@ -276,7 +281,7 @@
/datum/antagonist/bloodsucker/proc/FinalDeath()
//Dont bother if we are already supposed to be dead
if(FinalDeath)
return
return
FinalDeath = TRUE //We are now supposed to die. Lets not spam it.
if(!iscarbon(owner.current)) //Check for non carbons.
owner.current.gib()
@@ -30,6 +30,7 @@
var/can_be_staked = FALSE // Only Feed can happen with a stake in you.
var/cooldown_static = FALSE // Feed, Masquerade, and One-Shot powers don't improve their cooldown.
//var/not_bloodsucker = FALSE // This goes to Vassals or Hunters, but NOT bloodsuckers.
var/must_be_concious = TRUE //Can't use this ability while unconcious.
/datum/action/bloodsucker/New()
if(bloodcost > 0)
@@ -101,6 +102,11 @@
if(display_error)
to_chat(owner, "<span class='warning'>Garlic in your blood is interfering with your powers!</span>")
return FALSE
if(must_be_concious)
if(owner.stat != CONSCIOUS)
if(display_error)
to_chat(owner, "<span class='warning'>You can't do this while you are unconcious!</span>")
return FALSE
// Incap?
if(must_be_capacitated)
var/mob/living/L = owner
@@ -13,8 +13,9 @@
var/nightime_duration = 900 //15 Minutes
/obj/effect/sunlight/Initialize()
countdown()
hud_tick()
. = ..()
INVOKE_ASYNC(src, .proc/countdown)
INVOKE_ASYNC(src, .proc/hud_tick)
/obj/effect/sunlight/proc/countdown()
set waitfor = FALSE
@@ -113,4 +113,4 @@
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
*/
*/
@@ -90,7 +90,7 @@
/datum/antagonist/vassal/greet()
to_chat(owner, "<span class='userdanger'>You are now the mortal servant of [master.owner.current], a bloodsucking vampire!</span>")
to_chat(owner, "<span class='boldannounce'>The power of [master.owner.current.p_their()] immortal blood compells you to obey [master.owner.current.p_them()] in all things, even offering your own life to prolong theirs.<br>\
You are not required to obey any other Bloodsucker, for only [master.owner.current] is your master. The laws of Nanotransen do not apply to you now; only your vampiric master's word must be obeyed.<span>")
You are not required to obey any other Bloodsucker, for only [master.owner.current] is your master. The laws of Nanotrasen do not apply to you now; only your vampiric master's word must be obeyed.<span>")
// Effects...
owner.current.playsound_local(null, 'sound/magic/mutate.ogg', 100, FALSE, pressure_affected = FALSE)
//owner.store_memory("You became the mortal servant of [master.owner.current], a bloodsucking vampire!")
@@ -43,14 +43,13 @@
// This exists so Hardened/Silver Stake can't have a welding torch used on them.
/obj/item/stake/basic/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/weldingtool))
if(W.tool_behaviour == TOOL_WELDER)
//if (amWelded)
// to_chat(user, "<span class='warning'>This stake has already been treated with fire.</span>")
// return
//amWelded = TRUE
// Weld it
var/obj/item/weldingtool/WT = W
if(WT.use(0))//remove_fuel(0,user))
if(W.use(0))//remove_fuel(0,user))
user.visible_message("[user.name] scorched the pointy end of [src] with the welding tool.", \
"<span class='notice'>You scorch the pointy end of [src] with the welding tool.</span>", \
"<span class='italics'>You hear welding.</span>")
@@ -164,11 +164,11 @@
if(istype(W, cutting_tool))
to_chat(user, "<span class='notice'>This is a much more complex mechanical structure than you thought. You don't know where to begin cutting [src].</span>")
return
else if(anchored && istype(W, /obj/item/wrench)) // Can't unanchor unless owner.
else if(anchored && W.tool_behaviour == TOOL_WRENCH) // Can't unanchor unless owner.
to_chat(user, "<span class='danger'>The coffin won't come unanchored from the floor.</span>")
return
if(locked && istype(W, /obj/item/crowbar))
if(locked && W.tool_behaviour == TOOL_CROWBAR)
var/pry_time = pryLidTimer * W.toolspeed // Pry speed must be affected by the speed of the tool.
user.visible_message("<span class='notice'>[user] tries to pry the lid off of [src] with [W].</span>", \
"<span class='notice'>You begin prying the lid off of [src] with [W]. This should take about [DisplayTimeText(pry_time)].</span>")
@@ -16,6 +16,7 @@
can_use_in_torpor = TRUE
must_be_capacitated = TRUE
can_be_immobilized = TRUE
must_be_concious = FALSE
/datum/action/bloodsucker/gohome/CheckCanUse(display_error)
. = ..()
@@ -27,6 +27,7 @@
warn_constant_cost = TRUE
can_use_in_torpor = TRUE // Masquerade is maybe the only one that can do this. It stops your healing.
cooldown_static = TRUE
must_be_concious = FALSE
// NOTE: Firing off vulgar powers disables your Masquerade!
@@ -149,8 +149,6 @@
if(prob(50))
if(LAZYLEN(active_ais()) && prob(100/GLOB.joined_player_list.len))
add_objective(new/datum/objective/destroy, TRUE)
else if(prob(30))
add_objective(new/datum/objective/maroon, TRUE)
else
add_objective(new/datum/objective/assassinate, TRUE)
else
@@ -18,7 +18,6 @@
var/list/stored_profiles = list() //list of datum/changelingprofile
var/datum/changelingprofile/first_prof = null
var/dna_max = 6 //How many extra DNA strands the changeling can store for transformation.
var/absorbedcount = 0
/// did we get succed by another changeling
var/hostile_absorbed = FALSE
@@ -38,8 +37,9 @@
var/mimicing = ""
var/canrespec = 0
var/changeling_speak = 0
var/loudfactor = 0 //Used for blood tests. At 4, blood tests will succeed. At 10, blood tests will result in an explosion.
var/bloodtestwarnings = 0 //Used to track if the ling has been notified that they will pass blood tests.
var/loudfactor = 0 //Used for blood tests. This is is the average loudness of the ling's abilities calculated with the below two vars
var/loudtotal = 0 //Used to keep track of the sum of the ling's loudness
var/totalpurchases = 0 //Used to keep track of how many purchases the ling's made after free abilities have been added
var/datum/dna/chosen_dna
var/obj/effect/proc_holder/changeling/sting/chosen_sting
var/datum/cellular_emporium/cellular_emporium
@@ -139,8 +139,6 @@
/datum/antagonist/changeling/proc/reset_powers()
if(purchasedpowers)
remove_changeling_powers()
loudfactor = 0
bloodtestwarnings = 0
//Repurchase free powers.
for(var/path in all_powers)
var/obj/effect/proc_holder/changeling/S = new path()
@@ -148,6 +146,9 @@
if(!has_sting(S))
purchasedpowers += S
S.on_purchase(owner.current,TRUE)
loudfactor = 0
loudtotal = 0
totalpurchases = 0
/datum/antagonist/changeling/proc/has_sting(obj/effect/proc_holder/changeling/power)
for(var/obj/effect/proc_holder/changeling/P in purchasedpowers)
@@ -192,13 +193,18 @@
geneticpoints -= thepower.dna_cost
purchasedpowers += thepower
thepower.on_purchase(owner.current)
loudfactor += thepower.loudness
if(loudfactor >= 4 && !bloodtestwarnings)
to_chat(owner.current, "<span class='warning'>Our blood is growing flammable. Our blood will react violently to heat.</span>")
bloodtestwarnings = 1
if(loudfactor >= 10 && bloodtestwarnings < 2)
to_chat(owner.current, "<span class='warning'>Our blood has grown extremely flammable. Our blood will react explosively to heat.</span>")
bloodtestwarnings = 2
loudtotal += thepower.loudness
totalpurchases++
var/oldloudness = loudfactor
loudfactor = loudtotal/max(totalpurchases,1)
if(loudfactor >= LINGBLOOD_DETECTION_THRESHOLD && oldloudness < LINGBLOOD_DETECTION_THRESHOLD)
to_chat(owner.current, "<span class='warning'>Our blood has grown flammable. Our blood will now react violently to heat.</span>")
else if(loudfactor < LINGBLOOD_DETECTION_THRESHOLD && oldloudness >= LINGBLOOD_DETECTION_THRESHOLD)
to_chat(owner.current, "<span class='notice'>Our blood has stabilized, and will no longer react violently to heat.</span>")
if(loudfactor > LINGBLOOD_EXPLOSION_THRESHOLD && oldloudness <= LINGBLOOD_EXPLOSION_THRESHOLD)
to_chat(owner.current, "<span class='warning'>Our blood has grown extremely flammable. Our blood will now react explosively to heat.</span>")
else if(loudfactor <= LINGBLOOD_EXPLOSION_THRESHOLD && oldloudness > LINGBLOOD_EXPLOSION_THRESHOLD)
to_chat(owner.current, "<span class='notice'>Our blood has slightly stabilized, and will no longer explode when exposed to heat.</span>")
/datum/antagonist/changeling/proc/readapt()
if(!ishuman(owner.current))
@@ -207,7 +213,7 @@
if(canrespec)
to_chat(owner.current, "<span class='notice'>We have removed our evolutions from this form, and are now ready to readapt.</span>")
reset_powers()
playsound(get_turf(owner.current), 'sound/effects/lingreadapt.ogg', 75, TRUE, 5, soundenvwet = 0)
playsound(get_turf(owner.current), 'sound/effects/lingreadapt.ogg', 75, TRUE, 5)
canrespec = 0
SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, "Readapt")
return 1
@@ -245,12 +251,6 @@
var/mob/living/carbon/user = owner.current
if(!istype(user))
return
if(stored_profiles.len)
var/datum/changelingprofile/prof = stored_profiles[1]
if(prof.dna == user.dna && stored_profiles.len >= dna_max)//If our current DNA is the stalest, we gotta ditch it.
if(verbose)
to_chat(user, "<span class='warning'>We have reached our capacity to store genetic information! We must transform before absorbing more.</span>")
return
if(!target)
return
if(NO_DNA_COPY in target.dna.species.species_traits)
@@ -310,9 +310,6 @@
return prof
/datum/antagonist/changeling/proc/add_profile(datum/changelingprofile/prof)
if(stored_profiles.len > dna_max)
if(!push_out_profile())
return
if(!first_prof)
first_prof = prof
@@ -333,19 +330,6 @@
stored_profiles -= prof
qdel(prof)
/datum/antagonist/changeling/proc/get_profile_to_remove()
for(var/datum/changelingprofile/prof in stored_profiles)
if(!prof.protected)
return prof
/datum/antagonist/changeling/proc/push_out_profile()
var/datum/changelingprofile/removeprofile = get_profile_to_remove()
if(removeprofile)
stored_profiles -= removeprofile
return 1
return 0
/datum/antagonist/changeling/proc/create_initial_profile()
var/mob/living/carbon/C = owner.current //only carbons have dna now, so we have to typecaste
if(ishuman(C))
@@ -449,30 +433,21 @@
destroy_objective.find_target()
objectives += destroy_objective
else
if(prob(70))
var/datum/objective/assassinate/once/kill_objective = new
kill_objective.owner = owner
if(team_mode) //No backstabbing while in a team
kill_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
else
kill_objective.find_target()
objectives += kill_objective
var/datum/objective/assassinate/once/kill_objective = new
kill_objective.owner = owner
if(team_mode) //No backstabbing while in a team
kill_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
else
var/datum/objective/maroon/maroon_objective = new
maroon_objective.owner = owner
if(team_mode)
maroon_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
else
maroon_objective.find_target()
objectives += maroon_objective
kill_objective.find_target()
objectives += kill_objective
if (!(locate(/datum/objective/escape) in objectives) && escape_objective_possible)
var/datum/objective/escape/escape_with_identity/identity_theft = new
identity_theft.owner = owner
identity_theft.target = maroon_objective.target
identity_theft.update_explanation_text()
objectives += identity_theft
escape_objective_possible = FALSE
if(!(locate(/datum/objective/escape) in objectives) && escape_objective_possible && prob(50))
var/datum/objective/escape/escape_with_identity/identity_theft = new
identity_theft.owner = owner
identity_theft.target = kill_objective.target
identity_theft.update_explanation_text()
objectives += identity_theft
escape_objective_possible = FALSE
if (!(locate(/datum/objective/escape) in objectives) && escape_objective_possible)
if(prob(50))
@@ -24,4 +24,4 @@
/obj/effect/proc_holder/changeling/digitalcamo/on_refund(mob/user)
action.Remove(user)
user.digitalcamo = 0
user.digitalinvis = 0
user.digitalinvis = 0
@@ -16,7 +16,7 @@
if(user.stat != DEAD)
user.emote("deathgasp")
user.tod = STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)
user.fakedeath("changeling") //play dead
user.fakedeath("changeling", TRUE) //play dead
user.update_stat()
addtimer(CALLBACK(src, .proc/ready_to_regenerate, user), LING_FAKEDEATH_TIME, TIMER_UNIQUE)
@@ -6,6 +6,10 @@
Shield
Armor
Tentacles
Hatterhat Additions:
claws! (glove slot)
jagged (thieves') claws
bone gauntlets for punching dudes
*/
@@ -17,6 +21,7 @@
chemical_cost = 1000
dna_cost = -1
var/recharge_slowdown = 0
var/silent = FALSE
var/weapon_type
var/weapon_name_simple
@@ -33,6 +38,8 @@
if(!silent)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
user.visible_message("<span class='warning'>With a sickening crunch, [user] reforms [user.p_their()] [weapon_name_simple] into an arm!</span>", "<span class='notice'>We assimilate the [weapon_name_simple] back into our body.</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.chem_recharge_slowdown -= recharge_slowdown
user.update_inv_hands()
return 1
@@ -53,6 +60,8 @@
user.put_in_hands(W)
if(!silent)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.chem_recharge_slowdown += recharge_slowdown
return W
/obj/effect/proc_holder/changeling/weapon/on_refund(mob/user)
@@ -89,7 +98,7 @@
return 1
var/mob/living/carbon/human/H = user
if(istype(H.wear_suit, suit_type) || istype(H.head, helmet_type))
H.visible_message("<span class='warning'>[H] casts off [H.p_their()] [suit_name_simple]!</span>", "<span class='warning'>We cast off our [suit_name_simple].</span>", "<span class='italics'>You hear the organic matter ripping and tearing!</span>")
H.visible_message("<span class='warning'>[H] casts off [H.p_their()] [suit_name_simple]!</span>", "<span class='warning'>We cast off our [suit_name_simple].</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
H.temporarilyRemoveItemFromInventory(H.head, TRUE) //The qdel on dropped() takes care of it
H.temporarilyRemoveItemFromInventory(H.wear_suit, TRUE)
H.update_inv_wear_suit()
@@ -137,10 +146,11 @@
name = "Arm Blade"
desc = "We reform one of our arms into a deadly blade."
helptext = "We may retract our armblade in the same manner as we form it. Cannot be used while in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 10
chemical_cost = 5
dna_cost = 2
loudness = 2
req_human = 1
recharge_slowdown = 0.6
weapon_type = /obj/item/melee/arm_blade
weapon_name_simple = "blade"
action_icon = 'icons/mob/actions/actions_changeling.dmi'
@@ -461,7 +471,7 @@
if(--remaining_uses < 1)
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms [H.p_their()] shield into an arm!</span>", "<span class='notice'>We assimilate our shield into our body</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms [H.p_their()] shield into an arm!</span>", "<span class='notice'>We assimilate our shield into our body.</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
qdel(src)
/***************************************\
@@ -483,7 +493,7 @@
helmet_type = /obj/item/clothing/head/helmet/space/changeling
suit_name_simple = "flesh shell"
helmet_name_simple = "space helmet"
recharge_slowdown = 0.5
recharge_slowdown = 0.6
blood_on_castoff = 1
/obj/item/clothing/suit/space/changeling
@@ -528,11 +538,11 @@
name = "Chitinous Armor"
desc = "We turn our skin into tough chitin to protect us from damage."
helptext = "Upkeep of the armor requires a constant expenditure of chemicals, resulting in a reduced chemical generation. The armor is strong against brute force, but does not provide much protection from lasers. Cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 20
chemical_cost = 10
dna_cost = 1
loudness = 2
req_human = 1
recharge_slowdown = 0.5
recharge_slowdown = 0.6
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_armor"
action_background_icon_state = "bg_ling"
@@ -570,3 +580,167 @@
/obj/item/clothing/head/helmet/changeling/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
/***************************************\
|*****************CLAWS*****************|
\***************************************/
/obj/effect/proc_holder/changeling/gloves
name = "Mangled Claws"
desc = "Go tell a coder if you see this"
helptext = "Yell at Hatterhat, for fucking up Miauw and/or Perakp's work"
chemical_cost = 1000
dna_cost = -1
var/glove_type = /obj/item
var/glove_name_simple = " " // keep these plural bro
var/recharge_slowdown = 0
var/blood_on_castoff = 0
/obj/effect/proc_holder/changeling/gloves/try_to_sting(mob/user, mob/target)
if(check_gloves(user))
return
var/mob/living/carbon/human/H = user
..(H, target)
//checks if we already have claws and casts it off
/obj/effect/proc_holder/changeling/gloves/proc/check_gloves(mob/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(!ishuman(user) || !changeling)
return 1
var/mob/living/carbon/human/H = user
if(istype(H.gloves, glove_type))
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms [H.p_their()] [glove_name_simple] into hands!</span>", "<span class='warning'>We assimilate our [glove_name_simple].</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
H.temporarilyRemoveItemFromInventory(H.gloves, TRUE) //The qdel on dropped() takes care of it
H.update_inv_gloves()
playsound(H.loc, 'sound/effects/blobattack.ogg', 30, 1)
if(blood_on_castoff)
H.add_splatter_floor()
playsound(H.loc, 'sound/effects/splat.ogg', 50, 1) //So real sounds
changeling.chem_recharge_slowdown -= recharge_slowdown
return 1
/obj/effect/proc_holder/changeling/gloves/on_refund(mob/user)
if(!ishuman(user))
return
action.Remove(user)
var/mob/living/carbon/human/H = user
check_gloves(H)
/obj/effect/proc_holder/changeling/gloves/sting_action(mob/living/carbon/human/user)
if(!user.canUnEquip(user.gloves))
to_chat(user, "\the [user.gloves] is stuck to your body, you cannot grow [glove_name_simple] over it!")
return
user.dropItemToGround(user.gloves)
user.equip_to_slot_if_possible(new glove_type(user), SLOT_GLOVES, 1, 1, 1)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.chem_recharge_slowdown += recharge_slowdown
return TRUE
/obj/item/clothing/gloves/claws
name = "claws of doing nothing"
desc = "These shouldn't be here."
icon_state = "bracers"
item_state = "bracers"
transfer_prints = TRUE
body_parts_covered = HANDS
cold_protection = HANDS
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 35, "bio" = 35, "rad" = 35, "fire" = 0, "acid" = 0)
/obj/item/clothing/gloves/claws/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
/obj/item/clothing/gloves/claws/vulture // prying shit off dead/soon to be dead dudes!
name = "jagged claws"
desc = "Good for prying things off of people and looking incredibly creepy."
strip_mod = 2
/obj/effect/proc_holder/changeling/gloves/gauntlets
name = "Bone Gauntlets"
desc = "We turn our hands into solid bone and chitin, sacrificing dexterity for raw strength."
helptext = "These grotesque, bone-and-chitin gauntlets are remarkably good at beating victims senseless, and cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 5 // same cost as armblade because its a sidegrade (sacrifice utility for punching people violently)
dna_cost = 2
loudness = 2
req_human = 1
recharge_slowdown = 0.6
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_gauntlets"
action_background_icon_state = "bg_ling"
glove_type = /obj/item/clothing/gloves/fingerless/pugilist/cling // just punch his head off dude
glove_name_simple = "bone gauntlets"
/obj/item/clothing/gloves/fingerless/pugilist/cling // switches between lesser GotNS and Big Punchy Rib Breaky Hands
name = "hewn bone gauntlets"
icon_state = "ling_gauntlets"
item_state = "ling_gauntlets"
desc = "Rough bone and chitin, pulsing with an abomination barely called \"life\". Good for punching people, not so much for firearms."
transfer_prints = TRUE
item_flags = DROPDEL // whoops
body_parts_covered = ARMS|HANDS
cold_protection = ARMS|HANDS
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 35, "bio" = 35, "rad" = 35, "fire" = 0, "acid" = 0)
enhancement = 6 // first, do harm. all of it. all of the harm. just fuck em up.
wound_enhancement = 6
var/fast_enhancement = 6
var/fast_wound_enhancement = 6
var/slow_enhancement = 12
var/slow_wound_enhancement = 15
silent = TRUE
inherited_trait = TRAIT_CHUNKYFINGERS // how do you expect to shoot anyone with bone covered hands
secondary_trait = TRAIT_MAULER // just punch them idiot
var/fasthands = TRUE
/obj/item/clothing/gloves/fingerless/pugilist/cling/examine(mob/user)
. = ..()
. += "[src] are shaped for [fasthands ? "fast, precise strikes" : "crippling, damaging blows"]."
. += "Alt-click them to change between rapid strikes and strong blows."
/obj/item/clothing/gloves/fingerless/pugilist/cling/AltClick(mob/user)
. = ..()
use_buffs(user, FALSE) // reset
fasthands = !fasthands
if(fasthands)
enhancement = fast_enhancement
wound_enhancement = fast_wound_enhancement
else
enhancement = slow_enhancement // fuck em up kiddo
wound_enhancement = slow_wound_enhancement // really. fuck em up.
to_chat(user, "<span class='notice'>[src] are now formed to allow for [fasthands ? "fast, precise strikes" : "crippling, damaging blows"].</span>")
addtimer(CALLBACK(src, .proc/use_buffs, user, TRUE), 0.1) // go fuckin get em
/obj/item/clothing/gloves/fingerless/pugilist/cling/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
/obj/item/clothing/gloves/fingerless/pugilist/cling/equipped(mob/user, slot)
. = ..()
if(current_equipped_slot == SLOT_GLOVES)
to_chat(user, "<span class='notice'>With [src] formed around our arms, we are ready to fight.</span>")
/obj/item/clothing/gloves/fingerless/pugilist/cling/dropped(mob/user)
. = ..()
if(wornonce)
to_chat(user, "<span class='warning'>With [src] assimilated, we feel less ready to punch things.</span>")
/obj/item/clothing/gloves/fingerless/pugilist/cling/Touch(atom/target, proximity = TRUE)
if(!isliving(target))
return
var/mob/living/M = loc
if(fasthands)
M.SetNextAction(CLICK_CD_RANGE) // fast punches
else
M.SetNextAction(CLICK_CD_GRABBING) // strong punches
return NO_AUTO_CLICKDELAY_HANDLING | ATTACK_IGNORE_ACTION
@@ -57,4 +57,4 @@
/obj/screen/alert/status_effect/agent_pinpointer/changeling
name = "Pheromone Scent"
desc = "The nose always knows."
desc = "The nose always knows."
@@ -29,7 +29,7 @@
for(var/obj/machinery/light/L in range(4, user))
L.on = 1
L.break_light_tube()
playsound(get_turf(user), 'sound/effects/lingscreech.ogg', 75, TRUE, 5, soundenvwet = 0)
playsound(get_turf(user), 'sound/effects/lingscreech.ogg', 75, TRUE, 5)
return TRUE
/obj/effect/proc_holder/changeling/dissonant_shriek
@@ -49,5 +49,5 @@
L.on = 1
L.break_light_tube()
empulse_using_range(get_turf(user), 8, TRUE)
playsound(get_turf(user), 'sound/effects/lingempscreech.ogg', 75, TRUE, 5, soundenvwet = 0)
playsound(get_turf(user), 'sound/effects/lingempscreech.ogg', 75, TRUE, 5)
return TRUE
@@ -22,4 +22,4 @@
if((is_servant_of_ratvar(user) || isobserver(user)) && clockwork_desc)
desc = clockwork_desc
. = ..()
desc = initial(desc)
desc = initial(desc)
@@ -22,7 +22,7 @@
for(var/mob/M in GLOB.player_list)
if(M.z == z)
if(get_dist(src, M) >= 7)
M.playsound_local(src, 'sound/magic/blink.ogg', 10, FALSE, falloff = 10)
M.playsound_local(src, 'sound/magic/blink.ogg', 10, FALSE)
else
M.playsound_local(src, 'sound/magic/blink.ogg', 50, FALSE)
@@ -48,4 +48,4 @@
plane = FLOOR_PLANE
/obj/effect/clockwork/overlay/floor/bloodcult //this is used by BLOOD CULT, it shouldn't use such a path...
icon_state = "cult"
icon_state = "cult"
@@ -216,6 +216,14 @@
else if(get_clockwork_power())
to_chat(L, "<span class='brass'>You feel a slight, static shock.</span>")
/obj/effect/clockwork/sigil/transmission/Initialize()
. = ..()
START_PROCESSING(SSobj, src)
/obj/effect/clockwork/sigil/transmission/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/effect/clockwork/sigil/transmission/process()
var/power_drained = 0
var/power_mod = 0.005
@@ -340,9 +348,9 @@
L.dust()
else if(L.health > min_drain_health)
if(!GLOB.ratvar_awakens && L.stat == CONSCIOUS)
vitality_drained = L.adjustToxLoss(1, forced = TRUE)
vitality_drained = L.adjustToxLoss(1, forced = TRUE, toxins_type = TOX_OMNI)
else
vitality_drained = L.adjustToxLoss(1.5, forced = TRUE)
vitality_drained = L.adjustToxLoss(1.5, forced = TRUE, toxins_type = TOX_OMNI)
if(vitality_drained)
GLOB.clockwork_vitality += vitality_drained
else
@@ -408,7 +416,7 @@
/obj/effect/clockwork/sigil/rite
name = "radiant sigil"
desc = "A glowing sigil glowing with barely-contained power."
desc = "A sigil glowing with barely-contained power."
clockwork_desc = "A sigil that will allow you to perform certain rites on it, provided you have access to sufficient power and materials."
icon_state = "sigiltransmission" //am big lazy - recolored transmission sigil
sigil_name = "Sigil of Rites"
@@ -432,7 +440,8 @@
return
var/list/possible_rites = list()
for(var/datum/clockwork_rite/R in GLOB.all_clockwork_rites)
possible_rites[R] = R
if(is_servant_of_ratvar(user, require_full_power = TRUE) || !R.requires_full_power)
possible_rites[R] = R
var/input_key = input(user, "Choose a rite", "Choosing a rite") as null|anything in possible_rites
if(!input_key)
return
@@ -287,4 +287,4 @@
linked_gateway.visible_message("<span class='warning'>[linked_gateway] begins to destabilise!</span>")
/obj/effect/clockwork/spatial_gateway/stable/pass_through_gateway(atom/movable/A, no_cost = TRUE)
return ..()
return ..()
@@ -1,41 +1,57 @@
//horrifying power drain proc made for clockcult's power drain in lieu of six istypes or six for(x in view) loops
/atom/movable/proc/power_drain(clockcult_user, drain_weapons = FALSE) //This proc as of now is only in use for void volt
/*
horrifying power drain proc made for clockcult's power drain in lieu of six istypes or six for(x in view) loops
args:
clockcult_user: If the user / source has to do with clockcult stuff
drain_weapons: If this drains weaponry, such as batons and guns
recursive: If this recurses through mob / storage contents. ONLY USE THIS IF IT'S NOT CALLED TOO FREQUENTLY, or I'm not liable for any lag / functional issues caused
drain_amount: How much is drained by default; Influenced by a multiplier on most things depending on how much power they usually hold.
*/
/atom/movable/proc/power_drain(clockcult_user, drain_weapons = FALSE, recursive = FALSE, drain_amount = MIN_CLOCKCULT_POWER) //This proc as of now is only in use for void volt and transmission sigils
if(recursive)
var/succ = 0
for(var/V in contents)
var/atom/movable/target = V
succ += target.power_drain(clockcult_user, drain_weapons, recursive, drain_amount)
return succ
var/obj/item/stock_parts/cell/cell = get_cell()
if(cell)
return cell.power_drain(clockcult_user)
return cell.power_drain(clockcult_user, drain_weapons, recursive, drain_amount)
return 0 //Returns 0 instead of FALSE to symbolise it returning the power amount in other cases, not TRUE aka 1
/obj/item/melee/baton/power_drain(clockcult_user, drain_weapons = FALSE) //balance memes
/obj/item/melee/baton/power_drain(clockcult_user, drain_weapons = FALSE, recursive = FALSE, drain_amount = MIN_CLOCKCULT_POWER) //balance memes
if(!drain_weapons)
return 0
return ..()
var/obj/item/stock_parts/cell/cell = get_cell()
if(cell)
return cell.power_drain(clockcult_user, drain_weapons, recursive, drain_amount)
return 0 //No need to recurse further in batons
/obj/item/gun/power_drain(clockcult_user, drain_weapons = FALSE) //balance memes
/obj/item/gun/power_drain(clockcult_user, drain_weapons = FALSE, recursive = FALSE, drain_amount = MIN_CLOCKCULT_POWER) //balance memes
if(!drain_weapons)
return 0
var/obj/item/stock_parts/cell/cell = get_cell()
if(!cell)
return 0
if(cell.charge)
. = min(cell.charge, MIN_CLOCKCULT_POWER*4) //Done snowflakey because guns have far smaller cells than batons / other equipment
. = min(cell.charge, drain_amount*4) //Done snowflakey because guns have far smaller cells than batons / other equipment, also no need to recurse further in guns
cell.use(.)
update_icon()
/obj/machinery/power/apc/power_drain(clockcult_user, drain_weapons = FALSE)
/obj/machinery/power/apc/power_drain(clockcult_user, drain_weapons = FALSE, recursive = FALSE, drain_amount = MIN_CLOCKCULT_POWER)
if(cell && cell.charge)
playsound(src, "sparks", 50, 1)
flick("apc-spark", src)
. = min(cell.charge, MIN_CLOCKCULT_POWER*4)
cell.use(.) //Better than a power sink!
. = min(cell.charge, drain_amount*4)
cell.use(min(cell.charge, . * 4)) //Better than a power sink!
if(!cell.charge && !shorted)
shorted = 1
visible_message("<span class='warning'>The [name]'s screen blurs with static.</span>")
update()
update_icon()
/obj/machinery/power/smes/power_drain(clockcult_user, drain_weapons = FALSE)
/obj/machinery/power/smes/power_drain(clockcult_user, drain_weapons = FALSE, recursive = FALSE, drain_amount = MIN_CLOCKCULT_POWER)
if(charge)
. = min(charge, MIN_CLOCKCULT_POWER*4)
. = min(charge, drain_amount*4)
charge -= . * 50
if(!charge && !panel_open)
panel_open = TRUE
@@ -44,20 +60,26 @@
visible_message("<span class='warning'>[src]'s panel flies open with a flurry of sparks!</span>")
update_icon()
/obj/item/stock_parts/cell/power_drain(clockcult_user, drain_weapons = FALSE)
/obj/item/stock_parts/cell/power_drain(clockcult_user, drain_weapons = FALSE, recursive = FALSE, drain_amount = MIN_CLOCKCULT_POWER)
if(charge)
. = min(charge, MIN_CLOCKCULT_POWER * 4) //Done like this because normal cells are usually quite a bit bigger than the ones used in guns / APCs
. = min(charge, drain_amount * 4) //Done like this because normal cells are usually quite a bit bigger than the ones used in guns / APCs
use(min(charge, . * 10)) //Usually cell-powered equipment that is not a gun has at least ten times the capacity of a gun / 5 times the amount of an APC. This adjusts the drain to account for that.
update_icon()
/mob/living/silicon/robot/power_drain(clockcult_user, drain_weapons = FALSE)
/mob/living/silicon/robot/power_drain(clockcult_user, drain_weapons = FALSE, recursive = FALSE, drain_amount = MIN_CLOCKCULT_POWER)
if((!clockcult_user || !is_servant_of_ratvar(src)) && cell && cell.charge)
. = min(cell.charge, MIN_CLOCKCULT_POWER*4)
. = min(cell.charge, drain_amount*8) //Silicons are very susceptible to Ratvar's might
cell.use(.)
spark_system.start()
/obj/mecha/power_drain(clockcult_user, drain_weapons = FALSE)
if((!clockcult_user || (occupant && !is_servant_of_ratvar(occupant))) && cell && cell.charge)
. = min(cell.charge, MIN_CLOCKCULT_POWER*4)
cell.use(.)
spark_system.start()
/obj/mecha/power_drain(clockcult_user, drain_weapons = FALSE, recursive = FALSE, drain_amount = MIN_CLOCKCULT_POWER)
if(!clockcult_user || (occupant && !is_servant_of_ratvar(occupant)))
if(recursive)
var/succ = 0
for(var/atom/movable/target in contents) //Hiding in your mech won't save you.
succ += target.power_drain(clockcult_user, drain_weapons, recursive, drain_amount)
. = succ
else if(cell && cell.charge)
. = min(cell.charge, drain_amount*4)
cell.use(.)
spark_system.start()
@@ -6,12 +6,13 @@
//The base clockwork rite. This should never be visible
/datum/clockwork_rite
var/name = "Rite of THE frog" //The name of the rite
var/desc = "This rite is used to summon the legendary frog whose-name-shall-not-be-spoken, ender of many worlds." //What does this rite do? Shown to cultists if they choose 'Show Info' after selecting the rite.
var/desc = "This rite is used to summon the legendary frog whose-name-shall-not-be-spoken, ender of many worlds." //What does this rite do? Shown to servants if they choose 'Show Info' after selecting the rite.
var/list/required_ingredients = list(/obj/item/clockwork) //What does this rite require?
var/power_cost = 0 //How much power does this rite cost.. or does it even add power?
var/requires_human = FALSE //Does the rite require a ../carbon/human on the rune?
var/requires_human = FALSE //Does the rite require a ../carbon/human on the sigil?
var/must_be_servant = TRUE //If the above is true, does the human need to be a servant?
var/target_can_be_invoker = TRUE //Does this rite work if the invoker is also the target?
var/requires_full_power = FALSE //Does the invoker need to be an actual full-on servant, or is this available to neutered ones aswell?
var/cast_time = 0 //How long does the rite take to cast?
var/limit = INFINITE //How often can this rite be used per round? Set this to INFINITE for unlimited, 0 for disallowed, anything above 0 for a limit
var/times_used = 0 //How often has the rite already been used this shift?
@@ -164,14 +165,16 @@
/datum/clockwork_rite/treat_wounds/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
if(!target)
return FALSE
if(!target.all_wounds.len)
if(!target.all_wounds || !target.all_wounds.len)
to_chat(invoker, "<span class='inathneq_small'>This one does not require mending.</span>")
return FALSE
.= ..()
if(!.)
return FALSE
target.adjustToxLoss(10 * target.all_wounds.len)
QDEL_LIST(target.all_wounds)
for(var/i in target.all_wounds)
var/datum/wound/mended = i
mended.remove_wound()
to_chat(target, "<span class='warning'>You feel your wounds heal, but are overcome with deep nausea.</span>")
new /obj/effect/temp_visual/ratvar/sigil/vitality(T)
@@ -115,7 +115,7 @@
if(totaldamage)
L.heal_overall_damage(brutedamage, burndamage, only_organic = FALSE) //Maybe a machine god shouldn't murder augmented followers instead of healing them
L.adjustOxyLoss(-oxydamage)
L.adjustToxLoss(totaldamage * 0.5, TRUE, TRUE)
L.adjustToxLoss(totaldamage * 0.5, TRUE, TRUE, toxins_type = TOX_OMNI)
clockwork_say(ranged_ability_user, text2ratvar("[has_holy_water ? "Heal tainted" : "Mend wounded"] flesh!"))
log_combat(ranged_ability_user, L, "healed with Sentinel's Compromise")
L.visible_message("<span class='warning'>A blue light washes over [L], [has_holy_water ? "causing [L.p_them()] to briefly glow as it mends" : " mending"] [L.p_their()] bruises and burns!</span>", \
@@ -22,7 +22,7 @@
. = ..()
desc = initial(desc)
obj/item/shield/riot/ratvarian/proc/calc_bash_mult()
/obj/item/shield/riot/ratvarian/proc/calc_bash_mult()
var/bash_mult = 0
if(!dam_absorbed)
return 1
@@ -99,7 +99,7 @@
else
..()
/obj/item/clockwork/slab/cyborg/ratvar_act()
/obj/item/clockwork/slab/cyborg/ui_act()
..()
if(!GLOB.ratvar_awakens)
SStgui.close_uis(src)
@@ -203,10 +203,10 @@
to_chat(user, "<span class='warning'>You need to hold the slab in your active hand to recite scripture!</span>")
return FALSE
var/initial_tier = initial(scripture.tier)
if(initial_tier == SCRIPTURE_PERIPHERAL)
if(initial_tier == SCRIPTURE_PERIPHERAL && !issilicon(user)) //Silicons use peripheral scripture & cannot open the slab.
to_chat(user, "<span class='warning'>Nice try using href exploits</span>")
return
if(!GLOB.ratvar_awakens && !no_cost && !SSticker.scripture_states[initial_tier])
if(!GLOB.ratvar_awakens && !no_cost && !SSticker.scripture_states[initial_tier] &&!issilicon(user)) //silicons can't choose their spells, so lets allow them to always cast their assigned ones.
to_chat(user, "<span class='warning'>That scripture is not unlocked, and cannot be recited!</span>")
return FALSE
var/datum/clockwork_scripture/scripture_to_recite = new scripture
@@ -167,7 +167,7 @@ Judgement 5 converts
set waitfor = FALSE
chanting = TRUE
for(var/invocation in invocations)
sleep(channel_time / invocations.len)
sleep(channel_time / (invocations.len + 1)) //So it always finishes the invocation
if(QDELETED(src) || QDELETED(slab) || !chanting)
return
if(multiple_invokers_used)
@@ -8,7 +8,7 @@
descname = "Powers Nearby Structures"
name = "Sigil of Transmission"
desc = "Places a sigil that can drain and will store energy to power clockwork structures."
invocations = list("Divinity...", "...power our creations!")
invocations = list("Divinity...", "...power our creations.")
channel_time = 70
power_cost = 200
whispered = TRUE
@@ -28,7 +28,7 @@
descname = "Powered Structure, Delay Emergency Shuttles"
name = "Prolonging Prism"
desc = "Creates a mechanized prism which will delay the arrival of an emergency shuttle by 2 minutes at a massive power cost."
invocations = list("May this prism...", "...grant us time to enact his will!")
invocations = list("May this prism...", "...grant us time to enact his will.")
channel_time = 80
power_cost = 300
object_path = /obj/structure/destructible/clockwork/powered/prolonging_prism
@@ -60,7 +60,7 @@
descname = "Powered Structure, Area Denial"
name = "Mania Motor"
desc = "Creates a mania motor which causes minor damage and a variety of negative mental effects in nearby non-Servant humans, potentially up to and including conversion."
invocations = list("May this transmitter...", "...break the will of all who oppose us!")
invocations = list("May this transmitter...", "...break the will of all who oppose us.")
channel_time = 80
power_cost = 750
object_path = /obj/structure/destructible/clockwork/powered/mania_motor
@@ -83,7 +83,7 @@
descname = "Powered Structure, Teleportation Hub"
name = "Clockwork Obelisk"
desc = "Creates a clockwork obelisk that can broadcast messages over the Hierophant Network or open a Spatial Gateway to any living Servant or clockwork obelisk."
invocations = list("May this obelisk...", "...take us to all places!")
invocations = list("May this obelisk...", "...take us to all places.")
channel_time = 80
power_cost = 300
object_path = /obj/structure/destructible/clockwork/powered/clockwork_obelisk
@@ -163,7 +163,7 @@
descname = "Well-Rounded Combat Construct"
name = "Clockwork Marauder"
desc = "Creates a shell for a clockwork marauder, a balanced frontline construct that can deflect projectiles with its shield."
invocations = list("Arise, avatar of Arbiter!", "Defend the Ark with vengeful zeal.")
invocations = list("Arise, avatar of Arbiter!", "Defend the Ark with vengeful zeal!")
channel_time = 80
power_cost = 8000
creator_message = "<span class='brass'>Your slab disgorges several chunks of replicant alloy that form into a suit of thrumming armor.</span>"
@@ -7,7 +7,7 @@
descname = "Generates Power From Starlight"
name = "Stargazer"
desc = "Forms a weak structure that generates power every second while within three tiles of starlight."
invocations = list("Capture their inferior light for us!")
invocations = list("Capture their inferior light for us.")
channel_time = 50
power_cost = 200
object_path = /obj/structure/destructible/clockwork/stargazer
@@ -16,6 +16,7 @@
usage_tip = "For obvious reasons, make sure to place this near a window or somewhere else that can see space!"
tier = SCRIPTURE_DRIVER
one_per_tile = TRUE
whispered = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 1
quickbind = TRUE
@@ -34,7 +35,7 @@
descname = "Power Generation"
name = "Integration Cog"
desc = "Fabricates an integration cog, which can be used on an open APC to replace its innards and passively siphon its power."
invocations = list("Take that which sustains them!")
invocations = list("Take that which sustains them.")
channel_time = 10
power_cost = 10
whispered = TRUE
@@ -55,7 +56,7 @@
descname = "Trap, Stunning"
name = "Sigil of Transgression"
desc = "Wards a tile with a sigil, which will briefly stun the next non-Servant to cross it and apply Belligerent to them."
invocations = list("Divinity, smite...", "...those who trespass here!")
invocations = list("Divinity, smite...", "...those who trespass here.")
channel_time = 50
power_cost = 50
whispered = TRUE
@@ -75,7 +76,7 @@
descname = "Trap, Conversion"
name = "Sigil of Submission"
desc = "Places a luminous sigil that will convert any non-Servants that remain on it for 8 seconds."
invocations = list("Divinity, enlighten...", "...those who trespass here!")
invocations = list("Divinity, enlighten...", "...those who trespass here.")
channel_time = 60
power_cost = 125
whispered = TRUE
@@ -95,7 +96,7 @@
descname = "Short-Range Single-Target Stun"
name = "Kindle"
desc = "Charges your slab with divine energy, allowing you to overwhelm a target with Ratvar's light."
invocations = list("Divinity, show them your light!")
invocations = list("Divinity, show them your light.")
whispered = TRUE
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
@@ -118,7 +119,7 @@
descname = "Handcuffs"
name = "Hateful Manacles"
desc = "Forms replicant manacles around a target's wrists that function like handcuffs."
invocations = list("Shackle the heretic!", "Break them in body and spirit!")
invocations = list("Shackle the heretic!", "Break them in body and spirit.")
channel_time = 15
power_cost = 25
whispered = TRUE
@@ -269,7 +270,7 @@
descname = "New Clockwork Slab"
name = "Replicant"
desc = "Creates a new clockwork slab."
invocations = list("Metal, become greater!")
invocations = list("Metal, become greater.")
channel_time = 10
power_cost = 25
whispered = TRUE
@@ -290,7 +291,7 @@
descname = "Limited Xray Vision Glasses"
name = "Wraith Spectacles"
desc = "Fabricates a pair of glasses which grant true sight but cause gradual vision loss."
invocations = list("Show the truth of this world to me!")
invocations = list("Show the truth of this world to me.")
channel_time = 10
power_cost = 50
whispered = TRUE
@@ -310,7 +311,7 @@
name = "Spatial Gateway"
desc = "Tears open a miniaturized gateway in spacetime to any conscious servant that can transport objects or creatures to its destination. \
Each servant assisting in the invocation adds one additional use and four additional seconds to the gateway's uses and duration."
invocations = list("Spatial Gateway...", "...activate!")
invocations = list("Spatial Gateway...", "...activate.")
channel_time = 30
power_cost = 400
whispered = TRUE
@@ -349,4 +350,4 @@
if(GLOB.ratvar_awakens)
portal_uses = max(portal_uses, 100) //Very powerful if Ratvar has been summoned
duration = max(duration, 100)
return slab.procure_gateway(invoker, duration, portal_uses)
return slab.procure_gateway(invoker, duration, portal_uses)
@@ -29,7 +29,7 @@
descname = "Structure, Turret"
name = "Ocular Warden"
desc = "Forms an automatic short-range turret which will automatically attack nearby unrestrained non-Servants that can see it."
invocations = list("Guardians of Engine...", "...judge those who would harm us!")
invocations = list("Guardians of Engine...", "...judge those who would harm us.")
channel_time = 100
power_cost = 250
object_path = /obj/structure/destructible/clockwork/ocular_warden
@@ -105,7 +105,7 @@
descname = "Delayed Area Knockdown Glasses"
name = "Judicial Visor"
desc = "Creates a visor that can smite an area, applying Belligerent and briefly stunning. The smote area will explode after 3 seconds."
invocations = list("Grant me the flames of Engine!")
invocations = list("Grant me the flames of Engine.")
channel_time = 10
power_cost = 400
whispered = TRUE
@@ -124,7 +124,7 @@
descname = "Shield with empowerable bashes"
name = "Nezbere's shield"
desc = "Creates a shield which generates charge from blocking damage, using it to empower its bashes tremendously. It is repaired with brass, and while very durable, extremely weak to lasers and, even more so, to energy weaponry."
invocations = list("Shield me...", "... from the coming dark!")
invocations = list("Shield me...", "... from the coming dark.")
channel_time = 20
power_cost = 600 //Shouldn't be too spammable but not too hard to get either
whispered = TRUE
@@ -143,7 +143,7 @@
descname = "Summonable Armor and Weapons"
name = "Clockwork Armaments"
desc = "Allows the invoker to summon clockwork armor and a Ratvarian spear at will. The spear's attacks will generate Vitality, used for healing."
invocations = list("Grant me armaments...", "...from the forge of Armorer!")
invocations = list("Grant me armaments...", "...from the forge of Armorer.")
channel_time = 20
power_cost = 250
whispered = TRUE
@@ -393,45 +393,49 @@
<b>Left-click a target to fire, quickly!</b></span>"
timeout_time = 20
/datum/clockwork_scripture/channeled/void_volt
descname = "Channeled, Power Drain"
/datum/clockwork_scripture/void_volt
descname = "Pulse, Power Drain"
name = "Void Volt"
desc = "A channeled spell that quickly drains any powercells in a radius of eight tiles, but burns the invoker. \
Can be channeled with more cultists to increase range and split the caused damage evenly over all invokers. \
desc = "A spell that releases a pulse which drains the power of anything in a radius of eight tiles, but burns the invoker. \
Can be used with more servants to increase range and split the caused damage evenly among all invokers. \
Also charges clockwork power by a small percentage of the drained power amount, which can help offset this scriptures powercost."
invocations = list("Channel their energy through my body... ", "... so it may fuel Engine!")
chant_invocations = list("Make their lights fall dark!", "They shall be powerless!", "Rob them of their power!")
chant_amount = 20
chant_interval = 10 //100KW drain per pulse for guns / APCs / 1MW for other cells = 10 chants / 100ds / 10s to drain a charged weapon or a baton with a nonupgraded cell
channel_time = 50
power_cost = 300
invocations = list("Take the energy...", "...of their inventions...", "...and grant it to Engine...", "...for they already live in utter darkness!")
channel_time = 130 //You need alot of time, but it pays off. - ten times as powerful as a regular drain (done by transmission sigils) and recurses + affects weapons - incredibly useful if you can pull this off before a big fight.
power_cost = 500 //Relatively medium powercost, but can be offset due to it adding a part of drained power to the power pool.
multiple_invokers_used = TRUE
multiple_invokers_optional = TRUE
usage_tip = "It may be useful to end channelling early if the burning becomes too much to handle.."
usage_tip = "Be sure to not be injured when using this, or the power channeled through you may overwhelm your body."
tier = SCRIPTURE_SCRIPT
primary_component = GEIS_CAPACITOR
sort_priority = 11
quickbind = TRUE
quickbind_desc = "Quickly drains power in an area around the invoker, causing burns proportional to the amount of energy drained.<br><b>Maximum of 20 chants.</b>"
quickbind_desc = "Quickly drains power in an area around the invoker, causing burns proportional to the amount of energy drained."
/datum/clockwork_scripture/channeled/void_volt/scripture_effects()
/datum/clockwork_scripture/void_volt/chant()
invoker.visible_message("<span class='warning'>[invoker] glows in a brilliant golden light!</span>")
invoker.add_atom_colour("#FFD700", ADMIN_COLOUR_PRIORITY)
invoker.light_power = 2
invoker.light_range = 4
invoker.light_color = LIGHT_COLOR_FIRE
invoker.update_light()
return ..()
addtimer(CALLBACK(invoker, /mob.proc/stop_void_volt_glow), channel_time)
..()//Do the timer & Chant
/mob/proc/stop_void_volt_glow() //Needed so the scripture being qdel()d doesn't prevent it.
visible_message("<span class='warning'>[src] stops glowing...</span>")
remove_atom_colour(ADMIN_COLOUR_PRIORITY)
light_power = 0
light_range = 0
update_light()
/datum/clockwork_scripture/channeled/void_volt/chant_effects(chant_number)
/datum/clockwork_scripture/void_volt/scripture_effects()
var/power_drained = 0
var/power_mod = 0.005 //Amount of power drained (generally) is multiplied with this, and subsequently dealt in damage to the invoker, then 15 times that is added to the clockwork cult's power reserves.
var/drain_range = 8
var/drain_range = 12
var/additional_chanters = 0
var/list/chanters = list()
chanters += invoker
for(var/mob/living/L in range(1, invoker))
for(var/mob/living/L in orange(1, invoker))
if(!L.stat && is_servant_of_ratvar(L))
additional_chanters++
chanters += L
@@ -440,22 +444,14 @@
var/turf/T = t
for(var/M in T)
var/atom/movable/A = M
power_drained += A.power_drain(TRUE, TRUE) //Yes, this absolutely does drain weaponry. 10 pulses to drain guns / batons, though of course they can just be recharged.
power_drained += A.power_drain(TRUE, TRUE, TRUE, MIN_CLOCKCULT_POWER * 10) //Yes, this absolutely does drain weaponry, aswell as recurse through objects. No more hiding in lockers / mechs to avoid it.
new /obj/effect/temp_visual/ratvar/sigil/transgression(invoker.loc, 1 + (power_drained * power_mod))
var/datum/effect_system/spark_spread/S = new
S.set_up(round(1 + (power_drained * power_mod), 1), 0, get_turf(invoker))
S.start()
adjust_clockwork_power(power_drained * power_mod * 15)
for(var/mob/living/L in chanters)
L.adjustFireLoss(round(clamp(power_drained * power_mod / (1 + additional_chanters), 0, 20), 0.1)) //No you won't just immediately melt if you do this in a very power-rich area
L.adjustFireLoss(round(clamp(power_drained * power_mod / (1 + additional_chanters), 0, 70), 0.1)) //No you won't just immediately melt if you do this in a very power-rich area, but it'll be close.
return TRUE
/datum/clockwork_scripture/channeled/void_volt/chant_end_effects()
invoker.visible_message("<span class='warning'>[invoker] stops glowing...</span>")
invoker.remove_atom_colour(ADMIN_COLOUR_PRIORITY)
invoker.light_power = 0
invoker.light_range = 0
invoker.update_light()
return ..()
@@ -95,7 +95,7 @@
return ..()
/obj/structure/destructible/clockwork/attackby(obj/item/I, mob/user, params)
if(is_servant_of_ratvar(user) && istype(I, /obj/item/wrench) && unanchored_icon)
if(is_servant_of_ratvar(user) && I.tool_behaviour == TOOL_WRENCH && unanchored_icon)
if(default_unfasten_wrench(user, I, 50) == SUCCESSFUL_UNFASTEN)
update_anchored(user)
return 1

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