Merge remote-tracking branch 'origin/master' into semi-sync

This commit is contained in:
Letter N
2020-12-23 12:25:24 +08:00
84 changed files with 679 additions and 245 deletions
+2 -1
View File
@@ -35,8 +35,9 @@
#define R_SPAWN (1<<12)
#define R_AUTOLOGIN (1<<13)
#define R_DBRANKS (1<<14)
#define R_SENSITIVE (1<<15)
#define R_DEFAULT R_AUTOLOGIN
#define R_DEFAULT R_AUTOLOGIN | R_SENSITIVE
#define R_EVERYTHING ALL //the sum of all other rank permissions, used for +EVERYTHING
+1
View File
@@ -40,6 +40,7 @@
//#define ROLE_MONSTERHUNTER "monster hunter" Disabled for now
#define ROLE_GHOSTCAFE "ghostcafe"
#define ROLE_MINOR_ANTAG "minorantag"
#define ROLE_RESPAWN "respawnsystem"
//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR.
//The gamemode specific ones are just so the gamemodes can query whether a player is old enough
//(in game days played) to play that role
+2
View File
@@ -225,6 +225,8 @@
. += "[seperator]AUTOLOGIN"
if(rights & R_DBRANKS)
. += "[seperator]DBRANKS"
if(rights & R_SENSITIVE)
. += "[seperator]SENSITIVE"
if(!.)
. = "NONE"
return .
+2 -1
View File
@@ -67,7 +67,8 @@ GLOBAL_LIST_INIT(bitfields, list(
"SOUNDS" = R_SOUNDS,
"SPAWN" = R_SPAWN,
"AUTOLOGIN" = R_AUTOLOGIN,
"DBRANKS" = R_DBRANKS
"DBRANKS" = R_DBRANKS,
"SENSITIVE" = R_SENSITIVE
),
"interaction_flags_atom" = list(
"INTERACT_ATOM_REQUIRES_ANCHORED" = INTERACT_ATOM_REQUIRES_ANCHORED,
@@ -172,8 +172,6 @@
/datum/config_entry/string/hostedby
/datum/config_entry/flag/norespawn
/datum/config_entry/flag/guest_jobban
/datum/config_entry/flag/usewhitelist
@@ -0,0 +1,47 @@
/// Allows usage of respawn system
/datum/config_entry/flag/respawns_enabled
config_entry_value = FALSE
/// Minutes before allowing respawns.
/datum/config_entry/number/respawn_delay
config_entry_value = 15.0
integer = FALSE
/// Minutes before allowing respawn, if user cryo'd.
/datum/config_entry/number/respawn_delay_cryo
config_entry_value = 5.0
integer = FALSE
/// Allows respawning as non-assistant. Overrides all others of this type.
/datum/config_entry/flag/allow_non_assistant_respawn
config_entry_value = FALSE
/// Allows respawning as a combat role, defined as security/head.
/datum/config_entry/flag/allow_combat_role_respawn
config_entry_value = FALSE
/// Allows respawning as the same character as a previous life
/datum/config_entry/flag/allow_same_character_respawn
config_entry_value = FALSE
/// Observing penalizes for respawns, not just joining.
/datum/config_entry/flag/respawn_penalty_includes_observe
config_entry_value = FALSE
/// Minutes from roundstart before someone can respawn
/datum/config_entry/number/respawn_minimum_delay_roundstart
config_entry_value = 30.0
integer = FALSE
/// Gamemode config tags that are banned from respawning
/datum/config_entry/keyed_list/respawn_chaos_gamemodes
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_FLAG
/datum/config_entry/keyed_list/respawn_chaos_gamemodes/ValidateListEntry(key_name, key_value)
. = ..()
return . && (key_name in config.modes)
/datum/config_entry/keyed_list/respawn_chaos_gamemodes/preprocess_key(key)
. = ..()
return lowertext(key)
+1
View File
@@ -93,6 +93,7 @@ SUBSYSTEM_DEF(input)
user.full_macro_assert()
/datum/controller/subsystem/input/fire()
set waitfor = FALSE
var/list/clients = GLOB.clients // Let's sing the list cache song
for(var/i in 1 to clients.len)
var/client/C = clients[i]
+5
View File
@@ -371,6 +371,11 @@ SUBSYSTEM_DEF(ticker)
if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
GLOB.joined_player_list += player.ckey
player.create_character(FALSE)
if(player.new_character && player.client && player.client.prefs) // we cannot afford a runtime, ever
LAZYOR(player.client.prefs.slots_joined_as, player.client.prefs.default_slot)
LAZYOR(player.client.prefs.characters_joined_as, player.new_character.real_name)
else
stack_trace("WARNING: Either a player did not have a new_character, did not have a client, or did not have preferences. This is VERY bad.")
else
player.new_player_panel()
CHECK_TICK
+1 -1
View File
@@ -164,7 +164,7 @@
. = list()
.["version"] = GLOB.game_version
.["mode"] = "hidden" //CIT CHANGE - hides the gamemode in topic() calls to prevent meta'ing the gamemode
.["respawn"] = config ? !CONFIG_GET(flag/norespawn) : FALSE
.["respawn"] = config ? CONFIG_GET(flag/respawns_enabled) : FALSE
.["enter"] = GLOB.enter_allowed
.["vote"] = CONFIG_GET(flag/allow_vote_mode)
.["ai"] = CONFIG_GET(flag/allow_ai)
+3 -3
View File
@@ -150,14 +150,14 @@ Credit where due:
var/datum/team/clockcult/main_clockcult
/datum/game_mode/clockwork_cult/pre_setup() //Gamemode and job code is pain. Have fun codediving all of that stuff, whoever works on this next - Delta
var/list/errorList = list()
/*var/list/errorList = list()
var/list/reebes = SSmapping.LoadGroup(errorList, "Reebe", "map_files/generic", "City_of_Cogs.dmm", default_traits = ZTRAITS_REEBE, silent = TRUE)
if(errorList.len) // reebe failed to load
message_admins("Reebe failed to load!")
log_game("Reebe failed to load!")
return FALSE
for(var/datum/parsed_map/PM in reebes)
PM.initTemplateBounds()
for(var/datum/parsed_map/PM in reebes) //Temporarily commented because of z-level loading reliably segfaulting the server.
PM.initTemplateBounds()*/
if(CONFIG_GET(flag/protect_roles_from_antagonist))
restricted_jobs += protected_jobs
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
+104 -123
View File
@@ -22,17 +22,16 @@
//Used for logging people entering cryosleep and important items they are carrying.
var/list/frozen_crew = list()
var/list/frozen_items = list()
// Used for containing rare items traitors need to steal, so it's not
// game-over if they get iced
var/list/objective_items = list()
// A cache of theft datums so you don't have to re-create them for
// each item check
var/list/theft_cache = list()
var/list/obj/stored_packages = list()
var/allow_items = TRUE
/obj/machinery/computer/cryopod/deconstruct()
. = ..()
for(var/i in stored_packages)
var/obj/O = i
O.forceMove(drop_location())
/obj/machinery/computer/cryopod/attack_ai()
attack_hand()
@@ -67,11 +66,11 @@
if(3)
dat += "<a href='byond://?src=[REF(src)];menu=1'><< Back</a><br><br>"
dat += "<h3>Recently stored objects</h3><br/><hr/><br/>"
if(!frozen_items.len)
if(!stored_packages.len)
dat += "There has been no storage usage at this terminal.<br/>"
else
for(var/obj/item/I in frozen_items)
dat += "[I.name]<br/>"
for(var/obj/O in stored_packages)
dat += "[O.name]<br/>"
dat += "<hr/>"
var/datum/browser/popup = new(user, "cryopod_console", "Cryogenic System Control")
@@ -87,25 +86,27 @@
add_fingerprint(user)
if(href_list["item"])
if(!allowed(user))
if(!allowed(user) && !(obj_flags & EMAGGED))
to_chat(user, "<span class='warning'>Access Denied.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
updateUsrDialog()
return
if(!allow_items) return
if(frozen_items.len == 0)
if(!allow_items)
return
if(stored_packages.len == 0)
to_chat(user, "<span class='notice'>There is nothing to recover from storage.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
updateUsrDialog()
return
var/obj/item/I = input(user, "Please choose which object to retrieve.","Object recovery",null) as null|anything in frozen_items
var/obj/I = input(user, "Please choose which object to retrieve.","Object recovery",null) as null|anything in stored_packages
playsound(src, "terminal_type", 25, 0)
if(!I)
return
if(!(I in frozen_items))
if(!(I in stored_packages))
to_chat(user, "<span class='notice'>\The [I] is no longer in storage.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
updateUsrDialog()
@@ -117,20 +118,21 @@
I.forceMove(drop_location())
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(I)
frozen_items -= I
stored_packages -= I
updateUsrDialog()
else if(href_list["allitems"])
playsound(src, "terminal_type", 25, 0)
if(!allowed(user))
if(!allowed(user) && !(obj_flags & EMAGGED))
to_chat(user, "<span class='warning'>Access Denied.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
updateUsrDialog()
return
if(!allow_items)
return
if(frozen_items.len == 0)
if(stored_packages.len == 0)
to_chat(user, "<span class='notice'>There is nothing to recover from storage.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
@@ -138,10 +140,10 @@
visible_message("<span class='notice'>The console beeps happily as it disgorges the desired objects.</span>")
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
for(var/obj/item/I in frozen_items)
I.forceMove(drop_location())
frozen_items -= I
updateUsrDialog()
for(var/obj/O in stored_packages)
O.forceMove(get_turf(src))
stored_packages.Cut()
updateUsrDialog()
else if (href_list["menu"])
src.menu = text2num(href_list["menu"])
@@ -159,6 +161,13 @@
/obj/machinery/computer/cryopod/contents_explosion()
return
/obj/machinery/computer/cryopod/contents_explosion()
return //don't blow everyone's shit up.
/// The box
/obj/item/storage/box/blue/cryostorage_items
w_class = WEIGHT_CLASS_HUGE
//Cryopods themselves.
/obj/machinery/cryopod
name = "cryogenic freezer"
@@ -177,44 +186,9 @@
var/despawn_world_time = null // Used to keep track of the safe period.
var/obj/machinery/computer/cryopod/control_computer
var/item_storage_type = /obj/item/storage/box/blue/cryostorage_items //with how storage components work this can be anything the player can open or anything with a storage component.
var/last_no_computer_message = 0
// These items are preserved when the process() despawn proc occurs.
var/static/list/preserve_items = typecacheof(list(
/obj/item/hand_tele,
/obj/item/card/id/captains_spare,
/obj/item/aicard,
/obj/item/mmi,
/obj/item/paicard,
/obj/item/gun,
/obj/item/pinpointer,
/obj/item/clothing/shoes/magboots,
/obj/item/areaeditor/blueprints,
/obj/item/clothing/head/helmet/space,
/obj/item/clothing/suit/space,
/obj/item/clothing/suit/armor,
/obj/item/defibrillator/compact,
/obj/item/reagent_containers/hypospray/CMO,
/obj/item/clothing/accessory/medal/gold/captain,
/obj/item/clothing/gloves/krav_maga,
/obj/item/nullrod,
/obj/item/tank/jetpack,
/obj/item/documents,
/obj/item/nuke_core_container
))
// These items will NOT be preserved
var/static/list/do_not_preserve_items = typecacheof(list(
/obj/item/mmi/posibrain,
/obj/item/gun/energy/laser/mounted,
/obj/item/gun/energy/e_gun/advtaser/mounted,
/obj/item/gun/ballistic/revolver/grenadelauncher/cyborg,
/obj/item/gun/energy/disabler/cyborg,
/obj/item/gun/energy/e_gun/advtaser/cyborg,
/obj/item/gun/energy/printer,
/obj/item/gun/energy/kinetic_accelerator/cyborg,
/obj/item/gun/energy/laser/cyborg
))
/obj/machinery/cryopod/Initialize(mapload)
. = ..()
update_icon()
@@ -286,73 +260,89 @@
despawn_occupant()
#define CRYO_DESTROY 0
#define CRYO_PRESERVE 1
#define CRYO_OBJECTIVE 2
#define CRYO_IGNORE 3
#define CRYO_DESTROY_LATER 4
/obj/machinery/cryopod/proc/should_preserve_item(obj/item/I)
for(var/datum/objective_item/steal/T in control_computer.theft_cache)
if(istype(I, T.targetitem) && T.check_special_completion(I))
return CRYO_OBJECTIVE
if(preserve_items[I] && !do_not_preserve_items[I])
return CRYO_PRESERVE
return CRYO_DESTROY
// This function can not be undone; do not call this unless you are sure
/obj/machinery/cryopod/proc/despawn_occupant()
if(!control_computer)
find_control_computer()
var/mob/living/mob_occupant = occupant
var/list/obj/item/cryo_items = list()
var/list/obj/item/storing = list()
var/list/obj/item/destroying = list()
var/list/obj/item/destroy_later = list()
investigate_log("Despawning [key_name(mob_occupant)].", INVESTIGATE_CRYOGENICS)
//Handle Borg stuff first
var/atom/target_store = (control_computer?.allow_items && control_computer) || src //the double control computer check makes it return the control computer.
var/drop_to_ground = !istype(target_store, /obj/machinery/computer/cryopod)
var/mind_identity = mob_occupant.mind?.name
var/occupant_identity = mob_occupant.real_name
if(iscyborg(mob_occupant))
var/mob/living/silicon/robot/R = mob_occupant
if(R.mmi?.brain)
cryo_items[R.mmi] = CRYO_DESTROY_LATER
cryo_items[R.mmi.brain] = CRYO_DESTROY_LATER
for(var/obj/item/I in R.module) // the tools the borg has; metal, glass, guns etc
for(var/obj/item/O in I) // the things inside the tools, if anything; mainly for janiborg trash bags
cryo_items[O] = should_preserve_item(O)
O.forceMove(src)
R.module.remove_module(I, TRUE) //delete the module itself so it doesn't transfer over.
//Drop all items into the pod.
for(var/obj/item/I in mob_occupant)
if(cryo_items[I] == CRYO_IGNORE || cryo_items[I] ==CRYO_DESTROY_LATER)
continue
cryo_items[I] = should_preserve_item(I)
mob_occupant.transferItemToLoc(I, src, TRUE)
if(I.contents.len) //Make sure we catch anything not handled by qdel() on the items.
if(cryo_items[I] != CRYO_DESTROY) // Don't remove the contents of things that need preservation
destroy_later += R.mmi
destroy_later += R.mmi.brain
for(var/i in R.module)
if(!isitem(i))
destroying += i
continue
for(var/obj/item/O in I.contents)
cryo_items[O] = should_preserve_item(O)
O.forceMove(src)
for(var/A in cryo_items)
var/obj/item/I = A
if(QDELETED(I)) //edge cases and DROPDEL.
continue
var/preserve = cryo_items[I]
if(preserve == CRYO_DESTROY_LATER)
continue
if(preserve != CRYO_IGNORE)
if(preserve == CRYO_DESTROY)
qdel(I)
else if(control_computer?.allow_items)
control_computer.frozen_items += I
if(preserve == CRYO_OBJECTIVE)
control_computer.objective_items += I
I.moveToNullspace()
var/obj/item/I = i
// let's be honest we only care about the trash bag don't beat around the bush
if(SEND_SIGNAL(I, COMSIG_CONTAINS_STORAGE))
storing += I.contents
for(var/atom/movable/AM in I.contents)
AM.forceMove(src)
R.module.remove_module(I, TRUE)
else
var/list/gear = list()
if(iscarbon(mob_occupant)) // sorry simp-le-mobs deserve no mercy
var/mob/living/carbon/C = mob_occupant
gear = C.get_all_gear()
for(var/i in gear)
var/obj/item/I = i
I.forceMove(src)
if(!istype(I))
destroying += I
continue
if(I.item_flags & (DROPDEL | ABSTRACT))
destroying += I
continue
if(HAS_TRAIT(I, TRAIT_NODROP))
destroying += I
continue
// WEE WOO SNOWFLAKE TIME
if(istype(I, /obj/item/pda))
var/obj/item/pda/P = I
if((P.owner == mind_identity) || (P.owner == occupant_identity))
destroying += P
else
storing += P
else if(istype(I, /obj/item/card/id))
var/obj/item/card/id/idcard = I
if((idcard.registered_name == mind_identity) || (idcard.registered_name == occupant_identity))
destroying += idcard
else
storing += idcard
else
I.forceMove(loc)
cryo_items -= I
storing += I
// get rid of mobs
for(var/mob/living/L in mob_occupant.GetAllContents() - mob_occupant)
L.forceMove(drop_location())
if(storing.len)
var/obj/O = new item_storage_type
O.name = "cryogenic retrieval package: [mob_occupant.real_name]"
for(var/i in storing)
var/obj/item/I = i
I.forceMove(O)
O.forceMove(drop_to_ground? target_store.drop_location() : target_store)
if((target_store == control_computer) && !drop_to_ground)
control_computer.stored_packages += O
QDEL_LIST(destroying)
//Update any existing objectives involving this mob.
for(var/i in GLOB.objectives)
@@ -414,22 +404,13 @@
// Ghost and delete the mob.
if(!mob_occupant.get_ghost(1))
mob_occupant.ghostize(FALSE, penalize = TRUE, voluntary = TRUE)
mob_occupant.ghostize(FALSE, penalize = TRUE, voluntary = TRUE, cryo = TRUE)
QDEL_NULL(occupant)
for(var/I in cryo_items) //only "CRYO_DESTROY_LATER" atoms are left)
var/atom/A = I
if(!QDELETED(A))
qdel(A)
QDEL_LIST(destroy_later)
open_machine()
name = initial(name)
#undef CRYO_DESTROY
#undef CRYO_PRESERVE
#undef CRYO_OBJECTIVE
#undef CRYO_IGNORE
#undef CRYO_DESTROY_LATER
/obj/machinery/cryopod/MouseDrop_T(mob/living/target, mob/user)
if(!istype(target) || user.incapacitated() || !target.Adjacent(user) || !Adjacent(user) || !ismob(target) || (!ishuman(user) && !iscyborg(user)) || !istype(user.loc, /turf) || target.buckled)
return
+2 -2
View File
@@ -67,9 +67,9 @@
/obj/machinery/door/window/proc/open_and_close()
open()
if(src.check_access(null))
sleep(50)
sleep(60)
else //secure doors close faster
sleep(20)
sleep(40)
close()
/obj/machinery/door/window/Bumped(atom/movable/AM)
+6 -1
View File
@@ -175,7 +175,7 @@
/obj/item/kitchen/knife/combat/survival/knuckledagger/Initialize()
. = ..()
AddComponent(/datum/component/butchering, 50, 120, 5) // it's good for butchering stuff
AddComponent(/datum/component/butchering, 30, 130, 20) // it's good for butchering stuff
/obj/item/kitchen/knife/combat/survival/knuckledagger/ui_action_click(mob/user, actiontype)
light_on = !light_on
@@ -189,6 +189,11 @@
else
set_light(0)
/obj/item/kitchen/knife/combat/survival/knuckledagger/update_overlays()
. = ..()
if(light_on)
. += "[icon_state]_lit"
/obj/item/kitchen/knife/combat/bone
name = "bone dagger"
item_state = "bone_dagger"
+1 -1
View File
@@ -137,7 +137,7 @@
AM.set_anchored(TRUE)
flick("laserbox_burn", AM)
trigger()
QDEL_IN(AM, 15)
QDEL_IN(src, 15)
// snowflake code until undertile elements
/obj/item/pressure_plate/hologrid/hide()
+2 -2
View File
@@ -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
+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
+4 -1
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))
+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
/*
+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
+11 -2
View File
@@ -936,6 +936,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 +1805,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))
-7
View File
@@ -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
+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
@@ -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
@@ -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!
@@ -689,8 +689,8 @@
wound_enhancement = 6
var/fast_enhancement = 6
var/fast_wound_enhancement = 6
var/slow_enhancement = 20
var/slow_wound_enhancement = 20
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
+2 -2
View File
@@ -445,7 +445,7 @@
if(L.move_resist < MOVE_FORCE_STRONG)
var/atom/throw_target = get_edge_target_turf(L, user.dir)
L.throw_at(throw_target, 7, 1, user)
else if(!iscultist(L))
else if(!is_servant_of_ratvar(L))
L.DefaultCombatKnockdown(160)
L.adjustStaminaLoss(140) //Ensures hard stamcrit
L.flash_act(1,1)
@@ -465,7 +465,7 @@
C.drowsyness = max(10, C.drowsyness)
C.confused += clamp(20 - C.confused, 0, 10)
L.adjustBruteLoss(15)
to_chat(user, "<span class='cultitalic'>In an brilliant flash of red, [L] [iscultist(L) ? "writhes in pain" : "falls to the ground!"]</span>")
to_chat(user, "<span class='cultitalic'>In an brilliant flash of red, [L] [is_servant_of_ratvar(L) ? "writhes in pain!" : "falls to the ground!"]</span>")
uses--
..()
+1
View File
@@ -74,6 +74,7 @@
bluespace = TRUE
explosionSize = list(0,0,1,2)
landingDelay = 25 //Longer than others
reverseOptionList = list("Mobs"=TRUE,"Objects"=FALSE,"Anchored"=FALSE,"Underfloor"=FALSE,"Wallmounted"=FALSE,"Floors"=FALSE,"Walls"=FALSE)
/obj/structure/closet/supplypod/centcompod
style = STYLE_CENTCOM
+14
View File
@@ -921,8 +921,22 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (NAMEOF(src, view))
view_size.setDefault(var_value)
return TRUE
if(NAMEOF(src, computer_id))
return FALSE
if(NAMEOF(src, address))
return FALSE
. = ..()
/client/vv_get_var(var_name)
. = ..()
switch(var_name)
if(NAMEOF(src, computer_id))
if(!check_rights(R_SENSITIVE, FALSE))
return "SENSITIVE"
if(NAMEOF(src, address))
if(!check_rights(R_SENSITIVE, FALSE))
return "SENSITIVE"
/client/proc/rescale_view(change, min, max)
var/viewscale = getviewsize(view)
var/x = viewscale[1]
+20 -2
View File
@@ -12,11 +12,29 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used
var/max_save_slots = 24
//non-preference stuff
var/muted = 0
// Intra-round persistence begin
/// Flags for admin mutes
var/muted = NONE
/// Last IP the person was seen on
var/last_ip
/// Last CID the person was seen on
var/last_id
/// Do we log their clicks to disk?
var/log_clicks = FALSE
/// Characters they have joined the round under - Lazylist of names
var/list/characters_joined_as
/// Slots they have joined the round under - Lazylist of numbers
var/list/slots_joined_as
/// Are we currently subject to respawn restrictions? Usually set by us using the "respawn" verb, but can be lifted by admins.
var/respawn_restrictions_active = FALSE
/// time of death we consider for respawns
var/respawn_time_of_death = -INFINITY
/// did they DNR? used to prevent respawns.
var/dnr_triggered = FALSE
/// did they cryo on their last ghost?
var/respawn_did_cryo = FALSE
// Intra-round persistence end
var/icon/custom_holoform_icon
var/list/cached_holoform_icons
+1 -1
View File
@@ -179,7 +179,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
to_chat(usr, "<span class='notice'>Sorry, that function is not enabled on this server.</span>")
return
browse_messages(null, usr.ckey, null, TRUE)
browse_messages(null, usr.ckey, null, TRUE, override = TRUE)
/client/proc/self_playtime()
set name = "View tracked playtime"
+1 -1
View File
@@ -554,7 +554,7 @@ Since Ramadan is an entire month that lasts 29.5 days on average, the start and
/datum/holiday/xmas
name = CHRISTMAS
begin_day = 10
begin_day = 18
begin_month = DECEMBER
end_day = 27
drone_hat = /obj/item/clothing/head/santa
+12
View File
@@ -73,6 +73,12 @@
/// Starting skill modifiers.
var/list/starting_modifiers
// These can be flags but I don't care because they're never changed
/// Can you always join as this job even while respawning (should probably only be on for assistant)
var/always_can_respawn_as = FALSE
/// Is this job considered a combat role for respawning? (usually sec/command)
var/considered_combat_role = FALSE
/**
* Checks if we should be created on a certain map
*/
@@ -118,6 +124,12 @@
//Used for a special check of whether to allow a client to latejoin as this job.
/datum/job/proc/special_check_latejoin(client/C)
var/joined = LAZYLEN(C.prefs?.characters_joined_as)
if(C.prefs?.respawn_restrictions_active && (joined || CONFIG_GET(flag/respawn_penalty_includes_observe)))
if(!CONFIG_GET(flag/allow_non_assistant_respawn) && !always_can_respawn_as)
return FALSE
if(!CONFIG_GET(flag/allow_combat_role_respawn) && considered_combat_role)
return FALSE
return TRUE
/datum/job/proc/GetAntagRep()
+1
View File
@@ -16,6 +16,7 @@
display_order = JOB_DISPLAY_ORDER_AI
var/do_special_check = TRUE
threat = 5
considered_combat_role = TRUE
starting_modifiers = list(/datum/skill_modifier/job/level/wiring/basic)
+1
View File
@@ -18,6 +18,7 @@ Assistant
paycheck_department = ACCOUNT_CIV
display_order = JOB_DISPLAY_ORDER_ASSISTANT
dresscodecompliant = FALSE
always_can_respawn_as = TRUE
threat = 0.2
/datum/job/assistant/get_access()
+1
View File
@@ -14,6 +14,7 @@
exp_requirements = 180
exp_type = EXP_TYPE_COMMAND
exp_type_department = EXP_TYPE_COMMAND
considered_combat_role = TRUE
outfit = /datum/outfit/job/captain
@@ -15,6 +15,7 @@
exp_requirements = 180
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_ENGINEERING
considered_combat_role = TRUE
outfit = /datum/outfit/job/ce
plasma_outfit = /datum/outfit/plasmaman/ce
@@ -15,6 +15,7 @@
exp_requirements = 180
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_MEDICAL
considered_combat_role = TRUE
outfit = /datum/outfit/job/cmo
plasma_outfit = /datum/outfit/plasmaman/cmo
+1
View File
@@ -11,6 +11,7 @@
minimal_player_age = 21
exp_requirements = 120
exp_type = EXP_TYPE_CREW
considered_combat_role = TRUE
starting_modifiers = list(/datum/skill_modifier/job/level/wiring/basic)
+1
View File
@@ -15,6 +15,7 @@
outfit = /datum/outfit/job/detective
plasma_outfit = /datum/outfit/plasmaman/detective
considered_combat_role = TRUE
access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM)
@@ -15,7 +15,7 @@
exp_requirements = 180
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_SERVICE
considered_combat_role = TRUE
outfit = /datum/outfit/job/hop
plasma_outfit = /datum/outfit/plasmaman/hop
@@ -14,6 +14,7 @@
minimal_player_age = 10
exp_requirements = 300
exp_type = EXP_TYPE_CREW
considered_combat_role = TRUE
exp_type_department = EXP_TYPE_SECURITY
outfit = /datum/outfit/job/hos
@@ -15,6 +15,7 @@
exp_requirements = 180
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_SUPPLY
considered_combat_role = TRUE
outfit = /datum/outfit/job/quartermaster
@@ -15,6 +15,7 @@
exp_type_department = EXP_TYPE_SCIENCE
exp_requirements = 180
exp_type = EXP_TYPE_CREW
considered_combat_role = TRUE
outfit = /datum/outfit/job/rd
plasma_outfit = /datum/outfit/plasmaman/rd
@@ -12,6 +12,7 @@
minimal_player_age = 7
exp_requirements = 300
exp_type = EXP_TYPE_CREW
considered_combat_role = TRUE
outfit = /datum/outfit/job/security
plasma_outfit = /datum/outfit/plasmaman/security
+1
View File
@@ -12,6 +12,7 @@
minimal_player_age = 7
exp_requirements = 300
exp_type = EXP_TYPE_CREW
considered_combat_role = TRUE
outfit = /datum/outfit/job/warden
plasma_outfit = /datum/outfit/plasmaman/warden
+4 -2
View File
@@ -124,7 +124,8 @@
return TRUE
/datum/keybinding/living/hold_sprint
hotkey_keys = list("Shift")
hotkey_keys = list()
classic_keys = list()
name = "hold_sprint"
full_name = "Sprint (hold down)"
description = "Hold down to sprint"
@@ -144,7 +145,8 @@
return TRUE
/datum/keybinding/living/toggle_sprint
hotkey_keys = list()
hotkey_keys = list("Shift")
classic_keys = list("Shift")
name = "toggle_sprint"
full_name = "Sprint (toggle)"
description = "Press to toggle sprint"
+1 -1
View File
@@ -5,7 +5,7 @@
SHOULD_NOT_SLEEP(TRUE)
/datum/proc/keyLoop(client/user) // Called once every frame
//SHOULD_NOT_SLEEP(TRUE)
SHOULD_NOT_SLEEP(TRUE)
/client/verb/fix_macros()
set name = "Fix Keybindings"
+3 -4
View File
@@ -81,11 +81,10 @@
STOP_PROCESSING(SSmachines, src)
icon_state = "coinpress0"
/obj/machinery/mineral/mint/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/mineral/mint/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "Mint", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "Mint", name)
ui.open()
/obj/machinery/mineral/mint/ui_data()
@@ -375,7 +375,9 @@
ready = PLAYER_NOT_READY
return FALSE
var/this_is_like_playing_right = alert(src,"Are you sure you wish to observe? You will not be able to play this round!","Player Setup","Yes","No")
var/mintime = max(CONFIG_GET(number/respawn_delay), (SSticker.round_start_time + (CONFIG_GET(number/respawn_minimum_delay_roundstart) * 600)) - world.time, 0)
var/this_is_like_playing_right = alert(src,"Are you sure you wish to observe? You will not be able to respawn for [round(mintime / 600, 0.1)] minutes!!","Player Setup","Yes","No")
if(QDELETED(src) || !src.client || this_is_like_playing_right != "Yes")
ready = PLAYER_NOT_READY
@@ -397,6 +399,7 @@
stack_trace("There's no freaking observer landmark available on this map or you're making observers before the map is initialised")
transfer_ckey(observer, FALSE)
observer.client = client
observer.client.prefs?.respawn_time_of_death = world.time
observer.set_ghost_appearance()
if(observer.client && observer.client.prefs)
observer.real_name = observer.client.prefs.real_name
@@ -463,6 +466,9 @@
alert(src, "An administrator has disabled late join spawning.")
return FALSE
if(!respawn_latejoin_check(notify = TRUE))
return FALSE
var/arrivals_docked = TRUE
if(SSshuttle.arrivals)
close_spawn_windows() //In case we get held up
@@ -526,6 +532,8 @@
GLOB.joined_player_list += character.ckey
GLOB.latejoiners += character
LAZYOR(character.client.prefs.slots_joined_as, character.client.prefs.default_slot)
LAZYOR(character.client.prefs.characters_joined_as, character.real_name)
if(CONFIG_GET(flag/allow_latejoin_antagonists) && humanc) //Borgs aren't allowed to be antags. Will need to be tweaked if we get true latejoin ais.
if(SSshuttle.emergency)
+11 -3
View File
@@ -264,7 +264,7 @@ Transfer_mind is there to check if mob is being deleted/not going to have a body
Works together with spawning an observer, noted above.
*/
/mob/proc/ghostize(can_reenter_corpse = TRUE, special = FALSE, penalize = FALSE, voluntary = FALSE)
/mob/proc/ghostize(can_reenter_corpse = TRUE, special = FALSE, penalize = FALSE, voluntary = FALSE, cryo = FALSE)
var/sig_flags = SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, can_reenter_corpse, special, penalize)
penalize = !(sig_flags & COMPONENT_DO_NOT_PENALIZE_GHOSTING) && (suiciding || penalize) // suicide squad.
voluntary_ghosted = voluntary
@@ -277,6 +277,12 @@ Works together with spawning an observer, noted above.
if (client && client.prefs && client.prefs.auto_ooc)
if (!(client.prefs.chat_toggles & CHAT_OOC))
client.prefs.chat_toggles ^= CHAT_OOC
if(ckey && penalize)
var/datum/preferences/P = GLOB.preferences_datums[ckey]
if(P)
P.respawn_restrictions_active = TRUE
P.respawn_time_of_death = world.time
P.respawn_did_cryo = cryo
transfer_ckey(ghost, FALSE)
ghost.client.init_verbs()
if(penalize)
@@ -423,12 +429,14 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(usr, "<span class='warning'>You're already stuck out of your body!</span>")
return FALSE
var/response = alert(src, "Are you sure you want to prevent (almost) all means of resuscitation? This cannot be undone. ","Are you sure you want to stay dead?","Yes","No")
var/response = alert(src, "Are you sure you want to prevent (almost) all means of resuscitation? This cannot be undone. THIS WILL ALSO STOP YOU FROM RESPAWNING!!!","Are you sure you want to stay dead and never respawn?","Yes","No")
if(response != "Yes")
return
can_reenter_corpse = FALSE
to_chat(src, "You can no longer be brought back into your body.")
client.prefs?.dnr_triggered = TRUE
to_chat(src, "You can no longer be brought back into your body or respawn.")
return TRUE
/mob/dead/observer/proc/notify_cloning(var/message, var/sound, var/atom/source, flashwindow = TRUE)
+181
View File
@@ -0,0 +1,181 @@
// ADMIN VERBS BEGIN
/**
* Fully returns a player to lobby, allowing them to bypass all respawn restrictions
* Works on ghosts or new players (lobby players)
* If a lobby player is selected, their restrictions are removed.
*/
/client/proc/admin_cmd_respawn_return_to_lobby()
set name = "Respawn Player (Unrestricted)"
set desc = "Gives a player an unrestricted respawn, resetting all respawn restrictions for them."
set category = "Admin"
var/list/mob/valid = list()
for(var/mob/dead/observer/I in GLOB.dead_mob_list)
if(!I.client)
continue
valid["[I.ckey] - Observing: [I]"] = I.ckey
for(var/mob/dead/new_player/I in GLOB.dead_mob_list)
if(!I.client || !I.client.prefs.respawn_restrictions_active)
continue
valid["[I.ckey] - IN LOBBY"] = I.ckey
if(!valid.len)
to_chat(src, "<span class='warning'>No player found that is either a ghost or is in lobby with restrictions active.</span>")
return
var/ckey = valid[input(src, "Choose a player (only showing logged in players who have restrictions)", "Unrestricted Respawn") as null|anything in valid]
var/client/player = GLOB.directory[ckey]
if(!player)
to_chat(src, "<span class='warning'>Client not found.</span>")
return
var/mob/M = player.mob
if(istype(M, /mob/dead/observer))
var/mob/dead/observer/O = M
var/confirm = alert(src, "Send [O]([ckey]) back to the lobby without respawn restrictions?", "Send to Lobby", "Yes", "No")
if(confirm != "Yes")
return
message_admins("[key_name_admin(src)] gave [key_name_admin(O)] a full respawn and sent them back to the lobby.")
log_admin("[key_name(src)] gave [key_name(O)] a full respawn and sent them back to the lobby.")
to_chat(O, "<span class='userdanger'>You have been given a full respawn.</span>")
O.do_respawn(FALSE)
O.client.prefs.dnr_triggered = FALSE
else if(istype(M, /mob/dead/new_player))
var/mob/dead/new_player/NP = M
var/confirm = alert(src, "Remove [NP]'s respawn restrictions?", "Remove Restrictions", "Yes", "No")
if(confirm != "Yes")
return
message_admins("[key_name_admin(src)] removed [ckey]'s respawn restrictions.")
log_admin("[key_name(src)] removed [ckey]'s respawn restrictions")
NP.client.prefs.respawn_restrictions_active = FALSE
NP.client.prefs.dnr_triggered = FALSE
to_chat(NP, "<span class='boldnotie'>Your respawn restrictions have been removed.")
else
CRASH("Invalid mobtype")
/**
* Allows a ghost to bypass respawn delay without lifting respawn restrictions
*/
/client/proc/admin_cmd_remove_ghost_respawn_timer()
set name = "Remove Respawn Timer for Player"
set desc = "Removes a player's respawn timer without removing their respawning restrictions."
set category = "Admin"
var/list/mob/dead/observer/valid = list()
for(var/mob/dead/observer/I in GLOB.dead_mob_list)
if(!I.client)
continue
valid["[I.ckey] - [I.name]"] = I
if(!valid.len)
to_chat(src, "<span class='warning'>No logged in ghosts found.</span>")
return
var/mob/dead/observer/O = valid[input(src, "Choose a player (only showing logged in)", "Remove Respawn Timer") as null|anything in valid]
if(!O.client)
to_chat(src, "<span class='warning'>[O] has no client.</span>")
return
var/timeleft = O.time_left_to_respawn()
if(!timeleft)
to_chat(src, "<span class='warning'>[O] can already respawn.")
return
message_admins("[key_name_admin(src)] removed [key_name_admin(O)]'s respawn timer.")
log_admin("[key_name(src)] removed [key_name(O)]'s respawn timer.")
O.client.prefs.respawn_time_of_death = -INFINITY
to_chat(O, "<span class='boldnotice'>Your respawn timer has been removed.")
// ADMIN VERBS END
/**
* Checks if we can latejoin on the currently selected slot, taking into account respawn status.
*/
/mob/dead/new_player/proc/respawn_latejoin_check(notify = FALSE)
if(!client.prefs.respawn_restrictions_active)
return TRUE
var/can_same_person = CONFIG_GET(flag/allow_same_character_respawn)
if(can_same_person)
return TRUE
var/nameless = client.prefs.nameless
var/randomname = client.prefs.be_random_name
var/randombody = client.prefs.be_random_body
if(randombody && (nameless || randomname))
return TRUE // somewhat unrecognizable
if(client.prefs.slots_joined_as && (client.prefs.default_slot in client.prefs.slots_joined_as))
if(notify)
to_chat(src, "<span class='userdanger'>You cannot respawn on the same slot. Joined slots: [english_list(client.prefs.slots_joined_as)].")
return FALSE
if((!nameless && !randomname) && (client.prefs.characters_joined_as && (client.prefs.real_name in client.prefs.characters_joined_as)))
if(notify)
to_chat(src, "<span class='userdanger'>You cannot respawn on the same character. Joined slots: [english_list(client.prefs.characters_joined_as)].")
return FALSE
return TRUE
/**
* Attempts to respawn.
*/
/mob/dead/observer/verb/respawn()
set name = "Respawn"
set category = "OOC"
if(!CONFIG_GET(flag/respawns_enabled))
to_chat(src, "<span class='warning'>Respawns are disabled in configuration.</span>")
return
if(client.prefs.dnr_triggered)
to_chat(src, "<span class='danger'>You cannot respawn as you have enabled DNR.</span>")
return
var/roundstart_timeleft = (SSticker.round_start_time + (CONFIG_GET(number/respawn_minimum_delay_roundstart) * 600)) - world.time
if(roundstart_timeleft > 0)
to_chat(src, "<span class='warning'>It's been too short of a time since the round started! Please wait [CEILING(roundstart_timeleft / 600, 0.1)] more minutes.</span>")
return
var/list/banned_modes = CONFIG_GET(keyed_list/respawn_chaos_gamemodes)
if(SSticker.mode && banned_modes[lowertext(SSticker.mode.config_tag)])
to_chat(src, "<span class='warning'>The current mode tag, [SSticker.mode.config_tag], is not eligible for respawn.</span>")
return
var/timeleft = time_left_to_respawn()
if(timeleft)
to_chat(src, "<span class='warning'>It's been too short of a time since you died/observed! Please wait [round(timeleft / 600, 0.1)] more minutes.</span>")
return
do_respawn(TRUE)
/**
* Gets time left until we can respawn. Returns 0 if we can respawn now.
*/
/mob/dead/observer/verb/time_left_to_respawn()
ASSERT(client)
return max(0, ((client.prefs.respawn_did_cryo? CONFIG_GET(number/respawn_delay_cryo) : CONFIG_GET(number/respawn_delay)) MINUTES + client.prefs.respawn_time_of_death) - world.time)
/**
* Handles respawning
*/
/mob/dead/observer/proc/do_respawn(penalize)
if(!client)
return
if(isnull(penalize))
penalize = client.prefs.respawn_restrictions_active
client.prefs.respawn_restrictions_active = penalize
to_chat(src, "<span class='userdanger'>You have been respawned to the lobby. \
Remember to take heed of rules regarding round knowledge - notably, that ALL past lives are forgotten. \
Any character you join as has NO knowledge of round events unless specified otherwise by an admin.</span>")
message_admins("[key_name_admin(src)] was respawned to lobby [penalize? "with" : "without"] restrictions.")
log_game("[key_name(src)] was respawned to lobby [penalize? "with" : "without"] restrictions.")
transfer_to_lobby()
/**
* Actual proc that removes us and puts us back on lobby
*/
/mob/dead/observer/proc/transfer_to_lobby()
if(!client) // if no one's in us we can just be deleted
qdel(src)
return
client.screen.Cut()
client.view_size.resetToDefault()
client.generate_clickcatcher()
client.apply_clickcatcher()
client.view_size.setDefault(getScreenSize(client.prefs.widescreenpref))
client.view_size.resetToDefault()
var/mob/dead/new_player/M = new /mob/dead/new_player
M.ckey = ckey
@@ -14,6 +14,7 @@
blocks_emissive = EMISSIVE_BLOCK_UNIQUE
block_parry_data = /datum/block_parry_data/unarmed/human
default_block_parry_data = /datum/block_parry_data/unarmed/pugilist
//Hair colour and style
var/hair_color = "000"
@@ -98,20 +99,54 @@
parry_failed_clickcd_duration = 0.4
parry_data = list( // yeah it's snowflake
"HUMAN_PARRY_STAGGER" = 3 SECONDS,
"HUMAN_PARRY_PUNCH" = TRUE,
"HUMAN_PARRY_MININUM_EFFICIENCY" = 0.9
"UNARMED_PARRY_STAGGER" = 3 SECONDS,
"UNARMED_PARRY_PUNCH" = TRUE,
"UNARMED_PARRY_MININUM_EFFICIENCY" = 90
)
/mob/living/carbon/human/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time)
/mob/living/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time)
var/datum/block_parry_data/D = return_block_parry_datum(block_parry_data)
. = ..()
if(!owner.Adjacent(attacker))
return ..()
if(parry_efficiency < D.parry_data["HUMAN_PARRY_MINIMUM_EFFICIENCY"])
return ..()
return
if(parry_efficiency < D.parry_data["UNARMED_PARRY_MINIMUM_EFFICIENCY"])
return
visible_message("<span class='warning'>[src] strikes back perfectly at [attacker], staggering them!</span>")
if(D.parry_data["HUMAN_PARRY_PUNCH"])
if(D.parry_data["UNARMED_PARRY_PUNCH"])
UnarmedAttack(attacker, TRUE, INTENT_HARM, ATTACK_IS_PARRY_COUNTERATTACK | ATTACK_IGNORE_ACTION | ATTACK_IGNORE_CLICKDELAY | NO_AUTO_CLICKDELAY_HANDLING)
var/mob/living/L = attacker
if(istype(L))
L.Stagger(D.parry_data["HUMAN_PARRY_STAGGER"])
L.Stagger(D.parry_data["UNARMED_PARRY_STAGGER"])
/// Unarmed parry data for pugilists
/datum/block_parry_data/unarmed/pugilist
parry_respect_clickdelay = FALSE
parry_stamina_cost = 4
parry_attack_types = ATTACK_TYPE_UNARMED | ATTACK_TYPE_PROJECTILE | ATTACK_TYPE_TACKLE | ATTACK_TYPE_THROWN | ATTACK_TYPE_MELEE
parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING
parry_time_windup = 0
parry_time_spindown = 0
parry_time_active = 5
parry_time_perfect = 1.5
parry_time_perfect_leeway = 1.5
parry_imperfect_falloff_percent = 20
parry_efficiency_perfect = 100
parry_efficiency_perfect_override = list(
ATTACK_TYPE_PROJECTILE_TEXT = 60,
)
parry_efficiency_considered_successful = 0.01
parry_efficiency_to_counterattack = 0.01
parry_max_attacks = INFINITY
parry_failed_cooldown_duration = 1.5 SECONDS
parry_failed_stagger_duration = 0
parry_cooldown = 0
parry_failed_clickcd_duration = 0.8
parry_data = list( // yeah it's snowflake
"UNARMED_PARRY_STAGGER" = 3 SECONDS,
"UNARMED_PARRY_PUNCH" = TRUE,
"UNARMED_PARRY_MININUM_EFFICIENCY" = 90
)
+4
View File
@@ -66,6 +66,10 @@
GLOB.alive_mob_list -= src
if(!gibbed)
GLOB.dead_mob_list += src
if(ckey)
var/datum/preferences/P = GLOB.preferences_datums[ckey]
if(P)
P.respawn_time_of_death = world.time
set_drugginess(0)
set_disgust(0)
SetSleeping(0, 0)
+2 -2
View File
@@ -264,9 +264,9 @@
return FALSE
/mob/living/carbon/human/has_tail()
if(!dna || !dna.features)
if(!dna || !dna.species)
return ..()
var/list/L = dna.features // caches list because i refuse to type it out and because performance
var/list/L = dna.species.mutant_bodyparts // caches list because i refuse to type it out and because performance
return (L["mam_tail"] && (L["mam_tail"] != "None")) || (L["tail_human"] && (L["tail_human"] != "None")) || (L["tail_lizard"] && (L["tail_lizard"] != "None"))
/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE)
@@ -127,7 +127,7 @@
handle_parry_ending_effects(data, effect_text)
parrying = NOT_PARRYING
parry_start_time = 0
parry_end_time_last = world.time
parry_end_time_last = world.time + (successful? 0 : data.parry_failed_cooldown_duration)
successful_parries = null
/**
@@ -154,6 +154,8 @@ GLOBAL_LIST_EMPTY(block_parry_data)
var/parry_failed_stagger_duration = 3.5 SECONDS
/// Clickdelay duration post-parry if you fail to parry an attack
var/parry_failed_clickcd_duration = 2 SECONDS
/// Parry cooldown post-parry if failed. This is ADDED to parry_cooldown!!!
var/parry_failed_cooldown_duration = 0 SECONDS
/**
* Quirky proc to get average of flags in list that are in attack_type because why is attack_type a flag.
@@ -32,6 +32,10 @@
// Combat - Blocking/Parrying system
/// Our block_parry_data for unarmed blocks/parries. Currently only used for parrying, as unarmed block isn't implemented yet. YOU MUST RUN [get_block_parry_data(this)] INSTEAD OF DIRECTLY ACCESSING!
var/datum/block_parry_data/block_parry_data = /datum/block_parry_data // defaults to *something* because [combat_flags] dictates whether or not we can unarmed block/parry.
/// Default
var/datum/block_parry_data/default_block_parry_data = /datum/block_parry_data
/// If we're a pugilist
var/datum/block_parry_data/pugilist_block_parry_data = /datum/block_parry_data/unarmed/pugilist
// Blocking
/// The item the user is actively blocking with if any.
var/obj/item/active_block_item
@@ -1,12 +1,3 @@
/// IN THE FUTURE, WE WILL PROBABLY REFACTOR TO LESSEN THE NEED FOR UPDATE_MOBILITY, BUT FOR NOW.. WE CAN START DOING THIS.
/// FOR BLOCKING MOVEMENT, USE TRAIT_MOBILITY_NOMOVE AS MUCH AS POSSIBLE. IT WILL MAKE REFACTORS IN THE FUTURE EASIER.
/mob/living/ComponentInitialize()
. = ..()
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOMOVE), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOPICKUP), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOUSE), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOREST), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_LIVING_NO_DENSITY), .proc/update_density)
//Stuff like mobility flag updates, resting updates, etc.
+20
View File
@@ -0,0 +1,20 @@
/// IN THE FUTURE, WE WILL PROBABLY REFACTOR TO LESSEN THE NEED FOR UPDATE_MOBILITY, BUT FOR NOW.. WE CAN START DOING THIS.
/// FOR BLOCKING MOVEMENT, USE TRAIT_MOBILITY_NOMOVE AS MUCH AS POSSIBLE. IT WILL MAKE REFACTORS IN THE FUTURE EASIER.
/mob/living/ComponentInitialize()
. = ..()
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOMOVE), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOPICKUP), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOUSE), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_MOBILITY_NOREST), .proc/update_mobility)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_LIVING_NO_DENSITY), .proc/update_density)
RegisterSignal(src, SIGNAL_TRAIT(TRAIT_PUGILIST), .proc/update_pugilism)
/mob/living/proc/update_pugilism()
if(HAS_TRAIT(src, TRAIT_PUGILIST))
combat_flags |= COMBAT_FLAG_UNARMED_PARRY
block_parry_data = pugilist_block_parry_data
else
var/initial_combat_flags = initial(combat_flags)
if(!(initial_combat_flags & COMBAT_FLAG_UNARMED_PARRY))
combat_flags &= ~COMBAT_FLAG_UNARMED_PARRY
block_parry_data = default_block_parry_data
+6 -33
View File
@@ -457,39 +457,6 @@
else
to_chat(src, "You don't have a mind datum for some reason, so you can't add a note to it.")
/mob/verb/abandon_mob()
set name = "Respawn"
set category = "OOC"
if (CONFIG_GET(flag/norespawn))
return
if ((stat != DEAD || !( SSticker )))
to_chat(usr, "<span class='boldnotice'>You must be dead to use this!</span>")
return
log_game("[key_name(usr)] used abandon mob.")
to_chat(usr, "<span class='boldnotice'>Please roleplay correctly!</span>")
if(!client)
log_game("[key_name(usr)] AM failed due to disconnect.")
return
client.screen.Cut()
client.screen += client.void
if(!client)
log_game("[key_name(usr)] AM failed due to disconnect.")
return
var/mob/dead/new_player/M = new /mob/dead/new_player()
if(!client)
log_game("[key_name(usr)] AM failed due to disconnect.")
qdel(M)
return
M.key = key
// M.Login() //wat
return
/mob/proc/transfer_ckey(mob/new_mob, send_signal = TRUE)
if(!new_mob || (!ckey && new_mob.ckey))
CRASH("transfer_ckey() called [new_mob ? "on ckey-less mob with a player mob as target" : "without a valid mob target"]!")
@@ -1002,6 +969,12 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
switch(var_name)
if("logging")
return debug_variable(var_name, logging, 0, src, FALSE)
if(NAMEOF(src, lastKnownIP))
if(!check_rights(R_SENSITIVE, FALSE))
return "SENSITIVE"
if(NAMEOF(src, computer_id))
if(!check_rights(R_SENSITIVE, FALSE))
return "SENSITIVE"
. = ..()
/mob/vv_auto_rename(new_name)
@@ -305,12 +305,6 @@
results = list(/datum/reagent/medicine/regen_jelly = 2)
required_reagents = list(/datum/reagent/medicine/tricordrazine = 1, /datum/reagent/toxin/slimejelly = 1)
/datum/chemical_reaction/jelly_convert
name = "Blood Jelly Conversion"
id = /datum/reagent/toxin/slimejelly
results = list(/datum/reagent/toxin/slimejelly = 1)
required_reagents = list(/datum/reagent/toxin = 1, /datum/reagent/blood/jellyblood = 1)
/datum/chemical_reaction/corazone
name = "Corazone"
id = /datum/reagent/medicine/corazone
+1
View File
@@ -30,6 +30,7 @@
# +SPAWN (or +CREATE) = mob transformations, spawning of most atoms including mobs (high-risk atoms, e.g. blackholes, will require the +FUN flag too)
# +AUTOLOGIN = admin gains powers upon connect. This defaults to on, you can use -AUTOLOGIN to make a role require using the readmin verb to gain powers. (this does not effect the admin's ability to walk past bans or other on-connect limitations like panic bunker or pop limit.)
# +DBRANKS = when sql-based admin loading is enabled, allows for non-temporary changes in the permissions panel to be saved (requires DB)
# +SENSITIVE = Defaults to on, use - to remove it. Allows reading IPs, CIDs, grabbing logs, and proccalls to certain things like admin holders/clients/subsystems considered "sensitive".
# +EVERYTHING (or +HOST or +ALL) = Simply gives you everything without having to type every flag
# END_KEYWORDS
+1 -3
View File
@@ -11,6 +11,7 @@ $include dynamic_config.txt
$include plushies/defines.txt
$include job_threats.txt
$include policy.txt
$include respawns.txt
# You can use the @ character at the beginning of a config option to lock it from being edited in-game
# Example usage:
@@ -206,9 +207,6 @@ VOTE_AUTOTRANSFER_MAXIMUM 4
## players' votes default to "No vote" (otherwise, default to "No change")
# DEFAULT_NO_VOTE
## disable abandon mob
NORESPAWN
## disables calling del(src) on newmobs if they logout before spawnin in
# DONT_DEL_NEWMOB
+29
View File
@@ -0,0 +1,29 @@
## Allow usage of the respawn system
RESPAWNS_ENABLED
## Minutes delay before allowing respawns, either from death or observing. Not an integer.
RESPAWN_DELAY 15.0
## Minutes delay before allowing respawns, if the user cryo'd. Not an integer.
RESPAWN_DELAY_CRYO 5.0
## Allow respawning as anything but an assistant.
ALLOW_NON_ASSISTANT_RESPAWN
## Allow respawning as security and command. Only works if ALLOW_NON_ASSISTANT_RESPAWN is on.
# ALLOW_COMBAT_ROLE_RESPAWN
## Allow respawning as the same character
# ALLOW_SAME_CHARACTER_RESPAWN
## Observing is considered a respawn for the purposes of role lockouts. Defaults to disabled. When disabled, only RESPAWNING rather than returning from observe locks you out.
# RESPAWN_PENALTY_INCLUDES_OBSERVE
## Time in minutes from round start before respawn is enabled
RESPAWN_MINIMUM_DELAY_ROUNDSTART 30.0
## Gamemode (config tags!) banlist for respawn
RESPAWN_CHAOS_GAMEMODES WIZARD
RESPAWN_CHAOS_GAMEMODES NUCLEAR
RESPAWN_CHAOS_GAMEMODES CLONWOPS
RESPAWN_CHOAS_GAMEMODES REVOLUTION
@@ -0,0 +1,5 @@
author: "silicons"
delete-after: True
changes:
- rscadd: "players can now respawn/return to lobby as a ghost after a 15 minute (default) delay and rejoin on another character with some/many restrictions"
- rscadd: "cryo now preserves everything"
@@ -0,0 +1,6 @@
author: "Trilbyspaceclone"
delete-after: True
changes:
- tweak: "Zeolites now use gold rather then uranium for catalyst"
- tweak: "Zeolites are not as hard to make ph wise"
- tweak: "Making Zeolites heats up the beaker less allowing for better control"
@@ -0,0 +1,4 @@
author: "Yakumo Chen"
delete-after: True
changes:
- balance: "Slime Jelly is no longer obtainable from slimepeople. Go ask Xenobio"
@@ -0,0 +1,5 @@
author: "Hatterhat"
delete-after: True
changes:
- bugfix: "The survival dagger light on the sprite now actually turns on and off."
- balance: "The survival dagger in the glaive kit that can also be bought by itself is now better at butchering things."
@@ -0,0 +1,4 @@
author: "DeltaFire15"
delete-after: True
changes:
- bugfix: "Bloodcult stunhands now work against clockies like they were supposed to instead of hardstunning."
@@ -0,0 +1,4 @@
author: "MrJWhit"
delete-after: True
changes:
- tweak: "Christmas starts on the 18th now"
@@ -0,0 +1,4 @@
author: "raspy-on-osu"
delete-after: True
changes:
- tweak: "windoor open length"
@@ -0,0 +1,4 @@
author: "timothyteakettle"
delete-after: True
changes:
- bugfix: "you can now only entwine tails with people who have a tail"
@@ -0,0 +1,4 @@
author: "Arturlang"
delete-after: True
changes:
- bugfix: "Bloodsuckers tresspass ability can no longer work while they are not awake."
@@ -0,0 +1,4 @@
author: "kiwedespars"
delete-after: True
changes:
- balance: "balanced bone gauntlets."
@@ -0,0 +1,4 @@
author: "silicons"
delete-after: True
changes:
- rscadd: "pugilists can now parry"
@@ -0,0 +1,4 @@
author: "silicons"
delete-after: True
changes:
- tweak: "anyone new to the server is lucky enough to have their sprint default to toggle instead of hold"
@@ -0,0 +1,4 @@
author: "Arturlang"
delete-after: True
changes:
- bugfix: "Fixes the mint machine's UI"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

@@ -591,21 +591,21 @@
/datum/chemical_reaction/fermi/zeolites
name = "Zeolites"
id = /datum/reagent/fermi/zeolites
results = list(/datum/reagent/fermi/zeolites = 5) //We make a lot! - But it's now somewhat dangerous, and needs a bit of uranium to catalyze the reaction
results = list(/datum/reagent/fermi/zeolites = 5) //We make a lot! - But it's now somewhat dangerous, and needs a bit of gold to catalyze the reaction
required_reagents = list(/datum/reagent/medicine/potass_iodide = 1, /datum/reagent/aluminium = 1, /datum/reagent/silicon = 1, /datum/reagent/oxygen = 1)
required_catalysts = list(/datum/reagent/uranium = 5)
required_catalysts = list(/datum/reagent/gold = 5)
//FermiChem vars:
OptimalTempMin = 500
OptimalTempMax = 750
ExplodeTemp = 850
OptimalpHMin = 2.8
OptimalpHMax = 5 //2.2 ph levels of optimal ph zone - centered at 3.9 - ph of ingredients mixed at equal values is 9.55; ph of result is 8.
ReactpHLim = 4
OptimalpHMin = 4.8
OptimalpHMax = 7
ReactpHLim = 5
//CatalystFact = 0
CurveSharpT = 1.5
CurveSharppH = 3
ThermicConstant = 7
ThermicConstant = 5
HIonRelease = -0.15
RateUpLim = 4
PurityMin = 0.5 //Good luck.
PurityMin = 0.5 //Good luck!
FermiChem = TRUE
-2
View File
@@ -1,2 +0,0 @@
Badmin
<span class='adminooc'>Spanmin</span>
+1 -3
View File
@@ -23,7 +23,6 @@ Plenty of things that aren't traditionally considered weapons can still be used
DEATH IS IMMINENT!
This game is older than most of the people playing it.
Do not go gentle into that good night.
Flashbangs can weaken blob tiles, allowing for you and the crew to easily destroy them.
Just the tip?
You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on them once more. An aggressive grab will momentarily stun someone, allow you to place Mekhi on a table by clicking on it, or throw them by toggling on throwing.
Some people are unable to read text on a game where half of it is based on text.
@@ -42,8 +41,7 @@ Mhelp is basically useless, and never used correctly. Being a Mentor just means
Toxins Bombs are not the only way to get RnD points. However, nobody will ever do any other method to get sci points...
Plasma men are a powerful race with many perks! No really, I swear! So what if they need an "ERP Plasma room" to fuck, who cares!
As a Cargo Tech make sure to always buy a tesla to sell back to CC. They love those those. Trust me!
Help.
Maints.
;HELP MAINT
Admins always regret meme options in their polls.
Putting cat ears on securitrons makes them table people and nya. Mekhi isn't a cat, but he still goes on the table, just roll with it.
As a Changeling, you can live without a head as they are merely vestigal to you, now, finally, you can be a Dullahan without it being Halloween.
+2 -2
View File
@@ -237,7 +237,7 @@ As a Changeling, you can absorb someone by strangling them and using the Absorb
As a Changeling, absorbing someone will give you their full memory. This can include things such as a Traitor's uplink, thus absorbing one will allow you to access the Traitor uplink and buy toys for your Changeling self to abuse.
As a Changeling, absorbing another Changeling will permanently boost your chemical reserve, allow you to pick more abilities, and make the victim unable to revive. Be careful when exposing your identity to other Changelings, as they may be out of those wonderful benefits.
As a Changeling, BZ gas will dramatically slow down or even halt your natural chemical regeneration, be sure to avoid it at all costs as some lunatics may try and flood portions of the station to deal with you.
As a Changeling, death is not the end for you! You can revive after two minutes from being dead by triggering your stasis ability, and then waiting for the prompt to resurrect yourself to show up.
As a Changeling, death is not the end for you! You can revive after 40 seconds from being dead by triggering your stasis ability, and then waiting for the prompt to resurrect yourself to show up.
As a Changeling, your Regenerate Limbs power will quickly heal all of your wounds, but they'll still leave scars. Changelings can use Fleshmend to get rid of scars, or you can ingest Carpotoxin to get rid of them like a normal person.
As a Cultist, do not cause too much chaos before your objective is completed. If the shuttle gets called too soon, you may not have enough time to win.
As a Cultist, your team starts off very weak, but if necessary can quickly convert everything they have into raw power. Make sure you have the numbers and equipment to support going loud, or the cult will fall flat on its face.
@@ -300,4 +300,4 @@ Laser pointers can be upgraded by replacing its micro laser with a better one fr
Being out of combat mode makes makes you deal less damage to people and objects when attacking. This stacks with the penalty incurred by resting.
Resting makes you deal less damage to people and objects when attacking. This stacks with the penalty incurred by being out of combat mode.
You do not regenerate as much stamina while in combat mode. Resting (being on the ground) makes you regenerate stamina faster.
Remember to be in combat mode while in combat, as otherwise you will be penalized by taking more incoming damage and dealing less damage to your adversary.
Keybinds can be reassigned in character setup on the keybindings tab. This is extremely useful, especially if you know how to use independent binds.
+3
View File
@@ -295,6 +295,7 @@
#include "code\controllers\configuration\entries\plushies.dm"
#include "code\controllers\configuration\entries\policy.dm"
#include "code\controllers\configuration\entries\resources.dm"
#include "code\controllers\configuration\entries\respawns.dm"
#include "code\controllers\configuration\entries\stamina_combat.dm"
#include "code\controllers\subsystem\acid.dm"
#include "code\controllers\subsystem\adjacent_air.dm"
@@ -2454,6 +2455,7 @@
#include "code\modules\mob\dead\observer\observer.dm"
#include "code\modules\mob\dead\observer\observer_movement.dm"
#include "code\modules\mob\dead\observer\orbit.dm"
#include "code\modules\mob\dead\observer\respawn.dm"
#include "code\modules\mob\dead\observer\say.dm"
#include "code\modules\mob\living\blood.dm"
#include "code\modules\mob\living\bloodcrawl.dm"
@@ -2471,6 +2473,7 @@
#include "code\modules\mob\living\living_defines.dm"
#include "code\modules\mob\living\living_mobility.dm"
#include "code\modules\mob\living\living_movement.dm"
#include "code\modules\mob\living\living_signals.dm"
#include "code\modules\mob\living\living_sprint.dm"
#include "code\modules\mob\living\login.dm"
#include "code\modules\mob\living\logout.dm"