diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index 97f07acc05..143063b4e9 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -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 diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index d7487eb54b..605b3f6485 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -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 diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index bbbf99c9de..0987e95dc9 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -225,6 +225,8 @@ . += "[seperator]AUTOLOGIN" if(rights & R_DBRANKS) . += "[seperator]DBRANKS" + if(rights & R_SENSITIVE) + . += "[seperator]SENSITIVE" if(!.) . = "NONE" return . diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index b558b28cf2..3fc275d436 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -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, diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 90ec3bc289..3c93952b65 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -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 diff --git a/code/controllers/configuration/entries/respawns.dm b/code/controllers/configuration/entries/respawns.dm new file mode 100644 index 0000000000..40c7d248a4 --- /dev/null +++ b/code/controllers/configuration/entries/respawns.dm @@ -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) diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm index 9b292a219d..28505c70f8 100644 --- a/code/controllers/subsystem/input.dm +++ b/code/controllers/subsystem/input.dm @@ -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] diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 95d8928368..198c380f41 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -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 diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm index 261e423640..7a1f0f6980 100644 --- a/code/datums/world_topic.dm +++ b/code/datums/world_topic.dm @@ -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) diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm index be8dee5bf8..29455afe56 100644 --- a/code/game/gamemodes/clock_cult/clock_cult.dm +++ b/code/game/gamemodes/clock_cult/clock_cult.dm @@ -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)) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 5bc0ed20b0..ea41d77e2a 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -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 += "<< Back

" dat += "

Recently stored objects




" - if(!frozen_items.len) + if(!stored_packages.len) dat += "There has been no storage usage at this terminal.
" else - for(var/obj/item/I in frozen_items) - dat += "[I.name]
" + for(var/obj/O in stored_packages) + dat += "[O.name]
" dat += "
" 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, "Access Denied.") 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, "There is nothing to recover from storage.") 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, "\The [I] is no longer in storage.") 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, "Access Denied.") 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, "There is nothing to recover from storage.") playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) return @@ -138,10 +140,10 @@ visible_message("The console beeps happily as it disgorges the desired objects.") 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 diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index bed006d0b5..7847c3757e 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -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) diff --git a/code/game/objects/items/kitchen.dm b/code/game/objects/items/kitchen.dm index f15a4d00e1..7f2fe6a01d 100644 --- a/code/game/objects/items/kitchen.dm +++ b/code/game/objects/items/kitchen.dm @@ -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" diff --git a/code/game/objects/items/puzzle_pieces.dm b/code/game/objects/items/puzzle_pieces.dm index 432247eb9a..345f3185da 100644 --- a/code/game/objects/items/puzzle_pieces.dm +++ b/code/game/objects/items/puzzle_pieces.dm @@ -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() diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index d4b2d03428..d4afc3dbb4 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -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, "You may now respawn.", confidential = TRUE) else diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index ef4abeb0ad..39053b8e15 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -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 diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 52b4fa05b1..e2c12353f7 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -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)) diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 09f9664295..8f4a9742ea 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -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 /* diff --git a/code/modules/admin/sql_message_system.dm b/code/modules/admin/sql_message_system.dm index 356746ddbc..4218d4238f 100644 --- a/code/modules/admin/sql_message_system.dm +++ b/code/modules/admin/sql_message_system.dm @@ -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, "Failed to establish database connection.") return diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index ad8dd168eb..e0434a6fea 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -936,6 +936,12 @@ else dat += "Mind Transfer Potion" + //Respawns + if(jobban_isbanned(M, ROLE_RESPAWN)) + dat += "Respawns" + else + dat += "Respawns" + dat += "" 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)) diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index e3937e19b6..1cddbe9509 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -23,13 +23,6 @@ var/rendered = "DEAD: [uppertext(holder.rank)]([src.holder.fakekey ? pick(nicknames) : src.key]) says, \"[emoji_parse(msg)]\"" - // 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 = "DEAD: [rank_name]([admin_name]) says, \"[emoji_parse(msg)]\"" - for (var/mob/M in GLOB.player_list) if(isnewplayer(M)) continue diff --git a/code/modules/admin/verbs/getlogs.dm b/code/modules/admin/verbs/getlogs.dm index 4cbb0214f7..446dbcc69a 100644 --- a/code/modules/admin/verbs/getlogs.dm +++ b/code/modules/admin/verbs/getlogs.dm @@ -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 diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm b/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm index 26c8bd5949..2fb7a2ccca 100644 --- a/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm +++ b/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm @@ -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, "Garlic in your blood is interfering with your powers!") return FALSE + if(must_be_concious) + if(owner.stat != CONSCIOUS) + if(display_error) + to_chat(owner, "You can't do this while you are unconcious!") + return FALSE // Incap? if(must_be_capacitated) var/mob/living/L = owner diff --git a/code/modules/antagonists/bloodsucker/powers/go_home.dm b/code/modules/antagonists/bloodsucker/powers/go_home.dm index c46a7fce6c..a8060ad07e 100644 --- a/code/modules/antagonists/bloodsucker/powers/go_home.dm +++ b/code/modules/antagonists/bloodsucker/powers/go_home.dm @@ -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) . = ..() diff --git a/code/modules/antagonists/bloodsucker/powers/masquerade.dm b/code/modules/antagonists/bloodsucker/powers/masquerade.dm index d83f938e3f..f4ce531ef0 100644 --- a/code/modules/antagonists/bloodsucker/powers/masquerade.dm +++ b/code/modules/antagonists/bloodsucker/powers/masquerade.dm @@ -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! diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 90eaeb57ac..574ebf66a1 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -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 diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm index e6ec3a08b6..4a697a6bd7 100644 --- a/code/modules/antagonists/cult/blood_magic.dm +++ b/code/modules/antagonists/cult/blood_magic.dm @@ -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, "In an brilliant flash of red, [L] [iscultist(L) ? "writhes in pain" : "falls to the ground!"]") + to_chat(user, "In an brilliant flash of red, [L] [is_servant_of_ratvar(L) ? "writhes in pain!" : "falls to the ground!"]") uses-- ..() diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm index efac62c930..6169c4d3f0 100644 --- a/code/modules/cargo/supplypod.dm +++ b/code/modules/cargo/supplypod.dm @@ -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 diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 267ee3a5e1..c75454f3a6 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -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] diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 2de91f172d..e97f6dc3f4 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -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 diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index 5a8325fd64..c242509344 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -179,7 +179,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") to_chat(usr, "Sorry, that function is not enabled on this server.") 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" diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm index c6c97f7cba..e3f5264568 100644 --- a/code/modules/holiday/holidays.dm +++ b/code/modules/holiday/holidays.dm @@ -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 diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index 6535b86ffd..0d7cd75649 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -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() diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm index a7401791ab..d0eb690e94 100644 --- a/code/modules/jobs/job_types/ai.dm +++ b/code/modules/jobs/job_types/ai.dm @@ -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) diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm index b8fc963989..db5390f323 100644 --- a/code/modules/jobs/job_types/assistant.dm +++ b/code/modules/jobs/job_types/assistant.dm @@ -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() diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm index 047a07062d..4806bf5546 100644 --- a/code/modules/jobs/job_types/captain.dm +++ b/code/modules/jobs/job_types/captain.dm @@ -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 diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm index 18be8c9835..902be0bdc8 100644 --- a/code/modules/jobs/job_types/chief_engineer.dm +++ b/code/modules/jobs/job_types/chief_engineer.dm @@ -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 diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm index 627a7a2ca1..bb5fc68809 100644 --- a/code/modules/jobs/job_types/chief_medical_officer.dm +++ b/code/modules/jobs/job_types/chief_medical_officer.dm @@ -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 diff --git a/code/modules/jobs/job_types/cyborg.dm b/code/modules/jobs/job_types/cyborg.dm index 4f74542b2a..761882894f 100644 --- a/code/modules/jobs/job_types/cyborg.dm +++ b/code/modules/jobs/job_types/cyborg.dm @@ -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) diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm index 65724765e1..c704326879 100644 --- a/code/modules/jobs/job_types/detective.dm +++ b/code/modules/jobs/job_types/detective.dm @@ -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) diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm index 41fb4b99da..f1b7d3e8c4 100644 --- a/code/modules/jobs/job_types/head_of_personnel.dm +++ b/code/modules/jobs/job_types/head_of_personnel.dm @@ -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 diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm index cfd8d7f6c0..c772a8acae 100644 --- a/code/modules/jobs/job_types/head_of_security.dm +++ b/code/modules/jobs/job_types/head_of_security.dm @@ -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 diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm index 4c6b8e064f..301acff5c4 100644 --- a/code/modules/jobs/job_types/quartermaster.dm +++ b/code/modules/jobs/job_types/quartermaster.dm @@ -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 diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm index 33f7df8260..6c2cb94d13 100644 --- a/code/modules/jobs/job_types/research_director.dm +++ b/code/modules/jobs/job_types/research_director.dm @@ -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 diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index bc83eb752d..bdae7fe028 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -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 diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm index c909342d6f..dae3094ebe 100644 --- a/code/modules/jobs/job_types/warden.dm +++ b/code/modules/jobs/job_types/warden.dm @@ -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 diff --git a/code/modules/keybindings/keybind/movement.dm b/code/modules/keybindings/keybind/movement.dm index d3a0e98def..296b127cb4 100644 --- a/code/modules/keybindings/keybind/movement.dm +++ b/code/modules/keybindings/keybind/movement.dm @@ -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" diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm index 81c59d481e..b5f09c65ca 100644 --- a/code/modules/keybindings/setup.dm +++ b/code/modules/keybindings/setup.dm @@ -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" diff --git a/code/modules/mining/mint.dm b/code/modules/mining/mint.dm index 57a79553c3..2a8e0f2516 100644 --- a/code/modules/mining/mint.dm +++ b/code/modules/mining/mint.dm @@ -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() diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 35cf9f9440..00c22ad96d 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -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) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 0c57c13e3f..9493d11792 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -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, "You're already stuck out of your body!") 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) diff --git a/code/modules/mob/dead/observer/respawn.dm b/code/modules/mob/dead/observer/respawn.dm new file mode 100644 index 0000000000..f966051ed3 --- /dev/null +++ b/code/modules/mob/dead/observer/respawn.dm @@ -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, "No player found that is either a ghost or is in lobby with restrictions active.") + 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, "Client not found.") + 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, "You have been given a full respawn.") + 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, "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, "No logged in ghosts found.") + 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, "[O] has no client.") + return + var/timeleft = O.time_left_to_respawn() + if(!timeleft) + to_chat(src, "[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, "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, "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, "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, "Respawns are disabled in configuration.") + return + + if(client.prefs.dnr_triggered) + to_chat(src, "You cannot respawn as you have enabled DNR.") + 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, "It's been too short of a time since the round started! Please wait [CEILING(roundstart_timeleft / 600, 0.1)] more minutes.") + 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, "The current mode tag, [SSticker.mode.config_tag], is not eligible for respawn.") + return + + var/timeleft = time_left_to_respawn() + if(timeleft) + to_chat(src, "It's been too short of a time since you died/observed! Please wait [round(timeleft / 600, 0.1)] more minutes.") + 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, "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.") + + 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 diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 89a4570e8d..2e8641b43e 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -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("[src] strikes back perfectly at [attacker], staggering them!") - 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 + ) diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index c605f91528..688b0cf63e 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -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) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 0c7855ee16..fcc1d734a3 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -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) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 7353add745..16855d6f22 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -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 /** diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm index 08ef674b33..e290956873 100644 --- a/code/modules/mob/living/living_blocking_parrying.dm +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -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. diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 5621fab9c1..d4ccc63b34 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -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 diff --git a/code/modules/mob/living/living_mobility.dm b/code/modules/mob/living/living_mobility.dm index 62c23cca9b..2256a93fa1 100644 --- a/code/modules/mob/living/living_mobility.dm +++ b/code/modules/mob/living/living_mobility.dm @@ -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. diff --git a/code/modules/mob/living/living_signals.dm b/code/modules/mob/living/living_signals.dm new file mode 100644 index 0000000000..0eb83658d7 --- /dev/null +++ b/code/modules/mob/living/living_signals.dm @@ -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 diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 3416a8b337..824e262ae1 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -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, "You must be dead to use this!") - return - - log_game("[key_name(usr)] used abandon mob.") - - to_chat(usr, "Please roleplay correctly!") - - 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) diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index fca89a1c73..bf7b94483e 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -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 diff --git a/config/admin_ranks.txt b/config/admin_ranks.txt index fc1cb96c86..adbba1acb6 100644 --- a/config/admin_ranks.txt +++ b/config/admin_ranks.txt @@ -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 diff --git a/config/config.txt b/config/config.txt index 5be76972ab..72df74beb1 100644 --- a/config/config.txt +++ b/config/config.txt @@ -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 diff --git a/config/respawns.txt b/config/respawns.txt new file mode 100644 index 0000000000..337a691b07 --- /dev/null +++ b/config/respawns.txt @@ -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 diff --git a/html/changelogs/AutoChangeLog-pr-13772.yml b/html/changelogs/AutoChangeLog-pr-13772.yml new file mode 100644 index 0000000000..109360cace --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13772.yml @@ -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" diff --git a/html/changelogs/AutoChangeLog-pr-13773.yml b/html/changelogs/AutoChangeLog-pr-13773.yml new file mode 100644 index 0000000000..498ac42561 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13773.yml @@ -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" diff --git a/html/changelogs/AutoChangeLog-pr-13815.yml b/html/changelogs/AutoChangeLog-pr-13815.yml new file mode 100644 index 0000000000..630ca9d7f7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13815.yml @@ -0,0 +1,4 @@ +author: "Yakumo Chen" +delete-after: True +changes: + - balance: "Slime Jelly is no longer obtainable from slimepeople. Go ask Xenobio" diff --git a/html/changelogs/AutoChangeLog-pr-13819.yml b/html/changelogs/AutoChangeLog-pr-13819.yml new file mode 100644 index 0000000000..77b1ec9a3d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13819.yml @@ -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." diff --git a/html/changelogs/AutoChangeLog-pr-13822.yml b/html/changelogs/AutoChangeLog-pr-13822.yml new file mode 100644 index 0000000000..743bdb500b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13822.yml @@ -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." diff --git a/html/changelogs/AutoChangeLog-pr-13823.yml b/html/changelogs/AutoChangeLog-pr-13823.yml new file mode 100644 index 0000000000..35d70339f7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13823.yml @@ -0,0 +1,4 @@ +author: "MrJWhit" +delete-after: True +changes: + - tweak: "Christmas starts on the 18th now" diff --git a/html/changelogs/AutoChangeLog-pr-13825.yml b/html/changelogs/AutoChangeLog-pr-13825.yml new file mode 100644 index 0000000000..ae82c1cd8e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13825.yml @@ -0,0 +1,4 @@ +author: "raspy-on-osu" +delete-after: True +changes: + - tweak: "windoor open length" diff --git a/html/changelogs/AutoChangeLog-pr-13828.yml b/html/changelogs/AutoChangeLog-pr-13828.yml new file mode 100644 index 0000000000..282b8a01e0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13828.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - bugfix: "you can now only entwine tails with people who have a tail" diff --git a/html/changelogs/AutoChangeLog-pr-13832.yml b/html/changelogs/AutoChangeLog-pr-13832.yml new file mode 100644 index 0000000000..756edb7657 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13832.yml @@ -0,0 +1,4 @@ +author: "Arturlang" +delete-after: True +changes: + - bugfix: "Bloodsuckers tresspass ability can no longer work while they are not awake." diff --git a/html/changelogs/AutoChangeLog-pr-13833.yml b/html/changelogs/AutoChangeLog-pr-13833.yml new file mode 100644 index 0000000000..8312c7a5a3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13833.yml @@ -0,0 +1,4 @@ +author: "kiwedespars" +delete-after: True +changes: + - balance: "balanced bone gauntlets." diff --git a/html/changelogs/AutoChangeLog-pr-13835.yml b/html/changelogs/AutoChangeLog-pr-13835.yml new file mode 100644 index 0000000000..0ac90921c3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13835.yml @@ -0,0 +1,4 @@ +author: "silicons" +delete-after: True +changes: + - rscadd: "pugilists can now parry" diff --git a/html/changelogs/AutoChangeLog-pr-13837.yml b/html/changelogs/AutoChangeLog-pr-13837.yml new file mode 100644 index 0000000000..eda0b77a83 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13837.yml @@ -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" diff --git a/html/changelogs/AutoChangeLog-pr-13844.yml b/html/changelogs/AutoChangeLog-pr-13844.yml new file mode 100644 index 0000000000..cd1f7a25d4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-13844.yml @@ -0,0 +1,4 @@ +author: "Arturlang" +delete-after: True +changes: + - bugfix: "Fixes the mint machine's UI" diff --git a/icons/obj/kitchen.dmi b/icons/obj/kitchen.dmi index 8cdff2214b..31312ac9a1 100644 Binary files a/icons/obj/kitchen.dmi and b/icons/obj/kitchen.dmi differ diff --git a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm index f4b8850f55..bbecd2f6f4 100644 --- a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm +++ b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm @@ -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 diff --git a/strings/admin_nicknames.txt b/strings/admin_nicknames.txt deleted file mode 100644 index 76198b6c05..0000000000 --- a/strings/admin_nicknames.txt +++ /dev/null @@ -1,2 +0,0 @@ -Badmin -Spanmin \ No newline at end of file diff --git a/strings/sillytips.txt b/strings/sillytips.txt index e6710de95e..8e28284534 100644 --- a/strings/sillytips.txt +++ b/strings/sillytips.txt @@ -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. diff --git a/strings/tips.txt b/strings/tips.txt index 2039b30359..5336f767a5 100644 --- a/strings/tips.txt +++ b/strings/tips.txt @@ -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. diff --git a/tgstation.dme b/tgstation.dme index d516793abf..99b1799b08 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -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"